FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavfilter/af_channelsplit.c
Date: 2024-11-20 23:03:26
Exec Total Coverage
Lines: 86 115 74.8%
Functions: 5 5 100.0%
Branches: 40 68 58.8%

Line Branch Exec Source
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 #include "libavutil/avassert.h"
26 #include "libavutil/attributes.h"
27 #include "libavutil/channel_layout.h"
28 #include "libavutil/internal.h"
29 #include "libavutil/mem.h"
30 #include "libavutil/opt.h"
31
32 #include "audio.h"
33 #include "avfilter.h"
34 #include "filters.h"
35 #include "formats.h"
36
37 typedef struct ChannelSplitContext {
38 const AVClass *class;
39
40 AVChannelLayout channel_layout;
41 char *channels_str;
42
43 int *map;
44 } ChannelSplitContext;
45
46 #define OFFSET(x) offsetof(ChannelSplitContext, x)
47 #define A AV_OPT_FLAG_AUDIO_PARAM
48 #define F AV_OPT_FLAG_FILTERING_PARAM
49 static const AVOption channelsplit_options[] = {
50 { "channel_layout", "Input channel layout.", OFFSET(channel_layout), AV_OPT_TYPE_CHLAYOUT, { .str = "stereo" }, .flags = A|F },
51 { "channels", "Channels to extract.", OFFSET(channels_str), AV_OPT_TYPE_STRING, { .str = "all" }, .flags = A|F },
52 { NULL }
53 };
54
55 AVFILTER_DEFINE_CLASS(channelsplit);
56
57 4 static av_cold int init(AVFilterContext *ctx)
58 {
59 4 ChannelSplitContext *s = ctx->priv;
60 4 AVChannelLayout channel_layout = { 0 };
61 4 int all = 0, ret = 0, i;
62
63
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 if (!strcmp(s->channels_str, "all")) {
64
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
4 if ((ret = av_channel_layout_copy(&channel_layout, &s->channel_layout)) < 0)
65 goto fail;
66 4 all = 1;
67 } else {
68 if ((ret = av_channel_layout_from_string(&channel_layout, s->channels_str)) < 0)
69 goto fail;
70 }
71
72 4 s->map = av_calloc(channel_layout.nb_channels, sizeof(*s->map));
73
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (!s->map)
74 return AVERROR(ENOMEM);
75
76
2/2
✓ Branch 0 taken 136 times.
✓ Branch 1 taken 4 times.
140 for (i = 0; i < channel_layout.nb_channels; i++) {
77 136 enum AVChannel channel = av_channel_layout_channel_from_index(&channel_layout, i);
78 char buf[64];
79 136 AVFilterPad pad = { .flags = AVFILTERPAD_FLAG_FREE_NAME };
80
81 136 av_channel_name(buf, sizeof(buf), channel);
82 136 pad.type = AVMEDIA_TYPE_AUDIO;
83 136 pad.name = av_strdup(buf);
84
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 136 times.
136 if (!pad.name) {
85 ret = AVERROR(ENOMEM);
86 goto fail;
87 }
88
89
1/2
✓ Branch 0 taken 136 times.
✗ Branch 1 not taken.
136 if (all) {
90 136 s->map[i] = i;
91 } else {
92 char buf[128];
93 av_channel_layout_describe(&s->channel_layout, buf, sizeof(buf));
94 if ((ret = av_channel_layout_index_from_channel(&s->channel_layout, channel)) < 0) {
95 av_log(ctx, AV_LOG_ERROR, "Channel name '%s' not present in channel layout '%s'.\n",
96 pad.name, buf);
97 av_freep(&pad.name);
98 goto fail;
99 }
100
101 s->map[i] = ret;
102 }
103
104
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 136 times.
136 if ((ret = ff_append_outpad(ctx, &pad)) < 0)
105 goto fail;
106 }
107
108 4 fail:
109 4 av_channel_layout_uninit(&channel_layout);
110 4 return ret;
111 }
112
113 4 static av_cold void uninit(AVFilterContext *ctx)
114 {
115 4 ChannelSplitContext *s = ctx->priv;
116
117 4 av_channel_layout_uninit(&s->channel_layout);
118 4 av_freep(&s->map);
119 4 }
120
121 2 static int query_formats(const AVFilterContext *ctx,
122 AVFilterFormatsConfig **cfg_in,
123 AVFilterFormatsConfig **cfg_out)
124 {
125 2 ChannelSplitContext *s = ctx->priv;
126 2 AVFilterChannelLayouts *in_layouts = NULL;
127 int i, ret;
128
129 2 ret = ff_set_common_formats2(ctx, cfg_in, cfg_out, ff_planar_sample_fmts());
130
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (ret < 0)
131 return ret;
132
133
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
4 if ((ret = ff_add_channel_layout(&in_layouts, &s->channel_layout)) < 0 ||
134 2 (ret = ff_channel_layouts_ref(in_layouts, &cfg_in[0]->channel_layouts)) < 0)
135 return ret;
136
137
2/2
✓ Branch 0 taken 68 times.
✓ Branch 1 taken 2 times.
70 for (i = 0; i < ctx->nb_outputs; i++) {
138 68 AVChannelLayout channel_layout = { 0 };
139 68 AVFilterChannelLayouts *out_layouts = NULL;
140 68 enum AVChannel channel = av_channel_layout_channel_from_index(&s->channel_layout, s->map[i]);
141
142 68 channel_layout.u.map = av_mallocz(sizeof(*channel_layout.u.map));
143
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 68 times.
68 if (!channel_layout.u.map)
144 return AVERROR(ENOMEM);
145
146 68 channel_layout.u.map[0].id = channel;
147 68 channel_layout.nb_channels = 1;
148 68 channel_layout.order = AV_CHANNEL_ORDER_CUSTOM;
149
150 68 ret = av_channel_layout_retype(&channel_layout, 0, AV_CHANNEL_LAYOUT_RETYPE_FLAG_CANONICAL);
151
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 68 times.
68 if (ret < 0) {
152 av_channel_layout_uninit(&channel_layout);
153 return ret;
154 }
155
156 68 ret = ff_add_channel_layout(&out_layouts, &channel_layout);
157 68 av_channel_layout_uninit(&channel_layout);
158
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 68 times.
68 if (ret < 0)
159 return ret;
160
161 68 ret = ff_channel_layouts_ref(out_layouts, &cfg_out[i]->channel_layouts);
162
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 68 times.
68 if (ret < 0)
163 return ret;
164 }
165
166 2 return 0;
167 }
168
169 584 static int filter_frame(AVFilterLink *outlink, AVFrame *buf)
170 {
171 AVFrame *buf_out;
172 584 AVFilterContext *ctx = outlink->src;
173 584 ChannelSplitContext *s = ctx->priv;
174 584 const int i = FF_OUTLINK_IDX(outlink);
175 int ret;
176
177 584 buf_out = av_frame_clone(buf);
178
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 584 times.
584 if (!buf_out)
179 return AVERROR(ENOMEM);
180
181 584 buf_out->data[0] = buf_out->extended_data[0] = buf_out->extended_data[s->map[i]];
182
183 584 av_channel_layout_uninit(&buf_out->ch_layout);
184 584 ret = av_channel_layout_copy(&buf_out->ch_layout, &outlink->ch_layout);
185
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 584 times.
584 if (ret < 0) {
186 av_frame_free(&buf_out);
187 return ret;
188 }
189
190 584 return ff_filter_frame(ctx->outputs[i], buf_out);
191 }
192
193 523 static int activate(AVFilterContext *ctx)
194 {
195 523 AVFilterLink *inlink = ctx->inputs[0];
196 int status, ret;
197 AVFrame *in;
198 int64_t pts;
199
200
2/2
✓ Branch 0 taken 1302 times.
✓ Branch 1 taken 523 times.
1825 for (int i = 0; i < ctx->nb_outputs; i++) {
201
1/4
✗ Branch 1 not taken.
✓ Branch 2 taken 1302 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
1302 FF_FILTER_FORWARD_STATUS_BACK_ALL(ctx->outputs[i], ctx);
202 }
203
204 523 ret = ff_inlink_consume_frame(inlink, &in);
205
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 523 times.
523 if (ret < 0)
206 return ret;
207
2/2
✓ Branch 0 taken 260 times.
✓ Branch 1 taken 263 times.
523 if (ret > 0) {
208
2/2
✓ Branch 0 taken 584 times.
✓ Branch 1 taken 260 times.
844 for (int i = 0; i < ctx->nb_outputs; i++) {
209
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 584 times.
584 if (ff_outlink_get_status(ctx->outputs[i]))
210 continue;
211
212 584 ret = filter_frame(ctx->outputs[i], in);
213
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 584 times.
584 if (ret < 0)
214 break;
215 }
216
217 260 av_frame_free(&in);
218
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 260 times.
260 if (ret < 0)
219 return ret;
220 }
221
222
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 521 times.
523 if (ff_inlink_acknowledge_status(inlink, &status, &pts)) {
223
2/2
✓ Branch 0 taken 68 times.
✓ Branch 1 taken 2 times.
70 for (int i = 0; i < ctx->nb_outputs; i++) {
224
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 68 times.
68 if (ff_outlink_get_status(ctx->outputs[i]))
225 continue;
226 68 ff_outlink_set_status(ctx->outputs[i], status, pts);
227 }
228 2 return 0;
229 }
230
231
2/2
✓ Branch 0 taken 845 times.
✓ Branch 1 taken 260 times.
1105 for (int i = 0; i < ctx->nb_outputs; i++) {
232
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 845 times.
845 if (ff_outlink_get_status(ctx->outputs[i]))
233 continue;
234
235
2/2
✓ Branch 1 taken 261 times.
✓ Branch 2 taken 584 times.
845 if (ff_outlink_frame_wanted(ctx->outputs[i])) {
236 261 ff_inlink_request_frame(inlink);
237 261 return 0;
238 }
239 }
240
241 260 return FFERROR_NOT_READY;
242 }
243
244 const AVFilter ff_af_channelsplit = {
245 .name = "channelsplit",
246 .description = NULL_IF_CONFIG_SMALL("Split audio into per-channel streams."),
247 .priv_size = sizeof(ChannelSplitContext),
248 .priv_class = &channelsplit_class,
249 .init = init,
250 .activate = activate,
251 .uninit = uninit,
252 FILTER_INPUTS(ff_audio_default_filterpad),
253 .outputs = NULL,
254 FILTER_QUERY_FUNC2(query_formats),
255 .flags = AVFILTER_FLAG_DYNAMIC_OUTPUTS,
256 };
257