FFmpeg coverage


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