From: James Almer <jamrial@gmail.com> To: ffmpeg-devel@ffmpeg.org Subject: Re: [FFmpeg-devel] [PATCH v4] avformat/mov_muxer: Extended MOV muxer to handle APV video content Date: Tue, 15 Jul 2025 12:21:35 -0300 Message-ID: <db0907e0-035d-46ab-bd7b-2a4bb09933a1@gmail.com> (raw) In-Reply-To: <20250714091857.3338906-1-d.kozinski@samsung.com> [-- 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".
prev parent reply other threads:[~2025-07-15 15:21 UTC|newest] Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top [not found] <CGME20250714091904eucas1p165fc624c8b63297dc6b69e60f6080aa4@eucas1p1.samsung.com> 2025-07-14 9:18 ` Dawid Kozinski 2025-07-15 15:21 ` James Almer [this message]
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=db0907e0-035d-46ab-bd7b-2a4bb09933a1@gmail.com \ --to=jamrial@gmail.com \ --cc=ffmpeg-devel@ffmpeg.org \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: link
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