History

lavfi: add amerge audio filter.

Nicolas George committed 15 years ago

Commit 4962edf1 parent e90a69e6 files · +325 −1Browse files

Unified · Split

6 files
  1. Changelog +1 −0
  2. doc/filters.texi +33 −0
  3. libavfilter/Makefile +1 −0
  4. libavfilter/af_amerge.c +288 −0
  5. libavfilter/allfilters.c +1 −0
  6. libavfilter/avfilter.h +1 −1
Changelog+1−0
@@ -13,6 +13,7 @@ version next: · 12 unmodified lines
13- asplit audio filter13- asplit audio filter
14- tinterlace video filter14- tinterlace video filter
15- astreamsync audio filter15- astreamsync audio filter
16- amerge audio filter
16 17
17 18
18version 0.9:19version 0.9:
doc/filters.texi+33−0
@@ -156,6 +156,39 @@ aformat=u8\\,s16:mono:packed · 155 unmodified lines
156aformat=s16:mono\\,stereo:all156aformat=s16:mono\\,stereo:all
157@end example157@end example
158 158
159@section amerge
160
161Merge two audio streams into a single multi-channel stream.
162
163This filter does not need any argument.
164
165If the channel layouts of the inputs are disjoint, and therefore compatible,
166the channel layout of the output will be set accordingly and the channels
167will be reordered as necessary. If the channel layouts of the inputs are not
168disjoint, the output will have all the channels of the first input then all
169the channels of the second input, in that order, and the channel layout of
170the output will be the default value corresponding to the total number of
171channels.
172
173For example, if the first input is in 2.1 (FL+FR+LF) and the second input
174is FC+BL+BR, then the output will be in 5.1, with the channels in the
175following order: a1, a2, b1, a3, b2, b3 (a1 is the first channel of the
176first input, b1 is the first channel of the second input).
177
178On the other hand, if both input are in stereo, the output channels will be
179in the default order: a1, a2, b1, b2, and the channel layout will be
180arbitrarily set to 4.0, which may or may not be the expected value.
181
182Both inputs must have the same sample rate, format and packing.
183
184If inputs do not have the same duration, the output will stop with the
185shortest.
186
187Example: merge two mono files into a stereo stream:
188@example
189amovie=left.wav [l] ; amovie=right.mp3 [r] ; [l] [r] amerge
190@end example
191
159@section anull192@section anull
160 193
161Pass the audio source unchanged to the output.194Pass the audio source unchanged to the output.
libavfilter/Makefile+1−0
@@ -26,6 +26,7 @@ OBJS-$(CONFIG_AVCODEC) += avcodec.o · 25 unmodified lines
26 26
27OBJS-$(CONFIG_ACONVERT_FILTER) += af_aconvert.o27OBJS-$(CONFIG_ACONVERT_FILTER) += af_aconvert.o
28OBJS-$(CONFIG_AFORMAT_FILTER) += af_aformat.o28OBJS-$(CONFIG_AFORMAT_FILTER) += af_aformat.o
29OBJS-$(CONFIG_AMERGE_FILTER) += af_amerge.o
29OBJS-$(CONFIG_ANULL_FILTER) += af_anull.o30OBJS-$(CONFIG_ANULL_FILTER) += af_anull.o
30OBJS-$(CONFIG_ARESAMPLE_FILTER) += af_aresample.o31OBJS-$(CONFIG_ARESAMPLE_FILTER) += af_aresample.o
31OBJS-$(CONFIG_ASHOWINFO_FILTER) += af_ashowinfo.o32OBJS-$(CONFIG_ASHOWINFO_FILTER) += af_ashowinfo.o
libavfilter/af_amerge.c+288−0
@@ -0,0 +1,288 @@
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 * Audio merging filter
24 */
25
26#include "libswresample/swresample.h" // only for SWR_CH_MAX
27#include "avfilter.h"
28#include "internal.h"
29
30#define QUEUE_SIZE 16
31
32typedef struct {
33 int nb_in_ch[2]; /**< number of channels for each input */
34 int route[SWR_CH_MAX]; /**< channels routing, see copy_samples */
35 int bps;
36 struct amerge_queue {
37 AVFilterBufferRef *buf[QUEUE_SIZE];
38 int nb_buf, nb_samples, pos;
39 } queue[2];
40} AMergeContext;
41
42static av_cold void uninit(AVFilterContext *ctx)
43{
44 AMergeContext *am = ctx->priv;
45 int i, j;
46
47 for (i = 0; i < 2; i++)
48 for (j = 0; j < am->queue[i].nb_buf; j++)
49 avfilter_unref_buffer(am->queue[i].buf[j]);
50}
51
52static int query_formats(AVFilterContext *ctx)
53{
54 AMergeContext *am = ctx->priv;
55 int64_t inlayout[2], outlayout;
56 const int packing_fmts[] = { AVFILTER_PACKED, -1 };
57 AVFilterFormats *formats;
58 int i;
59
60 for (i = 0; i < 2; i++) {
61 if (!ctx->inputs[i]->in_chlayouts ||
62 !ctx->inputs[i]->in_chlayouts->format_count) {
63 av_log(ctx, AV_LOG_ERROR,
64 "No channel layout for input %d\n", i + 1);
65 return AVERROR(EINVAL);
66 }
67 inlayout[i] = ctx->inputs[i]->in_chlayouts->formats[0];
68 if (ctx->inputs[i]->in_chlayouts->format_count > 1) {
69 char buf[256];
70 av_get_channel_layout_string(buf, sizeof(buf), 0, inlayout[i]);
71 av_log(ctx, AV_LOG_INFO, "Using \"%s\" for input %d\n", buf, i + 1);
72 }
73 am->nb_in_ch[i] = av_get_channel_layout_nb_channels(inlayout[i]);
74 }
75 if (am->nb_in_ch[0] + am->nb_in_ch[1] > SWR_CH_MAX) {
76 av_log(ctx, AV_LOG_ERROR, "Too many channels (max %d)\n", SWR_CH_MAX);
77 return AVERROR(EINVAL);
78 }
79 if (inlayout[0] & inlayout[1]) {
80 av_log(ctx, AV_LOG_WARNING,
81 "Inputs overlap: output layout will be meaningless\n");
82 for (i = 0; i < am->nb_in_ch[0] + am->nb_in_ch[1]; i++)
83 am->route[i] = i;
84 outlayout = av_get_default_channel_layout(am->nb_in_ch[0] +
85 am->nb_in_ch[1]);
86 if (!outlayout)
87 outlayout = ((int64_t)1 << (am->nb_in_ch[0] + am->nb_in_ch[1])) - 1;
88 } else {
89 int *route[2] = { am->route, am->route + am->nb_in_ch[0] };
90 int c, out_ch_number = 0;
91
92 outlayout = inlayout[0] | inlayout[1];
93 for (c = 0; c < 64; c++)
94 for (i = 0; i < 2; i++)
95 if ((inlayout[i] >> c) & 1)
96 *(route[i]++) = out_ch_number++;
97 }
98 formats = avfilter_make_all_formats(AVMEDIA_TYPE_AUDIO);
99 avfilter_set_common_sample_formats(ctx, formats);
100 formats = avfilter_make_format_list(packing_fmts);
101 avfilter_set_common_packing_formats(ctx, formats);
102 for (i = 0; i < 2; i++) {
103 formats = NULL;
104 avfilter_add_format(&formats, inlayout[i]);
105 avfilter_formats_ref(formats, &ctx->inputs[i]->out_chlayouts);
106 }
107 formats = NULL;
108 avfilter_add_format(&formats, outlayout);
109 avfilter_formats_ref(formats, &ctx->outputs[0]->in_chlayouts);
110 return 0;
111}
112
113static int config_output(AVFilterLink *outlink)
114{
115 AVFilterContext *ctx = outlink->src;
116 AMergeContext *am = ctx->priv;
117 int64_t layout;
118 char name[3][256];
119 int i;
120
121 if (ctx->inputs[0]->sample_rate != ctx->inputs[1]->sample_rate) {
122 av_log(ctx, AV_LOG_ERROR,
123 "Inputs must have the same sample rate "
124 "(%"PRIi64" vs %"PRIi64")\n",
125 ctx->inputs[0]->sample_rate, ctx->inputs[1]->sample_rate);
126 return AVERROR(EINVAL);
127 }
128 am->bps = av_get_bytes_per_sample(ctx->outputs[0]->format);
129 outlink->sample_rate = ctx->inputs[0]->sample_rate;
130 outlink->time_base = ctx->inputs[0]->time_base;
131 for (i = 0; i < 3; i++) {
132 layout = (i < 2 ? ctx->inputs[i] : ctx->outputs[0])->channel_layout;
133 av_get_channel_layout_string(name[i], sizeof(name[i]), -1, layout);
134 }
135 av_log(ctx, AV_LOG_INFO,
136 "in1:%s + in2:%s -> out:%s\n", name[0], name[1], name[2]);
137 return 0;
138}
139
140static int request_frame(AVFilterLink *outlink)
141{
142 AVFilterContext *ctx = outlink->src;
143 AMergeContext *am = ctx->priv;
144 int i;
145
146 for (i = 0; i < 2; i++)
147 if (!am->queue[i].nb_samples)
148 avfilter_request_frame(ctx->inputs[i]);
149 return 0;
150}
151
152/**
153 * Copy samples from two input streams to one output stream.
154 * @param nb_in_ch number of channels in each input stream
155 * @param route routing values;
156 * input channel i goes to output channel route[i];
157 * i < nb_in_ch[0] are the channels from the first output;
158 * i >= nb_in_ch[0] are the channels from the second output
159 * @param ins pointer to the samples of each inputs, in packed format;
160 * will be left at the end of the copied samples
161 * @param outs pointer to the samples of the output, in packet format;
162 * must point to a buffer big enough;
163 * will be left at the end of the copied samples
164 * @param ns number of samples to copy
165 * @param bps bytes per sample
166 */
167static inline void copy_samples(int nb_in_ch[2], int *route, uint8_t *ins[2],
168 uint8_t **outs, int ns, int bps)
169{
170 int *route_cur;
171 int i, c;
172
173 while (ns--) {
174 route_cur = route;
175 for (i = 0; i < 2; i++) {
176 for (c = 0; c < nb_in_ch[i]; c++) {
177 memcpy((*outs) + bps * *(route_cur++), ins[i], bps);
178 ins[i] += bps;
179 }
180 }
181 *outs += (nb_in_ch[0] + nb_in_ch[1]) * bps;
182 }
183}
184
185static void filter_samples(AVFilterLink *inlink, AVFilterBufferRef *insamples)
186{
187 AVFilterContext *ctx = inlink->dst;
188 AMergeContext *am = ctx->priv;
189 int input_number = inlink == ctx->inputs[1];
190 struct amerge_queue *inq = &am->queue[input_number];
191 int nb_samples, ns, i;
192 AVFilterBufferRef *outbuf, **inbuf[2];
193 uint8_t *ins[2], *outs;
194
195 if (inq->nb_buf == QUEUE_SIZE) {
196 av_log(ctx, AV_LOG_ERROR, "Packet queue overflow; dropped\n");
197 avfilter_unref_buffer(insamples);
198 return;
199 }
200 inq->buf[inq->nb_buf++] = avfilter_ref_buffer(insamples, AV_PERM_READ |
201 AV_PERM_PRESERVE);
202 inq->nb_samples += insamples->audio->nb_samples;
203 avfilter_unref_buffer(insamples);
204 if (!am->queue[!input_number].nb_samples)
205 return;
206
207 nb_samples = FFMIN(am->queue[0].nb_samples,
208 am->queue[1].nb_samples);
209 outbuf = avfilter_get_audio_buffer(ctx->outputs[0], AV_PERM_WRITE,
210 nb_samples);
211 outs = outbuf->data[0];
212 for (i = 0; i < 2; i++) {
213 inbuf[i] = am->queue[i].buf;
214 ins[i] = (*inbuf[i])->data[0] +
215 am->queue[i].pos * am->nb_in_ch[i] * am->bps;
216 }
217 while (nb_samples) {
218 ns = nb_samples;
219 for (i = 0; i < 2; i++)
220 ns = FFMIN(ns, (*inbuf[i])->audio->nb_samples - am->queue[i].pos);
221 /* Unroll the most common sample formats: speed +~350% for the loop,
222 +~13% overall (including two common decoders) */
223 switch (am->bps) {
224 case 1:
225 copy_samples(am->nb_in_ch, am->route, ins, &outs, ns, 1);
226 break;
227 case 2:
228 copy_samples(am->nb_in_ch, am->route, ins, &outs, ns, 2);
229 break;
230 case 4:
231 copy_samples(am->nb_in_ch, am->route, ins, &outs, ns, 4);
232 break;
233 default:
234 copy_samples(am->nb_in_ch, am->route, ins, &outs, ns, am->bps);
235 break;
236 }
237
238 nb_samples -= ns;
239 for (i = 0; i < 2; i++) {
240 am->queue[i].nb_samples -= ns;
241 am->queue[i].pos += ns;
242 if (am->queue[i].pos == (*inbuf[i])->audio->nb_samples) {
243 am->queue[i].pos = 0;
244 avfilter_unref_buffer(*inbuf[i]);
245 *inbuf[i] = NULL;
246 inbuf[i]++;
247 ins[i] = *inbuf[i] ? (*inbuf[i])->data[0] : NULL;
248 }
249 }
250 }
251 for (i = 0; i < 2; i++) {
252 int nbufused = inbuf[i] - am->queue[i].buf;
253 if (nbufused) {
254 am->queue[i].nb_buf -= nbufused;
255 memmove(am->queue[i].buf, inbuf[i],
256 am->queue[i].nb_buf * sizeof(**inbuf));
257 }
258 }
259 avfilter_filter_samples(ctx->outputs[0], outbuf);
260}
261
262AVFilter avfilter_af_amerge = {
263 .name = "amerge",
264 .description = NULL_IF_CONFIG_SMALL("Merge two audio streams into "
265 "a single multi-channel stream."),
266 .priv_size = sizeof(AMergeContext),
267 .uninit = uninit,
268 .query_formats = query_formats,
269
270 .inputs = (const AVFilterPad[]) {
271 { .name = "in1",
272 .type = AVMEDIA_TYPE_AUDIO,
273 .filter_samples = filter_samples,
274 .min_perms = AV_PERM_READ, },
275 { .name = "in2",
276 .type = AVMEDIA_TYPE_AUDIO,
277 .filter_samples = filter_samples,
278 .min_perms = AV_PERM_READ, },
279 { .name = NULL }
280 },
281 .outputs = (const AVFilterPad[]) {
282 { .name = "default",
283 .type = AVMEDIA_TYPE_AUDIO,
284 .config_props = config_output,
285 .request_frame = request_frame, },
286 { .name = NULL }
287 },
288};
libavfilter/allfilters.c+1−0
@@ -36,6 +36,7 @@ void avfilter_register_all(void) · 35 unmodified lines
36 36
37 REGISTER_FILTER (ACONVERT, aconvert, af);37 REGISTER_FILTER (ACONVERT, aconvert, af);
38 REGISTER_FILTER (AFORMAT, aformat, af);38 REGISTER_FILTER (AFORMAT, aformat, af);
39 REGISTER_FILTER (AMERGE, amerge, af);
39 REGISTER_FILTER (ANULL, anull, af);40 REGISTER_FILTER (ANULL, anull, af);
40 REGISTER_FILTER (ARESAMPLE, aresample, af);41 REGISTER_FILTER (ARESAMPLE, aresample, af);
41 REGISTER_FILTER (ASHOWINFO, ashowinfo, af);42 REGISTER_FILTER (ASHOWINFO, ashowinfo, af);
libavfilter/avfilter.h+1−1
@@ -30,7 +30,7 @@ · 29 unmodified lines
30#include "libavcodec/avcodec.h"30#include "libavcodec/avcodec.h"
31 31
32#define LIBAVFILTER_VERSION_MAJOR 232#define LIBAVFILTER_VERSION_MAJOR 2
33#define LIBAVFILTER_VERSION_MINOR 5633#define LIBAVFILTER_VERSION_MINOR 57
34#define LIBAVFILTER_VERSION_MICRO 10034#define LIBAVFILTER_VERSION_MICRO 100
35 35
36#define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \36#define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \

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