From 72ec3358f44c2705cc544e2d6dcd403f73fca2f8 Mon Sep 17 00:00:00 2001 From: James Almer Date: Sun, 10 Nov 2019 21:09:02 -0300 Subject: [PATCH] avcodec: add an AV1 frame merge bitstream filter This BSF takes Temporal Units split across different AVPackets and merges them by looking for Temporal Delimiter OBUs. Signed-off-by: James Almer --- Changelog | 1 + configure | 1 + libavcodec/Makefile | 1 + libavcodec/av1_frame_merge_bsf.c | 155 +++++++++++++++++++++++++++++++ libavcodec/bitstream_filters.c | 1 + libavcodec/version.h | 2 +- 6 files changed, 160 insertions(+), 1 deletion(-) create mode 100644 libavcodec/av1_frame_merge_bsf.c diff --git a/Changelog b/Changelog index 11f15ed580..b100d3accd 100644 --- a/Changelog +++ b/Changelog @@ -22,6 +22,7 @@ version : - median filter - QSV-accelerated VP9 encoding - AV1 encoding support via librav1e +- AV1 frame merge bitstream filter version 4.2: diff --git a/configure b/configure index 1de90e93fd..70f60997c1 100755 --- a/configure +++ b/configure @@ -3115,6 +3115,7 @@ vc1_parser_select="vc1dsp" # bitstream_filters aac_adtstoasc_bsf_select="adts_header" +av1_frame_merge_bsf_select="cbs_av1" av1_frame_split_bsf_select="cbs_av1" av1_metadata_bsf_select="cbs_av1" eac3_core_bsf_select="ac3_parser" diff --git a/libavcodec/Makefile b/libavcodec/Makefile index b990c6ba87..006a472a6d 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -1075,6 +1075,7 @@ OBJS-$(CONFIG_XMA_PARSER) += xma_parser.o # bitstream filters OBJS-$(CONFIG_AAC_ADTSTOASC_BSF) += aac_adtstoasc_bsf.o mpeg4audio.o OBJS-$(CONFIG_AV1_METADATA_BSF) += av1_metadata_bsf.o +OBJS-$(CONFIG_AV1_FRAME_MERGE_BSF) += av1_frame_merge_bsf.o OBJS-$(CONFIG_AV1_FRAME_SPLIT_BSF) += av1_frame_split_bsf.o OBJS-$(CONFIG_CHOMP_BSF) += chomp_bsf.o OBJS-$(CONFIG_DUMP_EXTRADATA_BSF) += dump_extradata_bsf.o diff --git a/libavcodec/av1_frame_merge_bsf.c b/libavcodec/av1_frame_merge_bsf.c new file mode 100644 index 0000000000..49397111fc --- /dev/null +++ b/libavcodec/av1_frame_merge_bsf.c @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2019 James Almer + * + * 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 "avcodec.h" +#include "bsf.h" +#include "cbs.h" +#include "cbs_av1.h" + +typedef struct AV1FMergeContext { + CodedBitstreamContext *cbc; + CodedBitstreamFragment frag[2]; + AVPacket *pkt, *in; + int idx; +} AV1FMergeContext; + +static void av1_frame_merge_flush(AVBSFContext *bsf) +{ + AV1FMergeContext *ctx = bsf->priv_data; + + ff_cbs_fragment_reset(ctx->cbc, &ctx->frag[0]); + ff_cbs_fragment_reset(ctx->cbc, &ctx->frag[1]); + av_packet_unref(ctx->in); + av_packet_unref(ctx->pkt); +} + +static int av1_frame_merge_filter(AVBSFContext *bsf, AVPacket *out) +{ + AV1FMergeContext *ctx = bsf->priv_data; + CodedBitstreamFragment *frag = &ctx->frag[ctx->idx], *tu = &ctx->frag[!ctx->idx]; + AVPacket *in = ctx->in, *buffer_pkt = ctx->pkt; + int err, i; + + err = ff_bsf_get_packet_ref(bsf, in); + if (err < 0) { + if (err == AVERROR_EOF && tu->nb_units > 0) + goto eof; + return err; + } + + err = ff_cbs_read_packet(ctx->cbc, frag, in); + if (err < 0) { + av_log(bsf, AV_LOG_ERROR, "Failed to read packet.\n"); + goto fail; + } + + if (frag->nb_units == 0) { + av_log(bsf, AV_LOG_ERROR, "No OBU in packet.\n"); + err = AVERROR_INVALIDDATA; + goto fail; + } + + if (tu->nb_units == 0 && frag->units[0].type != AV1_OBU_TEMPORAL_DELIMITER) { + av_log(bsf, AV_LOG_ERROR, "Missing Temporal Delimiter.\n"); + err = AVERROR_INVALIDDATA; + goto fail; + } + + for (i = 1; i < frag->nb_units; i++) { + if (frag->units[i].type == AV1_OBU_TEMPORAL_DELIMITER) { + av_log(bsf, AV_LOG_ERROR, "Temporal Delimiter in the middle of a packet.\n"); + err = AVERROR_INVALIDDATA; + goto fail; + } + } + + if (tu->nb_units > 0 && frag->units[0].type == AV1_OBU_TEMPORAL_DELIMITER) { +eof: + err = ff_cbs_write_packet(ctx->cbc, buffer_pkt, tu); + if (err < 0) { + av_log(bsf, AV_LOG_ERROR, "Failed to write packet.\n"); + goto fail; + } + av_packet_move_ref(out, buffer_pkt); + + // Swap fragment index, to avoid copying fragment references. + ctx->idx = !ctx->idx; + } else { + for (i = 0; i < frag->nb_units; i++) { + err = ff_cbs_insert_unit_content(ctx->cbc, tu, -1, frag->units[i].type, + frag->units[i].content, frag->units[i].content_ref); + if (err < 0) + goto fail; + } + + err = AVERROR(EAGAIN); + } + + // Buffer packets with timestamps. There should be at most one per TU, be it split or not. + if (!buffer_pkt->data && in->pts != AV_NOPTS_VALUE) + av_packet_move_ref(buffer_pkt, in); + else + av_packet_unref(in); + + ff_cbs_fragment_reset(ctx->cbc, &ctx->frag[ctx->idx]); + +fail: + if (err < 0 && err != AVERROR(EAGAIN)) + av1_frame_merge_flush(bsf); + + return err; +} + +static int av1_frame_merge_init(AVBSFContext *bsf) +{ + AV1FMergeContext *ctx = bsf->priv_data; + + ctx->in = av_packet_alloc(); + ctx->pkt = av_packet_alloc(); + if (!ctx->in || !ctx->pkt) + return AVERROR(ENOMEM); + + return ff_cbs_init(&ctx->cbc, AV_CODEC_ID_AV1, bsf); +} + +static void av1_frame_merge_close(AVBSFContext *bsf) +{ + AV1FMergeContext *ctx = bsf->priv_data; + + ff_cbs_fragment_free(ctx->cbc, &ctx->frag[0]); + ff_cbs_fragment_free(ctx->cbc, &ctx->frag[1]); + av_packet_free(&ctx->in); + av_packet_free(&ctx->pkt); + ff_cbs_close(&ctx->cbc); +} + +static const enum AVCodecID av1_frame_merge_codec_ids[] = { + AV_CODEC_ID_AV1, AV_CODEC_ID_NONE, +}; + +const AVBitStreamFilter ff_av1_frame_merge_bsf = { + .name = "av1_frame_merge", + .priv_data_size = sizeof(AV1FMergeContext), + .init = av1_frame_merge_init, + .flush = av1_frame_merge_flush, + .close = av1_frame_merge_close, + .filter = av1_frame_merge_filter, + .codec_ids = av1_frame_merge_codec_ids, +}; diff --git a/libavcodec/bitstream_filters.c b/libavcodec/bitstream_filters.c index 463003966a..6b5ffe4d70 100644 --- a/libavcodec/bitstream_filters.c +++ b/libavcodec/bitstream_filters.c @@ -25,6 +25,7 @@ #include "bsf.h" extern const AVBitStreamFilter ff_aac_adtstoasc_bsf; +extern const AVBitStreamFilter ff_av1_frame_merge_bsf; extern const AVBitStreamFilter ff_av1_frame_split_bsf; extern const AVBitStreamFilter ff_av1_metadata_bsf; extern const AVBitStreamFilter ff_chomp_bsf; diff --git a/libavcodec/version.h b/libavcodec/version.h index b36f331c4e..58ea00a520 100644 --- a/libavcodec/version.h +++ b/libavcodec/version.h @@ -28,7 +28,7 @@ #include "libavutil/version.h" #define LIBAVCODEC_VERSION_MAJOR 58 -#define LIBAVCODEC_VERSION_MINOR 61 +#define LIBAVCODEC_VERSION_MINOR 62 #define LIBAVCODEC_VERSION_MICRO 100 #define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \