diff --git a/doc/plugins.rst b/doc/plugins.rst
index 571654df6..91f82d1af 100644
--- a/doc/plugins.rst
+++ b/doc/plugins.rst
@@ -643,11 +643,15 @@ Encodes into `Ogg Opus `_.
* - Setting
- Description
* - **bitrate**
- - Sets the data rate in bit per second. The special value "auto" lets libopus choose a rate (which is the default), and "max" uses the maximum possible data rate.
+ - Sets the data rate in bits per second. The special value "auto" lets libopus choose a rate (which is the default), and "max" uses the maximum possible data rate.
* - **complexity**
- Sets the `Opus complexity `_.
* - **signal**
- Sets the Opus signal type. Valid values are "auto" (the default), "voice" and "music".
+ * - **vbr yes|no|constrained**
+ - Sets the vbr mode. Setting to "yes" (default) enables variable bitrate, "no" forces constant bitrate and frame sizes, "constrained" uses constant bitrate analogous to CBR in AAC and MP3.
+ * - **packet_loss**
+ - Sets the expected packet loss percentage. This value can be increased from the default "0" for a more redundant stream at the expense of quality.
* - **opustags yes|no**
- Configures how metadata is interleaved into the stream. If set to yes, then metadata is inserted using ogg stream chaining, as specified in :rfc:`7845`. If set to no (the default), then ogg stream chaining is avoided and other output-dependent method is used, if available.
diff --git a/src/encoder/plugins/OpusEncoderPlugin.cxx b/src/encoder/plugins/OpusEncoderPlugin.cxx
index 5f3960446..d579d1e50 100644
--- a/src/encoder/plugins/OpusEncoderPlugin.cxx
+++ b/src/encoder/plugins/OpusEncoderPlugin.cxx
@@ -76,6 +76,9 @@ class PreparedOpusEncoder final : public PreparedEncoder {
opus_int32 bitrate;
int complexity;
int signal;
+ int packet_loss;
+ int vbr;
+ int vbr_constraint;
const bool chaining;
public:
@@ -118,6 +121,23 @@ PreparedOpusEncoder::PreparedOpusEncoder(const ConfigBlock &block)
signal = OPUS_SIGNAL_MUSIC;
else
throw std::runtime_error("Invalid signal");
+
+ value = block.GetBlockValue("vbr", "yes");
+ if (strcmp(value, "yes") == 0) {
+ vbr = 1U;
+ vbr_constraint = 0U;
+ } else if (strcmp(value, "no") == 0) {
+ vbr = 0U;
+ vbr_constraint = 0U;
+ } else if (strcmp(value, "constrained") == 0) {
+ vbr = 1U;
+ vbr_constraint = 1U;
+ } else
+ throw std::runtime_error("Invalid vbr");
+
+ packet_loss = block.GetBlockValue("packet_loss", 0U);
+ if (packet_loss > 100)
+ throw std::runtime_error("Invalid packet loss");
}
PreparedEncoder *
@@ -173,6 +193,9 @@ PreparedOpusEncoder::Open(AudioFormat &audio_format)
opus_encoder_ctl(enc, OPUS_SET_BITRATE(bitrate));
opus_encoder_ctl(enc, OPUS_SET_COMPLEXITY(complexity));
opus_encoder_ctl(enc, OPUS_SET_SIGNAL(signal));
+ opus_encoder_ctl(enc, OPUS_SET_VBR(vbr));
+ opus_encoder_ctl(enc, OPUS_SET_VBR_CONSTRAINT(vbr_constraint));
+ opus_encoder_ctl(enc, OPUS_SET_PACKET_LOSS_PERC(packet_loss));
return new OpusEncoder(audio_format, enc, chaining);
}