This is an automated email from the git hooks/post-receive script.

Git pushed a commit to branch master
in repository ffmpeg.

commit f2b3888754513ff728ecb8a4bf15fc2cb18d6b6a
Author:     James Almer <[email protected]>
AuthorDate: Wed Jun 24 08:26:37 2026 -0300
Commit:     James Almer <[email protected]>
CommitDate: Mon Jul 6 13:27:25 2026 -0300

    avcodec/bsf: add an LCEVC stream merging bsf
    
    Signed-off-by: James Almer <[email protected]>
---
 Changelog                                          |   1 +
 libavcodec/bitstream_filters.c                     |   1 +
 libavcodec/bsf/Makefile                            |   4 +-
 libavcodec/bsf/lcevc_merge.c                       | 182 +++++++++++
 .../framesync.c => libavcodec/bsf/packetsync.c     | 242 +++++++--------
 libavcodec/bsf/packetsync.h                        | 345 +++++++++++++++++++++
 libavcodec/version.h                               |   2 +-
 7 files changed, 650 insertions(+), 127 deletions(-)

diff --git a/Changelog b/Changelog
index c7b1c97110..af804cf9f4 100644
--- a/Changelog
+++ b/Changelog
@@ -5,6 +5,7 @@ version <next>:
 - extensively improved AAC encoder
 - APV Vulkan encoder
 - iTerm2 inline image protocol muxer
+- LCEVC payload merging bitstream filter
 
 
 version 9.0:
diff --git a/libavcodec/bitstream_filters.c b/libavcodec/bitstream_filters.c
index bcc3011526..a9f3218f0f 100644
--- a/libavcodec/bitstream_filters.c
+++ b/libavcodec/bitstream_filters.c
@@ -49,6 +49,7 @@ extern const FFBitStreamFilter ff_hapqa_extract_bsf;
 extern const FFBitStreamFilter ff_hevc_metadata_bsf;
 extern const FFBitStreamFilter ff_hevc_mp4toannexb_bsf;
 extern const FFBitStreamFilter ff_imx_dump_header_bsf;
+extern const FFBitStreamFilter ff_lcevc_merge_bsf;
 extern const FFBitStreamFilter ff_lcevc_metadata_bsf;
 extern const FFBitStreamFilter ff_media100_to_mjpegb_bsf;
 extern const FFBitStreamFilter ff_mjpeg2jpeg_bsf;
diff --git a/libavcodec/bsf/Makefile b/libavcodec/bsf/Makefile
index a1c7adea38..abbd4be49e 100644
--- a/libavcodec/bsf/Makefile
+++ b/libavcodec/bsf/Makefile
@@ -1,7 +1,8 @@
 clean::
        $(RM) $(CLEANSUFFIXES:%=libavcodec/bsf/%)
 
-OBJS +=                                      bsf/sink.o       \
+OBJS +=                                      bsf/packetsync.o \
+                                             bsf/sink.o       \
                                              bsf/source.o
 
 OBJS-$(CONFIG_AAC_ADTSTOASC_BSF)          += bsf/aac_adtstoasc.o
@@ -29,6 +30,7 @@ OBJS-$(CONFIG_DOVI_RPU_BSF)               += bsf/dovi_rpu.o
 OBJS-$(CONFIG_DOVI_SPLIT_BSF)             += bsf/dovi_split.o
 OBJS-$(CONFIG_HEVC_MP4TOANNEXB_BSF)       += bsf/hevc_mp4toannexb.o
 OBJS-$(CONFIG_IMX_DUMP_HEADER_BSF)        += bsf/imx_dump_header.o
+OBJS-$(CONFIG_LCEVC_MERGE_BSF)            += bsf/lcevc_merge.o
 OBJS-$(CONFIG_LCEVC_METADATA_BSF)         += bsf/lcevc_metadata.o
 OBJS-$(CONFIG_MEDIA100_TO_MJPEGB_BSF)     += bsf/media100_to_mjpegb.o
 OBJS-$(CONFIG_MJPEG2JPEG_BSF)             += bsf/mjpeg2jpeg.o
diff --git a/libavcodec/bsf/lcevc_merge.c b/libavcodec/bsf/lcevc_merge.c
new file mode 100644
index 0000000000..27827f8ba9
--- /dev/null
+++ b/libavcodec/bsf/lcevc_merge.c
@@ -0,0 +1,182 @@
+/*
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "libavutil/opt.h"
+
+#include "libavcodec/bsf.h"
+#include "libavcodec/bsf_internal.h"
+#include "libavcodec/bsf/packetsync.h"
+#include "libavcodec/bytestream.h"
+#include "libavcodec/h2645_parse.h"
+#include "libavcodec/packet.h"
+#include "libavcodec/packet_internal.h"
+
+typedef struct LCEVCMergeContext {
+    AVClass *class;
+    FFPacketSync ps;
+    int nal_length_size;
+} LCEVCMergeContext;
+
+static AVPacket *lcevc_merge_packets(AVBitStreamFilterContext *ctx,
+                                     AVPacket *base_pkt, const AVPacket 
*lcevc_pkt)
+{
+    LCEVCMergeContext *lcevc = ctx->priv_data;
+
+    if (lcevc->nal_length_size && lcevc_pkt->size > lcevc->nal_length_size) {
+        GetByteContext bc;
+        size_t size = 0;
+
+        bytestream2_init(&bc, lcevc_pkt->data, lcevc_pkt->size);
+        do {
+            int i = 0;
+            int nal_size = get_nalsize(lcevc->nal_length_size,
+                                       bc.buffer, 
bytestream2_get_bytes_left(&bc), &i, ctx);
+            if (nal_size < 0)
+                return base_pkt;
+            if (nal_size > INT_MAX - 4)
+                return base_pkt;
+            size += nal_size + 4;
+            nal_size += i;
+            if (nal_size > bytestream2_get_bytes_left(&bc))
+                return base_pkt;
+            bytestream2_skip(&bc, nal_size);
+        } while (bytestream2_get_bytes_left(&bc) > 0);
+
+        uint8_t *buf = av_packet_new_side_data(base_pkt, AV_PKT_DATA_LCEVC, 
size);
+        if (!buf)
+            return base_pkt;
+
+        PutByteContext pc;
+        bytestream2_init_writer(&pc, buf, size);
+        bytestream2_init(&bc, lcevc_pkt->data, lcevc_pkt->size);
+        do {
+            int i = 0;
+            int nal_size = get_nalsize(lcevc->nal_length_size,
+                                       bc.buffer, 
bytestream2_get_bytes_left(&bc), &i, ctx);
+            bytestream2_skipu(&bc, i);
+            bytestream2_put_be32u(&pc, 1); // start code
+            bytestream2_put_bufferu(&pc, bc.buffer, nal_size);
+            bytestream2_skipu(&bc, nal_size);
+        } while (bytestream2_get_bytes_left(&bc) > 0);
+    } else {
+        uint8_t *buf = av_packet_new_side_data(base_pkt, AV_PKT_DATA_LCEVC, 
lcevc_pkt->size);
+        if (buf)
+            memcpy(buf, lcevc_pkt->data, lcevc_pkt->size);
+    }
+
+    return base_pkt;
+}
+
+static int lcevc_merge(FFPacketSync *fs)
+{
+    AVBitStreamFilterContext *ctx = fs->parent;
+    AVPacket *base, *enhancement, *out = NULL;
+    int ret;
+
+    ret = ff_packetsync_dualinput_get(fs, &base, &enhancement);
+    if (ret < 0)
+        return ret;
+    if (!enhancement)
+        return ff_bsf_filter_packet(ctx->outputs[0], base);
+    out = lcevc_merge_packets(ctx, base, enhancement);
+    return ff_bsf_filter_packet(ctx->outputs[0], out);
+}
+
+static av_cold int config_enhancement(AVBitStreamFilterLink *inlink)
+{
+    AVBitStreamFilterContext *ctx = inlink->dst;
+    LCEVCMergeContext *lcevc = ctx->priv_data;
+
+    if (inlink->par->extradata_size > 4 && inlink->par->extradata[0])
+        lcevc->nal_length_size = (inlink->par->extradata[4] >> 6) + 1;
+
+    return 0;
+}
+
+static int config_output(AVBitStreamFilterLink *outlink)
+{
+    AVBitStreamFilterContext *ctx = outlink->src;
+    LCEVCMergeContext *lcevc = ctx->priv_data;
+    int ret;
+
+    ret = ff_packetsync_init_dualinput(&lcevc->ps, ctx);
+    if (ret < 0)
+        return ret;
+
+    ret = ff_packetsync_configure(&lcevc->ps);
+    outlink->time_base = lcevc->ps.time_base;
+
+    return ret;
+}
+
+static av_cold int init(AVBitStreamFilterContext *ctx)
+{
+    LCEVCMergeContext *lcevc = ctx->priv_data;
+
+    lcevc->ps.on_event = lcevc_merge;
+    return 0;
+}
+
+static av_cold void uninit(AVBitStreamFilterContext *ctx)
+{
+    LCEVCMergeContext *lcevc = ctx->priv_data;
+
+    ff_packetsync_uninit(&lcevc->ps);
+}
+
+PACKETSYNC_DEFINE_CLASS_EXT(lcevc_merge, LCEVCMergeContext, ps, NULL);
+
+static int activate(AVBitStreamFilterContext *ctx)
+{
+    LCEVCMergeContext *lcevc = ctx->priv_data;
+    return ff_packetsync_activate(&lcevc->ps);
+}
+
+static const enum AVCodecID enhancement_codec_ids[] = {
+    AV_CODEC_ID_LCEVC, AV_CODEC_ID_NONE,
+};
+
+static const AVBitStreamFilterPad lcevc_merge_inputs[] = {
+    {
+        .name          = "base",
+    },
+    {
+        .name          = "enhancement",
+        .codec_ids     = enhancement_codec_ids,
+        .config_props  = config_enhancement,
+    },
+};
+
+static const AVBitStreamFilterPad lcevc_merge_outputs[] = {
+    {
+        .name          = "default",
+        .config_props  = config_output,
+    },
+};
+
+const FFBitStreamFilter ff_lcevc_merge_bsf = {
+    .p.name         = "lcevc_merge",
+    .p.priv_class   = &lcevc_merge_class,
+    .priv_data_size = sizeof(LCEVCMergeContext),
+    .preinit        = lcevc_merge_packetsync_preinit,
+    .init2          = init,
+    .uninit         = uninit,
+    .activate       = activate,
+    BSFILTER_INPUTS(lcevc_merge_inputs),
+    BSFILTER_OUTPUTS(lcevc_merge_outputs),
+};
diff --git a/libavfilter/framesync.c b/libavcodec/bsf/packetsync.c
similarity index 54%
copy from libavfilter/framesync.c
copy to libavcodec/bsf/packetsync.c
index 846d1c7e50..2567f8417c 100644
--- a/libavfilter/framesync.c
+++ b/libavcodec/bsf/packetsync.c
@@ -1,6 +1,4 @@
 /*
- * Copyright (c) 2013 Nicolas George
- *
  * This file is part of FFmpeg.
  *
  * FFmpeg is free software; you can redistribute it and/or
@@ -18,51 +16,54 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
+/**
+ * @file
+ * Packet sync API. Heavily based on libavfilter/framesync.c by Nicolas George
+ */
+
 #include "libavutil/avassert.h"
 #include "libavutil/mem.h"
 #include "libavutil/opt.h"
-#include "avfilter.h"
+
+#include "libavcodec/bsf.h"
 #include "filters.h"
-#include "framesync.h"
+#include "packetsync.h"
 
-#define OFFSET(member) offsetof(FFFrameSync, member)
-#define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM
+#define OFFSET(member) offsetof(FFPacketSync, member)
+#define FLAGS 
(AV_OPT_FLAG_BSF_PARAM|AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_VIDEO_PARAM)
 
-static const char *framesync_name(void *ptr)
+static const char *packetsync_name(void *ptr)
 {
-    return "framesync";
+    return "packetsync";
 }
 
-static const AVOption framesync_options[] = {
+static const AVOption packetsync_options[] = {
     { "eof_action", "Action to take when encountering EOF from secondary input 
",
-        OFFSET(opt_eof_action), AV_OPT_TYPE_INT, { .i64 = EOF_ACTION_REPEAT },
-        EOF_ACTION_REPEAT, EOF_ACTION_PASS, .flags = FLAGS, .unit = 
"eof_action" },
-        { "repeat", "Repeat the previous frame.",   0, AV_OPT_TYPE_CONST, { 
.i64 = EOF_ACTION_REPEAT }, .flags = FLAGS, .unit = "eof_action" },
+        OFFSET(opt_eof_action), AV_OPT_TYPE_INT, { .i64 = EOF_ACTION_PASS },
+        EOF_ACTION_ENDALL, EOF_ACTION_PASS, .flags = FLAGS, .unit = 
"eof_action" },
         { "endall", "End both streams.",            0, AV_OPT_TYPE_CONST, { 
.i64 = EOF_ACTION_ENDALL }, .flags = FLAGS, .unit = "eof_action" },
         { "pass",   "Pass through the main input.", 0, AV_OPT_TYPE_CONST, { 
.i64 = EOF_ACTION_PASS },   .flags = FLAGS, .unit = "eof_action" },
-    { "shortest", "force termination when the shortest input terminates", 
OFFSET(opt_shortest), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS },
-    { "repeatlast", "extend last frame of secondary streams beyond EOF", 
OFFSET(opt_repeatlast), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, FLAGS },
     { "ts_sync_mode", "How strictly to sync streams based on secondary input 
timestamps",
         OFFSET(opt_ts_sync_mode), AV_OPT_TYPE_INT, { .i64 = TS_DEFAULT },
         TS_DEFAULT, TS_NEAREST, .flags = FLAGS, .unit = "ts_sync_mode" },
-        { "default", "Frame from secondary input with the nearest lower or 
equal timestamp to the primary input frame",
+        { "default", "Packet from secondary input with the nearest lower or 
equal timestamp to the primary input packet",
             0, AV_OPT_TYPE_CONST, { .i64 = TS_DEFAULT }, .flags = FLAGS, .unit 
= "ts_sync_mode" },
-        { "nearest", "Frame from secondary input with the absolute nearest 
timestamp to the primary input frame",
+        { "nearest", "Packet from secondary input with the absolute nearest 
timestamp to the primary input packet",
             0, AV_OPT_TYPE_CONST, { .i64 = TS_NEAREST }, .flags = FLAGS, .unit 
= "ts_sync_mode" },
     { NULL }
 };
-const AVClass ff_framesync_class = {
+const AVClass ff_packetsync_class = {
     .version                   = LIBAVUTIL_VERSION_INT,
-    .class_name                = "framesync",
-    .item_name                 = framesync_name,
-    .category                  = AV_CLASS_CATEGORY_FILTER,
-    .option                    = framesync_options,
+    .class_name                = "packetsync",
+    .item_name                 = packetsync_name,
+    .category                  = AV_CLASS_CATEGORY_BITSTREAM_FILTER,
+    .option                    = packetsync_options,
     .parent_log_context_offset = OFFSET(parent),
 };
 
-const AVClass *ff_framesync_child_class_iterate(void **iter)
+const AVClass *ff_packetsync_child_class_iterate(void **iter)
 {
-    const AVClass *c = *iter ? NULL : &ff_framesync_class;
+    const AVClass *c = *iter ? NULL : &ff_packetsync_class;
     *iter = (void *)(uintptr_t)c;
     return c;
 }
@@ -73,24 +74,24 @@ enum {
     STATE_EOF,
 };
 
-static int consume_from_fifos(FFFrameSync *fs);
+static int consume_from_fifos(FFPacketSync *fs);
 
-void ff_framesync_preinit(FFFrameSync *fs)
+void ff_packetsync_preinit(FFPacketSync *fs)
 {
     if (fs->class)
         return;
-    fs->class  = &ff_framesync_class;
+    fs->class  = &ff_packetsync_class;
     av_opt_set_defaults(fs);
 }
 
-int ff_framesync_init(FFFrameSync *fs, AVFilterContext *parent, unsigned nb_in)
+int ff_packetsync_init(FFPacketSync *fs, AVBitStreamFilterContext *parent, 
unsigned nb_in)
 {
     /* For filters with several outputs, we will not be able to assume which
-       output is relevant for ff_outlink_frame_wanted() and
-       ff_outlink_set_status(). To be designed when needed. */
+       output is relevant for ff_outlink_packet_wanted() and
+       ff_bsf_link_set_in_status(). To be designed when needed. */
     av_assert0(parent->nb_outputs == 1);
 
-    ff_framesync_preinit(fs);
+    ff_packetsync_preinit(fs);
     fs->parent = parent;
     fs->nb_in  = nb_in;
 
@@ -103,14 +104,14 @@ int ff_framesync_init(FFFrameSync *fs, AVFilterContext 
*parent, unsigned nb_in)
     return 0;
 }
 
-static void framesync_eof(FFFrameSync *fs, int64_t pts)
+static void packetsync_eof(FFPacketSync *fs, int64_t pts)
 {
     fs->eof = 1;
-    fs->frame_ready = 0;
-    ff_outlink_set_status(fs->parent->outputs[0], AVERROR_EOF, pts);
+    fs->pkt_ready = 0;
+    ff_bsf_link_set_in_status(fs->parent->outputs[0], AVERROR_EOF, pts);
 }
 
-static void framesync_sync_level_update(FFFrameSync *fs, int64_t eof_pts)
+static void packetsync_sync_level_update(FFPacketSync *fs, int64_t eof_pts)
 {
     unsigned i, level = 0;
 
@@ -131,28 +132,18 @@ static void framesync_sync_level_update(FFFrameSync *fs, 
int64_t eof_pts)
     if (level)
         fs->sync_level = level;
     else
-        framesync_eof(fs, eof_pts);
+        packetsync_eof(fs, eof_pts);
 }
 
-int ff_framesync_configure(FFFrameSync *fs)
+int ff_packetsync_configure(FFPacketSync *fs)
 {
     unsigned i;
 
-    if (!fs->opt_repeatlast || fs->opt_eof_action == EOF_ACTION_PASS) {
-        fs->opt_repeatlast = 0;
-        fs->opt_eof_action = EOF_ACTION_PASS;
-    }
-    if (fs->opt_shortest || fs->opt_eof_action == EOF_ACTION_ENDALL) {
-        fs->opt_shortest = 1;
-        fs->opt_eof_action = EOF_ACTION_ENDALL;
+    for (i = 1; i < fs->nb_in; i++) {
+        fs->in[i].after = EXT_NULL;
+        fs->in[i].sync  = 0;
     }
-    if (!fs->opt_repeatlast) {
-        for (i = 1; i < fs->nb_in; i++) {
-            fs->in[i].after = EXT_NULL;
-            fs->in[i].sync  = 0;
-        }
-    }
-    if (fs->opt_shortest) {
+    if (fs->opt_eof_action == EOF_ACTION_ENDALL) {
         for (i = 0; i < fs->nb_in; i++)
             fs->in[i].after = EXT_STOP;
     }
@@ -179,18 +170,18 @@ int ff_framesync_configure(FFFrameSync *fs)
     for (i = 0; i < fs->nb_in; i++)
         fs->in[i].pts = fs->in[i].pts_next = AV_NOPTS_VALUE;
     fs->sync_level = UINT_MAX;
-    framesync_sync_level_update(fs, AV_NOPTS_VALUE);
+    packetsync_sync_level_update(fs, AV_NOPTS_VALUE);
 
     return 0;
 }
 
-static int framesync_advance(FFFrameSync *fs)
+static int packetsync_advance(FFPacketSync *fs)
 {
     unsigned i;
     int64_t pts;
     int ret;
 
-    while (!(fs->frame_ready || fs->eof)) {
+    while (!(fs->pkt_ready || fs->eof)) {
         ret = consume_from_fifos(fs);
         if (ret <= 0)
             return ret;
@@ -200,7 +191,7 @@ static int framesync_advance(FFFrameSync *fs)
             if (fs->in[i].have_next && fs->in[i].pts_next < pts)
                 pts = fs->in[i].pts_next;
         if (pts == INT64_MAX) {
-            framesync_eof(fs, AV_NOPTS_VALUE);
+            packetsync_eof(fs, AV_NOPTS_VALUE);
             break;
         }
         for (i = 0; i < fs->nb_in; i++) {
@@ -208,112 +199,109 @@ static int framesync_advance(FFFrameSync *fs)
                 (fs->in[i].ts_mode == TS_NEAREST &&
                  fs->in[i].have_next &&
                  fs->in[i].pts_next != INT64_MAX && fs->in[i].pts != 
AV_NOPTS_VALUE &&
-                 fs->in[i].pts_next - pts < pts - fs->in[i].pts) ||
-                (fs->in[i].before == EXT_INFINITY &&
-                 fs->in[i].state == STATE_BOF)) {
-                av_frame_free(&fs->in[i].frame);
-                fs->in[i].frame      = fs->in[i].frame_next;
+                 fs->in[i].pts_next - pts < pts - fs->in[i].pts)) {
+                av_packet_free(&fs->in[i].pkt);
+                fs->in[i].pkt      = fs->in[i].pkt_next;
                 fs->in[i].pts        = fs->in[i].pts_next;
-                fs->in[i].frame_next = NULL;
+                fs->in[i].pkt_next = NULL;
                 fs->in[i].pts_next   = AV_NOPTS_VALUE;
                 fs->in[i].have_next  = 0;
-                fs->in[i].state      = fs->in[i].frame ? STATE_RUN : STATE_EOF;
-                if (fs->in[i].sync == fs->sync_level && fs->in[i].frame)
-                    fs->frame_ready = 1;
+                fs->in[i].state      = fs->in[i].pkt ? STATE_RUN : STATE_EOF;
+                if (fs->in[i].sync == fs->sync_level && fs->in[i].pkt)
+                    fs->pkt_ready = 1;
                 if (fs->in[i].state == STATE_EOF &&
                     fs->in[i].after == EXT_STOP)
-                    framesync_eof(fs, AV_NOPTS_VALUE);
+                    packetsync_eof(fs, AV_NOPTS_VALUE);
             }
         }
-        if (fs->frame_ready)
+        if (fs->pkt_ready)
             for (i = 0; i < fs->nb_in; i++)
                 if ((fs->in[i].state == STATE_BOF &&
                      fs->in[i].before == EXT_STOP))
-                    fs->frame_ready = 0;
+                    fs->pkt_ready = 0;
         fs->pts = pts;
     }
     return 0;
 }
 
-static int64_t framesync_pts_extrapolate(FFFrameSync *fs, unsigned in,
+static int64_t packetsync_pts_extrapolate(FFPacketSync *fs, unsigned in,
                                          int64_t pts)
 {
-    /* Possible enhancement: use the link's frame rate */
     return pts + 1;
 }
 
-static void framesync_inject_frame(FFFrameSync *fs, unsigned in, AVFrame 
*frame)
+static void packetsync_inject_packet(FFPacketSync *fs, unsigned in, AVPacket 
*pkt)
 {
     int64_t pts;
 
     av_assert0(!fs->in[in].have_next);
-    av_assert0(frame);
-    pts = av_rescale_q_rnd(frame->pts, fs->in[in].time_base, fs->time_base, 
AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX);
-    frame->pts = pts;
-    fs->in[in].frame_next = frame;
+    av_assert0(pkt);
+    pts = av_rescale_q_rnd(pkt->pts, fs->in[in].time_base, fs->time_base, 
AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX);
+    pkt->pts = pts;
+    fs->in[in].pkt_next = pkt;
     fs->in[in].pts_next   = pts;
     fs->in[in].have_next  = 1;
 }
 
-static void framesync_inject_status(FFFrameSync *fs, unsigned in, int status, 
int64_t eof_pts)
+static void packetsync_inject_status(FFPacketSync *fs, unsigned in, int 
status, int64_t eof_pts)
 {
     av_assert0(!fs->in[in].have_next);
     fs->in[in].sync = 0;
-    framesync_sync_level_update(fs, status == AVERROR_EOF ? eof_pts : 
AV_NOPTS_VALUE);
-    fs->in[in].frame_next = NULL;
-    fs->in[in].pts_next   = fs->in[in].state != STATE_RUN || fs->in[in].after 
== EXT_INFINITY
-                            ? INT64_MAX : framesync_pts_extrapolate(fs, in, 
fs->in[in].pts);
+    packetsync_sync_level_update(fs, status == AVERROR_EOF ? eof_pts : 
AV_NOPTS_VALUE);
+    fs->in[in].pkt_next = NULL;
+    fs->in[in].pts_next   = fs->in[in].state != STATE_RUN
+                            ? INT64_MAX : packetsync_pts_extrapolate(fs, in, 
fs->in[in].pts);
     fs->in[in].have_next  = 1;
 }
 
-int ff_framesync_get_frame(FFFrameSync *fs, unsigned in, AVFrame **rframe,
+int ff_packetsync_get_packet(FFPacketSync *fs, unsigned in, AVPacket **rpkt,
                             unsigned get)
 {
-    AVFrame *frame;
+    AVPacket *pkt;
     unsigned need_copy = 0, i;
     int64_t pts_next;
 
-    if (!fs->in[in].frame) {
-        *rframe = NULL;
+    if (!fs->in[in].pkt) {
+        *rpkt = NULL;
         return 0;
     }
-    frame = fs->in[in].frame;
+    pkt = fs->in[in].pkt;
     if (get) {
-        /* Find out if we need to copy the frame: is there another sync
-           stream, and do we know if its current frame will outlast this one? 
*/
+        /* Find out if we need to copy the packet: is there another sync
+           stream, and do we know if its current packet will outlast this one? 
*/
         pts_next = fs->in[in].have_next ? fs->in[in].pts_next : INT64_MAX;
         for (i = 0; i < fs->nb_in && !need_copy; i++)
             if (i != in && fs->in[i].sync &&
                 (!fs->in[i].have_next || fs->in[i].pts_next < pts_next))
                 need_copy = 1;
         if (need_copy) {
-            if (!(frame = av_frame_clone(frame)))
+            if (!(pkt = av_packet_clone(pkt)))
                 return AVERROR(ENOMEM);
         } else {
-            fs->in[in].frame = NULL;
+            fs->in[in].pkt = NULL;
         }
-        fs->frame_ready = 0;
+        fs->pkt_ready = 0;
     }
-    *rframe = frame;
+    *rpkt = pkt;
     return 0;
 }
 
-void ff_framesync_uninit(FFFrameSync *fs)
+void ff_packetsync_uninit(FFPacketSync *fs)
 {
     unsigned i;
 
     for (i = 0; i < fs->nb_in; i++) {
-        av_frame_free(&fs->in[i].frame);
-        av_frame_free(&fs->in[i].frame_next);
+        av_packet_free(&fs->in[i].pkt);
+        av_packet_free(&fs->in[i].pkt_next);
     }
 
     av_freep(&fs->in);
 }
 
-static int consume_from_fifos(FFFrameSync *fs)
+static int consume_from_fifos(FFPacketSync *fs)
 {
-    AVFilterContext *ctx = fs->parent;
-    AVFrame *frame = NULL;
+    AVBitStreamFilterContext *ctx = fs->parent;
+    AVPacket *pkt = NULL;
     int64_t pts;
     unsigned i, nb_active, nb_miss;
     int ret, status;
@@ -323,100 +311,104 @@ static int consume_from_fifos(FFFrameSync *fs)
         if (fs->in[i].have_next || fs->in[i].state == STATE_EOF)
             continue;
         nb_active++;
-        ret = ff_inlink_consume_frame(ctx->inputs[i], &frame);
+        ret = ff_bsf_inlink_consume_packet(ctx->inputs[i], &pkt);
         if (ret < 0)
             return ret;
         if (ret) {
-            av_assert0(frame);
-            framesync_inject_frame(fs, i, frame);
+            av_assert0(pkt);
+            packetsync_inject_packet(fs, i, pkt);
         } else {
-            ret = ff_inlink_acknowledge_status(ctx->inputs[i], &status, &pts);
+            ret = ff_bsf_inlink_acknowledge_status(ctx->inputs[i], &status, 
&pts);
             if (ret > 0) {
-                framesync_inject_status(fs, i, status, pts);
+                packetsync_inject_status(fs, i, status, pts);
             } else if (!ret) {
                 nb_miss++;
             }
         }
     }
     if (nb_miss) {
-        if (nb_miss == nb_active && !ff_outlink_frame_wanted(ctx->outputs[0]))
-            return FFERROR_NOT_READY;
+        if (nb_miss == nb_active && 
!ff_bsf_outlink_packet_wanted(ctx->outputs[0]))
+            return FFERROR_BSF_NOT_READY;
         for (i = 0; i < fs->nb_in; i++)
             if (!fs->in[i].have_next && fs->in[i].state != STATE_EOF)
-                ff_inlink_request_frame(ctx->inputs[i]);
+                ff_bsf_inlink_request_packet(ctx->inputs[i]);
         return 0;
     }
     return 1;
 }
 
-int ff_framesync_activate(FFFrameSync *fs)
+int ff_packetsync_activate(FFPacketSync *fs)
 {
-    AVFilterContext *ctx = fs->parent;
+    AVBitStreamFilterContext *ctx = fs->parent;
     int ret;
 
-    FF_FILTER_FORWARD_STATUS_BACK_ALL(ctx->outputs[0], ctx);
+    ret = ff_bsf_outlink_get_status(ctx->outputs[0]);
+    if (ret) {
+        unsigned i;
+        for (i = 0; i < ctx->nb_inputs; i++)
+            ff_bsf_inlink_set_status(ctx->inputs[i], ret);
+        return 0;
+    }
 
-    ret = framesync_advance(fs);
+    ret = packetsync_advance(fs);
     if (ret < 0)
         return ret;
-    if (fs->eof || !fs->frame_ready)
+    if (fs->eof || !fs->pkt_ready)
         return 0;
     ret = fs->on_event(fs);
     if (ret < 0)
         return ret;
-    fs->frame_ready = 0;
+    fs->pkt_ready = 0;
 
     return 0;
 }
 
-int ff_framesync_init_dualinput(FFFrameSync *fs, AVFilterContext *parent)
+int ff_packetsync_init_dualinput(FFPacketSync *fs, AVBitStreamFilterContext 
*parent)
 {
     int ret;
 
-    ret = ff_framesync_init(fs, parent, 2);
+    ret = ff_packetsync_init(fs, parent, 2);
     if (ret < 0)
         return ret;
     fs->in[0].time_base = parent->inputs[0]->time_base;
     fs->in[1].time_base = parent->inputs[1]->time_base;
     fs->in[0].sync   = 2;
     fs->in[0].before = EXT_STOP;
-    fs->in[0].after  = EXT_INFINITY;
+    fs->in[0].after  = EXT_STOP;
     fs->in[1].sync   = 1;
     fs->in[1].before = EXT_NULL;
-    fs->in[1].after  = EXT_INFINITY;
+    fs->in[1].after  = EXT_NULL;
     return 0;
 }
 
-int ff_framesync_dualinput_get(FFFrameSync *fs, AVFrame **f0, AVFrame **f1)
+int ff_packetsync_dualinput_get(FFPacketSync *fs, AVPacket **f0, AVPacket **f1)
 {
-    AVFilterContext *ctx = fs->parent;
-    AVFrame *mainpic = NULL, *secondpic = NULL;
+    AVBitStreamFilterContext *ctx = fs->parent;
+    AVPacket *mainpic = NULL, *secondpic = NULL;
     int ret;
 
-    if ((ret = ff_framesync_get_frame(fs, 0, &mainpic,   1)) < 0 ||
-        (ret = ff_framesync_get_frame(fs, 1, &secondpic, 0)) < 0) {
-        av_frame_free(&mainpic);
+    if ((ret = ff_packetsync_get_packet(fs, 0, &mainpic,   1)) < 0 ||
+        (ret = ff_packetsync_get_packet(fs, 1, &secondpic, 0)) < 0) {
+        av_packet_free(&mainpic);
         return ret;
     }
     av_assert0(mainpic);
     mainpic->pts = av_rescale_q(fs->pts, fs->time_base, 
ctx->outputs[0]->time_base);
-    if (ctx->is_disabled)
-        secondpic = NULL;
     *f0 = mainpic;
     *f1 = secondpic;
     return 0;
 }
 
-int ff_framesync_dualinput_get_writable(FFFrameSync *fs, AVFrame **f0, AVFrame 
**f1)
+int ff_packetsync_dualinput_get_writable(FFPacketSync *fs, AVPacket **f0, 
AVPacket **f1)
 {
     int ret;
 
-    ret = ff_framesync_dualinput_get(fs, f0, f1);
+    ret = ff_packetsync_dualinput_get(fs, f0, f1);
     if (ret < 0)
         return ret;
-    ret = ff_inlink_make_frame_writable(fs->parent->inputs[0], f0);
+    ret = av_packet_make_writable(*f0);
     if (ret < 0) {
-        av_frame_free(f0);
+        av_packet_free(f0);
         *f1 = NULL;
         return ret;
     }
diff --git a/libavcodec/bsf/packetsync.h b/libavcodec/bsf/packetsync.h
new file mode 100644
index 0000000000..03f7d01c82
--- /dev/null
+++ b/libavcodec/bsf/packetsync.h
@@ -0,0 +1,345 @@
+/*
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with FFmpeg; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVCODEC_BSF_PACKETSYNC_H
+#define AVCODEC_BSF_PACKETSYNC_H
+
+#include <stdint.h>
+
+#include "libavutil/log.h"
+
+#include "libavcodec/bsf.h"
+#include "libavcodec/packet.h"
+
+enum EOFAction {
+    EOF_ACTION_ENDALL,
+    EOF_ACTION_PASS
+};
+
+/**
+ * This API is intended as a helper for filters that have several video
+ * input and need to combine them somehow. If the inputs have different or
+ * variable frame rate, getting the input packets to match requires a rather
+ * complex logic and a few user-tunable options.
+ *
+ * In this API, when a set of synchronized input packets is ready to be
+ * processed is called a packet event. packet event can be generated in
+ * response to input packets on any or all inputs and the handling of
+ * situations where some stream extend beyond the beginning or the end of
+ * others can be configured.
+ *
+ * The basic working of this API is the following: set the on_event
+ * callback, then call ff_packetsync_activate() from the filter's activate
+ * callback.
+ */
+
+/**
+ * Stream extrapolation mode
+ *
+ * Describe how the packets of a stream are extrapolated before the first one
+ * and after EOF to keep sync with possibly longer other streams.
+ */
+enum FFPacketSyncExtMode {
+
+    /**
+     * Completely stop all streams with this one.
+     */
+    EXT_STOP,
+
+    /**
+     * Ignore this stream and continue processing the other ones.
+     */
+    EXT_NULL,
+};
+
+/**
+ * Timestamp synchronization mode
+ *
+ * Describe how the packets of a stream are synchronized based on timestamp
+ * distance.
+ */
+enum FFPacketTSSyncMode {
+
+    /**
+     * Sync to packets from secondary input with the nearest, lower or equal
+     * timestamp to the packet event one.
+     */
+    TS_DEFAULT,
+
+    /**
+     * Sync to packets from secondary input with the absolute nearest timestamp
+     * to the packet event one.
+     */
+    TS_NEAREST,
+};
+
+/**
+ * Input stream structure
+ */
+typedef struct FFPacketSyncIn {
+
+    /**
+     * Extrapolation mode for timestamps before the first packet
+     */
+    enum FFPacketSyncExtMode before;
+
+    /**
+     * Extrapolation mode for timestamps after the last packet
+     */
+    enum FFPacketSyncExtMode after;
+
+    /**
+     * Time base for the incoming packets
+     */
+    AVRational time_base;
+
+    /**
+     * Current packet, may be NULL before the first one or after EOF
+     */
+    AVPacket *pkt;
+
+    /**
+     * Next packet, for internal use
+     */
+    AVPacket *pkt_next;
+
+    /**
+     * PTS of the current packet
+     */
+    int64_t pts;
+
+    /**
+     * PTS of the next packet, for internal use
+     */
+    int64_t pts_next;
+
+    /**
+     * Boolean flagging the next packet, for internal use
+     */
+    uint8_t have_next;
+
+    /**
+     * State: before first, in stream or after EOF, for internal use
+     */
+    uint8_t state;
+
+    /**
+     * Synchronization level: packets on input at the highest sync level will
+     * generate output packet events.
+     *
+     * For example, if inputs #0 and #1 have sync level 2 and input #2 has
+     * sync level 1, then a packet on either input #0 or #1 will generate a
+     * packet event, but not a packet on input #2 until both inputs #0 and #1
+     * have reached EOF.
+     *
+     * If sync is 0, no packet event will be generated.
+     */
+    unsigned sync;
+
+    enum FFPacketTSSyncMode ts_mode;
+} FFPacketSyncIn;
+
+/**
+ * Packet sync structure.
+ */
+typedef struct FFPacketSync {
+    const AVClass *class;
+
+    /**
+     * Parent filter context.
+     */
+    AVBitStreamFilterContext *parent;
+
+    /**
+     * Number of input streams
+     */
+    unsigned nb_in;
+
+    /**
+     * Time base for the output events
+     */
+    AVRational time_base;
+
+    /**
+     * Timestamp of the current event
+     */
+    int64_t pts;
+
+    /**
+     * Callback called when a packet event is ready
+     */
+    int (*on_event)(struct FFPacketSync *fs);
+
+    /**
+     * Opaque pointer, not used by the API
+     */
+    void *opaque;
+
+    /**
+     * Index of the input that requires a request
+     */
+    unsigned in_request;
+
+    /**
+     * Synchronization level: only inputs with the same sync level are sync
+     * sources.
+     */
+    unsigned sync_level;
+
+    /**
+     * Flag indicating that a packet event is ready
+     */
+    uint8_t pkt_ready;
+
+    /**
+     * Flag indicating that output has reached EOF.
+     */
+    uint8_t eof;
+
+    /**
+     * Pointer to array of inputs.
+     */
+    FFPacketSyncIn *in;
+
+    int opt_eof_action;
+    int opt_ts_sync_mode;
+
+} FFPacketSync;
+
+/**
+ * Pre-initialize a packet sync structure.
+ *
+ * It sets the class pointer and inits the options to their default values.
+ * The entire structure is expected to be already set to 0.
+ * This step is optional, but necessary to use the options.
+ */
+void ff_packetsync_preinit(FFPacketSync *fs);
+
+/**
+ * Initialize a packet sync structure.
+ *
+ * The entire structure is expected to be already set to 0 or preinited.
+ *
+ * @param  fs      packet sync structure to initialize
+ * @param  parent  parent AVBitStreamFilterContext object
+ * @param  nb_in   number of inputs
+ * @return  >= 0 for success or a negative error code
+ */
+int ff_packetsync_init(FFPacketSync *fs, AVBitStreamFilterContext *parent, 
unsigned nb_in);
+
+/**
+ * Configure a packet sync structure.
+ *
+ * Must be called after all options are set but before all use.
+ *
+ * @return  >= 0 for success or a negative error code
+ */
+int ff_packetsync_configure(FFPacketSync *fs);
+
+/**
+ * Free all memory currently allocated.
+ */
+void ff_packetsync_uninit(FFPacketSync *fs);
+
+/**
+ * Get the current packet in an input.
+ *
+ * @param fs      packet sync structure
+ * @param in      index of the input
+ * @param rpacket  used to return the current packet (or NULL)
+ * @param get     if not zero, the calling code needs to get ownership of
+ *                the returned packet; the current packet will either be
+ *                duplicated or removed from the packetsync structure
+ */
+int ff_packetsync_get_packet(FFPacketSync *fs, unsigned in, AVPacket **rpacket,
+                             unsigned get);
+
+/**
+ * Examine the packets in the filter's input and try to produce output.
+ *
+ * This function can be the complete implementation of the activate
+ * method of a filter using packetsync.
+ */
+int ff_packetsync_activate(FFPacketSync *fs);
+
+/**
+ * Initialize a packet sync structure for dualinput.
+ *
+ * Compared to generic packetsync, dualinput assumes the first input is the
+ * main one and the filtering is performed on it. The first input will be
+ * the only one with sync set and generic timeline support will just pass it
+ * unchanged when disabled.
+ *
+ * Equivalent to ff_packetsync_init(fs, parent, 2) then setting the time
+ * base, sync and ext modes on the inputs.
+ */
+int ff_packetsync_init_dualinput(FFPacketSync *fs, AVBitStreamFilterContext 
*parent);
+
+/**
+ * @param f0  used to return the main packet
+ * @param f1  used to return the second packet, or NULL if disabled
+ * @return  >=0 for success or AVERROR code
+ * @note  The packet returned in f0 belongs to the caller (get = 1 in
+ * ff_packetsync_get_packet()) while the packet returned in f1 is still owned
+ * by the packetsync structure.
+ */
+int ff_packetsync_dualinput_get(FFPacketSync *fs, AVPacket **f0, AVPacket 
**f1);
+
+/**
+ * Same as ff_packetsync_dualinput_get(), but make sure that f0 is writable.
+ */
+int ff_packetsync_dualinput_get_writable(FFPacketSync *fs, AVPacket **f0, 
AVPacket **f1);
+
+const AVClass *ff_packetsync_child_class_iterate(void **iter);
+extern const AVClass ff_packetsync_class;
+
+#define PACKETSYNC_DEFINE_PURE_CLASS(name, desc, func_prefix, options)      \
+static const AVClass name##_class = {                                       \
+    .class_name          = desc,                                            \
+    .item_name           = av_default_item_name,                            \
+    .option              = options,                                         \
+    .version             = LIBAVUTIL_VERSION_INT,                           \
+    .category            = AV_CLASS_CATEGORY_BITSTREAM_FILTER,              \
+    .child_class_iterate = ff_packetsync_child_class_iterate,               \
+    .child_next          = func_prefix##_child_next,                        \
+}
+
+/* A filter that uses the *_child_next-function from this macro
+ * is required to initialize the FFPacketSync structure in 
AVBitStreamFilter.preinit
+ * via the *_packetsync_preinit function defined alongside it. */
+#define PACKETSYNC_AUXILIARY_FUNCS(func_prefix, context, field)             \
+static int func_prefix##_packetsync_preinit(AVBitStreamFilterContext *ctx)  \
+{                                                                           \
+    context *s = ctx->priv_data; \
+    ff_packetsync_preinit(&s->field); \
+    return 0; \
+} \
+static void *func_prefix##_child_next(void *obj, void *prev)                \
+{                                                                           \
+    context *s = obj; \
+    return prev ? NULL : &s->field; \
+}
+
+#define PACKETSYNC_DEFINE_CLASS_EXT(name, context, field, options)          \
+PACKETSYNC_AUXILIARY_FUNCS(name, context, field)                            \
+PACKETSYNC_DEFINE_PURE_CLASS(name, #name, name, options)
+
+#define PACKETSYNC_DEFINE_CLASS(name, context, field)                       \
+PACKETSYNC_DEFINE_CLASS_EXT(name, context, field, name##_options)
+
+#endif /* AVCODEC_BSF_PACKETSYNC_H */
diff --git a/libavcodec/version.h b/libavcodec/version.h
index 06631ffa8c..7aa95fc3f1 100644
--- a/libavcodec/version.h
+++ b/libavcodec/version.h
@@ -29,7 +29,7 @@
 
 #include "version_major.h"
 
-#define LIBAVCODEC_VERSION_MINOR   4
+#define LIBAVCODEC_VERSION_MINOR   5
 #define LIBAVCODEC_VERSION_MICRO 100
 
 #define LIBAVCODEC_VERSION_INT  AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \

_______________________________________________
ffmpeg-cvslog mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to