FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavfilter/f_segment.c
Date: 2024-11-20 23:03:26
Exec Total Coverage
Lines: 110 139 79.1%
Functions: 8 9 88.9%
Branches: 61 80 76.2%

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 "config_components.h"
20
21 #include <stdint.h>
22
23 #include "libavutil/avstring.h"
24 #include "libavutil/log.h"
25 #include "libavutil/mathematics.h"
26 #include "libavutil/mem.h"
27 #include "libavutil/opt.h"
28 #include "libavutil/parseutils.h"
29
30 #include "avfilter.h"
31 #include "filters.h"
32
33 typedef struct SegmentContext {
34 const AVClass *class;
35
36 char *timestamps_str;
37 char *points_str;
38 int use_timestamps;
39
40 int current_point;
41 int nb_points;
42 int64_t last_pts;
43
44 int64_t *points;
45 } SegmentContext;
46
47 8 static void count_points(char *item_str, int *nb_items)
48 {
49 char *p;
50
51
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (!item_str)
52 return;
53
54 8 *nb_items = 1;
55
2/2
✓ Branch 0 taken 60 times.
✓ Branch 1 taken 8 times.
68 for (p = item_str; *p; p++) {
56
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 52 times.
60 if (*p == '|')
57 8 (*nb_items)++;
58 }
59 }
60
61 8 static int parse_points(AVFilterContext *ctx, char *item_str, int nb_points, int64_t *points)
62 {
63 8 SegmentContext *s = ctx->priv;
64 8 char *arg, *p = item_str;
65 8 char *saveptr = NULL;
66 8 int64_t ref, cur = 0;
67 8 int ret = 0;
68
69
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 8 times.
24 for (int i = 0; i < nb_points; i++) {
70
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 16 times.
16 if (!(arg = av_strtok(p, "|", &saveptr)))
71 return AVERROR(EINVAL);
72
73 16 p = NULL;
74 16 ref = 0;
75
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 12 times.
16 if (*arg == '+') {
76 4 ref = cur;
77 4 arg++;
78 }
79
80
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 8 times.
16 if (s->use_timestamps) {
81 8 ret = av_parse_time(&points[i], arg, s->use_timestamps);
82 } else {
83
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (sscanf(arg, "%"SCNd64, &points[i]) != 1)
84 ret = AVERROR(EINVAL);
85 }
86
87
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
16 if (ret < 0) {
88 av_log(ctx, AV_LOG_ERROR, "Invalid splits supplied: %s\n", arg);
89 return ret;
90 }
91
92 16 cur = points[i];
93 16 points[i] += ref;
94 }
95
96 8 return 0;
97 }
98
99 8 static av_cold int init(AVFilterContext *ctx, enum AVMediaType type)
100 {
101 8 SegmentContext *s = ctx->priv;
102 char *split_str;
103 int ret;
104
105
3/4
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
8 if (s->timestamps_str && s->points_str) {
106 av_log(ctx, AV_LOG_ERROR, "Both timestamps and counts supplied.\n");
107 return AVERROR(EINVAL);
108
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4 times.
8 } else if (s->timestamps_str) {
109 4 s->use_timestamps = 1;
110 4 split_str = s->timestamps_str;
111
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 } else if (s->points_str) {
112 4 split_str = s->points_str;
113 } else {
114 av_log(ctx, AV_LOG_ERROR, "Neither timestamps nor durations nor counts supplied.\n");
115 return AVERROR(EINVAL);
116 }
117
118 8 count_points(split_str, &s->nb_points);
119 8 s->nb_points++;
120
121 8 s->points = av_calloc(s->nb_points, sizeof(*s->points));
122
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (!s->points)
123 return AVERROR(ENOMEM);
124
125 8 ret = parse_points(ctx, split_str, s->nb_points - 1, s->points);
126
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (ret < 0)
127 return ret;
128
129 8 s->points[s->nb_points - 1] = INT64_MAX;
130
131
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 8 times.
32 for (int i = 0; i < s->nb_points; i++) {
132 24 AVFilterPad pad = { 0 };
133
134 24 pad.type = type;
135 24 pad.name = av_asprintf("output%d", i);
136
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
24 if (!pad.name)
137 return AVERROR(ENOMEM);
138
139
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 24 times.
24 if ((ret = ff_append_outpad_free_name(ctx, &pad)) < 0)
140 return ret;
141 }
142
143 8 return 0;
144 }
145
146 4 static int config_input(AVFilterLink *inlink)
147 {
148 4 AVFilterContext *ctx = inlink->dst;
149 4 SegmentContext *s = ctx->priv;
150 4 AVRational tb = inlink->time_base;
151
152
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 if (s->use_timestamps) {
153
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2 times.
6 for (int i = 0; i < s->nb_points - 1; i++)
154 4 s->points[i] = av_rescale_q(s->points[i], AV_TIME_BASE_Q, tb);
155 }
156
157 4 return 0;
158 }
159
160 262 static int current_segment_finished(AVFilterContext *ctx, AVFrame *frame)
161 {
162 262 SegmentContext *s = ctx->priv;
163 262 AVFilterLink *inlink = ctx->inputs[0];
164 262 FilterLink *inl = ff_filter_link(inlink);
165 262 int ret = 0;
166
167
2/2
✓ Branch 0 taken 132 times.
✓ Branch 1 taken 130 times.
262 if (s->use_timestamps) {
168 132 ret = frame->pts >= s->points[s->current_point];
169 } else {
170
1/3
✗ Branch 0 not taken.
✓ Branch 1 taken 130 times.
✗ Branch 2 not taken.
130 switch (inlink->type) {
171 case AVMEDIA_TYPE_VIDEO:
172 ret = inl->frame_count_out - 1 >= s->points[s->current_point];
173 break;
174 130 case AVMEDIA_TYPE_AUDIO:
175 130 ret = inl->sample_count_out - frame->nb_samples >= s->points[s->current_point];
176 130 break;
177 }
178 }
179
180 262 return ret;
181 }
182
183 468 static int activate(AVFilterContext *ctx)
184 {
185 468 AVFilterLink *inlink = ctx->inputs[0];
186 468 FilterLink *inl = ff_filter_link(inlink);
187 468 SegmentContext *s = ctx->priv;
188 468 AVFrame *frame = NULL;
189 int ret, status;
190 int64_t max_samples;
191 int64_t diff;
192 int64_t pts;
193
194
2/2
✓ Branch 0 taken 596 times.
✓ Branch 1 taken 464 times.
1060 for (int i = s->current_point; i < s->nb_points; i++) {
195
4/4
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 592 times.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 4 times.
600 FF_FILTER_FORWARD_STATUS_BACK_ALL(ctx->outputs[i], ctx);
196 }
197
198
1/3
✗ Branch 0 not taken.
✓ Branch 1 taken 464 times.
✗ Branch 2 not taken.
464 switch (inlink->type) {
199 case AVMEDIA_TYPE_VIDEO:
200 ret = ff_inlink_consume_frame(inlink, &frame);
201 break;
202 464 case AVMEDIA_TYPE_AUDIO:
203 464 diff = s->points[s->current_point] - inl->sample_count_out;
204
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 464 times.
472 while (diff <= 0) {
205 8 ff_outlink_set_status(ctx->outputs[s->current_point], AVERROR_EOF, s->last_pts);
206 8 s->current_point++;
207
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (s->current_point >= s->nb_points)
208 return AVERROR(EINVAL);
209
210 8 diff = s->points[s->current_point] - inl->sample_count_out;
211 }
212
2/2
✓ Branch 0 taken 204 times.
✓ Branch 1 taken 260 times.
464 if (s->use_timestamps) {
213 204 max_samples = av_rescale_q(diff, av_make_q(1, inlink->sample_rate), inlink->time_base);
214 } else {
215 260 max_samples = FFMAX(1, FFMIN(diff, INT_MAX));
216 }
217
3/4
✓ Branch 0 taken 464 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 132 times.
✓ Branch 3 taken 332 times.
464 if (max_samples <= 0 || max_samples > INT_MAX)
218 132 ret = ff_inlink_consume_frame(inlink, &frame);
219 else
220 332 ret = ff_inlink_consume_samples(inlink, 1, max_samples, &frame);
221 464 break;
222 default:
223 return AVERROR_BUG;
224 }
225
226
2/2
✓ Branch 0 taken 262 times.
✓ Branch 1 taken 202 times.
464 if (ret > 0) {
227 262 s->last_pts = frame->pts;
228
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 262 times.
262 while (current_segment_finished(ctx, frame)) {
229 ff_outlink_set_status(ctx->outputs[s->current_point], AVERROR_EOF, frame->pts);
230 s->current_point++;
231 }
232
233
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 262 times.
262 if (s->current_point >= s->nb_points) {
234 av_frame_free(&frame);
235 return AVERROR(EINVAL);
236 }
237
238 262 ret = ff_filter_frame(ctx->outputs[s->current_point], frame);
239 }
240
241
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 464 times.
464 if (ret < 0) {
242 return ret;
243
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 460 times.
464 } else if (ff_inlink_acknowledge_status(inlink, &status, &pts)) {
244
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4 times.
8 for (int i = s->current_point; i < s->nb_points; i++)
245 4 ff_outlink_set_status(ctx->outputs[i], status, pts);
246 4 return 0;
247 } else {
248
2/2
✓ Branch 0 taken 580 times.
✓ Branch 1 taken 460 times.
1040 for (int i = s->current_point; i < s->nb_points; i++) {
249
2/2
✓ Branch 1 taken 260 times.
✓ Branch 2 taken 320 times.
580 if (ff_outlink_frame_wanted(ctx->outputs[i]))
250 260 ff_inlink_request_frame(inlink);
251 }
252 460 return 0;
253 }
254 }
255
256 8 static av_cold void uninit(AVFilterContext *ctx)
257 {
258 8 SegmentContext *s = ctx->priv;
259
260 8 av_freep(&s->points);
261 8 }
262
263 #define OFFSET(x) offsetof(SegmentContext, x)
264 #define COMMON_OPTS \
265 { "timestamps", "timestamps of input at which to split input", OFFSET(timestamps_str), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, FLAGS }, \
266
267 #if CONFIG_SEGMENT_FILTER
268
269 static av_cold int video_init(AVFilterContext *ctx)
270 {
271 return init(ctx, AVMEDIA_TYPE_VIDEO);
272 }
273
274 #define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM
275 static const AVOption segment_options[] = {
276 COMMON_OPTS
277 { "frames", "frames at which to split input", OFFSET(points_str), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, FLAGS },
278 { NULL }
279 };
280 #undef FLAGS
281
282 AVFILTER_DEFINE_CLASS(segment);
283
284 static const AVFilterPad segment_inputs[] = {
285 {
286 .name = "default",
287 .type = AVMEDIA_TYPE_VIDEO,
288 .config_props = config_input,
289 },
290 };
291
292 const AVFilter ff_vf_segment = {
293 .name = "segment",
294 .description = NULL_IF_CONFIG_SMALL("Segment video stream."),
295 .init = video_init,
296 .uninit = uninit,
297 .priv_size = sizeof(SegmentContext),
298 .priv_class = &segment_class,
299 .activate = activate,
300 FILTER_INPUTS(segment_inputs),
301 .outputs = NULL,
302 .flags = AVFILTER_FLAG_DYNAMIC_OUTPUTS | AVFILTER_FLAG_METADATA_ONLY,
303 };
304 #endif // CONFIG_SEGMENT_FILTER
305
306 #if CONFIG_ASEGMENT_FILTER
307
308 8 static av_cold int audio_init(AVFilterContext *ctx)
309 {
310 8 return init(ctx, AVMEDIA_TYPE_AUDIO);
311 }
312
313 #define FLAGS AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_FILTERING_PARAM
314 static const AVOption asegment_options[] = {
315 COMMON_OPTS
316 { "samples", "samples at which to split input", OFFSET(points_str), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, FLAGS },
317 { NULL }
318 };
319 #undef FLAGS
320
321 AVFILTER_DEFINE_CLASS(asegment);
322
323 static const AVFilterPad asegment_inputs[] = {
324 {
325 .name = "default",
326 .type = AVMEDIA_TYPE_AUDIO,
327 .config_props = config_input,
328 },
329 };
330
331 const AVFilter ff_af_asegment = {
332 .name = "asegment",
333 .description = NULL_IF_CONFIG_SMALL("Segment audio stream."),
334 .init = audio_init,
335 .uninit = uninit,
336 .priv_size = sizeof(SegmentContext),
337 .priv_class = &asegment_class,
338 .activate = activate,
339 FILTER_INPUTS(asegment_inputs),
340 .outputs = NULL,
341 .flags = AVFILTER_FLAG_DYNAMIC_OUTPUTS | AVFILTER_FLAG_METADATA_ONLY,
342 };
343 #endif // CONFIG_ASEGMENT_FILTER
344