* [FFmpeg-devel] [PATCH v4] avformat/mov_muxer: Extended MOV muxer to handle APV video content
[not found] <CGME20250714091904eucas1p165fc624c8b63297dc6b69e60f6080aa4@eucas1p1.samsung.com>
@ 2025-07-14 9:18 ` Dawid Kozinski
2025-07-15 15:21 ` James Almer
0 siblings, 1 reply; 2+ messages in thread
From: Dawid Kozinski @ 2025-07-14 9:18 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Dawid Kozinski
- Changes in mov_write_video_tag function to handle APV elementary stream
- Provided structure APVDecoderConfigurationRecord that specifies the decoder configuration information for APV video content
Signed-off-by: Dawid Kozinski <d.kozinski@samsung.com>
---
libavcodec/cbs_apv.c | 19 +-
libavformat/Makefile | 2 +-
libavformat/apv.c | 446 ++++++++++++++++++++++++++++++++++++++++
libavformat/apv.h | 88 ++++++++
libavformat/cbs.h | 1 -
libavformat/cbs_apv.c | 2 +
libavformat/isom_tags.c | 1 +
libavformat/movenc.c | 52 +++++
tests/ref/fate/source | 1 +
9 files changed, 603 insertions(+), 9 deletions(-)
create mode 100644 libavformat/apv.c
create mode 100644 libavformat/apv.h
create mode 100644 libavformat/cbs_apv.c
diff --git a/libavcodec/cbs_apv.c b/libavcodec/cbs_apv.c
index ebf57d3bbb..eebacd8870 100644
--- a/libavcodec/cbs_apv.c
+++ b/libavcodec/cbs_apv.c
@@ -68,7 +68,7 @@ static void cbs_apv_derive_tile_info(APVDerivedTileInfo *ti,
#define HEADER(name) do { \
- ff_cbs_trace_header(ctx, name); \
+ CBS_FUNC(trace_header)(ctx, name); \
} while (0)
#define CHECK(call) do { \
@@ -102,7 +102,7 @@ static void cbs_apv_derive_tile_info(APVDerivedTileInfo *ti,
#define xu(width, name, var, range_min, range_max, subs, ...) do { \
uint32_t value; \
- CHECK(ff_cbs_read_unsigned(ctx, rw, width, #name, \
+ CHECK(CBS_FUNC(read_unsigned)(ctx, rw, width, #name, \
SUBSCRIPTS(subs, __VA_ARGS__), \
&value, range_min, range_max)); \
var = value; \
@@ -124,6 +124,7 @@ static void cbs_apv_derive_tile_info(APVDerivedTileInfo *ti,
#undef infer
#undef byte_alignment
+#if CBS_WRITE
#define WRITE
#define READWRITE write
#define RWContext PutBitContext
@@ -131,7 +132,7 @@ static void cbs_apv_derive_tile_info(APVDerivedTileInfo *ti,
#define xu(width, name, var, range_min, range_max, subs, ...) do { \
uint32_t value = var; \
- CHECK(ff_cbs_write_unsigned(ctx, rw, width, #name, \
+ CHECK(CBS_FUNC(write_unsigned)(ctx, rw, width, #name, \
SUBSCRIPTS(subs, __VA_ARGS__), \
value, range_min, range_max)); \
} while (0)
@@ -157,7 +158,7 @@ static void cbs_apv_derive_tile_info(APVDerivedTileInfo *ti,
#undef xu
#undef infer
#undef byte_alignment
-
+#endif // CBS_WRITE
static int cbs_apv_split_fragment(CodedBitstreamContext *ctx,
CodedBitstreamFragment *frag,
@@ -234,7 +235,7 @@ static int cbs_apv_split_fragment(CodedBitstreamContext *ctx,
// Could select/skip frames based on type/group_id here.
- err = ff_cbs_append_unit_data(frag, pbu_header.pbu_type,
+ err = CBS_FUNC(append_unit_data)(frag, pbu_header.pbu_type,
data, pbu_size, frag->data_ref);
if (err < 0)
goto fail;
@@ -259,7 +260,7 @@ static int cbs_apv_read_unit(CodedBitstreamContext *ctx,
if (err < 0)
return err;
- err = ff_cbs_alloc_unit_content(ctx, unit);
+ err = CBS_FUNC(alloc_unit_content)(ctx, unit);
if (err < 0)
return err;
@@ -316,6 +317,7 @@ static int cbs_apv_write_unit(CodedBitstreamContext *ctx,
CodedBitstreamUnit *unit,
PutBitContext *pbc)
{
+#if CBS_WRITE
int err;
switch (unit->type) {
@@ -358,6 +360,9 @@ static int cbs_apv_write_unit(CodedBitstreamContext *ctx,
}
return 0;
+#else
+ return AVERROR(ENOSYS);
+#endif
}
static int cbs_apv_assemble_fragment(CodedBitstreamContext *ctx,
@@ -441,7 +446,7 @@ static const CodedBitstreamUnitTypeDescriptor cbs_apv_unit_types[] = {
CBS_UNIT_TYPE_END_OF_LIST
};
-const CodedBitstreamType ff_cbs_type_apv = {
+const CodedBitstreamType CBS_FUNC(type_apv) = {
.codec_id = AV_CODEC_ID_APV,
.priv_data_size = sizeof(CodedBitstreamAPVContext),
diff --git a/libavformat/Makefile b/libavformat/Makefile
index 816eb9be4a..841a0f3abf 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -381,7 +381,7 @@ OBJS-$(CONFIG_MOV_DEMUXER) += mov.o mov_chan.o mov_esds.o \
OBJS-$(CONFIG_MOV_MUXER) += movenc.o \
movenchint.o mov_chan.o rtp.o \
movenccenc.o movenc_ttml.o rawutils.o \
- dovi_isom.o evc.o cbs.o cbs_av1.o
+ dovi_isom.o evc.o cbs.o cbs_av1.o cbs_apv.o apv.o
OBJS-$(CONFIG_MP2_MUXER) += rawenc.o
OBJS-$(CONFIG_MP3_DEMUXER) += mp3dec.o replaygain.o
OBJS-$(CONFIG_MP3_MUXER) += mp3enc.o rawenc.o id3v2enc.o
diff --git a/libavformat/apv.c b/libavformat/apv.c
new file mode 100644
index 0000000000..1f0ca703ac
--- /dev/null
+++ b/libavformat/apv.c
@@ -0,0 +1,446 @@
+/*
+ * APV helper functions for muxers
+ * 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 <stdbool.h>
+
+#include "libavutil/intreadwrite.h"
+#include "libavutil/mem.h"
+
+#include "libavcodec/golomb.h"
+#include "avformat.h"
+#include "avio.h"
+#include "apv.h"
+#include "cbs.h"
+#include "libavcodec/cbs_apv.h"
+#include "avio_internal.h"
+
+/*****************************************************************************
+ * Frame types
+ *****************************************************************************/
+#define APV_FRAME_TYPE_PRIMARY_FRAME (0)
+#define APV_FRAME_TYPE_NON_PRIMARY_FRAME (1)
+#define APV_FRAME_TYPE_PREVIEW_FRAME (2)
+#define APV_FRAME_TYPE_DEPTH_FRAME (3)
+#define APV_FRAME_TYPE_ALPHA_FRAME (4)
+#define APV_FRAME_TYPE_NON_FRAME (-1)
+#define APV_PBU_FRAME_TYPE_NUM (5)
+#define CONFIGURATIONS_MAX (APV_PBU_FRAME_TYPE_NUM)
+
+typedef struct APVDecoderFrameInfo {
+ uint8_t reserved_zero_6bits; // 6 bits
+ uint8_t color_description_present_flag; // 1 bit
+
+ // The variable indicates whether the capture_time_distance value in the APV bitstream's frame header should be ignored during playback.
+ // If capture_time_distance_ignored is set to true, the capture_time_distance information will not be utilized,
+ // and timing information for playback should be calculated using an alternative method.
+ // If set to false, the capture_time_distance value will be used as is from the frame header.
+ // It is recommended to set this variable to true, allowing the use of MP4 timestamps for playback and recording,
+ // which enables the conventional compression and playback methods based on the timestamp table defined by the ISO-based file format.
+ uint8_t capture_time_distance_ignored; // 1-bit
+
+ uint8_t profile_idc; // 8 bits
+ uint8_t level_idc; // 8 bits
+ uint8_t band_idc; // 8 bits
+ uint32_t frame_width; // 32 bits
+ uint32_t frame_height; // 32 bits
+ uint8_t chroma_format_idc; // 4 bits
+ uint8_t bit_depth_minus8; // 4 bits
+ uint8_t capture_time_distance; // 8 bits
+
+ // if (color_description_present_flag)
+ uint8_t color_primaries; // 8 bits
+ uint8_t transfer_characteristics; // 8 bits
+ uint8_t matrix_coefficients; // 8 bits
+ uint8_t full_range_flag; // 1 bit
+ uint8_t reserved_zero_7bits; // 7 bits
+
+} APVDecoderFrameInfo;
+
+typedef struct APVDecoderConfigurationEntry {
+ uint8_t pbu_type; // 8 bits
+ uint8_t number_of_frame_info; // 8 bits
+
+ APVDecoderFrameInfo** frame_info; // An array of size number_of_frame_info storing elements of type APVDecoderFrameInfo*
+
+} APVDecoderConfigurationEntry;
+
+// ISOBMFF binding for APV
+// @see https://github.com/openapv/openapv/blob/main/readme/apv_isobmff.md
+typedef struct APVDecoderConfigurationRecord {
+ uint8_t configurationVersion; // 8 bits
+ uint8_t number_of_configuration_entry; // 8 bits
+
+ APVDecoderConfigurationEntry configuration_entry[CONFIGURATIONS_MAX]; // table of size number_of_configuration_entry
+
+} APVDecoderConfigurationRecord ;
+
+static void apvc_init(APVDecoderConfigurationRecord * apvc)
+{
+ memset(apvc, 0, sizeof(APVDecoderConfigurationRecord ));
+ apvc->configurationVersion = 1;
+}
+
+static void apvc_close(APVDecoderConfigurationRecord *apvc)
+{
+ for(int i=0;i<apvc->number_of_configuration_entry;i++) {
+ for(int j=0;j<apvc->configuration_entry[i].number_of_frame_info;j++) {
+ free(apvc->configuration_entry[i].frame_info[j]);
+ }
+ free(apvc->configuration_entry[i].frame_info);
+ apvc->configuration_entry[i].number_of_frame_info = 0;
+ }
+ apvc->number_of_configuration_entry = 0;
+}
+
+static int apvc_write(AVIOContext *pb, APVDecoderConfigurationRecord * apvc)
+{
+ av_log(NULL, AV_LOG_TRACE, "configurationVersion: %"PRIu8"\n",
+ apvc->configurationVersion);
+
+ av_log(NULL, AV_LOG_TRACE, "number_of_configuration_entry: %"PRIu8"\n",
+ apvc->number_of_configuration_entry);
+
+ for(int i=0; i<apvc->number_of_configuration_entry;i++) {
+ av_log(NULL, AV_LOG_TRACE, "pbu_type: %"PRIu8"\n",
+ apvc->configuration_entry[i].pbu_type);
+
+ av_log(NULL, AV_LOG_TRACE, "number_of_frame_info: %"PRIu8"\n",
+ apvc->configuration_entry[i].number_of_frame_info);
+
+ for(int j=0; j < apvc->configuration_entry[i].number_of_frame_info; j++) {
+ av_log(NULL, AV_LOG_TRACE, "color_description_present_flag: %"PRIu8"\n",
+ apvc->configuration_entry[i].frame_info[j]->color_description_present_flag);
+
+ av_log(NULL, AV_LOG_TRACE, "capture_time_distance_ignored: %"PRIu8"\n",
+ apvc->configuration_entry[i].frame_info[j]->capture_time_distance_ignored);
+
+ av_log(NULL, AV_LOG_TRACE, "profile_idc: %"PRIu8"\n",
+ apvc->configuration_entry[i].frame_info[j]->profile_idc);
+
+ av_log(NULL, AV_LOG_TRACE, "level_idc: %"PRIu8"\n",
+ apvc->configuration_entry[i].frame_info[j]->level_idc);
+
+ av_log(NULL, AV_LOG_TRACE, "band_idc: %"PRIu8"\n",
+ apvc->configuration_entry[i].frame_info[j]->band_idc);
+
+ av_log(NULL, AV_LOG_TRACE, "frame_width: %"PRIu32"\n",
+ apvc->configuration_entry[i].frame_info[j]->frame_width);
+
+ av_log(NULL, AV_LOG_TRACE, "frame_height: %"PRIu32"\n",
+ apvc->configuration_entry[i].frame_info[j]->frame_height);
+
+ av_log(NULL, AV_LOG_TRACE, "chroma_format_idc: %"PRIu8"\n",
+ apvc->configuration_entry[i].frame_info[j]->chroma_format_idc);
+
+ av_log(NULL, AV_LOG_TRACE, "bit_depth_minus8: %"PRIu8"\n",
+ apvc->configuration_entry[i].frame_info[j]->bit_depth_minus8);
+
+ av_log(NULL, AV_LOG_TRACE, "capture_time_distance: %"PRIu8"\n",
+ apvc->configuration_entry[i].frame_info[j]->capture_time_distance);
+
+ if(apvc->configuration_entry[i].frame_info[j]->color_description_present_flag) {
+
+ av_log(NULL, AV_LOG_TRACE, "color_primaries: %"PRIu8"\n",
+ apvc->configuration_entry[i].frame_info[j]->color_primaries);
+
+ av_log(NULL, AV_LOG_TRACE, "transfer_characteristics: %"PRIu8"\n",
+ apvc->configuration_entry[i].frame_info[j]->transfer_characteristics);
+
+ av_log(NULL, AV_LOG_TRACE, "matrix_coefficients: %"PRIu8"\n",
+ apvc->configuration_entry[i].frame_info[j]->matrix_coefficients);
+
+ av_log(NULL, AV_LOG_TRACE, "full_range_flag: %"PRIu8"\n",
+ apvc->configuration_entry[i].frame_info[j]->full_range_flag);
+ }
+
+ }
+ }
+
+ /* unsigned int(8) configurationVersion = 1; */
+ avio_w8(pb, apvc->configurationVersion);
+
+ avio_w8(pb, apvc->number_of_configuration_entry);
+
+ for(int i=0; i<apvc->number_of_configuration_entry;i++) {
+ avio_w8(pb, apvc->configuration_entry[i].pbu_type);
+ avio_w8(pb, apvc->configuration_entry[i].number_of_frame_info);
+
+ for(int j=0; j < apvc->configuration_entry[i].number_of_frame_info; j++) {
+
+ /* unsigned int(6) reserved_zero_6bits
+ * unsigned int(1) color_description_present_flag
+ * unsigned int(1) capture_time_distance_ignored
+ */
+ avio_w8(pb, apvc->configuration_entry[i].frame_info[j]->reserved_zero_6bits << 2 |
+ apvc->configuration_entry[i].frame_info[j]->color_description_present_flag << 1 |
+ apvc->configuration_entry[i].frame_info[j]->capture_time_distance_ignored);
+
+ /* unsigned int(8) profile_idc */
+ avio_w8(pb, apvc->configuration_entry[i].frame_info[j]->profile_idc);
+
+ /* unsigned int(8) level_idc */
+ avio_w8(pb, apvc->configuration_entry[i].frame_info[j]->level_idc);
+
+ /* unsigned int(8) band_idc */
+ avio_w8(pb, apvc->configuration_entry[i].frame_info[j]->band_idc);
+
+ /* unsigned int(32) frame_width_minus1 */
+ avio_wb32(pb, apvc->configuration_entry[i].frame_info[j]->frame_width);
+
+ /* unsigned int(32) frame_height_minus1 */
+ avio_wb32(pb, apvc->configuration_entry[i].frame_info[j]->frame_height);
+
+ /* unsigned int(4) chroma_format_idc */
+ /* unsigned int(4) bit_depth_minus8 */
+ avio_w8(pb, apvc->configuration_entry[i].frame_info[j]->chroma_format_idc << 4 | apvc->configuration_entry[i].frame_info[j]->bit_depth_minus8);
+
+ /* unsigned int(8) capture_time_distance */
+ avio_w8(pb, apvc->configuration_entry[i].frame_info[j]->capture_time_distance);
+
+ if(apvc->configuration_entry[i].frame_info[j]->color_description_present_flag) {
+ /* unsigned int(8) color_primaries */
+ avio_w8(pb, apvc->configuration_entry[i].frame_info[j]->color_primaries);
+
+ /* unsigned int(8) transfer_characteristics */
+ avio_w8(pb, apvc->configuration_entry[i].frame_info[j]->transfer_characteristics);
+
+ /* unsigned int(8) matrix_coefficients */
+ avio_w8(pb, apvc->configuration_entry[i].frame_info[j]->matrix_coefficients);
+
+ /* unsigned int(1) full_range_flag */
+ avio_w8(pb, apvc->configuration_entry[i].frame_info[j]->full_range_flag << 7 |
+ apvc->configuration_entry[i].frame_info[j]->reserved_zero_7bits);
+ }
+ }
+ }
+
+ return 0;
+}
+
+int ff_isom_write_apvc(AVIOContext *pb, const uint8_t *data,
+ int size, int ps_array_completeness)
+{
+ APVDecoderConfigurationRecord *apvc = (APVDecoderConfigurationRecord *)data;
+ int ret = 0;
+
+ if (size < 8) {
+ /* We can't write a valid apvC from the provided data */
+ return AVERROR_INVALIDDATA;
+ }
+
+ if(size!=sizeof(APVDecoderConfigurationRecord)) return -1;
+ ret = apvc_write(pb, apvc);
+
+ apvc_close(apvc);
+ return ret;
+}
+
+static int apv_add_frameinfo(APVDecoderConfigurationEntry *configuration_entry, APVDecoderFrameInfo *frame_info) {
+ APVDecoderFrameInfo **temp = NULL;
+ if(configuration_entry->number_of_frame_info == 0) {
+ temp = (APVDecoderFrameInfo **)malloc(sizeof(APVDecoderFrameInfo*));
+ if (temp == NULL) {
+ return AVERROR_INVALIDDATA;
+ }
+ } else {
+ temp = (APVDecoderFrameInfo **)realloc(configuration_entry->frame_info, (configuration_entry->number_of_frame_info + 1) * sizeof(APVDecoderFrameInfo*));
+ if (temp == NULL) {
+ return AVERROR_INVALIDDATA;
+ }
+ }
+
+ temp[configuration_entry->number_of_frame_info] = (APVDecoderFrameInfo*)malloc(sizeof(APVDecoderFrameInfo));
+ memcpy(temp[configuration_entry->number_of_frame_info], frame_info, sizeof(APVDecoderFrameInfo));
+
+ configuration_entry->frame_info = temp;
+
+ configuration_entry->number_of_frame_info++;
+
+ return 0;
+}
+
+static bool apv_cmp_frameinfo(const APVDecoderFrameInfo *a, const APVDecoderFrameInfo *b) {
+ if (a->reserved_zero_6bits != b->reserved_zero_6bits) return false;
+ if (a->color_description_present_flag != b->color_description_present_flag) return false;
+ if (a->capture_time_distance_ignored != b->capture_time_distance_ignored) return false;
+ if (a->profile_idc != b->profile_idc) return false;
+ if (a->level_idc != b->level_idc) return false;
+ if (a->band_idc != b->band_idc) return false;
+ if (a->frame_width != b->frame_width) return false;
+ if (a->frame_height != b->frame_height) return false;
+ if (a->chroma_format_idc != b->chroma_format_idc) return false;
+ if (a->bit_depth_minus8 != b->bit_depth_minus8) return false;
+ if (a->capture_time_distance != b->capture_time_distance) return false;
+ if (a->color_primaries != b->color_primaries) return false;
+ if (a->transfer_characteristics != b->transfer_characteristics) return false;
+ if (a->matrix_coefficients != b->matrix_coefficients) return false;
+ if (a->full_range_flag != b->full_range_flag) return false;
+ if (a->reserved_zero_7bits != b->reserved_zero_7bits) return false;
+
+ return true;
+}
+
+int ff_isom_create_apv_dconf_record(uint8_t **data, int *size) {
+ *size = sizeof(APVDecoderConfigurationRecord);
+ *data = (uint8_t*)av_malloc(sizeof(APVDecoderConfigurationRecord));
+ if(*data==NULL) {
+ *size = 0;
+ return AVERROR_INVALIDDATA;
+ }
+ apvc_init((APVDecoderConfigurationRecord*)*data);
+
+ return 0;
+}
+
+void ff_isom_free_apv_dconf_record(uint8_t **data) {
+ if (data != NULL && *data != NULL) {
+ APVDecoderConfigurationRecord* apvc = (APVDecoderConfigurationRecord*)*data;
+ apvc_close(apvc);
+
+ free(*data);
+ *data = NULL;
+ }
+}
+
+static void dummy_free(void *opaque, uint8_t *data)
+{
+ av_assert0(opaque == data);
+}
+
+static const CodedBitstreamUnitType decompose_unit_types[] = {
+ APV_PBU_PRIMARY_FRAME,
+};
+
+int ff_isom_fill_apv_dconf_record(const uint8_t *apvdcr, const uint8_t *data, int size, AVFormatContext *s) {
+
+ uint32_t number_of_pbu_entry = 0;
+
+ uint32_t frame_type = -1;
+ APVDecoderFrameInfo frame_info;
+
+ int bytes_to_read = size; // au size
+ int ret = 0;
+ CodedBitstreamContext *cbc = NULL;
+ CodedBitstreamFragment au = {0};
+ AVBufferRef *ref = NULL;
+
+ APVDecoderConfigurationRecord* apvc = (APVDecoderConfigurationRecord*)apvdcr;
+ if (size < 8) {
+ /* We can't write a valid apvC from the provided data */
+ return AVERROR_INVALIDDATA;
+ }
+
+ ref = av_buffer_create((uint8_t *)data, size, dummy_free,
+ (void *)data, AV_BUFFER_FLAG_READONLY);
+ if (!ref)
+ return AVERROR_INVALIDDATA;
+
+ ret = ff_lavf_cbs_init(&cbc, AV_CODEC_ID_APV, NULL);
+ if (ret < 0)
+ return AVERROR_INVALIDDATA;
+
+ cbc->decompose_unit_types = decompose_unit_types;
+ cbc->nb_decompose_unit_types = FF_ARRAY_ELEMS(decompose_unit_types);
+
+ ret = ff_lavf_cbs_read(cbc, &au, ref, data, size);
+ if (ret < 0) {
+ av_log(s, AV_LOG_ERROR, "Failed to parse access unit.\n");
+ goto end;
+ }
+
+ for (int i = 0; i < au.nb_units; i++) {
+ const CodedBitstreamUnit *pbu = &au.units[i];
+
+ switch (pbu->type)
+ {
+ case APV_PBU_PRIMARY_FRAME:
+ frame_type = APV_FRAME_TYPE_PRIMARY_FRAME;
+ break;
+ case APV_PBU_NON_PRIMARY_FRAME:
+ frame_type = APV_FRAME_TYPE_NON_PRIMARY_FRAME;
+ break;
+ case APV_PBU_PREVIEW_FRAME:
+ frame_type = APV_FRAME_TYPE_PREVIEW_FRAME;
+ break;
+ case APV_PBU_DEPTH_FRAME:
+ frame_type = APV_FRAME_TYPE_DEPTH_FRAME;
+ break;
+ case APV_PBU_ALPHA_FRAME:
+ frame_type = APV_FRAME_TYPE_ALPHA_FRAME;
+ break;
+ default:
+ frame_type = APV_FRAME_TYPE_NON_FRAME;
+ break;
+ };
+
+ if(frame_type == APV_FRAME_TYPE_NON_FRAME) continue;
+
+ const APVRawFrame *frame = pbu->content;
+ const APVRawFrameHeader *header = &frame->frame_header;
+ const APVRawFrameInfo *info = &header->frame_info;
+ int bit_depth = info->bit_depth_minus8 + 8;
+
+ if (bit_depth < 8 || bit_depth > 16 || bit_depth % 2)
+ break;
+
+ frame_info.profile_idc = info->profile_idc;
+ frame_info.level_idc = info->level_idc;
+ frame_info.band_idc = info->band_idc;
+
+ frame_info.frame_width = info->frame_width;
+ frame_info.frame_height =info->frame_height;
+ frame_info.chroma_format_idc = info->chroma_format_idc;
+ frame_info.bit_depth_minus8 = info->bit_depth_minus8;
+ frame_info.capture_time_distance = info->capture_time_distance;
+
+ frame_info.color_description_present_flag = header->color_description_present_flag;
+ if(frame_info.color_description_present_flag) {
+ frame_info.color_primaries = header->color_primaries;
+ frame_info.transfer_characteristics = header->transfer_characteristics;
+ frame_info.matrix_coefficients = header->matrix_coefficients;
+ frame_info.full_range_flag = header->full_range_flag;
+ }
+
+ if(apvc->configuration_entry[frame_type].number_of_frame_info == 0) {
+ apv_add_frameinfo(&apvc->configuration_entry[frame_type], &frame_info);
+ apvc->number_of_configuration_entry++;
+ } else {
+ for(i=0; i<apvc->configuration_entry[frame_type].number_of_frame_info;i++) {
+ if(!apv_cmp_frameinfo(apvc->configuration_entry[frame_type].frame_info[i], &frame_info)) {
+ apv_add_frameinfo(&apvc->configuration_entry[i], &frame_info);
+ break;
+ }
+ }
+ }
+ }
+
+end:
+
+ ff_lavf_cbs_fragment_reset(&au);
+ av_assert1(av_buffer_get_ref_count(ref) == 1);
+ av_buffer_unref(&ref);
+ cbc->log_ctx = NULL;
+
+ ff_lavf_cbs_fragment_free(&au);
+ ff_lavf_cbs_close(&cbc);
+
+ return ret;
+}
diff --git a/libavformat/apv.h b/libavformat/apv.h
new file mode 100644
index 0000000000..ef0c000b32
--- /dev/null
+++ b/libavformat/apv.h
@@ -0,0 +1,88 @@
+/*
+ * APV helper functions for muxers
+ * 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
+ */
+
+#ifndef AVFORMAT_APV_H
+#define AVFORMAT_APV_H
+
+#include <stdint.h>
+
+#include "libavutil/intreadwrite.h"
+#include "libavutil/rational.h"
+#include "libavcodec/apv.h"
+#include "avio.h"
+
+#define APV_AU_SIZE_PREFIX_LENGTH (4)
+
+static inline uint32_t apv_read_au_size(const uint8_t *bits, int bits_size)
+{
+ if (bits_size >= APV_AU_SIZE_PREFIX_LENGTH)
+ return AV_RB32(bits);
+
+ return 0;
+}
+
+/**
+ * Writes APV sample metadata to the provided AVIOContext.
+ *
+ * @param pb pointer to the AVIOContext where the apv sample metadata shall be written
+ * @param buf input data buffer
+ * @param size size in bytes of the input data buffer
+ * @param ps_array_completeness
+ *
+ * @return 0 in case of success, a negative error code in case of failure
+ */
+int ff_isom_write_apvc(AVIOContext *pb, const uint8_t *data,
+ int size, int ps_array_completeness);
+
+/**
+ * @brief Creates and allocates memory for an APV decoder configuration record.
+ *
+ * This function allocates memory for an APVDecoderConfigurationRecord and
+ * initializes it. The size of the record is returned through the `size` parameter.
+ *
+ * @param data Pointer to a pointer where the allocated data will be stored.
+ * @param size Pointer to an integer where the size of the allocated record will be stored.
+ * @return 0 on success, or AVERROR_INVALIDDATA if memory allocation fails.
+ */
+int ff_isom_create_apv_dconf_record(uint8_t **data, int *size);
+
+/**
+ * @brief Frees the memory allocated for the APV decoder configuration record.
+ *
+ * @param data data to be freed
+ */
+void ff_isom_free_apv_dconf_record(uint8_t **data);
+
+/**
+ * @brief Fills an APV decoder configuration record with data.
+ *
+ * This function populates the APVDecoderConfigurationRecord pointed to by
+ * `apvdcr` with the data from `data`, which has a specified size. The data
+ * represents an access unit.
+ *
+ * @param apvdcr Pointer to the APVDecoderConfigurationRecord to be filled.
+ * @param data Pointer to the data to fill the record with.
+ * @param size Size of the data to be copied into the record.
+ * @return 0 on success, or a negative value on error.
+ */
+int ff_isom_fill_apv_dconf_record(const uint8_t *apvc, const uint8_t *data, int size, AVFormatContext *s);
+
+#endif // AVFORMAT_APV_H
diff --git a/libavformat/cbs.h b/libavformat/cbs.h
index 0fab3a7457..e4dc231001 100644
--- a/libavformat/cbs.h
+++ b/libavformat/cbs.h
@@ -22,7 +22,6 @@
#define CBS_PREFIX lavf_cbs
#define CBS_WRITE 0
#define CBS_TRACE 0
-#define CBS_APV 0
#define CBS_H264 0
#define CBS_H265 0
#define CBS_H266 0
diff --git a/libavformat/cbs_apv.c b/libavformat/cbs_apv.c
new file mode 100644
index 0000000000..145e5d09bb
--- /dev/null
+++ b/libavformat/cbs_apv.c
@@ -0,0 +1,2 @@
+#include "cbs.h"
+#include "libavcodec/cbs_apv.c"
diff --git a/libavformat/isom_tags.c b/libavformat/isom_tags.c
index 69174b4a3f..5b4e6c84e1 100644
--- a/libavformat/isom_tags.c
+++ b/libavformat/isom_tags.c
@@ -156,6 +156,7 @@ const AVCodecTag ff_codec_movvideo_tags[] = {
{ AV_CODEC_ID_H264, MKTAG('d', 'v', 'a', 'v') }, /* AVC-based Dolby Vision derived from avc3 */
{ AV_CODEC_ID_EVC, MKTAG('e', 'v', 'c', '1') }, /* EVC/MPEG-5 */
+ { AV_CODEC_ID_APV, MKTAG('a', 'p', 'v', '1') }, /* APV */
{ AV_CODEC_ID_VP8, MKTAG('v', 'p', '0', '8') }, /* VP8 */
{ AV_CODEC_ID_VP9, MKTAG('v', 'p', '0', '9') }, /* VP9 */
diff --git a/libavformat/movenc.c b/libavformat/movenc.c
index a651d6d618..1f9939fb8b 100644
--- a/libavformat/movenc.c
+++ b/libavformat/movenc.c
@@ -37,6 +37,7 @@
#include "av1.h"
#include "avc.h"
#include "evc.h"
+#include "apv.h"
#include "libavcodec/ac3_parser_internal.h"
#include "libavcodec/dnxhddata.h"
#include "libavcodec/flac.h"
@@ -1643,6 +1644,24 @@ static int mov_write_vvcc_tag(AVIOContext *pb, MOVTrack *track)
return update_size(pb, pos);
}
+static int mov_write_apvc_tag(AVIOContext *pb, MOVTrack *track)
+{
+ int64_t pos = avio_tell(pb);
+
+ avio_wb32(pb, 0);
+ ffio_wfourcc(pb, "apvC");
+
+ avio_w8 (pb, 0); /* version */
+ avio_wb24(pb, 0); /* flags */
+
+ if (track->tag == MKTAG('a','p','v','1'))
+ ff_isom_write_apvc(pb, track->vos_data, track->vos_len, 1);
+ else
+ ff_isom_write_apvc(pb, track->vos_data, track->vos_len, 0);
+
+ return update_size(pb, pos);
+}
+
/* also used by all avid codecs (dv, imx, meridien) and their variants */
static int mov_write_avid_tag(AVIOContext *pb, MOVTrack *track)
{
@@ -1902,6 +1921,17 @@ static int mov_get_evc_codec_tag(AVFormatContext *s, MOVTrack *track)
return tag;
}
+static int mov_get_apv_codec_tag(AVFormatContext *s, MOVTrack *track)
+{
+ int tag = track->par->codec_tag;
+
+ if (!tag)
+ tag = MKTAG('a', 'p', 'v', '1');
+
+ return tag;
+}
+
+
static const struct {
enum AVPixelFormat pix_fmt;
uint32_t tag;
@@ -1988,6 +2018,8 @@ static unsigned int mov_get_codec_tag(AVFormatContext *s, MOVTrack *track)
tag = mov_get_h264_codec_tag(s, track);
else if (track->par->codec_id == AV_CODEC_ID_EVC)
tag = mov_get_evc_codec_tag(s, track);
+ else if (track->par->codec_id == AV_CODEC_ID_APV)
+ tag = mov_get_apv_codec_tag(s, track);
else if (track->par->codec_id == AV_CODEC_ID_DNXHD)
tag = mov_get_dnxhd_codec_tag(s, track);
else if (track->par->codec_type == AVMEDIA_TYPE_VIDEO) {
@@ -2753,6 +2785,8 @@ static int mov_write_video_tag(AVFormatContext *s, AVIOContext *pb, MOVMuxContex
}
else if (track->par->codec_id ==AV_CODEC_ID_EVC) {
mov_write_evcc_tag(pb, track);
+ } else if (track->par->codec_id ==AV_CODEC_ID_APV) {
+ mov_write_apvc_tag(pb, track);
} else if (track->par->codec_id == AV_CODEC_ID_VP9) {
mov_write_vpcc_tag(mov->fc, pb, track);
} else if (track->par->codec_id == AV_CODEC_ID_AV1) {
@@ -6714,6 +6748,18 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt)
memset(trk->vos_data + size, 0, AV_INPUT_BUFFER_PADDING_SIZE);
}
+ if (par->codec_id == AV_CODEC_ID_APV && !trk->vos_len) {
+ ret = ff_isom_create_apv_dconf_record(&trk->vos_data, &trk->vos_len);
+ if (!trk->vos_data) {
+ ret = AVERROR(ENOMEM);
+ goto err;
+ }
+ }
+
+ if (par->codec_id == AV_CODEC_ID_APV && trk->vos_len) {
+ ret = ff_isom_fill_apv_dconf_record(trk->vos_data, pkt->data, size, s);
+ }
+
if (par->codec_id == AV_CODEC_ID_AAC && pkt->size > 2 &&
(AV_RB16(pkt->data) & 0xfff0) == 0xfff0) {
if (!trk->st->nb_frames) {
@@ -6839,6 +6885,11 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt)
if (ret) {
goto err;
}
+ } else if (par->codec_id == AV_CODEC_ID_APV) {
+ avio_wb32(s->pb, pkt->size);
+ size += 4;
+
+ avio_write(s->pb, pkt->data, pkt->size);
} else {
avio_write(pb, pkt->data, size);
}
@@ -8658,6 +8709,7 @@ static const AVCodecTag codec_mp4_tags[] = {
{ AV_CODEC_ID_VVC, MKTAG('v', 'v', 'c', '1') },
{ AV_CODEC_ID_VVC, MKTAG('v', 'v', 'i', '1') },
{ AV_CODEC_ID_EVC, MKTAG('e', 'v', 'c', '1') },
+ { AV_CODEC_ID_APV, MKTAG('a', 'p', 'v', '1') },
{ AV_CODEC_ID_MPEG2VIDEO, MKTAG('m', 'p', '4', 'v') },
{ AV_CODEC_ID_MPEG1VIDEO, MKTAG('m', 'p', '4', 'v') },
{ AV_CODEC_ID_MJPEG, MKTAG('m', 'p', '4', 'v') },
diff --git a/tests/ref/fate/source b/tests/ref/fate/source
index d4b9bcee4c..54af72c008 100644
--- a/tests/ref/fate/source
+++ b/tests/ref/fate/source
@@ -11,6 +11,7 @@ libavfilter/file_open.c
libavfilter/log2_tab.c
libavfilter/riscv/cpu_common.c
libavformat/cbs.c
+libavformat/cbs_apv.c
libavformat/cbs_av1.c
libavformat/file_open.c
libavformat/golomb_tab.c
--
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] 2+ messages in thread
* Re: [FFmpeg-devel] [PATCH v4] avformat/mov_muxer: Extended MOV muxer to handle APV video content
2025-07-14 9:18 ` [FFmpeg-devel] [PATCH v4] avformat/mov_muxer: Extended MOV muxer to handle APV video content Dawid Kozinski
@ 2025-07-15 15:21 ` James Almer
0 siblings, 0 replies; 2+ messages in thread
From: James Almer @ 2025-07-15 15:21 UTC (permalink / raw)
To: ffmpeg-devel
[-- Attachment #1.1.1: Type: text/plain, Size: 36547 bytes --]
On 7/14/2025 6:18 AM, Dawid Kozinski wrote:
> - Changes in mov_write_video_tag function to handle APV elementary stream
> - Provided structure APVDecoderConfigurationRecord that specifies the decoder configuration information for APV video content
>
> Signed-off-by: Dawid Kozinski <d.kozinski@samsung.com>
> ---
> libavcodec/cbs_apv.c | 19 +-
> libavformat/Makefile | 2 +-
> libavformat/apv.c | 446 ++++++++++++++++++++++++++++++++++++++++
> libavformat/apv.h | 88 ++++++++
> libavformat/cbs.h | 1 -
> libavformat/cbs_apv.c | 2 +
> libavformat/isom_tags.c | 1 +
> libavformat/movenc.c | 52 +++++
> tests/ref/fate/source | 1 +
> 9 files changed, 603 insertions(+), 9 deletions(-)
> create mode 100644 libavformat/apv.c
> create mode 100644 libavformat/apv.h
> create mode 100644 libavformat/cbs_apv.c
>
> diff --git a/libavcodec/cbs_apv.c b/libavcodec/cbs_apv.c
> index ebf57d3bbb..eebacd8870 100644
> --- a/libavcodec/cbs_apv.c
> +++ b/libavcodec/cbs_apv.c
> @@ -68,7 +68,7 @@ static void cbs_apv_derive_tile_info(APVDerivedTileInfo *ti,
>
>
> #define HEADER(name) do { \
> - ff_cbs_trace_header(ctx, name); \
> + CBS_FUNC(trace_header)(ctx, name); \
> } while (0)
>
> #define CHECK(call) do { \
> @@ -102,7 +102,7 @@ static void cbs_apv_derive_tile_info(APVDerivedTileInfo *ti,
>
> #define xu(width, name, var, range_min, range_max, subs, ...) do { \
> uint32_t value; \
> - CHECK(ff_cbs_read_unsigned(ctx, rw, width, #name, \
> + CHECK(CBS_FUNC(read_unsigned)(ctx, rw, width, #name, \
> SUBSCRIPTS(subs, __VA_ARGS__), \
> &value, range_min, range_max)); \
> var = value; \
> @@ -124,6 +124,7 @@ static void cbs_apv_derive_tile_info(APVDerivedTileInfo *ti,
> #undef infer
> #undef byte_alignment
>
> +#if CBS_WRITE
> #define WRITE
> #define READWRITE write
> #define RWContext PutBitContext
> @@ -131,7 +132,7 @@ static void cbs_apv_derive_tile_info(APVDerivedTileInfo *ti,
>
> #define xu(width, name, var, range_min, range_max, subs, ...) do { \
> uint32_t value = var; \
> - CHECK(ff_cbs_write_unsigned(ctx, rw, width, #name, \
> + CHECK(CBS_FUNC(write_unsigned)(ctx, rw, width, #name, \
> SUBSCRIPTS(subs, __VA_ARGS__), \
> value, range_min, range_max)); \
> } while (0)
> @@ -157,7 +158,7 @@ static void cbs_apv_derive_tile_info(APVDerivedTileInfo *ti,
> #undef xu
> #undef infer
> #undef byte_alignment
> -
> +#endif // CBS_WRITE
>
> static int cbs_apv_split_fragment(CodedBitstreamContext *ctx,
> CodedBitstreamFragment *frag,
> @@ -234,7 +235,7 @@ static int cbs_apv_split_fragment(CodedBitstreamContext *ctx,
>
> // Could select/skip frames based on type/group_id here.
>
> - err = ff_cbs_append_unit_data(frag, pbu_header.pbu_type,
> + err = CBS_FUNC(append_unit_data)(frag, pbu_header.pbu_type,
> data, pbu_size, frag->data_ref);
> if (err < 0)
> goto fail;
> @@ -259,7 +260,7 @@ static int cbs_apv_read_unit(CodedBitstreamContext *ctx,
> if (err < 0)
> return err;
>
> - err = ff_cbs_alloc_unit_content(ctx, unit);
> + err = CBS_FUNC(alloc_unit_content)(ctx, unit);
> if (err < 0)
> return err;
>
> @@ -316,6 +317,7 @@ static int cbs_apv_write_unit(CodedBitstreamContext *ctx,
> CodedBitstreamUnit *unit,
> PutBitContext *pbc)
> {
> +#if CBS_WRITE
> int err;
>
> switch (unit->type) {
> @@ -358,6 +360,9 @@ static int cbs_apv_write_unit(CodedBitstreamContext *ctx,
> }
>
> return 0;
> +#else
> + return AVERROR(ENOSYS);
> +#endif
> }
>
> static int cbs_apv_assemble_fragment(CodedBitstreamContext *ctx,
> @@ -441,7 +446,7 @@ static const CodedBitstreamUnitTypeDescriptor cbs_apv_unit_types[] = {
> CBS_UNIT_TYPE_END_OF_LIST
> };
>
> -const CodedBitstreamType ff_cbs_type_apv = {
> +const CodedBitstreamType CBS_FUNC(type_apv) = {
> .codec_id = AV_CODEC_ID_APV,
>
> .priv_data_size = sizeof(CodedBitstreamAPVContext),
> diff --git a/libavformat/Makefile b/libavformat/Makefile
> index 816eb9be4a..841a0f3abf 100644
> --- a/libavformat/Makefile
> +++ b/libavformat/Makefile
> @@ -381,7 +381,7 @@ OBJS-$(CONFIG_MOV_DEMUXER) += mov.o mov_chan.o mov_esds.o \
> OBJS-$(CONFIG_MOV_MUXER) += movenc.o \
> movenchint.o mov_chan.o rtp.o \
> movenccenc.o movenc_ttml.o rawutils.o \
> - dovi_isom.o evc.o cbs.o cbs_av1.o
> + dovi_isom.o evc.o cbs.o cbs_av1.o cbs_apv.o apv.o
> OBJS-$(CONFIG_MP2_MUXER) += rawenc.o
> OBJS-$(CONFIG_MP3_DEMUXER) += mp3dec.o replaygain.o
> OBJS-$(CONFIG_MP3_MUXER) += mp3enc.o rawenc.o id3v2enc.o
> diff --git a/libavformat/apv.c b/libavformat/apv.c
> new file mode 100644
> index 0000000000..1f0ca703ac
> --- /dev/null
> +++ b/libavformat/apv.c
> @@ -0,0 +1,446 @@
> +/*
> + * APV helper functions for muxers
> + * 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 <stdbool.h>
> +
> +#include "libavutil/intreadwrite.h"
> +#include "libavutil/mem.h"
> +
> +#include "libavcodec/golomb.h"
> +#include "avformat.h"
> +#include "avio.h"
> +#include "apv.h"
> +#include "cbs.h"
> +#include "libavcodec/cbs_apv.h"
> +#include "avio_internal.h"
> +
> +/*****************************************************************************
> + * Frame types
> + *****************************************************************************/
> +#define APV_FRAME_TYPE_PRIMARY_FRAME (0)
> +#define APV_FRAME_TYPE_NON_PRIMARY_FRAME (1)
> +#define APV_FRAME_TYPE_PREVIEW_FRAME (2)
> +#define APV_FRAME_TYPE_DEPTH_FRAME (3)
> +#define APV_FRAME_TYPE_ALPHA_FRAME (4)
> +#define APV_FRAME_TYPE_NON_FRAME (-1)
> +#define APV_PBU_FRAME_TYPE_NUM (5)
> +#define CONFIGURATIONS_MAX (APV_PBU_FRAME_TYPE_NUM)
> +
> +typedef struct APVDecoderFrameInfo {
> + uint8_t reserved_zero_6bits; // 6 bits
No point in having these.
> + uint8_t color_description_present_flag; // 1 bit
> +
> + // The variable indicates whether the capture_time_distance value in the APV bitstream's frame header should be ignored during playback.
> + // If capture_time_distance_ignored is set to true, the capture_time_distance information will not be utilized,
> + // and timing information for playback should be calculated using an alternative method.
> + // If set to false, the capture_time_distance value will be used as is from the frame header.
> + // It is recommended to set this variable to true, allowing the use of MP4 timestamps for playback and recording,
> + // which enables the conventional compression and playback methods based on the timestamp table defined by the ISO-based file format.
> + uint8_t capture_time_distance_ignored; // 1-bit
> +
> + uint8_t profile_idc; // 8 bits
> + uint8_t level_idc; // 8 bits
> + uint8_t band_idc; // 8 bits
> + uint32_t frame_width; // 32 bits
> + uint32_t frame_height; // 32 bits
> + uint8_t chroma_format_idc; // 4 bits
> + uint8_t bit_depth_minus8; // 4 bits
> + uint8_t capture_time_distance; // 8 bits
> +
> + // if (color_description_present_flag)
> + uint8_t color_primaries; // 8 bits
> + uint8_t transfer_characteristics; // 8 bits
> + uint8_t matrix_coefficients; // 8 bits
> + uint8_t full_range_flag; // 1 bit
> + uint8_t reserved_zero_7bits; // 7 bits
> +
> +} APVDecoderFrameInfo;
> +
> +typedef struct APVDecoderConfigurationEntry {
> + uint8_t pbu_type; // 8 bits
> + uint8_t number_of_frame_info; // 8 bits
> +
> + APVDecoderFrameInfo** frame_info; // An array of size number_of_frame_info storing elements of type APVDecoderFrameInfo*
> +
> +} APVDecoderConfigurationEntry;
> +
> +// ISOBMFF binding for APV
> +// @see https://github.com/openapv/openapv/blob/main/readme/apv_isobmff.md
> +typedef struct APVDecoderConfigurationRecord {
> + uint8_t configurationVersion; // 8 bits
> + uint8_t number_of_configuration_entry; // 8 bits
> +
> + APVDecoderConfigurationEntry configuration_entry[CONFIGURATIONS_MAX]; // table of size number_of_configuration_entry
> +
> +} APVDecoderConfigurationRecord ;
> +
> +static void apvc_init(APVDecoderConfigurationRecord * apvc)
> +{
> + memset(apvc, 0, sizeof(APVDecoderConfigurationRecord ));
> + apvc->configurationVersion = 1;
> +}
> +
> +static void apvc_close(APVDecoderConfigurationRecord *apvc)
> +{
> + for(int i=0;i<apvc->number_of_configuration_entry;i++) {
> + for(int j=0;j<apvc->configuration_entry[i].number_of_frame_info;j++) {
> + free(apvc->configuration_entry[i].frame_info[j]);
> + }
> + free(apvc->configuration_entry[i].frame_info);
> + apvc->configuration_entry[i].number_of_frame_info = 0;
> + }
> + apvc->number_of_configuration_entry = 0;
> +}
> +
> +static int apvc_write(AVIOContext *pb, APVDecoderConfigurationRecord * apvc)
> +{
> + av_log(NULL, AV_LOG_TRACE, "configurationVersion: %"PRIu8"\n",
> + apvc->configurationVersion);
> +
> + av_log(NULL, AV_LOG_TRACE, "number_of_configuration_entry: %"PRIu8"\n",
> + apvc->number_of_configuration_entry);
> +
> + for(int i=0; i<apvc->number_of_configuration_entry;i++) {
> + av_log(NULL, AV_LOG_TRACE, "pbu_type: %"PRIu8"\n",
> + apvc->configuration_entry[i].pbu_type);
> +
> + av_log(NULL, AV_LOG_TRACE, "number_of_frame_info: %"PRIu8"\n",
> + apvc->configuration_entry[i].number_of_frame_info);
> +
> + for(int j=0; j < apvc->configuration_entry[i].number_of_frame_info; j++) {
> + av_log(NULL, AV_LOG_TRACE, "color_description_present_flag: %"PRIu8"\n",
> + apvc->configuration_entry[i].frame_info[j]->color_description_present_flag);
> +
> + av_log(NULL, AV_LOG_TRACE, "capture_time_distance_ignored: %"PRIu8"\n",
> + apvc->configuration_entry[i].frame_info[j]->capture_time_distance_ignored);
> +
> + av_log(NULL, AV_LOG_TRACE, "profile_idc: %"PRIu8"\n",
> + apvc->configuration_entry[i].frame_info[j]->profile_idc);
> +
> + av_log(NULL, AV_LOG_TRACE, "level_idc: %"PRIu8"\n",
> + apvc->configuration_entry[i].frame_info[j]->level_idc);
> +
> + av_log(NULL, AV_LOG_TRACE, "band_idc: %"PRIu8"\n",
> + apvc->configuration_entry[i].frame_info[j]->band_idc);
> +
> + av_log(NULL, AV_LOG_TRACE, "frame_width: %"PRIu32"\n",
> + apvc->configuration_entry[i].frame_info[j]->frame_width);
> +
> + av_log(NULL, AV_LOG_TRACE, "frame_height: %"PRIu32"\n",
> + apvc->configuration_entry[i].frame_info[j]->frame_height);
> +
> + av_log(NULL, AV_LOG_TRACE, "chroma_format_idc: %"PRIu8"\n",
> + apvc->configuration_entry[i].frame_info[j]->chroma_format_idc);
> +
> + av_log(NULL, AV_LOG_TRACE, "bit_depth_minus8: %"PRIu8"\n",
> + apvc->configuration_entry[i].frame_info[j]->bit_depth_minus8);
> +
> + av_log(NULL, AV_LOG_TRACE, "capture_time_distance: %"PRIu8"\n",
> + apvc->configuration_entry[i].frame_info[j]->capture_time_distance);
> +
> + if(apvc->configuration_entry[i].frame_info[j]->color_description_present_flag) {
> +
> + av_log(NULL, AV_LOG_TRACE, "color_primaries: %"PRIu8"\n",
> + apvc->configuration_entry[i].frame_info[j]->color_primaries);
> +
> + av_log(NULL, AV_LOG_TRACE, "transfer_characteristics: %"PRIu8"\n",
> + apvc->configuration_entry[i].frame_info[j]->transfer_characteristics);
> +
> + av_log(NULL, AV_LOG_TRACE, "matrix_coefficients: %"PRIu8"\n",
> + apvc->configuration_entry[i].frame_info[j]->matrix_coefficients);
> +
> + av_log(NULL, AV_LOG_TRACE, "full_range_flag: %"PRIu8"\n",
> + apvc->configuration_entry[i].frame_info[j]->full_range_flag);
> + }
> +
> + }
> + }
> +
> + /* unsigned int(8) configurationVersion = 1; */
> + avio_w8(pb, apvc->configurationVersion);
> +
> + avio_w8(pb, apvc->number_of_configuration_entry);
> +
> + for(int i=0; i<apvc->number_of_configuration_entry;i++) {
> + avio_w8(pb, apvc->configuration_entry[i].pbu_type);
> + avio_w8(pb, apvc->configuration_entry[i].number_of_frame_info);
> +
> + for(int j=0; j < apvc->configuration_entry[i].number_of_frame_info; j++) {
> +
> + /* unsigned int(6) reserved_zero_6bits
> + * unsigned int(1) color_description_present_flag
> + * unsigned int(1) capture_time_distance_ignored
> + */
> + avio_w8(pb, apvc->configuration_entry[i].frame_info[j]->reserved_zero_6bits << 2 |
> + apvc->configuration_entry[i].frame_info[j]->color_description_present_flag << 1 |
> + apvc->configuration_entry[i].frame_info[j]->capture_time_distance_ignored);
> +
> + /* unsigned int(8) profile_idc */
> + avio_w8(pb, apvc->configuration_entry[i].frame_info[j]->profile_idc);
> +
> + /* unsigned int(8) level_idc */
> + avio_w8(pb, apvc->configuration_entry[i].frame_info[j]->level_idc);
> +
> + /* unsigned int(8) band_idc */
> + avio_w8(pb, apvc->configuration_entry[i].frame_info[j]->band_idc);
> +
> + /* unsigned int(32) frame_width_minus1 */
> + avio_wb32(pb, apvc->configuration_entry[i].frame_info[j]->frame_width);
> +
> + /* unsigned int(32) frame_height_minus1 */
> + avio_wb32(pb, apvc->configuration_entry[i].frame_info[j]->frame_height);
> +
> + /* unsigned int(4) chroma_format_idc */
> + /* unsigned int(4) bit_depth_minus8 */
> + avio_w8(pb, apvc->configuration_entry[i].frame_info[j]->chroma_format_idc << 4 | apvc->configuration_entry[i].frame_info[j]->bit_depth_minus8);
> +
> + /* unsigned int(8) capture_time_distance */
> + avio_w8(pb, apvc->configuration_entry[i].frame_info[j]->capture_time_distance);
> +
> + if(apvc->configuration_entry[i].frame_info[j]->color_description_present_flag) {
> + /* unsigned int(8) color_primaries */
> + avio_w8(pb, apvc->configuration_entry[i].frame_info[j]->color_primaries);
> +
> + /* unsigned int(8) transfer_characteristics */
> + avio_w8(pb, apvc->configuration_entry[i].frame_info[j]->transfer_characteristics);
> +
> + /* unsigned int(8) matrix_coefficients */
> + avio_w8(pb, apvc->configuration_entry[i].frame_info[j]->matrix_coefficients);
> +
> + /* unsigned int(1) full_range_flag */
> + avio_w8(pb, apvc->configuration_entry[i].frame_info[j]->full_range_flag << 7 |
> + apvc->configuration_entry[i].frame_info[j]->reserved_zero_7bits);
> + }
> + }
> + }
> +
> + return 0;
> +}
> +
> +int ff_isom_write_apvc(AVIOContext *pb, const uint8_t *data,
> + int size, int ps_array_completeness)
> +{
> + APVDecoderConfigurationRecord *apvc = (APVDecoderConfigurationRecord *)data;
> + int ret = 0;
> +
> + if (size < 8) {
> + /* We can't write a valid apvC from the provided data */
> + return AVERROR_INVALIDDATA;
> + }
> +
> + if(size!=sizeof(APVDecoderConfigurationRecord)) return -1;
> + ret = apvc_write(pb, apvc);
> +
> + apvc_close(apvc);
> + return ret;
> +}
> +
> +static int apv_add_frameinfo(APVDecoderConfigurationEntry *configuration_entry, APVDecoderFrameInfo *frame_info) {
> + APVDecoderFrameInfo **temp = NULL;
> + if(configuration_entry->number_of_frame_info == 0) {
> + temp = (APVDecoderFrameInfo **)malloc(sizeof(APVDecoderFrameInfo*));
You're mixing avutil memory allocation functions with system provider
ones. It will lead to crashes.
> + if (temp == NULL) {
> + return AVERROR_INVALIDDATA;
> + }
> + } else {
> + temp = (APVDecoderFrameInfo **)realloc(configuration_entry->frame_info, (configuration_entry->number_of_frame_info + 1) * sizeof(APVDecoderFrameInfo*));
Same.
> + if (temp == NULL) {
> + return AVERROR_INVALIDDATA;
> + }
> + }
> +
> + temp[configuration_entry->number_of_frame_info] = (APVDecoderFrameInfo*)malloc(sizeof(APVDecoderFrameInfo));
> + memcpy(temp[configuration_entry->number_of_frame_info], frame_info, sizeof(APVDecoderFrameInfo));
> +
> + configuration_entry->frame_info = temp;
> +
> + configuration_entry->number_of_frame_info++;
You never set pbu_type.
> +
> + return 0;
> +}
> +
> +static bool apv_cmp_frameinfo(const APVDecoderFrameInfo *a, const APVDecoderFrameInfo *b) {
> + if (a->reserved_zero_6bits != b->reserved_zero_6bits) return false;
> + if (a->color_description_present_flag != b->color_description_present_flag) return false;
> + if (a->capture_time_distance_ignored != b->capture_time_distance_ignored) return false;
> + if (a->profile_idc != b->profile_idc) return false;
> + if (a->level_idc != b->level_idc) return false;
> + if (a->band_idc != b->band_idc) return false;
> + if (a->frame_width != b->frame_width) return false;
> + if (a->frame_height != b->frame_height) return false;
> + if (a->chroma_format_idc != b->chroma_format_idc) return false;
> + if (a->bit_depth_minus8 != b->bit_depth_minus8) return false;
> + if (a->capture_time_distance != b->capture_time_distance) return false;
> + if (a->color_primaries != b->color_primaries) return false;
> + if (a->transfer_characteristics != b->transfer_characteristics) return false;
> + if (a->matrix_coefficients != b->matrix_coefficients) return false;
> + if (a->full_range_flag != b->full_range_flag) return false;
> + if (a->reserved_zero_7bits != b->reserved_zero_7bits) return false;
> +
> + return true;
This entire function can be replaced with a single memcmp.
> +}
> +
> +int ff_isom_create_apv_dconf_record(uint8_t **data, int *size) {
> + *size = sizeof(APVDecoderConfigurationRecord);
> + *data = (uint8_t*)av_malloc(sizeof(APVDecoderConfigurationRecord));
> + if(*data==NULL) {
> + *size = 0;
> + return AVERROR_INVALIDDATA;
> + }
> + apvc_init((APVDecoderConfigurationRecord*)*data);
> +
> + return 0;
> +}
> +
> +void ff_isom_free_apv_dconf_record(uint8_t **data) {
> + if (data != NULL && *data != NULL) {
> + APVDecoderConfigurationRecord* apvc = (APVDecoderConfigurationRecord*)*data;
> + apvc_close(apvc);
> +
> + free(*data);
Don't use system provided free().
Also, this function is never called.
> + *data = NULL;
> + }
> +}
> +
> +static void dummy_free(void *opaque, uint8_t *data)
> +{
> + av_assert0(opaque == data);
> +}
> +
> +static const CodedBitstreamUnitType decompose_unit_types[] = {
> + APV_PBU_PRIMARY_FRAME,
What's the point in having five entries in the configuration_entry array
if you're only going to parse one frame type?
> +};
> +
> +int ff_isom_fill_apv_dconf_record(const uint8_t *apvdcr, const uint8_t *data, int size, AVFormatContext *s) {
> +
> + uint32_t number_of_pbu_entry = 0;
> +
> + uint32_t frame_type = -1;
> + APVDecoderFrameInfo frame_info;
> +
> + int bytes_to_read = size; // au size
> + int ret = 0;
> + CodedBitstreamContext *cbc = NULL;
> + CodedBitstreamFragment au = {0};
> + AVBufferRef *ref = NULL;
> +
> + APVDecoderConfigurationRecord* apvc = (APVDecoderConfigurationRecord*)apvdcr;
You're casting the const away.
> + if (size < 8) {
> + /* We can't write a valid apvC from the provided data */
> + return AVERROR_INVALIDDATA;
> + }
> +
> + ref = av_buffer_create((uint8_t *)data, size, dummy_free,
> + (void *)data, AV_BUFFER_FLAG_READONLY);
> + if (!ref)
> + return AVERROR_INVALIDDATA;
> +
> + ret = ff_lavf_cbs_init(&cbc, AV_CODEC_ID_APV, NULL);
> + if (ret < 0)
> + return AVERROR_INVALIDDATA;
> +
> + cbc->decompose_unit_types = decompose_unit_types;
> + cbc->nb_decompose_unit_types = FF_ARRAY_ELEMS(decompose_unit_types);
> +
> + ret = ff_lavf_cbs_read(cbc, &au, ref, data, size);
> + if (ret < 0) {
> + av_log(s, AV_LOG_ERROR, "Failed to parse access unit.\n");
> + goto end;
> + }
> +
> + for (int i = 0; i < au.nb_units; i++) {
> + const CodedBitstreamUnit *pbu = &au.units[i];
> +
> + switch (pbu->type)
> + {
> + case APV_PBU_PRIMARY_FRAME:
> + frame_type = APV_FRAME_TYPE_PRIMARY_FRAME;
> + break;
> + case APV_PBU_NON_PRIMARY_FRAME:
> + frame_type = APV_FRAME_TYPE_NON_PRIMARY_FRAME;
> + break;
> + case APV_PBU_PREVIEW_FRAME:
> + frame_type = APV_FRAME_TYPE_PREVIEW_FRAME;
> + break;
> + case APV_PBU_DEPTH_FRAME:
> + frame_type = APV_FRAME_TYPE_DEPTH_FRAME;
> + break;
> + case APV_PBU_ALPHA_FRAME:
> + frame_type = APV_FRAME_TYPE_ALPHA_FRAME;
> + break;
> + default:
> + frame_type = APV_FRAME_TYPE_NON_FRAME;
> + break;
> + };
> +
> + if(frame_type == APV_FRAME_TYPE_NON_FRAME) continue;
> +
> + const APVRawFrame *frame = pbu->content;
> + const APVRawFrameHeader *header = &frame->frame_header;
> + const APVRawFrameInfo *info = &header->frame_info;
> + int bit_depth = info->bit_depth_minus8 + 8;
> +
> + if (bit_depth < 8 || bit_depth > 16 || bit_depth % 2)
> + break;
> +
> + frame_info.profile_idc = info->profile_idc;
> + frame_info.level_idc = info->level_idc;
> + frame_info.band_idc = info->band_idc;
> +
> + frame_info.frame_width = info->frame_width;
> + frame_info.frame_height =info->frame_height;
> + frame_info.chroma_format_idc = info->chroma_format_idc;
> + frame_info.bit_depth_minus8 = info->bit_depth_minus8;
> + frame_info.capture_time_distance = info->capture_time_distance;
> +
> + frame_info.color_description_present_flag = header->color_description_present_flag;
> + if(frame_info.color_description_present_flag) {
> + frame_info.color_primaries = header->color_primaries;
> + frame_info.transfer_characteristics = header->transfer_characteristics;
> + frame_info.matrix_coefficients = header->matrix_coefficients;
> + frame_info.full_range_flag = header->full_range_flag;
> + }
> +
> + if(apvc->configuration_entry[frame_type].number_of_frame_info == 0) {
> + apv_add_frameinfo(&apvc->configuration_entry[frame_type], &frame_info);
> + apvc->number_of_configuration_entry++;
> + } else {
> + for(i=0; i<apvc->configuration_entry[frame_type].number_of_frame_info;i++) {
> + if(!apv_cmp_frameinfo(apvc->configuration_entry[frame_type].frame_info[i], &frame_info)) {
> + apv_add_frameinfo(&apvc->configuration_entry[i], &frame_info);
> + break;
> + }
> + }
> + }
> + }
> +
> +end:
> +
> + ff_lavf_cbs_fragment_reset(&au);
> + av_assert1(av_buffer_get_ref_count(ref) == 1);
> + av_buffer_unref(&ref);
> + cbc->log_ctx = NULL;
> +
> + ff_lavf_cbs_fragment_free(&au);
> + ff_lavf_cbs_close(&cbc);
> +
> + return ret;
> +}
> diff --git a/libavformat/apv.h b/libavformat/apv.h
> new file mode 100644
> index 0000000000..ef0c000b32
> --- /dev/null
> +++ b/libavformat/apv.h
> @@ -0,0 +1,88 @@
> +/*
> + * APV helper functions for muxers
> + * 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
> + */
> +
> +#ifndef AVFORMAT_APV_H
> +#define AVFORMAT_APV_H
> +
> +#include <stdint.h>
> +
> +#include "libavutil/intreadwrite.h"
> +#include "libavutil/rational.h"
> +#include "libavcodec/apv.h"
> +#include "avio.h"
> +
> +#define APV_AU_SIZE_PREFIX_LENGTH (4)
> +
> +static inline uint32_t apv_read_au_size(const uint8_t *bits, int bits_size)
> +{
> + if (bits_size >= APV_AU_SIZE_PREFIX_LENGTH)
> + return AV_RB32(bits);
> +
> + return 0;
> +}
Unused.
> +
> +/**
> + * Writes APV sample metadata to the provided AVIOContext.
> + *
> + * @param pb pointer to the AVIOContext where the apv sample metadata shall be written
> + * @param buf input data buffer
> + * @param size size in bytes of the input data buffer
> + * @param ps_array_completeness
> + *
> + * @return 0 in case of success, a negative error code in case of failure
> + */
> +int ff_isom_write_apvc(AVIOContext *pb, const uint8_t *data,
> + int size, int ps_array_completeness);
> +
> +/**
> + * @brief Creates and allocates memory for an APV decoder configuration record.
> + *
> + * This function allocates memory for an APVDecoderConfigurationRecord and
> + * initializes it. The size of the record is returned through the `size` parameter.
> + *
> + * @param data Pointer to a pointer where the allocated data will be stored.
> + * @param size Pointer to an integer where the size of the allocated record will be stored.
> + * @return 0 on success, or AVERROR_INVALIDDATA if memory allocation fails.
> + */
> +int ff_isom_create_apv_dconf_record(uint8_t **data, int *size);
> +
> +/**
> + * @brief Frees the memory allocated for the APV decoder configuration record.
> + *
> + * @param data data to be freed
> + */
> +void ff_isom_free_apv_dconf_record(uint8_t **data);
> +
> +/**
> + * @brief Fills an APV decoder configuration record with data.
> + *
> + * This function populates the APVDecoderConfigurationRecord pointed to by
> + * `apvdcr` with the data from `data`, which has a specified size. The data
> + * represents an access unit.
> + *
> + * @param apvdcr Pointer to the APVDecoderConfigurationRecord to be filled.
> + * @param data Pointer to the data to fill the record with.
> + * @param size Size of the data to be copied into the record.
> + * @return 0 on success, or a negative value on error.
> + */
> +int ff_isom_fill_apv_dconf_record(const uint8_t *apvc, const uint8_t *data, int size, AVFormatContext *s);
> +
> +#endif // AVFORMAT_APV_H
> diff --git a/libavformat/cbs.h b/libavformat/cbs.h
> index 0fab3a7457..e4dc231001 100644
> --- a/libavformat/cbs.h
> +++ b/libavformat/cbs.h
> @@ -22,7 +22,6 @@
> #define CBS_PREFIX lavf_cbs
> #define CBS_WRITE 0
> #define CBS_TRACE 0
> -#define CBS_APV 0
> #define CBS_H264 0
> #define CBS_H265 0
> #define CBS_H266 0
> diff --git a/libavformat/cbs_apv.c b/libavformat/cbs_apv.c
> new file mode 100644
> index 0000000000..145e5d09bb
> --- /dev/null
> +++ b/libavformat/cbs_apv.c
> @@ -0,0 +1,2 @@
> +#include "cbs.h"
> +#include "libavcodec/cbs_apv.c"
> diff --git a/libavformat/isom_tags.c b/libavformat/isom_tags.c
> index 69174b4a3f..5b4e6c84e1 100644
> --- a/libavformat/isom_tags.c
> +++ b/libavformat/isom_tags.c
> @@ -156,6 +156,7 @@ const AVCodecTag ff_codec_movvideo_tags[] = {
> { AV_CODEC_ID_H264, MKTAG('d', 'v', 'a', 'v') }, /* AVC-based Dolby Vision derived from avc3 */
>
> { AV_CODEC_ID_EVC, MKTAG('e', 'v', 'c', '1') }, /* EVC/MPEG-5 */
> + { AV_CODEC_ID_APV, MKTAG('a', 'p', 'v', '1') }, /* APV */
This entry already exists.
>
> { AV_CODEC_ID_VP8, MKTAG('v', 'p', '0', '8') }, /* VP8 */
> { AV_CODEC_ID_VP9, MKTAG('v', 'p', '0', '9') }, /* VP9 */
> diff --git a/libavformat/movenc.c b/libavformat/movenc.c
> index a651d6d618..1f9939fb8b 100644
> --- a/libavformat/movenc.c
> +++ b/libavformat/movenc.c
> @@ -37,6 +37,7 @@
> #include "av1.h"
> #include "avc.h"
> #include "evc.h"
> +#include "apv.h"
> #include "libavcodec/ac3_parser_internal.h"
> #include "libavcodec/dnxhddata.h"
> #include "libavcodec/flac.h"
> @@ -1643,6 +1644,24 @@ static int mov_write_vvcc_tag(AVIOContext *pb, MOVTrack *track)
> return update_size(pb, pos);
> }
>
> +static int mov_write_apvc_tag(AVIOContext *pb, MOVTrack *track)
> +{
> + int64_t pos = avio_tell(pb);
> +
> + avio_wb32(pb, 0);
> + ffio_wfourcc(pb, "apvC");
> +
> + avio_w8 (pb, 0); /* version */
> + avio_wb24(pb, 0); /* flags */
> +
> + if (track->tag == MKTAG('a','p','v','1'))
> + ff_isom_write_apvc(pb, track->vos_data, track->vos_len, 1);
> + else
> + ff_isom_write_apvc(pb, track->vos_data, track->vos_len, 0);
This else is pretty much dead code. And seeing the fact the last
parameter is called ps_array_completeness and unused, it looks like it's
a copy-paste from hevc code.
> +
> + return update_size(pb, pos);
> +}
> +
> /* also used by all avid codecs (dv, imx, meridien) and their variants */
> static int mov_write_avid_tag(AVIOContext *pb, MOVTrack *track)
> {
> @@ -1902,6 +1921,17 @@ static int mov_get_evc_codec_tag(AVFormatContext *s, MOVTrack *track)
> return tag;
> }
>
> +static int mov_get_apv_codec_tag(AVFormatContext *s, MOVTrack *track)
> +{
> + int tag = track->par->codec_tag;
> +
> + if (!tag)
> + tag = MKTAG('a', 'p', 'v', '1');
> +
> + return tag;
> +}
> +
> +
> static const struct {
> enum AVPixelFormat pix_fmt;
> uint32_t tag;
> @@ -1988,6 +2018,8 @@ static unsigned int mov_get_codec_tag(AVFormatContext *s, MOVTrack *track)
> tag = mov_get_h264_codec_tag(s, track);
> else if (track->par->codec_id == AV_CODEC_ID_EVC)
> tag = mov_get_evc_codec_tag(s, track);
> + else if (track->par->codec_id == AV_CODEC_ID_APV)
> + tag = mov_get_apv_codec_tag(s, track);
> else if (track->par->codec_id == AV_CODEC_ID_DNXHD)
> tag = mov_get_dnxhd_codec_tag(s, track);
> else if (track->par->codec_type == AVMEDIA_TYPE_VIDEO) {
> @@ -2753,6 +2785,8 @@ static int mov_write_video_tag(AVFormatContext *s, AVIOContext *pb, MOVMuxContex
> }
> else if (track->par->codec_id ==AV_CODEC_ID_EVC) {
> mov_write_evcc_tag(pb, track);
> + } else if (track->par->codec_id ==AV_CODEC_ID_APV) {
> + mov_write_apvc_tag(pb, track);
> } else if (track->par->codec_id == AV_CODEC_ID_VP9) {
> mov_write_vpcc_tag(mov->fc, pb, track);
> } else if (track->par->codec_id == AV_CODEC_ID_AV1) {
> @@ -6714,6 +6748,18 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt)
> memset(trk->vos_data + size, 0, AV_INPUT_BUFFER_PADDING_SIZE);
> }
>
> + if (par->codec_id == AV_CODEC_ID_APV && !trk->vos_len) {
> + ret = ff_isom_create_apv_dconf_record(&trk->vos_data, &trk->vos_len);
trk->vos_data should have a raw byte array, namely the input stream's
extradata, which for example the mov demuxer exports, and not some
custom structure.
> + if (!trk->vos_data) {
> + ret = AVERROR(ENOMEM);
> + goto err;
> + }
> + }
> +
> + if (par->codec_id == AV_CODEC_ID_APV && trk->vos_len) {
> + ret = ff_isom_fill_apv_dconf_record(trk->vos_data, pkt->data, size, s);
> + }
> +
> if (par->codec_id == AV_CODEC_ID_AAC && pkt->size > 2 &&
> (AV_RB16(pkt->data) & 0xfff0) == 0xfff0) {
> if (!trk->st->nb_frames) {
> @@ -6839,6 +6885,11 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt)
> if (ret) {
> goto err;
> }
> + } else if (par->codec_id == AV_CODEC_ID_APV) {
> + avio_wb32(s->pb, pkt->size);
> + size += 4;
> +
> + avio_write(s->pb, pkt->data, pkt->size);
> } else {
> avio_write(pb, pkt->data, size);
> }
> @@ -8658,6 +8709,7 @@ static const AVCodecTag codec_mp4_tags[] = {
> { AV_CODEC_ID_VVC, MKTAG('v', 'v', 'c', '1') },
> { AV_CODEC_ID_VVC, MKTAG('v', 'v', 'i', '1') },
> { AV_CODEC_ID_EVC, MKTAG('e', 'v', 'c', '1') },
> + { AV_CODEC_ID_APV, MKTAG('a', 'p', 'v', '1') },
> { AV_CODEC_ID_MPEG2VIDEO, MKTAG('m', 'p', '4', 'v') },
> { AV_CODEC_ID_MPEG1VIDEO, MKTAG('m', 'p', '4', 'v') },
> { AV_CODEC_ID_MJPEG, MKTAG('m', 'p', '4', 'v') },
> diff --git a/tests/ref/fate/source b/tests/ref/fate/source
> index d4b9bcee4c..54af72c008 100644
> --- a/tests/ref/fate/source
> +++ b/tests/ref/fate/source
> @@ -11,6 +11,7 @@ libavfilter/file_open.c
> libavfilter/log2_tab.c
> libavfilter/riscv/cpu_common.c
> libavformat/cbs.c
> +libavformat/cbs_apv.c
> libavformat/cbs_av1.c
> libavformat/file_open.c
> libavformat/golomb_tab.c
If you don't mind, i can rework this patch to address the stuff above.
[-- Attachment #1.2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 495 bytes --]
[-- Attachment #2: Type: text/plain, Size: 251 bytes --]
_______________________________________________
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] 2+ messages in thread
end of thread, other threads:[~2025-07-15 15:21 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
[not found] <CGME20250714091904eucas1p165fc624c8b63297dc6b69e60f6080aa4@eucas1p1.samsung.com>
2025-07-14 9:18 ` [FFmpeg-devel] [PATCH v4] avformat/mov_muxer: Extended MOV muxer to handle APV video content Dawid Kozinski
2025-07-15 15:21 ` James Almer
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