FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavcodec/bsf/packetsync.c
Date: 2026-07-21 08:37:06
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