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