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]
