aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTimothy B. Terriberry <tterribe@xiph.org>2024-02-22 06:12:55 -0800
committerJean-Marc Valin <jmvalin@jmvalin.ca>2024-02-22 14:30:40 -0500
commit3e2a6b6253fe7a514c50100e4144c52c065a7fa1 (patch)
tree2a81e78a2d13bb95143c910d9cecf0f9085a71d8
parent2fff6437763d5f1ea42ed1ed1bd8bf6e50643291 (diff)
downloadlibopus-3e2a6b6253fe7a514c50100e4144c52c065a7fa1.tar.gz
Add signaling for a maximum DRED quantizer.
Since any value of dQ > 0 will cause the initial quantizer to degrade to the format-implied maximum (15) with a sufficient number of DRED frames, allow signaling a maximum smaller than 15. This allows encoders to improve the minimum quality of long DRED sequences (at the expense of bitrate) without requiring a constant quantizer for all frames (dQ == 0).
-rw-r--r--silk/dred_coding.c4
-rw-r--r--silk/dred_coding.h2
-rw-r--r--silk/dred_decoder.c26
-rw-r--r--silk/dred_encoder.c13
-rw-r--r--silk/dred_encoder.h2
-rw-r--r--src/opus_encoder.c13
6 files changed, 47 insertions, 13 deletions
diff --git a/silk/dred_coding.c b/silk/dred_coding.c
index d702fa32..669ddc41 100644
--- a/silk/dred_coding.c
+++ b/silk/dred_coding.c
@@ -36,9 +36,9 @@
#include "dred_config.h"
#include "dred_coding.h"
-int compute_quantizer(int q0, int dQ, int i) {
+int compute_quantizer(int q0, int dQ, int qmax, int i) {
int quant;
static const int dQ_table[8] = {0, 2, 3, 4, 6, 8, 12, 16};
quant = q0 + (dQ_table[dQ]*i + 8)/16;
- return quant > 15 ? 15 : quant;
+ return quant > qmax ? qmax : quant;
}
diff --git a/silk/dred_coding.h b/silk/dred_coding.h
index 0a5ddb61..1ce040c2 100644
--- a/silk/dred_coding.h
+++ b/silk/dred_coding.h
@@ -31,6 +31,6 @@
#include "opus_types.h"
#include "entcode.h"
-int compute_quantizer(int q0, int dQ, int i);
+int compute_quantizer(int q0, int dQ, int qmax, int i);
#endif
diff --git a/silk/dred_decoder.c b/silk/dred_decoder.c
index fefbf41d..1b284330 100644
--- a/silk/dred_decoder.c
+++ b/silk/dred_decoder.c
@@ -57,6 +57,7 @@ int dred_ec_decode(OpusDRED *dec, const opus_uint8 *bytes, int num_bytes, int mi
int offset;
int q0;
int dQ;
+ int qmax;
int state_qoffset;
int extra_offset;
@@ -72,7 +73,28 @@ int dred_ec_decode(OpusDRED *dec, const opus_uint8 *bytes, int num_bytes, int mi
/* Compute total offset, including DRED position in a multiframe packet. */
dec->dred_offset = 16 - ec_dec_uint(&ec, 32) - extra_offset + dred_frame_offset;
/*printf("%d %d %d\n", dred_offset, q0, dQ);*/
-
+ qmax = 15;
+ if (q0 < 14 && dQ > 0) {
+ int nvals;
+ int ft;
+ int s;
+ /* The distribution for the dQmax symbol is split evenly between zero
+ (which implies qmax == 15) and larger values, with the probability of
+ all larger values being uniform.
+ This is equivalent to coding 1 bit to decide if the maximum is less than
+ 15 followed by a uint to decide the actual value if it is less than
+ 15, but combined into a single symbol. */
+ nvals = 15 - (q0 + 1);
+ ft = 2*nvals;
+ s = ec_decode(&ec, ft);
+ if (s >= nvals) {
+ qmax = q0 + (s - nvals) + 1;
+ ec_dec_update(&ec, s, s + 1, ft);
+ }
+ else {
+ ec_dec_update(&ec, 0, nvals, ft);
+ }
+ }
state_qoffset = q0*DRED_STATE_DIM;
dred_decode_latents(
&ec,
@@ -88,7 +110,7 @@ int dred_ec_decode(OpusDRED *dec, const opus_uint8 *bytes, int num_bytes, int mi
/* FIXME: Figure out how to avoid missing a last frame that would take up < 8 bits. */
if (8*num_bytes - ec_tell(&ec) <= 7)
break;
- q_level = compute_quantizer(q0, dQ, i/2);
+ q_level = compute_quantizer(q0, dQ, qmax, i/2);
offset = q_level*DRED_LATENT_DIM;
dred_decode_latents(
&ec,
diff --git a/silk/dred_encoder.c b/silk/dred_encoder.c
index 804a67ab..c3502bf3 100644
--- a/silk/dred_encoder.c
+++ b/silk/dred_encoder.c
@@ -257,7 +257,7 @@ static int dred_voice_active(const unsigned char *activity_mem, int offset) {
return 0;
}
-int dred_encode_silk_frame(DREDEnc *enc, unsigned char *buf, int max_chunks, int max_bytes, int q0, int dQ, unsigned char *activity_mem, int arch) {
+int dred_encode_silk_frame(DREDEnc *enc, unsigned char *buf, int max_chunks, int max_bytes, int q0, int dQ, int qmax, unsigned char *activity_mem, int arch) {
ec_enc ec_encoder;
int q_level;
@@ -301,6 +301,15 @@ int dred_encode_silk_frame(DREDEnc *enc, unsigned char *buf, int max_chunks, int
ec_enc_uint(&ec_encoder, 0, 2);
ec_enc_uint(&ec_encoder, total_offset, 32);
}
+ celt_assert(qmax >= q0);
+ if (q0 < 14 && dQ > 0) {
+ int nvals;
+ /* If you want to use qmax == q0, you should have set dQ = 0. */
+ celt_assert(qmax > q0);
+ nvals = 15 - (q0 + 1);
+ ec_encode(&ec_encoder, qmax >= 15 ? 0 : nvals + qmax - (q0 + 1),
+ qmax >= 15 ? nvals : nvals + qmax - q0, 2*nvals);
+ }
state_qoffset = q0*DRED_STATE_DIM;
dred_encode_latents(
&ec_encoder,
@@ -318,7 +327,7 @@ int dred_encode_silk_frame(DREDEnc *enc, unsigned char *buf, int max_chunks, int
for (i = 0; i < IMIN(2*max_chunks, enc->latents_buffer_fill-latent_offset-1); i += 2)
{
int active;
- q_level = compute_quantizer(q0, dQ, i/2);
+ q_level = compute_quantizer(q0, dQ, qmax, i/2);
offset = q_level * DRED_LATENT_DIM;
dred_encode_latents(
diff --git a/silk/dred_encoder.h b/silk/dred_encoder.h
index 137c963d..dd241049 100644
--- a/silk/dred_encoder.h
+++ b/silk/dred_encoder.h
@@ -66,6 +66,6 @@ void dred_deinit_encoder(DREDEnc *enc);
void dred_compute_latents(DREDEnc *enc, const float *pcm, int frame_size, int extra_delay, int arch);
-int dred_encode_silk_frame(DREDEnc *enc, unsigned char *buf, int max_chunks, int max_bytes, int q0, int dQ, unsigned char *activity_mem, int arch);
+int dred_encode_silk_frame(DREDEnc *enc, unsigned char *buf, int max_chunks, int max_bytes, int q0, int dQ, int qmax, unsigned char *activity_mem, int arch);
#endif
diff --git a/src/opus_encoder.c b/src/opus_encoder.c
index e9148a37..4c76182c 100644
--- a/src/opus_encoder.c
+++ b/src/opus_encoder.c
@@ -131,6 +131,7 @@ struct OpusEncoder {
int dred_duration;
int dred_q0;
int dred_dQ;
+ int dred_qmax;
int dred_target_chunks;
unsigned char activity_mem[DRED_MAX_FRAMES*4]; /* 2.5ms resolution*/
#endif
@@ -571,7 +572,7 @@ OpusEncoder *opus_encoder_create(opus_int32 Fs, int channels, int application, i
#ifdef ENABLE_DRED
static const float dred_bits_table[16] = {73.2f, 68.1f, 62.5f, 57.0f, 51.5f, 45.7f, 39.9f, 32.4f, 26.4f, 20.4f, 16.3f, 13.f, 9.3f, 8.2f, 7.2f, 6.4f};
-static int estimate_dred_bitrate(int q0, int dQ, int duration, opus_int32 target_bits, int *target_chunks) {
+static int estimate_dred_bitrate(int q0, int dQ, int qmax, int duration, opus_int32 target_bits, int *target_chunks) {
int dred_chunks;
int i;
float bits;
@@ -582,7 +583,7 @@ static int estimate_dred_bitrate(int q0, int dQ, int duration, opus_int32 target
dred_chunks = IMIN((duration+5)/4, DRED_NUM_REDUNDANCY_FRAMES/2);
if (target_chunks != NULL) *target_chunks = 0;
for (i=0;i<dred_chunks;i++) {
- int q = compute_quantizer(q0, dQ, i);
+ int q = compute_quantizer(q0, dQ, qmax, i);
bits += dred_bits_table[q];
if (target_chunks != NULL && bits < target_bits) *target_chunks = i+1;
}
@@ -597,7 +598,7 @@ static opus_int32 compute_dred_bitrate(OpusEncoder *st, opus_int32 bitrate_bps,
opus_int32 target_dred_bitrate;
int target_chunks;
opus_int32 max_dred_bits;
- int q0, dQ;
+ int q0, dQ, qmax;
if (st->silk_mode.useInBandFEC) {
dred_frac = MIN16(.7f, 3.f*st->silk_mode.packetLossPercentage/100.f);
bitrate_offset = 20000;
@@ -614,10 +615,11 @@ static opus_int32 compute_dred_bitrate(OpusEncoder *st, opus_int32 bitrate_bps,
/* Approximate fit based on a few experiments. Could probably be improved. */
q0 = IMIN(15, IMAX(4, 51 - 3*EC_ILOG(IMAX(1, bitrate_bps-bitrate_offset))));
dQ = bitrate_bps-bitrate_offset > 36000 ? 3 : 5;
+ qmax = 15;
target_dred_bitrate = IMAX(0, (int)(dred_frac*(bitrate_bps-bitrate_offset)));
if (st->dred_duration > 0) {
opus_int32 target_bits = target_dred_bitrate*frame_size/st->Fs;
- max_dred_bits = estimate_dred_bitrate(q0, dQ, st->dred_duration, target_bits, &target_chunks);
+ max_dred_bits = estimate_dred_bitrate(q0, dQ, qmax, st->dred_duration, target_bits, &target_chunks);
} else {
max_dred_bits = 0;
target_chunks=0;
@@ -628,6 +630,7 @@ static opus_int32 compute_dred_bitrate(OpusEncoder *st, opus_int32 bitrate_bps,
dred_bitrate = 0;
st->dred_q0 = q0;
st->dred_dQ = dQ;
+ st->dred_qmax = qmax;
st->dred_target_chunks = target_chunks;
return dred_bitrate;
}
@@ -2419,7 +2422,7 @@ static opus_int32 opus_encode_frame_native(OpusEncoder *st, const opus_val16 *pc
buf[1] = DRED_EXPERIMENTAL_VERSION;
#endif
dred_bytes = dred_encode_silk_frame(&st->dred_encoder, buf+DRED_EXPERIMENTAL_BYTES, dred_chunks, dred_bytes_left-DRED_EXPERIMENTAL_BYTES,
- st->dred_q0, st->dred_dQ, st->activity_mem, st->arch);
+ st->dred_q0, st->dred_dQ, st->dred_qmax, st->activity_mem, st->arch);
if (dred_bytes > 0) {
dred_bytes += DRED_EXPERIMENTAL_BYTES;
celt_assert(dred_bytes <= dred_bytes_left);