2 * Copyright (c) 2011 Nicolas George <nicolas.george@normalesup.org>
4 * This file is part of FFmpeg.
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.
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 Lesser General Public License for more details.
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
23 * Stream (de)synchronization filter
26 #include "libavutil/eval.h"
27 #include "libavutil/opt.h"
34 static const char * const var_names
[] = {
52 double var_values
[VAR_NB
];
54 AVFrame
*buf
[QUEUE_SIZE
];
56 /* buf[tail] is the oldest,
57 buf[(tail + nb) % QUEUE_SIZE] is where the next is added */
61 int eof
; /* bitmask, one bit for each stream */
64 #define OFFSET(x) offsetof(AStreamSyncContext, x)
65 #define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
66 static const AVOption astreamsync_options
[] = {
67 { "expr", "set stream selection expression", OFFSET(expr_str
), AV_OPT_TYPE_STRING
, { .str
= "t1-t2" }, .flags
= FLAGS
},
68 { "e", "set stream selection expression", OFFSET(expr_str
), AV_OPT_TYPE_STRING
, { .str
= "t1-t2" }, .flags
= FLAGS
},
72 AVFILTER_DEFINE_CLASS(astreamsync
);
74 static av_cold
int init(AVFilterContext
*ctx
)
76 AStreamSyncContext
*as
= ctx
->priv
;
79 r
= av_expr_parse(&as
->expr
, as
->expr_str
, var_names
,
80 NULL
, NULL
, NULL
, NULL
, 0, ctx
);
82 av_log(ctx
, AV_LOG_ERROR
, "Error in expression \"%s\"\n", as
->expr_str
);
85 for (i
= 0; i
< 42; i
++)
86 av_expr_eval(as
->expr
, as
->var_values
, NULL
); /* exercize prng */
90 static int query_formats(AVFilterContext
*ctx
)
93 AVFilterFormats
*formats
, *rates
;
94 AVFilterChannelLayouts
*layouts
;
96 for (i
= 0; i
< 2; i
++) {
97 formats
= ctx
->inputs
[i
]->in_formats
;
98 ff_formats_ref(formats
, &ctx
->inputs
[i
]->out_formats
);
99 ff_formats_ref(formats
, &ctx
->outputs
[i
]->in_formats
);
100 rates
= ff_all_samplerates();
101 ff_formats_ref(rates
, &ctx
->inputs
[i
]->out_samplerates
);
102 ff_formats_ref(rates
, &ctx
->outputs
[i
]->in_samplerates
);
103 layouts
= ctx
->inputs
[i
]->in_channel_layouts
;
104 ff_channel_layouts_ref(layouts
, &ctx
->inputs
[i
]->out_channel_layouts
);
105 ff_channel_layouts_ref(layouts
, &ctx
->outputs
[i
]->in_channel_layouts
);
110 static int config_output(AVFilterLink
*outlink
)
112 AVFilterContext
*ctx
= outlink
->src
;
113 int id
= outlink
== ctx
->outputs
[1];
115 outlink
->sample_rate
= ctx
->inputs
[id
]->sample_rate
;
116 outlink
->time_base
= ctx
->inputs
[id
]->time_base
;
120 static int send_out(AVFilterContext
*ctx
, int out_id
)
122 AStreamSyncContext
*as
= ctx
->priv
;
123 struct buf_queue
*queue
= &as
->queue
[out_id
];
124 AVFrame
*buf
= queue
->buf
[queue
->tail
];
127 queue
->buf
[queue
->tail
] = NULL
;
128 as
->var_values
[VAR_B1
+ out_id
]++;
129 as
->var_values
[VAR_S1
+ out_id
] += buf
->nb_samples
;
130 if (buf
->pts
!= AV_NOPTS_VALUE
)
131 as
->var_values
[VAR_T1
+ out_id
] =
132 av_q2d(ctx
->outputs
[out_id
]->time_base
) * buf
->pts
;
133 as
->var_values
[VAR_T1
+ out_id
] += buf
->nb_samples
/
134 (double)ctx
->inputs
[out_id
]->sample_rate
;
135 ret
= ff_filter_frame(ctx
->outputs
[out_id
], buf
);
137 queue
->tail
= (queue
->tail
+ 1) % QUEUE_SIZE
;
143 static void send_next(AVFilterContext
*ctx
)
145 AStreamSyncContext
*as
= ctx
->priv
;
149 if (!as
->queue
[as
->next_out
].nb
)
151 send_out(ctx
, as
->next_out
);
153 as
->next_out
= av_expr_eval(as
->expr
, as
->var_values
, NULL
) >= 0;
155 for (i
= 0; i
< 2; i
++)
156 if (as
->queue
[i
].nb
== QUEUE_SIZE
)
160 static int request_frame(AVFilterLink
*outlink
)
162 AVFilterContext
*ctx
= outlink
->src
;
163 AStreamSyncContext
*as
= ctx
->priv
;
164 int id
= outlink
== ctx
->outputs
[1];
167 while (as
->req
[id
] && !(as
->eof
& (1 << id
))) {
168 if (as
->queue
[as
->next_out
].nb
) {
171 as
->eof
|= 1 << as
->next_out
;
172 ff_request_frame(ctx
->inputs
[as
->next_out
]);
173 if (as
->eof
& (1 << as
->next_out
))
174 as
->next_out
= !as
->next_out
;
180 static int filter_frame(AVFilterLink
*inlink
, AVFrame
*insamples
)
182 AVFilterContext
*ctx
= inlink
->dst
;
183 AStreamSyncContext
*as
= ctx
->priv
;
184 int id
= inlink
== ctx
->inputs
[1];
186 as
->queue
[id
].buf
[(as
->queue
[id
].tail
+ as
->queue
[id
].nb
++) % QUEUE_SIZE
] =
188 as
->eof
&= ~(1 << id
);
193 static av_cold
void uninit(AVFilterContext
*ctx
)
195 AStreamSyncContext
*as
= ctx
->priv
;
197 av_expr_free(as
->expr
);
201 static const AVFilterPad astreamsync_inputs
[] = {
204 .type
= AVMEDIA_TYPE_AUDIO
,
205 .filter_frame
= filter_frame
,
208 .type
= AVMEDIA_TYPE_AUDIO
,
209 .filter_frame
= filter_frame
,
214 static const AVFilterPad astreamsync_outputs
[] = {
217 .type
= AVMEDIA_TYPE_AUDIO
,
218 .config_props
= config_output
,
219 .request_frame
= request_frame
,
222 .type
= AVMEDIA_TYPE_AUDIO
,
223 .config_props
= config_output
,
224 .request_frame
= request_frame
,
229 AVFilter ff_af_astreamsync
= {
230 .name
= "astreamsync",
231 .description
= NULL_IF_CONFIG_SMALL("Copy two streams of audio data "
232 "in a configurable order."),
233 .priv_size
= sizeof(AStreamSyncContext
),
236 .query_formats
= query_formats
,
237 .inputs
= astreamsync_inputs
,
238 .outputs
= astreamsync_outputs
,
239 .priv_class
= &astreamsync_class
,