- Provided AVInputFormat struct describing APV input format (ff_apv_demuxer)
Signed-off-by: Dawid Kozinski <d.kozin...@samsung.com> --- libavformat/Makefile | 1 + libavformat/allformats.c | 1 + libavformat/apvdec.c | 441 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 443 insertions(+) create mode 100644 libavformat/apvdec.c diff --git a/libavformat/Makefile b/libavformat/Makefile index 5ccd9ebfb0..5a4ffc1568 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -120,6 +120,7 @@ OBJS-$(CONFIG_APTX_MUXER) += rawenc.o OBJS-$(CONFIG_APTX_HD_DEMUXER) += aptxdec.o OBJS-$(CONFIG_APTX_HD_MUXER) += rawenc.o OBJS-$(CONFIG_APV_MUXER) += rawenc.o +OBJS-$(CONFIG_APV_DEMUXER) += apvdec.o rawdec.o OBJS-$(CONFIG_AQTITLE_DEMUXER) += aqtitledec.o subtitles.o OBJS-$(CONFIG_ARGO_ASF_DEMUXER) += argo_asf.o OBJS-$(CONFIG_ARGO_ASF_MUXER) += argo_asf.o diff --git a/libavformat/allformats.c b/libavformat/allformats.c index 59440f8ad5..b5a23f9c17 100644 --- a/libavformat/allformats.c +++ b/libavformat/allformats.c @@ -72,6 +72,7 @@ extern const FFInputFormat ff_aptx_demuxer; extern const FFOutputFormat ff_aptx_muxer; extern const FFInputFormat ff_aptx_hd_demuxer; extern const FFOutputFormat ff_aptx_hd_muxer; +extern const FFInputFormat ff_apv_demuxer; extern const FFOutputFormat ff_apv_muxer; extern const FFInputFormat ff_aqtitle_demuxer; extern const FFInputFormat ff_argo_asf_demuxer; diff --git a/libavformat/apvdec.c b/libavformat/apvdec.c new file mode 100644 index 0000000000..69b68cbe5e --- /dev/null +++ b/libavformat/apvdec.c @@ -0,0 +1,441 @@ +/* + * RAW APV video demuxer + * + * Copyright (c) 2025 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/apv.h" +#include "libavcodec/apv_parse.h" + +#include "libavutil/opt.h" + +#include "rawdec.h" +#include "avformat.h" +#include "avio_internal.h" +#include "internal.h" + +typedef struct APVParserContext { + int got_frame_data; + + APVPBUHeader pbu_header; + APVFrameInfo frame_info; +} APVParserContext; + +typedef struct APVDemuxContext { + const AVClass *class; + AVRational framerate; + + APVParamSets ps; + +} APVDemuxContext; + +#define DEC AV_OPT_FLAG_DECODING_PARAM +#define OFFSET(x) offsetof(APVDemuxContext, x) +static const AVOption apv_options[] = { + { "framerate", "", OFFSET(framerate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, INT_MAX, DEC}, + { NULL }, +}; +#undef OFFSET + +static const AVClass apv_demuxer_class = { + .class_name = "APV Annex B demuxer", + .item_name = av_default_item_name, + .option = apv_options, + .version = LIBAVUTIL_VERSION_INT, +}; + +// @see https://www.ietf.org/archive/id/draft-lim-apv-04.html#section-5.3.3 +// @see https://www.ietf.org/archive/id/draft-lim-apv-04.html#name-primitive-bitstream-unit-he +static int apv_parse_pbu_header(GetBitContext *gb, APVPBUHeader *pbuh) +{ + pbuh->pbu_type = get_bits(gb, 8); + pbuh->group_id = get_bits(gb, 16); + pbuh->reserved_zer_8bits = get_bits(gb, 8); + + return 0; +} + +// https://www.ietf.org/archive/id/draft-lim-apv-04.html#name-frame-information +static int apv_parse_frame_info(GetBitContext *gb, APVFrameInfo *frame_info) +{ + frame_info->profile_idc = get_bits(gb, 8); + frame_info->level_idc = get_bits(gb, 8); + frame_info->band_idc = get_bits(gb, 3); + frame_info->reserved_zero_5bits = get_bits(gb, 5); + frame_info->frame_width = get_bits(gb, 24); + frame_info->frame_height = get_bits(gb, 24); + frame_info->chroma_format_idc = get_bits(gb, 4); + frame_info->bit_depth_minus8 = get_bits(gb, 4); + frame_info->capture_time_distance = get_bits(gb, 8); + frame_info->reserved_zero_8bits = get_bits(gb, 8); + + return 0; +} + +// The implementation of the probe function is in accordance with the documentation provided in draft-lim-apv-04 version 04. +// https://www.ietf.org/archive/id/draft-lim-apv-04.html +static int apv_annexb_probe(const AVProbeData *p) +{ + APVParserContext ev = {0}; + + GetBitContext gb; + + unsigned char *bs = (unsigned char *)p->buf; + int bs_size = p->buf_size; + int ret = 0; + uint32_t signature; + + if (bs_size < APV_PBU_SIZE_PREFIX_LENGTH + APV_AU_SIZE_PREFIX_LENGTH + APV_PBU_HEADER_SIZE) + return 0; + + ret = init_get_bits8(&gb, bs, bs_size); + if (ret < 0) + return 0; + + // skip a four-byte length Access Unit Size syntax element, which indicates the size of the AU in bytes + skip_bits_long(&gb, 32); + + // A four-character code that identifies the bitstream as an APV AU. The value MUST be 'aPv1' (0x61507631) + signature = get_bits_long(&gb, 32); + if (signature != 0x61507631) { + return 0; + } + + // skip a four-byte length PBU Size syntax element, which indicates the size of the PBU in bytes + skip_bits_long(&gb, 32); + + apv_parse_pbu_header(&gb, &ev.pbu_header); + + if( ev.pbu_header.pbu_type == 1 || + ev.pbu_header.pbu_type == 2 || + ev.pbu_header.pbu_type == 25 || + ev.pbu_header.pbu_type == 26 || + ev.pbu_header.pbu_type == 27 ) { + + apv_parse_frame_info(&gb, &ev.frame_info); + + // @see https://www.ietf.org/archive/id/draft-lim-apv-04.html#name-422-10-profile + // Conformance of a coded frame to the 422-10 profile is indicated by profile_idc equal to 33 + if(ev.frame_info.profile_idc == 33 && + ev.frame_info.chroma_format_idc == 2 && + ev.frame_info.bit_depth_minus8 == 2 && + ev.pbu_header.pbu_type == 1 ) + { + ev.got_frame_data = 1; + } + // @see https://www.ietf.org/archive/id/draft-lim-apv-04.html#name-422-12-profile + // Conformance of a coded frame to the 422-12 profile is indicated by profile_idc equal to 44 + else if(ev.frame_info.profile_idc == 44 && + ev.frame_info.chroma_format_idc == 2 && + ev.frame_info.bit_depth_minus8 >= 2 && ev.frame_info.bit_depth_minus8 <= 4 && + ev.pbu_header.pbu_type == 1 ) + { + ev.got_frame_data = 1; + } + // @see https://www.ietf.org/archive/id/draft-lim-apv-04.html#name-444-10-profile + // Conformance of a coded frame to the 444-10 profile is indicated by profile_idc equal to 55 + else if(ev.frame_info.profile_idc == 55 && + ev.frame_info.chroma_format_idc >= 2 && ev.frame_info.chroma_format_idc <= 3 && + ev.frame_info.bit_depth_minus8 == 2 && + ev.pbu_header.pbu_type == 1 ) + { + ev.got_frame_data = 1; + } + // @see https://www.ietf.org/archive/id/draft-lim-apv-04.html#name-444-12-profile + // Conformance of a coded frame to the 444-12 profile is indicated by profile_idc equal to 66 + else if(ev.frame_info.profile_idc == 66 && + ev.frame_info.chroma_format_idc >= 2 && ev.frame_info.chroma_format_idc <= 3 && + ev.frame_info.bit_depth_minus8 >= 2 && ev.frame_info.bit_depth_minus8 <= 4 && + ev.pbu_header.pbu_type == 1 ) + { + ev.got_frame_data = 1; + } + // @see https://www.ietf.org/archive/id/draft-lim-apv-04.html#name-4444-10-profile + // Conformance of a coded frame to the 4444-10 profile is indicated by profile_idc equal to 77 + else if(ev.frame_info.profile_idc == 77 && + ev.frame_info.chroma_format_idc >= 2 && ev.frame_info.chroma_format_idc <= 4 && + ev.frame_info.bit_depth_minus8 == 2 && + ev.pbu_header.pbu_type == 1 ) + { + ev.got_frame_data = 1; + } + // @see https://www.ietf.org/archive/id/draft-lim-apv-04.html#name-4444-12-profile + // Conformance of a coded frame to the 4444-12 profile is indicated by profile_idc equal to 88 + else if(ev.frame_info.profile_idc == 88 && + ev.frame_info.chroma_format_idc >= 2 && ev.frame_info.chroma_format_idc <= 4 && + ev.frame_info.bit_depth_minus8 >= 2 && ev.frame_info.bit_depth_minus8 <= 4 && + ev.pbu_header.pbu_type == 1 ) + { + ev.got_frame_data = 1; + } + // @see https://www.ietf.org/archive/id/draft-lim-apv-04.html#name-400-10-profile + // Conformance of a coded frame to the 400-10 profile is indicated by profile_idc equal to 99 + else if(ev.frame_info.profile_idc == 99 && + ev.frame_info.chroma_format_idc == 0 && + ev.frame_info.bit_depth_minus8 == 2 && + ev.pbu_header.pbu_type == 1 ) + { + ev.got_frame_data = 1; + } + } + + if (ev.got_frame_data && + ev.pbu_header.reserved_zer_8bits == 0 && + ev.frame_info.reserved_zero_5bits == 0 && + ev.frame_info.reserved_zero_8bits == 0 ) { + return AVPROBE_SCORE_MAX; + } + + return 0; +} + +static int apv_read_header(AVFormatContext *s) +{ + AVStream *st; + FFStream *sti; + + APVDemuxContext *c = s->priv_data; + APVFrameInfo frame_info; + APVPBUHeader pbu_header; + + GetBitContext gb; + int ret; + uint32_t to_read = 0; + + int eof = avio_feof (s->pb); + if(eof) { + return AVERROR_EOF; + } + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + sti = ffstream(st); + + ret = init_get_bits8(&gb, s->pb->buffer, s->pb->buffer_size); + if (ret < 0) + return 0; + + to_read += APV_AU_SIZE_PREFIX_LENGTH; + to_read += APV_SIGNATURE_LENGTH; + to_read += APV_PBU_SIZE_PREFIX_LENGTH; + to_read += APV_PBU_HEADER_SIZE; + to_read += APV_FRAME_INFO_SIZE; + + if(s->pb->buffer_size < to_read) + return 0; + + skip_bits_long(&gb, 32); // AU size + skip_bits_long(&gb, 32); // signature + skip_bits_long(&gb, 32); // PBU size + + apv_parse_pbu_header(&gb, &pbu_header); + + if((1 <= pbu_header.pbu_type && pbu_header.pbu_type <=2) || + (25 <= pbu_header.pbu_type && pbu_header.pbu_type <= 27)) { + apv_parse_frame_info(&gb, &frame_info); + } else if(pbu_header.pbu_type == 65) { + apv_parse_frame_info(&gb, &frame_info); + } else { + return 0; + } + + st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; + st->codecpar->codec_id = AV_CODEC_ID_APV; + st->codecpar->width = frame_info.frame_width; + st->codecpar->height = frame_info.frame_height; + + // @see https://www.ietf.org/archive/id/draft-lim-apv-04.html#name-422-10-profile + // Conformance of a coded frame to the 422-10 profile is indicated by profile_idc equal to 33 + if(frame_info.profile_idc == 33 && + frame_info.chroma_format_idc == 2 && + frame_info.bit_depth_minus8 == 2 && + pbu_header.pbu_type == 1 ) + { + st->codecpar->format = AV_PIX_FMT_YUV422P10; + } + // @see https://www.ietf.org/archive/id/draft-lim-apv-04.html#name-422-12-profile + // Conformance of a coded frame to the 422-12 profile is indicated by profile_idc equal to 44 + else if(frame_info.profile_idc == 44 && + frame_info.chroma_format_idc == 2 && + frame_info.bit_depth_minus8 >= 2 && frame_info.bit_depth_minus8 <= 4 && + pbu_header.pbu_type == 1 ) + { + if(frame_info.bit_depth_minus8 == 2) { + st->codecpar->format = AV_PIX_FMT_YUV422P10; + } else { + st->codecpar->format = AV_PIX_FMT_YUV422P12; + } + } + // @see https://www.ietf.org/archive/id/draft-lim-apv-04.html#name-444-10-profile + // Conformance of a coded frame to the 444-10 profile is indicated by profile_idc equal to 55 + else if(frame_info.profile_idc == 55 && + frame_info.chroma_format_idc >= 2 && frame_info.chroma_format_idc <= 3 && + frame_info.bit_depth_minus8 == 2 && + pbu_header.pbu_type == 1 ) + { + if(frame_info.chroma_format_idc == 2) { + st->codecpar->format = AV_PIX_FMT_YUV422P10; + } else { + st->codecpar->format = AV_PIX_FMT_YUV444P10; + } + } + // @see https://www.ietf.org/archive/id/draft-lim-apv-04.html#name-444-12-profile + // Conformance of a coded frame to the 444-12 profile is indicated by profile_idc equal to 66 + else if(frame_info.profile_idc == 66 && + frame_info.chroma_format_idc >= 2 && frame_info.chroma_format_idc <= 3 && + frame_info.bit_depth_minus8 >= 2 && frame_info.bit_depth_minus8 <= 4 && + pbu_header.pbu_type == 1 ) + { + if(frame_info.chroma_format_idc == 2) { + if(frame_info.bit_depth_minus8 == 2) { + st->codecpar->format = AV_PIX_FMT_YUV422P10; + } else { + st->codecpar->format = AV_PIX_FMT_YUV422P12; + } + } else { + if(frame_info.bit_depth_minus8 == 2) { + st->codecpar->format = AV_PIX_FMT_YUV444P10; + } else { + st->codecpar->format = AV_PIX_FMT_YUV444P12; + } + } + } + // @see https://www.ietf.org/archive/id/draft-lim-apv-04.html#name-4444-10-profile + // Conformance of a coded frame to the 4444-10 profile is indicated by profile_idc equal to 77 + else if(frame_info.profile_idc == 77 && + frame_info.chroma_format_idc >= 2 && frame_info.chroma_format_idc <= 4 && + frame_info.bit_depth_minus8 == 2 && + pbu_header.pbu_type == 1 ) + { + if(frame_info.chroma_format_idc == 2) { + st->codecpar->format = AV_PIX_FMT_YUV422P10; + } else if (frame_info.chroma_format_idc == 3) { + st->codecpar->format = AV_PIX_FMT_YUV444P10; + } else { + st->codecpar->format = AV_PIX_FMT_YUVA444P10; + } + } + // @see https://www.ietf.org/archive/id/draft-lim-apv-04.html#name-4444-12-profile + // Conformance of a coded frame to the 4444-12 profile is indicated by profile_idc equal to 88 + else if(frame_info.profile_idc == 88 && + frame_info.chroma_format_idc >= 2 && frame_info.chroma_format_idc <= 4 && + frame_info.bit_depth_minus8 >= 2 && frame_info.bit_depth_minus8 <= 4 && + pbu_header.pbu_type == 1 ) + { + if(frame_info.chroma_format_idc == 2) { + if(frame_info.bit_depth_minus8 == 2) { + st->codecpar->format = AV_PIX_FMT_YUV422P10; + } else { + st->codecpar->format = AV_PIX_FMT_YUV422P12; + } + } else if(frame_info.chroma_format_idc == 3) { + if(frame_info.bit_depth_minus8 == 2) { + st->codecpar->format = AV_PIX_FMT_YUV444P10; + } else { + st->codecpar->format = AV_PIX_FMT_YUV444P12; + } + } else { + if(frame_info.bit_depth_minus8 == 2) { + st->codecpar->format = AV_PIX_FMT_YUVA444P10; + } else { + st->codecpar->format = AV_PIX_FMT_YUVA444P12; + } + } + } + // @see https://www.ietf.org/archive/id/draft-lim-apv-04.html#name-400-10-profile + // Conformance of a coded frame to the 400-10 profile is indicated by profile_idc equal to 99 + else if(frame_info.profile_idc == 99 && + frame_info.chroma_format_idc == 0 && + frame_info.bit_depth_minus8 == 2 && + pbu_header.pbu_type == 1 ) + { + st->codecpar->format = AV_PIX_FMT_GRAY10; + } else { + st->codecpar->format = AV_PIX_FMT_NONE; + } + + sti->need_parsing = PARSER_FLAG_COMPLETE_FRAMES; // AVSTREAM_PARSE_FULL_RAW; + + st->avg_frame_rate = c->framerate; + + avpriv_set_pts_info(st, 64, 1, 1200000); + + return 0; +} + +static int apv_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + int ret; + uint32_t au_size; + uint8_t buf[APV_AU_SIZE_PREFIX_LENGTH]; + + int eof = avio_feof (s->pb); + if(eof) { + return AVERROR_EOF; + } + + ret = ffio_ensure_seekback(s->pb, APV_AU_SIZE_PREFIX_LENGTH); + if (ret < 0) + return ret; + ret = avio_read(s->pb, buf, APV_AU_SIZE_PREFIX_LENGTH); + if (ret < 0) { + return ret; + } + + if (ret != APV_AU_SIZE_PREFIX_LENGTH) + return AVERROR_INVALIDDATA; + + au_size = apv_read_au_size(buf, APV_AU_SIZE_PREFIX_LENGTH, s); + if (!au_size || au_size > INT_MAX) + return AVERROR_INVALIDDATA; + + avio_seek(s->pb, -APV_AU_SIZE_PREFIX_LENGTH, SEEK_CUR); + + ret = av_get_packet(s->pb, pkt, au_size + APV_AU_SIZE_PREFIX_LENGTH); + if (ret < 0) + return ret; + + pkt->pos= avio_tell(s->pb); + pkt->stream_index = 0; + + if (ret != (au_size + APV_AU_SIZE_PREFIX_LENGTH)) { + return AVERROR_INVALIDDATA; + } + + return ret; +} + +const FFInputFormat ff_apv_demuxer = { + .p.name = "apv", + .p.long_name = NULL_IF_CONFIG_SMALL("APV Annex B"), + .p.extensions = "apv", + .p.flags = AVFMT_GENERIC_INDEX | AVFMT_NOTIMESTAMPS, + .p.priv_class = &apv_demuxer_class, + .read_probe = apv_annexb_probe, + .read_header = apv_read_header, + .read_packet = apv_read_packet, + .flags_internal = FF_INFMT_FLAG_INIT_CLEANUP, + .raw_codec_id = AV_CODEC_ID_APV, + .priv_data_size = sizeof(APVDemuxContext), +}; \ No newline at end of file -- 2.34.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".