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).
This commit is contained in:
Timothy B. Terriberry 2024-02-22 06:12:55 -08:00 committed by Jean-Marc Valin
parent 2fff643776
commit 3e2a6b6253
No known key found for this signature in database
GPG key ID: 5E5DD9A36F9189C8
6 changed files with 47 additions and 13 deletions

View file

@ -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;
}

View file

@ -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

View file

@ -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,

View file

@ -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(

View file

@ -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

View file

@ -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);