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