FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavcodec/bsf/packetsync.c
Date: 2026-09-28 11:37:26
Exec Total Coverage
Lines: 8 227 3.5%
Functions: 2 18 11.1%
Branches: 2 174 1.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 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 3 void ff_packetsync_preinit(FFPacketSync *fs)
80 {
81
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (fs->class)
82 ✗ return;
83 3 fs->class = &ff_packetsync_class;
84 3 av_opt_set_defaults(fs);
85 }
86
87 ✗ 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 ✗ av_assert0(parent->nb_outputs == 1);
93
94 ✗ ff_packetsync_preinit(fs);
95 ✗ fs->parent = parent;
96 ✗ fs->nb_in = nb_in;
97
98 ✗ fs->in = av_calloc(nb_in, sizeof(*fs->in));
99 ✗ if (!fs->in) {
100 ✗ fs->nb_in = 0;
101 ✗ return AVERROR(ENOMEM);
102 }
103
104 ✗ return 0;
105 }
106
107 ✗ static void packetsync_eof(FFPacketSync *fs, int64_t pts)
108 {
109 ✗ fs->eof = 1;
110 ✗ fs->pkt_ready = 0;
111 ✗ ff_bsf_link_set_in_status(fs->parent->outputs[0], AVERROR_EOF, pts);
112 ✗ }
113
114 ✗ static void packetsync_sync_level_update(FFPacketSync *fs, int64_t eof_pts)
115 {
116 ✗ unsigned i, level = 0;
117
118 ✗ for (i = 0; i < fs->nb_in; i++)
119 ✗ if (fs->in[i].state != STATE_EOF)
120 ✗ level = FFMAX(level, fs->in[i].sync);
121 ✗ av_assert0(level <= fs->sync_level);
122 ✗ if (level < fs->sync_level)
123 ✗ av_log(fs, AV_LOG_VERBOSE, "Sync level %u\n", level);
124 ✗ 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 ✗ if (level)
133 ✗ fs->sync_level = level;
134 else
135 ✗ packetsync_eof(fs, eof_pts);
136 ✗ }
137
138 ✗ int ff_packetsync_configure(FFPacketSync *fs)
139 {
140 unsigned i;
141
142 ✗ for (i = 1; i < fs->nb_in; i++) {
143 ✗ fs->in[i].after = EXT_NULL;
144 ✗ fs->in[i].sync = 0;
145 }
146 ✗ 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 ✗ if (!fs->time_base.num) {
152 ✗ for (i = 0; i < fs->nb_in; i++) {
153 ✗ if (fs->in[i].sync) {
154 ✗ 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 ✗ fs->time_base = fs->in[i].time_base;
159 }
160 }
161 }
162 ✗ if (!fs->time_base.num) {
163 ✗ av_log(fs, AV_LOG_ERROR, "Impossible to set time base\n");
164 ✗ return AVERROR(EINVAL);
165 }
166 ✗ av_log(fs, AV_LOG_VERBOSE, "Selected %d/%d time base\n",
167 fs->time_base.num, fs->time_base.den);
168 }
169
170 ✗ for (i = 0; i < fs->nb_in; i++)
171 ✗ fs->in[i].pts = fs->in[i].pts_next = AV_NOPTS_VALUE;
172 ✗ fs->sync_level = UINT_MAX;
173 ✗ packetsync_sync_level_update(fs, AV_NOPTS_VALUE);
174
175 ✗ return 0;
176 }
177
178 ✗ static int packetsync_advance(FFPacketSync *fs)
179 {
180 unsigned i;
181 int64_t pts;
182 int ret;
183
184 ✗ while (!(fs->pkt_ready || fs->eof)) {
185 ✗ ret = consume_from_fifos(fs);
186 ✗ if (ret <= 0)
187 ✗ return ret;
188
189 ✗ pts = INT64_MAX;
190 ✗ for (i = 0; i < fs->nb_in; i++)
191 ✗ if (fs->in[i].have_next && fs->in[i].pts_next < pts)
192 ✗ pts = fs->in[i].pts_next;
193 ✗ if (pts == INT64_MAX) {
194 ✗ packetsync_eof(fs, AV_NOPTS_VALUE);
195 ✗ break;
196 }
197 ✗ for (i = 0; i < fs->nb_in; i++) {
198 ✗ 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 ✗ av_packet_free(&fs->in[i].pkt);
204 ✗ fs->in[i].pkt = fs->in[i].pkt_next;
205 ✗ fs->in[i].pts = fs->in[i].pts_next;
206 ✗ fs->in[i].pkt_next = NULL;
207 ✗ fs->in[i].pts_next = AV_NOPTS_VALUE;
208 ✗ fs->in[i].have_next = 0;
209 ✗ fs->in[i].state = fs->in[i].pkt ? STATE_RUN : STATE_EOF;
210 ✗ if (fs->in[i].sync == fs->sync_level && fs->in[i].pkt)
211 ✗ fs->pkt_ready = 1;
212 ✗ if (fs->in[i].state == STATE_EOF &&
213 ✗ fs->in[i].after == EXT_STOP)
214 ✗ packetsync_eof(fs, AV_NOPTS_VALUE);
215 }
216 }
217 ✗ if (fs->pkt_ready)
218 ✗ for (i = 0; i < fs->nb_in; i++)
219 ✗ if ((fs->in[i].state == STATE_BOF &&
220 ✗ fs->in[i].before == EXT_STOP))
221 ✗ fs->pkt_ready = 0;
222 ✗ fs->pts = pts;
223 }
224 ✗ return 0;
225 }
226
227 ✗ static int64_t packetsync_pts_extrapolate(FFPacketSync *fs, unsigned in,
228 int64_t pts)
229 {
230 ✗ return pts + 1;
231 }
232
233 ✗ static void packetsync_inject_packet(FFPacketSync *fs, unsigned in, AVPacket *pkt)
234 {
235 int64_t pts;
236
237 ✗ av_assert0(!fs->in[in].have_next);
238 ✗ av_assert0(pkt);
239 ✗ pts = av_rescale_q_rnd(pkt->pts, fs->in[in].time_base, fs->time_base, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX);
240 ✗ pkt->pts = pts;
241 ✗ fs->in[in].pkt_next = pkt;
242 ✗ fs->in[in].pts_next = pts;
243 ✗ fs->in[in].have_next = 1;
244 ✗ }
245
246 ✗ static void packetsync_inject_status(FFPacketSync *fs, unsigned in, int status, int64_t eof_pts)
247 {
248 ✗ av_assert0(!fs->in[in].have_next);
249 ✗ fs->in[in].sync = 0;
250 ✗ packetsync_sync_level_update(fs, status == AVERROR_EOF ? eof_pts : AV_NOPTS_VALUE);
251 ✗ fs->in[in].pkt_next = NULL;
252 ✗ fs->in[in].pts_next = fs->in[in].state != STATE_RUN
253 ✗ ? INT64_MAX : packetsync_pts_extrapolate(fs, in, fs->in[in].pts);
254 ✗ fs->in[in].have_next = 1;
255 ✗ }
256
257 ✗ int ff_packetsync_get_packet(FFPacketSync *fs, unsigned in, AVPacket **rpkt,
258 unsigned get)
259 {
260 AVPacket *pkt;
261 ✗ unsigned need_copy = 0, i;
262 int64_t pts_next;
263
264 ✗ if (!fs->in[in].pkt) {
265 ✗ *rpkt = NULL;
266 ✗ return 0;
267 }
268 ✗ pkt = fs->in[in].pkt;
269 ✗ 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 ✗ pts_next = fs->in[in].have_next ? fs->in[in].pts_next : INT64_MAX;
273 ✗ for (i = 0; i < fs->nb_in && !need_copy; i++)
274 ✗ 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 ✗ if (need_copy) {
278 ✗ if (!(pkt = av_packet_clone(pkt)))
279 ✗ return AVERROR(ENOMEM);
280 } else {
281 ✗ fs->in[in].pkt = NULL;
282 }
283 ✗ fs->pkt_ready = 0;
284 }
285 ✗ *rpkt = pkt;
286 ✗ return 0;
287 }
288
289 3 void ff_packetsync_uninit(FFPacketSync *fs)
290 {
291 unsigned i;
292
293
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 for (i = 0; i < fs->nb_in; i++) {
294 ✗ av_packet_free(&fs->in[i].pkt);
295 ✗ av_packet_free(&fs->in[i].pkt_next);
296 }
297
298 3 av_freep(&fs->in);
299 3 }
300
301 ✗ static int consume_from_fifos(FFPacketSync *fs)
302 {
303 ✗ AVBitStreamFilterContext *ctx = fs->parent;
304 ✗ AVPacket *pkt = NULL;
305 int64_t pts;
306 unsigned i, nb_active, nb_miss;
307 int ret, status;
308
309 ✗ nb_active = nb_miss = 0;
310 ✗ for (i = 0; i < fs->nb_in; i++) {
311 ✗ if (fs->in[i].have_next || fs->in[i].state == STATE_EOF)
312 ✗ continue;
313 ✗ nb_active++;
314 ✗ ret = ff_bsf_inlink_consume_packet(ctx->inputs[i], &pkt);
315 ✗ if (ret < 0)
316 ✗ return ret;
317 ✗ if (ret) {
318 ✗ av_assert0(pkt);
319 ✗ packetsync_inject_packet(fs, i, pkt);
320 } else {
321 ✗ ret = ff_bsf_inlink_acknowledge_status(ctx->inputs[i], &status, &pts);
322 ✗ if (ret > 0) {
323 ✗ packetsync_inject_status(fs, i, status, pts);
324 ✗ } else if (!ret) {
325 ✗ nb_miss++;
326 }
327 }
328 }
329 ✗ if (nb_miss) {
330 ✗ if (nb_miss == nb_active && !ff_bsf_outlink_packet_wanted(ctx->outputs[0]))
331 ✗ return FFERROR_BSF_NOT_READY;
332 ✗ for (i = 0; i < fs->nb_in; i++)
333 ✗ if (!fs->in[i].have_next && fs->in[i].state != STATE_EOF)
334 ✗ ff_bsf_inlink_request_packet(ctx->inputs[i]);
335 ✗ return 0;
336 }
337 ✗ return 1;
338 }
339
340 ✗ int ff_packetsync_activate(FFPacketSync *fs)
341 {
342 ✗ AVBitStreamFilterContext *ctx = fs->parent;
343 int ret;
344
345 ✗ ret = ff_bsf_outlink_get_status(ctx->outputs[0]);
346 ✗ 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 ✗ ret = packetsync_advance(fs);
354 ✗ if (ret < 0)
355 ✗ return ret;
356 ✗ if (fs->eof || !fs->pkt_ready)
357 ✗ return 0;
358 ✗ ret = fs->on_event(fs);
359 ✗ if (ret < 0)
360 ✗ return ret;
361 ✗ fs->pkt_ready = 0;
362
363 ✗ return 0;
364 }
365
366 ✗ int ff_packetsync_init_dualinput(FFPacketSync *fs, AVBitStreamFilterContext *parent)
367 {
368 int ret;
369
370 ✗ ret = ff_packetsync_init(fs, parent, 2);
371 ✗ if (ret < 0)
372 ✗ return ret;
373 ✗ fs->in[0].time_base = parent->inputs[0]->time_base;
374 ✗ fs->in[1].time_base = parent->inputs[1]->time_base;
375 ✗ fs->in[0].sync = 2;
376 ✗ fs->in[0].before = EXT_STOP;
377 ✗ fs->in[0].after = EXT_STOP;
378 ✗ fs->in[1].sync = 1;
379 ✗ fs->in[1].before = EXT_NULL;
380 ✗ fs->in[1].after = EXT_NULL;
381 ✗ return 0;
382 }
383
384 ✗ int ff_packetsync_dualinput_get(FFPacketSync *fs, AVPacket **f0, AVPacket **f1)
385 {
386 ✗ AVBitStreamFilterContext *ctx = fs->parent;
387 ✗ AVPacket *mainpic = NULL, *secondpic = NULL;
388 int ret;
389
390 ✗ if ((ret = ff_packetsync_get_packet(fs, 0, &mainpic, 1)) < 0 ||
391 ✗ (ret = ff_packetsync_get_packet(fs, 1, &secondpic, 0)) < 0) {
392 ✗ av_packet_free(&mainpic);
393 ✗ return ret;
394 }
395 ✗ av_assert0(mainpic);
396 ✗ mainpic->pts = av_rescale_q(fs->pts, fs->time_base, ctx->outputs[0]->time_base);
397 ✗ *f0 = mainpic;
398 ✗ *f1 = secondpic;
399 ✗ 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