FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavcodec/bsf/packetsync.c
Date: 2026-10-19 01:23:10
Exec Total Coverage
Lines: 173 227 76.2%
Functions: 15 18 83.3%
Branches: 98 174 56.3%

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 License
6 * 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
12 * GNU Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with FFmpeg; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
19 /**
20 * @file
21 * Packet sync API. Heavily based on libavfilter/framesync.c by Nicolas George
22 */
23
24 #include "libavutil/avassert.h"
25 #include "libavutil/mem.h"
26 #include "libavutil/opt.h"
27
28 #include "libavcodec/bsf.h"
29 #include "filters.h"
30 #include "packetsync.h"
31
32 #define OFFSET(member) offsetof(FFPacketSync, member)
33 #define FLAGS (AV_OPT_FLAG_BSF_PARAM|AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_VIDEO_PARAM)
34
35 ✗ static const char *packetsync_name(void *ptr)
36 {
37 ✗ return "packetsync";
38 }
39
40 static const AVOption packetsync_options[] = {
41 { "eof_action", "Action to take when encountering EOF from secondary input ",
42 OFFSET(opt_eof_action), AV_OPT_TYPE_INT, { .i64 = EOF_ACTION_PASS },
43 EOF_ACTION_ENDALL, EOF_ACTION_PASS, .flags = FLAGS, .unit = "eof_action" },
44 { "endall", "End both streams.", 0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_ENDALL }, .flags = FLAGS, .unit = "eof_action" },
45 { "pass", "Pass through the main input.", 0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_PASS }, .flags = FLAGS, .unit = "eof_action" },
46 { "ts_sync_mode", "How strictly to sync streams based on secondary input timestamps",
47 OFFSET(opt_ts_sync_mode), AV_OPT_TYPE_INT, { .i64 = TS_DEFAULT },
48 TS_DEFAULT, TS_NEAREST, .flags = FLAGS, .unit = "ts_sync_mode" },
49 { "default", "Packet from secondary input with the nearest lower or equal timestamp to the primary input packet",
50 0, AV_OPT_TYPE_CONST, { .i64 = TS_DEFAULT }, .flags = FLAGS, .unit = "ts_sync_mode" },
51 { "nearest", "Packet from secondary input with the absolute nearest timestamp to the primary input packet",
52 0, AV_OPT_TYPE_CONST, { .i64 = TS_NEAREST }, .flags = FLAGS, .unit = "ts_sync_mode" },
53 { NULL }
54 };
55 const AVClass ff_packetsync_class = {
56 .version = LIBAVUTIL_VERSION_INT,
57 .class_name = "packetsync",
58 .item_name = packetsync_name,
59 .category = AV_CLASS_CATEGORY_BITSTREAM_FILTER,
60 .option = packetsync_options,
61 .parent_log_context_offset = OFFSET(parent),
62 };
63
64 ✗ const AVClass *ff_packetsync_child_class_iterate(void **iter)
65 {
66 ✗ const AVClass *c = *iter ? NULL : &ff_packetsync_class;
67 ✗ *iter = (void *)(uintptr_t)c;
68 ✗ return c;
69 }
70
71 enum {
72 STATE_BOF,
73 STATE_RUN,
74 STATE_EOF,
75 };
76
77 static int consume_from_fifos(FFPacketSync *fs);
78
79 9 void ff_packetsync_preinit(FFPacketSync *fs)
80 {
81
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 7 times.
9 if (fs->class)
82 2 return;
83 7 fs->class = &ff_packetsync_class;
84 7 av_opt_set_defaults(fs);
85 }
86
87 2 int ff_packetsync_init(FFPacketSync *fs, AVBitStreamFilterContext *parent, unsigned nb_in)
88 {
89 /* For filters with several outputs, we will not be able to assume which
90 output is relevant for ff_outlink_packet_wanted() and
91 ff_bsf_link_set_in_status(). To be designed when needed. */
92
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 av_assert0(parent->nb_outputs == 1);
93
94 2 ff_packetsync_preinit(fs);
95 2 fs->parent = parent;
96 2 fs->nb_in = nb_in;
97
98 2 fs->in = av_calloc(nb_in, sizeof(*fs->in));
99
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (!fs->in) {
100 ✗ fs->nb_in = 0;
101 ✗ return AVERROR(ENOMEM);
102 }
103
104 2 return 0;
105 }
106
107 2 static void packetsync_eof(FFPacketSync *fs, int64_t pts)
108 {
109 2 fs->eof = 1;
110 2 fs->pkt_ready = 0;
111 2 ff_bsf_link_set_in_status(fs->parent->outputs[0], AVERROR_EOF, pts);
112 2 }
113
114 4 static void packetsync_sync_level_update(FFPacketSync *fs, int64_t eof_pts)
115 {
116 4 unsigned i, level = 0;
117
118
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 4 times.
12 for (i = 0; i < fs->nb_in; i++)
119
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 if (fs->in[i].state != STATE_EOF)
120 8 level = FFMAX(level, fs->in[i].sync);
121
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 av_assert0(level <= fs->sync_level);
122
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 if (level < fs->sync_level)
123 4 av_log(fs, AV_LOG_VERBOSE, "Sync level %u\n", level);
124
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (fs->opt_ts_sync_mode > TS_DEFAULT) {
125 ✗ for (i = 0; i < fs->nb_in; i++) {
126 ✗ if (fs->in[i].sync < level)
127 ✗ fs->in[i].ts_mode = fs->opt_ts_sync_mode;
128 else
129 ✗ fs->in[i].ts_mode = TS_DEFAULT;
130 }
131 }
132
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 if (level)
133 2 fs->sync_level = level;
134 else
135 2 packetsync_eof(fs, eof_pts);
136 4 }
137
138 2 int ff_packetsync_configure(FFPacketSync *fs)
139 {
140 unsigned i;
141
142
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 for (i = 1; i < fs->nb_in; i++) {
143 2 fs->in[i].after = EXT_NULL;
144 2 fs->in[i].sync = 0;
145 }
146
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (fs->opt_eof_action == EOF_ACTION_ENDALL) {
147 ✗ for (i = 0; i < fs->nb_in; i++)
148 ✗ fs->in[i].after = EXT_STOP;
149 }
150
151
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (!fs->time_base.num) {
152
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2 times.
6 for (i = 0; i < fs->nb_in; i++) {
153
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 if (fs->in[i].sync) {
154
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (fs->time_base.num) {
155 ✗ fs->time_base = av_gcd_q(fs->time_base, fs->in[i].time_base,
156 ✗ AV_TIME_BASE / 2, AV_TIME_BASE_Q);
157 } else {
158 2 fs->time_base = fs->in[i].time_base;
159 }
160 }
161 }
162
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (!fs->time_base.num) {
163 ✗ av_log(fs, AV_LOG_ERROR, "Impossible to set time base\n");
164 ✗ return AVERROR(EINVAL);
165 }
166 2 av_log(fs, AV_LOG_VERBOSE, "Selected %d/%d time base\n",
167 fs->time_base.num, fs->time_base.den);
168 }
169
170
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2 times.
6 for (i = 0; i < fs->nb_in; i++)
171 4 fs->in[i].pts = fs->in[i].pts_next = AV_NOPTS_VALUE;
172 2 fs->sync_level = UINT_MAX;
173 2 packetsync_sync_level_update(fs, AV_NOPTS_VALUE);
174
175 2 return 0;
176 }
177
178 182 static int packetsync_advance(FFPacketSync *fs)
179 {
180 unsigned i;
181 int64_t pts;
182 int ret;
183
184
3/4
✓ Branch 0 taken 182 times.
✓ Branch 1 taken 60 times.
✓ Branch 2 taken 182 times.
✗ Branch 3 not taken.
242 while (!(fs->pkt_ready || fs->eof)) {
185 182 ret = consume_from_fifos(fs);
186
2/2
✓ Branch 0 taken 122 times.
✓ Branch 1 taken 60 times.
182 if (ret <= 0)
187 122 return ret;
188
189 60 pts = INT64_MAX;
190
2/2
✓ Branch 0 taken 120 times.
✓ Branch 1 taken 60 times.
180 for (i = 0; i < fs->nb_in; i++)
191
3/4
✓ Branch 0 taken 120 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 60 times.
✓ Branch 3 taken 60 times.
120 if (fs->in[i].have_next && fs->in[i].pts_next < pts)
192 60 pts = fs->in[i].pts_next;
193
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 60 times.
60 if (pts == INT64_MAX) {
194 ✗ packetsync_eof(fs, AV_NOPTS_VALUE);
195 ✗ break;
196 }
197
2/2
✓ Branch 0 taken 120 times.
✓ Branch 1 taken 60 times.
180 for (i = 0; i < fs->nb_in; i++) {
198
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 120 times.
120 if (fs->in[i].pts_next == pts ||
199 ✗ (fs->in[i].ts_mode == TS_NEAREST &&
200 ✗ fs->in[i].have_next &&
201 ✗ fs->in[i].pts_next != INT64_MAX && fs->in[i].pts != AV_NOPTS_VALUE &&
202 ✗ fs->in[i].pts_next - pts < pts - fs->in[i].pts)) {
203 120 av_packet_free(&fs->in[i].pkt);
204 120 fs->in[i].pkt = fs->in[i].pkt_next;
205 120 fs->in[i].pts = fs->in[i].pts_next;
206 120 fs->in[i].pkt_next = NULL;
207 120 fs->in[i].pts_next = AV_NOPTS_VALUE;
208 120 fs->in[i].have_next = 0;
209
1/2
✓ Branch 0 taken 120 times.
✗ Branch 1 not taken.
120 fs->in[i].state = fs->in[i].pkt ? STATE_RUN : STATE_EOF;
210
3/4
✓ Branch 0 taken 60 times.
✓ Branch 1 taken 60 times.
✓ Branch 2 taken 60 times.
✗ Branch 3 not taken.
120 if (fs->in[i].sync == fs->sync_level && fs->in[i].pkt)
211 60 fs->pkt_ready = 1;
212
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 120 times.
120 if (fs->in[i].state == STATE_EOF &&
213 ✗ fs->in[i].after == EXT_STOP)
214 ✗ packetsync_eof(fs, AV_NOPTS_VALUE);
215 }
216 }
217
1/2
✓ Branch 0 taken 60 times.
✗ Branch 1 not taken.
60 if (fs->pkt_ready)
218
2/2
✓ Branch 0 taken 120 times.
✓ Branch 1 taken 60 times.
180 for (i = 0; i < fs->nb_in; i++)
219
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 120 times.
120 if ((fs->in[i].state == STATE_BOF &&
220 ✗ fs->in[i].before == EXT_STOP))
221 ✗ fs->pkt_ready = 0;
222 60 fs->pts = pts;
223 }
224 60 return 0;
225 }
226
227 2 static int64_t packetsync_pts_extrapolate(FFPacketSync *fs, unsigned in,
228 int64_t pts)
229 {
230 2 return pts + 1;
231 }
232
233 120 static void packetsync_inject_packet(FFPacketSync *fs, unsigned in, AVPacket *pkt)
234 {
235 int64_t pts;
236
237
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 120 times.
120 av_assert0(!fs->in[in].have_next);
238
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 120 times.
120 av_assert0(pkt);
239 120 pts = av_rescale_q_rnd(pkt->pts, fs->in[in].time_base, fs->time_base, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX);
240 120 pkt->pts = pts;
241 120 fs->in[in].pkt_next = pkt;
242 120 fs->in[in].pts_next = pts;
243 120 fs->in[in].have_next = 1;
244 120 }
245
246 2 static void packetsync_inject_status(FFPacketSync *fs, unsigned in, int status, int64_t eof_pts)
247 {
248
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 av_assert0(!fs->in[in].have_next);
249 2 fs->in[in].sync = 0;
250
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 packetsync_sync_level_update(fs, status == AVERROR_EOF ? eof_pts : AV_NOPTS_VALUE);
251 2 fs->in[in].pkt_next = NULL;
252 4 fs->in[in].pts_next = fs->in[in].state != STATE_RUN
253
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 ? INT64_MAX : packetsync_pts_extrapolate(fs, in, fs->in[in].pts);
254 2 fs->in[in].have_next = 1;
255 2 }
256
257 120 int ff_packetsync_get_packet(FFPacketSync *fs, unsigned in, AVPacket **rpkt,
258 unsigned get)
259 {
260 AVPacket *pkt;
261 120 unsigned need_copy = 0, i;
262 int64_t pts_next;
263
264
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 120 times.
120 if (!fs->in[in].pkt) {
265 ✗ *rpkt = NULL;
266 ✗ return 0;
267 }
268 120 pkt = fs->in[in].pkt;
269
2/2
✓ Branch 0 taken 60 times.
✓ Branch 1 taken 60 times.
120 if (get) {
270 /* Find out if we need to copy the packet: is there another sync
271 stream, and do we know if its current packet will outlast this one? */
272
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 60 times.
60 pts_next = fs->in[in].have_next ? fs->in[in].pts_next : INT64_MAX;
273
3/4
✓ Branch 0 taken 120 times.
✓ Branch 1 taken 60 times.
✓ Branch 2 taken 120 times.
✗ Branch 3 not taken.
180 for (i = 0; i < fs->nb_in && !need_copy; i++)
274
3/4
✓ Branch 0 taken 60 times.
✓ Branch 1 taken 60 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 60 times.
120 if (i != in && fs->in[i].sync &&
275 ✗ (!fs->in[i].have_next || fs->in[i].pts_next < pts_next))
276 ✗ need_copy = 1;
277
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 60 times.
60 if (need_copy) {
278 ✗ if (!(pkt = av_packet_clone(pkt)))
279 ✗ return AVERROR(ENOMEM);
280 } else {
281 60 fs->in[in].pkt = NULL;
282 }
283 60 fs->pkt_ready = 0;
284 }
285 120 *rpkt = pkt;
286 120 return 0;
287 }
288
289 7 void ff_packetsync_uninit(FFPacketSync *fs)
290 {
291 unsigned i;
292
293
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 7 times.
11 for (i = 0; i < fs->nb_in; i++) {
294 4 av_packet_free(&fs->in[i].pkt);
295 4 av_packet_free(&fs->in[i].pkt_next);
296 }
297
298 7 av_freep(&fs->in);
299 7 }
300
301 182 static int consume_from_fifos(FFPacketSync *fs)
302 {
303 182 AVBitStreamFilterContext *ctx = fs->parent;
304 182 AVPacket *pkt = NULL;
305 int64_t pts;
306 unsigned i, nb_active, nb_miss;
307 int ret, status;
308
309 182 nb_active = nb_miss = 0;
310
2/2
✓ Branch 0 taken 364 times.
✓ Branch 1 taken 182 times.
546 for (i = 0; i < fs->nb_in; i++) {
311
3/4
✓ Branch 0 taken 244 times.
✓ Branch 1 taken 120 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 244 times.
364 if (fs->in[i].have_next || fs->in[i].state == STATE_EOF)
312 120 continue;
313 244 nb_active++;
314 244 ret = ff_bsf_inlink_consume_packet(ctx->inputs[i], &pkt);
315
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 244 times.
244 if (ret < 0)
316 ✗ return ret;
317
2/2
✓ Branch 0 taken 120 times.
✓ Branch 1 taken 124 times.
244 if (ret) {
318
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 120 times.
120 av_assert0(pkt);
319 120 packetsync_inject_packet(fs, i, pkt);
320 } else {
321 124 ret = ff_bsf_inlink_acknowledge_status(ctx->inputs[i], &status, &pts);
322
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 122 times.
124 if (ret > 0) {
323 2 packetsync_inject_status(fs, i, status, pts);
324
1/2
✓ Branch 0 taken 122 times.
✗ Branch 1 not taken.
122 } else if (!ret) {
325 122 nb_miss++;
326 }
327 }
328 }
329
2/2
✓ Branch 0 taken 122 times.
✓ Branch 1 taken 60 times.
182 if (nb_miss) {
330
3/4
✓ Branch 0 taken 60 times.
✓ Branch 1 taken 62 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 60 times.
122 if (nb_miss == nb_active && !ff_bsf_outlink_packet_wanted(ctx->outputs[0]))
331 ✗ return FFERROR_BSF_NOT_READY;
332
2/2
✓ Branch 0 taken 244 times.
✓ Branch 1 taken 122 times.
366 for (i = 0; i < fs->nb_in; i++)
333
3/4
✓ Branch 0 taken 122 times.
✓ Branch 1 taken 122 times.
✓ Branch 2 taken 122 times.
✗ Branch 3 not taken.
244 if (!fs->in[i].have_next && fs->in[i].state != STATE_EOF)
334 122 ff_bsf_inlink_request_packet(ctx->inputs[i]);
335 122 return 0;
336 }
337 60 return 1;
338 }
339
340 182 int ff_packetsync_activate(FFPacketSync *fs)
341 {
342 182 AVBitStreamFilterContext *ctx = fs->parent;
343 int ret;
344
345 182 ret = ff_bsf_outlink_get_status(ctx->outputs[0]);
346
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 182 times.
182 if (ret) {
347 unsigned i;
348 ✗ for (i = 0; i < ctx->nb_inputs; i++)
349 ✗ ff_bsf_inlink_set_status(ctx->inputs[i], ret);
350 ✗ return 0;
351 }
352
353 182 ret = packetsync_advance(fs);
354
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 182 times.
182 if (ret < 0)
355 ✗ return ret;
356
4/4
✓ Branch 0 taken 180 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 120 times.
✓ Branch 3 taken 60 times.
182 if (fs->eof || !fs->pkt_ready)
357 122 return 0;
358 60 ret = fs->on_event(fs);
359
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 60 times.
60 if (ret < 0)
360 ✗ return ret;
361 60 fs->pkt_ready = 0;
362
363 60 return 0;
364 }
365
366 2 int ff_packetsync_init_dualinput(FFPacketSync *fs, AVBitStreamFilterContext *parent)
367 {
368 int ret;
369
370 2 ret = ff_packetsync_init(fs, parent, 2);
371
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (ret < 0)
372 ✗ return ret;
373 2 fs->in[0].time_base = parent->inputs[0]->time_base;
374 2 fs->in[1].time_base = parent->inputs[1]->time_base;
375 2 fs->in[0].sync = 2;
376 2 fs->in[0].before = EXT_STOP;
377 2 fs->in[0].after = EXT_STOP;
378 2 fs->in[1].sync = 1;
379 2 fs->in[1].before = EXT_NULL;
380 2 fs->in[1].after = EXT_NULL;
381 2 return 0;
382 }
383
384 60 int ff_packetsync_dualinput_get(FFPacketSync *fs, AVPacket **f0, AVPacket **f1)
385 {
386 60 AVBitStreamFilterContext *ctx = fs->parent;
387 60 AVPacket *mainpic = NULL, *secondpic = NULL;
388 int ret;
389
390
2/4
✓ Branch 1 taken 60 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 60 times.
120 if ((ret = ff_packetsync_get_packet(fs, 0, &mainpic, 1)) < 0 ||
391 60 (ret = ff_packetsync_get_packet(fs, 1, &secondpic, 0)) < 0) {
392 ✗ av_packet_free(&mainpic);
393 ✗ return ret;
394 }
395
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 60 times.
60 av_assert0(mainpic);
396 60 mainpic->pts = av_rescale_q(fs->pts, fs->time_base, ctx->outputs[0]->time_base);
397 60 *f0 = mainpic;
398 60 *f1 = secondpic;
399 60 return 0;
400 }
401
402 ✗ int ff_packetsync_dualinput_get_writable(FFPacketSync *fs, AVPacket **f0, AVPacket **f1)
403 {
404 int ret;
405
406 ✗ ret = ff_packetsync_dualinput_get(fs, f0, f1);
407 ✗ if (ret < 0)
408 ✗ return ret;
409 ✗ ret = av_packet_make_writable(*f0);
410 ✗ if (ret < 0) {
411 ✗ av_packet_free(f0);
412 ✗ *f1 = NULL;
413 ✗ return ret;
414 }
415 ✗ return 0;
416 }
417