Disabling prediction on chaining joint
[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     opus_int32 pred;
334     int flush_needed;
335     ogg_packet op;
336     ogg_page og;
337     int nbBytes;
338     unsigned char packet[MAX_PACKET_SIZE];
339     opus_multistream_encoder_ctl(enc->st, OPUS_GET_PREDICTION_DISABLED(&pred));
340     if (enc->curr_granule + enc->frame_size>= end_granule48k && enc->streams->next) {
341       opus_multistream_encoder_ctl(enc->st, OPUS_SET_PREDICTION_DISABLED(1));
342     }
343     nbBytes = opus_multistream_encode_float(enc->st, &enc->buffer[enc->channels*enc->buffer_start],
344         enc->buffer_end-enc->buffer_start, packet, MAX_PACKET_SIZE);
345     /* FIXME: How do we handle failure here. */
346     opus_multistream_encoder_ctl(enc->st, OPUS_SET_PREDICTION_DISABLED(pred));
347     assert(nbBytes > 0);
348     enc->curr_granule += enc->frame_size;
349     op.packet=packet;
350     op.bytes=nbBytes;
351     op.b_o_s=0;
352     op.packetno=enc->streams->packetno++;
353     op.granulepos=enc->curr_granule;
354     op.e_o_s=enc->curr_granule >= end_granule48k;
355     do {
356       cont = 0;
357       if (op.e_o_s) op.granulepos=end_granule48k;
358       ogg_stream_packetin(&enc->streams->os, &op);
359       /* FIXME: Also flush on too many segments. */
360       flush_needed = op.e_o_s || enc->curr_granule - enc->last_page_granule > enc->max_ogg_delay;
361       if (flush_needed) {
362         while (ogg_stream_flush_fill(&enc->streams->os, &og, 255*255)) {
363           if (ogg_page_packets(&og) != 0) enc->last_page_granule = ogg_page_granulepos(&og);
364           int ret = oe_write_page(&og, &enc->callbacks, enc->streams->user_data);
365           /* FIXME: what do we do if this fails? */
366           assert(ret != -1);
367         }
368       } else {
369         while (ogg_stream_pageout_fill(&enc->streams->os, &og, 255*255)) {
370           if (ogg_page_packets(&og) != 0) enc->last_page_granule = ogg_page_granulepos(&og);
371           int ret = oe_write_page(&og, &enc->callbacks, enc->streams->user_data);
372           /* FIXME: what do we do if this fails? */
373           assert(ret != -1);
374         }
375       }
376       if (op.e_o_s) {
377         EncStream *tmp;
378         tmp = enc->streams->next;
379         if (enc->streams->close_at_end) enc->callbacks.close(enc->streams->user_data);
380         stream_destroy(enc->streams);
381         enc->streams = tmp;
382         if (!tmp) enc->last_stream = NULL;
383         if (enc->last_stream == NULL) return;
384         /* We're done with this stream, start the next one. */
385         /* FIXME: preskip seems to not work right. */
386         enc->header.preskip = end_granule48k + enc->frame_size - enc->curr_granule;
387         init_stream(enc);
388         end_granule48k = (enc->streams->end_granule*48000 + enc->rate - 1)/enc->rate + enc->granule_offset;
389         op.granulepos=enc->curr_granule;
390         op.e_o_s=enc->curr_granule >= end_granule48k;
391         cont = 1;
392       }
393     } while (cont);
394     enc->buffer_start += enc->frame_size;
395   }
396   /* If we've reached the end of the buffer, move everything back to the front. */
397   if (enc->buffer_end == BUFFER_SAMPLES) {
398     shift_buffer(enc);
399   }
400   /* This function must never leave the buffer full. */
401   assert(enc->buffer_end < BUFFER_SAMPLES);
402 }
403
404 /* Add/encode any number of float samples to the file. */
405 int ope_write_float(OggOpusEnc *enc, const float *pcm, int samples_per_channel) {
406   int channels = enc->channels;
407   enc->last_stream->header_is_frozen = 1;
408   if (!enc->streams->stream_is_init) init_stream(enc);
409   if (samples_per_channel < 0) return OPE_BAD_ARG;
410   enc->write_granule += samples_per_channel;
411   enc->last_stream->end_granule = enc->write_granule;
412   do {
413     int i;
414     spx_uint32_t in_samples, out_samples;
415     out_samples = BUFFER_SAMPLES-enc->buffer_end;
416     if (enc->re != NULL) {
417       in_samples = samples_per_channel;
418       speex_resampler_process_interleaved_float(enc->re, pcm, &in_samples, &enc->buffer[channels*enc->buffer_end], &out_samples);
419     } else {
420       int curr;
421       curr = MIN((spx_uint32_t)samples_per_channel, out_samples);
422       for (i=0;i<channels*curr;i++) {
423       enc->buffer[channels*enc->buffer_end+i] = pcm[i];
424       }
425       in_samples = out_samples = curr;
426     }
427     enc->buffer_end += out_samples;
428     pcm += in_samples*channels;
429     samples_per_channel -= in_samples;
430     encode_buffer(enc);
431   } while (samples_per_channel > 0);
432   return OPE_OK;
433 }
434
435 #define CONVERT_BUFFER 256
436
437 /* Add/encode any number of int16 samples to the file. */
438 int ope_write(OggOpusEnc *enc, const opus_int16 *pcm, int samples_per_channel) {
439   int channels = enc->channels;
440   enc->last_stream->header_is_frozen = 1;
441   if (!enc->streams->stream_is_init) init_stream(enc);
442   if (samples_per_channel < 0) return OPE_BAD_ARG;
443   enc->write_granule += samples_per_channel;
444   enc->last_stream->end_granule = enc->write_granule;
445   do {
446     int i;
447     spx_uint32_t in_samples, out_samples;
448     out_samples = BUFFER_SAMPLES-enc->buffer_end;
449     if (enc->re != NULL) {
450       float buf[CONVERT_BUFFER*MAX_CHANNELS];
451       in_samples = MIN(CONVERT_BUFFER, samples_per_channel);
452       for (i=0;i<channels*(int)in_samples;i++) {
453         buf[i] = (1.f/32768)*pcm[i];
454       }
455       speex_resampler_process_interleaved_float(enc->re, buf, &in_samples, &enc->buffer[channels*enc->buffer_end], &out_samples);
456     } else {
457       int curr;
458       curr = MIN((spx_uint32_t)samples_per_channel, out_samples);
459       for (i=0;i<channels*curr;i++) {
460         enc->buffer[channels*enc->buffer_end+i] = (1.f/32768)*pcm[i];
461       }
462       in_samples = out_samples = curr;
463     }
464     enc->buffer_end += out_samples;
465     pcm += in_samples*channels;
466     samples_per_channel -= in_samples;
467     encode_buffer(enc);
468   } while (samples_per_channel > 0);
469   return OPE_OK;
470 }
471
472 static void finalize_all_streams(OggOpusEnc *enc) {
473   /* FIXME: Use a better value. */
474   int pad_samples = 3000;
475   if (!enc->streams->stream_is_init) init_stream(enc);
476   shift_buffer(enc);
477   /* FIXME: Do LPC extension instead. */
478   memset(&enc->buffer[enc->channels*enc->buffer_end], 0, pad_samples*enc->channels);
479   enc->decision_delay = 0;
480   enc->buffer_end += pad_samples;
481   assert(enc->buffer_end <= BUFFER_SAMPLES);
482   encode_buffer(enc);
483   assert(enc->streams == NULL);
484 }
485
486 /* Close/finalize the stream. */
487 int ope_close_and_free(OggOpusEnc *enc) {
488   finalize_all_streams(enc);
489   free(enc->buffer);
490   opus_multistream_encoder_destroy(enc->st);
491   if (enc->re) speex_resampler_destroy(enc->re);
492   free(enc);
493   return OPE_OK;
494 }
495
496 /* Ends the stream and create a new stream within the same file. */
497 int ope_chain_current(OggOpusEnc *enc) {
498   enc->last_stream->close_at_end = 0;
499   return ope_continue_new_callbacks(enc, enc->last_stream->user_data);
500 }
501
502 /* Ends the stream and create a new file. */
503 int ope_continue_new_file(OggOpusEnc *enc, const char *path) {
504   (void)enc;
505   (void)path;
506   return OPE_UNIMPLEMENTED;
507 }
508
509 /* Ends the stream and create a new file (callback-based). */
510 int ope_continue_new_callbacks(OggOpusEnc *enc, void *user_data) {
511   EncStream *new_stream;
512   assert(enc->streams);
513   assert(enc->last_stream);
514   new_stream = stream_create();
515   if (!new_stream) return OPE_INTERNAL_ERROR;
516   new_stream->user_data = user_data;
517   enc->last_stream->next = new_stream;
518   enc->last_stream = new_stream;
519   return OPE_OK;
520 }
521
522 /* Add a comment to the file (can only be called before encoding samples). */
523 int ope_add_comment(OggOpusEnc *enc, const char *tag, const char *val) {
524   if (enc->last_stream->header_is_frozen) return OPE_TOO_LATE;
525   if (enc->last_stream->stream_is_init) return OPE_TOO_LATE;
526   if (comment_add(&enc->last_stream->comment, &enc->last_stream->comment_length, tag, val)) return OPE_INTERNAL_ERROR;
527   return OPE_OK;
528 }
529
530 int ope_add_picture(OggOpusEnc *enc, const char *spec) {
531   const char *error_message;
532   char *picture_data;
533   if (enc->last_stream->header_is_frozen) return OPE_TOO_LATE;
534   if (enc->last_stream->stream_is_init) return OPE_TOO_LATE;
535   picture_data = parse_picture_specification(spec, &error_message, &enc->last_stream->seen_file_icons);
536   if(picture_data==NULL){
537     /* FIXME: return proper errors rather than printing a message. */
538     fprintf(stderr,"Error parsing picture option: %s\n",error_message);
539     return OPE_BAD_ARG;
540   }
541   comment_add(&enc->last_stream->comment, &enc->last_stream->comment_length, "METADATA_BLOCK_PICTURE", picture_data);
542   free(picture_data);
543   return OPE_OK;
544 }
545
546 /* Sets the Opus comment vendor string (optional, defaults to library info). */
547 int ope_set_vendor_string(OggOpusEnc *enc, const char *vendor) {
548   if (enc->last_stream->header_is_frozen) return OPE_TOO_LATE;
549   if (enc->last_stream->stream_is_init) return OPE_TOO_LATE;
550   (void)vendor;
551   return OPE_UNIMPLEMENTED;
552 }
553
554 int ope_flush_header(OggOpusEnc *enc) {
555   if (enc->last_stream->header_is_frozen) return OPE_TOO_LATE;
556   if (enc->last_stream->stream_is_init) return OPE_TOO_LATE;
557   else init_stream(enc);
558   return OPE_OK;
559 }
560
561 /* Goes straight to the libopus ctl() functions. */
562 int ope_encoder_ctl(OggOpusEnc *enc, int request, ...) {
563   int ret;
564   va_list ap;
565   va_start(ap, request);
566   switch (request) {
567     case OPUS_SET_APPLICATION_REQUEST:
568     case OPUS_SET_BITRATE_REQUEST:
569     case OPUS_SET_MAX_BANDWIDTH_REQUEST:
570     case OPUS_SET_VBR_REQUEST:
571     case OPUS_SET_BANDWIDTH_REQUEST:
572     case OPUS_SET_COMPLEXITY_REQUEST:
573     case OPUS_SET_INBAND_FEC_REQUEST:
574     case OPUS_SET_PACKET_LOSS_PERC_REQUEST:
575     case OPUS_SET_DTX_REQUEST:
576     case OPUS_SET_VBR_CONSTRAINT_REQUEST:
577     case OPUS_SET_FORCE_CHANNELS_REQUEST:
578     case OPUS_SET_SIGNAL_REQUEST:
579     case OPUS_SET_LSB_DEPTH_REQUEST:
580     case OPUS_SET_PREDICTION_DISABLED_REQUEST:
581 #ifdef OPUS_SET_PHASE_INVERSION_DISABLED_REQUEST
582     case OPUS_SET_PHASE_INVERSION_DISABLED_REQUEST:
583 #endif
584     {
585       opus_int32 value = va_arg(ap, opus_int32);
586       ret = opus_multistream_encoder_ctl(enc->st, request, value);
587     }
588     break;
589     case OPUS_SET_EXPERT_FRAME_DURATION_REQUEST:
590     {
591       opus_int32 value = va_arg(ap, opus_int32);
592       int max_supported = OPUS_FRAMESIZE_60_MS;
593 #ifdef OPUS_FRAMESIZE_120_MS
594       max_supported = OPUS_FRAMESIZE_120_MS;
595 #endif
596       if (value < OPUS_FRAMESIZE_2_5_MS || value > max_supported) {
597         ret = OPUS_UNIMPLEMENTED;
598         break;
599       }
600       ret = opus_multistream_encoder_ctl(enc->st, request, value);
601       if (ret == OPUS_OK) {
602         if (value <= OPUS_FRAMESIZE_40_MS)
603           enc->frame_size = 120<<(value-OPUS_FRAMESIZE_2_5_MS);
604         else
605           enc->frame_size = (value-OPUS_FRAMESIZE_2_5_MS-2)*960;
606       }
607     }
608     break;
609     default:
610       ret = OPUS_UNIMPLEMENTED;
611   }
612   va_end(ap);
613   return ret;
614 }
615
616 /* ctl()-type call for the OggOpus layer. */
617 int ope_set_params(OggOpusEnc *enc, int request, ...) {
618   int ret;
619   va_list ap;
620   va_start(ap, request);
621   switch (request) {
622     case OPE_SET_DECISION_DELAY_REQUEST:
623     {
624       opus_int32 value = va_arg(ap, opus_int32);
625       if (value < 0) {
626         ret = OPE_BAD_ARG;
627         break;
628       }
629       enc->decision_delay = value;
630       ret = OPE_OK;
631     }
632     break;
633     case OPE_SET_MUXING_DELAY_REQUEST:
634     {
635       opus_int32 value = va_arg(ap, opus_int32);
636       if (value < 0) {
637         ret = OPE_BAD_ARG;
638         break;
639       }
640       enc->max_ogg_delay = value;
641       ret = OPE_OK;
642     }
643     break;
644     case OPE_SET_COMMENT_PADDING_REQUEST:
645     {
646       opus_int32 value = va_arg(ap, opus_int32);
647       if (value < 0) {
648         ret = OPE_BAD_ARG;
649         break;
650       }
651       enc->comment_padding = value;
652       ret = OPE_OK;
653     }
654     break;
655     case OPE_SET_SERIALNO_REQUEST:
656     {
657       opus_int32 value = va_arg(ap, opus_int32);
658       if (enc->last_stream->header_is_frozen) return OPE_TOO_LATE;
659       enc->last_stream->serialno = value;
660       enc->last_stream->serialno_is_set = 1;
661       ret = OPE_OK;
662     }
663     break;
664     default:
665       return OPE_UNIMPLEMENTED;
666   }
667   va_end(ap);
668   return ret;
669 }