1
0
mirror of https://github.com/FFmpeg/FFmpeg.git synced 2025-08-04 22:03:09 +02:00

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 <jamrial@gmail.com>
Signed-off-by: Dawid Kozinski <d.kozinski@samsung.com>
Signed-off-by: James Almer <jamrial@gmail.com>
This commit is contained in:
Dawid Kozinski
2025-07-14 11:18:57 +02:00
committed by James Almer
parent 585a8d5357
commit 8baa691e5f
10 changed files with 493 additions and 9 deletions

View File

@ -22,7 +22,7 @@ version <next>:
- G.728 decoder - G.728 decoder
- pad_cuda filter - pad_cuda filter
- Sanyo LD-ADPCM decoder - Sanyo LD-ADPCM decoder
- APV in MP4/ISOBMFF demuxing - APV in MP4/ISOBMFF muxing and demuxing
- OpenHarmony hardware decoder/encoder - OpenHarmony hardware decoder/encoder

View File

@ -68,7 +68,7 @@ static void cbs_apv_derive_tile_info(APVDerivedTileInfo *ti,
#define HEADER(name) do { \ #define HEADER(name) do { \
ff_cbs_trace_header(ctx, name); \ CBS_FUNC(trace_header)(ctx, name); \
} while (0) } while (0)
#define CHECK(call) do { \ #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 { \ #define xu(width, name, var, range_min, range_max, subs, ...) do { \
uint32_t value; \ 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__), \ SUBSCRIPTS(subs, __VA_ARGS__), \
&value, range_min, range_max)); \ &value, range_min, range_max)); \
var = value; \ var = value; \
@ -124,6 +124,7 @@ static void cbs_apv_derive_tile_info(APVDerivedTileInfo *ti,
#undef infer #undef infer
#undef byte_alignment #undef byte_alignment
#if CBS_WRITE
#define WRITE #define WRITE
#define READWRITE write #define READWRITE write
#define RWContext PutBitContext #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 { \ #define xu(width, name, var, range_min, range_max, subs, ...) do { \
uint32_t value = var; \ 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__), \ SUBSCRIPTS(subs, __VA_ARGS__), \
value, range_min, range_max)); \ value, range_min, range_max)); \
} while (0) } while (0)
@ -157,6 +158,7 @@ static void cbs_apv_derive_tile_info(APVDerivedTileInfo *ti,
#undef xu #undef xu
#undef infer #undef infer
#undef byte_alignment #undef byte_alignment
#endif // CBS_WRITE
static int cbs_apv_split_fragment(CodedBitstreamContext *ctx, 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. // 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); data, pbu_size, frag->data_ref);
if (err < 0) if (err < 0)
goto fail; goto fail;
@ -259,7 +261,7 @@ static int cbs_apv_read_unit(CodedBitstreamContext *ctx,
if (err < 0) if (err < 0)
return err; return err;
err = ff_cbs_alloc_unit_content(ctx, unit); err = CBS_FUNC(alloc_unit_content)(ctx, unit);
if (err < 0) if (err < 0)
return err; return err;
@ -316,6 +318,7 @@ static int cbs_apv_write_unit(CodedBitstreamContext *ctx,
CodedBitstreamUnit *unit, CodedBitstreamUnit *unit,
PutBitContext *pbc) PutBitContext *pbc)
{ {
#if CBS_WRITE
int err; int err;
switch (unit->type) { switch (unit->type) {
@ -358,6 +361,9 @@ static int cbs_apv_write_unit(CodedBitstreamContext *ctx,
} }
return 0; return 0;
#else
return AVERROR(ENOSYS);
#endif
} }
static int cbs_apv_assemble_fragment(CodedBitstreamContext *ctx, 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 CBS_UNIT_TYPE_END_OF_LIST
}; };
const CodedBitstreamType ff_cbs_type_apv = { const CodedBitstreamType CBS_FUNC(type_apv) = {
.codec_id = AV_CODEC_ID_APV, .codec_id = AV_CODEC_ID_APV,
.priv_data_size = sizeof(CodedBitstreamAPVContext), .priv_data_size = sizeof(CodedBitstreamAPVContext),

View File

@ -381,7 +381,7 @@ OBJS-$(CONFIG_MOV_DEMUXER) += mov.o mov_chan.o mov_esds.o \
OBJS-$(CONFIG_MOV_MUXER) += movenc.o \ OBJS-$(CONFIG_MOV_MUXER) += movenc.o \
movenchint.o mov_chan.o rtp.o \ movenchint.o mov_chan.o rtp.o \
movenccenc.o movenc_ttml.o rawutils.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_MP2_MUXER) += rawenc.o
OBJS-$(CONFIG_MP3_DEMUXER) += mp3dec.o replaygain.o OBJS-$(CONFIG_MP3_DEMUXER) += mp3dec.o replaygain.o
OBJS-$(CONFIG_MP3_MUXER) += mp3enc.o rawenc.o id3v2enc.o OBJS-$(CONFIG_MP3_MUXER) += mp3enc.o rawenc.o id3v2enc.o

382
libavformat/apv.c Normal file
View File

@ -0,0 +1,382 @@
/*
* 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 "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);
}

49
libavformat/apv.h Normal file
View File

@ -0,0 +1,49 @@
/*
* 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 "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

View File

@ -22,7 +22,6 @@
#define CBS_PREFIX lavf_cbs #define CBS_PREFIX lavf_cbs
#define CBS_WRITE 0 #define CBS_WRITE 0
#define CBS_TRACE 0 #define CBS_TRACE 0
#define CBS_APV 0
#define CBS_H264 0 #define CBS_H264 0
#define CBS_H265 0 #define CBS_H265 0
#define CBS_H266 0 #define CBS_H266 0

2
libavformat/cbs_apv.c Normal file
View File

@ -0,0 +1,2 @@
#include "cbs.h"
#include "libavcodec/cbs_apv.c"

View File

@ -37,6 +37,7 @@
#include "av1.h" #include "av1.h"
#include "avc.h" #include "avc.h"
#include "evc.h" #include "evc.h"
#include "apv.h"
#include "libavcodec/ac3_parser_internal.h" #include "libavcodec/ac3_parser_internal.h"
#include "libavcodec/dnxhddata.h" #include "libavcodec/dnxhddata.h"
#include "libavcodec/flac.h" #include "libavcodec/flac.h"
@ -1643,6 +1644,21 @@ static int mov_write_vvcc_tag(AVIOContext *pb, MOVTrack *track)
return update_size(pb, pos); 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 */ /* also used by all avid codecs (dv, imx, meridien) and their variants */
static int mov_write_avid_tag(AVIOContext *pb, MOVTrack *track) 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; 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 { static const struct {
enum AVPixelFormat pix_fmt; enum AVPixelFormat pix_fmt;
uint32_t tag; 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); tag = mov_get_h264_codec_tag(s, track);
else if (track->par->codec_id == AV_CODEC_ID_EVC) else if (track->par->codec_id == AV_CODEC_ID_EVC)
tag = mov_get_evc_codec_tag(s, track); 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) else if (track->par->codec_id == AV_CODEC_ID_DNXHD)
tag = mov_get_dnxhd_codec_tag(s, track); tag = mov_get_dnxhd_codec_tag(s, track);
else if (track->par->codec_type == AVMEDIA_TYPE_VIDEO) { 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) { else if (track->par->codec_id ==AV_CODEC_ID_EVC) {
mov_write_evcc_tag(pb, track); 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) { } else if (track->par->codec_id == AV_CODEC_ID_VP9) {
mov_write_vpcc_tag(mov->fc, pb, track); mov_write_vpcc_tag(mov->fc, pb, track);
} else if (track->par->codec_id == AV_CODEC_ID_AV1) { } 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]); 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 { } else {
if (trk->cenc.aes_ctr) { if (trk->cenc.aes_ctr) {
if (par->codec_id == AV_CODEC_ID_H264 && par->extradata_size > 4) { 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); ff_iamf_uninit_context(track->iamf);
av_freep(&track->iamf); av_freep(&track->iamf);
#endif #endif
ff_isom_close_apvc(&track->apv);
avpriv_packet_list_free(&track->squashed_packet_queue); 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 */ * 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"); av_log(s, AV_LOG_ERROR, "VP8 muxing is currently not supported.\n");
return AVERROR_PATCHWELCOME; 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)) { if (is_cover_image(st)) {
track->cover_image = av_packet_alloc(); 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', 'c', '1') },
{ AV_CODEC_ID_VVC, MKTAG('v', 'v', 'i', '1') }, { AV_CODEC_ID_VVC, MKTAG('v', 'v', 'i', '1') },
{ AV_CODEC_ID_EVC, MKTAG('e', 'v', 'c', '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_MPEG2VIDEO, MKTAG('m', 'p', '4', 'v') },
{ AV_CODEC_ID_MPEG1VIDEO, MKTAG('m', 'p', '4', 'v') }, { AV_CODEC_ID_MPEG1VIDEO, MKTAG('m', 'p', '4', 'v') },
{ AV_CODEC_ID_MJPEG, MKTAG('m', 'p', '4', 'v') }, { AV_CODEC_ID_MJPEG, MKTAG('m', 'p', '4', 'v') },

View File

@ -176,6 +176,8 @@ typedef struct MOVTrack {
int first_iamf_idx; int first_iamf_idx;
int last_iamf_idx; int last_iamf_idx;
AVIOContext *iamf_buf; AVIOContext *iamf_buf;
struct APVDecoderConfigurationRecord *apv;
} MOVTrack; } MOVTrack;
typedef enum { typedef enum {

View File

@ -11,6 +11,7 @@ libavfilter/file_open.c
libavfilter/log2_tab.c libavfilter/log2_tab.c
libavfilter/riscv/cpu_common.c libavfilter/riscv/cpu_common.c
libavformat/cbs.c libavformat/cbs.c
libavformat/cbs_apv.c
libavformat/cbs_av1.c libavformat/cbs_av1.c
libavformat/file_open.c libavformat/file_open.c
libavformat/golomb_tab.c libavformat/golomb_tab.c