e18bea69db8cc062c298eef1de9a03a00315ad4c
[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   int header_is_frozen;
91   ogg_int64_t end_granule;
92   EncStream *next;
93 };
94
95 struct OggOpusEnc {
96   OpusMSEncoder *st;
97   int rate;
98   int channels;
99   float *buffer;
100   int buffer_start;
101   int buffer_end;
102   SpeexResamplerState *re;
103   int frame_size;
104   int decision_delay;
105   int max_ogg_delay;
106   ogg_int64_t curr_granule;
107   ogg_int64_t write_granule;
108   ogg_int64_t last_page_granule;
109   OpusEncCallbacks callbacks;
110   OpusHeader header;
111   int comment_padding;
112   EncStream *streams;
113   EncStream *last_stream;
114 };
115
116 static int oe_flush_page(OggOpusEnc *enc) {
117   ogg_page og;
118   int ret;
119   int written = 0;
120   while ( (ret = ogg_stream_flush(&enc->streams->os, &og)) ) {
121     if (!ret) break;
122     ret = oe_write_page(&og, &enc->callbacks, enc->streams->user_data);
123     if (ret == -1) {
124       return -1;
125     }
126     written += ret;
127   }
128   return written;
129 }
130
131 int stdio_write(void *user_data, const unsigned char *ptr, int len) {
132   struct StdioObject *obj = (struct StdioObject*)user_data;
133   return fwrite(ptr, 1, len, obj->file) != (size_t)len;
134 }
135
136 int stdio_close(void *user_data) {
137   struct StdioObject *obj = (struct StdioObject*)user_data;
138   int ret = fclose(obj->file);
139   free(obj);
140   return ret;
141 }
142
143 static const OpusEncCallbacks stdio_callbacks = {
144   stdio_write,
145   stdio_close
146 };
147
148 /* Create a new OggOpus file. */
149 OggOpusEnc *ope_create_file(const char *path, int rate, int channels, int family, int *error) {
150   OggOpusEnc *enc;
151   struct StdioObject *obj;
152   obj = malloc(sizeof(*obj));
153   enc = ope_create_callbacks(&stdio_callbacks, obj, rate, channels, family, error);
154   if (enc == NULL || (error && *error)) {
155     return NULL;
156   }
157   obj->file = fopen(path, "wb");
158   if (!obj->file) {
159     if (error) *error = OPE_CANNOT_OPEN;
160     /* FIXME: Destroy the encoder properly. */
161     free(obj);
162     return NULL;
163   }
164   return enc;
165 }
166
167 EncStream *stream_create() {
168   EncStream *stream;
169   stream = malloc(sizeof(*stream));
170   if (!stream) return NULL;
171   stream->next = NULL;
172   stream->close_at_end = 1;
173   stream->serialno_is_set = 0;
174   stream->seen_file_icons = 0;
175   stream->stream_is_init = 0;
176   stream->header_is_frozen = 0;
177   stream->comment = NULL;
178   comment_init(&stream->comment, &stream->comment_length, opus_get_version_string());
179   if (!stream->comment) goto fail;
180   {
181     char encoder_string[1024];
182     snprintf(encoder_string, sizeof(encoder_string), "%s version %s", PACKAGE_NAME, PACKAGE_VERSION);
183     comment_add(&stream->comment, &stream->comment_length, "ENCODER", encoder_string);
184   }
185   return stream;
186 fail:
187   if (stream->comment) free(stream->comment);
188   free(stream);
189   return NULL;
190 }
191
192 static void stream_destroy(EncStream *stream) {
193   if (stream->comment) free(stream->comment);
194   if (stream->stream_is_init) ogg_stream_clear(&stream->os);
195   free(stream);
196 }
197
198 /* Create a new OggOpus file (callback-based). */
199 OggOpusEnc *ope_create_callbacks(const OpusEncCallbacks *callbacks, void *user_data,
200     int rate, int channels, int family, int *error) {
201   OpusMSEncoder *st=NULL;
202   OggOpusEnc *enc=NULL;
203   int ret;
204   if (family != 0 && family != 1 && family != 255) {
205     if (error) *error = OPE_UNIMPLEMENTED;
206     return NULL;
207   }
208   if (channels <= 0 || channels > 255) {
209     if (error) *error = OPE_BAD_ARG;
210     return NULL;
211   }
212   /* FIXME: Add resampling support. */
213   if (rate <= 0) {
214     if (error) *error = OPE_BAD_ARG;
215     return NULL;
216   }
217
218   if ( (enc = malloc(sizeof(*enc))) == NULL) goto fail;
219   enc->streams = NULL;
220   if ( (enc->streams = stream_create()) == NULL) goto fail;
221   enc->streams->next = NULL;
222   enc->last_stream = enc->streams;
223   enc->rate = rate;
224   enc->channels = channels;
225   enc->frame_size = 960;
226   enc->decision_delay = 96000;
227   enc->max_ogg_delay = 48000;
228   enc->comment_padding = 512;
229   enc->header.channels=channels;
230   enc->header.channel_mapping=family;
231   enc->header.input_sample_rate=rate;
232   enc->header.gain=0;
233   st=opus_multistream_surround_encoder_create(48000, channels, enc->header.channel_mapping,
234       &enc->header.nb_streams, &enc->header.nb_coupled,
235       enc->header.stream_map, OPUS_APPLICATION_AUDIO, &ret);
236   if (! (ret == OPUS_OK && st != NULL) ) {
237     goto fail;
238   }
239   if (rate != 48000) {
240     enc->re = speex_resampler_init(channels, rate, 48000, 5, NULL);
241     if (enc->re == NULL) goto fail;
242     speex_resampler_skip_zeros(enc->re);
243   } else {
244     enc->re = NULL;
245   }
246   opus_multistream_encoder_ctl(st, OPUS_SET_EXPERT_FRAME_DURATION(OPUS_FRAMESIZE_20_MS));
247   {
248     opus_int32 tmp;
249     int ret;
250     ret = opus_multistream_encoder_ctl(st, OPUS_GET_LOOKAHEAD(&tmp));
251     if (ret == OPUS_OK) enc->header.preskip = tmp;
252     else enc->header.preskip = 0;
253   }
254   enc->curr_granule = 0;
255   enc->write_granule = 0;
256   enc->last_page_granule = 0;
257   if ( (enc->buffer = malloc(sizeof(*enc->buffer)*BUFFER_SAMPLES*channels)) == NULL) goto fail;
258   enc->buffer_start = enc->buffer_end = 0;
259   enc->st = st;
260   enc->callbacks = *callbacks;
261   enc->streams->user_data = user_data;
262   if (error) *error = OPE_OK;
263   return enc;
264 fail:
265   if (enc) {
266     free(enc);
267     if (enc->buffer) free(enc->buffer);
268     if (enc->streams) stream_destroy(enc->streams);
269   }
270   if (st) {
271     opus_multistream_encoder_destroy(st);
272   }
273   if (error) *error = OPE_INTERNAL_ERROR;
274   return NULL;
275 }
276
277 static void init_stream(OggOpusEnc *enc) {
278   time_t start_time;
279   assert(!enc->streams->stream_is_init);
280   if (!enc->streams->serialno_is_set) {
281     start_time = time(NULL);
282     srand(((getpid()&65535)<<15)^start_time);
283
284     enc->streams->serialno = rand();
285   }
286   
287   if (ogg_stream_init(&enc->streams->os, enc->streams->serialno) == -1) {
288     assert(0);
289     /* FIXME: How the hell do we handle that? */
290   }
291   comment_pad(&enc->streams->comment, &enc->streams->comment_length, enc->comment_padding);
292
293   /*Write header*/
294   {
295     ogg_packet op;
296     /*The Identification Header is 19 bytes, plus a Channel Mapping Table for
297       mapping families other than 0. The Channel Mapping Table is 2 bytes +
298       1 byte per channel. Because the maximum number of channels is 255, the
299       maximum size of this header is 19 + 2 + 255 = 276 bytes.*/
300     unsigned char header_data[276];
301     int packet_size = opus_header_to_packet(&enc->header, header_data, sizeof(header_data));
302     op.packet=header_data;
303     op.bytes=packet_size;
304     op.b_o_s=1;
305     op.e_o_s=0;
306     op.granulepos=0;
307     op.packetno=0;
308     ogg_stream_packetin(&enc->streams->os, &op);
309     oe_flush_page(enc);
310
311     op.packet = (unsigned char *)enc->streams->comment;
312     op.bytes = enc->streams->comment_length;
313     op.b_o_s = 0;
314     op.e_o_s = 0;
315     op.granulepos = 0;
316     op.packetno = 1;
317     ogg_stream_packetin(&enc->streams->os, &op);
318     oe_flush_page(enc);
319   }
320   enc->streams->stream_is_init = 1;
321   enc->streams->packetno = 2;
322 }
323
324 static void shift_buffer(OggOpusEnc *enc) {
325     memmove(enc->buffer, &enc->buffer[enc->channels*enc->buffer_start], enc->channels*(enc->buffer_end-enc->buffer_start)*sizeof(*enc->buffer));
326     enc->buffer_end -= enc->buffer_start;
327     enc->buffer_start = 0;
328 }
329
330 static void encode_buffer(OggOpusEnc *enc) {
331   /* Round up when converting the granule pos because the decoder will round down. */
332   ogg_int64_t end_granule48k = (enc->streams->end_granule*48000 + enc->rate - 1)/enc->rate + enc->header.preskip;
333   while (enc->buffer_end-enc->buffer_start > enc->frame_size + enc->decision_delay) {
334     int cont;
335     int flush_needed;
336     ogg_packet op;
337     ogg_page og;
338     int nbBytes;
339     unsigned char packet[MAX_PACKET_SIZE];
340     nbBytes = opus_multistream_encode_float(enc->st, &enc->buffer[enc->channels*enc->buffer_start],
341         enc->buffer_end-enc->buffer_start, packet, MAX_PACKET_SIZE);
342     /* FIXME: How do we handle failure here. */
343     assert(nbBytes > 0);
344     enc->curr_granule += enc->frame_size;
345     op.packet=packet;
346     op.bytes=nbBytes;
347     op.b_o_s=0;
348     op.packetno=enc->streams->packetno++;
349     op.granulepos=enc->curr_granule;
350     op.e_o_s=enc->curr_granule >= end_granule48k;
351     do {
352       cont = 0;
353       if (op.e_o_s) op.granulepos=end_granule48k;
354       ogg_stream_packetin(&enc->streams->os, &op);
355       /* FIXME: Also flush on too many segments. */
356       flush_needed = op.e_o_s || enc->curr_granule - enc->last_page_granule > enc->max_ogg_delay;
357       if (flush_needed) {
358         while (ogg_stream_flush_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       } else {
365         while (ogg_stream_pageout_fill(&enc->streams->os, &og, 255*255)) {
366           if (ogg_page_packets(&og) != 0) enc->last_page_granule = ogg_page_granulepos(&og);
367           int ret = oe_write_page(&og, &enc->callbacks, enc->streams->user_data);
368           /* FIXME: what do we do if this fails? */
369           assert(ret != -1);
370         }
371       }
372       if (op.e_o_s) {
373         EncStream *tmp;
374         tmp = enc->streams->next;
375         if (enc->streams->close_at_end) enc->callbacks.close(enc->streams->user_data);
376         stream_destroy(enc->streams);
377         enc->streams = tmp;
378         if (!tmp) enc->last_stream = NULL;
379         if (enc->last_stream == NULL) return;
380         /* We're done with this stream, start the next one. */
381         /* FIXME: Update preskip. */
382         init_stream(enc);
383         end_granule48k = (enc->streams->end_granule*48000 + enc->rate - 1)/enc->rate + enc->header.preskip;
384         op.granulepos=enc->curr_granule;
385         op.e_o_s=enc->curr_granule >= end_granule48k;
386         cont = 1;
387       }
388     } while (cont);
389     enc->buffer_start += enc->frame_size;
390   }
391   /* If we've reached the end of the buffer, move everything back to the front. */
392   if (enc->buffer_end == BUFFER_SAMPLES) {
393     shift_buffer(enc);
394   }
395   /* This function must never leave the buffer full. */
396   assert(enc->buffer_end < BUFFER_SAMPLES);
397 }
398
399 /* Add/encode any number of float samples to the file. */
400 int ope_write_float(OggOpusEnc *enc, const float *pcm, int samples_per_channel) {
401   int channels = enc->channels;
402   enc->last_stream->header_is_frozen = 1;
403   if (!enc->streams->stream_is_init) init_stream(enc);
404   if (samples_per_channel < 0) return OPE_BAD_ARG;
405   enc->write_granule += samples_per_channel;
406   enc->last_stream->end_granule = enc->write_granule;
407   do {
408     int i;
409     spx_uint32_t in_samples, out_samples;
410     out_samples = BUFFER_SAMPLES-enc->buffer_end;
411     if (enc->re != NULL) {
412       in_samples = samples_per_channel;
413       speex_resampler_process_interleaved_float(enc->re, pcm, &in_samples, &enc->buffer[channels*enc->buffer_end], &out_samples);
414     } else {
415       int curr;
416       curr = MIN((spx_uint32_t)samples_per_channel, out_samples);
417       for (i=0;i<channels*curr;i++) {
418       enc->buffer[channels*enc->buffer_end+i] = pcm[i];
419       }
420       in_samples = out_samples = curr;
421     }
422     enc->buffer_end += out_samples;
423     pcm += in_samples*channels;
424     samples_per_channel -= in_samples;
425     encode_buffer(enc);
426   } while (samples_per_channel > 0);
427   return OPE_OK;
428 }
429
430 #define CONVERT_BUFFER 256
431
432 /* Add/encode any number of int16 samples to the file. */
433 int ope_write(OggOpusEnc *enc, const opus_int16 *pcm, int samples_per_channel) {
434   int channels = enc->channels;
435   enc->last_stream->header_is_frozen = 1;
436   if (!enc->streams->stream_is_init) init_stream(enc);
437   if (samples_per_channel < 0) return OPE_BAD_ARG;
438   enc->write_granule += samples_per_channel;
439   enc->last_stream->end_granule = enc->write_granule;
440   do {
441     int i;
442     spx_uint32_t in_samples, out_samples;
443     out_samples = BUFFER_SAMPLES-enc->buffer_end;
444     if (enc->re != NULL) {
445       float buf[CONVERT_BUFFER*MAX_CHANNELS];
446       in_samples = MIN(CONVERT_BUFFER, samples_per_channel);
447       for (i=0;i<channels*(int)in_samples;i++) {
448         buf[i] = (1.f/32768)*pcm[i];
449       }
450       speex_resampler_process_interleaved_float(enc->re, buf, &in_samples, &enc->buffer[channels*enc->buffer_end], &out_samples);
451     } else {
452       int curr;
453       curr = MIN((spx_uint32_t)samples_per_channel, out_samples);
454       for (i=0;i<channels*curr;i++) {
455         enc->buffer[channels*enc->buffer_end+i] = (1.f/32768)*pcm[i];
456       }
457       in_samples = out_samples = curr;
458     }
459     enc->buffer_end += out_samples;
460     pcm += in_samples*channels;
461     samples_per_channel -= in_samples;
462     encode_buffer(enc);
463   } while (samples_per_channel > 0);
464   return OPE_OK;
465 }
466
467 static void finalize_all_streams(OggOpusEnc *enc) {
468   /* FIXME: Use a better value. */
469   int pad_samples = 3000;
470   if (!enc->streams->stream_is_init) init_stream(enc);
471   shift_buffer(enc);
472   /* FIXME: Do LPC extension instead. */
473   memset(&enc->buffer[enc->channels*enc->buffer_end], 0, pad_samples*enc->channels);
474   enc->decision_delay = 0;
475   enc->buffer_end += pad_samples;
476   assert(enc->buffer_end <= BUFFER_SAMPLES);
477   encode_buffer(enc);
478   assert(enc->streams == NULL);
479 }
480
481 /* Close/finalize the stream. */
482 int ope_close_and_free(OggOpusEnc *enc) {
483   finalize_all_streams(enc);
484   free(enc->buffer);
485   opus_multistream_encoder_destroy(enc->st);
486   if (enc->re) speex_resampler_destroy(enc->re);
487   free(enc);
488   return OPE_OK;
489 }
490
491 /* Ends the stream and create a new stream within the same file. */
492 int ope_chain_current(OggOpusEnc *enc) {
493   enc->last_stream->close_at_end = 0;
494   return ope_continue_new_callbacks(enc, enc->last_stream->user_data);
495 }
496
497 /* Ends the stream and create a new file. */
498 int ope_continue_new_file(OggOpusEnc *enc, const char *path) {
499   (void)enc;
500   (void)path;
501   return OPE_UNIMPLEMENTED;
502 }
503
504 /* Ends the stream and create a new file (callback-based). */
505 int ope_continue_new_callbacks(OggOpusEnc *enc, void *user_data) {
506   EncStream *new_stream;
507   assert(enc->streams);
508   assert(enc->last_stream);
509   new_stream = stream_create();
510   if (!new_stream) return OPE_INTERNAL_ERROR;
511   new_stream->user_data = user_data;
512   enc->last_stream->next = new_stream;
513   enc->last_stream = new_stream;
514   return OPE_OK;
515 }
516
517 /* Add a comment to the file (can only be called before encoding samples). */
518 int ope_add_comment(OggOpusEnc *enc, const char *tag, const char *val) {
519   if (enc->last_stream->header_is_frozen) return OPE_TOO_LATE;
520   if (enc->last_stream->stream_is_init) return OPE_TOO_LATE;
521   if (comment_add(&enc->last_stream->comment, &enc->last_stream->comment_length, tag, val)) return OPE_INTERNAL_ERROR;
522   return OPE_OK;
523 }
524
525 int ope_add_picture(OggOpusEnc *enc, const char *spec) {
526   const char *error_message;
527   char *picture_data;
528   if (enc->last_stream->header_is_frozen) return OPE_TOO_LATE;
529   if (enc->last_stream->stream_is_init) return OPE_TOO_LATE;
530   picture_data = parse_picture_specification(spec, &error_message, &enc->last_stream->seen_file_icons);
531   if(picture_data==NULL){
532     /* FIXME: return proper errors rather than printing a message. */
533     fprintf(stderr,"Error parsing picture option: %s\n",error_message);
534     return OPE_BAD_ARG;
535   }
536   comment_add(&enc->last_stream->comment, &enc->last_stream->comment_length, "METADATA_BLOCK_PICTURE", picture_data);
537   free(picture_data);
538   return OPE_OK;
539 }
540
541 /* Sets the Opus comment vendor string (optional, defaults to library info). */
542 int ope_set_vendor_string(OggOpusEnc *enc, const char *vendor) {
543   if (enc->last_stream->header_is_frozen) return OPE_TOO_LATE;
544   if (enc->last_stream->stream_is_init) return OPE_TOO_LATE;
545   (void)vendor;
546   return OPE_UNIMPLEMENTED;
547 }
548
549 int ope_flush_header(OggOpusEnc *enc) {
550   if (enc->last_stream->header_is_frozen) return OPE_TOO_LATE;
551   if (enc->last_stream->stream_is_init) return OPE_TOO_LATE;
552   else init_stream(enc);
553   return OPE_OK;
554 }
555
556 /* Goes straight to the libopus ctl() functions. */
557 int ope_encoder_ctl(OggOpusEnc *enc, int request, ...) {
558   int ret;
559   va_list ap;
560   va_start(ap, request);
561   switch (request) {
562     case OPUS_SET_APPLICATION_REQUEST:
563     case OPUS_SET_BITRATE_REQUEST:
564     case OPUS_SET_MAX_BANDWIDTH_REQUEST:
565     case OPUS_SET_VBR_REQUEST:
566     case OPUS_SET_BANDWIDTH_REQUEST:
567     case OPUS_SET_COMPLEXITY_REQUEST:
568     case OPUS_SET_INBAND_FEC_REQUEST:
569     case OPUS_SET_PACKET_LOSS_PERC_REQUEST:
570     case OPUS_SET_DTX_REQUEST:
571     case OPUS_SET_VBR_CONSTRAINT_REQUEST:
572     case OPUS_SET_FORCE_CHANNELS_REQUEST:
573     case OPUS_SET_SIGNAL_REQUEST:
574     case OPUS_SET_LSB_DEPTH_REQUEST:
575     case OPUS_SET_PREDICTION_DISABLED_REQUEST:
576 #ifdef OPUS_SET_PHASE_INVERSION_DISABLED_REQUEST
577     case OPUS_SET_PHASE_INVERSION_DISABLED_REQUEST:
578 #endif
579     {
580       opus_int32 value = va_arg(ap, opus_int32);
581       ret = opus_multistream_encoder_ctl(enc->st, request, value);
582     }
583     break;
584     case OPUS_SET_EXPERT_FRAME_DURATION_REQUEST:
585     {
586       opus_int32 value = va_arg(ap, opus_int32);
587       int max_supported = OPUS_FRAMESIZE_60_MS;
588 #ifdef OPUS_FRAMESIZE_120_MS
589       max_supported = OPUS_FRAMESIZE_120_MS;
590 #endif
591       if (value < OPUS_FRAMESIZE_2_5_MS || value > max_supported) {
592         ret = OPUS_UNIMPLEMENTED;
593         break;
594       }
595       ret = opus_multistream_encoder_ctl(enc->st, request, value);
596       if (ret == OPUS_OK) {
597         if (value <= OPUS_FRAMESIZE_40_MS)
598           enc->frame_size = 120<<(value-OPUS_FRAMESIZE_2_5_MS);
599         else
600           enc->frame_size = (value-OPUS_FRAMESIZE_2_5_MS-2)*960;
601       }
602     }
603     break;
604     default:
605       ret = OPUS_UNIMPLEMENTED;
606   }
607   va_end(ap);
608   return ret;
609 }
610
611 /* ctl()-type call for the OggOpus layer. */
612 int ope_set_params(OggOpusEnc *enc, int request, ...) {
613   int ret;
614   va_list ap;
615   va_start(ap, request);
616   switch (request) {
617     case OPE_SET_DECISION_DELAY_REQUEST:
618     {
619       opus_int32 value = va_arg(ap, opus_int32);
620       if (value < 0) {
621         ret = OPE_BAD_ARG;
622         break;
623       }
624       enc->decision_delay = value;
625       ret = OPE_OK;
626     }
627     break;
628     case OPE_SET_MUXING_DELAY_REQUEST:
629     {
630       opus_int32 value = va_arg(ap, opus_int32);
631       if (value < 0) {
632         ret = OPE_BAD_ARG;
633         break;
634       }
635       enc->max_ogg_delay = value;
636       ret = OPE_OK;
637     }
638     break;
639     case OPE_SET_COMMENT_PADDING_REQUEST:
640     {
641       opus_int32 value = va_arg(ap, opus_int32);
642       if (value < 0) {
643         ret = OPE_BAD_ARG;
644         break;
645       }
646       enc->comment_padding = value;
647       ret = OPE_OK;
648     }
649     break;
650     case OPE_SET_SERIALNO_REQUEST:
651     {
652       opus_int32 value = va_arg(ap, opus_int32);
653       if (enc->last_stream->header_is_frozen) return OPE_TOO_LATE;
654       enc->last_stream->serialno = value;
655       enc->last_stream->serialno_is_set = 1;
656       ret = OPE_OK;
657     }
658     break;
659     default:
660       return OPE_UNIMPLEMENTED;
661   }
662   va_end(ap);
663   return ret;
664 }