9a767d72bdf415f55acb946545ac96e1576da5d5
[paraslash.git] / opusdec_filter.c
1 /*
2 * Copyright (c) 2002-2007 Jean-Marc Valin
3 * Copyright (c) 2008 CSIRO
4 * Copyright (c) 2007-2012 Xiph.Org Foundation
5 * Copyright (C) 2012-2013 Andre Noll <maan@systemlinux.org>
6 *
7 * Licensed under the GPL v2. For licencing details see COPYING.
8 */
9
10 /** \file opusdec_filter.c The ogg/opus decoder. */
11
12 /* This file is based on opusdec.c, by Jean-Marc Valin, see below. */
13
14 /*
15 * Redistribution and use in source and binary forms, with or without
16 * modification, are permitted provided that the following conditions are met:
17 *
18 * - Redistributions of source code must retain the above copyright notice,
19 * this list of conditions and the following disclaimer.
20 *
21 * - Redistributions in binary form must reproduce the above copyright notice,
22 * this list of conditions and the following disclaimer in the documentation
23 * and/or other materials provided with the distribution.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS
26 * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
27 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE
29 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35 * POSSIBILITY OF SUCH DAMAGE.
36 */
37
38
39 /* Silence gcc warning caused by including opus.h */
40 #if !defined(__STDC_VERSION__)
41 #define __STDC_VERSION__ 0
42 #endif
43
44 #include <regex.h>
45 #include <opus/opus.h>
46 #include <opus/opus_multistream.h>
47 #include <ogg/ogg.h>
48 #include <math.h>
49
50 #include "para.h"
51 #include "list.h"
52 #include "sched.h"
53 #include "ggo.h"
54 #include "buffer_tree.h"
55 #include "filter.h"
56 #include "error.h"
57 #include "string.h"
58 #include "opus_common.h"
59
60 /** 120ms at 48000 */
61 #define MAX_FRAME_SIZE (960*6)
62
63 struct opusdec_context {
64 OpusMSDecoder *st;
65 opus_int64 packet_count;
66 int total_links;
67 bool stream_init;
68 ogg_sync_state oy;
69 ogg_stream_state os;
70 ogg_page ogg_page;
71 bool eos;
72 int channels;
73 int preskip;
74 bool have_opus_stream;
75 ogg_int32_t opus_serialno;
76 };
77
78 static int opusdec_execute(struct btr_node *btrn, const char *cmd,
79 char **result)
80 {
81 struct filter_node *fn = btr_context(btrn);
82 struct opusdec_context *ctx = fn->private_data;
83
84 return decoder_execute(cmd, 48000, ctx->channels, result);
85 }
86
87 static void opusdec_open(struct filter_node *fn)
88 {
89 struct opusdec_context *ctx = para_calloc(sizeof(*ctx));
90
91 ogg_sync_init(&ctx->oy);
92 fn->private_data = ctx;
93 }
94
95 static void opusdec_close(struct filter_node *fn)
96 {
97 struct opusdec_context *ctx = fn->private_data;
98
99 if (ctx->st) {
100 opus_multistream_decoder_destroy(ctx->st);
101 if (ctx->stream_init)
102 ogg_stream_clear(&ctx->os);
103 ogg_sync_clear(&ctx->oy);
104 }
105 free(ctx);
106 fn->private_data = NULL;
107 }
108
109 /* Process an Opus header and setup the opus decoder based on it. */
110 static int opusdec_init(ogg_packet *op, struct opusdec_context *ctx)
111 {
112 int ret;
113 struct opus_header header;
114
115 ctx->st = NULL;
116 ret = opus_parse_header((char *)op->packet, op->bytes, &header);
117 if (ret < 0)
118 return ret;
119 PARA_INFO_LOG("detected header v%d\n", header.version);
120 ctx->channels = header.channels;
121 ctx->preskip = header.preskip;
122 ctx->st = opus_multistream_decoder_create(48000, header.channels,
123 header.nb_streams, header.nb_coupled, header.stream_map, &ret);
124 if (ret != OPUS_OK || !ctx->st) {
125 PARA_ERROR_LOG("%s\n", opus_strerror(ret));
126 return -E_CREATE_OPUS_DECODER;
127 }
128 if (header.gain != 0) {
129 ret = opus_multistream_decoder_ctl(ctx->st,
130 OPUS_SET_GAIN(header.gain));
131 if (ret != OPUS_OK) {
132 PARA_ERROR_LOG("%s\n", opus_strerror(ret));
133 return -E_OPUS_SET_GAIN;
134 }
135 PARA_INFO_LOG("playback gain: %fdB\n", header.gain / 256.);
136 }
137 PARA_INFO_LOG("%d channel(s), 48KHz\n", ctx->channels);
138 return 1;
139 }
140
141 static void opusdec_add_output(short *pcm, int frames_available,
142 struct btr_node *btrn, struct opusdec_context *ctx)
143 {
144 int tmp_skip, num_frames, bytes;
145
146 tmp_skip = PARA_MIN(ctx->preskip, frames_available);
147 ctx->preskip -= tmp_skip;
148 num_frames = frames_available - tmp_skip;
149 if (num_frames <= 0)
150 return;
151 bytes = sizeof(short) * num_frames * ctx->channels;
152
153 if (tmp_skip > 0) {
154 short *in = pcm + ctx->channels * tmp_skip;
155 short *out = para_malloc(bytes);
156 memcpy(out, in, bytes);
157 free(pcm);
158 pcm = out;
159 }
160 btr_add_output((char *)pcm, bytes, btrn);
161 }
162
163 /* returns > 1 if packet was decoded, 0 if it was ignored, negative on errors. */
164 static int decode_packet(struct opusdec_context *ctx, ogg_packet *op,
165 struct btr_node *btrn)
166 {
167 int ret;
168 short *output;
169
170 /*
171 * OggOpus streams are identified by a magic string in the initial
172 * stream header.
173 */
174 if (op->b_o_s && op->bytes >= 8 && !memcmp(op->packet, "OpusHead", 8)) {
175 if (!ctx->have_opus_stream) {
176 ctx->opus_serialno = ctx->os.serialno;
177 ctx->have_opus_stream = true;
178 ctx->packet_count = 0;
179 ctx->eos = false;
180 ctx->total_links++;
181 } else
182 PARA_NOTICE_LOG("ignoring opus stream %llu\n",
183 (long long unsigned)ctx->os.serialno);
184 }
185 if (!ctx->have_opus_stream || ctx->os.serialno != ctx->opus_serialno)
186 return 0;
187 /* If first packet in a logical stream, process the Opus header. */
188 if (ctx->packet_count == 0)
189 return opusdec_init(op, ctx);
190 if (ctx->packet_count == 1)
191 return 1;
192 /* don't care for anything except opus eos */
193 if (op->e_o_s && ctx->os.serialno == ctx->opus_serialno)
194 ctx->eos = true;
195 output = para_malloc(sizeof(short) * MAX_FRAME_SIZE * ctx->channels);
196 ret = opus_multistream_decode(ctx->st, (unsigned char *)op->packet,
197 op->bytes, output, MAX_FRAME_SIZE, 0);
198 if (ret < 0) {
199 PARA_ERROR_LOG("%s\n", opus_strerror(ret));
200 free(output);
201 return -E_OPUS_DECODE;
202 }
203 opusdec_add_output(output, ret, btrn, ctx);
204 return 1;
205 }
206
207 static void opusdec_pre_select(struct sched *s, struct task *t)
208 {
209 struct filter_node *fn = container_of(t, struct filter_node, task);
210 struct btr_node *btrn = fn->btrn;
211 int ns;
212
213 ns = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL);
214 if (ns != 0)
215 return sched_min_delay(s);
216 sched_request_timeout_ms(100, s);
217 }
218
219 static int opusdec_post_select(__a_unused struct sched *s, struct task *t)
220 {
221 struct filter_node *fn = container_of(t, struct filter_node, task);
222 struct opusdec_context *ctx = fn->private_data;
223 struct btr_node *btrn = fn->btrn;
224 int ret;
225 char *btr_buf, *data;
226 size_t nbytes;
227 ogg_packet op;
228
229 ret = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL);
230 if (ret <= 0)
231 goto out;
232 btr_merge(btrn, fn->min_iqs);
233 nbytes = btr_next_buffer(btrn, &btr_buf);
234 nbytes = PARA_MIN(nbytes, (size_t)32768);
235 ret = 0;
236 if (nbytes == 0)
237 goto out;
238 data = ogg_sync_buffer(&ctx->oy, nbytes);
239 memcpy(data, btr_buf, nbytes);
240 btr_consume(btrn, nbytes);
241 ogg_sync_wrote(&ctx->oy, nbytes);
242 for (;;) { /* loop over all ogg pages we got */
243 ret = 0;
244 if (ogg_sync_pageout(&ctx->oy, &ctx->ogg_page) != 1)
245 goto out;
246 if (!ctx->stream_init) {
247 ogg_stream_init(&ctx->os, ogg_page_serialno(&ctx->ogg_page));
248 ctx->stream_init = true;
249 }
250 if (ogg_page_serialno(&ctx->ogg_page) != ctx->os.serialno)
251 ogg_stream_reset_serialno(&ctx->os,
252 ogg_page_serialno(&ctx->ogg_page));
253 /* Add page to the bitstream */
254 ogg_stream_pagein(&ctx->os, &ctx->ogg_page);
255 for (;;) { /* loop over all opus packets */
256 ret = ogg_stream_packetout(&ctx->os, &op);
257 if (ret != 1)
258 break;
259 ret = decode_packet(ctx, &op, btrn);
260 if (ret < 0)
261 goto out;
262 ctx->packet_count++;
263 if (ctx->eos)
264 ctx->have_opus_stream = false;
265 }
266 }
267 out:
268 if (ret < 0)
269 btr_remove_node(&fn->btrn);
270 return ret;
271 }
272
273 /**
274 * The init function of the opusdec filter.
275 *
276 * \param f Pointer to the filter struct to initialize.
277 *
278 * \sa filter::init.
279 */
280 void opusdec_filter_init(struct filter *f)
281 {
282 f->open = opusdec_open;
283 f->close = opusdec_close;
284 f->pre_select = generic_filter_pre_select;
285 f->pre_select = opusdec_pre_select;
286 f->post_select = opusdec_post_select;
287 f->execute = opusdec_execute;
288 }