Add ope_encoder_deferred_init_with_mapping()
authorJean-Marc Valin <jmvalin@jmvalin.ca>
Sat, 3 Feb 2018 00:42:14 +0000 (19:42 -0500)
committerJean-Marc Valin <jmvalin@jmvalin.ca>
Sat, 3 Feb 2018 00:42:14 +0000 (19:42 -0500)
Makes it possible to manually specify a channel mapping

include/opusenc.h
src/opusenc.c

index ca1a5ef..998aefc 100644 (file)
@@ -264,6 +264,17 @@ OPE_EXPORT OggOpusEnc *ope_encoder_create_callbacks(const OpusEncCallbacks *call
     */
 OPE_EXPORT OggOpusEnc *ope_encoder_create_pull(OggOpusComments *comments, opus_int32 rate, int channels, int family, int *error);
 
+/** Deferred initialization of the encoder to force an explicit channel mapping.
+    \param[in,out] enc         Encoder
+    \param family              Mapping family (0 for mono/stereo, 1 for surround)
+    \param streams             Total number of streams
+    \param coupled_streams     Number of coupled streams
+    \param mapping             Channel mapping
+    \return Error code
+ */
+OPE_EXPORT int ope_encoder_deferred_init_with_mapping(OggOpusEnc *enc, int family, int streams, 
+    int coupled_streams, const unsigned char *mapping);
+
 /** Add/encode any number of float samples to the stream.
     \param[in,out] enc         Encoder
     \param pcm                 Floating-point PCM values in the +/-1 range (interleaved if multiple channels)
index bc4edde..27b8e14 100644 (file)
@@ -283,7 +283,7 @@ OggOpusEnc *ope_encoder_create_callbacks(const OpusEncCallbacks *callbacks, void
   OpusMSEncoder *st=NULL;
   OggOpusEnc *enc=NULL;
   int ret;
-  if (family != 0 && family != 1 && family != 255) {
+  if (family != 0 && family != 1 && family != 255 && family != -1) {
     if (error) *error = OPE_UNIMPLEMENTED;
     return NULL;
   }
@@ -302,7 +302,8 @@ OggOpusEnc *ope_encoder_create_callbacks(const OpusEncCallbacks *callbacks, void
   if ( (enc->streams = stream_create(comments)) == NULL) goto fail;
   enc->last_stream = enc->streams;
   enc->oggp = NULL;
-  enc->unrecoverable = 0;
+  /* Not initializing anything is an unrecoverable error. */
+  enc->unrecoverable = family == -1;
   enc->pull_api = 0;
   enc->packet_callback = NULL;
   enc->rate = rate;
@@ -317,11 +318,15 @@ OggOpusEnc *ope_encoder_create_callbacks(const OpusEncCallbacks *callbacks, void
   enc->header.channel_mapping=family;
   enc->header.input_sample_rate=rate;
   enc->header.gain=0;
-  st=opus_multistream_surround_encoder_create(48000, channels, enc->header.channel_mapping,
-      &enc->header.nb_streams, &enc->header.nb_coupled,
-      enc->header.stream_map, OPUS_APPLICATION_AUDIO, &ret);
-  if (! (ret == OPUS_OK && st != NULL) ) {
-    goto fail;
+  if (family != -1) {
+    st=opus_multistream_surround_encoder_create(48000, channels, enc->header.channel_mapping,
+        &enc->header.nb_streams, &enc->header.nb_coupled,
+        enc->header.stream_map, OPUS_APPLICATION_AUDIO, &ret);
+    if (! (ret == OPUS_OK && st != NULL) ) {
+      goto fail;
+    }
+    enc->st = st;
+    opus_multistream_encoder_ctl(st, OPUS_SET_EXPERT_FRAME_DURATION(OPUS_FRAMESIZE_20_MS));
   }
   if (rate != 48000) {
     enc->re = speex_resampler_init(channels, rate, 48000, 5, NULL);
@@ -330,7 +335,6 @@ OggOpusEnc *ope_encoder_create_callbacks(const OpusEncCallbacks *callbacks, void
   } else {
     enc->re = NULL;
   }
-  opus_multistream_encoder_ctl(st, OPUS_SET_EXPERT_FRAME_DURATION(OPUS_FRAMESIZE_20_MS));
   enc->global_granule_offset = -1;
   enc->curr_granule = 0;
   enc->write_granule = 0;
@@ -343,7 +347,6 @@ OggOpusEnc *ope_encoder_create_callbacks(const OpusEncCallbacks *callbacks, void
     memset(enc->lpc_buffer, 0, sizeof(*enc->lpc_buffer)*LPC_INPUT*channels);
   }
   enc->buffer_start = enc->buffer_end = 0;
-  enc->st = st;
   if (callbacks != NULL)
   {
     enc->callbacks = *callbacks;
@@ -372,6 +375,32 @@ OggOpusEnc *ope_encoder_create_pull(OggOpusComments *comments, opus_int32 rate,
   return enc;
 }
 
+int ope_encoder_deferred_init_with_mapping(OggOpusEnc *enc, int family, int streams, 
+    int coupled_streams, const unsigned char *mapping) {
+  int ret;
+  int i;
+  OpusMSEncoder *st;
+  if (enc->st!=NULL) {
+    return OPE_TOO_LATE;
+  }
+  if (streams <= 0 || streams>255 || coupled_streams<0 || coupled_streams >= 128 || streams+coupled_streams > 255) return OPE_BAD_ARG;
+  st=opus_multistream_encoder_create(48000, enc->channels, streams, coupled_streams, mapping, OPUS_APPLICATION_AUDIO, &ret);
+  if (! (ret == OPUS_OK && st != NULL) ) {
+    goto fail;
+  }
+  enc->st = st;
+  opus_multistream_encoder_ctl(st, OPUS_SET_EXPERT_FRAME_DURATION(OPUS_FRAMESIZE_20_MS));
+  enc->unrecoverable = 0;
+  enc->header.channel_mapping=family;
+  enc->header.nb_streams = streams;
+  enc->header.nb_coupled = coupled_streams;
+  for (i=0;i<streams+coupled_streams;i++)
+    enc->header.stream_map[i] = mapping[i];
+  return OPE_OK;
+fail:
+  return OPE_ALLOC_FAIL;
+}
+
 static void init_stream(OggOpusEnc *enc) {
   assert(!enc->streams->stream_is_init);
   if (!enc->streams->serialno_is_set) {
@@ -699,7 +728,7 @@ void ope_encoder_destroy(OggOpusEnc *enc) {
   if (enc->chaining_keyframe) free(enc->chaining_keyframe);
   free(enc->buffer);
   if (enc->oggp) oggp_destroy(enc->oggp);
-  opus_multistream_encoder_destroy(enc->st);
+  if (enc->st) opus_multistream_encoder_destroy(enc->st);
   if (enc->re) speex_resampler_destroy(enc->re);
   if (enc->lpc_buffer) free(enc->lpc_buffer);
   free(enc);