Applying comments to the last created stream
[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 = OPUS_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   return NULL;
274 }
275
276 static void init_stream(OggOpusEnc *enc) {
277   time_t start_time;
278   assert(!enc->streams->stream_is_init);
279   if (!enc->streams->serialno_is_set) {
280     start_time = time(NULL);
281     srand(((getpid()&65535)<<15)^start_time);
282
283     enc->streams->serialno = rand();
284   }
285   
286   if (ogg_stream_init(&enc->streams->os, enc->streams->serialno) == -1) {
287     assert(0);
288     /* FIXME: How the hell do we handle that? */
289   }
290   comment_pad(&enc->streams->comment, &enc->streams->comment_length, enc->comment_padding);
291
292   /*Write header*/
293   {
294     ogg_packet op;
295     /*The Identification Header is 19 bytes, plus a Channel Mapping Table for
296       mapping families other than 0. The Channel Mapping Table is 2 bytes +
297       1 byte per channel. Because the maximum number of channels is 255, the
298       maximum size of this header is 19 + 2 + 255 = 276 bytes.*/
299     unsigned char header_data[276];
300     int packet_size = opus_header_to_packet(&enc->header, header_data, sizeof(header_data));
301     op.packet=header_data;
302     op.bytes=packet_size;
303     op.b_o_s=1;
304     op.e_o_s=0;
305     op.granulepos=0;
306     op.packetno=0;
307     ogg_stream_packetin(&enc->streams->os, &op);
308     oe_flush_page(enc);
309
310     op.packet = (unsigned char *)enc->streams->comment;
311     op.bytes = enc->streams->comment_length;
312     op.b_o_s = 0;
313     op.e_o_s = 0;
314     op.granulepos = 0;
315     op.packetno = 1;
316     ogg_stream_packetin(&enc->streams->os, &op);
317     oe_flush_page(enc);
318   }
319   enc->streams->stream_is_init = 1;
320   enc->streams->packetno = 2;
321 }
322
323 static void shift_buffer(OggOpusEnc *enc) {
324     memmove(enc->buffer, &enc->buffer[enc->channels*enc->buffer_start], enc->channels*(enc->buffer_end-enc->buffer_start)*sizeof(*enc->buffer));
325     enc->buffer_end -= enc->buffer_start;
326     enc->buffer_start = 0;
327 }
328
329 static void encode_buffer(OggOpusEnc *enc) {
330   /* Round up when converting the granule pos because the decoder will round down. */
331   ogg_int64_t end_granule48k = (enc->streams->end_granule*48000 + enc->rate - 1)/enc->rate + enc->header.preskip;
332   while (enc->buffer_end-enc->buffer_start > enc->frame_size + enc->decision_delay) {
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     if (op.e_o_s) op.granulepos=end_granule48k;
350     ogg_stream_packetin(&enc->streams->os, &op);
351     /* FIXME: Also flush on too many segments. */
352     flush_needed = op.e_o_s || enc->curr_granule - enc->last_page_granule > enc->max_ogg_delay;
353     if (flush_needed) {
354       while (ogg_stream_flush_fill(&enc->streams->os, &og, 255*255)) {
355         if (ogg_page_packets(&og) != 0) enc->last_page_granule = ogg_page_granulepos(&og);
356         int ret = oe_write_page(&og, &enc->callbacks, enc->streams->user_data);
357         /* FIXME: what do we do if this fails? */
358         assert(ret != -1);
359       }
360     } else {
361       while (ogg_stream_pageout_fill(&enc->streams->os, &og, 255*255)) {
362         if (ogg_page_packets(&og) != 0) enc->last_page_granule = ogg_page_granulepos(&og);
363         int ret = oe_write_page(&og, &enc->callbacks, enc->streams->user_data);
364         /* FIXME: what do we do if this fails? */
365         assert(ret != -1);
366       }
367     }
368     if (op.e_o_s) {
369       EncStream *tmp;
370       tmp = enc->streams->next;
371       if (enc->streams->close_at_end) enc->callbacks.close(enc->streams->user_data);
372       stream_destroy(enc->streams);
373       enc->streams = tmp;
374       if (!tmp) enc->last_stream = 0;
375       return;
376     }
377     enc->buffer_start += enc->frame_size;
378   }
379   /* If we've reached the end of the buffer, move everything back to the front. */
380   if (enc->buffer_end == BUFFER_SAMPLES) {
381     shift_buffer(enc);
382   }
383   /* This function must never leave the buffer full. */
384   assert(enc->buffer_end < BUFFER_SAMPLES);
385 }
386
387 /* Add/encode any number of float samples to the file. */
388 int ope_write_float(OggOpusEnc *enc, const float *pcm, int samples_per_channel) {
389   int channels = enc->channels;
390   enc->last_stream->header_is_frozen = 1;
391   if (!enc->streams->stream_is_init) init_stream(enc);
392   if (samples_per_channel < 0) return OPE_BAD_ARG;
393   enc->write_granule += samples_per_channel;
394   enc->last_stream->end_granule = enc->write_granule;
395   do {
396     int i;
397     spx_uint32_t in_samples, out_samples;
398     out_samples = BUFFER_SAMPLES-enc->buffer_end;
399     if (enc->re != NULL) {
400       in_samples = samples_per_channel;
401       speex_resampler_process_interleaved_float(enc->re, pcm, &in_samples, &enc->buffer[channels*enc->buffer_end], &out_samples);
402     } else {
403       int curr;
404       curr = MIN((spx_uint32_t)samples_per_channel, out_samples);
405       for (i=0;i<channels*curr;i++) {
406       enc->buffer[channels*enc->buffer_end+i] = pcm[i];
407       }
408       in_samples = out_samples = curr;
409     }
410     enc->buffer_end += out_samples;
411     pcm += in_samples*channels;
412     samples_per_channel -= in_samples;
413     encode_buffer(enc);
414   } while (samples_per_channel > 0);
415   return OPE_OK;
416 }
417
418 #define CONVERT_BUFFER 256
419
420 /* Add/encode any number of int16 samples to the file. */
421 int ope_write(OggOpusEnc *enc, const opus_int16 *pcm, int samples_per_channel) {
422   int channels = enc->channels;
423   enc->last_stream->header_is_frozen = 1;
424   if (!enc->streams->stream_is_init) init_stream(enc);
425   if (samples_per_channel < 0) return OPE_BAD_ARG;
426   enc->write_granule += samples_per_channel;
427   enc->last_stream->end_granule = enc->write_granule;
428   do {
429     int i;
430     spx_uint32_t in_samples, out_samples;
431     out_samples = BUFFER_SAMPLES-enc->buffer_end;
432     if (enc->re != NULL) {
433       float buf[CONVERT_BUFFER*MAX_CHANNELS];
434       in_samples = MIN(CONVERT_BUFFER, samples_per_channel);
435       for (i=0;i<channels*(int)in_samples;i++) {
436         buf[i] = (1.f/32768)*pcm[i];
437       }
438       speex_resampler_process_interleaved_float(enc->re, buf, &in_samples, &enc->buffer[channels*enc->buffer_end], &out_samples);
439     } else {
440       int curr;
441       curr = MIN((spx_uint32_t)samples_per_channel, out_samples);
442       for (i=0;i<channels*curr;i++) {
443         enc->buffer[channels*enc->buffer_end+i] = (1.f/32768)*pcm[i];
444       }
445       in_samples = out_samples = curr;
446     }
447     enc->buffer_end += out_samples;
448     pcm += in_samples*channels;
449     samples_per_channel -= in_samples;
450     encode_buffer(enc);
451   } while (samples_per_channel > 0);
452   return OPE_OK;
453 }
454
455 static void finalize_all_streams(OggOpusEnc *enc) {
456   /* FIXME: Use a better value. */
457   int pad_samples = 3000;
458   if (!enc->streams->stream_is_init) init_stream(enc);
459   shift_buffer(enc);
460   /* FIXME: Do LPC extension instead. */
461   memset(&enc->buffer[enc->channels*enc->buffer_end], 0, pad_samples*enc->channels);
462   enc->decision_delay = 0;
463   enc->buffer_end += pad_samples;
464   assert(enc->buffer_end <= BUFFER_SAMPLES);
465   encode_buffer(enc);
466   assert(enc->streams == NULL);
467 }
468
469 /* Close/finalize the stream. */
470 int ope_close_and_free(OggOpusEnc *enc) {
471   finalize_all_streams(enc);
472   free(enc->buffer);
473   opus_multistream_encoder_destroy(enc->st);
474   if (enc->re) speex_resampler_destroy(enc->re);
475   free(enc);
476   return OPE_OK;
477 }
478
479 /* Ends the stream and create a new stream within the same file. */
480 int ope_chain_current(OggOpusEnc *enc) {
481   enc->last_stream->close_at_end = 0;
482   return ope_continue_new_callbacks(enc, enc->last_stream->user_data);
483 }
484
485 /* Ends the stream and create a new file. */
486 int ope_continue_new_file(OggOpusEnc *enc, const char *path) {
487   (void)enc;
488   (void)path;
489   return OPE_UNIMPLEMENTED;
490 }
491
492 /* Ends the stream and create a new file (callback-based). */
493 int ope_continue_new_callbacks(OggOpusEnc *enc, void *user_data) {
494   EncStream *new_stream;
495   assert(enc->streams);
496   assert(enc->last_stream);
497   new_stream = stream_create();
498   new_stream->user_data = user_data;
499   enc->last_stream->next = new_stream;
500   enc->last_stream = new_stream;
501   return OPE_UNIMPLEMENTED;
502 }
503
504 /* Add a comment to the file (can only be called before encoding samples). */
505 int ope_add_comment(OggOpusEnc *enc, const char *tag, const char *val) {
506   if (enc->last_stream->header_is_frozen) return OPE_TOO_LATE;
507   if (enc->last_stream->stream_is_init) return OPE_TOO_LATE;
508   if (comment_add(&enc->last_stream->comment, &enc->last_stream->comment_length, tag, val)) return OPE_INTERNAL_ERROR;
509   return OPE_OK;
510 }
511
512 int ope_add_picture(OggOpusEnc *enc, const char *spec) {
513   const char *error_message;
514   char *picture_data;
515   if (enc->last_stream->header_is_frozen) return OPE_TOO_LATE;
516   if (enc->last_stream->stream_is_init) return OPE_TOO_LATE;
517   picture_data = parse_picture_specification(spec, &error_message, &enc->last_stream->seen_file_icons);
518   if(picture_data==NULL){
519     /* FIXME: return proper errors rather than printing a message. */
520     fprintf(stderr,"Error parsing picture option: %s\n",error_message);
521     return OPE_BAD_ARG;
522   }
523   comment_add(&enc->last_stream->comment, &enc->last_stream->comment_length, "METADATA_BLOCK_PICTURE", picture_data);
524   free(picture_data);
525   return OPE_OK;
526 }
527
528 /* Sets the Opus comment vendor string (optional, defaults to library info). */
529 int ope_set_vendor_string(OggOpusEnc *enc, const char *vendor) {
530   if (enc->last_stream->header_is_frozen) return OPE_TOO_LATE;
531   if (enc->last_stream->stream_is_init) return OPE_TOO_LATE;
532   (void)vendor;
533   return OPE_UNIMPLEMENTED;
534 }
535
536 int ope_flush_header(OggOpusEnc *enc) {
537   if (enc->last_stream->header_is_frozen) return OPE_TOO_LATE;
538   if (enc->last_stream->stream_is_init) return OPE_TOO_LATE;
539   else init_stream(enc);
540   return OPE_OK;
541 }
542
543 /* Goes straight to the libopus ctl() functions. */
544 int ope_encoder_ctl(OggOpusEnc *enc, int request, ...) {
545   int ret;
546   va_list ap;
547   va_start(ap, request);
548   switch (request) {
549     case OPUS_SET_APPLICATION_REQUEST:
550     case OPUS_SET_BITRATE_REQUEST:
551     case OPUS_SET_MAX_BANDWIDTH_REQUEST:
552     case OPUS_SET_VBR_REQUEST:
553     case OPUS_SET_BANDWIDTH_REQUEST:
554     case OPUS_SET_COMPLEXITY_REQUEST:
555     case OPUS_SET_INBAND_FEC_REQUEST:
556     case OPUS_SET_PACKET_LOSS_PERC_REQUEST:
557     case OPUS_SET_DTX_REQUEST:
558     case OPUS_SET_VBR_CONSTRAINT_REQUEST:
559     case OPUS_SET_FORCE_CHANNELS_REQUEST:
560     case OPUS_SET_SIGNAL_REQUEST:
561     case OPUS_SET_LSB_DEPTH_REQUEST:
562     case OPUS_SET_PREDICTION_DISABLED_REQUEST:
563 #ifdef OPUS_SET_PHASE_INVERSION_DISABLED_REQUEST
564     case OPUS_SET_PHASE_INVERSION_DISABLED_REQUEST:
565 #endif
566     {
567       opus_int32 value = va_arg(ap, opus_int32);
568       ret = opus_multistream_encoder_ctl(enc->st, request, value);
569     }
570     break;
571     case OPUS_SET_EXPERT_FRAME_DURATION_REQUEST:
572     {
573       opus_int32 value = va_arg(ap, opus_int32);
574       int max_supported = OPUS_FRAMESIZE_60_MS;
575 #ifdef OPUS_FRAMESIZE_120_MS
576       max_supported = OPUS_FRAMESIZE_120_MS;
577 #endif
578       if (value < OPUS_FRAMESIZE_2_5_MS || value > max_supported) {
579         ret = OPUS_UNIMPLEMENTED;
580         break;
581       }
582       ret = opus_multistream_encoder_ctl(enc->st, request, value);
583       if (ret == OPUS_OK) {
584         if (value <= OPUS_FRAMESIZE_40_MS)
585           enc->frame_size = 120<<(value-OPUS_FRAMESIZE_2_5_MS);
586         else
587           enc->frame_size = (value-OPUS_FRAMESIZE_2_5_MS-2)*960;
588       }
589     }
590     break;
591     default:
592       ret = OPUS_UNIMPLEMENTED;
593   }
594   va_end(ap);
595   return ret;
596 }
597
598 /* ctl()-type call for the OggOpus layer. */
599 int ope_set_params(OggOpusEnc *enc, int request, ...) {
600   int ret;
601   va_list ap;
602   va_start(ap, request);
603   switch (request) {
604     case OPE_SET_DECISION_DELAY_REQUEST:
605     {
606       opus_int32 value = va_arg(ap, opus_int32);
607       if (value < 0) {
608         ret = OPE_BAD_ARG;
609         break;
610       }
611       enc->decision_delay = value;
612       ret = OPE_OK;
613     }
614     break;
615     case OPE_SET_MUXING_DELAY_REQUEST:
616     {
617       opus_int32 value = va_arg(ap, opus_int32);
618       if (value < 0) {
619         ret = OPE_BAD_ARG;
620         break;
621       }
622       enc->max_ogg_delay = value;
623       ret = OPE_OK;
624     }
625     break;
626     case OPE_SET_COMMENT_PADDING_REQUEST:
627     {
628       opus_int32 value = va_arg(ap, opus_int32);
629       if (value < 0) {
630         ret = OPE_BAD_ARG;
631         break;
632       }
633       enc->comment_padding = value;
634       ret = OPE_OK;
635     }
636     break;
637     case OPE_SET_SERIALNO_REQUEST:
638     {
639       opus_int32 value = va_arg(ap, opus_int32);
640       if (enc->last_stream->header_is_frozen) return OPE_TOO_LATE;
641       enc->last_stream->serialno = value;
642       enc->last_stream->serialno_is_set = 1;
643       ret = OPE_OK;
644     }
645     break;
646     default:
647       return OPE_UNIMPLEMENTED;
648   }
649   va_end(ap);
650   return ret;
651 }