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