• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * This file is part of FFmpeg.
3  *
4  * FFmpeg is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * FFmpeg is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with FFmpeg; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17  */
18 
19 /**
20  * @file
21  * Channel split filter
22  *
23  * Split an audio stream into per-channel streams.
24  */
25 
26 #include "libavutil/attributes.h"
27 #include "libavutil/channel_layout.h"
28 #include "libavutil/internal.h"
29 #include "libavutil/opt.h"
30 
31 #include "audio.h"
32 #include "avfilter.h"
33 #include "filters.h"
34 #include "formats.h"
35 #include "internal.h"
36 
37 #define MAX_CH 64
38 
39 typedef struct ChannelSplitContext {
40     const AVClass *class;
41 
42     AVChannelLayout channel_layout;
43     char    *channel_layout_str;
44     char    *channels_str;
45 
46     int      map[64];
47 } ChannelSplitContext;
48 
49 #define OFFSET(x) offsetof(ChannelSplitContext, x)
50 #define A AV_OPT_FLAG_AUDIO_PARAM
51 #define F AV_OPT_FLAG_FILTERING_PARAM
52 static const AVOption channelsplit_options[] = {
53     { "channel_layout", "Input channel layout.", OFFSET(channel_layout_str), AV_OPT_TYPE_STRING, { .str = "stereo" }, .flags = A|F },
54     { "channels",        "Channels to extract.", OFFSET(channels_str),       AV_OPT_TYPE_STRING, { .str = "all" },    .flags = A|F },
55     { NULL }
56 };
57 
58 AVFILTER_DEFINE_CLASS(channelsplit);
59 
init(AVFilterContext * ctx)60 static av_cold int init(AVFilterContext *ctx)
61 {
62     ChannelSplitContext *s = ctx->priv;
63     AVChannelLayout channel_layout = { 0 };
64     int all = 0, ret = 0, i;
65 
66     if ((ret = av_channel_layout_from_string(&s->channel_layout, s->channel_layout_str)) < 0) {
67         av_log(ctx, AV_LOG_ERROR, "Error parsing channel layout '%s'.\n",
68                s->channel_layout_str);
69         ret = AVERROR(EINVAL);
70         goto fail;
71     }
72 
73     if (!strcmp(s->channels_str, "all")) {
74         if ((ret = av_channel_layout_copy(&channel_layout, &s->channel_layout)) < 0)
75             goto fail;
76         all = 1;
77     } else {
78         if ((ret = av_channel_layout_from_string(&channel_layout, s->channels_str)) < 0)
79             goto fail;
80     }
81 
82     if (channel_layout.nb_channels > MAX_CH) {
83         av_log(ctx, AV_LOG_ERROR, "Too many channels\n");
84         goto fail;
85     }
86 
87     for (i = 0; i < channel_layout.nb_channels; i++) {
88         enum AVChannel channel = av_channel_layout_channel_from_index(&channel_layout, i);
89         char buf[64];
90         AVFilterPad pad = { .flags = AVFILTERPAD_FLAG_FREE_NAME };
91 
92         av_channel_name(buf, sizeof(buf), channel);
93         pad.type = AVMEDIA_TYPE_AUDIO;
94         pad.name = av_strdup(buf);
95         if (!pad.name) {
96             ret = AVERROR(ENOMEM);
97             goto fail;
98         }
99 
100         if (all) {
101             s->map[i] = i;
102         } else {
103             if ((ret = av_channel_layout_index_from_channel(&s->channel_layout, channel)) < 0) {
104                 av_log(ctx, AV_LOG_ERROR, "Channel name '%s' not present in channel layout '%s'.\n",
105                        pad.name, s->channel_layout_str);
106                 av_freep(&pad.name);
107                 goto fail;
108             }
109 
110             s->map[i] = ret;
111         }
112 
113         if ((ret = ff_append_outpad(ctx, &pad)) < 0)
114             goto fail;
115     }
116 
117 fail:
118     av_channel_layout_uninit(&channel_layout);
119     return ret;
120 }
121 
uninit(AVFilterContext * ctx)122 static av_cold void uninit(AVFilterContext *ctx)
123 {
124     ChannelSplitContext *s = ctx->priv;
125 
126     av_channel_layout_uninit(&s->channel_layout);
127 }
128 
query_formats(AVFilterContext * ctx)129 static int query_formats(AVFilterContext *ctx)
130 {
131     ChannelSplitContext *s = ctx->priv;
132     AVFilterChannelLayouts *in_layouts = NULL;
133     int i, ret;
134 
135     if ((ret = ff_set_common_formats(ctx, ff_planar_sample_fmts())) < 0 ||
136         (ret = ff_set_common_all_samplerates(ctx)) < 0)
137         return ret;
138 
139     if ((ret = ff_add_channel_layout(&in_layouts, &s->channel_layout)) < 0 ||
140         (ret = ff_channel_layouts_ref(in_layouts, &ctx->inputs[0]->outcfg.channel_layouts)) < 0)
141         return ret;
142 
143     for (i = 0; i < ctx->nb_outputs; i++) {
144         AVChannelLayout channel_layout = { 0 };
145         AVFilterChannelLayouts *out_layouts = NULL;
146         enum AVChannel channel = av_channel_layout_channel_from_index(&s->channel_layout, s->map[i]);
147 
148         if ((ret = av_channel_layout_from_mask(&channel_layout, 1ULL << channel)) < 0 ||
149             (ret = ff_add_channel_layout(&out_layouts, &channel_layout)) < 0 ||
150             (ret = ff_channel_layouts_ref(out_layouts, &ctx->outputs[i]->incfg.channel_layouts)) < 0)
151             return ret;
152     }
153 
154     return 0;
155 }
156 
filter_frame(AVFilterLink * outlink,AVFrame * buf)157 static int filter_frame(AVFilterLink *outlink, AVFrame *buf)
158 {
159     AVFilterContext *ctx = outlink->src;
160     ChannelSplitContext *s = ctx->priv;
161     const int i = FF_OUTLINK_IDX(outlink);
162     enum AVChannel channel = av_channel_layout_channel_from_index(&buf->ch_layout, s->map[i]);
163     int ret;
164 
165     AVFrame *buf_out = av_frame_clone(buf);
166     if (!buf_out)
167         return AVERROR(ENOMEM);
168 
169     buf_out->data[0] = buf_out->extended_data[0] = buf_out->extended_data[s->map[i]];
170     ret = av_channel_layout_from_mask(&buf_out->ch_layout, 1ULL << channel);
171     if (ret < 0)
172         return ret;
173 #if FF_API_OLD_CHANNEL_LAYOUT
174 FF_DISABLE_DEPRECATION_WARNINGS
175     buf_out->channel_layout =
176         av_channel_layout_extract_channel(buf->channel_layout, s->map[i]);
177     buf_out->channels = 1;
178 FF_ENABLE_DEPRECATION_WARNINGS
179 #endif
180 
181     return ff_filter_frame(ctx->outputs[i], buf_out);
182 }
183 
activate(AVFilterContext * ctx)184 static int activate(AVFilterContext *ctx)
185 {
186     AVFilterLink *inlink = ctx->inputs[0];
187     int status, ret;
188     AVFrame *in;
189     int64_t pts;
190 
191     for (int i = 0; i < ctx->nb_outputs; i++) {
192         FF_FILTER_FORWARD_STATUS_BACK_ALL(ctx->outputs[i], ctx);
193     }
194 
195     ret = ff_inlink_consume_frame(inlink, &in);
196     if (ret < 0)
197         return ret;
198     if (ret > 0) {
199         for (int i = 0; i < ctx->nb_outputs; i++) {
200             if (ff_outlink_get_status(ctx->outputs[i]))
201                 continue;
202 
203             ret = filter_frame(ctx->outputs[i], in);
204             if (ret < 0)
205                 break;
206         }
207 
208         av_frame_free(&in);
209         if (ret < 0)
210             return ret;
211     }
212 
213     if (ff_inlink_acknowledge_status(inlink, &status, &pts)) {
214         for (int i = 0; i < ctx->nb_outputs; i++) {
215             if (ff_outlink_get_status(ctx->outputs[i]))
216                 continue;
217             ff_outlink_set_status(ctx->outputs[i], status, pts);
218         }
219         return 0;
220     }
221 
222     for (int i = 0; i < ctx->nb_outputs; i++) {
223         if (ff_outlink_get_status(ctx->outputs[i]))
224             continue;
225 
226         if (ff_outlink_frame_wanted(ctx->outputs[i])) {
227             ff_inlink_request_frame(inlink);
228             return 0;
229         }
230     }
231 
232     return FFERROR_NOT_READY;
233 }
234 
235 static const AVFilterPad avfilter_af_channelsplit_inputs[] = {
236     {
237         .name         = "default",
238         .type         = AVMEDIA_TYPE_AUDIO,
239     },
240 };
241 
242 const AVFilter ff_af_channelsplit = {
243     .name           = "channelsplit",
244     .description    = NULL_IF_CONFIG_SMALL("Split audio into per-channel streams."),
245     .priv_size      = sizeof(ChannelSplitContext),
246     .priv_class     = &channelsplit_class,
247     .init           = init,
248     .activate       = activate,
249     .uninit         = uninit,
250     FILTER_INPUTS(avfilter_af_channelsplit_inputs),
251     .outputs        = NULL,
252     FILTER_QUERY_FUNC(query_formats),
253     .flags          = AVFILTER_FLAG_DYNAMIC_OUTPUTS,
254 };
255