yading@10
|
1 /*
|
yading@10
|
2 * Copyright (c) 2011 Nicolas George <nicolas.george@normalesup.org>
|
yading@10
|
3 *
|
yading@10
|
4 * This file is part of FFmpeg.
|
yading@10
|
5 *
|
yading@10
|
6 * FFmpeg is free software; you can redistribute it and/or
|
yading@10
|
7 * modify it under the terms of the GNU Lesser General Public
|
yading@10
|
8 * License as published by the Free Software Foundation; either
|
yading@10
|
9 * version 2.1 of the License, or (at your option) any later version.
|
yading@10
|
10 *
|
yading@10
|
11 * FFmpeg is distributed in the hope that it will be useful,
|
yading@10
|
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
yading@10
|
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
yading@10
|
14 * GNU Lesser General Public License for more details.
|
yading@10
|
15 *
|
yading@10
|
16 * You should have received a copy of the GNU Lesser General Public
|
yading@10
|
17 * License along with FFmpeg; if not, write to the Free Software
|
yading@10
|
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
yading@10
|
19 */
|
yading@10
|
20
|
yading@10
|
21 /**
|
yading@10
|
22 * @file
|
yading@10
|
23 * Stream (de)synchronization filter
|
yading@10
|
24 */
|
yading@10
|
25
|
yading@10
|
26 #include "libavutil/eval.h"
|
yading@10
|
27 #include "libavutil/opt.h"
|
yading@10
|
28 #include "avfilter.h"
|
yading@10
|
29 #include "audio.h"
|
yading@10
|
30 #include "internal.h"
|
yading@10
|
31
|
yading@10
|
32 #define QUEUE_SIZE 16
|
yading@10
|
33
|
yading@10
|
34 static const char * const var_names[] = {
|
yading@10
|
35 "b1", "b2",
|
yading@10
|
36 "s1", "s2",
|
yading@10
|
37 "t1", "t2",
|
yading@10
|
38 NULL
|
yading@10
|
39 };
|
yading@10
|
40
|
yading@10
|
41 enum var_name {
|
yading@10
|
42 VAR_B1, VAR_B2,
|
yading@10
|
43 VAR_S1, VAR_S2,
|
yading@10
|
44 VAR_T1, VAR_T2,
|
yading@10
|
45 VAR_NB
|
yading@10
|
46 };
|
yading@10
|
47
|
yading@10
|
48 typedef struct {
|
yading@10
|
49 const AVClass *class;
|
yading@10
|
50 AVExpr *expr;
|
yading@10
|
51 char *expr_str;
|
yading@10
|
52 double var_values[VAR_NB];
|
yading@10
|
53 struct buf_queue {
|
yading@10
|
54 AVFrame *buf[QUEUE_SIZE];
|
yading@10
|
55 unsigned tail, nb;
|
yading@10
|
56 /* buf[tail] is the oldest,
|
yading@10
|
57 buf[(tail + nb) % QUEUE_SIZE] is where the next is added */
|
yading@10
|
58 } queue[2];
|
yading@10
|
59 int req[2];
|
yading@10
|
60 int next_out;
|
yading@10
|
61 int eof; /* bitmask, one bit for each stream */
|
yading@10
|
62 } AStreamSyncContext;
|
yading@10
|
63
|
yading@10
|
64 #define OFFSET(x) offsetof(AStreamSyncContext, x)
|
yading@10
|
65 #define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
|
yading@10
|
66 static const AVOption astreamsync_options[] = {
|
yading@10
|
67 { "expr", "set stream selection expression", OFFSET(expr_str), AV_OPT_TYPE_STRING, { .str = "t1-t2" }, .flags = FLAGS },
|
yading@10
|
68 { "e", "set stream selection expression", OFFSET(expr_str), AV_OPT_TYPE_STRING, { .str = "t1-t2" }, .flags = FLAGS },
|
yading@10
|
69 { NULL }
|
yading@10
|
70 };
|
yading@10
|
71
|
yading@10
|
72 AVFILTER_DEFINE_CLASS(astreamsync);
|
yading@10
|
73
|
yading@10
|
74 static av_cold int init(AVFilterContext *ctx)
|
yading@10
|
75 {
|
yading@10
|
76 AStreamSyncContext *as = ctx->priv;
|
yading@10
|
77 int r, i;
|
yading@10
|
78
|
yading@10
|
79 r = av_expr_parse(&as->expr, as->expr_str, var_names,
|
yading@10
|
80 NULL, NULL, NULL, NULL, 0, ctx);
|
yading@10
|
81 if (r < 0) {
|
yading@10
|
82 av_log(ctx, AV_LOG_ERROR, "Error in expression \"%s\"\n", as->expr_str);
|
yading@10
|
83 return r;
|
yading@10
|
84 }
|
yading@10
|
85 for (i = 0; i < 42; i++)
|
yading@10
|
86 av_expr_eval(as->expr, as->var_values, NULL); /* exercize prng */
|
yading@10
|
87 return 0;
|
yading@10
|
88 }
|
yading@10
|
89
|
yading@10
|
90 static int query_formats(AVFilterContext *ctx)
|
yading@10
|
91 {
|
yading@10
|
92 int i;
|
yading@10
|
93 AVFilterFormats *formats, *rates;
|
yading@10
|
94 AVFilterChannelLayouts *layouts;
|
yading@10
|
95
|
yading@10
|
96 for (i = 0; i < 2; i++) {
|
yading@10
|
97 formats = ctx->inputs[i]->in_formats;
|
yading@10
|
98 ff_formats_ref(formats, &ctx->inputs[i]->out_formats);
|
yading@10
|
99 ff_formats_ref(formats, &ctx->outputs[i]->in_formats);
|
yading@10
|
100 rates = ff_all_samplerates();
|
yading@10
|
101 ff_formats_ref(rates, &ctx->inputs[i]->out_samplerates);
|
yading@10
|
102 ff_formats_ref(rates, &ctx->outputs[i]->in_samplerates);
|
yading@10
|
103 layouts = ctx->inputs[i]->in_channel_layouts;
|
yading@10
|
104 ff_channel_layouts_ref(layouts, &ctx->inputs[i]->out_channel_layouts);
|
yading@10
|
105 ff_channel_layouts_ref(layouts, &ctx->outputs[i]->in_channel_layouts);
|
yading@10
|
106 }
|
yading@10
|
107 return 0;
|
yading@10
|
108 }
|
yading@10
|
109
|
yading@10
|
110 static int config_output(AVFilterLink *outlink)
|
yading@10
|
111 {
|
yading@10
|
112 AVFilterContext *ctx = outlink->src;
|
yading@10
|
113 int id = outlink == ctx->outputs[1];
|
yading@10
|
114
|
yading@10
|
115 outlink->sample_rate = ctx->inputs[id]->sample_rate;
|
yading@10
|
116 outlink->time_base = ctx->inputs[id]->time_base;
|
yading@10
|
117 return 0;
|
yading@10
|
118 }
|
yading@10
|
119
|
yading@10
|
120 static int send_out(AVFilterContext *ctx, int out_id)
|
yading@10
|
121 {
|
yading@10
|
122 AStreamSyncContext *as = ctx->priv;
|
yading@10
|
123 struct buf_queue *queue = &as->queue[out_id];
|
yading@10
|
124 AVFrame *buf = queue->buf[queue->tail];
|
yading@10
|
125 int ret;
|
yading@10
|
126
|
yading@10
|
127 queue->buf[queue->tail] = NULL;
|
yading@10
|
128 as->var_values[VAR_B1 + out_id]++;
|
yading@10
|
129 as->var_values[VAR_S1 + out_id] += buf->nb_samples;
|
yading@10
|
130 if (buf->pts != AV_NOPTS_VALUE)
|
yading@10
|
131 as->var_values[VAR_T1 + out_id] =
|
yading@10
|
132 av_q2d(ctx->outputs[out_id]->time_base) * buf->pts;
|
yading@10
|
133 as->var_values[VAR_T1 + out_id] += buf->nb_samples /
|
yading@10
|
134 (double)ctx->inputs[out_id]->sample_rate;
|
yading@10
|
135 ret = ff_filter_frame(ctx->outputs[out_id], buf);
|
yading@10
|
136 queue->nb--;
|
yading@10
|
137 queue->tail = (queue->tail + 1) % QUEUE_SIZE;
|
yading@10
|
138 if (as->req[out_id])
|
yading@10
|
139 as->req[out_id]--;
|
yading@10
|
140 return ret;
|
yading@10
|
141 }
|
yading@10
|
142
|
yading@10
|
143 static void send_next(AVFilterContext *ctx)
|
yading@10
|
144 {
|
yading@10
|
145 AStreamSyncContext *as = ctx->priv;
|
yading@10
|
146 int i;
|
yading@10
|
147
|
yading@10
|
148 while (1) {
|
yading@10
|
149 if (!as->queue[as->next_out].nb)
|
yading@10
|
150 break;
|
yading@10
|
151 send_out(ctx, as->next_out);
|
yading@10
|
152 if (!as->eof)
|
yading@10
|
153 as->next_out = av_expr_eval(as->expr, as->var_values, NULL) >= 0;
|
yading@10
|
154 }
|
yading@10
|
155 for (i = 0; i < 2; i++)
|
yading@10
|
156 if (as->queue[i].nb == QUEUE_SIZE)
|
yading@10
|
157 send_out(ctx, i);
|
yading@10
|
158 }
|
yading@10
|
159
|
yading@10
|
160 static int request_frame(AVFilterLink *outlink)
|
yading@10
|
161 {
|
yading@10
|
162 AVFilterContext *ctx = outlink->src;
|
yading@10
|
163 AStreamSyncContext *as = ctx->priv;
|
yading@10
|
164 int id = outlink == ctx->outputs[1];
|
yading@10
|
165
|
yading@10
|
166 as->req[id]++;
|
yading@10
|
167 while (as->req[id] && !(as->eof & (1 << id))) {
|
yading@10
|
168 if (as->queue[as->next_out].nb) {
|
yading@10
|
169 send_next(ctx);
|
yading@10
|
170 } else {
|
yading@10
|
171 as->eof |= 1 << as->next_out;
|
yading@10
|
172 ff_request_frame(ctx->inputs[as->next_out]);
|
yading@10
|
173 if (as->eof & (1 << as->next_out))
|
yading@10
|
174 as->next_out = !as->next_out;
|
yading@10
|
175 }
|
yading@10
|
176 }
|
yading@10
|
177 return 0;
|
yading@10
|
178 }
|
yading@10
|
179
|
yading@10
|
180 static int filter_frame(AVFilterLink *inlink, AVFrame *insamples)
|
yading@10
|
181 {
|
yading@10
|
182 AVFilterContext *ctx = inlink->dst;
|
yading@10
|
183 AStreamSyncContext *as = ctx->priv;
|
yading@10
|
184 int id = inlink == ctx->inputs[1];
|
yading@10
|
185
|
yading@10
|
186 as->queue[id].buf[(as->queue[id].tail + as->queue[id].nb++) % QUEUE_SIZE] =
|
yading@10
|
187 insamples;
|
yading@10
|
188 as->eof &= ~(1 << id);
|
yading@10
|
189 send_next(ctx);
|
yading@10
|
190 return 0;
|
yading@10
|
191 }
|
yading@10
|
192
|
yading@10
|
193 static av_cold void uninit(AVFilterContext *ctx)
|
yading@10
|
194 {
|
yading@10
|
195 AStreamSyncContext *as = ctx->priv;
|
yading@10
|
196
|
yading@10
|
197 av_expr_free(as->expr);
|
yading@10
|
198 as->expr = NULL;
|
yading@10
|
199 }
|
yading@10
|
200
|
yading@10
|
201 static const AVFilterPad astreamsync_inputs[] = {
|
yading@10
|
202 {
|
yading@10
|
203 .name = "in1",
|
yading@10
|
204 .type = AVMEDIA_TYPE_AUDIO,
|
yading@10
|
205 .filter_frame = filter_frame,
|
yading@10
|
206 },{
|
yading@10
|
207 .name = "in2",
|
yading@10
|
208 .type = AVMEDIA_TYPE_AUDIO,
|
yading@10
|
209 .filter_frame = filter_frame,
|
yading@10
|
210 },
|
yading@10
|
211 { NULL }
|
yading@10
|
212 };
|
yading@10
|
213
|
yading@10
|
214 static const AVFilterPad astreamsync_outputs[] = {
|
yading@10
|
215 {
|
yading@10
|
216 .name = "out1",
|
yading@10
|
217 .type = AVMEDIA_TYPE_AUDIO,
|
yading@10
|
218 .config_props = config_output,
|
yading@10
|
219 .request_frame = request_frame,
|
yading@10
|
220 },{
|
yading@10
|
221 .name = "out2",
|
yading@10
|
222 .type = AVMEDIA_TYPE_AUDIO,
|
yading@10
|
223 .config_props = config_output,
|
yading@10
|
224 .request_frame = request_frame,
|
yading@10
|
225 },
|
yading@10
|
226 { NULL }
|
yading@10
|
227 };
|
yading@10
|
228
|
yading@10
|
229 AVFilter avfilter_af_astreamsync = {
|
yading@10
|
230 .name = "astreamsync",
|
yading@10
|
231 .description = NULL_IF_CONFIG_SMALL("Copy two streams of audio data "
|
yading@10
|
232 "in a configurable order."),
|
yading@10
|
233 .priv_size = sizeof(AStreamSyncContext),
|
yading@10
|
234 .init = init,
|
yading@10
|
235 .uninit = uninit,
|
yading@10
|
236 .query_formats = query_formats,
|
yading@10
|
237 .inputs = astreamsync_inputs,
|
yading@10
|
238 .outputs = astreamsync_outputs,
|
yading@10
|
239 .priv_class = &astreamsync_class,
|
yading@10
|
240 };
|