writing comment
[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 <time.h>
35 #include <unistd.h>
36 #include <stdlib.h>
37 #include <stdio.h>
38 #include <assert.h>
39 #include <opus_multistream.h>
40 #include "opusenc.h"
41 #include "opus_header.h"
42
43 #define BUFFER_SAMPLES 96000
44
45 static int oe_write_page(ogg_page *page, OpusEncCallbacks *cb, void *user_data)
46 {
47    int err;
48    err = cb->write(user_data, page->header, page->header_len);
49    if (err) return -1;
50    err = cb->write(user_data, page->body, page->body_len);
51    if (err) return -1;
52    return page->header_len+page->body_len;
53 }
54
55 struct StdioObject {
56   FILE *file;
57 };
58
59 struct OggOpusEnc {
60   OpusMSEncoder *st;
61   float *buffer;
62   OpusEncCallbacks callbacks;
63   void *user_data;
64   int os_allocated;
65   ogg_stream_state os;
66   ogg_page og;
67   ogg_packet op;
68   OpusHeader header;
69   char *comment;
70   int comment_length;
71 };
72
73 static int oe_flush_page(OggOpusEnc *enc) {
74   ogg_page og;
75   int ret;
76   int written = 0;
77   while ( (ret = ogg_stream_flush(&enc->os, &og)) ) {
78     if (!ret) break;
79     ret = oe_write_page(&og, &enc->callbacks, enc->user_data);
80     if (ret == -1) {
81       return -1;
82     }
83     written += ret;
84   }
85   return written;
86 }
87
88 int stdio_write(void *user_data, const unsigned char *ptr, int len) {
89   struct StdioObject *obj = (struct StdioObject*)user_data;
90   return fwrite(ptr, 1, len, obj->file) != (size_t)len;
91 }
92
93 int stdio_close(void *user_data) {
94   struct StdioObject *obj = (struct StdioObject*)user_data;
95   int ret = fclose(obj->file);
96   free(obj);
97   return ret;
98 }
99
100 static const OpusEncCallbacks stdio_callbacks = {
101   stdio_write,
102   stdio_close
103 };
104
105 /* Create a new OggOpus file. */
106 OggOpusEnc *ope_create_file(const char *path, int rate, int channels, int family, int *error) {
107   OggOpusEnc *enc;
108   struct StdioObject *obj;
109   obj = malloc(sizeof(*obj));
110   enc = ope_create_callbacks(&stdio_callbacks, obj, rate, channels, family, error);
111   if (enc == NULL || (error && *error)) {
112     return NULL;
113   }
114   obj->file = fopen(path, "wb");
115   if (!obj->file) {
116     if (error) *error = OPE_CANNOT_OPEN;
117     /* FIXME: Destroy the encoder properly. */
118     free(obj);
119     return NULL;
120   }
121   return enc;
122 }
123
124 /* Create a new OggOpus file (callback-based). */
125 OggOpusEnc *ope_create_callbacks(const OpusEncCallbacks *callbacks, void *user_data,
126     int rate, int channels, int family, int *error) {
127   OpusMSEncoder *st=NULL;
128   OggOpusEnc *enc=NULL;
129   int ret;
130   if (family != 0 && family != 1 && family != 255) {
131     if (error) *error = OPE_UNIMPLEMENTED;
132     return NULL;
133   }
134   if (channels <= 0 || channels > 255) {
135     if (error) *error = OPE_BAD_ARG;
136     return NULL;
137   }
138   enc->header.channels=channels;
139   enc->header.channel_mapping=family;
140   enc->header.input_sample_rate=rate;
141   enc->header.gain=0;
142   st=opus_multistream_surround_encoder_create(48000, channels, enc->header.channel_mapping,
143       &enc->header.nb_streams, &enc->header.nb_coupled,
144       enc->header.stream_map, OPUS_APPLICATION_AUDIO, &ret);
145   if (! (ret == OPUS_OK && st != NULL) ) {
146     goto fail;
147   }
148   if ( (enc = malloc(sizeof(*enc))) == NULL) goto fail;
149   enc->os_allocated = 0;
150   enc->comment = NULL;
151   comment_init(&enc->comment, &enc->comment_length, opus_get_version_string());
152   {
153     char encoder_string[1024];
154     snprintf(encoder_string, sizeof(encoder_string), "libopusenc version %s %s",PACKAGE_NAME,PACKAGE_VERSION);
155     comment_add(&enc->comment, &enc->comment_length, "ENCODER", encoder_string);
156     comment_pad(&enc->comment, &enc->comment_length, 512);
157   }
158   if (enc->comment == NULL) goto fail;
159   if ( (enc->buffer = malloc(sizeof(*enc->buffer)*BUFFER_SAMPLES*channels)) == NULL) goto fail;
160   enc->st = st;
161   enc->callbacks = *callbacks;
162   enc->user_data = user_data;
163   return enc;
164 fail:
165   if (enc) {
166     free(enc);
167     if (enc->buffer) free(enc->buffer);
168   }
169   if (st) {
170     opus_multistream_encoder_destroy(st);
171   }
172   return NULL;
173 }
174
175 static void init_stream(OggOpusEnc *enc) {
176   time_t start_time;
177   int serialno;
178   start_time = time(NULL);
179   srand(((getpid()&65535)<<15)^start_time);
180
181   serialno = rand();
182   if (ogg_stream_init(&enc->os, serialno) == -1) {
183     assert(0);
184     /* FIXME: How the hell do we handle that? */
185   }
186   /* FIXME: Compute preskip. */
187   enc->header.preskip = 0;
188
189   /*Write header*/
190   {
191     ogg_packet op;
192     /*The Identification Header is 19 bytes, plus a Channel Mapping Table for
193       mapping families other than 0. The Channel Mapping Table is 2 bytes +
194       1 byte per channel. Because the maximum number of channels is 255, the
195       maximum size of this header is 19 + 2 + 255 = 276 bytes.*/
196     unsigned char header_data[276];
197     int packet_size = opus_header_to_packet(&enc->header, header_data, sizeof(header_data));
198     op.packet=header_data;
199     op.bytes=packet_size;
200     op.b_o_s=1;
201     op.e_o_s=0;
202     op.granulepos=0;
203     op.packetno=0;
204     ogg_stream_packetin(&enc->os, &op);
205     oe_flush_page(enc);
206
207     op.packet = (unsigned char *)enc->comment;
208     op.bytes = enc->comment_length;
209     op.b_o_s = 0;
210     op.e_o_s = 0;
211     op.granulepos = 0;
212     op.packetno = 1;
213     ogg_stream_packetin(&enc->os, &op);
214     oe_flush_page(enc);
215   }
216
217 }
218
219 /* Add/encode any number of float samples to the file. */
220 int ope_write_float(OggOpusEnc *enc, float *pcm, int samples_per_channel) {
221   (void)enc;
222   (void)pcm;
223   (void)samples_per_channel;
224   return 0;
225 }
226
227 /* Add/encode any number of int16 samples to the file. */
228 int ope_write(OggOpusEnc *enc, opus_int16 *pcm, int samples_per_channel) {
229   (void)enc;
230   (void)pcm;
231   (void)samples_per_channel;
232   return 0;
233 }
234
235 static void finalize_stream(OggOpusEnc *enc) {
236   (void)enc;
237 }
238
239 /* Close/finalize the stream. */
240 int ope_close_and_free(OggOpusEnc *enc) {
241   finalize_stream(enc);
242   free(enc->buffer);
243   opus_multistream_encoder_destroy(enc->st);
244   if (enc->os_allocated) ogg_stream_clear(&enc->os);
245   return OPE_OK;
246 }
247
248 /* Ends the stream and create a new stream within the same file. */
249 int ope_chain_current(OggOpusEnc *enc) {
250   (void)enc;
251   return 0;
252 }
253
254 /* Ends the stream and create a new file. */
255 int ope_continue_new_file(OggOpusEnc *enc, const char *path) {
256   (void)enc;
257   (void)path;
258   return 0;
259 }
260
261 /* Ends the stream and create a new file (callback-based). */
262 int ope_continue_new_callbacks(OggOpusEnc *enc, void *user_data) {
263   (void)enc;
264   (void)user_data;
265   return 0;
266 }
267
268 /* Add a comment to the file (can only be called before encoding samples). */
269 int ope_add_comment(OggOpusEnc *enc, char *tag, char *val) {
270   (void)enc;
271   (void)tag;
272   (void)val;
273   return OPE_OK;
274 }
275
276 /* Sets the Opus comment vendor string (optional, defaults to library info). */
277 int ope_set_vendor_string(OggOpusEnc *enc, char *vendor) {
278   (void)enc;
279   (void)vendor;
280   return OPE_OK;
281 }
282
283 /* Goes straight to the libopus ctl() functions. */
284 int ope_encoder_ctl(OggOpusEnc *enc, int request, ...) {
285   (void)enc;
286   (void)request;
287   return 0;
288 }
289
290 /* ctl()-type call for the OggOpus layer. */
291 int ope_set_params(OggOpusEnc *enc, int request, ...) {
292   (void)enc;
293   (void)request;
294   return 0;
295 }