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:
parent
2fff643776
commit
3e2a6b6253
6 changed files with 47 additions and 13 deletions
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue