* [FFmpeg-devel] [PATCH v1 4/8] avformat/apv_demuxer: Added demuxer to handle reading APV video files
[not found] <CGME20250423141255eucas1p1a1c35cdfe1d12ca5615a4f6533abb69a@eucas1p1.samsung.com>
@ 2025-04-23 14:12 ` Dawid Kozinski
0 siblings, 0 replies; only message in thread
From: Dawid Kozinski @ 2025-04-23 14:12 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Dawid Kozinski
- Provided AVInputFormat struct describing APV input format (ff_apv_demuxer)
Signed-off-by: Dawid Kozinski <d.kozinski@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.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/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-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2025-04-23 14:13 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
[not found] <CGME20250423141255eucas1p1a1c35cdfe1d12ca5615a4f6533abb69a@eucas1p1.samsung.com>
2025-04-23 14:12 ` [FFmpeg-devel] [PATCH v1 4/8] avformat/apv_demuxer: Added demuxer to handle reading APV video files Dawid Kozinski
Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
This inbox may be cloned and mirrored by anyone:
git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git
# If you have public-inbox 1.1+ installed, you may
# initialize and index your mirror using the following commands:
public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \
ffmpegdev@gitmailbox.com
public-inbox-index ffmpegdev
Example config snippet for mirrors.
AGPL code for this site: git clone https://public-inbox.org/public-inbox.git