Multistream support for variable frame duration
authorJean-Marc Valin <jmvalin@jmvalin.ca>
Mon, 17 Dec 2012 21:23:42 +0000 (16:23 -0500)
committerJean-Marc Valin <jmvalin@jmvalin.ca>
Mon, 11 Feb 2013 04:53:43 +0000 (23:53 -0500)
Also fixes a bug with stereo streams where the initial memory was only
using the left channel.

src/opus_encoder.c
src/opus_multistream_encoder.c
src/opus_private.h

index c4a4655..4c0840f 100644 (file)
@@ -665,19 +665,21 @@ static int transient_viterbi(const float *E, const float *E_1, int N, int frame_
    return best_state;
 }
 
-static int optimize_framesize(const opus_val16 *x, int len, int C, opus_int32 Fs,
+int optimize_framesize(const opus_val16 *x, int len, int C, opus_int32 Fs,
                 int bitrate, opus_val16 tonality, opus_val32 *mem, int buffering)
 {
    int N;
-   int i;
+   int i, c;
    float e[MAX_DYNAMIC_FRAMESIZE+4];
    float e_1[MAX_DYNAMIC_FRAMESIZE+3];
    float memx;
    int bestLM=0;
    int subframe;
    int pos;
+   VARDECL(opus_val16, sub);
 
    subframe = Fs/400;
+   ALLOC(sub, subframe, opus_val16);
    e[0]=mem[0];
    e_1[0]=1./(EPSILON+mem[0]);
    if (buffering)
@@ -698,27 +700,26 @@ static int optimize_framesize(const opus_val16 *x, int len, int C, opus_int32 Fs
    }
    N=IMIN(len/subframe, MAX_DYNAMIC_FRAMESIZE);
    memx = x[0];
+   for (c=1;c<C;c++)
+      memx += x[c];
    for (i=0;i<N;i++)
    {
       float tmp;
       float tmpx;
       int j;
       tmp=EPSILON;
-      if (C==1)
-      {
-         for (j=0;j<subframe;j++)
-         {
-            tmpx = x[subframe*i+j];
-            tmp += (tmpx-memx)*(tmpx-memx);
-            memx = tmpx;
-         }
-      } else {
+
+      for (j=0;j<subframe;j++)
+         sub[j] = x[(subframe*i+j)*C];
+      for (c=1;c<C;c++)
          for (j=0;j<subframe;j++)
-         {
-            tmpx = x[(subframe*i+j)*2]+x[(subframe*i+j)*2+1];
-            tmp += (tmpx-memx)*(tmpx-memx);
-            memx = tmpx;
-         }
+            sub[j] += x[(subframe*i+j)*C+c];
+
+      for (j=0;j<subframe;j++)
+      {
+         tmpx = sub[j];
+         tmp += (tmpx-memx)*(tmpx-memx);
+         memx = tmpx;
       }
       e[i+pos] = tmp;
       e_1[i+pos] = 1.f/tmp;
@@ -1425,7 +1426,7 @@ opus_int32 opus_encode_native(OpusEncoder *st, const opus_val16 *pcm, int frame_
             {
                 opus_int32 bonus=0;
 #ifndef FIXED_POINT
-                if (orig_frame_size != frame_size)
+                if (st->variable_duration && orig_frame_size != frame_size)
                 {
                    bonus = (40*st->stream_channels+40)*(48000/frame_size-48000/orig_frame_size);
                    if (analysis_info.valid)
index db9fc78..d048f53 100644 (file)
@@ -40,6 +40,9 @@
 struct OpusMSEncoder {
    ChannelLayout layout;
    int bitrate;
+   int variable_duration;
+   opus_int32 bitrate_bps;
+   opus_val32 subframe_mem[3];
    /* Encoder states go here */
 };
 
@@ -193,10 +196,38 @@ static int opus_multistream_encode_native
    VARDECL(opus_val16, buf);
    unsigned char tmp_data[MS_FRAME_TMP];
    OpusRepacketizer rp;
+   int orig_frame_size;
+   int coded_channels;
+   opus_int32 channel_rate;
    ALLOC_STACK;
 
    ptr = (char*)st + align(sizeof(OpusMSEncoder));
    opus_encoder_ctl((OpusEncoder*)ptr, OPUS_GET_SAMPLE_RATE(&Fs));
+
+   if (400*frame_size < Fs)
+   {
+      RESTORE_STACK;
+      return OPUS_BAD_ARG;
+   }
+   orig_frame_size = IMIN(frame_size,Fs/50);
+   if (st->variable_duration)
+   {
+      int LM = 3;
+      int channels;
+      opus_int32 delay_compensation;
+
+      channels = st->layout.nb_streams + st->layout.nb_coupled_streams;
+      opus_encoder_ctl((OpusEncoder*)ptr, OPUS_GET_LOOKAHEAD(&delay_compensation));
+      delay_compensation -= Fs/400;
+#ifndef FIXED_POINT
+      LM = optimize_framesize(pcm, frame_size, channels, Fs, st->bitrate_bps,
+            0.f, st->subframe_mem, delay_compensation);
+#endif
+      while ((Fs/400<<LM)>frame_size)
+         LM--;
+      frame_size = (Fs/400<<LM);
+   }
+
    /* Validate frame_size before using it to allocate stack space.
       This mirrors the checks in opus_encode[_float](). */
    if (400*frame_size != Fs && 200*frame_size != Fs &&
@@ -215,6 +246,31 @@ static int opus_multistream_encode_native
       RESTORE_STACK;
       return OPUS_BUFFER_TOO_SMALL;
    }
+
+   /* Compute bitrate allocation between streams (this could be a lot better) */
+   coded_channels = st->layout.nb_streams + st->layout.nb_coupled_streams;
+   channel_rate = st->bitrate_bps / coded_channels;
+#ifndef FIXED_POINT
+   if (st->variable_duration && orig_frame_size != frame_size)
+   {
+      opus_int32 bonus;
+      bonus = 60*(48000/frame_size-48000/orig_frame_size);
+      channel_rate += bonus;
+   }
+#endif
+   ptr = (char*)st + align(sizeof(OpusMSEncoder));
+   for (s=0;s<st->layout.nb_streams;s++)
+   {
+      OpusEncoder *enc;
+      enc = (OpusEncoder*)ptr;
+      if (s < st->layout.nb_coupled_streams)
+         ptr += align(coupled_size);
+      else
+         ptr += align(mono_size);
+      opus_encoder_ctl(enc, OPUS_SET_BITRATE(channel_rate * (s < st->layout.nb_coupled_streams ? 2 : 1)));
+   }
+
+   ptr = (char*)st + align(sizeof(OpusMSEncoder));
    /* Counting ToC */
    tot_size = 0;
    for (s=0;s<st->layout.nb_streams;s++)
@@ -378,20 +434,8 @@ int opus_multistream_encoder_ctl(OpusMSEncoder *st, int request, ...)
    {
    case OPUS_SET_BITRATE_REQUEST:
    {
-      int chan, s;
       opus_int32 value = va_arg(ap, opus_int32);
-      chan = st->layout.nb_streams + st->layout.nb_coupled_streams;
-      value /= chan;
-      for (s=0;s<st->layout.nb_streams;s++)
-      {
-         OpusEncoder *enc;
-         enc = (OpusEncoder*)ptr;
-         if (s < st->layout.nb_coupled_streams)
-            ptr += align(coupled_size);
-         else
-            ptr += align(mono_size);
-         opus_encoder_ctl(enc, request, value * (s < st->layout.nb_coupled_streams ? 2 : 1));
-      }
+      st->bitrate_bps = value;
    }
    break;
    case OPUS_GET_BITRATE_REQUEST:
@@ -504,7 +548,21 @@ int opus_multistream_encoder_ctl(OpusMSEncoder *st, int request, ...)
       }
       *value = (OpusEncoder*)ptr;
    }
-      break;
+   break;
+   case OPUS_SET_EXPERT_VARIABLE_DURATION_REQUEST:
+   {
+       opus_int32 value = va_arg(ap, opus_int32);
+       if (value<0 || value>1)
+          goto bad_arg;
+       st->variable_duration = value;
+   }
+   break;
+   case OPUS_GET_EXPERT_VARIABLE_DURATION_REQUEST:
+   {
+       opus_int32 *value = va_arg(ap, opus_int32*);
+       *value = st->variable_duration;
+   }
+   break;
    default:
       ret = OPUS_UNIMPLEMENTED;
       break;
@@ -512,6 +570,9 @@ int opus_multistream_encoder_ctl(OpusMSEncoder *st, int request, ...)
 
    va_end(ap);
    return ret;
+bad_arg:
+   va_end(ap);
+   return OPUS_BAD_ARG;
 }
 
 void opus_multistream_encoder_destroy(OpusMSEncoder *st)
index 977f4a2..94de003 100644 (file)
@@ -81,6 +81,8 @@ int get_mono_channel(const ChannelLayout *layout, int stream_id, int prev);
 #define OPUS_SET_FORCE_MODE_REQUEST    11002
 #define OPUS_SET_FORCE_MODE(x) OPUS_SET_FORCE_MODE_REQUEST, __opus_check_int(x)
 
+int optimize_framesize(const opus_val16 *x, int len, int C, opus_int32 Fs,
+                int bitrate, opus_val16 tonality, opus_val32 *mem, int buffering);
 
 int encode_size(int size, unsigned char *data);