[FFmpeg-devel] [PATCH 2/6] lavf: APV demuxer
Mark Thompson
sw at jkqxz.net
Sat Apr 19 22:07:00 EEST 2025
Demuxes raw streams as defined in draft spec section 10.2.
---
libavformat/Makefile | 1 +
libavformat/allformats.c | 1 +
libavformat/apvdec.c | 245 +++++++++++++++++++++++++++++++++++++++
3 files changed, 247 insertions(+)
create mode 100644 libavformat/apvdec.c
diff --git a/libavformat/Makefile b/libavformat/Makefile
index a94ac66e7e..ef96c2762e 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -119,6 +119,7 @@ OBJS-$(CONFIG_APTX_DEMUXER) += aptxdec.o
OBJS-$(CONFIG_APTX_MUXER) += rawenc.o
OBJS-$(CONFIG_APTX_HD_DEMUXER) += aptxdec.o
OBJS-$(CONFIG_APTX_HD_MUXER) += rawenc.o
+OBJS-$(CONFIG_APV_DEMUXER) += apvdec.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 445f13f42a..90a4fe64ec 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 FFInputFormat ff_aqtitle_demuxer;
extern const FFInputFormat ff_argo_asf_demuxer;
extern const FFOutputFormat ff_argo_asf_muxer;
diff --git a/libavformat/apvdec.c b/libavformat/apvdec.c
new file mode 100644
index 0000000000..008cbef708
--- /dev/null
+++ b/libavformat/apvdec.c
@@ -0,0 +1,245 @@
+/*
+ * 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/apv.h"
+#include "libavcodec/get_bits.h"
+#include "avformat.h"
+#include "avio_internal.h"
+#include "demux.h"
+#include "internal.h"
+
+
+#define APV_TAG MKBETAG('a', 'P', 'v', '1')
+
+typedef struct APVHeaderInfo {
+ uint8_t pbu_type;
+ uint16_t group_id;
+
+ uint8_t profile_idc;
+ uint8_t level_idc;
+ uint8_t band_idc;
+
+ uint32_t frame_width;
+ uint32_t frame_height;
+
+ uint8_t chroma_format_idc;
+ uint8_t bit_depth_minus8;
+
+ enum AVPixelFormat pixel_format;
+} APVHeaderInfo;
+
+static const enum AVPixelFormat apv_format_table[5][5] = {
+ { AV_PIX_FMT_GRAY8, AV_PIX_FMT_GRAY10, AV_PIX_FMT_GRAY12, AV_PIX_FMT_GRAY14, AV_PIX_FMT_GRAY16 },
+ { 0 }, // 4:2:0 is not valid.
+ { AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV422P12, AV_PIX_FMT_GRAY14, AV_PIX_FMT_YUV422P16 },
+ { AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV444P10, AV_PIX_FMT_YUV444P12, AV_PIX_FMT_GRAY14, AV_PIX_FMT_YUV444P16 },
+ { AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUVA444P10, AV_PIX_FMT_YUVA444P12, AV_PIX_FMT_GRAY14, AV_PIX_FMT_YUVA444P16 },
+};
+
+static int apv_extract_header_info(APVHeaderInfo *info,
+ GetBitContext *gbc)
+{
+ int zero, bit_depth_index;
+
+ info->pbu_type = get_bits(gbc, 8);
+ info->group_id = get_bits(gbc, 16);
+
+ zero = get_bits(gbc, 8);
+ if (zero != 0)
+ return 0;
+
+ if (info->pbu_type != APV_PBU_PRIMARY_FRAME)
+ return 0;
+
+ info->profile_idc = get_bits(gbc, 8);
+ info->level_idc = get_bits(gbc, 8);
+ info->band_idc = get_bits(gbc, 3);
+
+ zero = get_bits(gbc, 5);
+ if (zero != 0)
+ return 0;
+
+ info->frame_width = get_bits(gbc, 24);
+ info->frame_height = get_bits(gbc, 24);
+ if (info->frame_width < 1 || info->frame_width > 65536 ||
+ info->frame_height < 1 || info->frame_height > 65536)
+ return 0;
+
+ info->chroma_format_idc = get_bits(gbc, 4);
+ info->bit_depth_minus8 = get_bits(gbc, 4);
+
+ if (info->bit_depth_minus8 > 8) {
+ return 0;
+ }
+ if (info->bit_depth_minus8 % 2) {
+ // Odd bit depths are technically valid but not useful here.
+ return 0;
+ }
+ bit_depth_index = info->bit_depth_minus8 / 2;
+
+ switch (info->chroma_format_idc) {
+ case APV_CHROMA_FORMAT_400:
+ case APV_CHROMA_FORMAT_422:
+ case APV_CHROMA_FORMAT_444:
+ case APV_CHROMA_FORMAT_4444:
+ info->pixel_format = apv_format_table[info->chroma_format_idc][bit_depth_index];
+ break;
+ default:
+ return 0;
+ }
+
+ skip_bits(gbc, 8); // capture_time_distance
+
+ zero = get_bits(gbc, 8);
+ if (zero != 0)
+ return 0;
+
+ return 1;
+}
+
+static int apv_probe(const AVProbeData *p)
+{
+ GetBitContext gbc;
+ APVHeaderInfo header;
+ uint32_t au_size, tag, pbu_size;
+ int score = AVPROBE_SCORE_EXTENSION + 1;
+ int ret;
+
+ init_get_bits8(&gbc, p->buf, p->buf_size);
+
+ au_size = get_bits_long(&gbc, 32);
+ if (au_size < 16) {
+ // Too small.
+ return 0;
+ }
+ // The spec doesn't have this tag, but the reference software and
+ // all current files do. Treat it as optional and skip if present,
+ // but if it is there then this is definitely an APV file.
+ tag = get_bits_long(&gbc, 32);
+ if (tag == APV_TAG) {
+ pbu_size = get_bits_long(&gbc, 32);
+ score = AVPROBE_SCORE_MAX;
+ } else {
+ pbu_size = tag;
+ }
+ if (pbu_size < 16) {
+ // Too small.
+ return 0;
+ }
+
+ ret = apv_extract_header_info(&header, &gbc);
+ if (ret == 0)
+ return 0;
+ return score;
+}
+
+static int apv_read_header(AVFormatContext *s)
+{
+ AVStream *st;
+ GetBitContext gbc;
+ APVHeaderInfo header;
+ uint8_t buffer[32];
+ uint32_t au_size, tag, pbu_size;
+ int ret, size;
+
+ ret = ffio_ensure_seekback(s->pb, sizeof(buffer));
+ if (ret < 0)
+ return ret;
+ size = avio_read(s->pb, buffer, sizeof(buffer));
+ if (size < 0)
+ return size;
+
+ init_get_bits8(&gbc, buffer, sizeof(buffer));
+
+ au_size = get_bits_long(&gbc, 32);
+ if (au_size < 16) {
+ // Too small.
+ return AVERROR_INVALIDDATA;
+ }
+ tag = get_bits_long(&gbc, 32);
+ if (tag == APV_TAG) {
+ pbu_size = get_bits_long(&gbc, 32);
+ } else {
+ pbu_size = tag;
+ }
+ if (pbu_size < 16) {
+ // Too small.
+ return AVERROR_INVALIDDATA;
+ }
+
+ ret = apv_extract_header_info(&header, &gbc);
+ if (ret == 0)
+ return AVERROR_INVALIDDATA;
+
+ st = avformat_new_stream(s, NULL);
+ if (!st)
+ return AVERROR(ENOMEM);
+
+ st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
+ st->codecpar->codec_id = AV_CODEC_ID_APV;
+ st->codecpar->format = header.pixel_format;
+ st->codecpar->profile = header.profile_idc;
+ st->codecpar->level = header.level_idc;
+ st->codecpar->width = header.frame_width;
+ st->codecpar->height = header.frame_height;
+
+ st->avg_frame_rate = (AVRational){ 30, 1 };
+ avpriv_set_pts_info(st, 64, 1, 30);
+
+ avio_seek(s->pb, -size, SEEK_CUR);
+
+ return 0;
+}
+
+static int apv_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ uint32_t au_size, tag;
+ int ret;
+
+ au_size = avio_rb32(s->pb);
+ if (au_size == 0 && avio_feof(s->pb))
+ return AVERROR_EOF;
+
+ tag = avio_rb32(s->pb);
+ if (tag == APV_TAG) {
+ au_size -= 4;
+ } else {
+ avio_seek(s->pb, -4, SEEK_CUR);
+ }
+
+ if (au_size < 16 || au_size > 1 << 24) {
+ av_log(s, AV_LOG_ERROR, "APV AU is bad\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ ret = av_get_packet(s->pb, pkt, au_size);
+ pkt->flags = AV_PKT_FLAG_KEY;
+
+ return ret;
+}
+
+const FFInputFormat ff_apv_demuxer = {
+ .p.name = "apv",
+ .p.long_name = NULL_IF_CONFIG_SMALL("APV raw bitstream"),
+ .p.extensions = "apv",
+ .p.flags = AVFMT_GENERIC_INDEX | AVFMT_NOTIMESTAMPS,
+ .flags_internal = FF_INFMT_FLAG_INIT_CLEANUP,
+ .read_probe = apv_probe,
+ .read_header = apv_read_header,
+ .read_packet = apv_read_packet,
+};
--
2.47.2
More information about the ffmpeg-devel
mailing list