History

lavfi: add astreamsync audio filter.

Nicolas George committed 14 years ago

Commit e90a69e1 parent e6362f35 files · +241 −0Browse files

Unified · Split

5 files
  1. Changelog +1 −0
  2. doc/filters.texi +29 −0
  3. libavfilter/Makefile +1 −0
  4. libavfilter/af_astreamsync.c +209 −0
  5. libavfilter/allfilters.c +1 −0
Changelog+1−0
@@ -12,6 +12,7 @@ version next: · 11 unmodified lines
12- XML output in ffprobe12- XML output in ffprobe
13- asplit audio filter13- asplit audio filter
14- tinterlace video filter14- tinterlace video filter
15- astreamsync audio filter
15 16
16 17
17version 0.9:18version 0.9:
doc/filters.texi+29−0
@@ -237,6 +237,35 @@ For example: · 236 unmodified lines
237will create two separate outputs from the same input, one cropped and237will create two separate outputs from the same input, one cropped and
238one padded.238one padded.
239 239
240@section astreamsync
241
242Forward two audio streams and control the order the buffers are forwarded.
243
244The argument to the filter is an expression deciding which stream should be
245forwarded next: if the result is negative, the first stream is forwarded; if
246the result is positive or zero, the second stream is forwarded. It can use
247the following variables:
248
249@table @var
250@item b1 b2
251number of buffers forwarded so far on each stream
252@item s1 s2
253number of samples forwarded so far on each stream
254@item t1 t2
255current timestamp of each stream
256@end table
257
258The default value is @code{t1-t2}, which means to always forward the stream
259that has a smaller timestamp.
260
261Example: stress-test @code{amerge} by randomly sending buffers on the wrong
262input, while avoiding too much of a desynchronization:
263@example
264amovie=file.ogg [a] ; amovie=file.mp3 [b] ;
265[a] [b] astreamsync=(2*random(1))-1+tanh(5*(t1-t2)) [a2] [b2] ;
266[a2] [b2] amerge
267@end example
268
240@section earwax269@section earwax
241 270
242Make audio easier to listen to on headphones.271Make audio easier to listen to on headphones.
libavfilter/Makefile+1−0
@@ -30,6 +30,7 @@ OBJS-$(CONFIG_ANULL_FILTER) += af_anull.o · 29 unmodified lines
30OBJS-$(CONFIG_ARESAMPLE_FILTER) += af_aresample.o30OBJS-$(CONFIG_ARESAMPLE_FILTER) += af_aresample.o
31OBJS-$(CONFIG_ASHOWINFO_FILTER) += af_ashowinfo.o31OBJS-$(CONFIG_ASHOWINFO_FILTER) += af_ashowinfo.o
32OBJS-$(CONFIG_ASPLIT_FILTER) += af_asplit.o32OBJS-$(CONFIG_ASPLIT_FILTER) += af_asplit.o
33OBJS-$(CONFIG_ASTREAMSYNC_FILTER) += af_astreamsync.o
33OBJS-$(CONFIG_EARWAX_FILTER) += af_earwax.o34OBJS-$(CONFIG_EARWAX_FILTER) += af_earwax.o
34OBJS-$(CONFIG_PAN_FILTER) += af_pan.o35OBJS-$(CONFIG_PAN_FILTER) += af_pan.o
35OBJS-$(CONFIG_VOLUME_FILTER) += af_volume.o36OBJS-$(CONFIG_VOLUME_FILTER) += af_volume.o
libavfilter/af_astreamsync.c+209−0
@@ -0,0 +1,209 @@
1/*
2 * Copyright (c) 2011 Nicolas George <nicolas.george@normalesup.org>
3 *
4 * This file is part of FFmpeg.
5 *
6 * FFmpeg is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * FFmpeg is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with FFmpeg; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21/**
22 * @file
23 * Stream (de)synchronization filter
24 */
25
26#include "libavutil/eval.h"
27#include "avfilter.h"
28#include "internal.h"
29
30#define QUEUE_SIZE 16
31
32static const char * const var_names[] = {
33 "b1", "b2",
34 "s1", "s2",
35 "t1", "t2",
36 NULL
37};
38
39enum var_name {
40 VAR_B1, VAR_B2,
41 VAR_S1, VAR_S2,
42 VAR_T1, VAR_T2,
43 VAR_NB
44};
45
46typedef struct {
47 AVExpr *expr;
48 double var_values[VAR_NB];
49 struct buf_queue {
50 AVFilterBufferRef *buf[QUEUE_SIZE];
51 unsigned tail, nb;
52 /* buf[tail] is the oldest,
53 buf[(tail + nb) % QUEUE_SIZE] is where the next is added */
54 } queue[2];
55 int req[2];
56 int next_out;
57 int eof; /* bitmask, one bit for each stream */
58} AStreamSyncContext;
59
60static const char *default_expr = "t1-t2";
61
62static av_cold int init(AVFilterContext *ctx, const char *args0, void *opaque)
63{
64 AStreamSyncContext *as = ctx->priv;
65 const char *expr = args0 ? args0 : default_expr;
66 int r, i;
67
68 r = av_expr_parse(&as->expr, expr, var_names,
69 NULL, NULL, NULL, NULL, 0, ctx);
70 if (r < 0) {
71 av_log(ctx, AV_LOG_ERROR, "Error in expression \"%s\"\n", expr);
72 return r;
73 }
74 for (i = 0; i < 42; i++)
75 av_expr_eval(as->expr, as->var_values, NULL); /* exercize prng */
76 return 0;
77}
78
79static int query_formats(AVFilterContext *ctx)
80{
81 int i;
82 AVFilterFormats *formats;
83
84 for (i = 0; i < 2; i++) {
85 formats = ctx->inputs[i]->in_formats;
86 avfilter_formats_ref(formats, &ctx->inputs[i]->out_formats);
87 avfilter_formats_ref(formats, &ctx->outputs[i]->in_formats);
88 formats = ctx->inputs[i]->in_packing;
89 avfilter_formats_ref(formats, &ctx->inputs[i]->out_packing);
90 avfilter_formats_ref(formats, &ctx->outputs[i]->in_packing);
91 formats = ctx->inputs[i]->in_chlayouts;
92 avfilter_formats_ref(formats, &ctx->inputs[i]->out_chlayouts);
93 avfilter_formats_ref(formats, &ctx->outputs[i]->in_chlayouts);
94 }
95 return 0;
96}
97
98static int config_output(AVFilterLink *outlink)
99{
100 AVFilterContext *ctx = outlink->src;
101 int id = outlink == ctx->outputs[1];
102 int i;
103
104 outlink->sample_rate = ctx->inputs[id]->sample_rate;
105 outlink->time_base = ctx->inputs[id]->time_base;
106 return 0;
107}
108
109static void send_out(AVFilterContext *ctx, int out_id)
110{
111 AStreamSyncContext *as = ctx->priv;
112 struct buf_queue *queue = &as->queue[out_id];
113 AVFilterBufferRef *buf = queue->buf[queue->tail];
114
115 queue->buf[queue->tail] = NULL;
116 as->var_values[VAR_B1 + out_id]++;
117 as->var_values[VAR_S1 + out_id] += buf->audio->nb_samples;
118 if (buf->pts != AV_NOPTS_VALUE)
119 as->var_values[VAR_T1 + out_id] =
120 av_q2d(ctx->outputs[out_id]->time_base) * buf->pts;
121 as->var_values[VAR_T1 + out_id] += buf->audio->nb_samples /
122 (double)ctx->inputs[out_id]->sample_rate;
123 avfilter_filter_samples(ctx->outputs[out_id], buf);
124 queue->nb--;
125 queue->tail = (queue->tail + 1) % QUEUE_SIZE;
126 if (as->req[out_id])
127 as->req[out_id]--;
128}
129
130static void send_next(AVFilterContext *ctx)
131{
132 AStreamSyncContext *as = ctx->priv;
133 int i;
134
135 while (1) {
136 if (!as->queue[as->next_out].nb)
137 break;
138 send_out(ctx, as->next_out);
139 if (!as->eof)
140 as->next_out = av_expr_eval(as->expr, as->var_values, NULL) >= 0;
141 }
142 for (i = 0; i < 2; i++)
143 if (as->queue[i].nb == QUEUE_SIZE)
144 send_out(ctx, i);
145}
146
147static int request_frame(AVFilterLink *outlink)
148{
149 AVFilterContext *ctx = outlink->src;
150 AStreamSyncContext *as = ctx->priv;
151 int id = outlink == ctx->outputs[1];
152
153 as->req[id]++;
154 while (as->req[id] && !(as->eof & (1 << id))) {
155 if (as->queue[as->next_out].nb) {
156 send_next(ctx);
157 } else {
158 as->eof |= 1 << as->next_out;
159 avfilter_request_frame(ctx->inputs[as->next_out]);
160 if (as->eof & (1 << as->next_out))
161 as->next_out = !as->next_out;
162 }
163 }
164 return 0;
165}
166
167static void filter_samples(AVFilterLink *inlink, AVFilterBufferRef *insamples)
168{
169 AVFilterContext *ctx = inlink->dst;
170 AStreamSyncContext *as = ctx->priv;
171 int id = inlink == ctx->inputs[1];
172
173 as->queue[id].buf[(as->queue[id].tail + as->queue[id].nb++) % QUEUE_SIZE] =
174 insamples;
175 as->eof &= ~(1 << id);
176 send_next(ctx);
177}
178
179AVFilter avfilter_af_astreamsync = {
180 .name = "astreamsync",
181 .description = NULL_IF_CONFIG_SMALL("Copy two streams of audio data "
182 "in a configurable order."),
183 .priv_size = sizeof(AStreamSyncContext),
184 .init = init,
185 .query_formats = query_formats,
186
187 .inputs = (const AVFilterPad[]) {
188 { .name = "in1",
189 .type = AVMEDIA_TYPE_AUDIO,
190 .filter_samples = filter_samples,
191 .min_perms = AV_PERM_READ, },
192 { .name = "in2",
193 .type = AVMEDIA_TYPE_AUDIO,
194 .filter_samples = filter_samples,
195 .min_perms = AV_PERM_READ, },
196 { .name = NULL }
197 },
198 .outputs = (const AVFilterPad[]) {
199 { .name = "out1",
200 .type = AVMEDIA_TYPE_AUDIO,
201 .config_props = config_output,
202 .request_frame = request_frame, },
203 { .name = "out2",
204 .type = AVMEDIA_TYPE_AUDIO,
205 .config_props = config_output,
206 .request_frame = request_frame, },
207 { .name = NULL }
208 },
209};
libavfilter/allfilters.c+1−0
@@ -40,6 +40,7 @@ void avfilter_register_all(void) · 39 unmodified lines
40 REGISTER_FILTER (ARESAMPLE, aresample, af);40 REGISTER_FILTER (ARESAMPLE, aresample, af);
41 REGISTER_FILTER (ASHOWINFO, ashowinfo, af);41 REGISTER_FILTER (ASHOWINFO, ashowinfo, af);
42 REGISTER_FILTER (ASPLIT, asplit, af);42 REGISTER_FILTER (ASPLIT, asplit, af);
43 REGISTER_FILTER (ASTREAMSYNC, astreamsync, af);
43 REGISTER_FILTER (EARWAX, earwax, af);44 REGISTER_FILTER (EARWAX, earwax, af);
44 REGISTER_FILTER (PAN, pan, af);45 REGISTER_FILTER (PAN, pan, af);
45 REGISTER_FILTER (VOLUME, volume, af);46 REGISTER_FILTER (VOLUME, volume, af);

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t