some refactoring
[libopusenc.git] / src / opusenc.c
1 /* Copyright (C)2002-2017 Jean-Marc Valin
2    Copyright (C)2007-2013 Xiph.Org Foundation
3    Copyright (C)2008-2013 Gregory Maxwell
4    File: opusenc.c
5
6    Redistribution and use in source and binary forms, with or without
7    modification, are permitted provided that the following conditions
8    are met:
9
10    - Redistributions of source code must retain the above copyright
11    notice, this list of conditions and the following disclaimer.
12
13    - Redistributions in binary form must reproduce the above copyright
14    notice, this list of conditions and the following disclaimer in the
15    documentation and/or other materials provided with the distribution.
16
17    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18    ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20    A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21    OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22    EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23    PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24    PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25    LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26    NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #ifdef HAVE_CONFIG_H
31 #include "config.h"
32 #endif
33
34 #include <stdarg.h>
35 #include <time.h>
36 #include <unistd.h>
37 #include <stdlib.h>
38 #include <stdio.h>
39 #include <string.h>
40 #include <assert.h>
41 #include <opus_multistream.h>
42 #include "opusenc.h"
43 #include "opus_header.h"
44 #include "speex_resampler.h"
45 #include "picture.h"
46
47 #define MAX_CHANNELS 8
48
49 #define LPC_PADDING 120
50
51 /* Allow up to 2 seconds for delayed decision. */
52 #define MAX_LOOKAHEAD 96000
53 /* We can't have a circular buffer (because of delayed decision), so let's not copy too often. */
54 #define BUFFER_EXTRA 24000
55
56 #define BUFFER_SAMPLES (MAX_LOOKAHEAD + BUFFER_EXTRA)
57
58 #define MIN(a,b) ((a) < (b) ? (a) : (b))
59 #define MAX(a,b) ((a) > (b) ? (a) : (b))
60
61 #define MAX_PACKET_SIZE (1276*8)
62
63 static int oe_write_page(ogg_page *page, OpusEncCallbacks *cb, void *user_data)
64 {
65    int err;
66    err = cb->write(user_data, page->header, page->header_len);
67    if (err) return -1;
68    err = cb->write(user_data, page->body, page->body_len);
69    if (err) return -1;
70    return page->header_len+page->body_len;
71 }
72
73 struct StdioObject {
74   FILE *file;
75 };
76
77 typedef struct EncStream EncStream;
78
79 struct EncStream {
80   void *user_data;
81   ogg_stream_state os;
82   int serialno_is_set;
83   int serialno;
84   int stream_is_init;
85   int packetno;
86   char *comment;
87   int comment_length;
88   int seen_file_icons;
89   int close_at_end;
90   EncStream *next;
91 };
92
93 struct OggOpusEnc {
94   OpusMSEncoder *st;
95   int rate;
96   int channels;
97   float *buffer;
98   int buffer_start;
99   int buffer_end;
100   SpeexResamplerState *re;
101   int frame_size;
102   int decision_delay;
103   int max_ogg_delay;
104   ogg_int64_t curr_granule;
105   ogg_int64_t end_granule;
106   ogg_int64_t last_page_granule;
107   OpusEncCallbacks callbacks;
108   OpusHeader header;
109   int comment_padding;
110   EncStream *streams;
111   EncStream *last_stream;
112 };
113
114 static int oe_flush_page(OggOpusEnc *enc) {
115   ogg_page og;
116   int ret;
117   int written = 0;
118   while ( (ret = ogg_stream_flush(&enc->streams->os, &og)) ) {
119     if (!ret) break;
120     ret = oe_write_page(&og, &enc->callbacks, enc->streams->user_data);
121     if (ret == -1) {
122       return -1;
123     }
124     written += ret;
125   }
126   return written;
127 }
128
129 int stdio_write(void *user_data, const unsigned char *ptr, int len) {
130   struct StdioObject *obj = (struct StdioObject*)user_data;
131   return fwrite(ptr, 1, len, obj->file) != (size_t)len;
132 }
133
134 int stdio_close(void *user_data) {
135   struct StdioObject *obj = (struct StdioObject*)user_data;
136   int ret = fclose(obj->file);
137   free(obj);
138   return ret;
139 }
140
141 static const OpusEncCallbacks stdio_callbacks = {
142   stdio_write,
143   stdio_close
144 };
145
146 /* Create a new OggOpus file. */
147 OggOpusEnc *ope_create_file(const char *path, int rate, int channels, int family, int *error) {
148   OggOpusEnc *enc;
149   struct StdioObject *obj;
150   obj = malloc(sizeof(*obj));
151   enc = ope_create_callbacks(&stdio_callbacks, obj, rate, channels, family, error);
152   if (enc == NULL || (error && *error)) {
153     return NULL;
154   }
155   obj->file = fopen(path, "wb");
156   if (!obj->file) {
157     if (error) *error = OPE_CANNOT_OPEN;
158     /* FIXME: Destroy the encoder properly. */
159     free(obj);
160     return NULL;
161   }
162   return enc;
163 }
164
165 EncStream *stream_create() {
166   EncStream *stream;
167   stream = malloc(sizeof(*stream));
168   if (!stream) return NULL;
169   stream->next = NULL;
170   stream->close_at_end = 1;
171   stream->serialno_is_set = 0;
172   stream->seen_file_icons = 0;
173   stream->stream_is_init = 0;
174   stream->comment = NULL;
175   comment_init(&stream->comment, &stream->comment_length, opus_get_version_string());
176   if (!stream->comment) goto fail;
177   {
178     char encoder_string[1024];
179     snprintf(encoder_string, sizeof(encoder_string), "%s version %s", PACKAGE_NAME, PACKAGE_VERSION);
180     comment_add(&stream->comment, &stream->comment_length, "ENCODER", encoder_string);
181   }
182   return stream;
183 fail:
184   if (stream->comment) free(stream->comment);
185   free(stream);
186   return NULL;
187 }
188
189 static void stream_destroy(EncStream *stream) {
190   if (stream->comment) free(stream->comment);
191   if (stream->stream_is_init) ogg_stream_clear(&stream->os);
192   free(stream);
193 }
194
195 /* Create a new OggOpus file (callback-based). */
196 OggOpusEnc *ope_create_callbacks(const OpusEncCallbacks *callbacks, void *user_data,
197     int rate, int channels, int family, int *error) {
198   OpusMSEncoder *st=NULL;
199   OggOpusEnc *enc=NULL;
200   int ret;
201   if (family != 0 && family != 1 && family != 255) {
202     if (error) *error = OPE_UNIMPLEMENTED;
203     return NULL;
204   }
205   if (channels <= 0 || channels > 255) {
206     if (error) *error = OPE_BAD_ARG;
207     return NULL;
208   }
209   /* FIXME: Add resampling support. */
210   if (rate <= 0) {
211     if (error) *error = OPE_BAD_ARG;
212     return NULL;
213   }
214
215   if ( (enc = malloc(sizeof(*enc))) == NULL) goto fail;
216   enc->streams = NULL;
217   if ( (enc->streams = stream_create()) == NULL) goto fail;
218   enc->streams->next = NULL;
219   enc->last_stream = enc->streams;
220   enc->rate = rate;
221   enc->channels = channels;
222   enc->frame_size = 960;
223   enc->decision_delay = 96000;
224   enc->max_ogg_delay = 48000;
225   enc->comment_padding = 512;
226   enc->header.channels=channels;
227   enc->header.channel_mapping=family;
228   enc->header.input_sample_rate=rate;
229   enc->header.gain=0;
230   st=opus_multistream_surround_encoder_create(48000, channels, enc->header.channel_mapping,
231       &enc->header.nb_streams, &enc->header.nb_coupled,
232       enc->header.stream_map, OPUS_APPLICATION_AUDIO, &ret);
233   if (! (ret == OPUS_OK && st != NULL) ) {
234     goto fail;
235   }
236   if (rate != 48000) {
237     enc->re = speex_resampler_init(channels, rate, 48000, 5, NULL);
238     if (enc->re == NULL) goto fail;
239     speex_resampler_skip_zeros(enc->re);
240   } else {
241     enc->re = NULL;
242   }
243   opus_multistream_encoder_ctl(st, OPUS_SET_EXPERT_FRAME_DURATION(OPUS_FRAMESIZE_20_MS));
244   {
245     opus_int32 tmp;
246     int ret;
247     ret = opus_multistream_encoder_ctl(st, OPUS_GET_LOOKAHEAD(&tmp));
248     if (ret == OPUS_OK) enc->header.preskip = tmp;
249     else enc->header.preskip = 0;
250   }
251   enc->curr_granule = 0;
252   enc->end_granule = 0;
253   enc->last_page_granule = 0;
254   if ( (enc->buffer = malloc(sizeof(*enc->buffer)*BUFFER_SAMPLES*channels)) == NULL) goto fail;
255   enc->buffer_start = enc->buffer_end = 0;
256   enc->st = st;
257   enc->callbacks = *callbacks;
258   enc->streams->user_data = user_data;
259   if (error) *error = OPUS_OK;
260   return enc;
261 fail:
262   if (enc) {
263     free(enc);
264     if (enc->buffer) free(enc->buffer);
265     if (enc->streams) stream_destroy(enc->streams);
266   }
267   if (st) {
268     opus_multistream_encoder_destroy(st);
269   }
270   return NULL;
271 }
272
273 static void init_stream(OggOpusEnc *enc) {
274   time_t start_time;
275   assert(!enc->streams->stream_is_init);
276   if (!enc->streams->serialno_is_set) {
277     start_time = time(NULL);
278     srand(((getpid()&65535)<<15)^start_time);
279
280     enc->streams->serialno = rand();
281   }
282   
283   if (ogg_stream_init(&enc->streams->os, enc->streams->serialno) == -1) {
284     assert(0);
285     /* FIXME: How the hell do we handle that? */
286   }
287   comment_pad(&enc->streams->comment, &enc->streams->comment_length, enc->comment_padding);
288
289   /*Write header*/
290   {
291     ogg_packet op;
292     /*The Identification Header is 19 bytes, plus a Channel Mapping Table for
293       mapping families other than 0. The Channel Mapping Table is 2 bytes +
294       1 byte per channel. Because the maximum number of channels is 255, the
295       maximum size of this header is 19 + 2 + 255 = 276 bytes.*/
296     unsigned char header_data[276];
297     int packet_size = opus_header_to_packet(&enc->header, header_data, sizeof(header_data));
298     op.packet=header_data;
299     op.bytes=packet_size;
300     op.b_o_s=1;
301     op.e_o_s=0;
302     op.granulepos=0;
303     op.packetno=0;
304     ogg_stream_packetin(&enc->streams->os, &op);
305     oe_flush_page(enc);
306
307     op.packet = (unsigned char *)enc->streams->comment;
308     op.bytes = enc->streams->comment_length;
309     op.b_o_s = 0;
310     op.e_o_s = 0;
311     op.granulepos = 0;
312     op.packetno = 1;
313     ogg_stream_packetin(&enc->streams->os, &op);
314     oe_flush_page(enc);
315   }
316   enc->streams->stream_is_init = 1;
317   enc->streams->packetno = 2;
318 }
319
320 static void shift_buffer(OggOpusEnc *enc) {
321     memmove(enc->buffer, &enc->buffer[enc->channels*enc->buffer_start], enc->channels*(enc->buffer_end-enc->buffer_start)*sizeof(*enc->buffer));
322     enc->buffer_end -= enc->buffer_start;
323     enc->buffer_start = 0;
324 }
325
326 static void encode_buffer(OggOpusEnc *enc) {
327   /* Round up when converting the granule pos because the decoder will round down. */
328   ogg_int64_t end_granule48k = (enc->end_granule*48000 + enc->rate - 1)/enc->rate + enc->header.preskip;
329   while (enc->buffer_end-enc->buffer_start > enc->frame_size + enc->decision_delay) {
330     int flush_needed;
331     ogg_packet op;
332     ogg_page og;
333     int nbBytes;
334     unsigned char packet[MAX_PACKET_SIZE];
335     nbBytes = opus_multistream_encode_float(enc->st, &enc->buffer[enc->channels*enc->buffer_start],
336         enc->buffer_end-enc->buffer_start, packet, MAX_PACKET_SIZE);
337     /* FIXME: How do we handle failure here. */
338     assert(nbBytes > 0);
339     enc->curr_granule += enc->frame_size;
340     op.packet=packet;
341     op.bytes=nbBytes;
342     op.b_o_s=0;
343     op.packetno=enc->streams->packetno++;
344     op.granulepos=enc->curr_granule;
345     op.e_o_s=enc->curr_granule >= end_granule48k;
346     if (op.e_o_s) op.granulepos=end_granule48k;
347     ogg_stream_packetin(&enc->streams->os, &op);
348     /* FIXME: Also flush on too many segments. */
349     flush_needed = op.e_o_s || enc->curr_granule - enc->last_page_granule > enc->max_ogg_delay;
350     if (flush_needed) {
351       while (ogg_stream_flush_fill(&enc->streams->os, &og, 255*255)) {
352         if (ogg_page_packets(&og) != 0) enc->last_page_granule = ogg_page_granulepos(&og);
353         int ret = oe_write_page(&og, &enc->callbacks, enc->streams->user_data);
354         /* FIXME: what do we do if this fails? */
355         assert(ret != -1);
356       }
357     } else {
358       while (ogg_stream_pageout_fill(&enc->streams->os, &og, 255*255)) {
359         if (ogg_page_packets(&og) != 0) enc->last_page_granule = ogg_page_granulepos(&og);
360         int ret = oe_write_page(&og, &enc->callbacks, enc->streams->user_data);
361         /* FIXME: what do we do if this fails? */
362         assert(ret != -1);
363       }
364     }
365     if (op.e_o_s) {
366       EncStream *tmp;
367       tmp = enc->streams->next;
368       if (enc->streams->close_at_end) enc->callbacks.close(enc->streams->user_data);
369       stream_destroy(enc->streams);
370       enc->streams = tmp;
371       if (!tmp) enc->last_stream = 0;
372       return;
373     }
374     enc->buffer_start += enc->frame_size;
375   }
376   /* If we've reached the end of the buffer, move everything back to the front. */
377   if (enc->buffer_end == BUFFER_SAMPLES) {
378     shift_buffer(enc);
379   }
380   /* This function must never leave the buffer full. */
381   assert(enc->buffer_end < BUFFER_SAMPLES);
382 }
383
384 /* Add/encode any number of float samples to the file. */
385 int ope_write_float(OggOpusEnc *enc, const float *pcm, int samples_per_channel) {
386   int channels = enc->channels;
387   if (!enc->streams->stream_is_init) init_stream(enc);
388   if (samples_per_channel < 0) return OPE_BAD_ARG;
389   enc->end_granule += samples_per_channel;
390   do {
391     int i;
392     spx_uint32_t in_samples, out_samples;
393     out_samples = BUFFER_SAMPLES-enc->buffer_end;
394     if (enc->re != NULL) {
395       in_samples = samples_per_channel;
396       speex_resampler_process_interleaved_float(enc->re, pcm, &in_samples, &enc->buffer[channels*enc->buffer_end], &out_samples);
397     } else {
398       int curr;
399       curr = MIN((spx_uint32_t)samples_per_channel, out_samples);
400       for (i=0;i<channels*curr;i++) {
401       enc->buffer[channels*enc->buffer_end+i] = pcm[i];
402       }
403       in_samples = out_samples = curr;
404     }
405     enc->buffer_end += out_samples;
406     pcm += in_samples*channels;
407     samples_per_channel -= in_samples;
408     encode_buffer(enc);
409   } while (samples_per_channel > 0);
410   return OPE_OK;
411 }
412
413 #define CONVERT_BUFFER 256
414
415 /* Add/encode any number of int16 samples to the file. */
416 int ope_write(OggOpusEnc *enc, const opus_int16 *pcm, int samples_per_channel) {
417   int channels = enc->channels;
418   if (!enc->streams->stream_is_init) init_stream(enc);
419   if (samples_per_channel < 0) return OPE_BAD_ARG;
420   enc->end_granule += samples_per_channel;
421   do {
422     int i;
423     spx_uint32_t in_samples, out_samples;
424     out_samples = BUFFER_SAMPLES-enc->buffer_end;
425     if (enc->re != NULL) {
426       float buf[CONVERT_BUFFER*MAX_CHANNELS];
427       in_samples = MIN(CONVERT_BUFFER, samples_per_channel);
428       for (i=0;i<channels*(int)in_samples;i++) {
429         buf[i] = (1.f/32768)*pcm[i];
430       }
431       speex_resampler_process_interleaved_float(enc->re, buf, &in_samples, &enc->buffer[channels*enc->buffer_end], &out_samples);
432     } else {
433       int curr;
434       curr = MIN((spx_uint32_t)samples_per_channel, out_samples);
435       for (i=0;i<channels*curr;i++) {
436         enc->buffer[channels*enc->buffer_end+i] = (1.f/32768)*pcm[i];
437       }
438       in_samples = out_samples = curr;
439     }
440     enc->buffer_end += out_samples;
441     pcm += in_samples*channels;
442     samples_per_channel -= in_samples;
443     encode_buffer(enc);
444   } while (samples_per_channel > 0);
445   return OPE_OK;
446 }
447
448 static void finalize_all_streams(OggOpusEnc *enc) {
449   /* FIXME: Use a better value. */
450   int pad_samples = 3000;
451   if (!enc->streams->stream_is_init) init_stream(enc);
452   shift_buffer(enc);
453   /* FIXME: Do LPC extension instead. */
454   memset(&enc->buffer[enc->channels*enc->buffer_end], 0, pad_samples*enc->channels);
455   enc->decision_delay = 0;
456   enc->buffer_end += pad_samples;
457   assert(enc->buffer_end <= BUFFER_SAMPLES);
458   encode_buffer(enc);
459   assert(enc->streams == NULL);
460 }
461
462 /* Close/finalize the stream. */
463 int ope_close_and_free(OggOpusEnc *enc) {
464   finalize_all_streams(enc);
465   free(enc->buffer);
466   opus_multistream_encoder_destroy(enc->st);
467   if (enc->re) speex_resampler_destroy(enc->re);
468   free(enc);
469   return OPE_OK;
470 }
471
472 /* Ends the stream and create a new stream within the same file. */
473 int ope_chain_current(OggOpusEnc *enc) {
474   enc->last_stream->close_at_end = 0;
475   return ope_continue_new_callbacks(enc, enc->last_stream->user_data);
476 }
477
478 /* Ends the stream and create a new file. */
479 int ope_continue_new_file(OggOpusEnc *enc, const char *path) {
480   (void)enc;
481   (void)path;
482   return OPE_UNIMPLEMENTED;
483 }
484
485 /* Ends the stream and create a new file (callback-based). */
486 int ope_continue_new_callbacks(OggOpusEnc *enc, void *user_data) {
487   (void)enc;
488   (void)user_data;
489   return OPE_UNIMPLEMENTED;
490 }
491
492 /* Add a comment to the file (can only be called before encoding samples). */
493 int ope_add_comment(OggOpusEnc *enc, const char *tag, const char *val) {
494   if (enc->streams->stream_is_init) return OPE_TOO_LATE;
495   if (comment_add(&enc->streams->comment, &enc->streams->comment_length, tag, val)) return OPE_INTERNAL_ERROR;
496   return OPE_OK;
497 }
498
499 int ope_add_picture(OggOpusEnc *enc, const char *spec) {
500   const char *error_message;
501   char *picture_data;
502   if (enc->streams->stream_is_init) return OPE_TOO_LATE;
503   picture_data = parse_picture_specification(spec, &error_message, &enc->streams->seen_file_icons);
504   if(picture_data==NULL){
505     /* FIXME: return proper errors rather than printing a message. */
506     fprintf(stderr,"Error parsing picture option: %s\n",error_message);
507     return OPE_BAD_ARG;
508   }
509   comment_add(&enc->streams->comment, &enc->streams->comment_length, "METADATA_BLOCK_PICTURE", picture_data);
510   free(picture_data);
511   return OPE_OK;
512 }
513
514 /* Sets the Opus comment vendor string (optional, defaults to library info). */
515 int ope_set_vendor_string(OggOpusEnc *enc, const char *vendor) {
516   if (enc->streams->stream_is_init) return OPE_TOO_LATE;
517   (void)vendor;
518   return OPE_UNIMPLEMENTED;
519 }
520
521 int ope_flush_header(OggOpusEnc *enc) {
522   if (enc->streams->stream_is_init) return OPE_TOO_LATE;
523   else init_stream(enc);
524   return OPE_OK;
525 }
526
527 /* Goes straight to the libopus ctl() functions. */
528 int ope_encoder_ctl(OggOpusEnc *enc, int request, ...) {
529   int ret;
530   va_list ap;
531   va_start(ap, request);
532   switch (request) {
533     case OPUS_SET_APPLICATION_REQUEST:
534     case OPUS_SET_BITRATE_REQUEST:
535     case OPUS_SET_MAX_BANDWIDTH_REQUEST:
536     case OPUS_SET_VBR_REQUEST:
537     case OPUS_SET_BANDWIDTH_REQUEST:
538     case OPUS_SET_COMPLEXITY_REQUEST:
539     case OPUS_SET_INBAND_FEC_REQUEST:
540     case OPUS_SET_PACKET_LOSS_PERC_REQUEST:
541     case OPUS_SET_DTX_REQUEST:
542     case OPUS_SET_VBR_CONSTRAINT_REQUEST:
543     case OPUS_SET_FORCE_CHANNELS_REQUEST:
544     case OPUS_SET_SIGNAL_REQUEST:
545     case OPUS_SET_LSB_DEPTH_REQUEST:
546     case OPUS_SET_PREDICTION_DISABLED_REQUEST:
547 #ifdef OPUS_SET_PHASE_INVERSION_DISABLED_REQUEST
548     case OPUS_SET_PHASE_INVERSION_DISABLED_REQUEST:
549 #endif
550     {
551       opus_int32 value = va_arg(ap, opus_int32);
552       ret = opus_multistream_encoder_ctl(enc->st, request, value);
553     }
554     break;
555     case OPUS_SET_EXPERT_FRAME_DURATION_REQUEST:
556     {
557       opus_int32 value = va_arg(ap, opus_int32);
558       int max_supported = OPUS_FRAMESIZE_60_MS;
559 #ifdef OPUS_FRAMESIZE_120_MS
560       max_supported = OPUS_FRAMESIZE_120_MS;
561 #endif
562       if (value < OPUS_FRAMESIZE_2_5_MS || value > max_supported) {
563         ret = OPUS_UNIMPLEMENTED;
564         break;
565       }
566       ret = opus_multistream_encoder_ctl(enc->st, request, value);
567       if (ret == OPUS_OK) {
568         if (value <= OPUS_FRAMESIZE_40_MS)
569           enc->frame_size = 120<<(value-OPUS_FRAMESIZE_2_5_MS);
570         else
571           enc->frame_size = (value-OPUS_FRAMESIZE_2_5_MS-2)*960;
572       }
573     }
574     break;
575     default:
576       ret = OPUS_UNIMPLEMENTED;
577   }
578   va_end(ap);
579   return ret;
580 }
581
582 /* ctl()-type call for the OggOpus layer. */
583 int ope_set_params(OggOpusEnc *enc, int request, ...) {
584   int ret;
585   va_list ap;
586   va_start(ap, request);
587   switch (request) {
588     case OPE_SET_DECISION_DELAY_REQUEST:
589     {
590       opus_int32 value = va_arg(ap, opus_int32);
591       if (value < 0) {
592         ret = OPE_BAD_ARG;
593         break;
594       }
595       enc->decision_delay = value;
596       ret = OPE_OK;
597     }
598     break;
599     case OPE_SET_MUXING_DELAY_REQUEST:
600     {
601       opus_int32 value = va_arg(ap, opus_int32);
602       if (value < 0) {
603         ret = OPE_BAD_ARG;
604         break;
605       }
606       enc->max_ogg_delay = value;
607       ret = OPE_OK;
608     }
609     break;
610     case OPE_SET_COMMENT_PADDING_REQUEST:
611     {
612       opus_int32 value = va_arg(ap, opus_int32);
613       if (value < 0) {
614         ret = OPE_BAD_ARG;
615         break;
616       }
617       enc->comment_padding = value;
618       ret = OPE_OK;
619     }
620     break;
621     case OPE_SET_SERIALNO_REQUEST:
622     {
623       opus_int32 value = va_arg(ap, opus_int32);
624       enc->streams->serialno = value;
625       enc->streams->serialno_is_set = 1;
626       ret = OPE_OK;
627     }
628     break;
629     default:
630       return OPE_UNIMPLEMENTED;
631   }
632   va_end(ap);
633   return ret;
634 }