Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
 help / color / mirror / Atom feed
* [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