ffmpeg | branch: master | Dawid Kozinski <d.kozin...@samsung.com> | Mon Jul 14 11:18:57 2025 +0200| [8baa691e5fa63ff8e1ac2684e70f2f7f40f77615] | committer: James Almer
avformat/mov_muxer: Extended MOV muxer to handle APV video content - 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 Co-Authored-by: James Almer <jamr...@gmail.com> Signed-off-by: Dawid Kozinski <d.kozin...@samsung.com> Signed-off-by: James Almer <jamr...@gmail.com> > http://git.videolan.org/gitweb.cgi/ffmpeg.git/?a=commit;h=8baa691e5fa63ff8e1ac2684e70f2f7f40f77615 --- Changelog | 2 +- libavcodec/cbs_apv.c | 18 ++- libavformat/Makefile | 2 +- libavformat/apv.c | 382 ++++++++++++++++++++++++++++++++++++++++++++++++++ libavformat/apv.h | 49 +++++++ libavformat/cbs.h | 1 - libavformat/cbs_apv.c | 2 + libavformat/movenc.c | 43 ++++++ libavformat/movenc.h | 2 + tests/ref/fate/source | 1 + 10 files changed, 493 insertions(+), 9 deletions(-) diff --git a/Changelog b/Changelog index ecea094a5f..e6f5058de1 100644 --- a/Changelog +++ b/Changelog @@ -22,7 +22,7 @@ version <next>: - G.728 decoder - pad_cuda filter - Sanyo LD-ADPCM decoder -- APV in MP4/ISOBMFF demuxing +- APV in MP4/ISOBMFF muxing and demuxing - OpenHarmony hardware decoder/encoder diff --git a/libavcodec/cbs_apv.c b/libavcodec/cbs_apv.c index ebf57d3bbb..6182b62663 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,6 +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, @@ -234,7 +236,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 +261,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 +318,7 @@ static int cbs_apv_write_unit(CodedBitstreamContext *ctx, CodedBitstreamUnit *unit, PutBitContext *pbc) { +#if CBS_WRITE int err; switch (unit->type) { @@ -358,6 +361,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 +447,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..c39c015e7d 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 + apv.o dovi_isom.o evc.o cbs.o cbs_av1.o cbs_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..432d57318d --- /dev/null +++ b/libavformat/apv.c @@ -0,0 +1,382 @@ +/* + * APV helper functions for muxers + * Copyright (c) 2025 Dawid Kozinski <d.kozin...@samsung.com> + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/avassert.h" +#include "libavutil/intreadwrite.h" +#include "libavutil/mem.h" + +#include "apv.h" +#include "cbs.h" +#include "avformat.h" +#include "avio.h" +#include "avio_internal.h" +#include "libavcodec/cbs_apv.h" +#include "libavcodec/packet.h" + +typedef struct APVDecoderFrameInfo { + 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 +} 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; // table of size number_of_configuration_entry + + CodedBitstreamContext *cbc; + CodedBitstreamFragment frag; +} APVDecoderConfigurationRecord; + +void ff_isom_write_apvc(AVIOContext *pb, const APVDecoderConfigurationRecord *apvc, void *logctx) +{ + av_log(logctx, AV_LOG_TRACE, "configurationVersion: %"PRIu8"\n", + apvc->configurationVersion); + + av_log(logctx, 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++) { + const APVDecoderConfigurationEntry *configuration_entry = &apvc->configuration_entry[i]; + + av_log(logctx, AV_LOG_TRACE, "pbu_type: %"PRIu8"\n", + configuration_entry->pbu_type); + + av_log(logctx, AV_LOG_TRACE, "number_of_frame_info: %"PRIu8"\n", + configuration_entry->number_of_frame_info); + + for (int j = 0; j < configuration_entry->number_of_frame_info; j++) { + const APVDecoderFrameInfo *frame_info = &configuration_entry->frame_info[j]; + + av_log(logctx, AV_LOG_TRACE, "color_description_present_flag: %"PRIu8"\n", + frame_info->color_description_present_flag); + + av_log(logctx, AV_LOG_TRACE, "capture_time_distance_ignored: %"PRIu8"\n", + frame_info->capture_time_distance_ignored); + + av_log(logctx, AV_LOG_TRACE, "profile_idc: %"PRIu8"\n", + frame_info->profile_idc); + + av_log(logctx, AV_LOG_TRACE, "level_idc: %"PRIu8"\n", + frame_info->level_idc); + + av_log(logctx, AV_LOG_TRACE, "band_idc: %"PRIu8"\n", + frame_info->band_idc); + + av_log(logctx, AV_LOG_TRACE, "frame_width: %"PRIu32"\n", + frame_info->frame_width); + + av_log(logctx, AV_LOG_TRACE, "frame_height: %"PRIu32"\n", + frame_info->frame_height); + + av_log(logctx, AV_LOG_TRACE, "chroma_format_idc: %"PRIu8"\n", + frame_info->chroma_format_idc); + + av_log(logctx, AV_LOG_TRACE, "bit_depth_minus8: %"PRIu8"\n", + frame_info->bit_depth_minus8); + + av_log(logctx, AV_LOG_TRACE, "capture_time_distance: %"PRIu8"\n", + frame_info->capture_time_distance); + + if (frame_info->color_description_present_flag) { + av_log(logctx, AV_LOG_TRACE, "color_primaries: %"PRIu8"\n", + frame_info->color_primaries); + + av_log(logctx, AV_LOG_TRACE, "transfer_characteristics: %"PRIu8"\n", + frame_info->transfer_characteristics); + + av_log(logctx, AV_LOG_TRACE, "matrix_coefficients: %"PRIu8"\n", + frame_info->matrix_coefficients); + + av_log(logctx, AV_LOG_TRACE, "full_range_flag: %"PRIu8"\n", + frame_info->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++) { + const APVDecoderConfigurationEntry *configuration_entry = &apvc->configuration_entry[i]; + + avio_w8(pb, configuration_entry->pbu_type); + avio_w8(pb, configuration_entry->number_of_frame_info); + + for (int j = 0; j < configuration_entry->number_of_frame_info; j++) { + const APVDecoderFrameInfo *frame_info = &configuration_entry->frame_info[j]; + + /* reserved_zero_6bits + * unsigned int(1) color_description_present_flag + * unsigned int(1) capture_time_distance_ignored */ + avio_w8(pb, frame_info->color_description_present_flag << 1 | + frame_info->capture_time_distance_ignored); + + /* unsigned int(8) profile_idc */ + avio_w8(pb, frame_info->profile_idc); + + /* unsigned int(8) level_idc */ + avio_w8(pb, frame_info->level_idc); + + /* unsigned int(8) band_idc */ + avio_w8(pb, frame_info->band_idc); + + /* unsigned int(32) frame_width_minus1 */ + avio_wb32(pb, frame_info->frame_width); + + /* unsigned int(32) frame_height_minus1 */ + avio_wb32(pb, frame_info->frame_height); + + /* unsigned int(4) chroma_format_idc */ + /* unsigned int(4) bit_depth_minus8 */ + avio_w8(pb, (frame_info->chroma_format_idc << 4) | + frame_info->bit_depth_minus8); + + /* unsigned int(8) capture_time_distance */ + avio_w8(pb, frame_info->capture_time_distance); + + if (frame_info->color_description_present_flag) { + /* unsigned int(8) color_primaries */ + avio_w8(pb, frame_info->color_primaries); + + /* unsigned int(8) transfer_characteristics */ + avio_w8(pb, frame_info->transfer_characteristics); + + /* unsigned int(8) matrix_coefficients */ + avio_w8(pb, frame_info->matrix_coefficients); + + /* unsigned int(1) full_range_flag + * reserved_zero_7bits */ + avio_w8(pb, frame_info->full_range_flag << 7); + } + } + } +} + +static const CodedBitstreamUnitType decompose_unit_types[] = { + APV_PBU_PRIMARY_FRAME, APV_PBU_NON_PRIMARY_FRAME, + APV_PBU_PREVIEW_FRAME, APV_PBU_DEPTH_FRAME, APV_PBU_ALPHA_FRAME +}; + +static int apv_add_configuration_entry(APVDecoderConfigurationRecord *apvc, int pbu_type) +{ + APVDecoderConfigurationEntry *temp; + + av_assert0(apvc->number_of_configuration_entry < FF_ARRAY_ELEMS(decompose_unit_types)); + temp = av_realloc_array(apvc->configuration_entry, + apvc->number_of_configuration_entry + 1, sizeof(*apvc->configuration_entry)); + + if (!temp) + return AVERROR(ENOMEM); + + apvc->configuration_entry = temp; + memset(&apvc->configuration_entry[apvc->number_of_configuration_entry], 0, sizeof(*apvc->configuration_entry)); + apvc->configuration_entry[apvc->number_of_configuration_entry].pbu_type = pbu_type; + apvc->number_of_configuration_entry++; + + return 0; +} + +static int apv_add_frameinfo(APVDecoderConfigurationEntry *configuration_entry, + const APVDecoderFrameInfo *frame_info) +{ + APVDecoderFrameInfo *temp; + + if (configuration_entry->number_of_frame_info >= UINT8_MAX) + return AVERROR(EINVAL); + + temp = av_realloc_array(configuration_entry->frame_info, + configuration_entry->number_of_frame_info + 1, sizeof(*configuration_entry->frame_info)); + + if (!temp) + return AVERROR(ENOMEM); + + configuration_entry->frame_info = temp; + memcpy(&configuration_entry->frame_info[configuration_entry->number_of_frame_info], frame_info, sizeof(*frame_info)); + configuration_entry->number_of_frame_info++; + + return 0; +} + +int ff_isom_parse_apvc(APVDecoderConfigurationRecord *apvc, + const AVPacket *pkt, void *logctx) +{ + APVDecoderFrameInfo frame_info = { .capture_time_distance_ignored = 1 }; + int ret; + + if (pkt->size < 8 || AV_RB32(pkt->data) != APV_SIGNATURE) + /* We can't write a valid apvC from the provided data */ + return AVERROR_INVALIDDATA; + + ret = ff_lavf_cbs_read(apvc->cbc, &apvc->frag, pkt->buf, pkt->data, pkt->size); + if (ret < 0) { + av_log(logctx, AV_LOG_ERROR, "Failed to parse access unit.\n"); + return ret; + } + + for (int i = 0; i < apvc->frag.nb_units; i++) { + const CodedBitstreamUnit *pbu = &apvc->frag.units[i]; + int j; + + switch (pbu->type) { + case APV_PBU_PRIMARY_FRAME: + case APV_PBU_NON_PRIMARY_FRAME: + case APV_PBU_PREVIEW_FRAME: + case APV_PBU_DEPTH_FRAME: + case APV_PBU_ALPHA_FRAME: + break; + default: + 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; + } + + for (j = 0; j < apvc->number_of_configuration_entry; j++) { + int k; + + if (apvc->configuration_entry[j].pbu_type != pbu->type) + continue; + + for (k = 0; k < apvc->configuration_entry[j].number_of_frame_info; k++) { + if (!memcmp(&apvc->configuration_entry[j].frame_info[k], &frame_info, sizeof(frame_info))) + break; + } + if (k == apvc->configuration_entry[j].number_of_frame_info) { + ret = apv_add_frameinfo(&apvc->configuration_entry[j], &frame_info); + if (ret < 0) + goto end; + } + break; + } + + if (j == apvc->number_of_configuration_entry) { + ret = apv_add_configuration_entry(apvc, pbu->type); + if (ret < 0) + goto end; + ret = apv_add_frameinfo(&apvc->configuration_entry[j], &frame_info); + if (ret < 0) + goto end; + } + } + + ret = 0; +end: + ff_lavf_cbs_fragment_reset(&apvc->frag); + + return ret; +} + +int ff_isom_init_apvc(APVDecoderConfigurationRecord **papvc, void *logctx) +{ + APVDecoderConfigurationRecord *apvc = av_mallocz(sizeof(*apvc)); + + if (!apvc) + return AVERROR(ENOMEM); + + int ret = ff_lavf_cbs_init(&apvc->cbc, AV_CODEC_ID_APV, logctx); + if (ret < 0) { + av_freep(&apvc); + return ret; + } + + apvc->cbc->decompose_unit_types = decompose_unit_types; + apvc->cbc->nb_decompose_unit_types = FF_ARRAY_ELEMS(decompose_unit_types); + + apvc->configurationVersion = 1; + + *papvc = apvc; + + return 0; +} + +void ff_isom_close_apvc(APVDecoderConfigurationRecord **papvc) +{ + APVDecoderConfigurationRecord *apvc = *papvc; + + if (!apvc) + return; + + for (int i = 0; i < apvc->number_of_configuration_entry; i++) + av_freep(&apvc->configuration_entry[i].frame_info); + av_freep(&apvc->configuration_entry); + + ff_lavf_cbs_fragment_free(&apvc->frag); + ff_lavf_cbs_close(&apvc->cbc); + + av_freep(papvc); +} diff --git a/libavformat/apv.h b/libavformat/apv.h new file mode 100644 index 0000000000..00c9094904 --- /dev/null +++ b/libavformat/apv.h @@ -0,0 +1,49 @@ +/* + * APV helper functions for muxers + * Copyright (c) 2025 Dawid Kozinski <d.kozin...@samsung.com> + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVFORMAT_APV_H +#define AVFORMAT_APV_H + +#include <stdint.h> + +#include "avio.h" + +struct APVDecoderConfigurationRecord; +struct AVPacket; + +/** + * 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 + * + * @return 0 in case of success, a negative error code in case of failure + */ +void ff_isom_write_apvc(AVIOContext *pb, const struct APVDecoderConfigurationRecord *apvc, + void *logctx); + +int ff_isom_init_apvc(struct APVDecoderConfigurationRecord **papvc, void *logctx); +int ff_isom_parse_apvc(struct APVDecoderConfigurationRecord *apvc, + const struct AVPacket *pkt, void *logctx); +void ff_isom_close_apvc(struct APVDecoderConfigurationRecord **papvc); + +#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/movenc.c b/libavformat/movenc.c index a651d6d618..8d13ac81cd 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,21 @@ static int mov_write_vvcc_tag(AVIOContext *pb, MOVTrack *track) return update_size(pb, pos); } +static int mov_write_apvc_tag(AVFormatContext *s, 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 */ + + ff_isom_write_apvc(pb, track->apv, s); + + 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 +1918,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 +2015,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 +2782,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(mov->fc, 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) { @@ -6815,6 +6846,12 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt) avio_w8(pb, pkt->data[i + 2]); } } + } else if (par->codec_id == AV_CODEC_ID_APV) { + ff_isom_parse_apvc(trk->apv, pkt, s); + avio_wb32(s->pb, pkt->size); + size += 4; + + avio_write(s->pb, pkt->data, pkt->size); } else { if (trk->cenc.aes_ctr) { if (par->codec_id == AV_CODEC_ID_H264 && par->extradata_size > 4) { @@ -7544,6 +7581,7 @@ static void mov_free(AVFormatContext *s) ff_iamf_uninit_context(track->iamf); av_freep(&track->iamf); #endif + ff_isom_close_apvc(&track->apv); avpriv_packet_list_free(&track->squashed_packet_queue); } @@ -8049,6 +8087,10 @@ static int mov_init(AVFormatContext *s) * so just forbid muxing VP8 streams altogether until a new version does */ av_log(s, AV_LOG_ERROR, "VP8 muxing is currently not supported.\n"); return AVERROR_PATCHWELCOME; + } else if (track->par->codec_id == AV_CODEC_ID_APV) { + ret = ff_isom_init_apvc(&track->apv, s); + if (ret < 0) + return ret; } if (is_cover_image(st)) { track->cover_image = av_packet_alloc(); @@ -8658,6 +8700,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/libavformat/movenc.h b/libavformat/movenc.h index d73f7f314d..2b81d38982 100644 --- a/libavformat/movenc.h +++ b/libavformat/movenc.h @@ -176,6 +176,8 @@ typedef struct MOVTrack { int first_iamf_idx; int last_iamf_idx; AVIOContext *iamf_buf; + + struct APVDecoderConfigurationRecord *apv; } MOVTrack; typedef enum { 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 _______________________________________________ ffmpeg-cvslog mailing list ffmpeg-cvslog@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-cvslog To unsubscribe, visit link above, or email ffmpeg-cvslog-requ...@ffmpeg.org with subject "unsubscribe".