FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavcodec/bsf/filter_units.c
Date: 2024-11-20 23:03:26
Exec Total Coverage
Lines: 44 109 40.4%
Functions: 3 4 75.0%
Branches: 19 70 27.1%

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 #include <stdbool.h>
20 #include <stdlib.h>
21
22 #include "libavutil/mem.h"
23 #include "libavutil/opt.h"
24
25 #include "bsf.h"
26 #include "bsf_internal.h"
27 #include "cbs.h"
28
29
30 typedef struct FilterUnitsContext {
31 const AVClass *class;
32
33 CodedBitstreamContext *cbc;
34 CodedBitstreamFragment fragment;
35
36 const char *pass_types;
37 const char *remove_types;
38 enum AVDiscard discard;
39 int discard_flags;
40
41 enum {
42 NOOP,
43 PASS,
44 REMOVE,
45 } mode;
46 CodedBitstreamUnitType *type_list;
47 int nb_types;
48 bool passthrough;
49 } FilterUnitsContext;
50
51
52 static int filter_units_make_type_list(const char *list_string,
53 CodedBitstreamUnitType **type_list,
54 int *nb_types)
55 {
56 CodedBitstreamUnitType *list = NULL;
57 int pass, count;
58
59 for (pass = 1; pass <= 2; pass++) {
60 long value, range_start, range_end;
61 const char *str;
62 char *value_end;
63
64 count = 0;
65 for (str = list_string; *str;) {
66 value = strtol(str, &value_end, 0);
67 if (str == value_end)
68 goto invalid;
69 str = (const char *)value_end;
70 if (*str == '-') {
71 ++str;
72 range_start = value;
73 range_end = strtol(str, &value_end, 0);
74 if (str == value_end)
75 goto invalid;
76
77 for (value = range_start; value < range_end; value++) {
78 if (pass == 2)
79 list[count] = value;
80 ++count;
81 }
82 } else {
83 if (pass == 2)
84 list[count] = value;
85 ++count;
86 }
87 if (*str == '|')
88 ++str;
89 }
90 if (pass == 1) {
91 list = av_malloc_array(count, sizeof(*list));
92 if (!list)
93 return AVERROR(ENOMEM);
94 }
95 }
96
97 *type_list = list;
98 *nb_types = count;
99 return 0;
100
101 invalid:
102 av_freep(&list);
103 return AVERROR(EINVAL);
104 }
105
106 838 static int filter_units_filter(AVBSFContext *bsf, AVPacket *pkt)
107 {
108 838 FilterUnitsContext *ctx = bsf->priv_data;
109 838 CodedBitstreamFragment *frag = &ctx->fragment;
110 int err, i, j;
111
112 838 err = ff_bsf_get_packet_ref(bsf, pkt);
113
2/2
✓ Branch 0 taken 142 times.
✓ Branch 1 taken 696 times.
838 if (err < 0)
114 142 return err;
115
116
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 696 times.
696 if (ctx->passthrough)
117 return 0;
118
119 696 err = ff_cbs_read_packet(ctx->cbc, frag, pkt);
120
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 696 times.
696 if (err < 0) {
121 av_log(bsf, AV_LOG_ERROR, "Failed to read packet.\n");
122 goto fail;
123 }
124
125 696 ff_cbs_discard_units(ctx->cbc, frag, ctx->discard, ctx->discard_flags);
126
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 696 times.
696 if (ctx->mode != NOOP) {
127 for (i = frag->nb_units - 1; i >= 0; i--) {
128 for (j = 0; j < ctx->nb_types; j++) {
129 if (frag->units[i].type == ctx->type_list[j])
130 break;
131 }
132 if (ctx->mode == REMOVE ? j < ctx->nb_types
133 : j >= ctx->nb_types)
134 ff_cbs_delete_unit(frag, i);
135 }
136 }
137
138
2/2
✓ Branch 0 taken 562 times.
✓ Branch 1 taken 134 times.
696 if (frag->nb_units == 0) {
139 // Don't return packets with nothing in them.
140 562 err = AVERROR(EAGAIN);
141 562 goto fail;
142 }
143
144 134 err = ff_cbs_write_packet(ctx->cbc, pkt, frag);
145
1/2
✓ Branch 0 taken 134 times.
✗ Branch 1 not taken.
134 if (err < 0) {
146 av_log(bsf, AV_LOG_ERROR, "Failed to write packet.\n");
147 goto fail;
148 }
149
150 134 fail:
151
2/2
✓ Branch 0 taken 562 times.
✓ Branch 1 taken 134 times.
696 if (err < 0)
152 562 av_packet_unref(pkt);
153 696 ff_cbs_fragment_reset(frag);
154
155 696 return err;
156 }
157
158 8 static int filter_units_init(AVBSFContext *bsf)
159 {
160 8 FilterUnitsContext *ctx = bsf->priv_data;
161 int err;
162
163
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
8 if (ctx->pass_types && ctx->remove_types) {
164 av_log(bsf, AV_LOG_ERROR, "Exactly one of pass_types or "
165 "remove_types is required.\n");
166 return AVERROR(EINVAL);
167 }
168
169
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (ctx->pass_types) {
170 ctx->mode = PASS;
171 err = filter_units_make_type_list(ctx->pass_types,
172 &ctx->type_list, &ctx->nb_types);
173 if (err < 0) {
174 av_log(bsf, AV_LOG_ERROR, "Failed to parse pass_types.\n");
175 return err;
176 }
177
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 } else if (ctx->remove_types) {
178 ctx->mode = REMOVE;
179 err = filter_units_make_type_list(ctx->remove_types,
180 &ctx->type_list, &ctx->nb_types);
181 if (err < 0) {
182 av_log(bsf, AV_LOG_ERROR, "Failed to parse remove_types.\n");
183 return err;
184 }
185
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 } else if (ctx->discard == AVDISCARD_NONE) {
186 ctx->passthrough = true;
187 return 0;
188 }
189
190 8 err = ff_cbs_init(&ctx->cbc, bsf->par_in->codec_id, bsf);
191
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (err < 0)
192 return err;
193
194
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (ctx->discard == AVDISCARD_NONE) {
195 // Don't actually decompose anything, we only want the unit data.
196 ctx->cbc->decompose_unit_types = ctx->type_list;
197 ctx->cbc->nb_decompose_unit_types = 0;
198 }
199
200
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 if (bsf->par_in->extradata) {
201 8 CodedBitstreamFragment *frag = &ctx->fragment;
202
203 8 err = ff_cbs_read_extradata(ctx->cbc, frag, bsf->par_in);
204
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (err < 0) {
205 av_log(bsf, AV_LOG_ERROR, "Failed to read extradata.\n");
206 } else {
207 8 err = ff_cbs_write_extradata(ctx->cbc, bsf->par_out, frag);
208
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (err < 0)
209 av_log(bsf, AV_LOG_ERROR, "Failed to write extradata.\n");
210 }
211
212 8 ff_cbs_fragment_reset(frag);
213 }
214
215 8 return err;
216 }
217
218 8 static void filter_units_close(AVBSFContext *bsf)
219 {
220 8 FilterUnitsContext *ctx = bsf->priv_data;
221
222 8 av_freep(&ctx->type_list);
223
224 8 ff_cbs_fragment_free(&ctx->fragment);
225 8 ff_cbs_close(&ctx->cbc);
226 8 }
227
228 #define OFFSET(x) offsetof(FilterUnitsContext, x)
229 #define FLAGS (AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_BSF_PARAM)
230 static const AVOption filter_units_options[] = {
231 { "pass_types", "List of unit types to pass through the filter.",
232 OFFSET(pass_types), AV_OPT_TYPE_STRING,
233 { .str = NULL }, .flags = FLAGS },
234 { "remove_types", "List of unit types to remove in the filter.",
235 OFFSET(remove_types), AV_OPT_TYPE_STRING,
236 { .str = NULL }, .flags = FLAGS },
237
238 { "discard", "Remove the selected frames",
239 OFFSET(discard), AV_OPT_TYPE_INT,
240 { .i64 = AVDISCARD_NONE }, INT_MIN, INT_MAX, FLAGS, .unit = "discard"},
241 { "none" , "discard none",
242 0, AV_OPT_TYPE_CONST,
243 { .i64 = AVDISCARD_NONE }, INT_MIN, INT_MAX, FLAGS, .unit = "discard"},
244 { "default" , "discard none, but can be changed after dynamically",
245 0, AV_OPT_TYPE_CONST,
246 { .i64 = AVDISCARD_DEFAULT }, INT_MIN, INT_MAX, FLAGS, .unit = "discard"},
247 { "nonref", "discard all non-reference frames",
248 0, AV_OPT_TYPE_CONST,
249 { .i64 = AVDISCARD_NONREF }, INT_MIN, INT_MAX, FLAGS, .unit = "discard"},
250 { "bidir", "discard all bidirectional frames",
251 0, AV_OPT_TYPE_CONST,
252 { .i64 = AVDISCARD_BIDIR }, INT_MIN, INT_MAX, FLAGS, .unit = "discard"},
253 { "nonintra", "discard all frames except I frames",
254 0, AV_OPT_TYPE_CONST,
255 { .i64 = AVDISCARD_NONINTRA }, INT_MIN, INT_MAX, FLAGS, .unit = "discard"},
256 { "nonkey", "discard all frames except keyframes",
257 0, AV_OPT_TYPE_CONST,
258 { .i64 = AVDISCARD_NONKEY }, INT_MIN, INT_MAX, FLAGS, .unit = "discard"},
259 { "all", "discard all frames",
260 0, AV_OPT_TYPE_CONST,
261 { .i64 = AVDISCARD_ALL }, INT_MIN, INT_MAX, FLAGS, .unit = "discard"},
262
263 { "discard_flags", "flags to control the discard frame behavior",
264 OFFSET(discard_flags), AV_OPT_TYPE_FLAGS,
265 { .i64 = DISCARD_FLAG_NONE }, INT_MIN, INT_MAX, FLAGS, .unit = "discard_flags"},
266 { "keep_non_vcl", "non-vcl units even if the picture has been dropped",
267 0, AV_OPT_TYPE_CONST,
268 { .i64 = DISCARD_FLAG_KEEP_NON_VCL }, INT_MIN, INT_MAX, FLAGS, .unit = "discard_flags"},
269 { NULL }
270 };
271
272 static const AVClass filter_units_class = {
273 .class_name = "filter_units",
274 .item_name = av_default_item_name,
275 .option = filter_units_options,
276 .version = LIBAVUTIL_VERSION_INT,
277 };
278
279 const FFBitStreamFilter ff_filter_units_bsf = {
280 .p.name = "filter_units",
281 .p.codec_ids = ff_cbs_all_codec_ids,
282 .p.priv_class = &filter_units_class,
283 .priv_data_size = sizeof(FilterUnitsContext),
284 .init = &filter_units_init,
285 .close = &filter_units_close,
286 .filter = &filter_units_filter,
287 };
288