Some refactoring for chaining
authorJean-Marc Valin <jmvalin@jmvalin.ca>
Tue, 2 May 2017 17:13:30 +0000 (13:13 -0400)
committerJean-Marc Valin <jmvalin@jmvalin.ca>
Tue, 2 May 2017 17:13:30 +0000 (13:13 -0400)
src/opusenc.c

index 6849c89..4b6d225 100644 (file)
@@ -74,6 +74,21 @@ struct StdioObject {
   FILE *file;
 };
 
+typedef struct EncStream EncStream;
+
+struct EncStream {
+  void *user_data;
+  ogg_stream_state os;
+  int serialno_is_set;
+  int serialno;
+  int stream_is_init;
+  int packetno;
+  char *comment;
+  int comment_length;
+  int seen_file_icons;
+  EncStream *next;
+};
+
 struct OggOpusEnc {
   OpusMSEncoder *st;
   int rate;
@@ -89,26 +104,18 @@ struct OggOpusEnc {
   ogg_int64_t end_granule;
   ogg_int64_t last_page_granule;
   OpusEncCallbacks callbacks;
-  void *user_data;
   OpusHeader header;
   int comment_padding;
-  char *comment;
-  int serialno_is_set;
-  int serialno;
-  int comment_length;
-  int seen_file_icons;
-  ogg_stream_state os;
-  int stream_is_init;
-  int packetno;
+  EncStream *streams;
 };
 
 static int oe_flush_page(OggOpusEnc *enc) {
   ogg_page og;
   int ret;
   int written = 0;
-  while ( (ret = ogg_stream_flush(&enc->os, &og)) ) {
+  while ( (ret = ogg_stream_flush(&enc->streams->os, &og)) ) {
     if (!ret) break;
-    ret = oe_write_page(&og, &enc->callbacks, enc->user_data);
+    ret = oe_write_page(&og, &enc->callbacks, enc->streams->user_data);
     if (ret == -1) {
       return -1;
     }
@@ -174,14 +181,17 @@ OggOpusEnc *ope_create_callbacks(const OpusEncCallbacks *callbacks, void *user_d
   }
 
   if ( (enc = malloc(sizeof(*enc))) == NULL) goto fail;
+  enc->streams = NULL;
+  if ( (enc->streams = malloc(sizeof(*enc->streams))) == NULL) goto fail;
+  enc->streams->next = NULL;
   enc->rate = rate;
   enc->channels = channels;
   enc->frame_size = 960;
   enc->decision_delay = 96000;
   enc->max_ogg_delay = 48000;
   enc->comment_padding = 512;
-  enc->serialno_is_set = 0;
-  enc->seen_file_icons = 0;
+  enc->streams->serialno_is_set = 0;
+  enc->streams->seen_file_icons = 0;
   enc->header.channels=channels;
   enc->header.channel_mapping=family;
   enc->header.input_sample_rate=rate;
@@ -200,8 +210,8 @@ OggOpusEnc *ope_create_callbacks(const OpusEncCallbacks *callbacks, void *user_d
     enc->re = NULL;
   }
   opus_multistream_encoder_ctl(st, OPUS_SET_EXPERT_FRAME_DURATION(OPUS_FRAMESIZE_20_MS));
-  enc->stream_is_init = 0;
-  enc->comment = NULL;
+  enc->streams->stream_is_init = 0;
+  enc->streams->comment = NULL;
   {
     opus_int32 tmp;
     int ret;
@@ -212,24 +222,28 @@ OggOpusEnc *ope_create_callbacks(const OpusEncCallbacks *callbacks, void *user_d
   enc->curr_granule = 0;
   enc->end_granule = 0;
   enc->last_page_granule = 0;
-  comment_init(&enc->comment, &enc->comment_length, opus_get_version_string());
+  comment_init(&enc->streams->comment, &enc->streams->comment_length, opus_get_version_string());
   {
     char encoder_string[1024];
     snprintf(encoder_string, sizeof(encoder_string), "%s version %s", PACKAGE_NAME, PACKAGE_VERSION);
-    comment_add(&enc->comment, &enc->comment_length, "ENCODER", encoder_string);
+    comment_add(&enc->streams->comment, &enc->streams->comment_length, "ENCODER", encoder_string);
   }
-  if (enc->comment == NULL) goto fail;
+  if (enc->streams->comment == NULL) goto fail;
   if ( (enc->buffer = malloc(sizeof(*enc->buffer)*BUFFER_SAMPLES*channels)) == NULL) goto fail;
   enc->buffer_start = enc->buffer_end = 0;
   enc->st = st;
   enc->callbacks = *callbacks;
-  enc->user_data = user_data;
+  enc->streams->user_data = user_data;
   if (error) *error = OPUS_OK;
   return enc;
 fail:
   if (enc) {
     free(enc);
     if (enc->buffer) free(enc->buffer);
+    if (enc->streams) {
+      free(enc->streams);
+      if (enc->streams->comment) free (enc->streams->comment);
+    }
   }
   if (st) {
     opus_multistream_encoder_destroy(st);
@@ -239,19 +253,19 @@ fail:
 
 static void init_stream(OggOpusEnc *enc) {
   time_t start_time;
-  assert(!enc->stream_is_init);
-  if (!enc->serialno_is_set) {
+  assert(!enc->streams->stream_is_init);
+  if (!enc->streams->serialno_is_set) {
     start_time = time(NULL);
     srand(((getpid()&65535)<<15)^start_time);
 
-    enc->serialno = rand();
+    enc->streams->serialno = rand();
   }
   
-  if (ogg_stream_init(&enc->os, enc->serialno) == -1) {
+  if (ogg_stream_init(&enc->streams->os, enc->streams->serialno) == -1) {
     assert(0);
     /* FIXME: How the hell do we handle that? */
   }
-  comment_pad(&enc->comment, &enc->comment_length, enc->comment_padding);
+  comment_pad(&enc->streams->comment, &enc->streams->comment_length, enc->comment_padding);
 
   /*Write header*/
   {
@@ -268,20 +282,20 @@ static void init_stream(OggOpusEnc *enc) {
     op.e_o_s=0;
     op.granulepos=0;
     op.packetno=0;
-    ogg_stream_packetin(&enc->os, &op);
+    ogg_stream_packetin(&enc->streams->os, &op);
     oe_flush_page(enc);
 
-    op.packet = (unsigned char *)enc->comment;
-    op.bytes = enc->comment_length;
+    op.packet = (unsigned char *)enc->streams->comment;
+    op.bytes = enc->streams->comment_length;
     op.b_o_s = 0;
     op.e_o_s = 0;
     op.granulepos = 0;
     op.packetno = 1;
-    ogg_stream_packetin(&enc->os, &op);
+    ogg_stream_packetin(&enc->streams->os, &op);
     oe_flush_page(enc);
   }
-  enc->stream_is_init = 1;
-  enc->packetno = 2;
+  enc->streams->stream_is_init = 1;
+  enc->streams->packetno = 2;
 }
 
 static void shift_buffer(OggOpusEnc *enc) {
@@ -307,24 +321,24 @@ static void encode_buffer(OggOpusEnc *enc) {
     op.packet=packet;
     op.bytes=nbBytes;
     op.b_o_s=0;
-    op.packetno=enc->packetno++;
+    op.packetno=enc->streams->packetno++;
     op.granulepos=enc->curr_granule;
     op.e_o_s=enc->curr_granule >= end_granule48k;
     if (op.e_o_s) op.granulepos=end_granule48k;
-    ogg_stream_packetin(&enc->os, &op);
+    ogg_stream_packetin(&enc->streams->os, &op);
     /* FIXME: Also flush on too many segments. */
     flush_needed = op.e_o_s || enc->curr_granule - enc->last_page_granule > enc->max_ogg_delay;
     if (flush_needed) {
-      while (ogg_stream_flush_fill(&enc->os, &og, 255*255)) {
+      while (ogg_stream_flush_fill(&enc->streams->os, &og, 255*255)) {
         if (ogg_page_packets(&og) != 0) enc->last_page_granule = ogg_page_granulepos(&og);
-        int ret = oe_write_page(&og, &enc->callbacks, enc->user_data);
+        int ret = oe_write_page(&og, &enc->callbacks, enc->streams->user_data);
         /* FIXME: what do we do if this fails? */
         assert(ret != -1);
       }
     } else {
-      while (ogg_stream_pageout_fill(&enc->os, &og, 255*255)) {
+      while (ogg_stream_pageout_fill(&enc->streams->os, &og, 255*255)) {
         if (ogg_page_packets(&og) != 0) enc->last_page_granule = ogg_page_granulepos(&og);
-        int ret = oe_write_page(&og, &enc->callbacks, enc->user_data);
+        int ret = oe_write_page(&og, &enc->callbacks, enc->streams->user_data);
         /* FIXME: what do we do if this fails? */
         assert(ret != -1);
       }
@@ -343,7 +357,7 @@ static void encode_buffer(OggOpusEnc *enc) {
 /* Add/encode any number of float samples to the file. */
 int ope_write_float(OggOpusEnc *enc, const float *pcm, int samples_per_channel) {
   int channels = enc->channels;
-  if (!enc->stream_is_init) init_stream(enc);
+  if (!enc->streams->stream_is_init) init_stream(enc);
   if (samples_per_channel < 0) return OPE_BAD_ARG;
   enc->end_granule += samples_per_channel;
   do {
@@ -374,7 +388,7 @@ int ope_write_float(OggOpusEnc *enc, const float *pcm, int samples_per_channel)
 /* Add/encode any number of int16 samples to the file. */
 int ope_write(OggOpusEnc *enc, const opus_int16 *pcm, int samples_per_channel) {
   int channels = enc->channels;
-  if (!enc->stream_is_init) init_stream(enc);
+  if (!enc->streams->stream_is_init) init_stream(enc);
   if (samples_per_channel < 0) return OPE_BAD_ARG;
   enc->end_granule += samples_per_channel;
   do {
@@ -407,7 +421,7 @@ int ope_write(OggOpusEnc *enc, const opus_int16 *pcm, int samples_per_channel) {
 static void finalize_stream(OggOpusEnc *enc) {
   /* FIXME: Use a better value. */
   int pad_samples = 3000;
-  if (!enc->stream_is_init) init_stream(enc);
+  if (!enc->streams->stream_is_init) init_stream(enc);
   shift_buffer(enc);
   /* FIXME: Do LPC extension instead. */
   memset(&enc->buffer[enc->channels*enc->buffer_end], 0, pad_samples*enc->channels);
@@ -420,11 +434,11 @@ static void finalize_stream(OggOpusEnc *enc) {
 /* Close/finalize the stream. */
 int ope_close_and_free(OggOpusEnc *enc) {
   finalize_stream(enc);
-  enc->callbacks.close(enc->user_data);
-  free(enc->comment);
+  enc->callbacks.close(enc->streams->user_data);
+  free(enc->streams->comment);
   free(enc->buffer);
   opus_multistream_encoder_destroy(enc->st);
-  if (enc->stream_is_init) ogg_stream_clear(&enc->os);
+  if (enc->streams->stream_is_init) ogg_stream_clear(&enc->streams->os);
   if (enc->re) speex_resampler_destroy(enc->re);
   free(enc);
   return OPE_OK;
@@ -432,8 +446,10 @@ int ope_close_and_free(OggOpusEnc *enc) {
 
 /* Ends the stream and create a new stream within the same file. */
 int ope_chain_current(OggOpusEnc *enc) {
+  EncStream *stream = enc->streams;
+  while (stream->next) stream = stream->next;
   /* FIXME: Make sure we don't end up calling the close callback too early. */
-  return ope_continue_new_callbacks(enc, enc->user_data);
+  return ope_continue_new_callbacks(enc, stream->user_data);
 }
 
 /* Ends the stream and create a new file. */
@@ -452,36 +468,35 @@ int ope_continue_new_callbacks(OggOpusEnc *enc, void *user_data) {
 
 /* Add a comment to the file (can only be called before encoding samples). */
 int ope_add_comment(OggOpusEnc *enc, const char *tag, const char *val) {
-  if (enc->stream_is_init) return OPE_TOO_LATE;
-  if (comment_add(&enc->comment, &enc->comment_length, tag, val)) return OPE_INTERNAL_ERROR;
+  if (enc->streams->stream_is_init) return OPE_TOO_LATE;
+  if (comment_add(&enc->streams->comment, &enc->streams->comment_length, tag, val)) return OPE_INTERNAL_ERROR;
   return OPE_OK;
 }
 
 int ope_add_picture(OggOpusEnc *enc, const char *spec) {
   const char *error_message;
   char *picture_data;
-  if (enc->stream_is_init) return OPE_TOO_LATE;
-  picture_data = parse_picture_specification(spec, &error_message, &enc->seen_file_icons);
+  if (enc->streams->stream_is_init) return OPE_TOO_LATE;
+  picture_data = parse_picture_specification(spec, &error_message, &enc->streams->seen_file_icons);
   if(picture_data==NULL){
     /* FIXME: return proper errors rather than printing a message. */
     fprintf(stderr,"Error parsing picture option: %s\n",error_message);
     return OPE_BAD_ARG;
   }
-  comment_add(&enc->comment, &enc->comment_length, "METADATA_BLOCK_PICTURE", picture_data);
+  comment_add(&enc->streams->comment, &enc->streams->comment_length, "METADATA_BLOCK_PICTURE", picture_data);
   free(picture_data);
   return OPE_OK;
 }
 
 /* Sets the Opus comment vendor string (optional, defaults to library info). */
 int ope_set_vendor_string(OggOpusEnc *enc, const char *vendor) {
-  if (enc->stream_is_init) return OPE_TOO_LATE;
-  (void)enc;
+  if (enc->streams->stream_is_init) return OPE_TOO_LATE;
   (void)vendor;
   return OPE_UNIMPLEMENTED;
 }
 
 int ope_flush_header(OggOpusEnc *enc) {
-  if (enc->stream_is_init) return OPE_TOO_LATE;
+  if (enc->streams->stream_is_init) return OPE_TOO_LATE;
   else init_stream(enc);
   return OPE_OK;
 }
@@ -583,8 +598,8 @@ int ope_set_params(OggOpusEnc *enc, int request, ...) {
     case OPE_SET_SERIALNO_REQUEST:
     {
       opus_int32 value = va_arg(ap, opus_int32);
-      enc->serialno = value;
-      enc->serialno_is_set = 1;
+      enc->streams->serialno = value;
+      enc->streams->serialno_is_set = 1;
       ret = OPE_OK;
     }
     break;