stream allocation
[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 struct StdioObject {
46   FILE *file;
47 };
48
49 struct OggOpusEnc {
50   OpusMSEncoder *st;
51   float *buffer;
52   OpusEncCallbacks callbacks;
53   void *user_data;
54   int os_allocated;
55   ogg_stream_state os;
56   ogg_page og;
57   ogg_packet op;
58 };
59
60 int stdio_write(void *user_data, const unsigned char *ptr, int len) {
61   struct StdioObject *obj = (struct StdioObject*)user_data;
62   return fwrite(ptr, 1, len, obj->file) != (size_t)len;
63 }
64
65 int stdio_close(void *user_data) {
66   struct StdioObject *obj = (struct StdioObject*)user_data;
67   int ret = fclose(obj->file);
68   free(obj);
69   return ret;
70 }
71
72 static const OpusEncCallbacks stdio_callbacks = {
73   stdio_write,
74   stdio_close
75 };
76
77 /* Create a new OggOpus file. */
78 OggOpusEnc *ope_create_file(const char *path, const OggOpusComments *comments,
79     int rate, int channels, int family, int *error) {
80   OggOpusEnc *enc;
81   struct StdioObject *obj;
82   obj = malloc(sizeof(*obj));
83   enc = ope_create_callbacks(&stdio_callbacks, obj, comments, rate, channels, family, error);
84   if (enc == NULL || (error && *error)) {
85     return NULL;
86   }
87   obj->file = fopen(path, "wb");
88   if (!obj->file) {
89     if (error) *error = OPE_CANNOT_OPEN;
90     /* FIXME: Destroy the encoder properly. */
91     free(obj);
92     return NULL;
93   }
94   return enc;
95 }
96
97 /* Create a new OggOpus file (callback-based). */
98 OggOpusEnc *ope_create_callbacks(const OpusEncCallbacks *callbacks, void *user_data,
99     const OggOpusComments *comments, int rate, int channels, int family, int *error) {
100   OpusMSEncoder *st=NULL;
101   OggOpusEnc *enc=NULL;
102   OpusHeader header;
103   int ret;
104   if (family != 0 && family != 1 && family != 255) {
105     if (error) *error = OPE_UNIMPLEMENTED;
106     return NULL;
107   }
108   if (channels <= 0 || channels > 255) {
109     if (error) *error = OPE_BAD_ARG;
110     return NULL;
111   }
112   header.channels=channels;
113   header.channel_mapping=family;
114   header.input_sample_rate=rate;
115   header.gain=0;
116   st=opus_multistream_surround_encoder_create(48000, channels, header.channel_mapping, &header.nb_streams, &header.nb_coupled,
117      header.stream_map, OPUS_APPLICATION_AUDIO, &ret);
118   if (! (ret == OPUS_OK && st != NULL) ) {
119     goto fail;
120   }
121   if ( (enc = malloc(sizeof(*enc))) == NULL) goto fail;
122   enc->os_allocated = 0;
123   if ( (enc->buffer = malloc(sizeof(*enc->buffer)*BUFFER_SAMPLES*channels)) == NULL) goto fail;
124   enc->st = st;
125   enc->callbacks = *callbacks;
126   enc->user_data = user_data;
127   (void)comments;
128   return enc;
129 fail:
130   if (enc) {
131     free(enc);
132     if (enc->buffer) free(enc->buffer);
133   }
134   if (st) {
135     opus_multistream_encoder_destroy(st);
136   }
137   return NULL;
138 }
139
140 static void init_stream(OggOpusEnc *enc) {
141   time_t start_time;
142   int serialno;
143   start_time = time(NULL);
144   srand(((getpid()&65535)<<15)^start_time);
145
146   serialno = rand();
147   if (ogg_stream_init(&enc->os, serialno) == -1) {
148     assert(0);
149     /* FIXME: How the hell do we handle that? */
150   }
151 }
152
153 /* Add/encode any number of float samples to the file. */
154 int ope_write_float(OggOpusEnc *enc, float *pcm, int samples_per_channel) {
155   (void)enc;
156   (void)pcm;
157   (void)samples_per_channel;
158   return 0;
159 }
160
161 /* Add/encode any number of int16 samples to the file. */
162 int ope_write(OggOpusEnc *enc, opus_int16 *pcm, int samples_per_channel) {
163   (void)enc;
164   (void)pcm;
165   (void)samples_per_channel;
166   return 0;
167 }
168
169 static void finalize_stream(OggOpusEnc *enc) {
170   (void)enc;
171 }
172
173 /* Close/finalize the stream. */
174 int ope_close_and_free(OggOpusEnc *enc) {
175   finalize_stream(enc);
176   free(enc->buffer);
177   opus_multistream_encoder_destroy(enc->st);
178   if (enc->os_allocated) ogg_stream_clear(&enc->os);
179   return OPE_OK;
180 }
181
182 /* Ends the stream and create a new stream within the same file. */
183 int ope_chain_current(OggOpusEnc *enc, const OggOpusComments *comments) {
184   (void)enc;
185   (void)comments;
186   return 0;
187 }
188
189 /* Ends the stream and create a new file. */
190 int ope_continue_new_file(OggOpusEnc *enc, const OggOpusComments *comments, const char *path) {
191   (void)enc;
192   (void)comments;
193   (void)path;
194   return 0;
195 }
196
197 /* Ends the stream and create a new file (callback-based). */
198 int ope_continue_new_callbacks(OggOpusEnc *enc, const OggOpusComments *comments, void *user_data) {
199   (void)enc;
200   (void)comments;
201   (void)user_data;
202   return 0;
203 }
204
205 /* Goes straight to the libopus ctl() functions. */
206 int ope_encoder_ctl(OggOpusEnc *enc, int request, ...) {
207   (void)enc;
208   (void)request;
209   return 0;
210 }
211
212 /* ctl()-type call for the OggOpus layer. */
213 int ope_set_params(OggOpusEnc *enc, int request, ...) {
214   (void)enc;
215   (void)request;
216   return 0;
217 }