diff --git a/silk/dred_coding.c b/silk/dred_coding.c index d702fa3225a8f293654fcc6edfd31d2d2e097016..669ddc4133240b31fe2e2a75f7539c187eecd683 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 0a5ddb61fb51c914fa2045633021c6be42b49509..1ce040c23c46b201e7c99871030ad01cc6ae19ab 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 fefbf41d6f7f449b6c6d7cb10ea4f5bd0d5fa596..1b284330de56e269711f28f0bac2cd1786b145c1 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 804a67abe87c347612f780d4266ea8763483d99a..c3502bf32d56e7539a77c3ae7b88a9e734b7d878 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 137c963dd4f42fba206c2f324d606608490f357c..dd241049924ced832a43b94dea7d329779836e33 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 e9148a37468f8b0e99e64f7c68b5d4dcd8c2aa09..4c76182c9f248a99c143280ae57c514f7517cea0 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);