On Mon, Jun 12, 2023 at 2:29 PM Dawid Kozinski <d.kozin...@samsung.com> wrote:
> - Provided AVInputFormat struct describing EVC input format > (ff_evc_demuxer) > > Signed-off-by: Dawid Kozinski <d.kozin...@samsung.com> > --- > libavcodec/Makefile | 1 + > libavcodec/evc_frame_merge_bsf.c | 170 +++++++++++++++++++ > libavformat/Makefile | 1 + > libavformat/allformats.c | 1 + > libavformat/evcdec.c | 276 +++++++++++++++++++++++++++++++ > 5 files changed, 449 insertions(+) > create mode 100644 libavcodec/evc_frame_merge_bsf.c > create mode 100644 libavformat/evcdec.c > > diff --git a/libavcodec/Makefile b/libavcodec/Makefile > index 13e6582be3..376c6465cf 100644 > --- a/libavcodec/Makefile > +++ b/libavcodec/Makefile > @@ -1257,6 +1257,7 @@ OBJS-$(CONFIG_VP9_METADATA_BSF) += > vp9_metadata_bsf.o > OBJS-$(CONFIG_VP9_RAW_REORDER_BSF) += vp9_raw_reorder_bsf.o > OBJS-$(CONFIG_VP9_SUPERFRAME_BSF) += vp9_superframe_bsf.o > OBJS-$(CONFIG_VP9_SUPERFRAME_SPLIT_BSF) += vp9_superframe_split_bsf.o > +OBJS-$(CONFIG_EVC_FRAME_MERGE_BSF) += evc_frame_merge_bsf.o > > # thread libraries > OBJS-$(HAVE_LIBC_MSVCRT) += file_open.o > diff --git a/libavcodec/evc_frame_merge_bsf.c > b/libavcodec/evc_frame_merge_bsf.c > new file mode 100644 > index 0000000000..f7c4e18d76 > --- /dev/null > +++ b/libavcodec/evc_frame_merge_bsf.c > @@ -0,0 +1,170 @@ > +/* > + * Copyright (c) 2019 James Almer <jamr...@gmail.com> > + * > + * 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 "get_bits.h" > +#include "golomb.h" > +#include "bsf.h" > +#include "bsf_internal.h" > +#include "avcodec.h" > + > +#include "evc.h" > +#include "evc_parse.h" > + > +#define INIT_AU_BUF_CAPACITY 1024 > + > +// Access unit data > +typedef struct AccessUnitBuffer { > + uint8_t *data; // the data buffer > + size_t data_size; // size of data in bytes > + size_t capacity; // buffer capacity > +} AccessUnitBuffer; > + > +typedef struct EVCFMergeContext { > + AVPacket *in; > + EVCParserContext parser_ctx; > + AccessUnitBuffer au_buffer; > +} EVCFMergeContext; > + > +static int end_of_access_unit_found(EVCParserContext *parser_ctx) > +{ > + if (parser_ctx->profile == 0) { // BASELINE profile > + if (parser_ctx->nalu_type == EVC_NOIDR_NUT || > parser_ctx->nalu_type == EVC_IDR_NUT) > + return 1; > + } else { // MAIN profile > + if (parser_ctx->nalu_type == EVC_NOIDR_NUT) { > + if (parser_ctx->poc.PicOrderCntVal != > parser_ctx->poc.prevPicOrderCntVal) > + return 1; > + } else if (parser_ctx->nalu_type == EVC_IDR_NUT) > + return 1; > + } > + return 0; > +} > + > +static void evc_frame_merge_flush(AVBSFContext *bsf) > +{ > + EVCFMergeContext *ctx = bsf->priv_data; > + > + av_packet_unref(ctx->in); > +} > + > +static int evc_frame_merge_filter(AVBSFContext *bsf, AVPacket *out) > +{ > + EVCFMergeContext *ctx = bsf->priv_data; > + EVCParserContext *parser_ctx = &ctx->parser_ctx; > + > + AVPacket *in = ctx->in; > + > + int free_space = 0; > + size_t nalu_size = 0; > + uint8_t *nalu = NULL; > + int au_end_found = 0; > + int err; > + > + err = ff_bsf_get_packet_ref(bsf, in); > + if (err < 0) > + return err; > + > + nalu_size = ff_evc_read_nal_unit_length(in->data, > EVC_NALU_LENGTH_PREFIX_SIZE, bsf); > + if(nalu_size <= 0) { > Keep code style consistent. Here is missing space, ' '. + av_packet_unref(in); > + return AVERROR_INVALIDDATA; > + } > + > + nalu = in->data + EVC_NALU_LENGTH_PREFIX_SIZE; > + nalu_size = in->size - EVC_NALU_LENGTH_PREFIX_SIZE; > + > + // NAL unit parsing needed to determine if end of AU was found > + err = ff_evc_parse_nal_unit(parser_ctx, nalu, nalu_size, bsf); > + if (err < 0) { > + av_log(bsf, AV_LOG_ERROR, "NAL Unit parsing error\n"); > + av_packet_unref(in); > + > + return err; > + } > + > + au_end_found = end_of_access_unit_found(parser_ctx); > + > + free_space = ctx->au_buffer.capacity - ctx->au_buffer.data_size; > + while( free_space < in->size ) { > Wrong style, keep style consistent with already existing files in libavformat. > + ctx->au_buffer.capacity *= 2; > + free_space = ctx->au_buffer.capacity - ctx->au_buffer.data_size; > + > + if(free_space >= in->size) > + ctx->au_buffer.data = av_realloc(ctx->au_buffer.data, > ctx->au_buffer.capacity); > + } > + > + memcpy(ctx->au_buffer.data + ctx->au_buffer.data_size, in->data, > in->size); > + > + ctx->au_buffer.data_size += in->size; > + > + av_packet_unref(in); > + > + if(au_end_found) { > + uint8_t *data = av_memdup(ctx->au_buffer.data, > ctx->au_buffer.data_size); > + err = av_packet_from_data(out, data, ctx->au_buffer.data_size); > + > + ctx->au_buffer.data_size = 0; > + } else > + err = AVERROR(EAGAIN); > + > + if (err < 0 && err != AVERROR(EAGAIN)) > + evc_frame_merge_flush(bsf); > + > + return err; > +} > + > +static int evc_frame_merge_init(AVBSFContext *bsf) > +{ > + EVCFMergeContext *ctx = bsf->priv_data; > + > + ctx->in = av_packet_alloc(); > + if (!ctx->in) > + return AVERROR(ENOMEM); > + > + ctx->au_buffer.capacity = INIT_AU_BUF_CAPACITY; > + ctx->au_buffer.data = av_malloc(INIT_AU_BUF_CAPACITY); > + ctx->au_buffer.data_size = 0; > + > + return 0; > +} > + > +static void evc_frame_merge_close(AVBSFContext *bsf) > +{ > + EVCFMergeContext *ctx = bsf->priv_data; > + > + av_packet_free(&ctx->in); > + > + ctx->au_buffer.capacity = 0; > + av_freep(&ctx->au_buffer.data); > + ctx->au_buffer.data_size = 0; > +} > + > +static const enum AVCodecID evc_frame_merge_codec_ids[] = { > + AV_CODEC_ID_EVC, AV_CODEC_ID_NONE, > +}; > + > +const FFBitStreamFilter ff_evc_frame_merge_bsf = { > + .p.name = "evc_frame_merge", > + .p.codec_ids = evc_frame_merge_codec_ids, > + .priv_data_size = sizeof(EVCFMergeContext), > + .init = evc_frame_merge_init, > + .flush = evc_frame_merge_flush, > + .close = evc_frame_merge_close, > + .filter = evc_frame_merge_filter, > +}; > diff --git a/libavformat/Makefile b/libavformat/Makefile > index f31135d806..6e4231fda2 100644 > --- a/libavformat/Makefile > +++ b/libavformat/Makefile > @@ -251,6 +251,7 @@ OBJS-$(CONFIG_HCOM_DEMUXER) += hcom.o > pcm.o > OBJS-$(CONFIG_HDS_MUXER) += hdsenc.o > OBJS-$(CONFIG_HEVC_DEMUXER) += hevcdec.o rawdec.o > OBJS-$(CONFIG_HEVC_MUXER) += rawenc.o > +OBJS-$(CONFIG_EVC_DEMUXER) += evcdec.o rawdec.o > OBJS-$(CONFIG_EVC_MUXER) += rawenc.o > OBJS-$(CONFIG_HLS_DEMUXER) += hls.o hls_sample_encryption.o > OBJS-$(CONFIG_HLS_MUXER) += hlsenc.o hlsplaylist.o avc.o > diff --git a/libavformat/allformats.c b/libavformat/allformats.c > index d3871de268..ae604236ae 100644 > --- a/libavformat/allformats.c > +++ b/libavformat/allformats.c > @@ -154,6 +154,7 @@ extern const AVInputFormat ff_ea_cdata_demuxer; > extern const AVInputFormat ff_eac3_demuxer; > extern const FFOutputFormat ff_eac3_muxer; > extern const AVInputFormat ff_epaf_demuxer; > +extern const AVInputFormat ff_evc_demuxer; > extern const FFOutputFormat ff_evc_muxer; > extern const FFOutputFormat ff_f4v_muxer; > extern const AVInputFormat ff_ffmetadata_demuxer; > diff --git a/libavformat/evcdec.c b/libavformat/evcdec.c > new file mode 100644 > index 0000000000..89eda0f53e > --- /dev/null > +++ b/libavformat/evcdec.c > @@ -0,0 +1,276 @@ > +/* > + * RAW EVC video demuxer > + * > + * Copyright (c) 2021 Dawid Kozinski <d.kozin...@samsung.com> > + * > + * 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 "libavcodec/get_bits.h" > +#include "libavcodec/golomb.h" > +#include "libavcodec/internal.h" > +#include "libavcodec/evc.h" > +#include "libavcodec/bsf.h" > + > +#include "libavutil/opt.h" > + > +#include "rawdec.h" > +#include "avformat.h" > +#include "internal.h" > + > + > +#define RAW_PACKET_SIZE 1024 > + > +typedef struct EVCParserContext { > + int got_sps; > + int got_pps; > + int got_idr; > + int got_nonidr; > + > +} EVCParserContext; > + > +typedef struct EVCDemuxContext { > + const AVClass *class; > + AVRational framerate; > + > + AVBSFContext *bsf; > + > +} EVCDemuxContext; > + > +#define DEC AV_OPT_FLAG_DECODING_PARAM > +#define OFFSET(x) offsetof(EVCDemuxContext, x) > +static const AVOption evc_options[] = { > + { "framerate", "", OFFSET(framerate), AV_OPT_TYPE_VIDEO_RATE, {.str = > "25"}, 0, INT_MAX, DEC}, > + { NULL }, > +}; > +#undef OFFSET > + > +static const AVClass evc_demuxer_class = { > + .class_name = "EVC Annex B demuxer", > + .item_name = av_default_item_name, > + .option = evc_options, > + .version = LIBAVUTIL_VERSION_INT, > +}; > + > +static int get_nalu_type(const uint8_t *bits, int bits_size) > +{ > + int unit_type_plus1 = 0; > + > + if (bits_size >= EVC_NALU_HEADER_SIZE) { > + unsigned char *p = (unsigned char *)bits; > + // forbidden_zero_bit > + if ((p[0] & 0x80) != 0) // Cannot get bitstream information. > Malformed bitstream. > + return -1; > + > + // nal_unit_type > + unit_type_plus1 = (p[0] >> 1) & 0x3F; > + } > + > + return unit_type_plus1 - 1; > +} > + > +static uint32_t read_nal_unit_length(const uint8_t *bits, int bits_size) > +{ > + uint32_t nalu_len = 0; > + > + if (bits_size >= EVC_NALU_LENGTH_PREFIX_SIZE) { > + > + int t = 0; > + unsigned char *p = (unsigned char *)bits; > + > + for (int i = 0; i < EVC_NALU_LENGTH_PREFIX_SIZE; i++) > + t = (t << 8) | p[i]; > + > + nalu_len = t; > + if (nalu_len == 0) // Invalid bitstream size > + return 0; > + } > + > + return nalu_len; > +} > + > +static int parse_nal_units(const AVProbeData *p, EVCParserContext *ev) > +{ > + int nalu_type; > + size_t nalu_size; > + unsigned char *bits = (unsigned char *)p->buf; > + int bytes_to_read = p->buf_size; > + > + while (bytes_to_read > EVC_NALU_LENGTH_PREFIX_SIZE) { > + > + nalu_size = read_nal_unit_length(bits, > EVC_NALU_LENGTH_PREFIX_SIZE); > + if (nalu_size == 0) break; > + > + bits += EVC_NALU_LENGTH_PREFIX_SIZE; > + bytes_to_read -= EVC_NALU_LENGTH_PREFIX_SIZE; > + > + if(bytes_to_read < nalu_size) break; > + > + nalu_type = get_nalu_type(bits, bytes_to_read); > + > + if (nalu_type == EVC_SPS_NUT) > + ev->got_sps++; > + else if (nalu_type == EVC_PPS_NUT) > + ev->got_pps++; > + else if (nalu_type == EVC_IDR_NUT ) > + ev->got_idr++; > + else if (nalu_type == EVC_NOIDR_NUT) > + ev->got_nonidr++; > + > + bits += nalu_size; > + bytes_to_read -= nalu_size; > + } > + > + return 0; > +} > + > +static int annexb_probe(const AVProbeData *p) > +{ > + EVCParserContext ev = {0}; > + int ret = parse_nal_units(p, &ev); > + > + if (ret == 0 && ev.got_sps && ev.got_pps && (ev.got_idr || > ev.got_nonidr > 3)) > + return AVPROBE_SCORE_EXTENSION + 1; // 1 more than .mpg > + > + return 0; > +} > + > +static int evc_read_header(AVFormatContext *s) > +{ > + AVStream *st; > + FFStream *sti; > + const AVBitStreamFilter *filter = > av_bsf_get_by_name("evc_frame_merge"); > + EVCDemuxContext *c = s->priv_data; > + int ret = 0; > + > + st = avformat_new_stream(s, NULL); > + if (!st) { > + ret = AVERROR(ENOMEM); > + goto fail; > + } > + sti = ffstream(st); > + > + st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; > + st->codecpar->codec_id = AV_CODEC_ID_EVC; > + > + // This causes sending to the parser full frames, not chunks of data > + // The flag PARSER_FLAG_COMPLETE_FRAMES will be set in demux.c > (demux.c: 1316) > + sti->need_parsing = AVSTREAM_PARSE_HEADERS; > + > + st->avg_frame_rate = c->framerate; > + st->codecpar->framerate = c->framerate; > + > + // taken from rawvideo demuxers > + avpriv_set_pts_info(st, 64, 1, 1200000); > + > + ret = av_bsf_alloc(filter, &c->bsf); > + if (ret < 0) > + return ret; > + > + ret = avcodec_parameters_copy(c->bsf->par_in, st->codecpar); > + if (ret < 0) > + return ret; > + > + ret = av_bsf_init(c->bsf); > + if (ret < 0) > + return ret; > + > +fail: > + return ret; > +} > + > +static int evc_read_packet(AVFormatContext *s, AVPacket *pkt) > +{ > + int ret; > + int32_t nalu_size; > + int au_end_found; > + > + EVCDemuxContext *const c = s->priv_data; > + > + int eof = avio_feof (s->pb); > + if(eof) { > + av_packet_unref(pkt); > + return AVERROR_EOF; > + } > + > + au_end_found = 0; > + > + while(!au_end_found) { > + > + uint8_t buf[EVC_NALU_LENGTH_PREFIX_SIZE]; > + ret = avio_read(s->pb, (unsigned char *)&buf, > EVC_NALU_LENGTH_PREFIX_SIZE); > + if (ret < 0) { > + av_packet_unref(pkt); > + return ret; > + } > + > + nalu_size = read_nal_unit_length((const uint8_t *)&buf, > EVC_NALU_LENGTH_PREFIX_SIZE); > + if(nalu_size <= 0) { > + av_packet_unref(pkt); > + return -1; > + } > + > + avio_seek(s->pb, -EVC_NALU_LENGTH_PREFIX_SIZE, SEEK_CUR); > + > + ret = av_get_packet(s->pb, pkt, nalu_size + > EVC_NALU_LENGTH_PREFIX_SIZE); > + if (ret < 0) > + return ret; > + if (ret != (nalu_size + EVC_NALU_LENGTH_PREFIX_SIZE)) > + return AVERROR(EIO); > + > + ret = av_bsf_send_packet(c->bsf, pkt); > + if (ret < 0) { > + av_log(s, AV_LOG_ERROR, "Failed to send packet to " > + "evc_frame_merge filter\n"); > + return ret; > + } > + > + ret = av_bsf_receive_packet(c->bsf, pkt); > + if (ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF) > + av_log(s, AV_LOG_ERROR, "evc_frame_merge filter failed to " > + "send output packet\n"); > + > + au_end_found = 1; > + if (ret == AVERROR(EAGAIN)) > + au_end_found = 0; > + } > + > + return ret; > +} > + > +static int evc_read_close(AVFormatContext *s) > +{ > + EVCDemuxContext *const c = s->priv_data; > + > + av_bsf_free(&c->bsf); > + return 0; > +} > + > +const AVInputFormat ff_evc_demuxer = { > + .name = "evc", > + .long_name = NULL_IF_CONFIG_SMALL("EVC Annex B"), > + .read_probe = annexb_probe, > + .read_header = evc_read_header, // annexb_read_header > + .read_packet = evc_read_packet, // annexb_read_packet > + .read_close = evc_read_close, > + .extensions = "evc", > + .flags = AVFMT_GENERIC_INDEX, > + .flags_internal = FF_FMT_INIT_CLEANUP, > + .raw_codec_id = AV_CODEC_ID_EVC, > + .priv_data_size = sizeof(EVCDemuxContext), > + .priv_class = &evc_demuxer_class, > +}; > -- > 2.25.1 > > _______________________________________________ > ffmpeg-devel mailing list > ffmpeg-devel@ffmpeg.org > https://ffmpeg.org/mailman/listinfo/ffmpeg-devel > > To unsubscribe, visit link above, or email > ffmpeg-devel-requ...@ffmpeg.org with subject "unsubscribe". > _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-requ...@ffmpeg.org with subject "unsubscribe".