From a3ba0108d5a9dc122b1c623663b3480b192c7fbd Mon Sep 17 00:00:00 2001 From: Jean-Marc Valin Date: Mon, 22 Jan 2024 03:29:37 -0500 Subject: Initial DRED tuning Adjust q0, qD and duration based on bitrate and loss. --- silk/dred_config.h | 2 +- silk/dred_encoder.c | 6 +----- silk/dred_encoder.h | 2 +- src/opus_encoder.c | 59 +++++++++++++++++++++++++++++++++++++++++++++-------- 4 files changed, 54 insertions(+), 15 deletions(-) diff --git a/silk/dred_config.h b/silk/dred_config.h index 86da3b00..a729e696 100644 --- a/silk/dred_config.h +++ b/silk/dred_config.h @@ -36,7 +36,7 @@ #define DRED_EXPERIMENTAL_BYTES 2 -#define DRED_MIN_BYTES 16 +#define DRED_MIN_BYTES 8 /* these are inpart duplicates to the values defined in dred_rdovae_constants.h */ #define DRED_SILK_ENCODER_DELAY (79+12-80) diff --git a/silk/dred_encoder.c b/silk/dred_encoder.c index 0e1bb33f..bd0fe3fb 100644 --- a/silk/dred_encoder.c +++ b/silk/dred_encoder.c @@ -249,21 +249,17 @@ static void dred_encode_latents(ec_enc *enc, const float *x, const opus_uint8 *s } } -int dred_encode_silk_frame(const DREDEnc *enc, unsigned char *buf, int max_chunks, int max_bytes, int arch) { +int dred_encode_silk_frame(const DREDEnc *enc, unsigned char *buf, int max_chunks, int max_bytes, int q0, int dQ, int arch) { ec_enc ec_encoder; int q_level; int i; int offset; int ec_buffer_fill; - int q0; - int dQ; int state_qoffset; /* entropy coding of state and latents */ ec_enc_init(&ec_encoder, buf, max_bytes); - q0 = DRED_ENC_Q0; - dQ = 3; ec_enc_uint(&ec_encoder, enc->dred_offset, 32); ec_enc_uint(&ec_encoder, q0, 16); ec_enc_uint(&ec_encoder, dQ, 8); diff --git a/silk/dred_encoder.h b/silk/dred_encoder.h index 795bee4f..469b8085 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(const DREDEnc *enc, unsigned char *buf, int max_chunks, int max_bytes, int arch); +int dred_encode_silk_frame(const DREDEnc *enc, unsigned char *buf, int max_chunks, int max_bytes, int q0, int dQ, int arch); #endif diff --git a/src/opus_encoder.c b/src/opus_encoder.c index 21dfe4ff..024a9efd 100644 --- a/src/opus_encoder.c +++ b/src/opus_encoder.c @@ -45,6 +45,7 @@ #include "analysis.h" #include "mathops.h" #include "tuning_parameters.h" +#include "silk/dred_coding.h" #ifdef FIXED_POINT #include "fixed/structs_FIX.h" #else @@ -124,6 +125,9 @@ struct OpusEncoder { #endif #ifdef ENABLE_DRED int dred_duration; + int dred_q0; + int dred_dQ; + int dred_target_chunks; #endif int nonfinal_frame; /* current frame is not the final in a packet */ opus_uint32 rangeFinal; @@ -560,22 +564,60 @@ 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) { + int dred_chunks; + int i; + float bits; + /* Signaling DRED costs 3 bytes. */ + bits = 8*(3+DRED_EXPERIMENTAL_BYTES); + /* Approximation for the size of the IS. */ + bits += 50.f+dred_bits_table[q0]; + dred_chunks = IMIN((duration+5)/4, DRED_NUM_REDUNDANCY_FRAMES/2); + if (target_chunks != NULL) *target_chunks = 0; + for (i=0;idred_duration > 0) max_dred_bitrate = (120 + 6*st->dred_duration)*st->Fs/frame_size; - else max_dred_bitrate = 0; - dred_frac = MIN16(.75f, 3.f*st->silk_mode.packetLossPercentage/100.f); - bitrate_offset = st->silk_mode.useInBandFEC ? 18000 : 12000; + int target_chunks; + opus_int32 max_dred_bits; + int q0, dQ; + if (st->silk_mode.useInBandFEC) { + dred_frac = MIN16(.7f, 3.f*st->silk_mode.packetLossPercentage/100.f); + bitrate_offset = 20000; + } else { + dred_frac = MIN16(.8f, 4.f*st->silk_mode.packetLossPercentage/100.f); + bitrate_offset = 12000; + } + /* 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; target_dred_bitrate = IMAX(0, (int)(dred_frac*(bitrate_bps-bitrate_offset))); - dred_bitrate = IMIN(target_dred_bitrate, max_dred_bitrate); + 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); + } else { + max_dred_bits = 0; + target_chunks=0; + } + dred_bitrate = IMIN(target_dred_bitrate, max_dred_bits*st->Fs/frame_size); /* If we can't afford enough bits, don't bother with DRED at all. */ - if (dred_bitrate <= (DRED_MIN_BYTES+DRED_EXPERIMENTAL_BYTES)*8*st->Fs/frame_size) + if (target_chunks < 2) dred_bitrate = 0; + st->dred_q0 = q0; + st->dred_dQ = dQ; + st->dred_target_chunks = target_chunks; return dred_bitrate; } #endif @@ -2289,6 +2331,7 @@ opus_int32 opus_encode_native(OpusEncoder *st, const opus_val16 *pcm, int frame_ int dred_chunks; int dred_bytes_left; dred_chunks = IMIN((st->dred_duration+5)/4, DRED_NUM_REDUNDANCY_FRAMES/2); + if (st->use_vbr) dred_chunks = IMIN(dred_chunks, st->dred_target_chunks); /* Remaining space for DRED, accounting for cost the 3 extra bytes for code 3, padding length, and extension number. */ dred_bytes_left = IMIN(DRED_MAX_DATA_SIZE, max_data_bytes-ret-3); /* Check whether we actually have something to encode. */ @@ -2300,7 +2343,7 @@ opus_int32 opus_encode_native(OpusEncoder *st, const opus_val16 *pcm, int frame_ buf[0] = 'D'; 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->arch); + 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->arch); if (dred_bytes > 0) { dred_bytes += DRED_EXPERIMENTAL_BYTES; celt_assert(dred_bytes <= dred_bytes_left); -- cgit v1.2.3