* [FFmpeg-devel] [PATCH 1/8] avformat/demux: allow demuxers to output more than one packet per read_packet() call
@ 2024-02-17 22:02 James Almer
2024-02-17 22:02 ` [FFmpeg-devel] [PATCH 2/8] avformat/iamfdec: further split into shareable modules James Almer
` (6 more replies)
0 siblings, 7 replies; 13+ messages in thread
From: James Almer @ 2024-02-17 22:02 UTC (permalink / raw)
To: ffmpeg-devel
Signed-off-by: James Almer <jamrial@gmail.com>
---
libavformat/demux.c | 72 +++++++++++++++++++++++++++++++++------------
libavformat/demux.h | 2 ++
2 files changed, 56 insertions(+), 18 deletions(-)
diff --git a/libavformat/demux.c b/libavformat/demux.c
index b70979c520..e0205f9dec 100644
--- a/libavformat/demux.c
+++ b/libavformat/demux.c
@@ -540,6 +540,59 @@ static int update_wrap_reference(AVFormatContext *s, AVStream *st, int stream_in
return 1;
}
+static void update_timestamps(AVFormatContext *s, AVStream *st, AVPacket *pkt)
+{
+ FFStream *const sti = ffstream(st);
+
+ if (update_wrap_reference(s, st, pkt->stream_index, pkt) && sti->pts_wrap_behavior == AV_PTS_WRAP_SUB_OFFSET) {
+ // correct first time stamps to negative values
+ if (!is_relative(sti->first_dts))
+ sti->first_dts = wrap_timestamp(st, sti->first_dts);
+ if (!is_relative(st->start_time))
+ st->start_time = wrap_timestamp(st, st->start_time);
+ if (!is_relative(sti->cur_dts))
+ sti->cur_dts = wrap_timestamp(st, sti->cur_dts);
+ }
+
+ pkt->dts = wrap_timestamp(st, pkt->dts);
+ pkt->pts = wrap_timestamp(st, pkt->pts);
+
+ force_codec_ids(s, st);
+
+ /* TODO: audio: time filter; video: frame reordering (pts != dts) */
+ if (s->use_wallclock_as_timestamps)
+ pkt->dts = pkt->pts = av_rescale_q(av_gettime(), AV_TIME_BASE_Q, st->time_base);
+}
+
+int ff_buffer_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ FFFormatContext *const si = ffformatcontext(s);
+ AVStream *st = s->streams[pkt->stream_index];
+ FFStream *sti = ffstream(st);
+ const AVPacket *pkt1;
+ int err;
+
+ update_timestamps(s, st, pkt);
+
+ err = avpriv_packet_list_put(&si->raw_packet_buffer, pkt, NULL, 0);
+ if (err < 0) {
+ av_packet_unref(pkt);
+ return err;
+ }
+
+ pkt1 = &si->raw_packet_buffer.tail->pkt;
+ si->raw_packet_buffer_size += pkt1->size;
+
+ if (sti->request_probe <= 0)
+ return 0;
+
+ err = probe_codec(s, st, pkt1);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
int ff_read_packet(AVFormatContext *s, AVPacket *pkt)
{
FFFormatContext *const si = ffformatcontext(s);
@@ -619,24 +672,7 @@ FF_ENABLE_DEPRECATION_WARNINGS
st = s->streams[pkt->stream_index];
sti = ffstream(st);
- if (update_wrap_reference(s, st, pkt->stream_index, pkt) && sti->pts_wrap_behavior == AV_PTS_WRAP_SUB_OFFSET) {
- // correct first time stamps to negative values
- if (!is_relative(sti->first_dts))
- sti->first_dts = wrap_timestamp(st, sti->first_dts);
- if (!is_relative(st->start_time))
- st->start_time = wrap_timestamp(st, st->start_time);
- if (!is_relative(sti->cur_dts))
- sti->cur_dts = wrap_timestamp(st, sti->cur_dts);
- }
-
- pkt->dts = wrap_timestamp(st, pkt->dts);
- pkt->pts = wrap_timestamp(st, pkt->pts);
-
- force_codec_ids(s, st);
-
- /* TODO: audio: time filter; video: frame reordering (pts != dts) */
- if (s->use_wallclock_as_timestamps)
- pkt->dts = pkt->pts = av_rescale_q(av_gettime(), AV_TIME_BASE_Q, st->time_base);
+ update_timestamps(s, st, pkt);
if (!pktl && sti->request_probe <= 0)
return 0;
diff --git a/libavformat/demux.h b/libavformat/demux.h
index d65eb16ff8..04a426c420 100644
--- a/libavformat/demux.h
+++ b/libavformat/demux.h
@@ -238,4 +238,6 @@ int ff_get_extradata(void *logctx, AVCodecParameters *par, AVIOContext *pb, int
*/
int ff_find_stream_index(const AVFormatContext *s, int id);
+int ff_buffer_packet(AVFormatContext *s, AVPacket *pkt);
+
#endif /* AVFORMAT_DEMUX_H */
--
2.43.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] 13+ messages in thread
* [FFmpeg-devel] [PATCH 2/8] avformat/iamfdec: further split into shareable modules
2024-02-17 22:02 [FFmpeg-devel] [PATCH 1/8] avformat/demux: allow demuxers to output more than one packet per read_packet() call James Almer
@ 2024-02-17 22:02 ` James Almer
2024-02-17 22:02 ` [FFmpeg-devel] [PATCH 3/8] avformat/mov: factorize out setting the output packet properties James Almer
` (5 subsequent siblings)
6 siblings, 0 replies; 13+ messages in thread
From: James Almer @ 2024-02-17 22:02 UTC (permalink / raw)
To: ffmpeg-devel
Signed-off-by: James Almer <jamrial@gmail.com>
---
configure | 2 +
libavformat/Makefile | 3 +-
libavformat/iamf_reader.c | 366 ++++++++++++++++++++++++++++++++++++++
libavformat/iamf_reader.h | 49 +++++
libavformat/iamfdec.c | 347 ++----------------------------------
5 files changed, 432 insertions(+), 335 deletions(-)
create mode 100644 libavformat/iamf_reader.c
create mode 100644 libavformat/iamf_reader.h
diff --git a/configure b/configure
index f72533b7d2..472de63276 100755
--- a/configure
+++ b/configure
@@ -2516,6 +2516,7 @@ CONFIG_EXTRA="
huffman
huffyuvdsp
huffyuvencdsp
+ iamfdec
idctdsp
iirfilter
inflate_wrapper
@@ -3534,6 +3535,7 @@ gxf_muxer_select="pcm_rechunk_bsf"
hds_muxer_select="flv_muxer"
hls_demuxer_select="adts_header ac3_parser mov_demuxer mpegts_demuxer"
hls_muxer_select="mov_muxer mpegts_muxer"
+iamf_demuxer_select="iamfdec"
image2_alias_pix_demuxer_select="image2_demuxer"
image2_brender_pix_demuxer_select="image2_demuxer"
imf_demuxer_deps="libxml2"
diff --git a/libavformat/Makefile b/libavformat/Makefile
index 9e8e7c9cb8..3b83f5a685 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -35,6 +35,7 @@ OBJS-$(HAVE_LIBC_MSVCRT) += file_open.o
# subsystems
OBJS-$(CONFIG_ISO_MEDIA) += isom.o
+OBJS-$(CONFIG_IAMFDEC) += iamf_reader.o iamf_parse.o iamf.o
OBJS-$(CONFIG_NETWORK) += network.o
OBJS-$(CONFIG_RIFFDEC) += riffdec.o
OBJS-$(CONFIG_RIFFENC) += riffenc.o
@@ -258,7 +259,7 @@ OBJS-$(CONFIG_EVC_MUXER) += rawenc.o
OBJS-$(CONFIG_HLS_DEMUXER) += hls.o hls_sample_encryption.o
OBJS-$(CONFIG_HLS_MUXER) += hlsenc.o hlsplaylist.o avc.o
OBJS-$(CONFIG_HNM_DEMUXER) += hnm.o
-OBJS-$(CONFIG_IAMF_DEMUXER) += iamfdec.o iamf_parse.o iamf.o
+OBJS-$(CONFIG_IAMF_DEMUXER) += iamfdec.o
OBJS-$(CONFIG_IAMF_MUXER) += iamfenc.o iamf_writer.o iamf.o
OBJS-$(CONFIG_ICO_DEMUXER) += icodec.o
OBJS-$(CONFIG_ICO_MUXER) += icoenc.o
diff --git a/libavformat/iamf_reader.c b/libavformat/iamf_reader.c
new file mode 100644
index 0000000000..12affe2497
--- /dev/null
+++ b/libavformat/iamf_reader.c
@@ -0,0 +1,366 @@
+/*
+ * Immersive Audio Model and Formats demuxing utils
+ * Copyright (c) 2024 James Almer <jamrial@gmail.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/log.h"
+#include "libavcodec/mathops.h"
+#include "libavcodec/packet.h"
+#include "avformat.h"
+#include "avio_internal.h"
+#include "iamf.h"
+#include "iamf_parse.h"
+#include "iamf_reader.h"
+
+static AVStream *find_stream_by_id(AVFormatContext *s, int id)
+{
+ for (int i = 0; i < s->nb_streams; i++)
+ if (s->streams[i]->id == id)
+ return s->streams[i];
+
+ av_log(s, AV_LOG_ERROR, "Invalid stream id %d\n", id);
+ return NULL;
+}
+
+static int audio_frame_obu(AVFormatContext *s, const IAMFDemuxContext *c,
+ AVIOContext *pb, AVPacket *pkt,
+ int len, enum IAMF_OBU_Type type,
+ unsigned skip_samples, unsigned discard_padding,
+ int id_in_bitstream)
+{
+ AVStream *st;
+ int ret, audio_substream_id;
+
+ if (id_in_bitstream) {
+ unsigned explicit_audio_substream_id;
+ int64_t pos = avio_tell(pb);
+ explicit_audio_substream_id = ffio_read_leb(pb);
+ len -= avio_tell(pb) - pos;
+ audio_substream_id = explicit_audio_substream_id;
+ } else
+ audio_substream_id = type - IAMF_OBU_IA_AUDIO_FRAME_ID0;
+
+ st = find_stream_by_id(s, audio_substream_id);
+ if (!st)
+ return AVERROR_INVALIDDATA;
+
+ ret = av_get_packet(pb, pkt, len);
+ if (ret < 0)
+ return ret;
+ if (ret != len)
+ return AVERROR_INVALIDDATA;
+
+ if (skip_samples || discard_padding) {
+ uint8_t *side_data = av_packet_new_side_data(pkt, AV_PKT_DATA_SKIP_SAMPLES, 10);
+ if (!side_data)
+ return AVERROR(ENOMEM);
+ AV_WL32(side_data, skip_samples);
+ AV_WL32(side_data + 4, discard_padding);
+ }
+ if (c->mix) {
+ uint8_t *side_data = av_packet_new_side_data(pkt, AV_PKT_DATA_IAMF_MIX_GAIN_PARAM, c->mix_size);
+ if (!side_data)
+ return AVERROR(ENOMEM);
+ memcpy(side_data, c->mix, c->mix_size);
+ }
+ if (c->demix) {
+ uint8_t *side_data = av_packet_new_side_data(pkt, AV_PKT_DATA_IAMF_DEMIXING_INFO_PARAM, c->demix_size);
+ if (!side_data)
+ return AVERROR(ENOMEM);
+ memcpy(side_data, c->demix, c->demix_size);
+ }
+ if (c->recon) {
+ uint8_t *side_data = av_packet_new_side_data(pkt, AV_PKT_DATA_IAMF_RECON_GAIN_INFO_PARAM, c->recon_size);
+ if (!side_data)
+ return AVERROR(ENOMEM);
+ memcpy(side_data, c->recon, c->recon_size);
+ }
+
+ pkt->stream_index = st->index;
+ return 0;
+}
+
+static const IAMFParamDefinition *get_param_definition(AVFormatContext *s,
+ const IAMFDemuxContext *c,
+ unsigned int parameter_id)
+{
+ const IAMFContext *const iamf = &c->iamf;
+ const IAMFParamDefinition *param_definition = NULL;
+
+ for (int i = 0; i < iamf->nb_param_definitions; i++)
+ if (iamf->param_definitions[i]->param->parameter_id == parameter_id) {
+ param_definition = iamf->param_definitions[i];
+ break;
+ }
+
+ return param_definition;
+}
+
+static int parameter_block_obu(AVFormatContext *s, IAMFDemuxContext *c,
+ AVIOContext *pbc, int len)
+{
+ const IAMFParamDefinition *param_definition;
+ const AVIAMFParamDefinition *param;
+ AVIAMFParamDefinition *out_param = NULL;
+ FFIOContext b;
+ AVIOContext *pb;
+ uint8_t *buf;
+ unsigned int duration, constant_subblock_duration;
+ unsigned int nb_subblocks;
+ unsigned int parameter_id;
+ size_t out_param_size;
+ int ret;
+
+ buf = av_malloc(len);
+ if (!buf)
+ return AVERROR(ENOMEM);
+
+ ret = avio_read(pbc, buf, len);
+ if (ret != len) {
+ if (ret >= 0)
+ ret = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+
+ ffio_init_context(&b, buf, len, 0, NULL, NULL, NULL, NULL);
+ pb = &b.pub;
+
+ parameter_id = ffio_read_leb(pb);
+ param_definition = get_param_definition(s, c, parameter_id);
+ if (!param_definition) {
+ av_log(s, AV_LOG_VERBOSE, "Non existant parameter_id %d referenced in a parameter block. Ignoring\n",
+ parameter_id);
+ ret = 0;
+ goto fail;
+ }
+
+ param = param_definition->param;
+ if (!param_definition->mode) {
+ duration = ffio_read_leb(pb);
+ if (!duration) {
+ ret = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+ constant_subblock_duration = ffio_read_leb(pb);
+ if (constant_subblock_duration == 0)
+ nb_subblocks = ffio_read_leb(pb);
+ else
+ nb_subblocks = duration / constant_subblock_duration;
+ } else {
+ duration = param->duration;
+ constant_subblock_duration = param->constant_subblock_duration;
+ nb_subblocks = param->nb_subblocks;
+ }
+
+ out_param = av_iamf_param_definition_alloc(param->type, nb_subblocks, &out_param_size);
+ if (!out_param) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+
+ out_param->parameter_id = param->parameter_id;
+ out_param->type = param->type;
+ out_param->parameter_rate = param->parameter_rate;
+ out_param->duration = duration;
+ out_param->constant_subblock_duration = constant_subblock_duration;
+ out_param->nb_subblocks = nb_subblocks;
+
+ for (int i = 0; i < nb_subblocks; i++) {
+ void *subblock = av_iamf_param_definition_get_subblock(out_param, i);
+ unsigned int subblock_duration = constant_subblock_duration;
+
+ if (!param_definition->mode && !constant_subblock_duration)
+ subblock_duration = ffio_read_leb(pb);
+
+ switch (param->type) {
+ case AV_IAMF_PARAMETER_DEFINITION_MIX_GAIN: {
+ AVIAMFMixGain *mix = subblock;
+
+ mix->animation_type = ffio_read_leb(pb);
+ if (mix->animation_type > AV_IAMF_ANIMATION_TYPE_BEZIER) {
+ ret = 0;
+ av_free(out_param);
+ goto fail;
+ }
+
+ mix->start_point_value = av_make_q(sign_extend(avio_rb16(pb), 16), 1 << 8);
+ if (mix->animation_type >= AV_IAMF_ANIMATION_TYPE_LINEAR)
+ mix->end_point_value = av_make_q(sign_extend(avio_rb16(pb), 16), 1 << 8);
+ if (mix->animation_type == AV_IAMF_ANIMATION_TYPE_BEZIER) {
+ mix->control_point_value = av_make_q(sign_extend(avio_rb16(pb), 16), 1 << 8);
+ mix->control_point_relative_time = av_make_q(avio_r8(pb), 1 << 8);
+ }
+ mix->subblock_duration = subblock_duration;
+ break;
+ }
+ case AV_IAMF_PARAMETER_DEFINITION_DEMIXING: {
+ AVIAMFDemixingInfo *demix = subblock;
+
+ demix->dmixp_mode = avio_r8(pb) >> 5;
+ demix->subblock_duration = subblock_duration;
+ break;
+ }
+ case AV_IAMF_PARAMETER_DEFINITION_RECON_GAIN: {
+ AVIAMFReconGain *recon = subblock;
+ const IAMFAudioElement *audio_element = param_definition->audio_element;
+ const AVIAMFAudioElement *element = audio_element->element;
+
+ av_assert0(audio_element && element);
+ for (int i = 0; i < element->nb_layers; i++) {
+ const AVIAMFLayer *layer = element->layers[i];
+ if (layer->flags & AV_IAMF_LAYER_FLAG_RECON_GAIN) {
+ unsigned int recon_gain_flags = ffio_read_leb(pb);
+ unsigned int bitcount = 7 + 5 * !!(recon_gain_flags & 0x80);
+ recon_gain_flags = (recon_gain_flags & 0x7F) | ((recon_gain_flags & 0xFF00) >> 1);
+ for (int j = 0; j < bitcount; j++) {
+ if (recon_gain_flags & (1 << j))
+ recon->recon_gain[i][j] = avio_r8(pb);
+ }
+ }
+ }
+ recon->subblock_duration = subblock_duration;
+ break;
+ }
+ default:
+ av_assert0(0);
+ }
+ }
+
+ len -= avio_tell(pb);
+ if (len) {
+ int level = (s->error_recognition & AV_EF_EXPLODE) ? AV_LOG_ERROR : AV_LOG_WARNING;
+ av_log(s, level, "Underread in parameter_block_obu. %d bytes left at the end\n", len);
+ }
+
+ switch (param->type) {
+ case AV_IAMF_PARAMETER_DEFINITION_MIX_GAIN:
+ av_free(c->mix);
+ c->mix = out_param;
+ c->mix_size = out_param_size;
+ break;
+ case AV_IAMF_PARAMETER_DEFINITION_DEMIXING:
+ av_free(c->demix);
+ c->demix = out_param;
+ c->demix_size = out_param_size;
+ break;
+ case AV_IAMF_PARAMETER_DEFINITION_RECON_GAIN:
+ av_free(c->recon);
+ c->recon = out_param;
+ c->recon_size = out_param_size;
+ break;
+ default:
+ av_assert0(0);
+ }
+
+ ret = 0;
+fail:
+ if (ret < 0)
+ av_free(out_param);
+ av_free(buf);
+
+ return ret;
+}
+
+int ff_iamf_read_packet(AVFormatContext *s, IAMFDemuxContext *c,
+ AVIOContext *pb, int max_size, AVPacket *pkt)
+{
+ int read = 0;
+
+ while (1) {
+ uint8_t header[MAX_IAMF_OBU_HEADER_SIZE + AV_INPUT_BUFFER_PADDING_SIZE];
+ enum IAMF_OBU_Type type;
+ unsigned obu_size;
+ unsigned skip_samples, discard_padding;
+ int ret, len, size, start_pos;
+
+ if ((ret = ffio_ensure_seekback(pb, FFMIN(MAX_IAMF_OBU_HEADER_SIZE, max_size))) < 0)
+ return ret;
+ size = avio_read(pb, header, FFMIN(MAX_IAMF_OBU_HEADER_SIZE, max_size));
+ if (size < 0)
+ return size;
+
+ len = ff_iamf_parse_obu_header(header, size, &obu_size, &start_pos, &type,
+ &skip_samples, &discard_padding);
+ if (len < 0 || obu_size > max_size) {
+ av_log(s, AV_LOG_ERROR, "Failed to read obu\n");
+ return len;
+ }
+ avio_seek(pb, -(size - start_pos), SEEK_CUR);
+
+ read += len;
+ if (type >= IAMF_OBU_IA_AUDIO_FRAME && type <= IAMF_OBU_IA_AUDIO_FRAME_ID17) {
+ ret = audio_frame_obu(s, c, pb, pkt, obu_size, type,
+ skip_samples, discard_padding,
+ type == IAMF_OBU_IA_AUDIO_FRAME);
+ if (ret < 0)
+ return ret;
+ return read;
+ } else if (type == IAMF_OBU_IA_PARAMETER_BLOCK) {
+ ret = parameter_block_obu(s, c, pb, obu_size);
+ if (ret < 0)
+ return ret;
+ } else if (type == IAMF_OBU_IA_TEMPORAL_DELIMITER) {
+ av_freep(&c->mix);
+ c->mix_size = 0;
+ av_freep(&c->demix);
+ c->demix_size = 0;
+ av_freep(&c->recon);
+ c->recon_size = 0;
+ } else {
+ int64_t offset = avio_skip(pb, obu_size);
+ if (offset < 0) {
+ ret = offset;
+ break;
+ }
+ }
+ max_size -= len;
+ if (max_size < 0)
+ return AVERROR_INVALIDDATA;
+ if (!max_size)
+ break;
+ }
+
+ return read;
+}
+
+void ff_iamf_read_deinit(IAMFDemuxContext *c)
+{
+ IAMFContext *const iamf = &c->iamf;
+
+ for (int i = 0; i < iamf->nb_audio_elements; i++) {
+ IAMFAudioElement *audio_element = iamf->audio_elements[i];
+ audio_element->element = NULL;
+ }
+
+ for (int i = 0; i < iamf->nb_mix_presentations; i++) {
+ IAMFMixPresentation *mix_presentation = iamf->mix_presentations[i];
+ mix_presentation->mix = NULL;
+ }
+
+ ff_iamf_uninit_context(iamf);
+
+ av_freep(&c->mix);
+ c->mix_size = 0;
+ av_freep(&c->demix);
+ c->demix_size = 0;
+ av_freep(&c->recon);
+ c->recon_size = 0;
+}
diff --git a/libavformat/iamf_reader.h b/libavformat/iamf_reader.h
new file mode 100644
index 0000000000..ecb92d485a
--- /dev/null
+++ b/libavformat/iamf_reader.h
@@ -0,0 +1,49 @@
+/*
+ * Immersive Audio Model and Formats demuxing utils
+ * Copyright (c) 2024 James Almer <jamrial@gmail.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_IAMF_READER_H
+#define AVFORMAT_IAMF_READER_H
+
+#include <stdint.h>
+
+#include "libavcodec/packet.h"
+#include "avformat.h"
+#include "avio.h"
+#include "iamf.h"
+
+typedef struct IAMFDemuxContext {
+ IAMFContext iamf;
+
+ // Packet side data
+ AVIAMFParamDefinition *mix;
+ size_t mix_size;
+ AVIAMFParamDefinition *demix;
+ size_t demix_size;
+ AVIAMFParamDefinition *recon;
+ size_t recon_size;
+} IAMFDemuxContext;
+
+int ff_iamf_read_packet(AVFormatContext *s, IAMFDemuxContext *c,
+ AVIOContext *pb, int max_size, AVPacket *pkt);
+
+void ff_iamf_read_deinit(IAMFDemuxContext *c);
+
+#endif /* AVFORMAT_IAMF_READER_H */
diff --git a/libavformat/iamfdec.c b/libavformat/iamfdec.c
index 99622f697b..2c9dec37e8 100644
--- a/libavformat/iamfdec.c
+++ b/libavformat/iamfdec.c
@@ -20,329 +20,13 @@
*/
#include "libavutil/avassert.h"
-#include "libavutil/iamf.h"
#include "libavutil/intreadwrite.h"
-#include "libavutil/log.h"
-#include "libavcodec/mathops.h"
#include "avformat.h"
-#include "avio_internal.h"
#include "iamf.h"
+#include "iamf_reader.h"
#include "iamf_parse.h"
#include "internal.h"
-typedef struct IAMFDemuxContext {
- IAMFContext iamf;
-
- // Packet side data
- AVIAMFParamDefinition *mix;
- size_t mix_size;
- AVIAMFParamDefinition *demix;
- size_t demix_size;
- AVIAMFParamDefinition *recon;
- size_t recon_size;
-} IAMFDemuxContext;
-
-static AVStream *find_stream_by_id(AVFormatContext *s, int id)
-{
- for (int i = 0; i < s->nb_streams; i++)
- if (s->streams[i]->id == id)
- return s->streams[i];
-
- av_log(s, AV_LOG_ERROR, "Invalid stream id %d\n", id);
- return NULL;
-}
-
-static int audio_frame_obu(AVFormatContext *s, AVPacket *pkt, int len,
- enum IAMF_OBU_Type type,
- unsigned skip_samples, unsigned discard_padding,
- int id_in_bitstream)
-{
- const IAMFDemuxContext *const c = s->priv_data;
- AVStream *st;
- int ret, audio_substream_id;
-
- if (id_in_bitstream) {
- unsigned explicit_audio_substream_id;
- int64_t pos = avio_tell(s->pb);
- explicit_audio_substream_id = ffio_read_leb(s->pb);
- len -= avio_tell(s->pb) - pos;
- audio_substream_id = explicit_audio_substream_id;
- } else
- audio_substream_id = type - IAMF_OBU_IA_AUDIO_FRAME_ID0;
-
- st = find_stream_by_id(s, audio_substream_id);
- if (!st)
- return AVERROR_INVALIDDATA;
-
- ret = av_get_packet(s->pb, pkt, len);
- if (ret < 0)
- return ret;
- if (ret != len)
- return AVERROR_INVALIDDATA;
-
- if (skip_samples || discard_padding) {
- uint8_t *side_data = av_packet_new_side_data(pkt, AV_PKT_DATA_SKIP_SAMPLES, 10);
- if (!side_data)
- return AVERROR(ENOMEM);
- AV_WL32(side_data, skip_samples);
- AV_WL32(side_data + 4, discard_padding);
- }
- if (c->mix) {
- uint8_t *side_data = av_packet_new_side_data(pkt, AV_PKT_DATA_IAMF_MIX_GAIN_PARAM, c->mix_size);
- if (!side_data)
- return AVERROR(ENOMEM);
- memcpy(side_data, c->mix, c->mix_size);
- }
- if (c->demix) {
- uint8_t *side_data = av_packet_new_side_data(pkt, AV_PKT_DATA_IAMF_DEMIXING_INFO_PARAM, c->demix_size);
- if (!side_data)
- return AVERROR(ENOMEM);
- memcpy(side_data, c->demix, c->demix_size);
- }
- if (c->recon) {
- uint8_t *side_data = av_packet_new_side_data(pkt, AV_PKT_DATA_IAMF_RECON_GAIN_INFO_PARAM, c->recon_size);
- if (!side_data)
- return AVERROR(ENOMEM);
- memcpy(side_data, c->recon, c->recon_size);
- }
-
- pkt->stream_index = st->index;
- return 0;
-}
-
-static const IAMFParamDefinition *get_param_definition(AVFormatContext *s, unsigned int parameter_id)
-{
- const IAMFDemuxContext *const c = s->priv_data;
- const IAMFContext *const iamf = &c->iamf;
- const IAMFParamDefinition *param_definition = NULL;
-
- for (int i = 0; i < iamf->nb_param_definitions; i++)
- if (iamf->param_definitions[i]->param->parameter_id == parameter_id) {
- param_definition = iamf->param_definitions[i];
- break;
- }
-
- return param_definition;
-}
-
-static int parameter_block_obu(AVFormatContext *s, int len)
-{
- IAMFDemuxContext *const c = s->priv_data;
- const IAMFParamDefinition *param_definition;
- const AVIAMFParamDefinition *param;
- AVIAMFParamDefinition *out_param = NULL;
- FFIOContext b;
- AVIOContext *pb;
- uint8_t *buf;
- unsigned int duration, constant_subblock_duration;
- unsigned int nb_subblocks;
- unsigned int parameter_id;
- size_t out_param_size;
- int ret;
-
- buf = av_malloc(len);
- if (!buf)
- return AVERROR(ENOMEM);
-
- ret = avio_read(s->pb, buf, len);
- if (ret != len) {
- if (ret >= 0)
- ret = AVERROR_INVALIDDATA;
- goto fail;
- }
-
- ffio_init_context(&b, buf, len, 0, NULL, NULL, NULL, NULL);
- pb = &b.pub;
-
- parameter_id = ffio_read_leb(pb);
- param_definition = get_param_definition(s, parameter_id);
- if (!param_definition) {
- av_log(s, AV_LOG_VERBOSE, "Non existant parameter_id %d referenced in a parameter block. Ignoring\n",
- parameter_id);
- ret = 0;
- goto fail;
- }
-
- param = param_definition->param;
- if (!param_definition->mode) {
- duration = ffio_read_leb(pb);
- if (!duration) {
- ret = AVERROR_INVALIDDATA;
- goto fail;
- }
- constant_subblock_duration = ffio_read_leb(pb);
- if (constant_subblock_duration == 0)
- nb_subblocks = ffio_read_leb(pb);
- else
- nb_subblocks = duration / constant_subblock_duration;
- } else {
- duration = param->duration;
- constant_subblock_duration = param->constant_subblock_duration;
- nb_subblocks = param->nb_subblocks;
- }
-
- out_param = av_iamf_param_definition_alloc(param->type, nb_subblocks, &out_param_size);
- if (!out_param) {
- ret = AVERROR(ENOMEM);
- goto fail;
- }
-
- out_param->parameter_id = param->parameter_id;
- out_param->type = param->type;
- out_param->parameter_rate = param->parameter_rate;
- out_param->duration = duration;
- out_param->constant_subblock_duration = constant_subblock_duration;
- out_param->nb_subblocks = nb_subblocks;
-
- for (int i = 0; i < nb_subblocks; i++) {
- void *subblock = av_iamf_param_definition_get_subblock(out_param, i);
- unsigned int subblock_duration = constant_subblock_duration;
-
- if (!param_definition->mode && !constant_subblock_duration)
- subblock_duration = ffio_read_leb(pb);
-
- switch (param->type) {
- case AV_IAMF_PARAMETER_DEFINITION_MIX_GAIN: {
- AVIAMFMixGain *mix = subblock;
-
- mix->animation_type = ffio_read_leb(pb);
- if (mix->animation_type > AV_IAMF_ANIMATION_TYPE_BEZIER) {
- ret = 0;
- av_free(out_param);
- goto fail;
- }
-
- mix->start_point_value = av_make_q(sign_extend(avio_rb16(pb), 16), 1 << 8);
- if (mix->animation_type >= AV_IAMF_ANIMATION_TYPE_LINEAR)
- mix->end_point_value = av_make_q(sign_extend(avio_rb16(pb), 16), 1 << 8);
- if (mix->animation_type == AV_IAMF_ANIMATION_TYPE_BEZIER) {
- mix->control_point_value = av_make_q(sign_extend(avio_rb16(pb), 16), 1 << 8);
- mix->control_point_relative_time = av_make_q(avio_r8(pb), 1 << 8);
- }
- mix->subblock_duration = subblock_duration;
- break;
- }
- case AV_IAMF_PARAMETER_DEFINITION_DEMIXING: {
- AVIAMFDemixingInfo *demix = subblock;
-
- demix->dmixp_mode = avio_r8(pb) >> 5;
- demix->subblock_duration = subblock_duration;
- break;
- }
- case AV_IAMF_PARAMETER_DEFINITION_RECON_GAIN: {
- AVIAMFReconGain *recon = subblock;
- const IAMFAudioElement *audio_element = param_definition->audio_element;
- const AVIAMFAudioElement *element = audio_element->element;
-
- av_assert0(audio_element && element);
- for (int i = 0; i < element->nb_layers; i++) {
- const AVIAMFLayer *layer = element->layers[i];
- if (layer->flags & AV_IAMF_LAYER_FLAG_RECON_GAIN) {
- unsigned int recon_gain_flags = ffio_read_leb(pb);
- unsigned int bitcount = 7 + 5 * !!(recon_gain_flags & 0x80);
- recon_gain_flags = (recon_gain_flags & 0x7F) | ((recon_gain_flags & 0xFF00) >> 1);
- for (int j = 0; j < bitcount; j++) {
- if (recon_gain_flags & (1 << j))
- recon->recon_gain[i][j] = avio_r8(pb);
- }
- }
- }
- recon->subblock_duration = subblock_duration;
- break;
- }
- default:
- av_assert0(0);
- }
- }
-
- len -= avio_tell(pb);
- if (len) {
- int level = (s->error_recognition & AV_EF_EXPLODE) ? AV_LOG_ERROR : AV_LOG_WARNING;
- av_log(s, level, "Underread in parameter_block_obu. %d bytes left at the end\n", len);
- }
-
- switch (param->type) {
- case AV_IAMF_PARAMETER_DEFINITION_MIX_GAIN:
- av_free(c->mix);
- c->mix = out_param;
- c->mix_size = out_param_size;
- break;
- case AV_IAMF_PARAMETER_DEFINITION_DEMIXING:
- av_free(c->demix);
- c->demix = out_param;
- c->demix_size = out_param_size;
- break;
- case AV_IAMF_PARAMETER_DEFINITION_RECON_GAIN:
- av_free(c->recon);
- c->recon = out_param;
- c->recon_size = out_param_size;
- break;
- default:
- av_assert0(0);
- }
-
- ret = 0;
-fail:
- if (ret < 0)
- av_free(out_param);
- av_free(buf);
-
- return ret;
-}
-
-static int iamf_read_packet(AVFormatContext *s, AVPacket *pkt)
-{
- IAMFDemuxContext *const c = s->priv_data;
- uint8_t header[MAX_IAMF_OBU_HEADER_SIZE + AV_INPUT_BUFFER_PADDING_SIZE];
- unsigned obu_size;
- int ret;
-
- while (1) {
- enum IAMF_OBU_Type type;
- unsigned skip_samples, discard_padding;
- int len, size, start_pos;
-
- if ((ret = ffio_ensure_seekback(s->pb, MAX_IAMF_OBU_HEADER_SIZE)) < 0)
- return ret;
- size = avio_read(s->pb, header, MAX_IAMF_OBU_HEADER_SIZE);
- if (size < 0)
- return size;
-
- len = ff_iamf_parse_obu_header(header, size, &obu_size, &start_pos, &type,
- &skip_samples, &discard_padding);
- if (len < 0) {
- av_log(s, AV_LOG_ERROR, "Failed to read obu\n");
- return len;
- }
- avio_seek(s->pb, -(size - start_pos), SEEK_CUR);
-
- if (type >= IAMF_OBU_IA_AUDIO_FRAME && type <= IAMF_OBU_IA_AUDIO_FRAME_ID17)
- return audio_frame_obu(s, pkt, obu_size, type,
- skip_samples, discard_padding,
- type == IAMF_OBU_IA_AUDIO_FRAME);
- else if (type == IAMF_OBU_IA_PARAMETER_BLOCK) {
- ret = parameter_block_obu(s, obu_size);
- if (ret < 0)
- return ret;
- } else if (type == IAMF_OBU_IA_TEMPORAL_DELIMITER) {
- av_freep(&c->mix);
- c->mix_size = 0;
- av_freep(&c->demix);
- c->demix_size = 0;
- av_freep(&c->recon);
- c->recon_size = 0;
- } else {
- int64_t offset = avio_skip(s->pb, obu_size);
- if (offset < 0) {
- ret = offset;
- break;
- }
- }
- }
-
- return ret;
-}
-
//return < 0 if we need more data
static int get_score(const uint8_t *buf, int buf_size, enum IAMF_OBU_Type type, int *seq)
{
@@ -464,28 +148,23 @@ static int iamf_read_header(AVFormatContext *s)
return 0;
}
-static int iamf_read_close(AVFormatContext *s)
+static int iamf_read_packet(AVFormatContext *s, AVPacket *pkt)
{
IAMFDemuxContext *const c = s->priv_data;
- IAMFContext *const iamf = &c->iamf;
+ int ret;
- for (int i = 0; i < iamf->nb_audio_elements; i++) {
- IAMFAudioElement *audio_element = iamf->audio_elements[i];
- audio_element->element = NULL;
- }
- for (int i = 0; i < iamf->nb_mix_presentations; i++) {
- IAMFMixPresentation *mix_presentation = iamf->mix_presentations[i];
- mix_presentation->mix = NULL;
- }
+ ret = ff_iamf_read_packet(s, c, s->pb, INT_MAX, pkt);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
- ff_iamf_uninit_context(&c->iamf);
+static int iamf_read_close(AVFormatContext *s)
+{
+ IAMFDemuxContext *const c = s->priv_data;
- av_freep(&c->mix);
- c->mix_size = 0;
- av_freep(&c->demix);
- c->demix_size = 0;
- av_freep(&c->recon);
- c->recon_size = 0;
+ ff_iamf_read_deinit(c);
return 0;
}
--
2.43.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] 13+ messages in thread
* [FFmpeg-devel] [PATCH 3/8] avformat/mov: factorize out setting the output packet properties
2024-02-17 22:02 [FFmpeg-devel] [PATCH 1/8] avformat/demux: allow demuxers to output more than one packet per read_packet() call James Almer
2024-02-17 22:02 ` [FFmpeg-devel] [PATCH 2/8] avformat/iamfdec: further split into shareable modules James Almer
@ 2024-02-17 22:02 ` James Almer
2024-02-17 22:02 ` [FFmpeg-devel] [PATCH 4/8] avformat/mov: make MOVStreamContext refcounted James Almer
` (4 subsequent siblings)
6 siblings, 0 replies; 13+ messages in thread
From: James Almer @ 2024-02-17 22:02 UTC (permalink / raw)
To: ffmpeg-devel
Signed-off-by: James Almer <jamrial@gmail.com>
---
libavformat/mov.c | 113 ++++++++++++++++++++++++++--------------------
1 file changed, 64 insertions(+), 49 deletions(-)
diff --git a/libavformat/mov.c b/libavformat/mov.c
index 92304565df..e548f93f17 100644
--- a/libavformat/mov.c
+++ b/libavformat/mov.c
@@ -9192,8 +9192,9 @@ static int mov_switch_root(AVFormatContext *s, int64_t target, int index)
return 1;
}
-static int mov_change_extradata(MOVStreamContext *sc, AVPacket *pkt)
+static int mov_change_extradata(AVStream *st, AVPacket *pkt)
{
+ MOVStreamContext *sc = st->priv_data;
uint8_t *side, *extradata;
int extradata_size;
@@ -9203,7 +9204,7 @@ static int mov_change_extradata(MOVStreamContext *sc, AVPacket *pkt)
/* Notify the decoder that extradata changed. */
extradata_size = sc->extradata_size[sc->last_stsd_index];
extradata = sc->extradata[sc->last_stsd_index];
- if (extradata_size > 0 && extradata) {
+ if (st->discard != AVDISCARD_ALL && extradata_size > 0 && extradata) {
side = av_packet_new_side_data(pkt,
AV_PKT_DATA_NEW_EXTRADATA,
extradata_size);
@@ -9236,6 +9237,64 @@ static int get_eia608_packet(AVIOContext *pb, AVPacket *pkt, int size)
return 0;
}
+static int mov_finalize_packet(AVFormatContext *s, AVStream *st, AVIndexEntry *sample,
+ int64_t current_index, AVPacket *pkt)
+{
+ MOVStreamContext *sc = st->priv_data;
+
+ pkt->stream_index = sc->ffindex;
+ pkt->dts = sample->timestamp;
+ if (sample->flags & AVINDEX_DISCARD_FRAME) {
+ pkt->flags |= AV_PKT_FLAG_DISCARD;
+ }
+ if (sc->ctts_data && sc->ctts_index < sc->ctts_count) {
+ pkt->pts = av_sat_add64(pkt->dts, av_sat_add64(sc->dts_shift, sc->ctts_data[sc->ctts_index].duration));
+ /* update ctts context */
+ sc->ctts_sample++;
+ if (sc->ctts_index < sc->ctts_count &&
+ sc->ctts_data[sc->ctts_index].count == sc->ctts_sample) {
+ sc->ctts_index++;
+ sc->ctts_sample = 0;
+ }
+ } else {
+ int64_t next_dts = (sc->current_sample < ffstream(st)->nb_index_entries) ?
+ ffstream(st)->index_entries[sc->current_sample].timestamp : st->duration;
+
+ if (next_dts >= pkt->dts)
+ pkt->duration = next_dts - pkt->dts;
+ pkt->pts = pkt->dts;
+ }
+
+ if (sc->sdtp_data && sc->current_sample <= sc->sdtp_count) {
+ uint8_t sample_flags = sc->sdtp_data[sc->current_sample - 1];
+ uint8_t sample_is_depended_on = (sample_flags >> 2) & 0x3;
+ pkt->flags |= sample_is_depended_on == MOV_SAMPLE_DEPENDENCY_NO ? AV_PKT_FLAG_DISPOSABLE : 0;
+ }
+ pkt->flags |= sample->flags & AVINDEX_KEYFRAME ? AV_PKT_FLAG_KEY : 0;
+ pkt->pos = sample->pos;
+
+ /* Multiple stsd handling. */
+ if (sc->stsc_data) {
+ if (sc->stsc_data[sc->stsc_index].id > 0 &&
+ sc->stsc_data[sc->stsc_index].id - 1 < sc->stsd_count &&
+ sc->stsc_data[sc->stsc_index].id - 1 != sc->last_stsd_index) {
+ int ret = mov_change_extradata(st, pkt);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* Update the stsc index for the next sample */
+ sc->stsc_sample++;
+ if (mov_stsc_index_valid(sc->stsc_index, sc->stsc_count) &&
+ mov_get_stsc_samples(sc, sc->stsc_index) == sc->stsc_sample) {
+ sc->stsc_index++;
+ sc->stsc_sample = 0;
+ }
+ }
+
+ return 0;
+}
+
static int mov_read_packet(AVFormatContext *s, AVPacket *pkt)
{
MOVContext *mov = s->priv_data;
@@ -9320,56 +9379,12 @@ static int mov_read_packet(AVFormatContext *s, AVPacket *pkt)
}
}
- pkt->stream_index = sc->ffindex;
- pkt->dts = sample->timestamp;
- if (sample->flags & AVINDEX_DISCARD_FRAME) {
- pkt->flags |= AV_PKT_FLAG_DISCARD;
- }
- if (sc->ctts_data && sc->ctts_index < sc->ctts_count) {
- pkt->pts = av_sat_add64(pkt->dts, av_sat_add64(sc->dts_shift, sc->ctts_data[sc->ctts_index].duration));
- /* update ctts context */
- sc->ctts_sample++;
- if (sc->ctts_index < sc->ctts_count &&
- sc->ctts_data[sc->ctts_index].count == sc->ctts_sample) {
- sc->ctts_index++;
- sc->ctts_sample = 0;
- }
- } else {
- int64_t next_dts = (sc->current_sample < ffstream(st)->nb_index_entries) ?
- ffstream(st)->index_entries[sc->current_sample].timestamp : st->duration;
+ ret = mov_finalize_packet(s, st, sample, current_index, pkt);
+ if (ret < 0)
+ return ret;
- if (next_dts >= pkt->dts)
- pkt->duration = next_dts - pkt->dts;
- pkt->pts = pkt->dts;
- }
if (st->discard == AVDISCARD_ALL)
goto retry;
- if (sc->sdtp_data && sc->current_sample <= sc->sdtp_count) {
- uint8_t sample_flags = sc->sdtp_data[sc->current_sample - 1];
- uint8_t sample_is_depended_on = (sample_flags >> 2) & 0x3;
- pkt->flags |= sample_is_depended_on == MOV_SAMPLE_DEPENDENCY_NO ? AV_PKT_FLAG_DISPOSABLE : 0;
- }
- pkt->flags |= sample->flags & AVINDEX_KEYFRAME ? AV_PKT_FLAG_KEY : 0;
- pkt->pos = sample->pos;
-
- /* Multiple stsd handling. */
- if (sc->stsc_data) {
- if (sc->stsc_data[sc->stsc_index].id > 0 &&
- sc->stsc_data[sc->stsc_index].id - 1 < sc->stsd_count &&
- sc->stsc_data[sc->stsc_index].id - 1 != sc->last_stsd_index) {
- ret = mov_change_extradata(sc, pkt);
- if (ret < 0)
- return ret;
- }
-
- /* Update the stsc index for the next sample */
- sc->stsc_sample++;
- if (mov_stsc_index_valid(sc->stsc_index, sc->stsc_count) &&
- mov_get_stsc_samples(sc, sc->stsc_index) == sc->stsc_sample) {
- sc->stsc_index++;
- sc->stsc_sample = 0;
- }
- }
if (mov->aax_mode)
aax_filter(pkt->data, pkt->size, mov);
--
2.43.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] 13+ messages in thread
* [FFmpeg-devel] [PATCH 4/8] avformat/mov: make MOVStreamContext refcounted
2024-02-17 22:02 [FFmpeg-devel] [PATCH 1/8] avformat/demux: allow demuxers to output more than one packet per read_packet() call James Almer
2024-02-17 22:02 ` [FFmpeg-devel] [PATCH 2/8] avformat/iamfdec: further split into shareable modules James Almer
2024-02-17 22:02 ` [FFmpeg-devel] [PATCH 3/8] avformat/mov: factorize out setting the output packet properties James Almer
@ 2024-02-17 22:02 ` James Almer
2024-02-17 22:02 ` [FFmpeg-devel] [PATCH 5/8] avformat/mov: add support for Immersive Audio Model and Formats in ISOBMFF James Almer
` (3 subsequent siblings)
6 siblings, 0 replies; 13+ messages in thread
From: James Almer @ 2024-02-17 22:02 UTC (permalink / raw)
To: ffmpeg-devel
This will be useful in the next commit.
Signed-off-by: James Almer <jamrial@gmail.com>
---
libavformat/isom.h | 1 +
libavformat/mov.c | 7 ++++++-
2 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/libavformat/isom.h b/libavformat/isom.h
index a4cca4c798..eee94d0449 100644
--- a/libavformat/isom.h
+++ b/libavformat/isom.h
@@ -166,6 +166,7 @@ typedef struct MOVIndexRange {
typedef struct MOVStreamContext {
AVIOContext *pb;
+ int refcount;
int pb_is_copied;
int ffindex; ///< AVStream index
int next_chunk;
diff --git a/libavformat/mov.c b/libavformat/mov.c
index e548f93f17..66b36eaa4a 100644
--- a/libavformat/mov.c
+++ b/libavformat/mov.c
@@ -212,6 +212,7 @@ static int mov_read_covr(MOVContext *c, AVIOContext *pb, int type, int len)
}
st = c->fc->streams[c->fc->nb_streams - 1];
st->priv_data = sc;
+ sc->refcount = 1;
if (st->attached_pic.size >= 8 && id != AV_CODEC_ID_BMP) {
if (AV_RB64(st->attached_pic.data) == 0x89504e470d0a1a0a) {
@@ -4672,6 +4673,7 @@ static int mov_read_trak(MOVContext *c, AVIOContext *pb, MOVAtom atom)
st->codecpar->codec_type = AVMEDIA_TYPE_DATA;
sc->ffindex = st->index;
c->trak_index = st->index;
+ sc->refcount = 1;
if ((ret = mov_read_default(c, pb, atom)) < 0)
return ret;
@@ -4959,6 +4961,7 @@ static int heif_add_stream(MOVContext *c, HEIFItem *item)
sc = st->priv_data;
sc->pb = c->fc->pb;
sc->pb_is_copied = 1;
+ sc->refcount = 1;
// Populate the necessary fields used by mov_build_index.
sc->stsc_count = 1;
@@ -8660,8 +8663,10 @@ static void mov_free_stream_context(AVFormatContext *s, AVStream *st)
{
MOVStreamContext *sc = st->priv_data;
- if (!sc)
+ if (!sc || --sc->refcount) {
+ st->priv_data = NULL;
return;
+ }
av_freep(&sc->ctts_data);
for (int i = 0; i < sc->drefs_count; i++) {
--
2.43.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] 13+ messages in thread
* [FFmpeg-devel] [PATCH 5/8] avformat/mov: add support for Immersive Audio Model and Formats in ISOBMFF
2024-02-17 22:02 [FFmpeg-devel] [PATCH 1/8] avformat/demux: allow demuxers to output more than one packet per read_packet() call James Almer
` (2 preceding siblings ...)
2024-02-17 22:02 ` [FFmpeg-devel] [PATCH 4/8] avformat/mov: make MOVStreamContext refcounted James Almer
@ 2024-02-17 22:02 ` James Almer
2024-02-17 22:02 ` [FFmpeg-devel] [PATCH 6/8] avformat/iamfenc: further split into shareable modules James Almer
` (2 subsequent siblings)
6 siblings, 0 replies; 13+ messages in thread
From: James Almer @ 2024-02-17 22:02 UTC (permalink / raw)
To: ffmpeg-devel
Signed-off-by: James Almer <jamrial@gmail.com>
---
configure | 2 +-
libavformat/isom.h | 3 +
libavformat/mov.c | 291 ++++++++++++++++++++++++++++++++++++++++++---
3 files changed, 279 insertions(+), 17 deletions(-)
diff --git a/configure b/configure
index 472de63276..968f1ad198 100755
--- a/configure
+++ b/configure
@@ -3550,7 +3550,7 @@ matroska_demuxer_suggest="bzlib zlib"
matroska_muxer_select="mpeg4audio riffenc aac_adtstoasc_bsf pgs_frame_merge_bsf vp9_superframe_bsf"
mlp_demuxer_select="mlp_parser"
mmf_muxer_select="riffenc"
-mov_demuxer_select="iso_media riffdec"
+mov_demuxer_select="iso_media riffdec iamfdec"
mov_demuxer_suggest="zlib"
mov_muxer_select="iso_media riffenc rtpenc_chain vp9_superframe_bsf aac_adtstoasc_bsf ac3_parser"
mp3_demuxer_select="mpegaudio_parser"
diff --git a/libavformat/isom.h b/libavformat/isom.h
index eee94d0449..25025e4cee 100644
--- a/libavformat/isom.h
+++ b/libavformat/isom.h
@@ -168,6 +168,7 @@ typedef struct MOVStreamContext {
AVIOContext *pb;
int refcount;
int pb_is_copied;
+ int id; ///< AVStream id
int ffindex; ///< AVStream index
int next_chunk;
unsigned int chunk_count;
@@ -264,6 +265,8 @@ typedef struct MOVStreamContext {
AVEncryptionInfo *default_encrypted_sample;
MOVEncryptionIndex *encryption_index;
} cenc;
+
+ struct IAMFDemuxContext *iamf;
} MOVStreamContext;
typedef struct HEIFItem {
diff --git a/libavformat/mov.c b/libavformat/mov.c
index 66b36eaa4a..1a1db2746c 100644
--- a/libavformat/mov.c
+++ b/libavformat/mov.c
@@ -58,6 +58,8 @@
#include "internal.h"
#include "avio_internal.h"
#include "demux.h"
+#include "iamf_parse.h"
+#include "iamf_reader.h"
#include "dovi_isom.h"
#include "riff.h"
#include "isom.h"
@@ -212,6 +214,7 @@ static int mov_read_covr(MOVContext *c, AVIOContext *pb, int type, int len)
}
st = c->fc->streams[c->fc->nb_streams - 1];
st->priv_data = sc;
+ sc->id = st->id;
sc->refcount = 1;
if (st->attached_pic.size >= 8 && id != AV_CODEC_ID_BMP) {
@@ -836,6 +839,171 @@ static int mov_read_dac3(MOVContext *c, AVIOContext *pb, MOVAtom atom)
return 0;
}
+static int mov_read_iacb(MOVContext *c, AVIOContext *pb, MOVAtom atom)
+{
+ AVStream *st;
+ MOVStreamContext *sc;
+ FFIOContext b;
+ AVIOContext *descriptor_pb;
+ AVDictionary *metadata;
+ IAMFContext *iamf;
+ int64_t start_time, duration;
+ unsigned descriptors_size;
+ int nb_frames, disposition;
+ int version, ret;
+
+ if (atom.size < 5)
+ return AVERROR_INVALIDDATA;
+
+ if (c->fc->nb_streams < 1)
+ return 0;
+
+ version = avio_r8(pb);
+ if (version != 1) {
+ av_log(c->fc, AV_LOG_ERROR, "%s configurationVersion %d",
+ version < 1 ? "invalid" : "unsupported", version);
+ return AVERROR_INVALIDDATA;
+ }
+
+ descriptors_size = ffio_read_leb(pb);
+ if (!descriptors_size || descriptors_size > INT_MAX)
+ return AVERROR_INVALIDDATA;
+
+ st = c->fc->streams[c->fc->nb_streams - 1];
+ sc = st->priv_data;
+
+ sc->iamf = av_mallocz(sizeof(*sc->iamf));
+ if (!sc->iamf)
+ return AVERROR(ENOMEM);
+ iamf = &sc->iamf->iamf;
+
+ st->codecpar->extradata = av_malloc(descriptors_size);
+ if (!st->codecpar->extradata)
+ return AVERROR(ENOMEM);
+ st->codecpar->extradata_size = descriptors_size;
+
+ ret = avio_read(pb, st->codecpar->extradata, descriptors_size);
+ if (ret != descriptors_size)
+ return ret < 0 ? ret : AVERROR_INVALIDDATA;
+
+ ffio_init_read_context(&b, st->codecpar->extradata, descriptors_size);
+ descriptor_pb = &b.pub;
+
+ ret = ff_iamfdec_read_descriptors(iamf, descriptor_pb, descriptors_size, c->fc);
+ if (ret < 0)
+ return ret;
+
+ metadata = st->metadata;
+ st->metadata = NULL;
+ start_time = st->start_time;
+ nb_frames = st->nb_frames;
+ duration = st->duration;
+ disposition = st->disposition;
+
+ for (int i = 0; i < iamf->nb_audio_elements; i++) {
+ IAMFAudioElement *audio_element = iamf->audio_elements[i];
+ AVStreamGroup *stg =
+ avformat_stream_group_create(c->fc, AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT, NULL);
+
+ if (!stg) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+
+ av_iamf_audio_element_free(&stg->params.iamf_audio_element);
+ stg->id = audio_element->audio_element_id;
+ stg->params.iamf_audio_element = audio_element->element;
+
+ for (int j = 0; j < audio_element->nb_substreams; j++) {
+ IAMFSubStream *substream = &audio_element->substreams[j];
+ AVStream *stream;
+
+ if (!i && !j)
+ stream = st;
+ else
+ stream = avformat_new_stream(c->fc, NULL);
+ if (!stream) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+
+ stream->start_time = start_time;
+ stream->nb_frames = nb_frames;
+ stream->duration = duration;
+ stream->disposition = disposition;
+ if (stream != st) {
+ stream->priv_data = sc;
+ sc->refcount++;
+ }
+
+ ret = avcodec_parameters_copy(stream->codecpar, substream->codecpar);
+ if (ret < 0)
+ goto fail;
+
+ stream->id = substream->audio_substream_id;
+
+ avpriv_set_pts_info(st, 64, 1, sc->time_scale);
+
+ ret = avformat_stream_group_add_stream(stg, stream);
+ if (ret < 0)
+ goto fail;
+ }
+
+ ret = av_dict_copy(&stg->metadata, metadata, 0);
+ if (ret < 0)
+ goto fail;
+ }
+
+ for (int i = 0; i < iamf->nb_mix_presentations; i++) {
+ IAMFMixPresentation *mix_presentation = iamf->mix_presentations[i];
+ const AVIAMFMixPresentation *mix = mix_presentation->mix;
+ AVStreamGroup *stg =
+ avformat_stream_group_create(c->fc, AV_STREAM_GROUP_PARAMS_IAMF_MIX_PRESENTATION, NULL);
+
+ if (!stg) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+
+ av_iamf_mix_presentation_free(&stg->params.iamf_mix_presentation);
+ stg->id = mix_presentation->mix_presentation_id;
+ stg->params.iamf_mix_presentation = mix_presentation->mix;
+
+ for (int j = 0; j < mix->nb_submixes; j++) {
+ const AVIAMFSubmix *submix = mix->submixes[j];
+
+ for (int k = 0; k < submix->nb_elements; k++) {
+ const AVIAMFSubmixElement *submix_element = submix->elements[k];
+ const AVStreamGroup *audio_element = NULL;
+
+ for (int l = 0; l < c->fc->nb_stream_groups; l++)
+ if (c->fc->stream_groups[l]->type == AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT &&
+ c->fc->stream_groups[l]->id == submix_element->audio_element_id) {
+ audio_element = c->fc->stream_groups[l];
+ break;
+ }
+ av_assert0(audio_element);
+
+ for (int l = 0; l < audio_element->nb_streams; l++) {
+ ret = avformat_stream_group_add_stream(stg, audio_element->streams[l]);
+ if (ret < 0 && ret != AVERROR(EEXIST))
+ goto fail;
+ }
+ }
+ }
+
+ ret = av_dict_copy(&stg->metadata, metadata, 0);
+ if (ret < 0)
+ goto fail;
+ }
+
+ ret = 0;
+fail:
+ av_dict_free(&metadata);
+
+ return ret;
+}
+
static int mov_read_dec3(MOVContext *c, AVIOContext *pb, MOVAtom atom)
{
AVStream *st;
@@ -1381,7 +1549,7 @@ static int64_t get_frag_time(AVFormatContext *s, AVStream *dst_st,
// If the stream is referenced by any sidx, limit the search
// to fragments that referenced this stream in the sidx
if (sc->has_sidx) {
- frag_stream_info = get_frag_stream_info(frag_index, index, dst_st->id);
+ frag_stream_info = get_frag_stream_info(frag_index, index, sc->id);
if (frag_stream_info->sidx_pts != AV_NOPTS_VALUE)
return frag_stream_info->sidx_pts;
if (frag_stream_info->first_tfra_pts != AV_NOPTS_VALUE)
@@ -1392,9 +1560,11 @@ static int64_t get_frag_time(AVFormatContext *s, AVStream *dst_st,
for (i = 0; i < frag_index->item[index].nb_stream_info; i++) {
AVStream *frag_stream = NULL;
frag_stream_info = &frag_index->item[index].stream_info[i];
- for (j = 0; j < s->nb_streams; j++)
- if (s->streams[j]->id == frag_stream_info->id)
+ for (j = 0; j < s->nb_streams; j++) {
+ MOVStreamContext *sc2 = s->streams[j]->priv_data;
+ if (sc2->id == frag_stream_info->id)
frag_stream = s->streams[j];
+ }
if (!frag_stream) {
av_log(s, AV_LOG_WARNING, "No stream matching sidx ID found.\n");
continue;
@@ -1460,12 +1630,13 @@ static int update_frag_index(MOVContext *c, int64_t offset)
for (i = 0; i < c->fc->nb_streams; i++) {
// Avoid building frag index if streams lack track id.
- if (c->fc->streams[i]->id < 0) {
+ MOVStreamContext *sc = c->fc->streams[i]->priv_data;
+ if (sc->id < 0) {
av_free(frag_stream_info);
return AVERROR_INVALIDDATA;
}
- frag_stream_info[i].id = c->fc->streams[i]->id;
+ frag_stream_info[i].id = sc->id;
frag_stream_info[i].sidx_pts = AV_NOPTS_VALUE;
frag_stream_info[i].tfdt_dts = AV_NOPTS_VALUE;
frag_stream_info[i].next_trun_dts = AV_NOPTS_VALUE;
@@ -3260,7 +3431,7 @@ static int mov_read_stts(MOVContext *c, AVIOContext *pb, MOVAtom atom)
"All samples in data stream index:id [%d:%d] have zero "
"duration, stream set to be discarded by default. Override "
"using AVStream->discard or -discard for ffmpeg command.\n",
- st->index, st->id);
+ st->index, sc->id);
st->discard = AVDISCARD_ALL;
}
sc->track_end = duration;
@@ -4640,6 +4811,51 @@ static void fix_timescale(MOVContext *c, MOVStreamContext *sc)
}
}
+static int mov_update_iamf_streams(MOVContext *c, const AVStream *st)
+{
+ const MOVStreamContext *sc = st->priv_data;
+ const IAMFContext *iamf = &sc->iamf->iamf;
+
+ for (int i = 0; i < iamf->nb_audio_elements; i++) {
+ const AVStreamGroup *stg = NULL;
+
+ for (int j = 0; j < c->fc->nb_stream_groups; j++)
+ if (c->fc->stream_groups[j]->id == iamf->audio_elements[i]->audio_element_id)
+ stg = c->fc->stream_groups[j];
+ av_assert0(stg);
+
+ for (int j = 0; j < stg->nb_streams; j++) {
+ const FFStream *sti = cffstream(st);
+ AVStream *out = stg->streams[j];
+ FFStream *out_sti = ffstream(stg->streams[j]);
+
+ out->codecpar->bit_rate = 0;
+
+ if (out == st)
+ continue;
+
+ out->time_base = st->time_base;
+ out->start_time = st->start_time;
+ out->duration = st->duration;
+ out->nb_frames = st->nb_frames;
+ out->disposition = st->disposition;
+ out->discard = st->discard;
+
+ av_assert0(!out_sti->index_entries);
+ out_sti->index_entries = av_malloc(sti->index_entries_allocated_size);
+ if (!out_sti->index_entries)
+ return AVERROR(ENOMEM);
+
+ out_sti->index_entries_allocated_size = sti->index_entries_allocated_size;
+ out_sti->nb_index_entries = sti->nb_index_entries;
+ out_sti->skip_samples = sti->skip_samples;
+ memcpy(out_sti->index_entries, sti->index_entries, sti->index_entries_allocated_size);
+ }
+ }
+
+ return 0;
+}
+
static int mov_read_trak(MOVContext *c, AVIOContext *pb, MOVAtom atom)
{
AVStream *st;
@@ -4720,6 +4936,12 @@ static int mov_read_trak(MOVContext *c, AVIOContext *pb, MOVAtom atom)
mov_build_index(c, st);
+ if (sc->iamf) {
+ ret = mov_update_iamf_streams(c, st);
+ if (ret < 0)
+ return ret;
+ }
+
if (sc->dref_id-1 < sc->drefs_count && sc->drefs[sc->dref_id-1].path) {
MOVDref *dref = &sc->drefs[sc->dref_id - 1];
if (c->enable_drefs) {
@@ -4952,6 +5174,7 @@ static int heif_add_stream(MOVContext *c, HEIFItem *item)
st->priv_data = sc;
st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
st->codecpar->codec_id = mov_codec_id(st, item->type);
+ sc->id = st->id;
sc->ffindex = st->index;
c->trak_index = st->index;
st->avg_frame_rate.num = st->avg_frame_rate.den = 1;
@@ -5050,6 +5273,7 @@ static int mov_read_tkhd(MOVContext *c, AVIOContext *pb, MOVAtom atom)
avio_rb32(pb); /* modification time */
}
st->id = (int)avio_rb32(pb); /* track id (NOT 0 !)*/
+ sc->id = st->id;
avio_rb32(pb); /* reserved */
/* highlevel (considering edits) duration in movie timebase */
@@ -5224,7 +5448,8 @@ static int mov_read_tfdt(MOVContext *c, AVIOContext *pb, MOVAtom atom)
int64_t base_media_decode_time;
for (i = 0; i < c->fc->nb_streams; i++) {
- if (c->fc->streams[i]->id == frag->track_id) {
+ sc = c->fc->streams[i]->priv_data;
+ if (sc->id == frag->track_id) {
st = c->fc->streams[i];
break;
}
@@ -5277,7 +5502,8 @@ static int mov_read_trun(MOVContext *c, AVIOContext *pb, MOVAtom atom)
}
for (i = 0; i < c->fc->nb_streams; i++) {
- if (c->fc->streams[i]->id == frag->track_id) {
+ sc = c->fc->streams[i]->priv_data;
+ if (sc->id == frag->track_id) {
st = c->fc->streams[i];
sti = ffstream(st);
break;
@@ -5580,7 +5806,8 @@ static int mov_read_sidx(MOVContext *c, AVIOContext *pb, MOVAtom atom)
track_id = avio_rb32(pb); // Reference ID
for (i = 0; i < c->fc->nb_streams; i++) {
- if (c->fc->streams[i]->id == track_id) {
+ sc = c->fc->streams[i]->priv_data;
+ if (sc->id == track_id) {
st = c->fc->streams[i];
break;
}
@@ -6497,7 +6724,8 @@ static int get_current_encryption_info(MOVContext *c, MOVEncryptionIndex **encry
frag_stream_info = get_current_frag_stream_info(&c->frag_index);
if (frag_stream_info) {
for (i = 0; i < c->fc->nb_streams; i++) {
- if (c->fc->streams[i]->id == frag_stream_info->id) {
+ *sc = c->fc->streams[i]->priv_data;
+ if ((*sc)->id == frag_stream_info->id) {
st = c->fc->streams[i];
break;
}
@@ -7441,7 +7669,7 @@ static int cenc_filter(MOVContext *mov, AVStream* st, MOVStreamContext *sc, AVPa
AVEncryptionInfo *encrypted_sample;
int encrypted_index, ret;
- frag_stream_info = get_frag_stream_info_from_pkt(&mov->frag_index, pkt, st->id);
+ frag_stream_info = get_frag_stream_info_from_pkt(&mov->frag_index, pkt, sc->id);
encrypted_index = current_index;
encryption_index = NULL;
if (frag_stream_info) {
@@ -8244,6 +8472,7 @@ static const MOVParseTableEntry mov_default_parse_table[] = {
{ MKTAG('i','p','r','p'), mov_read_iprp },
{ MKTAG('i','i','n','f'), mov_read_iinf },
{ MKTAG('a','m','v','e'), mov_read_amve }, /* ambient viewing environment box */
+{ MKTAG('i','a','c','b'), mov_read_iacb },
{ 0, NULL }
};
@@ -8475,11 +8704,13 @@ static void mov_read_chapters(AVFormatContext *s)
AVStream *st = NULL;
FFStream *sti = NULL;
chapter_track = mov->chapter_tracks[j];
- for (i = 0; i < s->nb_streams; i++)
- if (s->streams[i]->id == chapter_track) {
+ for (i = 0; i < s->nb_streams; i++) {
+ sc = mov->fc->streams[i]->priv_data;
+ if (sc->id == chapter_track) {
st = s->streams[i];
break;
}
+ }
if (!st) {
av_log(s, AV_LOG_ERROR, "Referenced QT chapter track not found\n");
continue;
@@ -8712,6 +8943,10 @@ static void mov_free_stream_context(AVFormatContext *s, AVStream *st)
av_freep(&sc->mastering);
av_freep(&sc->coll);
av_freep(&sc->ambient);
+
+ if (sc->iamf)
+ ff_iamf_read_deinit(sc->iamf);
+ av_freep(&sc->iamf);
}
static int mov_read_close(AVFormatContext *s)
@@ -8966,9 +9201,11 @@ static int mov_read_header(AVFormatContext *s)
AVDictionaryEntry *tcr;
int tmcd_st_id = -1;
- for (j = 0; j < s->nb_streams; j++)
- if (s->streams[j]->id == sc->timecode_track)
+ for (j = 0; j < s->nb_streams; j++) {
+ MOVStreamContext *sc2 = s->streams[j]->priv_data;
+ if (sc2->id == sc->timecode_track)
tmcd_st_id = j;
+ }
if (tmcd_st_id < 0 || tmcd_st_id == i)
continue;
@@ -9348,7 +9585,29 @@ static int mov_read_packet(AVFormatContext *s, AVPacket *pkt)
if (st->codecpar->codec_id == AV_CODEC_ID_EIA_608 && sample->size > 8)
ret = get_eia608_packet(sc->pb, pkt, sample->size);
- else
+ else if (sc->iamf) {
+ int64_t pts, dts, pos, duration;
+ int flags, size = sample->size;
+ ret = mov_finalize_packet(s, st, sample, current_index, pkt);
+ pts = pkt->pts; dts = pkt->dts;
+ pos = pkt->pos; flags = pkt->flags;
+ duration = pkt->duration;
+ while (!ret && size > 0) {
+ ret = ff_iamf_read_packet(s, sc->iamf, sc->pb, size, pkt);
+ if (ret < 0) {
+ if (should_retry(sc->pb, ret))
+ mov_current_sample_dec(sc);
+ return ret;
+ }
+ size -= ret;
+ pkt->pts = pts; pkt->dts = dts;
+ pkt->pos = pos; pkt->flags |= flags;
+ pkt->duration = duration;
+ ret = ff_buffer_packet(s, pkt);
+ }
+ if (!ret)
+ return FFERROR_REDO;
+ } else
ret = av_get_packet(sc->pb, pkt, sample->size);
if (ret < 0) {
if (should_retry(sc->pb, ret)) {
--
2.43.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] 13+ messages in thread
* [FFmpeg-devel] [PATCH 6/8] avformat/iamfenc: further split into shareable modules
2024-02-17 22:02 [FFmpeg-devel] [PATCH 1/8] avformat/demux: allow demuxers to output more than one packet per read_packet() call James Almer
` (3 preceding siblings ...)
2024-02-17 22:02 ` [FFmpeg-devel] [PATCH 5/8] avformat/mov: add support for Immersive Audio Model and Formats in ISOBMFF James Almer
@ 2024-02-17 22:02 ` James Almer
2024-02-17 22:02 ` [FFmpeg-devel] [PATCH 7/8] avformat/movenc: add support for Immersive Audio Model and Formats in ISOBMFF James Almer
2024-02-17 22:02 ` [FFmpeg-devel] [PATCH 8/8] fate: add IAMF in mp4 tests James Almer
6 siblings, 0 replies; 13+ messages in thread
From: James Almer @ 2024-02-17 22:02 UTC (permalink / raw)
To: ffmpeg-devel
Signed-off-by: James Almer <jamrial@gmail.com>
---
configure | 4 +-
libavformat/Makefile | 3 +-
libavformat/iamf_writer.c | 239 ++++++++++++++++++++++++++++++++++++++
libavformat/iamf_writer.h | 7 ++
libavformat/iamfenc.c | 228 ++----------------------------------
5 files changed, 258 insertions(+), 223 deletions(-)
diff --git a/configure b/configure
index 968f1ad198..adaeae2604 100755
--- a/configure
+++ b/configure
@@ -2517,6 +2517,7 @@ CONFIG_EXTRA="
huffyuvdsp
huffyuvencdsp
iamfdec
+ iamfenc
idctdsp
iirfilter
inflate_wrapper
@@ -3536,6 +3537,7 @@ hds_muxer_select="flv_muxer"
hls_demuxer_select="adts_header ac3_parser mov_demuxer mpegts_demuxer"
hls_muxer_select="mov_muxer mpegts_muxer"
iamf_demuxer_select="iamfdec"
+iamf_muxer_select="iamfenc"
image2_alias_pix_demuxer_select="image2_demuxer"
image2_brender_pix_demuxer_select="image2_demuxer"
imf_demuxer_deps="libxml2"
@@ -3552,7 +3554,7 @@ mlp_demuxer_select="mlp_parser"
mmf_muxer_select="riffenc"
mov_demuxer_select="iso_media riffdec iamfdec"
mov_demuxer_suggest="zlib"
-mov_muxer_select="iso_media riffenc rtpenc_chain vp9_superframe_bsf aac_adtstoasc_bsf ac3_parser"
+mov_muxer_select="iamfenc iso_media riffenc rtpenc_chain vp9_superframe_bsf aac_adtstoasc_bsf ac3_parser"
mp3_demuxer_select="mpegaudio_parser"
mp3_muxer_select="mpegaudioheader"
mp4_muxer_select="mov_muxer"
diff --git a/libavformat/Makefile b/libavformat/Makefile
index 3b83f5a685..4a380668bd 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -36,6 +36,7 @@ OBJS-$(HAVE_LIBC_MSVCRT) += file_open.o
# subsystems
OBJS-$(CONFIG_ISO_MEDIA) += isom.o
OBJS-$(CONFIG_IAMFDEC) += iamf_reader.o iamf_parse.o iamf.o
+OBJS-$(CONFIG_IAMFENC) += iamf_writer.o iamf.o
OBJS-$(CONFIG_NETWORK) += network.o
OBJS-$(CONFIG_RIFFDEC) += riffdec.o
OBJS-$(CONFIG_RIFFENC) += riffenc.o
@@ -260,7 +261,7 @@ OBJS-$(CONFIG_HLS_DEMUXER) += hls.o hls_sample_encryption.o
OBJS-$(CONFIG_HLS_MUXER) += hlsenc.o hlsplaylist.o avc.o
OBJS-$(CONFIG_HNM_DEMUXER) += hnm.o
OBJS-$(CONFIG_IAMF_DEMUXER) += iamfdec.o
-OBJS-$(CONFIG_IAMF_MUXER) += iamfenc.o iamf_writer.o iamf.o
+OBJS-$(CONFIG_IAMF_MUXER) += iamfenc.o
OBJS-$(CONFIG_ICO_DEMUXER) += icodec.o
OBJS-$(CONFIG_ICO_MUXER) += icoenc.o
OBJS-$(CONFIG_IDCIN_DEMUXER) += idcin.o
diff --git a/libavformat/iamf_writer.c b/libavformat/iamf_writer.c
index 1a360dee2f..25e2fd9e8a 100644
--- a/libavformat/iamf_writer.c
+++ b/libavformat/iamf_writer.c
@@ -835,3 +835,242 @@ int ff_iamf_write_descriptors(const IAMFContext *iamf, AVIOContext *pb, void *lo
return 0;
}
+
+static int write_parameter_block(const IAMFContext *iamf, AVIOContext *pb,
+ const AVIAMFParamDefinition *param, void *log_ctx)
+{
+ uint8_t header[MAX_IAMF_OBU_HEADER_SIZE];
+ IAMFParamDefinition *param_definition = ff_iamf_get_param_definition(iamf, param->parameter_id);
+ PutBitContext pbc;
+ AVIOContext *dyn_bc;
+ uint8_t *dyn_buf = NULL;
+ int dyn_size, ret;
+
+ if (param->type > AV_IAMF_PARAMETER_DEFINITION_RECON_GAIN) {
+ av_log(log_ctx, AV_LOG_DEBUG, "Ignoring side data with unknown type %u\n",
+ param->type);
+ return 0;
+ }
+
+ if (!param_definition) {
+ av_log(log_ctx, AV_LOG_ERROR, "Non-existent Parameter Definition with ID %u referenced by a packet\n",
+ param->parameter_id);
+ return AVERROR(EINVAL);
+ }
+
+ if (param->type != param_definition->param->type) {
+ av_log(log_ctx, AV_LOG_ERROR, "Inconsistent values for Parameter Definition "
+ "with ID %u in a packet\n",
+ param->parameter_id);
+ return AVERROR(EINVAL);
+ }
+
+ ret = avio_open_dyn_buf(&dyn_bc);
+ if (ret < 0)
+ return ret;
+
+ // Sequence Header
+ init_put_bits(&pbc, header, sizeof(header));
+ put_bits(&pbc, 5, IAMF_OBU_IA_PARAMETER_BLOCK);
+ put_bits(&pbc, 3, 0);
+ flush_put_bits(&pbc);
+ avio_write(pb, header, put_bytes_count(&pbc, 1));
+
+ ffio_write_leb(dyn_bc, param->parameter_id);
+ if (!param_definition->mode) {
+ ffio_write_leb(dyn_bc, param->duration);
+ ffio_write_leb(dyn_bc, param->constant_subblock_duration);
+ if (param->constant_subblock_duration == 0)
+ ffio_write_leb(dyn_bc, param->nb_subblocks);
+ }
+
+ for (int i = 0; i < param->nb_subblocks; i++) {
+ const void *subblock = av_iamf_param_definition_get_subblock(param, i);
+
+ switch (param->type) {
+ case AV_IAMF_PARAMETER_DEFINITION_MIX_GAIN: {
+ const AVIAMFMixGain *mix = subblock;
+ if (!param_definition->mode && param->constant_subblock_duration == 0)
+ ffio_write_leb(dyn_bc, mix->subblock_duration);
+
+ ffio_write_leb(dyn_bc, mix->animation_type);
+
+ avio_wb16(dyn_bc, rescale_rational(mix->start_point_value, 1 << 8));
+ if (mix->animation_type >= AV_IAMF_ANIMATION_TYPE_LINEAR)
+ avio_wb16(dyn_bc, rescale_rational(mix->end_point_value, 1 << 8));
+ if (mix->animation_type == AV_IAMF_ANIMATION_TYPE_BEZIER) {
+ avio_wb16(dyn_bc, rescale_rational(mix->control_point_value, 1 << 8));
+ avio_w8(dyn_bc, av_clip_uint8(av_rescale(mix->control_point_relative_time.num, 1 << 8,
+ mix->control_point_relative_time.den)));
+ }
+ break;
+ }
+ case AV_IAMF_PARAMETER_DEFINITION_DEMIXING: {
+ const AVIAMFDemixingInfo *demix = subblock;
+ if (!param_definition->mode && param->constant_subblock_duration == 0)
+ ffio_write_leb(dyn_bc, demix->subblock_duration);
+
+ avio_w8(dyn_bc, demix->dmixp_mode << 5);
+ break;
+ }
+ case AV_IAMF_PARAMETER_DEFINITION_RECON_GAIN: {
+ const AVIAMFReconGain *recon = subblock;
+ const AVIAMFAudioElement *audio_element = param_definition->audio_element->element;
+
+ if (!param_definition->mode && param->constant_subblock_duration == 0)
+ ffio_write_leb(dyn_bc, recon->subblock_duration);
+
+ if (!audio_element) {
+ av_log(log_ctx, AV_LOG_ERROR, "Invalid Parameter Definition with ID %u referenced by a packet\n", param->parameter_id);
+ return AVERROR(EINVAL);
+ }
+
+ for (int j = 0; j < audio_element->nb_layers; j++) {
+ const AVIAMFLayer *layer = audio_element->layers[j];
+
+ if (layer->flags & AV_IAMF_LAYER_FLAG_RECON_GAIN) {
+ unsigned int recon_gain_flags = 0;
+ int k = 0;
+
+ for (; k < 7; k++)
+ recon_gain_flags |= (1 << k) * !!recon->recon_gain[j][k];
+ for (; k < 12; k++)
+ recon_gain_flags |= (2 << k) * !!recon->recon_gain[j][k];
+ if (recon_gain_flags >> 8)
+ recon_gain_flags |= (1 << k);
+
+ ffio_write_leb(dyn_bc, recon_gain_flags);
+ for (k = 0; k < 12; k++) {
+ if (recon->recon_gain[j][k])
+ avio_w8(dyn_bc, recon->recon_gain[j][k]);
+ }
+ }
+ }
+ break;
+ }
+ default:
+ av_assert0(0);
+ }
+ }
+
+ dyn_size = avio_get_dyn_buf(dyn_bc, &dyn_buf);
+ ffio_write_leb(pb, dyn_size);
+ avio_write(pb, dyn_buf, dyn_size);
+ ffio_free_dyn_buf(&dyn_bc);
+
+ return 0;
+}
+
+int ff_iamf_write_parameter_blocks(const IAMFContext *iamf, AVIOContext *pb,
+ AVPacket *pkt, void *log_ctx)
+{
+ AVIAMFParamDefinition *mix =
+ (AVIAMFParamDefinition *)av_packet_get_side_data(pkt,
+ AV_PKT_DATA_IAMF_MIX_GAIN_PARAM,
+ NULL);
+ AVIAMFParamDefinition *demix =
+ (AVIAMFParamDefinition *)av_packet_get_side_data(pkt,
+ AV_PKT_DATA_IAMF_DEMIXING_INFO_PARAM,
+ NULL);
+ AVIAMFParamDefinition *recon =
+ (AVIAMFParamDefinition *)av_packet_get_side_data(pkt,
+ AV_PKT_DATA_IAMF_RECON_GAIN_INFO_PARAM,
+ NULL);
+
+ if (mix) {
+ int ret = write_parameter_block(iamf, pb, mix, log_ctx);
+ if (ret < 0)
+ return ret;
+ }
+ if (demix) {
+ int ret = write_parameter_block(iamf, pb, demix, log_ctx);
+ if (ret < 0)
+ return ret;
+ }
+ if (recon) {
+ int ret = write_parameter_block(iamf, pb, recon, log_ctx);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+int ff_iamf_write_audio_frame(const IAMFContext *iamf, AVIOContext *pb,
+ unsigned audio_substream_id, AVPacket *pkt)
+{
+ uint8_t header[MAX_IAMF_OBU_HEADER_SIZE];
+ PutBitContext pbc;
+ AVIOContext *dyn_bc;
+ uint8_t *side_data, *dyn_buf = NULL;
+ unsigned int skip_samples = 0, discard_padding = 0;
+ size_t side_data_size;
+ int dyn_size, type = audio_substream_id <= 17 ?
+ audio_substream_id + IAMF_OBU_IA_AUDIO_FRAME_ID0 : IAMF_OBU_IA_AUDIO_FRAME;
+ int ret;
+
+ if (!pkt->size) {
+ uint8_t *new_extradata = av_packet_get_side_data(pkt,
+ AV_PKT_DATA_NEW_EXTRADATA,
+ NULL);
+
+ if (!new_extradata)
+ return AVERROR_INVALIDDATA;
+
+ // TODO: update FLAC Streaminfo on seekable output
+ return 0;
+ }
+
+ side_data = av_packet_get_side_data(pkt, AV_PKT_DATA_SKIP_SAMPLES,
+ &side_data_size);
+
+ if (side_data && side_data_size >= 10) {
+ skip_samples = AV_RL32(side_data);
+ discard_padding = AV_RL32(side_data + 4);
+ }
+
+ ret = avio_open_dyn_buf(&dyn_bc);
+ if (ret < 0)
+ return ret;
+
+ init_put_bits(&pbc, header, sizeof(header));
+ put_bits(&pbc, 5, type);
+ put_bits(&pbc, 1, 0); // obu_redundant_copy
+ put_bits(&pbc, 1, skip_samples || discard_padding);
+ put_bits(&pbc, 1, 0); // obu_extension_flag
+ flush_put_bits(&pbc);
+ avio_write(pb, header, put_bytes_count(&pbc, 1));
+
+ if (skip_samples || discard_padding) {
+ ffio_write_leb(dyn_bc, discard_padding);
+ ffio_write_leb(dyn_bc, skip_samples);
+ }
+
+ if (audio_substream_id > 17)
+ ffio_write_leb(dyn_bc, audio_substream_id);
+
+ dyn_size = avio_get_dyn_buf(dyn_bc, &dyn_buf);
+ ffio_write_leb(pb, dyn_size + pkt->size);
+ avio_write(pb, dyn_buf, dyn_size);
+ ffio_free_dyn_buf(&dyn_bc);
+ avio_write(pb, pkt->data, pkt->size);
+
+ return 0;
+}
+
+void ff_iamf_write_deinit(IAMFContext *iamf)
+{
+ for (int i = 0; i < iamf->nb_audio_elements; i++) {
+ IAMFAudioElement *audio_element = iamf->audio_elements[i];
+ audio_element->element = NULL;
+ }
+
+ for (int i = 0; i < iamf->nb_mix_presentations; i++) {
+ IAMFMixPresentation *mix_presentation = iamf->mix_presentations[i];
+ mix_presentation->mix = NULL;
+ }
+
+ ff_iamf_uninit_context(iamf);
+
+ return;
+}
diff --git a/libavformat/iamf_writer.h b/libavformat/iamf_writer.h
index 24f1c14769..bc08bbc05a 100644
--- a/libavformat/iamf_writer.h
+++ b/libavformat/iamf_writer.h
@@ -24,6 +24,7 @@
#include <stddef.h>
+#include "libavcodec/packet.h"
#include "avformat.h"
#include "avio.h"
#include "iamf.h"
@@ -47,4 +48,10 @@ int ff_iamf_add_mix_presentation(IAMFContext *iamf, const AVStreamGroup *stg, vo
int ff_iamf_write_descriptors(const IAMFContext *iamf, AVIOContext *pb, void *log_ctx);
+int ff_iamf_write_parameter_blocks(const IAMFContext *iamf, AVIOContext *pb,
+ AVPacket *pkt, void *log_ctx);
+int ff_iamf_write_audio_frame(const IAMFContext *iamf, AVIOContext *pb,
+ unsigned audio_substream_id, AVPacket *pkt);
+void ff_iamf_write_deinit(IAMFContext *iamf);
+
#endif /* AVFORMAT_IAMF_WRITER_H */
diff --git a/libavformat/iamfenc.c b/libavformat/iamfenc.c
index b588a507bb..a838cc77fe 100644
--- a/libavformat/iamfenc.c
+++ b/libavformat/iamfenc.c
@@ -133,239 +133,25 @@ static int iamf_write_header(AVFormatContext *s)
return 0;
}
-static inline int rescale_rational(AVRational q, int b)
-{
- return av_clip_int16(av_rescale(q.num, b, q.den));
-}
-
-static int write_parameter_block(AVFormatContext *s, const AVIAMFParamDefinition *param)
-{
- const IAMFMuxContext *const c = s->priv_data;
- const IAMFContext *const iamf = &c->iamf;
- uint8_t header[MAX_IAMF_OBU_HEADER_SIZE];
- IAMFParamDefinition *param_definition = ff_iamf_get_param_definition(iamf, param->parameter_id);
- PutBitContext pb;
- AVIOContext *dyn_bc;
- uint8_t *dyn_buf = NULL;
- int dyn_size, ret;
-
- if (param->type > AV_IAMF_PARAMETER_DEFINITION_RECON_GAIN) {
- av_log(s, AV_LOG_DEBUG, "Ignoring side data with unknown type %u\n",
- param->type);
- return 0;
- }
-
- if (!param_definition) {
- av_log(s, AV_LOG_ERROR, "Non-existent Parameter Definition with ID %u referenced by a packet\n",
- param->parameter_id);
- return AVERROR(EINVAL);
- }
-
- if (param->type != param_definition->param->type) {
- av_log(s, AV_LOG_ERROR, "Inconsistent values for Parameter Definition "
- "with ID %u in a packet\n",
- param->parameter_id);
- return AVERROR(EINVAL);
- }
-
- ret = avio_open_dyn_buf(&dyn_bc);
- if (ret < 0)
- return ret;
-
- // Sequence Header
- init_put_bits(&pb, header, sizeof(header));
- put_bits(&pb, 5, IAMF_OBU_IA_PARAMETER_BLOCK);
- put_bits(&pb, 3, 0);
- flush_put_bits(&pb);
- avio_write(s->pb, header, put_bytes_count(&pb, 1));
-
- ffio_write_leb(dyn_bc, param->parameter_id);
- if (!param_definition->mode) {
- ffio_write_leb(dyn_bc, param->duration);
- ffio_write_leb(dyn_bc, param->constant_subblock_duration);
- if (param->constant_subblock_duration == 0)
- ffio_write_leb(dyn_bc, param->nb_subblocks);
- }
-
- for (int i = 0; i < param->nb_subblocks; i++) {
- const void *subblock = av_iamf_param_definition_get_subblock(param, i);
-
- switch (param->type) {
- case AV_IAMF_PARAMETER_DEFINITION_MIX_GAIN: {
- const AVIAMFMixGain *mix = subblock;
- if (!param_definition->mode && param->constant_subblock_duration == 0)
- ffio_write_leb(dyn_bc, mix->subblock_duration);
-
- ffio_write_leb(dyn_bc, mix->animation_type);
-
- avio_wb16(dyn_bc, rescale_rational(mix->start_point_value, 1 << 8));
- if (mix->animation_type >= AV_IAMF_ANIMATION_TYPE_LINEAR)
- avio_wb16(dyn_bc, rescale_rational(mix->end_point_value, 1 << 8));
- if (mix->animation_type == AV_IAMF_ANIMATION_TYPE_BEZIER) {
- avio_wb16(dyn_bc, rescale_rational(mix->control_point_value, 1 << 8));
- avio_w8(dyn_bc, av_clip_uint8(av_rescale(mix->control_point_relative_time.num, 1 << 8,
- mix->control_point_relative_time.den)));
- }
- break;
- }
- case AV_IAMF_PARAMETER_DEFINITION_DEMIXING: {
- const AVIAMFDemixingInfo *demix = subblock;
- if (!param_definition->mode && param->constant_subblock_duration == 0)
- ffio_write_leb(dyn_bc, demix->subblock_duration);
-
- avio_w8(dyn_bc, demix->dmixp_mode << 5);
- break;
- }
- case AV_IAMF_PARAMETER_DEFINITION_RECON_GAIN: {
- const AVIAMFReconGain *recon = subblock;
- const AVIAMFAudioElement *audio_element = param_definition->audio_element->element;
-
- if (!param_definition->mode && param->constant_subblock_duration == 0)
- ffio_write_leb(dyn_bc, recon->subblock_duration);
-
- if (!audio_element) {
- av_log(s, AV_LOG_ERROR, "Invalid Parameter Definition with ID %u referenced by a packet\n", param->parameter_id);
- return AVERROR(EINVAL);
- }
-
- for (int j = 0; j < audio_element->nb_layers; j++) {
- const AVIAMFLayer *layer = audio_element->layers[j];
-
- if (layer->flags & AV_IAMF_LAYER_FLAG_RECON_GAIN) {
- unsigned int recon_gain_flags = 0;
- int k = 0;
-
- for (; k < 7; k++)
- recon_gain_flags |= (1 << k) * !!recon->recon_gain[j][k];
- for (; k < 12; k++)
- recon_gain_flags |= (2 << k) * !!recon->recon_gain[j][k];
- if (recon_gain_flags >> 8)
- recon_gain_flags |= (1 << k);
-
- ffio_write_leb(dyn_bc, recon_gain_flags);
- for (k = 0; k < 12; k++) {
- if (recon->recon_gain[j][k])
- avio_w8(dyn_bc, recon->recon_gain[j][k]);
- }
- }
- }
- break;
- }
- default:
- av_assert0(0);
- }
- }
-
- dyn_size = avio_get_dyn_buf(dyn_bc, &dyn_buf);
- ffio_write_leb(s->pb, dyn_size);
- avio_write(s->pb, dyn_buf, dyn_size);
- ffio_free_dyn_buf(&dyn_bc);
-
- return 0;
-}
-
static int iamf_write_packet(AVFormatContext *s, AVPacket *pkt)
{
const IAMFMuxContext *const c = s->priv_data;
AVStream *st = s->streams[pkt->stream_index];
- uint8_t header[MAX_IAMF_OBU_HEADER_SIZE];
- PutBitContext pb;
- AVIOContext *dyn_bc;
- uint8_t *side_data, *dyn_buf = NULL;
- unsigned int skip_samples = 0, discard_padding = 0;
- size_t side_data_size;
- int dyn_size, type = st->id <= 17 ? st->id + IAMF_OBU_IA_AUDIO_FRAME_ID0 : IAMF_OBU_IA_AUDIO_FRAME;
- int ret;
+ int ret = 0;
- if (!pkt->size) {
- uint8_t *new_extradata = av_packet_get_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA, NULL);
-
- if (!new_extradata)
- return AVERROR_INVALIDDATA;
-
- // TODO: update FLAC Streaminfo on seekable output
- return 0;
- }
-
- if (s->nb_stream_groups && st->id == c->first_stream_id) {
- AVIAMFParamDefinition *mix =
- (AVIAMFParamDefinition *)av_packet_get_side_data(pkt, AV_PKT_DATA_IAMF_MIX_GAIN_PARAM, NULL);
- AVIAMFParamDefinition *demix =
- (AVIAMFParamDefinition *)av_packet_get_side_data(pkt, AV_PKT_DATA_IAMF_DEMIXING_INFO_PARAM, NULL);
- AVIAMFParamDefinition *recon =
- (AVIAMFParamDefinition *)av_packet_get_side_data(pkt, AV_PKT_DATA_IAMF_RECON_GAIN_INFO_PARAM, NULL);
-
- if (mix) {
- ret = write_parameter_block(s, mix);
- if (ret < 0)
- return ret;
- }
- if (demix) {
- ret = write_parameter_block(s, demix);
- if (ret < 0)
- return ret;
- }
- if (recon) {
- ret = write_parameter_block(s, recon);
- if (ret < 0)
- return ret;
- }
- }
- side_data = av_packet_get_side_data(pkt, AV_PKT_DATA_SKIP_SAMPLES,
- &side_data_size);
+ if (st->id == c->first_stream_id)
+ ret = ff_iamf_write_parameter_blocks(&c->iamf, s->pb, pkt, s);
+ if (!ret)
+ ret = ff_iamf_write_audio_frame(&c->iamf, s->pb, st->id, pkt);
- if (side_data && side_data_size >= 10) {
- skip_samples = AV_RL32(side_data);
- discard_padding = AV_RL32(side_data + 4);
- }
-
- ret = avio_open_dyn_buf(&dyn_bc);
- if (ret < 0)
- return ret;
-
- init_put_bits(&pb, header, sizeof(header));
- put_bits(&pb, 5, type);
- put_bits(&pb, 1, 0); // obu_redundant_copy
- put_bits(&pb, 1, skip_samples || discard_padding);
- put_bits(&pb, 1, 0); // obu_extension_flag
- flush_put_bits(&pb);
- avio_write(s->pb, header, put_bytes_count(&pb, 1));
-
- if (skip_samples || discard_padding) {
- ffio_write_leb(dyn_bc, discard_padding);
- ffio_write_leb(dyn_bc, skip_samples);
- }
-
- if (st->id > 17)
- ffio_write_leb(dyn_bc, st->id);
-
- dyn_size = avio_get_dyn_buf(dyn_bc, &dyn_buf);
- ffio_write_leb(s->pb, dyn_size + pkt->size);
- avio_write(s->pb, dyn_buf, dyn_size);
- ffio_free_dyn_buf(&dyn_bc);
- avio_write(s->pb, pkt->data, pkt->size);
-
- return 0;
+ return ret;
}
static void iamf_deinit(AVFormatContext *s)
{
IAMFMuxContext *const c = s->priv_data;
- IAMFContext *const iamf = &c->iamf;
-
- for (int i = 0; i < iamf->nb_audio_elements; i++) {
- IAMFAudioElement *audio_element = iamf->audio_elements[i];
- audio_element->element = NULL;
- }
-
- for (int i = 0; i < iamf->nb_mix_presentations; i++) {
- IAMFMixPresentation *mix_presentation = iamf->mix_presentations[i];
- mix_presentation->mix = NULL;
- }
-
- ff_iamf_uninit_context(iamf);
- return;
+ ff_iamf_write_deinit(&c->iamf);
}
static const AVCodecTag iamf_codec_tags[] = {
--
2.43.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] 13+ messages in thread
* [FFmpeg-devel] [PATCH 7/8] avformat/movenc: add support for Immersive Audio Model and Formats in ISOBMFF
2024-02-17 22:02 [FFmpeg-devel] [PATCH 1/8] avformat/demux: allow demuxers to output more than one packet per read_packet() call James Almer
` (4 preceding siblings ...)
2024-02-17 22:02 ` [FFmpeg-devel] [PATCH 6/8] avformat/iamfenc: further split into shareable modules James Almer
@ 2024-02-17 22:02 ` James Almer
2024-02-20 13:37 ` Andreas Rheinhardt
2024-02-17 22:02 ` [FFmpeg-devel] [PATCH 8/8] fate: add IAMF in mp4 tests James Almer
6 siblings, 1 reply; 13+ messages in thread
From: James Almer @ 2024-02-17 22:02 UTC (permalink / raw)
To: ffmpeg-devel
Signed-off-by: James Almer <jamrial@gmail.com>
---
libavformat/movenc.c | 349 +++++++++++++++++++++++++++++++++++--------
libavformat/movenc.h | 6 +
2 files changed, 293 insertions(+), 62 deletions(-)
diff --git a/libavformat/movenc.c b/libavformat/movenc.c
index c71a9983ed..cd63b353b8 100644
--- a/libavformat/movenc.c
+++ b/libavformat/movenc.c
@@ -32,6 +32,7 @@
#include "dovi_isom.h"
#include "riff.h"
#include "avio.h"
+#include "iamf_writer.h"
#include "isom.h"
#include "av1.h"
#include "avc.h"
@@ -41,12 +42,14 @@
#include "libavcodec/flac.h"
#include "libavcodec/get_bits.h"
+#include "libavcodec/bsf.h"
#include "libavcodec/internal.h"
#include "libavcodec/put_bits.h"
#include "libavcodec/vc1_common.h"
#include "libavcodec/raw.h"
#include "internal.h"
#include "libavutil/avstring.h"
+#include "libavutil/bprint.h"
#include "libavutil/channel_layout.h"
#include "libavutil/csp.h"
#include "libavutil/intfloat.h"
@@ -316,6 +319,32 @@ static int mov_write_sdtp_tag(AVIOContext *pb, MOVTrack *track)
return update_size(pb, pos);
}
+static int mov_write_iacb_tag(AVFormatContext *s, AVIOContext *pb, MOVTrack *track)
+{
+ AVIOContext *dyn_bc;
+ int64_t pos = avio_tell(pb);
+ uint8_t *dyn_buf = NULL;
+ int dyn_size;
+ int ret = avio_open_dyn_buf(&dyn_bc);
+ if (ret < 0)
+ return ret;
+
+ avio_wb32(pb, 0);
+ ffio_wfourcc(pb, "iacb");
+ avio_w8(pb, 1); // configurationVersion
+
+ ret = ff_iamf_write_descriptors(track->iamf, dyn_bc, s);
+ if (ret < 0)
+ return ret;
+
+ dyn_size = avio_close_dyn_buf(dyn_bc, &dyn_buf);
+ ffio_write_leb(pb, dyn_size);
+ avio_write(pb, dyn_buf, dyn_size);
+ av_free(dyn_buf);
+
+ return update_size(pb, pos);
+}
+
static int mov_write_amr_tag(AVIOContext *pb, MOVTrack *track)
{
avio_wb32(pb, 0x11); /* size */
@@ -1358,6 +1387,8 @@ static int mov_write_audio_tag(AVFormatContext *s, AVIOContext *pb, MOVMuxContex
ret = mov_write_wave_tag(s, pb, track);
else if (track->tag == MKTAG('m','p','4','a'))
ret = mov_write_esds_tag(pb, track);
+ else if (track->tag == MKTAG('i','a','m','f'))
+ ret = mov_write_iacb_tag(mov->fc, pb, track);
else if (track->par->codec_id == AV_CODEC_ID_AMR_NB)
ret = mov_write_amr_tag(pb, track);
else if (track->par->codec_id == AV_CODEC_ID_AC3)
@@ -2529,7 +2560,7 @@ static int mov_write_video_tag(AVFormatContext *s, AVIOContext *pb, MOVMuxContex
if (track->mode == MODE_AVIF) {
mov_write_ccst_tag(pb);
- if (s->nb_streams > 0 && track == &mov->tracks[1])
+ if (mov->nb_streams > 0 && track == &mov->tracks[1])
mov_write_aux_tag(pb, "auxi");
}
@@ -3124,9 +3155,9 @@ static int mov_write_iloc_tag(AVIOContext *pb, MOVMuxContext *mov, AVFormatConte
avio_wb32(pb, 0); /* Version & flags */
avio_w8(pb, (4 << 4) + 4); /* offset_size(4) and length_size(4) */
avio_w8(pb, 0); /* base_offset_size(4) and reserved(4) */
- avio_wb16(pb, s->nb_streams); /* item_count */
+ avio_wb16(pb, mov->nb_streams); /* item_count */
- for (int i = 0; i < s->nb_streams; i++) {
+ for (int i = 0; i < mov->nb_streams; i++) {
avio_wb16(pb, i + 1); /* item_id */
avio_wb16(pb, 0); /* data_reference_index */
avio_wb16(pb, 1); /* extent_count */
@@ -3145,9 +3176,9 @@ static int mov_write_iinf_tag(AVIOContext *pb, MOVMuxContext *mov, AVFormatConte
avio_wb32(pb, 0); /* size */
ffio_wfourcc(pb, "iinf");
avio_wb32(pb, 0); /* Version & flags */
- avio_wb16(pb, s->nb_streams); /* entry_count */
+ avio_wb16(pb, mov->nb_streams); /* entry_count */
- for (int i = 0; i < s->nb_streams; i++) {
+ for (int i = 0; i < mov->nb_streams; i++) {
int64_t infe_pos = avio_tell(pb);
avio_wb32(pb, 0); /* size */
ffio_wfourcc(pb, "infe");
@@ -3216,7 +3247,7 @@ static int mov_write_ipco_tag(AVIOContext *pb, MOVMuxContext *mov, AVFormatConte
int64_t pos = avio_tell(pb);
avio_wb32(pb, 0); /* size */
ffio_wfourcc(pb, "ipco");
- for (int i = 0; i < s->nb_streams; i++) {
+ for (int i = 0; i < mov->nb_streams; i++) {
mov_write_ispe_tag(pb, mov, s, i);
mov_write_pixi_tag(pb, mov, s, i);
mov_write_av1c_tag(pb, &mov->tracks[i]);
@@ -3234,9 +3265,9 @@ static int mov_write_ipma_tag(AVIOContext *pb, MOVMuxContext *mov, AVFormatConte
avio_wb32(pb, 0); /* size */
ffio_wfourcc(pb, "ipma");
avio_wb32(pb, 0); /* Version & flags */
- avio_wb32(pb, s->nb_streams); /* entry_count */
+ avio_wb32(pb, mov->nb_streams); /* entry_count */
- for (int i = 0, index = 1; i < s->nb_streams; i++) {
+ for (int i = 0, index = 1; i < mov->nb_streams; i++) {
avio_wb16(pb, i + 1); /* item_ID */
avio_w8(pb, 4); /* association_count */
@@ -4213,7 +4244,7 @@ static int mov_write_covr(AVIOContext *pb, AVFormatContext *s)
int64_t pos = 0;
int i;
- for (i = 0; i < s->nb_streams; i++) {
+ for (i = 0; i < mov->nb_streams; i++) {
MOVTrack *trk = &mov->tracks[i];
if (!is_cover_image(trk->st) || trk->cover_image->size <= 0)
@@ -4360,7 +4391,7 @@ static int mov_write_meta_tag(AVIOContext *pb, MOVMuxContext *mov,
mov_write_pitm_tag(pb, 1);
mov_write_iloc_tag(pb, mov, s);
mov_write_iinf_tag(pb, mov, s);
- if (s->nb_streams > 1)
+ if (mov->nb_streams > 1)
mov_write_iref_tag(pb, mov, s);
mov_write_iprp_tag(pb, mov, s);
} else {
@@ -4611,16 +4642,17 @@ static int mov_setup_track_ids(MOVMuxContext *mov, AVFormatContext *s)
if (mov->use_stream_ids_as_track_ids) {
int next_generated_track_id = 0;
- for (i = 0; i < s->nb_streams; i++) {
- if (s->streams[i]->id > next_generated_track_id)
- next_generated_track_id = s->streams[i]->id;
+ for (i = 0; i < mov->nb_streams; i++) {
+ AVStream *st = mov->tracks[i].st;
+ if (st->id > next_generated_track_id)
+ next_generated_track_id = st->id;
}
for (i = 0; i < mov->nb_tracks; i++) {
if (mov->tracks[i].entry <= 0 && !(mov->flags & FF_MOV_FLAG_FRAGMENT))
continue;
- mov->tracks[i].track_id = i >= s->nb_streams ? ++next_generated_track_id : s->streams[i]->id;
+ mov->tracks[i].track_id = i >= mov->nb_streams ? ++next_generated_track_id : mov->tracks[i].st->id;
}
} else {
for (i = 0; i < mov->nb_tracks; i++) {
@@ -4657,7 +4689,7 @@ static int mov_write_moov_tag(AVIOContext *pb, MOVMuxContext *mov,
}
if (mov->chapter_track)
- for (i = 0; i < s->nb_streams; i++) {
+ for (i = 0; i < mov->nb_streams; i++) {
mov->tracks[i].tref_tag = MKTAG('c','h','a','p');
mov->tracks[i].tref_id = mov->tracks[mov->chapter_track].track_id;
}
@@ -4697,7 +4729,7 @@ static int mov_write_moov_tag(AVIOContext *pb, MOVMuxContext *mov,
for (i = 0; i < mov->nb_tracks; i++) {
if (mov->tracks[i].entry > 0 || mov->flags & FF_MOV_FLAG_FRAGMENT ||
mov->mode == MODE_AVIF) {
- int ret = mov_write_trak_tag(s, pb, mov, &(mov->tracks[i]), i < s->nb_streams ? s->streams[i] : NULL);
+ int ret = mov_write_trak_tag(s, pb, mov, &(mov->tracks[i]), i < mov->nb_streams ? mov->tracks[i].st : NULL);
if (ret < 0)
return ret;
}
@@ -5489,10 +5521,20 @@ static int mov_write_ftyp_tag(AVIOContext *pb, AVFormatContext *s)
MOVMuxContext *mov = s->priv_data;
int64_t pos = avio_tell(pb);
int has_h264 = 0, has_av1 = 0, has_video = 0, has_dolby = 0;
+ int has_iamf = 0;
int i;
- for (i = 0; i < s->nb_streams; i++) {
- AVStream *st = s->streams[i];
+ for (i = 0; i < s->nb_stream_groups; i++) {
+ const AVStreamGroup *stg = s->stream_groups[i];
+
+ if (stg->type == AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT ||
+ stg->type == AV_STREAM_GROUP_PARAMS_IAMF_MIX_PRESENTATION) {
+ has_iamf = 1;
+ break;
+ }
+ }
+ for (i = 0; i < mov->nb_streams; i++) {
+ AVStream *st = mov->tracks[i].st;
if (is_cover_image(st))
continue;
if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
@@ -5560,6 +5602,8 @@ static int mov_write_ftyp_tag(AVIOContext *pb, AVFormatContext *s)
ffio_wfourcc(pb, "av01");
if (has_dolby)
ffio_wfourcc(pb, "dby1");
+ if (has_iamf)
+ ffio_wfourcc(pb, "iamf");
} else {
if (mov->flags & FF_MOV_FLAG_FRAGMENT)
ffio_wfourcc(pb, "iso6");
@@ -5667,8 +5711,8 @@ static int mov_write_identification(AVIOContext *pb, AVFormatContext *s)
mov_write_ftyp_tag(pb,s);
if (mov->mode == MODE_PSP) {
int video_streams_nb = 0, audio_streams_nb = 0, other_streams_nb = 0;
- for (i = 0; i < s->nb_streams; i++) {
- AVStream *st = s->streams[i];
+ for (i = 0; i < mov->nb_streams; i++) {
+ AVStream *st = mov->tracks[i].st;
if (is_cover_image(st))
continue;
if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
@@ -5855,7 +5899,7 @@ static int mov_write_squashed_packets(AVFormatContext *s)
{
MOVMuxContext *mov = s->priv_data;
- for (int i = 0; i < s->nb_streams; i++) {
+ for (int i = 0; i < mov->nb_streams; i++) {
MOVTrack *track = &mov->tracks[i];
int ret = AVERROR_BUG;
@@ -5896,7 +5940,7 @@ static int mov_flush_fragment(AVFormatContext *s, int force)
// of fragments was triggered automatically by an AVPacket, we
// already have reliable info for the end of that track, but other
// tracks may need to be filled in.
- for (i = 0; i < s->nb_streams; i++) {
+ for (i = 0; i < mov->nb_streams; i++) {
MOVTrack *track = &mov->tracks[i];
if (!track->end_reliable) {
const AVPacket *pkt = ff_interleaved_peek(s, i);
@@ -6097,10 +6141,8 @@ static int mov_auto_flush_fragment(AVFormatContext *s, int force)
return ret;
}
-static int check_pkt(AVFormatContext *s, AVPacket *pkt)
+static int check_pkt(AVFormatContext *s, MOVTrack *trk, AVPacket *pkt)
{
- MOVMuxContext *mov = s->priv_data;
- MOVTrack *trk = &mov->tracks[pkt->stream_index];
int64_t ref;
uint64_t duration;
@@ -6138,15 +6180,21 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt)
{
MOVMuxContext *mov = s->priv_data;
AVIOContext *pb = s->pb;
- MOVTrack *trk = &mov->tracks[pkt->stream_index];
- AVCodecParameters *par = trk->par;
+ MOVTrack *trk;
+ AVCodecParameters *par;
AVProducerReferenceTime *prft;
unsigned int samples_in_chunk = 0;
int size = pkt->size, ret = 0, offset = 0;
size_t prft_size;
uint8_t *reformatted_data = NULL;
- ret = check_pkt(s, pkt);
+ if (pkt->stream_index < s->nb_streams)
+ trk = s->streams[pkt->stream_index]->priv_data;
+ else // Timecode or chapter
+ trk = &mov->tracks[pkt->stream_index];
+ par = trk->par;
+
+ ret = check_pkt(s, trk, pkt);
if (ret < 0)
return ret;
@@ -6236,7 +6284,7 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt)
if (par->codec_id == AV_CODEC_ID_AAC && pkt->size > 2 &&
(AV_RB16(pkt->data) & 0xfff0) == 0xfff0) {
- if (!s->streams[pkt->stream_index]->nb_frames) {
+ if (!trk->st->nb_frames) {
av_log(s, AV_LOG_ERROR, "Malformed AAC bitstream detected: "
"use the audio bitstream filter 'aac_adtstoasc' to fix it "
"('-bsf:a aac_adtstoasc' option with ffmpeg)\n");
@@ -6498,18 +6546,18 @@ err:
static int mov_write_single_packet(AVFormatContext *s, AVPacket *pkt)
{
MOVMuxContext *mov = s->priv_data;
- MOVTrack *trk = &mov->tracks[pkt->stream_index];
+ MOVTrack *trk = s->streams[pkt->stream_index]->priv_data;
AVCodecParameters *par = trk->par;
int64_t frag_duration = 0;
int size = pkt->size;
- int ret = check_pkt(s, pkt);
+ int ret = check_pkt(s, trk, pkt);
if (ret < 0)
return ret;
if (mov->flags & FF_MOV_FLAG_FRAG_DISCONT) {
int i;
- for (i = 0; i < s->nb_streams; i++)
+ for (i = 0; i < mov->nb_streams; i++)
mov->tracks[i].frag_discont = 1;
mov->flags &= ~FF_MOV_FLAG_FRAG_DISCONT;
}
@@ -6551,7 +6599,7 @@ static int mov_write_single_packet(AVFormatContext *s, AVPacket *pkt)
return 0; /* Discard 0 sized packets */
}
- if (trk->entry && pkt->stream_index < s->nb_streams)
+ if (trk->entry && pkt->stream_index < mov->nb_streams)
frag_duration = av_rescale_q(pkt->dts - trk->cluster[0].dts,
s->streams[pkt->stream_index]->time_base,
AV_TIME_BASE_Q);
@@ -6606,17 +6654,80 @@ static int mov_write_subtitle_end_packet(AVFormatContext *s,
return ret;
}
+static int mov_build_iamf_packet(AVFormatContext *s, MOVTrack *trk, AVPacket *pkt)
+{
+ int ret;
+
+ if (pkt->stream_index == trk->first_iamf_idx) {
+ ret = ff_iamf_write_parameter_blocks(trk->iamf, trk->iamf_buf, pkt, s);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = ff_iamf_write_audio_frame(trk->iamf, trk->iamf_buf,
+ s->streams[pkt->stream_index]->id, pkt);
+ if (ret < 0)
+ return ret;
+
+ if (pkt->stream_index == trk->last_iamf_idx) {
+ uint8_t *data;
+
+ ret = avio_close_dyn_buf(trk->iamf_buf, &data);
+ trk->iamf_buf = NULL;
+
+ if (!ret) {
+ if (pkt->size) {
+ // Either all or none of the packets for a single
+ // IA Sample may be empty.
+ av_log(s, AV_LOG_ERROR, "Unexpected packet from "
+ "stream #%d\n", pkt->stream_index);
+ ret = AVERROR_INVALIDDATA;
+ }
+ av_free(data);
+ return ret;
+ }
+ av_buffer_unref(&pkt->buf);
+ pkt->buf = av_buffer_create(data, ret, NULL, NULL, 0);
+ if (!pkt->buf) {
+ av_free(data);
+ return AVERROR(ENOMEM);
+ }
+ pkt->data = data;
+ pkt->size = ret;
+ pkt->stream_index = trk->first_iamf_idx;
+
+ ret = avio_open_dyn_buf(&trk->iamf_buf);
+ if (ret < 0)
+ return ret;
+ } else
+ ret = AVERROR(EAGAIN);
+
+ return ret;
+}
+
static int mov_write_packet(AVFormatContext *s, AVPacket *pkt)
{
MOVMuxContext *mov = s->priv_data;
MOVTrack *trk;
+ int ret;
if (!pkt) {
mov_flush_fragment(s, 1);
return 1;
}
- trk = &mov->tracks[pkt->stream_index];
+ trk = s->streams[pkt->stream_index]->priv_data;
+
+ if (trk->iamf) {
+ ret = mov_build_iamf_packet(s, trk, pkt);
+ if (ret < 0) {
+ if (ret == AVERROR(EAGAIN))
+ return 0;
+ av_log(s, AV_LOG_ERROR, "Error assembling an IAMF packet "
+ "for stream #%d\n", trk->st->index);
+ return ret;
+ }
+ }
if (is_cover_image(trk->st)) {
int ret;
@@ -6817,12 +6928,12 @@ static int mov_create_chapter_track(AVFormatContext *s, int tracknum)
}
-static int mov_check_timecode_track(AVFormatContext *s, AVTimecode *tc, int src_index, const char *tcstr)
+static int mov_check_timecode_track(AVFormatContext *s, AVTimecode *tc, AVStream *src_st, const char *tcstr)
{
int ret;
/* compute the frame number */
- ret = av_timecode_init_from_string(tc, s->streams[src_index]->avg_frame_rate, tcstr, s);
+ ret = av_timecode_init_from_string(tc, src_st->avg_frame_rate, tcstr, s);
return ret;
}
@@ -6830,7 +6941,7 @@ static int mov_create_timecode_track(AVFormatContext *s, int index, int src_inde
{
MOVMuxContext *mov = s->priv_data;
MOVTrack *track = &mov->tracks[index];
- AVStream *src_st = s->streams[src_index];
+ AVStream *src_st = mov->tracks[src_index].st;
uint8_t data[4];
AVPacket *pkt = mov->pkt;
AVRational rate = src_st->avg_frame_rate;
@@ -6890,8 +7001,8 @@ static void enable_tracks(AVFormatContext *s)
first[i] = -1;
}
- for (i = 0; i < s->nb_streams; i++) {
- AVStream *st = s->streams[i];
+ for (i = 0; i < mov->nb_streams; i++) {
+ AVStream *st = mov->tracks[i].st;
if (st->codecpar->codec_type <= AVMEDIA_TYPE_UNKNOWN ||
st->codecpar->codec_type >= AVMEDIA_TYPE_NB ||
@@ -6925,6 +7036,9 @@ static void mov_free(AVFormatContext *s)
MOVMuxContext *mov = s->priv_data;
int i;
+ for (i = 0; i < s->nb_streams; i++)
+ s->streams[i]->priv_data = NULL;
+
if (!mov->tracks)
return;
@@ -6954,6 +7068,11 @@ static void mov_free(AVFormatContext *s)
ff_mov_cenc_free(&track->cenc);
ffio_free_dyn_buf(&track->mdat_buf);
+ ffio_free_dyn_buf(&track->iamf_buf);
+ if (track->iamf)
+ ff_iamf_write_deinit(track->iamf);
+ av_freep(&track->iamf);
+
avpriv_packet_list_free(&track->squashed_packet_queue);
}
@@ -7027,6 +7146,66 @@ static int mov_create_dvd_sub_decoder_specific_info(MOVTrack *track,
return 0;
}
+static int mov_init_iamf_track(AVFormatContext *s)
+{
+ MOVMuxContext *mov = s->priv_data;
+ MOVTrack *track = &mov->tracks[0]; // IAMF if present is always the first track
+ int nb_audio_elements = 0, nb_mix_presentations = 0;
+ int ret;
+
+ for (int i = 0; i < s->nb_stream_groups; i++) {
+ const AVStreamGroup *stg = s->stream_groups[i];
+
+ if (stg->type == AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT)
+ nb_audio_elements++;
+ if (stg->type == AV_STREAM_GROUP_PARAMS_IAMF_MIX_PRESENTATION)
+ nb_mix_presentations++;
+ }
+
+ if (!nb_audio_elements && !nb_mix_presentations)
+ return 0;
+
+ if ((nb_audio_elements < 1 && nb_audio_elements > 2) || nb_mix_presentations < 1) {
+ av_log(s, AV_LOG_ERROR, "There must be >= 1 and <= 2 IAMF_AUDIO_ELEMENT and at least "
+ "one IAMF_MIX_PRESENTATION stream groups to write a IMAF track\n");
+ return AVERROR(EINVAL);
+ }
+
+ track->iamf = av_mallocz(sizeof(*track->iamf));
+ if (!track->iamf)
+ return AVERROR(ENOMEM);
+
+ for (int i = 0; i < s->nb_stream_groups; i++) {
+ const AVStreamGroup *stg = s->stream_groups[i];
+ switch(stg->type) {
+ case AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT:
+ for (int j = 0; j < stg->nb_streams; j++) {
+ track->first_iamf_idx = FFMIN(stg->streams[j]->index, track->first_iamf_idx);
+ track->last_iamf_idx = FFMAX(stg->streams[j]->index, track->last_iamf_idx);
+ stg->streams[j]->priv_data = track;
+ }
+
+ ret = ff_iamf_add_audio_element(track->iamf, stg, s);
+ break;
+ case AV_STREAM_GROUP_PARAMS_IAMF_MIX_PRESENTATION:
+ ret = ff_iamf_add_mix_presentation(track->iamf, stg, s);
+ break;
+ default:
+ av_assert0(0);
+ }
+ if (ret < 0)
+ return ret;
+ }
+
+ track->tag = MKTAG('i','a','m','f');
+
+ ret = avio_open_dyn_buf(&track->iamf_buf);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
static int mov_init(AVFormatContext *s)
{
MOVMuxContext *mov = s->priv_data;
@@ -7164,7 +7343,37 @@ static int mov_init(AVFormatContext *s)
s->streams[0]->disposition |= AV_DISPOSITION_DEFAULT;
}
- mov->nb_tracks = s->nb_streams;
+ for (i = 0; i < s->nb_stream_groups; i++) {
+ AVStreamGroup *stg = s->stream_groups[i];
+
+ if (stg->type != AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT)
+ continue;
+
+ for (int j = 0; j < stg->nb_streams; j++) {
+ AVStream *st = stg->streams[j];
+
+ if (st->priv_data) {
+ av_log(s, AV_LOG_ERROR, "Stream %d is present in more than one Stream Group of type "
+ "IAMF Audio Element\n", j);
+ return AVERROR(EINVAL);
+ }
+ st->priv_data = st;
+ }
+
+ if (!mov->nb_tracks) // We support one track for the entire IAMF structure
+ mov->nb_tracks++;
+ }
+
+ for (i = 0; i < s->nb_streams; i++) {
+ AVStream *st = s->streams[i];
+ if (st->priv_data)
+ continue;
+ st->priv_data = st;
+ mov->nb_tracks++;
+ }
+
+ mov->nb_streams = mov->nb_tracks;
+
if (mov->mode & (MODE_MP4|MODE_MOV|MODE_IPOD) && s->nb_chapters)
mov->chapter_track = mov->nb_tracks++;
@@ -7190,7 +7399,7 @@ static int mov_init(AVFormatContext *s)
if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
(t || (t=av_dict_get(st->metadata, "timecode", NULL, 0)))) {
AVTimecode tc;
- ret = mov_check_timecode_track(s, &tc, i, t->value);
+ ret = mov_check_timecode_track(s, &tc, st, t->value);
if (ret >= 0)
mov->nb_meta_tmcd++;
}
@@ -7239,18 +7448,33 @@ static int mov_init(AVFormatContext *s)
}
}
+ ret = mov_init_iamf_track(s);
+ if (ret < 0)
+ return ret;
+
+ for (int j = 0, i = 0; j < s->nb_streams; j++) {
+ AVStream *st = s->streams[j];
+
+ if (st != st->priv_data)
+ continue;
+ st->priv_data = &mov->tracks[i++];
+ }
+
for (i = 0; i < s->nb_streams; i++) {
AVStream *st= s->streams[i];
- MOVTrack *track= &mov->tracks[i];
+ MOVTrack *track = st->priv_data;
AVDictionaryEntry *lang = av_dict_get(st->metadata, "language", NULL,0);
- track->st = st;
- track->par = st->codecpar;
+ if (!track->st) {
+ track->st = st;
+ track->par = st->codecpar;
+ }
track->language = ff_mov_iso639_to_lang(lang?lang->value:"und", mov->mode!=MODE_MOV);
if (track->language < 0)
track->language = 32767; // Unspecified Macintosh language code
track->mode = mov->mode;
- track->tag = mov_find_codec_tag(s, track);
+ if (!track->tag)
+ track->tag = mov_find_codec_tag(s, track);
if (!track->tag) {
av_log(s, AV_LOG_ERROR, "Could not find tag for codec %s in stream #%d, "
"codec not currently supported in container\n",
@@ -7442,25 +7666,26 @@ static int mov_write_header(AVFormatContext *s)
{
AVIOContext *pb = s->pb;
MOVMuxContext *mov = s->priv_data;
- int i, ret, hint_track = 0, tmcd_track = 0, nb_tracks = s->nb_streams;
+ int i, ret, hint_track = 0, tmcd_track = 0, nb_tracks = mov->nb_streams;
if (mov->mode & (MODE_MP4|MODE_MOV|MODE_IPOD) && s->nb_chapters)
nb_tracks++;
if (mov->flags & FF_MOV_FLAG_RTP_HINT) {
hint_track = nb_tracks;
- for (i = 0; i < s->nb_streams; i++)
- if (rtp_hinting_needed(s->streams[i]))
+ for (i = 0; i < mov->nb_streams; i++) {
+ if (rtp_hinting_needed(mov->tracks[i].st))
nb_tracks++;
+ }
}
if (mov->nb_meta_tmcd)
tmcd_track = nb_tracks;
- for (i = 0; i < s->nb_streams; i++) {
+ for (i = 0; i < mov->nb_streams; i++) {
int j;
- AVStream *st= s->streams[i];
- MOVTrack *track= &mov->tracks[i];
+ MOVTrack *track = &mov->tracks[i];
+ AVStream *st = track->st;
/* copy extradata if it exists */
if (st->codecpar->extradata_size) {
@@ -7482,8 +7707,8 @@ static int mov_write_header(AVFormatContext *s)
&(AVChannelLayout)AV_CHANNEL_LAYOUT_MONO))
continue;
- for (j = 0; j < s->nb_streams; j++) {
- AVStream *stj= s->streams[j];
+ for (j = 0; j < mov->nb_streams; j++) {
+ AVStream *stj= mov->tracks[j].st;
MOVTrack *trackj= &mov->tracks[j];
if (j == i)
continue;
@@ -7546,8 +7771,8 @@ static int mov_write_header(AVFormatContext *s)
return ret;
if (mov->flags & FF_MOV_FLAG_RTP_HINT) {
- for (i = 0; i < s->nb_streams; i++) {
- if (rtp_hinting_needed(s->streams[i])) {
+ for (i = 0; i < mov->nb_streams; i++) {
+ if (rtp_hinting_needed(mov->tracks[i].st)) {
if ((ret = ff_mov_init_hinting(s, hint_track, i)) < 0)
return ret;
hint_track++;
@@ -7559,8 +7784,8 @@ static int mov_write_header(AVFormatContext *s)
const AVDictionaryEntry *t, *global_tcr = av_dict_get(s->metadata,
"timecode", NULL, 0);
/* Initialize the tmcd tracks */
- for (i = 0; i < s->nb_streams; i++) {
- AVStream *st = s->streams[i];
+ for (i = 0; i < mov->nb_streams; i++) {
+ AVStream *st = mov->tracks[i].st;
t = global_tcr;
if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
@@ -7569,7 +7794,7 @@ static int mov_write_header(AVFormatContext *s)
t = av_dict_get(st->metadata, "timecode", NULL, 0);
if (!t)
continue;
- if (mov_check_timecode_track(s, &tc, i, t->value) < 0)
+ if (mov_check_timecode_track(s, &tc, st, t->value) < 0)
continue;
if ((ret = mov_create_timecode_track(s, tmcd_track, i, tc)) < 0)
return ret;
@@ -7690,7 +7915,7 @@ static int mov_write_trailer(AVFormatContext *s)
int64_t moov_pos;
if (mov->need_rewrite_extradata) {
- for (i = 0; i < s->nb_streams; i++) {
+ for (i = 0; i < mov->nb_streams; i++) {
MOVTrack *track = &mov->tracks[i];
AVCodecParameters *par = track->par;
@@ -7830,7 +8055,7 @@ static int avif_write_trailer(AVFormatContext *s)
if (mov->moov_written) return 0;
mov->is_animated_avif = s->streams[0]->nb_frames > 1;
- if (mov->is_animated_avif && s->nb_streams > 1) {
+ if (mov->is_animated_avif && mov->nb_streams > 1) {
// For animated avif with alpha channel, we need to write a tref tag
// with type "auxl".
mov->tracks[1].tref_tag = MKTAG('a', 'u', 'x', 'l');
@@ -7840,7 +8065,7 @@ static int avif_write_trailer(AVFormatContext *s)
mov_write_meta_tag(pb, mov, s);
moov_size = get_moov_size(s);
- for (i = 0; i < s->nb_streams; i++)
+ for (i = 0; i < mov->nb_tracks; i++)
mov->tracks[i].data_offset = avio_tell(pb) + moov_size + 8;
if (mov->is_animated_avif) {
@@ -7862,7 +8087,7 @@ static int avif_write_trailer(AVFormatContext *s)
// write extent offsets.
pos_backup = avio_tell(pb);
- for (i = 0; i < s->nb_streams; i++) {
+ for (i = 0; i < mov->nb_streams; i++) {
if (extent_offsets[i] != (uint32_t)extent_offsets[i]) {
av_log(s, AV_LOG_ERROR, "extent offset does not fit in 32 bits\n");
return AVERROR_INVALIDDATA;
diff --git a/libavformat/movenc.h b/libavformat/movenc.h
index 60363198c9..08d580594d 100644
--- a/libavformat/movenc.h
+++ b/libavformat/movenc.h
@@ -170,6 +170,11 @@ typedef struct MOVTrack {
unsigned int squash_fragment_samples_to_one; //< flag to note formats where all samples for a fragment are to be squashed
PacketList squashed_packet_queue;
+
+ struct IAMFContext *iamf;
+ int first_iamf_idx;
+ int last_iamf_idx;
+ AVIOContext *iamf_buf;
} MOVTrack;
typedef enum {
@@ -188,6 +193,7 @@ typedef struct MOVMuxContext {
const AVClass *av_class;
int mode;
int64_t time;
+ int nb_streams;
int nb_tracks;
int nb_meta_tmcd; ///< number of new created tmcd track based on metadata (aka not data copy)
int chapter_track; ///< qt chapter track number
--
2.43.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] 13+ messages in thread
* [FFmpeg-devel] [PATCH 8/8] fate: add IAMF in mp4 tests
2024-02-17 22:02 [FFmpeg-devel] [PATCH 1/8] avformat/demux: allow demuxers to output more than one packet per read_packet() call James Almer
` (5 preceding siblings ...)
2024-02-17 22:02 ` [FFmpeg-devel] [PATCH 7/8] avformat/movenc: add support for Immersive Audio Model and Formats in ISOBMFF James Almer
@ 2024-02-17 22:02 ` James Almer
2024-02-20 13:29 ` James Almer
6 siblings, 1 reply; 13+ messages in thread
From: James Almer @ 2024-02-17 22:02 UTC (permalink / raw)
To: ffmpeg-devel
Signed-off-by: James Almer <jamrial@gmail.com>
---
tests/fate/mov.mak | 35 ++++++++
tests/ref/fate/mov-mp4-iamf-5_1_4 | 98 ++++++++++++++++++++
tests/ref/fate/mov-mp4-iamf-7_1_4 | 114 ++++++++++++++++++++++++
tests/ref/fate/mov-mp4-iamf-ambisonic_1 | 66 ++++++++++++++
tests/ref/fate/mov-mp4-iamf-stereo | 18 ++++
5 files changed, 331 insertions(+)
create mode 100644 tests/ref/fate/mov-mp4-iamf-5_1_4
create mode 100644 tests/ref/fate/mov-mp4-iamf-7_1_4
create mode 100644 tests/ref/fate/mov-mp4-iamf-ambisonic_1
create mode 100644 tests/ref/fate/mov-mp4-iamf-stereo
diff --git a/tests/fate/mov.mak b/tests/fate/mov.mak
index 4850c8aa94..17fd99d8d2 100644
--- a/tests/fate/mov.mak
+++ b/tests/fate/mov.mak
@@ -194,6 +194,41 @@ fate-mov-pcm-remux: CMD = md5 -i $(TARGET_PATH)/tests/data/asynth-44100-1.wav -m
fate-mov-pcm-remux: CMP = oneline
fate-mov-pcm-remux: REF = e76115bc392d702da38f523216bba165
+FATE_MOV_FFMPEG-$(call TRANSCODE, FLAC, MOV, WAV_DEMUXER PCM_S16LE_DECODER) += fate-mov-mp4-iamf-stereo
+fate-mov-mp4-iamf-stereo: tests/data/asynth-44100-2.wav tests/data/streamgroups/audio_element-stereo tests/data/streamgroups/mix_presentation-stereo
+fate-mov-mp4-iamf-stereo: SRC = $(TARGET_PATH)/tests/data/asynth-44100-2.wav
+fate-mov-mp4-iamf-stereo: CMD = transcode wav $(SRC) mp4 " \
+ -/stream_group $(TARGET_PATH)/tests/data/streamgroups/audio_element-stereo \
+ -/stream_group $(TARGET_PATH)/tests/data/streamgroups/mix_presentation-stereo \
+ -streamid 0:0 -c:a flac -t 1" "-c:a copy -map 0"
+
+FATE_MOV_FFMPEG-$(call TRANSCODE, FLAC, MOV, WAV_DEMUXER PCM_S16LE_DECODER) += fate-mov-mp4-iamf-5_1_4
+fate-mov-mp4-iamf-5_1_4: tests/data/asynth-44100-10.wav tests/data/filtergraphs/iamf_5_1_4 tests/data/streamgroups/audio_element-5_1_4 tests/data/streamgroups/mix_presentation-5_1_4
+fate-mov-mp4-iamf-5_1_4: SRC = $(TARGET_PATH)/tests/data/asynth-44100-10.wav
+fate-mov-mp4-iamf-5_1_4: CMD = transcode wav $(SRC) mp4 "-auto_conversion_filters \
+ -/filter_complex $(TARGET_PATH)/tests/data/filtergraphs/iamf_5_1_4 \
+ -/stream_group $(TARGET_PATH)/tests/data/streamgroups/audio_element-5_1_4 \
+ -/stream_group $(TARGET_PATH)/tests/data/streamgroups/mix_presentation-5_1_4 \
+ -streamid 0:0 -streamid 1:1 -streamid 2:2 -streamid 3:3 -streamid 4:4 -streamid 5:5 -map [FRONT] -map [BACK] -map [CENTER] -map [LFE] -map [TOP_FRONT] -map [TOP_BACK] -c:a flac -t 1" "-c:a copy -map 0"
+
+FATE_MOV_FFMPEG-$(call TRANSCODE, FLAC, MOV, WAV_DEMUXER PCM_S16LE_DECODER) += fate-mov-mp4-iamf-7_1_4
+fate-mov-mp4-iamf-7_1_4: tests/data/asynth-44100-12.wav tests/data/filtergraphs/iamf_7_1_4 tests/data/streamgroups/audio_element-7_1_4 tests/data/streamgroups/mix_presentation-7_1_4
+fate-mov-mp4-iamf-7_1_4: SRC = $(TARGET_PATH)/tests/data/asynth-44100-12.wav
+fate-mov-mp4-iamf-7_1_4: CMD = transcode wav $(SRC) mp4 "-auto_conversion_filters \
+ -/filter_complex $(TARGET_PATH)/tests/data/filtergraphs/iamf_7_1_4 \
+ -/stream_group $(TARGET_PATH)/tests/data/streamgroups/audio_element-7_1_4 \
+ -/stream_group $(TARGET_PATH)/tests/data/streamgroups/mix_presentation-7_1_4 \
+ -streamid 0:0 -streamid 1:1 -streamid 2:2 -streamid 3:3 -streamid 4:4 -streamid 5:5 -streamid 6:6 -map [FRONT] -map [BACK] -map [CENTER] -map [LFE] -map [SIDE] -map [TOP_FRONT] -map [TOP_BACK] -c:a flac -t 1" "-c:a copy -map 0"
+
+FATE_MOV_FFMPEG-$(call TRANSCODE, FLAC, MOV, WAV_DEMUXER PCM_S16LE_DECODER) += fate-mov-mp4-iamf-ambisonic_1
+fate-mov-mp4-iamf-ambisonic_1: tests/data/asynth-44100-4.wav tests/data/filtergraphs/iamf_ambisonic_1 tests/data/streamgroups/audio_element-ambisonic_1 tests/data/streamgroups/mix_presentation-ambisonic_1
+fate-mov-mp4-iamf-ambisonic_1: SRC = $(TARGET_PATH)/tests/data/asynth-44100-4.wav
+fate-mov-mp4-iamf-ambisonic_1: CMD = transcode wav $(SRC) mp4 "-auto_conversion_filters \
+ -/filter_complex $(TARGET_PATH)/tests/data/filtergraphs/iamf_ambisonic_1 \
+ -/stream_group $(TARGET_PATH)/tests/data/streamgroups/audio_element-ambisonic_1 \
+ -/stream_group $(TARGET_PATH)/tests/data/streamgroups/mix_presentation-ambisonic_1 \
+ -streamid 0:0 -streamid 1:1 -streamid 2:2 -streamid 3:3 -map [MONO0] -map [MONO1] -map [MONO2] -map [MONO3] -c:a flac -t 1" "-c:a copy -map 0"
+
FATE_FFMPEG += $(FATE_MOV_FFMPEG-yes)
fate-mov: $(FATE_MOV) $(FATE_MOV_FFMPEG-yes) $(FATE_MOV_FFPROBE) $(FATE_MOV_FASTSTART) $(FATE_MOV_FFMPEG_FFPROBE-yes)
diff --git a/tests/ref/fate/mov-mp4-iamf-5_1_4 b/tests/ref/fate/mov-mp4-iamf-5_1_4
new file mode 100644
index 0000000000..2f29a83cf4
--- /dev/null
+++ b/tests/ref/fate/mov-mp4-iamf-5_1_4
@@ -0,0 +1,98 @@
+65421714a3fd372d2babc9c6400ff00b *tests/data/fate/mov-mp4-iamf-5_1_4.mp4
+86340 tests/data/fate/mov-mp4-iamf-5_1_4.mp4
+#extradata 0: 34, 0x40a802c6
+#extradata 1: 34, 0x40a802c6
+#extradata 2: 34, 0x407c02c4
+#extradata 3: 34, 0x407c02c4
+#extradata 4: 34, 0x40a802c6
+#extradata 5: 34, 0x40a802c6
+#tb 0: 1/44100
+#media_type 0: audio
+#codec_id 0: flac
+#sample_rate 0: 44100
+#channel_layout_name 0: stereo
+#tb 1: 1/44100
+#media_type 1: audio
+#codec_id 1: flac
+#sample_rate 1: 44100
+#channel_layout_name 1: stereo
+#tb 2: 1/44100
+#media_type 2: audio
+#codec_id 2: flac
+#sample_rate 2: 44100
+#channel_layout_name 2: mono
+#tb 3: 1/44100
+#media_type 3: audio
+#codec_id 3: flac
+#sample_rate 3: 44100
+#channel_layout_name 3: mono
+#tb 4: 1/44100
+#media_type 4: audio
+#codec_id 4: flac
+#sample_rate 4: 44100
+#channel_layout_name 4: stereo
+#tb 5: 1/44100
+#media_type 5: audio
+#codec_id 5: flac
+#sample_rate 5: 44100
+#channel_layout_name 5: stereo
+0, 0, 0, 4608, 1399, 0x6e89566e
+1, 0, 0, 4608, 1399, 0x6e89566e
+2, 0, 0, 4608, 1396, 0x0dcb5677
+3, 0, 0, 4608, 1396, 0x0dcb5677
+4, 0, 0, 4608, 1399, 0x6e89566e
+5, 0, 0, 4608, 1399, 0x6e89566e
+0, 4608, 4608, 4608, 1442, 0x6c3c5b13
+1, 4608, 4608, 4608, 1442, 0x6c3c5b13
+2, 4608, 4608, 4608, 1439, 0xc46b5ac5
+3, 4608, 4608, 4608, 1439, 0xc46b5ac5
+4, 4608, 4608, 4608, 1442, 0x6c3c5b13
+5, 4608, 4608, 4608, 1442, 0x6c3c5b13
+0, 9216, 9216, 4608, 1380, 0xc497571b
+1, 9216, 9216, 4608, 1380, 0xc497571b
+2, 9216, 9216, 4608, 1377, 0x5b2a55fe
+3, 9216, 9216, 4608, 1377, 0x5b2a55fe
+4, 9216, 9216, 4608, 1380, 0xc497571b
+5, 9216, 9216, 4608, 1380, 0xc497571b
+0, 13824, 13824, 4608, 1383, 0x48e9510f
+1, 13824, 13824, 4608, 1383, 0x48e9510f
+2, 13824, 13824, 4608, 1380, 0x045550d3
+3, 13824, 13824, 4608, 1380, 0x045550d3
+4, 13824, 13824, 4608, 1383, 0x48e9510f
+5, 13824, 13824, 4608, 1383, 0x48e9510f
+0, 18432, 18432, 4608, 1572, 0x9a514719
+1, 18432, 18432, 4608, 1572, 0x9a514719
+2, 18432, 18432, 4608, 1568, 0xa2bc45f4
+3, 18432, 18432, 4608, 1568, 0xa2bc45f4
+4, 18432, 18432, 4608, 1572, 0x9a514719
+5, 18432, 18432, 4608, 1572, 0x9a514719
+0, 23040, 23040, 4608, 1391, 0x74ac5014
+1, 23040, 23040, 4608, 1391, 0x74ac5014
+2, 23040, 23040, 4608, 1388, 0x96c85007
+3, 23040, 23040, 4608, 1388, 0x96c85007
+4, 23040, 23040, 4608, 1391, 0x74ac5014
+5, 23040, 23040, 4608, 1391, 0x74ac5014
+0, 27648, 27648, 4608, 1422, 0x2f9d47c5
+1, 27648, 27648, 4608, 1422, 0x2f9d47c5
+2, 27648, 27648, 4608, 1419, 0x4d4d466a
+3, 27648, 27648, 4608, 1419, 0x4d4d466a
+4, 27648, 27648, 4608, 1422, 0x2f9d47c5
+5, 27648, 27648, 4608, 1422, 0x2f9d47c5
+0, 32256, 32256, 4608, 1768, 0x2a044b99
+1, 32256, 32256, 4608, 1768, 0x2a044b99
+2, 32256, 32256, 4608, 1765, 0xacb84b24
+3, 32256, 32256, 4608, 1765, 0xacb84b24
+4, 32256, 32256, 4608, 1768, 0x2a044b99
+5, 32256, 32256, 4608, 1768, 0x2a044b99
+0, 36864, 36864, 4608, 1534, 0xb0b35a3f
+1, 36864, 36864, 4608, 1534, 0xb0b35a3f
+2, 36864, 36864, 4608, 1531, 0x996458aa
+3, 36864, 36864, 4608, 1531, 0x996458aa
+4, 36864, 36864, 4608, 1534, 0xb0b35a3f
+5, 36864, 36864, 4608, 1534, 0xb0b35a3f
+0, 41472, 41472, 2628, 926, 0xc26a5eae
+1, 41472, 41472, 2628, 926, 0xc26a5eae
+2, 41472, 41472, 2628, 923, 0xa7225edf
+3, 41472, 41472, 2628, 923, 0xa7225edf
+4, 41472, 41472, 2628, 926, 0xc26a5eae
+5, 41472, 41472, 2628, 926, 0xc26a5eae
diff --git a/tests/ref/fate/mov-mp4-iamf-7_1_4 b/tests/ref/fate/mov-mp4-iamf-7_1_4
new file mode 100644
index 0000000000..891803470a
--- /dev/null
+++ b/tests/ref/fate/mov-mp4-iamf-7_1_4
@@ -0,0 +1,114 @@
+9a0cd5cc7ca09da36f08c6ae2aa44a73 *tests/data/fate/mov-mp4-iamf-7_1_4.mp4
+100588 tests/data/fate/mov-mp4-iamf-7_1_4.mp4
+#extradata 0: 34, 0x40a802c6
+#extradata 1: 34, 0x40a802c6
+#extradata 2: 34, 0x407c02c4
+#extradata 3: 34, 0x407c02c4
+#extradata 4: 34, 0x40a802c6
+#extradata 5: 34, 0x40a802c6
+#extradata 6: 34, 0x40a802c6
+#tb 0: 1/44100
+#media_type 0: audio
+#codec_id 0: flac
+#sample_rate 0: 44100
+#channel_layout_name 0: stereo
+#tb 1: 1/44100
+#media_type 1: audio
+#codec_id 1: flac
+#sample_rate 1: 44100
+#channel_layout_name 1: stereo
+#tb 2: 1/44100
+#media_type 2: audio
+#codec_id 2: flac
+#sample_rate 2: 44100
+#channel_layout_name 2: mono
+#tb 3: 1/44100
+#media_type 3: audio
+#codec_id 3: flac
+#sample_rate 3: 44100
+#channel_layout_name 3: mono
+#tb 4: 1/44100
+#media_type 4: audio
+#codec_id 4: flac
+#sample_rate 4: 44100
+#channel_layout_name 4: stereo
+#tb 5: 1/44100
+#media_type 5: audio
+#codec_id 5: flac
+#sample_rate 5: 44100
+#channel_layout_name 5: stereo
+#tb 6: 1/44100
+#media_type 6: audio
+#codec_id 6: flac
+#sample_rate 6: 44100
+#channel_layout_name 6: stereo
+0, 0, 0, 4608, 1399, 0x6e89566e
+1, 0, 0, 4608, 1399, 0x6e89566e
+2, 0, 0, 4608, 1396, 0x0dcb5677
+3, 0, 0, 4608, 1396, 0x0dcb5677
+4, 0, 0, 4608, 1399, 0x6e89566e
+5, 0, 0, 4608, 1399, 0x6e89566e
+6, 0, 0, 4608, 1399, 0x6e89566e
+0, 4608, 4608, 4608, 1442, 0x6c3c5b13
+1, 4608, 4608, 4608, 1442, 0x6c3c5b13
+2, 4608, 4608, 4608, 1439, 0xc46b5ac5
+3, 4608, 4608, 4608, 1439, 0xc46b5ac5
+4, 4608, 4608, 4608, 1442, 0x6c3c5b13
+5, 4608, 4608, 4608, 1442, 0x6c3c5b13
+6, 4608, 4608, 4608, 1442, 0x6c3c5b13
+0, 9216, 9216, 4608, 1380, 0xc497571b
+1, 9216, 9216, 4608, 1380, 0xc497571b
+2, 9216, 9216, 4608, 1377, 0x5b2a55fe
+3, 9216, 9216, 4608, 1377, 0x5b2a55fe
+4, 9216, 9216, 4608, 1380, 0xc497571b
+5, 9216, 9216, 4608, 1380, 0xc497571b
+6, 9216, 9216, 4608, 1380, 0xc497571b
+0, 13824, 13824, 4608, 1383, 0x48e9510f
+1, 13824, 13824, 4608, 1383, 0x48e9510f
+2, 13824, 13824, 4608, 1380, 0x045550d3
+3, 13824, 13824, 4608, 1380, 0x045550d3
+4, 13824, 13824, 4608, 1383, 0x48e9510f
+5, 13824, 13824, 4608, 1383, 0x48e9510f
+6, 13824, 13824, 4608, 1383, 0x48e9510f
+0, 18432, 18432, 4608, 1572, 0x9a514719
+1, 18432, 18432, 4608, 1572, 0x9a514719
+2, 18432, 18432, 4608, 1568, 0xa2bc45f4
+3, 18432, 18432, 4608, 1568, 0xa2bc45f4
+4, 18432, 18432, 4608, 1572, 0x9a514719
+5, 18432, 18432, 4608, 1572, 0x9a514719
+6, 18432, 18432, 4608, 1572, 0x9a514719
+0, 23040, 23040, 4608, 1391, 0x74ac5014
+1, 23040, 23040, 4608, 1391, 0x74ac5014
+2, 23040, 23040, 4608, 1388, 0x96c85007
+3, 23040, 23040, 4608, 1388, 0x96c85007
+4, 23040, 23040, 4608, 1391, 0x74ac5014
+5, 23040, 23040, 4608, 1391, 0x74ac5014
+6, 23040, 23040, 4608, 1391, 0x74ac5014
+0, 27648, 27648, 4608, 1422, 0x2f9d47c5
+1, 27648, 27648, 4608, 1422, 0x2f9d47c5
+2, 27648, 27648, 4608, 1419, 0x4d4d466a
+3, 27648, 27648, 4608, 1419, 0x4d4d466a
+4, 27648, 27648, 4608, 1422, 0x2f9d47c5
+5, 27648, 27648, 4608, 1422, 0x2f9d47c5
+6, 27648, 27648, 4608, 1422, 0x2f9d47c5
+0, 32256, 32256, 4608, 1768, 0x2a044b99
+1, 32256, 32256, 4608, 1768, 0x2a044b99
+2, 32256, 32256, 4608, 1765, 0xacb84b24
+3, 32256, 32256, 4608, 1765, 0xacb84b24
+4, 32256, 32256, 4608, 1768, 0x2a044b99
+5, 32256, 32256, 4608, 1768, 0x2a044b99
+6, 32256, 32256, 4608, 1768, 0x2a044b99
+0, 36864, 36864, 4608, 1534, 0xb0b35a3f
+1, 36864, 36864, 4608, 1534, 0xb0b35a3f
+2, 36864, 36864, 4608, 1531, 0x996458aa
+3, 36864, 36864, 4608, 1531, 0x996458aa
+4, 36864, 36864, 4608, 1534, 0xb0b35a3f
+5, 36864, 36864, 4608, 1534, 0xb0b35a3f
+6, 36864, 36864, 4608, 1534, 0xb0b35a3f
+0, 41472, 41472, 2628, 926, 0xc26a5eae
+1, 41472, 41472, 2628, 926, 0xc26a5eae
+2, 41472, 41472, 2628, 923, 0xa7225edf
+3, 41472, 41472, 2628, 923, 0xa7225edf
+4, 41472, 41472, 2628, 926, 0xc26a5eae
+5, 41472, 41472, 2628, 926, 0xc26a5eae
+6, 41472, 41472, 2628, 926, 0xc26a5eae
diff --git a/tests/ref/fate/mov-mp4-iamf-ambisonic_1 b/tests/ref/fate/mov-mp4-iamf-ambisonic_1
new file mode 100644
index 0000000000..c4af88ce10
--- /dev/null
+++ b/tests/ref/fate/mov-mp4-iamf-ambisonic_1
@@ -0,0 +1,66 @@
+fa740a4e2b84453c4e84908190094e28 *tests/data/fate/mov-mp4-iamf-ambisonic_1.mp4
+57743 tests/data/fate/mov-mp4-iamf-ambisonic_1.mp4
+#extradata 0: 34, 0x3615025b
+#extradata 1: 34, 0x3615025b
+#extradata 2: 34, 0x3615025b
+#extradata 3: 34, 0x3615025b
+#tb 0: 1/44100
+#media_type 0: audio
+#codec_id 0: flac
+#sample_rate 0: 44100
+#channel_layout_name 0: mono
+#tb 1: 1/44100
+#media_type 1: audio
+#codec_id 1: flac
+#sample_rate 1: 44100
+#channel_layout_name 1: mono
+#tb 2: 1/44100
+#media_type 2: audio
+#codec_id 2: flac
+#sample_rate 2: 44100
+#channel_layout_name 2: mono
+#tb 3: 1/44100
+#media_type 3: audio
+#codec_id 3: flac
+#sample_rate 3: 44100
+#channel_layout_name 3: mono
+0, 0, 0, 4608, 1396, 0x0dcb5677
+1, 0, 0, 4608, 1396, 0x0dcb5677
+2, 0, 0, 4608, 1396, 0x0dcb5677
+3, 0, 0, 4608, 1396, 0x0dcb5677
+0, 4608, 4608, 4608, 1439, 0xc46b5ac5
+1, 4608, 4608, 4608, 1439, 0xc46b5ac5
+2, 4608, 4608, 4608, 1439, 0xc46b5ac5
+3, 4608, 4608, 4608, 1439, 0xc46b5ac5
+0, 9216, 9216, 4608, 1377, 0x5b2a55fe
+1, 9216, 9216, 4608, 1377, 0x5b2a55fe
+2, 9216, 9216, 4608, 1377, 0x5b2a55fe
+3, 9216, 9216, 4608, 1377, 0x5b2a55fe
+0, 13824, 13824, 4608, 1380, 0x045550d3
+1, 13824, 13824, 4608, 1380, 0x045550d3
+2, 13824, 13824, 4608, 1380, 0x045550d3
+3, 13824, 13824, 4608, 1380, 0x045550d3
+0, 18432, 18432, 4608, 1568, 0xa2bc45f4
+1, 18432, 18432, 4608, 1568, 0xa2bc45f4
+2, 18432, 18432, 4608, 1568, 0xa2bc45f4
+3, 18432, 18432, 4608, 1568, 0xa2bc45f4
+0, 23040, 23040, 4608, 1388, 0x96c85007
+1, 23040, 23040, 4608, 1388, 0x96c85007
+2, 23040, 23040, 4608, 1388, 0x96c85007
+3, 23040, 23040, 4608, 1388, 0x96c85007
+0, 27648, 27648, 4608, 1419, 0x4d4d466a
+1, 27648, 27648, 4608, 1419, 0x4d4d466a
+2, 27648, 27648, 4608, 1419, 0x4d4d466a
+3, 27648, 27648, 4608, 1419, 0x4d4d466a
+0, 32256, 32256, 4608, 1765, 0xacb84b24
+1, 32256, 32256, 4608, 1765, 0xacb84b24
+2, 32256, 32256, 4608, 1765, 0xacb84b24
+3, 32256, 32256, 4608, 1765, 0xacb84b24
+0, 36864, 36864, 4608, 1531, 0x996458aa
+1, 36864, 36864, 4608, 1531, 0x996458aa
+2, 36864, 36864, 4608, 1531, 0x996458aa
+3, 36864, 36864, 4608, 1531, 0x996458aa
+0, 41472, 41472, 2628, 923, 0xa7225edf
+1, 41472, 41472, 2628, 923, 0xa7225edf
+2, 41472, 41472, 2628, 923, 0xa7225edf
+3, 41472, 41472, 2628, 923, 0xa7225edf
diff --git a/tests/ref/fate/mov-mp4-iamf-stereo b/tests/ref/fate/mov-mp4-iamf-stereo
new file mode 100644
index 0000000000..5c66c3e188
--- /dev/null
+++ b/tests/ref/fate/mov-mp4-iamf-stereo
@@ -0,0 +1,18 @@
+7df5cc61ee480e558389051a83040dd2 *tests/data/fate/mov-mp4-iamf-stereo.mp4
+15163 tests/data/fate/mov-mp4-iamf-stereo.mp4
+#extradata 0: 34, 0x40a802c6
+#tb 0: 1/44100
+#media_type 0: audio
+#codec_id 0: flac
+#sample_rate 0: 44100
+#channel_layout_name 0: stereo
+0, 0, 0, 4608, 1399, 0x6e89566e
+0, 4608, 4608, 4608, 1442, 0x6c3c5b13
+0, 9216, 9216, 4608, 1380, 0xc497571b
+0, 13824, 13824, 4608, 1383, 0x48e9510f
+0, 18432, 18432, 4608, 1572, 0x9a514719
+0, 23040, 23040, 4608, 1391, 0x74ac5014
+0, 27648, 27648, 4608, 1422, 0x2f9d47c5
+0, 32256, 32256, 4608, 1768, 0x2a044b99
+0, 36864, 36864, 4608, 1534, 0xb0b35a3f
+0, 41472, 41472, 2628, 926, 0xc26a5eae
--
2.43.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] 13+ messages in thread
* Re: [FFmpeg-devel] [PATCH 8/8] fate: add IAMF in mp4 tests
2024-02-17 22:02 ` [FFmpeg-devel] [PATCH 8/8] fate: add IAMF in mp4 tests James Almer
@ 2024-02-20 13:29 ` James Almer
0 siblings, 0 replies; 13+ messages in thread
From: James Almer @ 2024-02-20 13:29 UTC (permalink / raw)
To: ffmpeg-devel
On 2/17/2024 7:02 PM, James Almer wrote:
> Signed-off-by: James Almer <jamrial@gmail.com>
> ---
> tests/fate/mov.mak | 35 ++++++++
> tests/ref/fate/mov-mp4-iamf-5_1_4 | 98 ++++++++++++++++++++
> tests/ref/fate/mov-mp4-iamf-7_1_4 | 114 ++++++++++++++++++++++++
> tests/ref/fate/mov-mp4-iamf-ambisonic_1 | 66 ++++++++++++++
> tests/ref/fate/mov-mp4-iamf-stereo | 18 ++++
> 5 files changed, 331 insertions(+)
> create mode 100644 tests/ref/fate/mov-mp4-iamf-5_1_4
> create mode 100644 tests/ref/fate/mov-mp4-iamf-7_1_4
> create mode 100644 tests/ref/fate/mov-mp4-iamf-ambisonic_1
> create mode 100644 tests/ref/fate/mov-mp4-iamf-stereo
Applied patches 2 and 6 last night. Will apply the rest of the set soon.
_______________________________________________
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] 13+ messages in thread
* Re: [FFmpeg-devel] [PATCH 7/8] avformat/movenc: add support for Immersive Audio Model and Formats in ISOBMFF
2024-02-17 22:02 ` [FFmpeg-devel] [PATCH 7/8] avformat/movenc: add support for Immersive Audio Model and Formats in ISOBMFF James Almer
@ 2024-02-20 13:37 ` Andreas Rheinhardt
2024-02-20 14:07 ` James Almer
0 siblings, 1 reply; 13+ messages in thread
From: Andreas Rheinhardt @ 2024-02-20 13:37 UTC (permalink / raw)
To: ffmpeg-devel
James Almer:
> Signed-off-by: James Almer <jamrial@gmail.com>
> ---
> libavformat/movenc.c | 349 +++++++++++++++++++++++++++++++++++--------
> libavformat/movenc.h | 6 +
> 2 files changed, 293 insertions(+), 62 deletions(-)
>
> diff --git a/libavformat/movenc.c b/libavformat/movenc.c
> index c71a9983ed..cd63b353b8 100644
> --- a/libavformat/movenc.c
> +++ b/libavformat/movenc.c
> @@ -32,6 +32,7 @@
> #include "dovi_isom.h"
> #include "riff.h"
> #include "avio.h"
> +#include "iamf_writer.h"
> #include "isom.h"
> #include "av1.h"
> #include "avc.h"
> @@ -41,12 +42,14 @@
> #include "libavcodec/flac.h"
> #include "libavcodec/get_bits.h"
>
> +#include "libavcodec/bsf.h"
> #include "libavcodec/internal.h"
> #include "libavcodec/put_bits.h"
> #include "libavcodec/vc1_common.h"
> #include "libavcodec/raw.h"
> #include "internal.h"
> #include "libavutil/avstring.h"
> +#include "libavutil/bprint.h"
> #include "libavutil/channel_layout.h"
> #include "libavutil/csp.h"
> #include "libavutil/intfloat.h"
> @@ -316,6 +319,32 @@ static int mov_write_sdtp_tag(AVIOContext *pb, MOVTrack *track)
> return update_size(pb, pos);
> }
>
> +static int mov_write_iacb_tag(AVFormatContext *s, AVIOContext *pb, MOVTrack *track)
> +{
> + AVIOContext *dyn_bc;
> + int64_t pos = avio_tell(pb);
> + uint8_t *dyn_buf = NULL;
> + int dyn_size;
> + int ret = avio_open_dyn_buf(&dyn_bc);
> + if (ret < 0)
> + return ret;
> +
> + avio_wb32(pb, 0);
> + ffio_wfourcc(pb, "iacb");
> + avio_w8(pb, 1); // configurationVersion
> +
> + ret = ff_iamf_write_descriptors(track->iamf, dyn_bc, s);
> + if (ret < 0)
> + return ret;
> +
> + dyn_size = avio_close_dyn_buf(dyn_bc, &dyn_buf);
> + ffio_write_leb(pb, dyn_size);
> + avio_write(pb, dyn_buf, dyn_size);
> + av_free(dyn_buf);
> +
> + return update_size(pb, pos);
> +}
> +
> static int mov_write_amr_tag(AVIOContext *pb, MOVTrack *track)
> {
> avio_wb32(pb, 0x11); /* size */
> @@ -1358,6 +1387,8 @@ static int mov_write_audio_tag(AVFormatContext *s, AVIOContext *pb, MOVMuxContex
> ret = mov_write_wave_tag(s, pb, track);
> else if (track->tag == MKTAG('m','p','4','a'))
> ret = mov_write_esds_tag(pb, track);
> + else if (track->tag == MKTAG('i','a','m','f'))
> + ret = mov_write_iacb_tag(mov->fc, pb, track);
> else if (track->par->codec_id == AV_CODEC_ID_AMR_NB)
> ret = mov_write_amr_tag(pb, track);
> else if (track->par->codec_id == AV_CODEC_ID_AC3)
> @@ -2529,7 +2560,7 @@ static int mov_write_video_tag(AVFormatContext *s, AVIOContext *pb, MOVMuxContex
>
> if (track->mode == MODE_AVIF) {
> mov_write_ccst_tag(pb);
> - if (s->nb_streams > 0 && track == &mov->tracks[1])
> + if (mov->nb_streams > 0 && track == &mov->tracks[1])
> mov_write_aux_tag(pb, "auxi");
> }
>
> @@ -3124,9 +3155,9 @@ static int mov_write_iloc_tag(AVIOContext *pb, MOVMuxContext *mov, AVFormatConte
> avio_wb32(pb, 0); /* Version & flags */
> avio_w8(pb, (4 << 4) + 4); /* offset_size(4) and length_size(4) */
> avio_w8(pb, 0); /* base_offset_size(4) and reserved(4) */
> - avio_wb16(pb, s->nb_streams); /* item_count */
> + avio_wb16(pb, mov->nb_streams); /* item_count */
>
> - for (int i = 0; i < s->nb_streams; i++) {
> + for (int i = 0; i < mov->nb_streams; i++) {
> avio_wb16(pb, i + 1); /* item_id */
> avio_wb16(pb, 0); /* data_reference_index */
> avio_wb16(pb, 1); /* extent_count */
> @@ -3145,9 +3176,9 @@ static int mov_write_iinf_tag(AVIOContext *pb, MOVMuxContext *mov, AVFormatConte
> avio_wb32(pb, 0); /* size */
> ffio_wfourcc(pb, "iinf");
> avio_wb32(pb, 0); /* Version & flags */
> - avio_wb16(pb, s->nb_streams); /* entry_count */
> + avio_wb16(pb, mov->nb_streams); /* entry_count */
>
> - for (int i = 0; i < s->nb_streams; i++) {
> + for (int i = 0; i < mov->nb_streams; i++) {
> int64_t infe_pos = avio_tell(pb);
> avio_wb32(pb, 0); /* size */
> ffio_wfourcc(pb, "infe");
> @@ -3216,7 +3247,7 @@ static int mov_write_ipco_tag(AVIOContext *pb, MOVMuxContext *mov, AVFormatConte
> int64_t pos = avio_tell(pb);
> avio_wb32(pb, 0); /* size */
> ffio_wfourcc(pb, "ipco");
> - for (int i = 0; i < s->nb_streams; i++) {
> + for (int i = 0; i < mov->nb_streams; i++) {
> mov_write_ispe_tag(pb, mov, s, i);
> mov_write_pixi_tag(pb, mov, s, i);
> mov_write_av1c_tag(pb, &mov->tracks[i]);
> @@ -3234,9 +3265,9 @@ static int mov_write_ipma_tag(AVIOContext *pb, MOVMuxContext *mov, AVFormatConte
> avio_wb32(pb, 0); /* size */
> ffio_wfourcc(pb, "ipma");
> avio_wb32(pb, 0); /* Version & flags */
> - avio_wb32(pb, s->nb_streams); /* entry_count */
> + avio_wb32(pb, mov->nb_streams); /* entry_count */
>
> - for (int i = 0, index = 1; i < s->nb_streams; i++) {
> + for (int i = 0, index = 1; i < mov->nb_streams; i++) {
> avio_wb16(pb, i + 1); /* item_ID */
> avio_w8(pb, 4); /* association_count */
>
> @@ -4213,7 +4244,7 @@ static int mov_write_covr(AVIOContext *pb, AVFormatContext *s)
> int64_t pos = 0;
> int i;
>
> - for (i = 0; i < s->nb_streams; i++) {
> + for (i = 0; i < mov->nb_streams; i++) {
> MOVTrack *trk = &mov->tracks[i];
>
> if (!is_cover_image(trk->st) || trk->cover_image->size <= 0)
> @@ -4360,7 +4391,7 @@ static int mov_write_meta_tag(AVIOContext *pb, MOVMuxContext *mov,
> mov_write_pitm_tag(pb, 1);
> mov_write_iloc_tag(pb, mov, s);
> mov_write_iinf_tag(pb, mov, s);
> - if (s->nb_streams > 1)
> + if (mov->nb_streams > 1)
> mov_write_iref_tag(pb, mov, s);
> mov_write_iprp_tag(pb, mov, s);
> } else {
> @@ -4611,16 +4642,17 @@ static int mov_setup_track_ids(MOVMuxContext *mov, AVFormatContext *s)
>
> if (mov->use_stream_ids_as_track_ids) {
> int next_generated_track_id = 0;
> - for (i = 0; i < s->nb_streams; i++) {
> - if (s->streams[i]->id > next_generated_track_id)
> - next_generated_track_id = s->streams[i]->id;
> + for (i = 0; i < mov->nb_streams; i++) {
> + AVStream *st = mov->tracks[i].st;
> + if (st->id > next_generated_track_id)
> + next_generated_track_id = st->id;
> }
>
> for (i = 0; i < mov->nb_tracks; i++) {
> if (mov->tracks[i].entry <= 0 && !(mov->flags & FF_MOV_FLAG_FRAGMENT))
> continue;
>
> - mov->tracks[i].track_id = i >= s->nb_streams ? ++next_generated_track_id : s->streams[i]->id;
> + mov->tracks[i].track_id = i >= mov->nb_streams ? ++next_generated_track_id : mov->tracks[i].st->id;
> }
> } else {
> for (i = 0; i < mov->nb_tracks; i++) {
> @@ -4657,7 +4689,7 @@ static int mov_write_moov_tag(AVIOContext *pb, MOVMuxContext *mov,
> }
>
> if (mov->chapter_track)
> - for (i = 0; i < s->nb_streams; i++) {
> + for (i = 0; i < mov->nb_streams; i++) {
> mov->tracks[i].tref_tag = MKTAG('c','h','a','p');
> mov->tracks[i].tref_id = mov->tracks[mov->chapter_track].track_id;
> }
> @@ -4697,7 +4729,7 @@ static int mov_write_moov_tag(AVIOContext *pb, MOVMuxContext *mov,
> for (i = 0; i < mov->nb_tracks; i++) {
> if (mov->tracks[i].entry > 0 || mov->flags & FF_MOV_FLAG_FRAGMENT ||
> mov->mode == MODE_AVIF) {
> - int ret = mov_write_trak_tag(s, pb, mov, &(mov->tracks[i]), i < s->nb_streams ? s->streams[i] : NULL);
> + int ret = mov_write_trak_tag(s, pb, mov, &(mov->tracks[i]), i < mov->nb_streams ? mov->tracks[i].st : NULL);
> if (ret < 0)
> return ret;
> }
> @@ -5489,10 +5521,20 @@ static int mov_write_ftyp_tag(AVIOContext *pb, AVFormatContext *s)
> MOVMuxContext *mov = s->priv_data;
> int64_t pos = avio_tell(pb);
> int has_h264 = 0, has_av1 = 0, has_video = 0, has_dolby = 0;
> + int has_iamf = 0;
> int i;
>
> - for (i = 0; i < s->nb_streams; i++) {
> - AVStream *st = s->streams[i];
> + for (i = 0; i < s->nb_stream_groups; i++) {
> + const AVStreamGroup *stg = s->stream_groups[i];
> +
> + if (stg->type == AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT ||
> + stg->type == AV_STREAM_GROUP_PARAMS_IAMF_MIX_PRESENTATION) {
> + has_iamf = 1;
> + break;
> + }
> + }
> + for (i = 0; i < mov->nb_streams; i++) {
> + AVStream *st = mov->tracks[i].st;
> if (is_cover_image(st))
> continue;
> if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
> @@ -5560,6 +5602,8 @@ static int mov_write_ftyp_tag(AVIOContext *pb, AVFormatContext *s)
> ffio_wfourcc(pb, "av01");
> if (has_dolby)
> ffio_wfourcc(pb, "dby1");
> + if (has_iamf)
> + ffio_wfourcc(pb, "iamf");
> } else {
> if (mov->flags & FF_MOV_FLAG_FRAGMENT)
> ffio_wfourcc(pb, "iso6");
> @@ -5667,8 +5711,8 @@ static int mov_write_identification(AVIOContext *pb, AVFormatContext *s)
> mov_write_ftyp_tag(pb,s);
> if (mov->mode == MODE_PSP) {
> int video_streams_nb = 0, audio_streams_nb = 0, other_streams_nb = 0;
> - for (i = 0; i < s->nb_streams; i++) {
> - AVStream *st = s->streams[i];
> + for (i = 0; i < mov->nb_streams; i++) {
> + AVStream *st = mov->tracks[i].st;
> if (is_cover_image(st))
> continue;
> if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
> @@ -5855,7 +5899,7 @@ static int mov_write_squashed_packets(AVFormatContext *s)
> {
> MOVMuxContext *mov = s->priv_data;
>
> - for (int i = 0; i < s->nb_streams; i++) {
> + for (int i = 0; i < mov->nb_streams; i++) {
> MOVTrack *track = &mov->tracks[i];
> int ret = AVERROR_BUG;
>
> @@ -5896,7 +5940,7 @@ static int mov_flush_fragment(AVFormatContext *s, int force)
> // of fragments was triggered automatically by an AVPacket, we
> // already have reliable info for the end of that track, but other
> // tracks may need to be filled in.
> - for (i = 0; i < s->nb_streams; i++) {
> + for (i = 0; i < mov->nb_streams; i++) {
> MOVTrack *track = &mov->tracks[i];
> if (!track->end_reliable) {
> const AVPacket *pkt = ff_interleaved_peek(s, i);
> @@ -6097,10 +6141,8 @@ static int mov_auto_flush_fragment(AVFormatContext *s, int force)
> return ret;
> }
>
> -static int check_pkt(AVFormatContext *s, AVPacket *pkt)
> +static int check_pkt(AVFormatContext *s, MOVTrack *trk, AVPacket *pkt)
> {
> - MOVMuxContext *mov = s->priv_data;
> - MOVTrack *trk = &mov->tracks[pkt->stream_index];
> int64_t ref;
> uint64_t duration;
>
> @@ -6138,15 +6180,21 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt)
> {
> MOVMuxContext *mov = s->priv_data;
> AVIOContext *pb = s->pb;
> - MOVTrack *trk = &mov->tracks[pkt->stream_index];
> - AVCodecParameters *par = trk->par;
> + MOVTrack *trk;
> + AVCodecParameters *par;
> AVProducerReferenceTime *prft;
> unsigned int samples_in_chunk = 0;
> int size = pkt->size, ret = 0, offset = 0;
> size_t prft_size;
> uint8_t *reformatted_data = NULL;
>
> - ret = check_pkt(s, pkt);
> + if (pkt->stream_index < s->nb_streams)
> + trk = s->streams[pkt->stream_index]->priv_data;
> + else // Timecode or chapter
> + trk = &mov->tracks[pkt->stream_index];
> + par = trk->par;
> +
> + ret = check_pkt(s, trk, pkt);
> if (ret < 0)
> return ret;
>
> @@ -6236,7 +6284,7 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt)
>
> if (par->codec_id == AV_CODEC_ID_AAC && pkt->size > 2 &&
> (AV_RB16(pkt->data) & 0xfff0) == 0xfff0) {
> - if (!s->streams[pkt->stream_index]->nb_frames) {
> + if (!trk->st->nb_frames) {
> av_log(s, AV_LOG_ERROR, "Malformed AAC bitstream detected: "
> "use the audio bitstream filter 'aac_adtstoasc' to fix it "
> "('-bsf:a aac_adtstoasc' option with ffmpeg)\n");
> @@ -6498,18 +6546,18 @@ err:
> static int mov_write_single_packet(AVFormatContext *s, AVPacket *pkt)
> {
> MOVMuxContext *mov = s->priv_data;
> - MOVTrack *trk = &mov->tracks[pkt->stream_index];
> + MOVTrack *trk = s->streams[pkt->stream_index]->priv_data;
> AVCodecParameters *par = trk->par;
> int64_t frag_duration = 0;
> int size = pkt->size;
>
> - int ret = check_pkt(s, pkt);
> + int ret = check_pkt(s, trk, pkt);
> if (ret < 0)
> return ret;
>
> if (mov->flags & FF_MOV_FLAG_FRAG_DISCONT) {
> int i;
> - for (i = 0; i < s->nb_streams; i++)
> + for (i = 0; i < mov->nb_streams; i++)
> mov->tracks[i].frag_discont = 1;
> mov->flags &= ~FF_MOV_FLAG_FRAG_DISCONT;
> }
> @@ -6551,7 +6599,7 @@ static int mov_write_single_packet(AVFormatContext *s, AVPacket *pkt)
> return 0; /* Discard 0 sized packets */
> }
>
> - if (trk->entry && pkt->stream_index < s->nb_streams)
> + if (trk->entry && pkt->stream_index < mov->nb_streams)
> frag_duration = av_rescale_q(pkt->dts - trk->cluster[0].dts,
> s->streams[pkt->stream_index]->time_base,
> AV_TIME_BASE_Q);
> @@ -6606,17 +6654,80 @@ static int mov_write_subtitle_end_packet(AVFormatContext *s,
> return ret;
> }
>
> +static int mov_build_iamf_packet(AVFormatContext *s, MOVTrack *trk, AVPacket *pkt)
> +{
> + int ret;
> +
> + if (pkt->stream_index == trk->first_iamf_idx) {
> + ret = ff_iamf_write_parameter_blocks(trk->iamf, trk->iamf_buf, pkt, s);
> + if (ret < 0)
> + return ret;
> + }
> +
> + ret = ff_iamf_write_audio_frame(trk->iamf, trk->iamf_buf,
> + s->streams[pkt->stream_index]->id, pkt);
> + if (ret < 0)
> + return ret;
> +
> + if (pkt->stream_index == trk->last_iamf_idx) {
> + uint8_t *data;
> +
> + ret = avio_close_dyn_buf(trk->iamf_buf, &data);
> + trk->iamf_buf = NULL;
> +
> + if (!ret) {
> + if (pkt->size) {
> + // Either all or none of the packets for a single
> + // IA Sample may be empty.
> + av_log(s, AV_LOG_ERROR, "Unexpected packet from "
> + "stream #%d\n", pkt->stream_index);
> + ret = AVERROR_INVALIDDATA;
> + }
> + av_free(data);
> + return ret;
> + }
> + av_buffer_unref(&pkt->buf);
> + pkt->buf = av_buffer_create(data, ret, NULL, NULL, 0);
> + if (!pkt->buf) {
> + av_free(data);
> + return AVERROR(ENOMEM);
> + }
> + pkt->data = data;
> + pkt->size = ret;
> + pkt->stream_index = trk->first_iamf_idx;
> +
> + ret = avio_open_dyn_buf(&trk->iamf_buf);
> + if (ret < 0)
> + return ret;
> + } else
> + ret = AVERROR(EAGAIN);
> +
> + return ret;
> +}
> +
> static int mov_write_packet(AVFormatContext *s, AVPacket *pkt)
> {
> MOVMuxContext *mov = s->priv_data;
> MOVTrack *trk;
> + int ret;
>
> if (!pkt) {
> mov_flush_fragment(s, 1);
> return 1;
> }
>
> - trk = &mov->tracks[pkt->stream_index];
> + trk = s->streams[pkt->stream_index]->priv_data;
> +
> + if (trk->iamf) {
> + ret = mov_build_iamf_packet(s, trk, pkt);
> + if (ret < 0) {
> + if (ret == AVERROR(EAGAIN))
> + return 0;
> + av_log(s, AV_LOG_ERROR, "Error assembling an IAMF packet "
> + "for stream #%d\n", trk->st->index);
> + return ret;
> + }
> + }
>
> if (is_cover_image(trk->st)) {
> int ret;
> @@ -6817,12 +6928,12 @@ static int mov_create_chapter_track(AVFormatContext *s, int tracknum)
> }
>
>
> -static int mov_check_timecode_track(AVFormatContext *s, AVTimecode *tc, int src_index, const char *tcstr)
> +static int mov_check_timecode_track(AVFormatContext *s, AVTimecode *tc, AVStream *src_st, const char *tcstr)
> {
> int ret;
>
> /* compute the frame number */
> - ret = av_timecode_init_from_string(tc, s->streams[src_index]->avg_frame_rate, tcstr, s);
> + ret = av_timecode_init_from_string(tc, src_st->avg_frame_rate, tcstr, s);
> return ret;
> }
>
> @@ -6830,7 +6941,7 @@ static int mov_create_timecode_track(AVFormatContext *s, int index, int src_inde
> {
> MOVMuxContext *mov = s->priv_data;
> MOVTrack *track = &mov->tracks[index];
> - AVStream *src_st = s->streams[src_index];
> + AVStream *src_st = mov->tracks[src_index].st;
> uint8_t data[4];
> AVPacket *pkt = mov->pkt;
> AVRational rate = src_st->avg_frame_rate;
> @@ -6890,8 +7001,8 @@ static void enable_tracks(AVFormatContext *s)
> first[i] = -1;
> }
>
> - for (i = 0; i < s->nb_streams; i++) {
> - AVStream *st = s->streams[i];
> + for (i = 0; i < mov->nb_streams; i++) {
> + AVStream *st = mov->tracks[i].st;
>
> if (st->codecpar->codec_type <= AVMEDIA_TYPE_UNKNOWN ||
> st->codecpar->codec_type >= AVMEDIA_TYPE_NB ||
> @@ -6925,6 +7036,9 @@ static void mov_free(AVFormatContext *s)
> MOVMuxContext *mov = s->priv_data;
> int i;
>
> + for (i = 0; i < s->nb_streams; i++)
> + s->streams[i]->priv_data = NULL;
> +
> if (!mov->tracks)
> return;
>
> @@ -6954,6 +7068,11 @@ static void mov_free(AVFormatContext *s)
> ff_mov_cenc_free(&track->cenc);
> ffio_free_dyn_buf(&track->mdat_buf);
>
> + ffio_free_dyn_buf(&track->iamf_buf);
> + if (track->iamf)
> + ff_iamf_write_deinit(track->iamf);
> + av_freep(&track->iamf);
> +
> avpriv_packet_list_free(&track->squashed_packet_queue);
> }
>
> @@ -7027,6 +7146,66 @@ static int mov_create_dvd_sub_decoder_specific_info(MOVTrack *track,
> return 0;
> }
>
> +static int mov_init_iamf_track(AVFormatContext *s)
> +{
> + MOVMuxContext *mov = s->priv_data;
> + MOVTrack *track = &mov->tracks[0]; // IAMF if present is always the first track
> + int nb_audio_elements = 0, nb_mix_presentations = 0;
> + int ret;
> +
> + for (int i = 0; i < s->nb_stream_groups; i++) {
> + const AVStreamGroup *stg = s->stream_groups[i];
> +
> + if (stg->type == AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT)
> + nb_audio_elements++;
> + if (stg->type == AV_STREAM_GROUP_PARAMS_IAMF_MIX_PRESENTATION)
> + nb_mix_presentations++;
> + }
> +
> + if (!nb_audio_elements && !nb_mix_presentations)
> + return 0;
> +
> + if ((nb_audio_elements < 1 && nb_audio_elements > 2) || nb_mix_presentations < 1) {
> + av_log(s, AV_LOG_ERROR, "There must be >= 1 and <= 2 IAMF_AUDIO_ELEMENT and at least "
> + "one IAMF_MIX_PRESENTATION stream groups to write a IMAF track\n");
> + return AVERROR(EINVAL);
> + }
> +
> + track->iamf = av_mallocz(sizeof(*track->iamf));
> + if (!track->iamf)
> + return AVERROR(ENOMEM);
> +
> + for (int i = 0; i < s->nb_stream_groups; i++) {
> + const AVStreamGroup *stg = s->stream_groups[i];
> + switch(stg->type) {
> + case AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT:
> + for (int j = 0; j < stg->nb_streams; j++) {
> + track->first_iamf_idx = FFMIN(stg->streams[j]->index, track->first_iamf_idx);
> + track->last_iamf_idx = FFMAX(stg->streams[j]->index, track->last_iamf_idx);
> + stg->streams[j]->priv_data = track;
> + }
> +
> + ret = ff_iamf_add_audio_element(track->iamf, stg, s);
> + break;
> + case AV_STREAM_GROUP_PARAMS_IAMF_MIX_PRESENTATION:
> + ret = ff_iamf_add_mix_presentation(track->iamf, stg, s);
> + break;
> + default:
> + av_assert0(0);
> + }
> + if (ret < 0)
> + return ret;
> + }
> +
> + track->tag = MKTAG('i','a','m','f');
> +
> + ret = avio_open_dyn_buf(&track->iamf_buf);
> + if (ret < 0)
> + return ret;
> +
> + return 0;
> +}
> +
> static int mov_init(AVFormatContext *s)
> {
> MOVMuxContext *mov = s->priv_data;
> @@ -7164,7 +7343,37 @@ static int mov_init(AVFormatContext *s)
> s->streams[0]->disposition |= AV_DISPOSITION_DEFAULT;
> }
>
> - mov->nb_tracks = s->nb_streams;
> + for (i = 0; i < s->nb_stream_groups; i++) {
> + AVStreamGroup *stg = s->stream_groups[i];
> +
> + if (stg->type != AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT)
> + continue;
> +
> + for (int j = 0; j < stg->nb_streams; j++) {
> + AVStream *st = stg->streams[j];
> +
> + if (st->priv_data) {
> + av_log(s, AV_LOG_ERROR, "Stream %d is present in more than one Stream Group of type "
> + "IAMF Audio Element\n", j);
> + return AVERROR(EINVAL);
> + }
> + st->priv_data = st;
> + }
> +
> + if (!mov->nb_tracks) // We support one track for the entire IAMF structure
> + mov->nb_tracks++;
> + }
> +
> + for (i = 0; i < s->nb_streams; i++) {
> + AVStream *st = s->streams[i];
> + if (st->priv_data)
> + continue;
> + st->priv_data = st;
> + mov->nb_tracks++;
> + }
> +
> + mov->nb_streams = mov->nb_tracks;
> +
> if (mov->mode & (MODE_MP4|MODE_MOV|MODE_IPOD) && s->nb_chapters)
> mov->chapter_track = mov->nb_tracks++;
>
> @@ -7190,7 +7399,7 @@ static int mov_init(AVFormatContext *s)
> if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
> (t || (t=av_dict_get(st->metadata, "timecode", NULL, 0)))) {
> AVTimecode tc;
> - ret = mov_check_timecode_track(s, &tc, i, t->value);
> + ret = mov_check_timecode_track(s, &tc, st, t->value);
> if (ret >= 0)
> mov->nb_meta_tmcd++;
> }
> @@ -7239,18 +7448,33 @@ static int mov_init(AVFormatContext *s)
> }
> }
>
> + ret = mov_init_iamf_track(s);
> + if (ret < 0)
> + return ret;
> +
> + for (int j = 0, i = 0; j < s->nb_streams; j++) {
> + AVStream *st = s->streams[j];
> +
> + if (st != st->priv_data)
> + continue;
> + st->priv_data = &mov->tracks[i++];
> + }
> +
> for (i = 0; i < s->nb_streams; i++) {
> AVStream *st= s->streams[i];
> - MOVTrack *track= &mov->tracks[i];
> + MOVTrack *track = st->priv_data;
> AVDictionaryEntry *lang = av_dict_get(st->metadata, "language", NULL,0);
>
> - track->st = st;
> - track->par = st->codecpar;
> + if (!track->st) {
> + track->st = st;
> + track->par = st->codecpar;
> + }
> track->language = ff_mov_iso639_to_lang(lang?lang->value:"und", mov->mode!=MODE_MOV);
> if (track->language < 0)
> track->language = 32767; // Unspecified Macintosh language code
> track->mode = mov->mode;
> - track->tag = mov_find_codec_tag(s, track);
> + if (!track->tag)
> + track->tag = mov_find_codec_tag(s, track);
> if (!track->tag) {
> av_log(s, AV_LOG_ERROR, "Could not find tag for codec %s in stream #%d, "
> "codec not currently supported in container\n",
> @@ -7442,25 +7666,26 @@ static int mov_write_header(AVFormatContext *s)
> {
> AVIOContext *pb = s->pb;
> MOVMuxContext *mov = s->priv_data;
> - int i, ret, hint_track = 0, tmcd_track = 0, nb_tracks = s->nb_streams;
> + int i, ret, hint_track = 0, tmcd_track = 0, nb_tracks = mov->nb_streams;
>
> if (mov->mode & (MODE_MP4|MODE_MOV|MODE_IPOD) && s->nb_chapters)
> nb_tracks++;
>
> if (mov->flags & FF_MOV_FLAG_RTP_HINT) {
> hint_track = nb_tracks;
> - for (i = 0; i < s->nb_streams; i++)
> - if (rtp_hinting_needed(s->streams[i]))
> + for (i = 0; i < mov->nb_streams; i++) {
> + if (rtp_hinting_needed(mov->tracks[i].st))
> nb_tracks++;
> + }
> }
>
> if (mov->nb_meta_tmcd)
> tmcd_track = nb_tracks;
>
> - for (i = 0; i < s->nb_streams; i++) {
> + for (i = 0; i < mov->nb_streams; i++) {
> int j;
> - AVStream *st= s->streams[i];
> - MOVTrack *track= &mov->tracks[i];
> + MOVTrack *track = &mov->tracks[i];
> + AVStream *st = track->st;
>
> /* copy extradata if it exists */
> if (st->codecpar->extradata_size) {
> @@ -7482,8 +7707,8 @@ static int mov_write_header(AVFormatContext *s)
> &(AVChannelLayout)AV_CHANNEL_LAYOUT_MONO))
> continue;
>
> - for (j = 0; j < s->nb_streams; j++) {
> - AVStream *stj= s->streams[j];
> + for (j = 0; j < mov->nb_streams; j++) {
> + AVStream *stj= mov->tracks[j].st;
> MOVTrack *trackj= &mov->tracks[j];
> if (j == i)
> continue;
> @@ -7546,8 +7771,8 @@ static int mov_write_header(AVFormatContext *s)
> return ret;
>
> if (mov->flags & FF_MOV_FLAG_RTP_HINT) {
> - for (i = 0; i < s->nb_streams; i++) {
> - if (rtp_hinting_needed(s->streams[i])) {
> + for (i = 0; i < mov->nb_streams; i++) {
> + if (rtp_hinting_needed(mov->tracks[i].st)) {
> if ((ret = ff_mov_init_hinting(s, hint_track, i)) < 0)
> return ret;
> hint_track++;
> @@ -7559,8 +7784,8 @@ static int mov_write_header(AVFormatContext *s)
> const AVDictionaryEntry *t, *global_tcr = av_dict_get(s->metadata,
> "timecode", NULL, 0);
> /* Initialize the tmcd tracks */
> - for (i = 0; i < s->nb_streams; i++) {
> - AVStream *st = s->streams[i];
> + for (i = 0; i < mov->nb_streams; i++) {
> + AVStream *st = mov->tracks[i].st;
> t = global_tcr;
>
> if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
> @@ -7569,7 +7794,7 @@ static int mov_write_header(AVFormatContext *s)
> t = av_dict_get(st->metadata, "timecode", NULL, 0);
> if (!t)
> continue;
> - if (mov_check_timecode_track(s, &tc, i, t->value) < 0)
> + if (mov_check_timecode_track(s, &tc, st, t->value) < 0)
> continue;
> if ((ret = mov_create_timecode_track(s, tmcd_track, i, tc)) < 0)
> return ret;
> @@ -7690,7 +7915,7 @@ static int mov_write_trailer(AVFormatContext *s)
> int64_t moov_pos;
>
> if (mov->need_rewrite_extradata) {
> - for (i = 0; i < s->nb_streams; i++) {
> + for (i = 0; i < mov->nb_streams; i++) {
> MOVTrack *track = &mov->tracks[i];
> AVCodecParameters *par = track->par;
>
> @@ -7830,7 +8055,7 @@ static int avif_write_trailer(AVFormatContext *s)
> if (mov->moov_written) return 0;
>
> mov->is_animated_avif = s->streams[0]->nb_frames > 1;
> - if (mov->is_animated_avif && s->nb_streams > 1) {
> + if (mov->is_animated_avif && mov->nb_streams > 1) {
> // For animated avif with alpha channel, we need to write a tref tag
> // with type "auxl".
> mov->tracks[1].tref_tag = MKTAG('a', 'u', 'x', 'l');
> @@ -7840,7 +8065,7 @@ static int avif_write_trailer(AVFormatContext *s)
> mov_write_meta_tag(pb, mov, s);
>
> moov_size = get_moov_size(s);
> - for (i = 0; i < s->nb_streams; i++)
> + for (i = 0; i < mov->nb_tracks; i++)
> mov->tracks[i].data_offset = avio_tell(pb) + moov_size + 8;
>
> if (mov->is_animated_avif) {
> @@ -7862,7 +8087,7 @@ static int avif_write_trailer(AVFormatContext *s)
>
> // write extent offsets.
> pos_backup = avio_tell(pb);
> - for (i = 0; i < s->nb_streams; i++) {
> + for (i = 0; i < mov->nb_streams; i++) {
Can you use loop-scope for all the iterators that you touch where
possible, please?
> if (extent_offsets[i] != (uint32_t)extent_offsets[i]) {
> av_log(s, AV_LOG_ERROR, "extent offset does not fit in 32 bits\n");
> return AVERROR_INVALIDDATA;
> diff --git a/libavformat/movenc.h b/libavformat/movenc.h
> index 60363198c9..08d580594d 100644
> --- a/libavformat/movenc.h
> +++ b/libavformat/movenc.h
> @@ -170,6 +170,11 @@ typedef struct MOVTrack {
> unsigned int squash_fragment_samples_to_one; //< flag to note formats where all samples for a fragment are to be squashed
>
> PacketList squashed_packet_queue;
> +
> + struct IAMFContext *iamf;
> + int first_iamf_idx;
> + int last_iamf_idx;
> + AVIOContext *iamf_buf;
> } MOVTrack;
>
> typedef enum {
> @@ -188,6 +193,7 @@ typedef struct MOVMuxContext {
> const AVClass *av_class;
> int mode;
> int64_t time;
> + int nb_streams;
> int nb_tracks;
> int nb_meta_tmcd; ///< number of new created tmcd track based on metadata (aka not data copy)
> int chapter_track; ///< qt chapter track number
_______________________________________________
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] 13+ messages in thread
* Re: [FFmpeg-devel] [PATCH 7/8] avformat/movenc: add support for Immersive Audio Model and Formats in ISOBMFF
2024-02-20 13:37 ` Andreas Rheinhardt
@ 2024-02-20 14:07 ` James Almer
2024-02-20 14:21 ` Andreas Rheinhardt
0 siblings, 1 reply; 13+ messages in thread
From: James Almer @ 2024-02-20 14:07 UTC (permalink / raw)
To: ffmpeg-devel
On 2/20/2024 10:37 AM, Andreas Rheinhardt wrote:
>> @@ -7862,7 +8087,7 @@ static int avif_write_trailer(AVFormatContext *s)
>>
>> // write extent offsets.
>> pos_backup = avio_tell(pb);
>> - for (i = 0; i < s->nb_streams; i++) {
>> + for (i = 0; i < mov->nb_streams; i++) {
> Can you use loop-scope for all the iterators that you touch where
> possible, please?
Can you elaborate?
_______________________________________________
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] 13+ messages in thread
* Re: [FFmpeg-devel] [PATCH 7/8] avformat/movenc: add support for Immersive Audio Model and Formats in ISOBMFF
2024-02-20 14:07 ` James Almer
@ 2024-02-20 14:21 ` Andreas Rheinhardt
2024-02-20 14:47 ` James Almer
0 siblings, 1 reply; 13+ messages in thread
From: Andreas Rheinhardt @ 2024-02-20 14:21 UTC (permalink / raw)
To: ffmpeg-devel
James Almer:
> On 2/20/2024 10:37 AM, Andreas Rheinhardt wrote:
>>> @@ -7862,7 +8087,7 @@ static int avif_write_trailer(AVFormatContext *s)
>>> // write extent offsets.
>>> pos_backup = avio_tell(pb);
>>> - for (i = 0; i < s->nb_streams; i++) {
>>> + for (i = 0; i < mov->nb_streams; i++) {
>> Can you use loop-scope for all the iterators that you touch where
>> possible, please?
>
> Can you elaborate?
Use "for (int i = 0;" when you already touch the line (where possible).
- Andreas
_______________________________________________
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] 13+ messages in thread
* Re: [FFmpeg-devel] [PATCH 7/8] avformat/movenc: add support for Immersive Audio Model and Formats in ISOBMFF
2024-02-20 14:21 ` Andreas Rheinhardt
@ 2024-02-20 14:47 ` James Almer
0 siblings, 0 replies; 13+ messages in thread
From: James Almer @ 2024-02-20 14:47 UTC (permalink / raw)
To: ffmpeg-devel
On 2/20/2024 11:21 AM, Andreas Rheinhardt wrote:
> James Almer:
>> On 2/20/2024 10:37 AM, Andreas Rheinhardt wrote:
>>>> @@ -7862,7 +8087,7 @@ static int avif_write_trailer(AVFormatContext *s)
>>>> // write extent offsets.
>>>> pos_backup = avio_tell(pb);
>>>> - for (i = 0; i < s->nb_streams; i++) {
>>>> + for (i = 0; i < mov->nb_streams; i++) {
>>> Can you use loop-scope for all the iterators that you touch where
>>> possible, please?
>>
>> Can you elaborate?
>
> Use "for (int i = 0;" when you already touch the line (where possible).
>
> - Andreas
Done. Thanks.
_______________________________________________
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] 13+ messages in thread
end of thread, other threads:[~2024-02-20 14:47 UTC | newest]
Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-02-17 22:02 [FFmpeg-devel] [PATCH 1/8] avformat/demux: allow demuxers to output more than one packet per read_packet() call James Almer
2024-02-17 22:02 ` [FFmpeg-devel] [PATCH 2/8] avformat/iamfdec: further split into shareable modules James Almer
2024-02-17 22:02 ` [FFmpeg-devel] [PATCH 3/8] avformat/mov: factorize out setting the output packet properties James Almer
2024-02-17 22:02 ` [FFmpeg-devel] [PATCH 4/8] avformat/mov: make MOVStreamContext refcounted James Almer
2024-02-17 22:02 ` [FFmpeg-devel] [PATCH 5/8] avformat/mov: add support for Immersive Audio Model and Formats in ISOBMFF James Almer
2024-02-17 22:02 ` [FFmpeg-devel] [PATCH 6/8] avformat/iamfenc: further split into shareable modules James Almer
2024-02-17 22:02 ` [FFmpeg-devel] [PATCH 7/8] avformat/movenc: add support for Immersive Audio Model and Formats in ISOBMFF James Almer
2024-02-20 13:37 ` Andreas Rheinhardt
2024-02-20 14:07 ` James Almer
2024-02-20 14:21 ` Andreas Rheinhardt
2024-02-20 14:47 ` James Almer
2024-02-17 22:02 ` [FFmpeg-devel] [PATCH 8/8] fate: add IAMF in mp4 tests James Almer
2024-02-20 13:29 ` 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