shithub: opus

Download patch

ref: 20bf3b33009b85f37bec9c8f4d0e6f6957602d06
parent: 1fd53f9a928a7c76d1a89c01f0e8466efaa071bf
author: Mark Harris <mark.hsj@gmail.com>
date: Wed Nov 2 02:18:25 EDT 2016

Reduce redundancy when SILK uses too many bits

Fix Hybrid redundancy assertion failure in ec_enc_shrink(), even
if SILK produces more than maxBits, by reducing or eliminating
redundancy when necessary.  Don't reserve space for redundancy that
is too small to be used for redundancy.  When there is not enough
space for redundancy, allow the regular frame to use all the bits.

--- a/src/opus_encoder.c
+++ b/src/opus_encoder.c
@@ -1961,7 +1961,6 @@
               st->silk_mode.bitRate += 3*rate_offset/5;
            else
               st->silk_mode.bitRate += rate_offset;
-           bytes_target += rate_offset * frame_size / (8 * st->Fs);
         }
 
         st->silk_mode.payloadSize_ms = 1000 * frame_size / st->Fs;
@@ -2005,11 +2004,11 @@
         /* Call SILK encoder for the low band */
 
         /* Max bits for SILK, counting ToC, redundancy bytes, and optionally redundancy. */
-        st->silk_mode.maxBits = IMIN(1275, max_data_bytes-1-redundancy_bytes)*8;
-        if (redundancy)
+        st->silk_mode.maxBits = (max_data_bytes-1)*8;
+        if (redundancy && redundancy_bytes >= 2)
         {
            /* Counting 1 bit for redundancy position and 20 bits for flag+size (only for hybrid). */
-           st->silk_mode.maxBits -= 1;
+           st->silk_mode.maxBits -= redundancy_bytes*8 + 1;
            if (st->mode == MODE_HYBRID)
               st->silk_mode.maxBits -= 20;
         }
@@ -2134,18 +2133,9 @@
 
         if (st->mode == MODE_HYBRID)
         {
-            int len;
-
-            len = (ec_tell(&enc)+7)>>3;
-            if (redundancy)
-               len += st->mode == MODE_HYBRID ? 3 : 1;
             if( st->use_vbr ) {
                 celt_encoder_ctl(celt_enc, OPUS_SET_BITRATE(st->bitrate_bps-st->silk_mode.bitRate));
-                nb_compr_bytes = max_data_bytes-1-redundancy_bytes;
                 celt_encoder_ctl(celt_enc, OPUS_SET_VBR_CONSTRAINT(0));
-            } else {
-                /* check if SILK used up too much */
-                nb_compr_bytes = len > bytes_target ? len : bytes_target;
             }
         } else {
             if (st->use_vbr)
@@ -2162,14 +2152,8 @@
                 celt_encoder_ctl(celt_enc, OPUS_SET_VBR(1));
                 celt_encoder_ctl(celt_enc, OPUS_SET_VBR_CONSTRAINT(st->vbr_constraint));
                 celt_encoder_ctl(celt_enc, OPUS_SET_BITRATE(st->bitrate_bps+bonus));
-                nb_compr_bytes = max_data_bytes-1-redundancy_bytes;
-            } else {
-                nb_compr_bytes = bytes_target;
             }
         }
-
-    } else {
-        nb_compr_bytes = 0;
     }
 
     ALLOC(tmp_prefill, st->channels*st->Fs/400, opus_val16);
@@ -2218,7 +2202,7 @@
     if ( st->mode != MODE_CELT_ONLY && ec_tell(&enc)+17+20*(st->mode == MODE_HYBRID) <= 8*(max_data_bytes-1))
     {
         /* For SILK mode, the redundancy is inferred from the length */
-        if (st->mode == MODE_HYBRID && (redundancy || ec_tell(&enc)+37 <= 8*nb_compr_bytes))
+        if (st->mode == MODE_HYBRID)
            ec_enc_bit_logp(&enc, redundancy, 12);
         if (redundancy)
         {
@@ -2225,7 +2209,12 @@
             int max_redundancy;
             ec_enc_bit_logp(&enc, celt_to_silk, 1);
             if (st->mode == MODE_HYBRID)
-               max_redundancy = (max_data_bytes-1)-nb_compr_bytes;
+            {
+               /* Reserve the 8 bits needed for the redundancy length,
+                  and at least a few bits for CELT if possible */
+               max_redundancy = (max_data_bytes-1)-((ec_tell(&enc)+8+3+7)>>3);
+               max_redundancy = IMIN(max_redundancy, redundancy_bytes);
+            }
             else
                max_redundancy = (max_data_bytes-1)-((ec_tell(&enc)+7)>>3);
             /* Target the same bit-rate for redundancy as for the rest,
@@ -2252,7 +2241,7 @@
         ec_enc_done(&enc);
         nb_compr_bytes = ret;
     } else {
-       nb_compr_bytes = IMIN((max_data_bytes-1)-redundancy_bytes, nb_compr_bytes);
+       nb_compr_bytes = (max_data_bytes-1)-redundancy_bytes;
        ec_enc_shrink(&enc, nb_compr_bytes);
     }
 
@@ -2299,7 +2288,7 @@
            celt_encode_with_ec(celt_enc, tmp_prefill, st->Fs/400, dummy, 2, NULL);
            celt_encoder_ctl(celt_enc, CELT_SET_PREDICTION(0));
         }
-        /* If false, we already busted the budget and we'll end up with a "PLC packet" */
+        /* If false, we already busted the budget and we'll end up with a "PLC frame" */
         if (ec_tell(&enc) <= 8*nb_compr_bytes)
         {
            /* Set the bitrate again if it was overridden in the redundancy code above*/
@@ -2413,7 +2402,6 @@
     if (!st->use_vbr)
     {
        if (opus_packet_pad(data, ret, max_data_bytes) != OPUS_OK)
-
        {
           RESTORE_STACK;
           return OPUS_INTERNAL_ERROR;
--- a/tests/opus_encode_regressions.c
+++ b/tests/opus_encode_regressions.c
@@ -951,6 +951,38 @@
     return 0;
 }
 
+static int ec_enc_shrink_assert2(void)
+{
+    OpusEncoder *enc;
+    int err;
+    int data_len;
+    unsigned char data[2000];
+
+    enc = opus_encoder_create(48000, 1, OPUS_APPLICATION_AUDIO, &err);
+    opus_encoder_ctl(enc, OPUS_SET_COMPLEXITY(6));
+    opus_encoder_ctl(enc, OPUS_SET_SIGNAL(OPUS_SIGNAL_VOICE));
+    opus_encoder_ctl(enc, OPUS_SET_BANDWIDTH(OPUS_BANDWIDTH_FULLBAND));
+    opus_encoder_ctl(enc, OPUS_SET_PACKET_LOSS_PERC(26));
+    opus_encoder_ctl(enc, OPUS_SET_BITRATE(27000));
+    {
+        static const short pcm[960] = { 0 };
+        data_len = opus_encode(enc, pcm, 960, data, 2000);
+        assert(data_len > 0);
+    }
+    opus_encoder_ctl(enc, OPUS_SET_SIGNAL(OPUS_SIGNAL_MUSIC));
+    {
+        static const short pcm[480] =
+        {
+            32767, 32767, 0, 0, 32767, 32767, 0, 0, 32767, 32767,
+            -32768, -32768, 0, 0, -32768, -32768, 0, 0, -32768, -32768
+        };
+        data_len = opus_encode(enc, pcm, 480, data, 19);
+        assert(data_len > 0);
+    }
+    opus_encoder_destroy(enc);
+    return 0;
+}
+
 void regression_test(void)
 {
    fprintf(stderr, "Running simple tests for bugs that have been fixed previously\n");
@@ -959,4 +991,5 @@
    mscbr_encode_fail();
    surround_analysis_uninit();
    ec_enc_shrink_assert();
+   ec_enc_shrink_assert2();
 }