Message ID | 20230612122916.1894-1-d.kozinski@samsung.com |
---|---|
State | New |
Headers | show |
Series | [FFmpeg-devel,v24,1/9] avcodec/evc_parser: Added parser implementation for EVC format | expand |
Context | Check | Description |
---|---|---|
yinshiyou/make_fate_loongarch64 | success | Make fate finished |
yinshiyou/make_loongarch64 | warning | New warnings during build |
andriy/make_fate_x86 | success | Make fate finished |
andriy/make_x86 | warning | New warnings during build |
On Mon, Jun 12, 2023 at 2:29 PM Dawid Kozinski <d.kozinski@samsung.com> wrote: > - Provided AVInputFormat struct describing EVC input format > (ff_evc_demuxer) > > Signed-off-by: Dawid Kozinski <d.kozinski@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 <jamrial@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.kozinski@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-request@ffmpeg.org with subject "unsubscribe". >
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 <jamrial@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) { + 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 ) { + 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.kozinski@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, +};
- Provided AVInputFormat struct describing EVC input format (ff_evc_demuxer) Signed-off-by: Dawid Kozinski <d.kozinski@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