Generalize the C-language Opus interface.

Switch to explicit channel mappings (RFC 7845) when creating
multi-stream Opus en/de-coders. The responsibility of setting up the
channel mappings will shift from WebRTC to the WebRTC user.

See https://webrtc-review.googlesource.com/c/src/+/121764 for the
current vision. See also the first child CL
https://webrtc-review.googlesource.com/c/src/+/129768
that sets up the Decoder to use this code.

Bug: webrtc:8649
Change-Id: I55959a293d54bb4c982eff68ec107c5ef8666c5c
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/129767
Commit-Queue: Alex Loiko <aleloi@webrtc.org>
Reviewed-by: Oskar Sundbom <ossu@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#27452}
diff --git a/modules/audio_coding/codecs/opus/opus_interface.c b/modules/audio_coding/codecs/opus/opus_interface.c
index 3369f8e..f6e053a 100644
--- a/modules/audio_coding/codecs/opus/opus_interface.c
+++ b/modules/audio_coding/codecs/opus/opus_interface.c
@@ -37,40 +37,6 @@
   kWebRtcOpusDefaultFrameSize = 960,
 };
 
-int16_t GetSurroundParameters(int channels,
-                              int *streams,
-                              int *coupled_streams,
-                              unsigned char *mapping) {
-  int opus_error;
-  int ret = 0;
-  // Use 'surround encoder create' to get values for 'coupled_streams',
-  // 'streams' and 'mapping'.
-  OpusMSEncoder* ms_encoder_ptr = opus_multistream_surround_encoder_create(
-      48000,
-      channels,
-      /* mapping family */ channels <= 2 ? 0 : 1,
-      streams,
-      coupled_streams,
-      mapping,
-      OPUS_APPLICATION_VOIP, // Application type shouldn't affect
-                             // streams/mapping values.
-      &opus_error);
-
-  // This shouldn't fail; if it fails,
-  // signal an error and return invalid values.
-  if (opus_error != OPUS_OK || ms_encoder_ptr == NULL) {
-    ret = -1;
-    *streams = -1;
-    *coupled_streams = -1;
-  }
-
-  // We don't need the encoder.
-  if (ms_encoder_ptr != NULL) {
-    opus_multistream_encoder_destroy(ms_encoder_ptr);
-  }
-  return ret;
-}
-
 int16_t WebRtcOpus_EncoderCreate(OpusEncInst** inst,
                                  size_t channels,
                                  int32_t application) {
@@ -93,30 +59,60 @@
   RTC_DCHECK(state);
 
   int error;
-  if (channels <= 2) {
-    state->encoder.encoder = opus_encoder_create(48000, (int)channels, opus_app,
-                                         &error);
+  state->encoder = opus_encoder_create(48000, (int)channels, opus_app,
+                                       &error);
 
-  } else {
-    unsigned char mapping[255];
-    memset(mapping, 0, 255);
-    int streams = -1;
-    int coupled_streams = -1;
-
-    state->encoder.multistream_encoder =
-        opus_multistream_surround_encoder_create(
-        48000,
-        channels,
-        /* mapping family */ 1,
-        &streams,
-        &coupled_streams,
-        mapping,
-        opus_app,
-        &error);
+  if (error != OPUS_OK || (!state->encoder &&
+                           !state->multistream_encoder)) {
+    WebRtcOpus_EncoderFree(state);
+    return -1;
   }
 
-  if (error != OPUS_OK || (!state->encoder.encoder &&
-                           !state->encoder.multistream_encoder)) {
+  state->in_dtx_mode = 0;
+  state->channels = channels;
+
+  *inst = state;
+  return 0;
+}
+
+int16_t WebRtcOpus_MultistreamEncoderCreate(
+    OpusEncInst** inst,
+    size_t channels,
+    int32_t application,
+    size_t coupled_streams,
+    const unsigned char *channel_mapping) {
+  int opus_app;
+  if (!inst)
+    return -1;
+
+  switch (application) {
+    case 0:
+      opus_app = OPUS_APPLICATION_VOIP;
+      break;
+    case 1:
+      opus_app = OPUS_APPLICATION_AUDIO;
+      break;
+    default:
+      return -1;
+  }
+
+  OpusEncInst* state = (OpusEncInst*)calloc(1, sizeof(OpusEncInst));
+  RTC_DCHECK(state);
+
+  int streams = channels - coupled_streams;
+  int error;
+  state->multistream_encoder =
+      opus_multistream_encoder_create(
+          48000,
+          channels,
+          streams,
+          coupled_streams,
+          channel_mapping,
+          opus_app,
+          &error);
+
+  if (error != OPUS_OK || (!state->encoder &&
+                           !state->multistream_encoder)) {
     WebRtcOpus_EncoderFree(state);
     return -1;
   }
@@ -130,10 +126,10 @@
 
 int16_t WebRtcOpus_EncoderFree(OpusEncInst* inst) {
   if (inst) {
-    if (inst->channels <= 2) {
-      opus_encoder_destroy(inst->encoder.encoder);
+    if (inst->encoder) {
+      opus_encoder_destroy(inst->encoder);
     } else {
-      opus_multistream_encoder_destroy(inst->encoder.multistream_encoder);
+      opus_multistream_encoder_destroy(inst->multistream_encoder);
     }
     free(inst);
     return 0;
@@ -153,14 +149,14 @@
     return -1;
   }
 
-  if (inst->channels <= 2) {
-    res = opus_encode(inst->encoder.encoder,
+  if (inst->encoder) {
+    res = opus_encode(inst->encoder,
                       (const opus_int16*)audio_in,
                       (int)samples,
                       encoded,
                       (opus_int32)length_encoded_buffer);
   } else {
-    res = opus_multistream_encode(inst->encoder.multistream_encoder,
+    res = opus_multistream_encode(inst->multistream_encoder,
                                   (const opus_int16*)audio_in,
                                   (int)samples,
                                   encoded,
@@ -187,10 +183,10 @@
   return res;
 }
 
-#define ENCODER_CTL(inst, vargs) (                                      \
-    inst->channels <= 2 ?                                               \
-    opus_encoder_ctl(inst->encoder.encoder, vargs)                      \
-    : opus_multistream_encoder_ctl(inst->encoder.multistream_encoder, vargs))
+#define ENCODER_CTL(inst, vargs) (                                   \
+    inst->encoder ?                                                  \
+    opus_encoder_ctl(inst->encoder, vargs)                           \
+    : opus_multistream_encoder_ctl(inst->multistream_encoder, vargs))
 
 
 int16_t WebRtcOpus_SetBitRate(OpusEncInst* inst, int32_t rate) {
@@ -231,9 +227,9 @@
 
 int16_t WebRtcOpus_GetMaxPlaybackRate(OpusEncInst* const inst,
                                       int32_t* result_hz) {
-  if (inst->channels <= 2) {
+  if (inst->encoder) {
     if (opus_encoder_ctl(
-            inst->encoder.encoder,
+            inst->encoder,
             OPUS_GET_MAX_BANDWIDTH(result_hz)) == OPUS_OK) {
       return 0;
     }
@@ -388,30 +384,10 @@
       return -1;
     }
 
-    if (channels <= 2) {
-      state->decoder.decoder = opus_decoder_create(48000,
-                                                   (int)channels, &error);
-    } else {
-      unsigned char mapping[255];
-      memset(mapping, 0, 255);
-      int streams = -1;
-      int coupled_streams = -1;
-      if (GetSurroundParameters(channels, &streams,
-                                &coupled_streams, mapping) != 0) {
-        free(state);
-        return -1;
-      }
-
-      // Create new memory, always at 48000 Hz.
-      state->decoder.multistream_decoder = opus_multistream_decoder_create(
-          48000, (int)channels,
-          /* streams = */ streams,
-          /* coupled streams = */ coupled_streams,
-          mapping,
-          &error);
-    }
-    if (error == OPUS_OK && (state->decoder.decoder ||
-                             state->decoder.multistream_decoder)) {
+    // Create new memory, always at 48000 Hz.
+    state->decoder = opus_decoder_create(48000,
+                                                 (int)channels, &error);
+    if (error == OPUS_OK && state->decoder) {
       // Creation of memory all ok.
       state->channels = channels;
       state->prev_decoded_samples = kWebRtcOpusDefaultFrameSize;
@@ -421,23 +397,60 @@
     }
 
     // If memory allocation was unsuccessful, free the entire state.
-    if (state->decoder.decoder) {
-      opus_decoder_destroy(state->decoder.decoder);
-
-    } else if (state->decoder.multistream_decoder) {
-      opus_multistream_decoder_destroy(state->decoder.multistream_decoder);
+    if (state->decoder) {
+      opus_decoder_destroy(state->decoder);
     }
     free(state);
   }
   return -1;
 }
 
+int16_t WebRtcOpus_MultistreamDecoderCreate(
+    OpusDecInst** inst, size_t channels,
+    size_t coupled_streams,
+    const unsigned char* channel_mapping) {
+  int error;
+  OpusDecInst* state;
+
+  if (inst != NULL) {
+    // Create Opus decoder state.
+    state = (OpusDecInst*) calloc(1, sizeof(OpusDecInst));
+    if (state == NULL) {
+      return -1;
+    }
+
+    int streams = channels - coupled_streams;
+
+    // Create new memory, always at 48000 Hz.
+    state->multistream_decoder = opus_multistream_decoder_create(
+        48000, (int)channels,
+        streams,
+        coupled_streams,
+        channel_mapping,
+        &error);
+
+    if (error == OPUS_OK && state->multistream_decoder) {
+      // Creation of memory all ok.
+      state->channels = channels;
+      state->prev_decoded_samples = kWebRtcOpusDefaultFrameSize;
+      state->in_dtx_mode = 0;
+      *inst = state;
+      return 0;
+    }
+
+    // If memory allocation was unsuccessful, free the entire state.
+    opus_multistream_decoder_destroy(state->multistream_decoder);
+    free(state);
+  }
+  return -1;
+}
+
 int16_t WebRtcOpus_DecoderFree(OpusDecInst* inst) {
   if (inst) {
-    if (inst->channels <= 2) {
-      opus_decoder_destroy(inst->decoder.decoder);
-    } else if (inst->channels > 2) {
-      opus_multistream_decoder_destroy(inst->decoder.multistream_decoder);
+    if (inst->decoder) {
+      opus_decoder_destroy(inst->decoder);
+    } else if (inst->multistream_decoder) {
+      opus_multistream_decoder_destroy(inst->multistream_decoder);
     }
     free(inst);
     return 0;
@@ -451,10 +464,10 @@
 }
 
 void WebRtcOpus_DecoderInit(OpusDecInst* inst) {
-  if (inst->channels <= 2) {
-    opus_decoder_ctl(inst->decoder.decoder, OPUS_RESET_STATE);
+  if (inst->decoder) {
+    opus_decoder_ctl(inst->decoder, OPUS_RESET_STATE);
   } else {
-    opus_multistream_decoder_ctl(inst->decoder.multistream_decoder,
+    opus_multistream_decoder_ctl(inst->multistream_decoder,
                                  OPUS_RESET_STATE);
   }
   inst->in_dtx_mode = 0;
@@ -490,12 +503,12 @@
                         size_t encoded_bytes, int frame_size,
                         int16_t* decoded, int16_t* audio_type, int decode_fec) {
   int res = -1;
-  if (inst->channels <= 2) {
-    res = opus_decode(inst->decoder.decoder, encoded, (opus_int32)encoded_bytes,
+  if (inst->decoder) {
+    res = opus_decode(inst->decoder, encoded, (opus_int32)encoded_bytes,
                       (opus_int16*)decoded, frame_size, decode_fec);
   } else {
     res = opus_multistream_decode(
-        inst->decoder.multistream_decoder, encoded, (opus_int32)encoded_bytes,
+        inst->multistream_decoder, encoded, (opus_int32)encoded_bytes,
         (opus_int16*)decoded, frame_size, decode_fec);
   }