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/7 v4] avcodec: add an Immersive Audio Model and Formats frame split bsf
@ 2024-02-06 13:05 James Almer
  2024-02-06 13:05 ` [FFmpeg-devel] [PATCH 2/7 v4] avformat/demux: support inserting bitstream filters in demuxing scenarios James Almer
                   ` (6 more replies)
  0 siblings, 7 replies; 17+ messages in thread
From: James Almer @ 2024-02-06 13:05 UTC (permalink / raw)
  To: ffmpeg-devel

Signed-off-by: James Almer <jamrial@gmail.com>
---
Now reading decriptors from extradata, plus a setting to ensure any descriptors
present inband are omitted has been added.

 doc/bitstream_filters.texi            |  16 +
 libavcodec/bitstream_filters.c        |   1 +
 libavcodec/bsf/Makefile               |   1 +
 libavcodec/bsf/iamf_frame_split_bsf.c | 887 ++++++++++++++++++++++++++
 4 files changed, 905 insertions(+)
 create mode 100644 libavcodec/bsf/iamf_frame_split_bsf.c

diff --git a/doc/bitstream_filters.texi b/doc/bitstream_filters.texi
index d5bac105ff..3d1a5e7a24 100644
--- a/doc/bitstream_filters.texi
+++ b/doc/bitstream_filters.texi
@@ -465,6 +465,22 @@ Please note that this filter is auto-inserted for MPEG-TS (muxer
 @code{mpegts}) and raw HEVC/H.265 (muxer @code{h265} or
 @code{hevc}) output formats.
 
+@section iamf_frame_split
+
+Split a packet containing one or more Audio Frame OBUs into several
+packets each containing the respective extracted audio data without the
+Audio Frame OBU encapsulation.
+Stream index in output packets will be set based on the order the OBUs
+are coded.
+
+@table @option
+@item first_index
+Lowest stream index value to set in output packets
+
+@item inband_descriptors
+Enable parsing in-band descriptor OBUs
+@end table
+
 @section imxdump
 
 Modifies the bitstream to fit in MOV and to be usable by the Final Cut
diff --git a/libavcodec/bitstream_filters.c b/libavcodec/bitstream_filters.c
index 1bae113d92..633187bc6e 100644
--- a/libavcodec/bitstream_filters.c
+++ b/libavcodec/bitstream_filters.c
@@ -42,6 +42,7 @@ extern const FFBitStreamFilter ff_h264_redundant_pps_bsf;
 extern const FFBitStreamFilter ff_hapqa_extract_bsf;
 extern const FFBitStreamFilter ff_hevc_metadata_bsf;
 extern const FFBitStreamFilter ff_hevc_mp4toannexb_bsf;
+extern const FFBitStreamFilter ff_iamf_frame_split_bsf;
 extern const FFBitStreamFilter ff_imx_dump_header_bsf;
 extern const FFBitStreamFilter ff_media100_to_mjpegb_bsf;
 extern const FFBitStreamFilter ff_mjpeg2jpeg_bsf;
diff --git a/libavcodec/bsf/Makefile b/libavcodec/bsf/Makefile
index 62609eb24e..738b97bdd1 100644
--- a/libavcodec/bsf/Makefile
+++ b/libavcodec/bsf/Makefile
@@ -20,6 +20,7 @@ OBJS-$(CONFIG_H264_REDUNDANT_PPS_BSF)     += bsf/h264_redundant_pps.o
 OBJS-$(CONFIG_HAPQA_EXTRACT_BSF)          += bsf/hapqa_extract.o
 OBJS-$(CONFIG_HEVC_METADATA_BSF)          += bsf/h265_metadata.o
 OBJS-$(CONFIG_HEVC_MP4TOANNEXB_BSF)       += bsf/hevc_mp4toannexb.o
+OBJS-$(CONFIG_IAMF_FRAME_SPLIT_BSF)       += bsf/iamf_frame_split_bsf.o
 OBJS-$(CONFIG_IMX_DUMP_HEADER_BSF)        += bsf/imx_dump_header.o
 OBJS-$(CONFIG_MEDIA100_TO_MJPEGB_BSF)     += bsf/media100_to_mjpegb.o
 OBJS-$(CONFIG_MJPEG2JPEG_BSF)             += bsf/mjpeg2jpeg.o
diff --git a/libavcodec/bsf/iamf_frame_split_bsf.c b/libavcodec/bsf/iamf_frame_split_bsf.c
new file mode 100644
index 0000000000..dc8ad6730a
--- /dev/null
+++ b/libavcodec/bsf/iamf_frame_split_bsf.c
@@ -0,0 +1,887 @@
+/*
+ * Copyright (c) 2023 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 <stdint.h>
+#include <stddef.h>
+
+#include "libavutil/dict.h"
+#include "libavutil/opt.h"
+#include "libavformat/iamf.h"
+#include "bsf.h"
+#include "bsf_internal.h"
+#include "get_bits.h"
+#include "leb.h"
+
+typedef struct ParamDefinition {
+    AVIAMFParamDefinition *param;
+    size_t param_size;
+    int mode;
+    int recon_gain_present_bitmask;
+} ParamDefinition;
+
+typedef struct IAMFSplitContext {
+    AVClass *class;
+    AVPacket *buffer_pkt;
+
+    ParamDefinition *param_definitions;
+    unsigned int nb_param_definitions;
+
+    unsigned int *ids;
+    int nb_ids;
+
+    // AVOptions
+    int first_index;
+    int inband_descriptors;
+
+    // Packet side data
+    AVIAMFParamDefinition *mix;
+    size_t mix_size;
+    AVIAMFParamDefinition *demix;
+    size_t demix_size;
+    AVIAMFParamDefinition *recon;
+    size_t recon_size;
+} IAMFSplitContext;
+
+static int param_parse(AVBSFContext *ctx, GetBitContext *gb,
+                       unsigned int type,
+                       ParamDefinition **out)
+{
+    IAMFSplitContext *const c = ctx->priv_data;
+    ParamDefinition *param_definition = NULL;
+    AVIAMFParamDefinition *param;
+    unsigned int parameter_id, parameter_rate, mode;
+    unsigned int duration = 0, constant_subblock_duration = 0, nb_subblocks = 0;
+    size_t param_size;
+
+    parameter_id = get_leb(gb);
+
+    for (int i = 0; i < c->nb_param_definitions; i++)
+        if (c->param_definitions[i].param->parameter_id == parameter_id) {
+            param_definition = &c->param_definitions[i];
+            break;
+        }
+
+    parameter_rate = get_leb(gb);
+    mode = get_bits(gb, 8) >> 7;
+
+    if (mode == 0) {
+        duration = get_leb(gb);
+        constant_subblock_duration = get_leb(gb);
+        if (constant_subblock_duration == 0) {
+            nb_subblocks = get_leb(gb);
+        } else
+            nb_subblocks = duration / constant_subblock_duration;
+    }
+
+    param = av_iamf_param_definition_alloc(type, nb_subblocks, &param_size);
+    if (!param)
+        return AVERROR(ENOMEM);
+
+    for (int i = 0; i < nb_subblocks; i++) {
+        if (constant_subblock_duration == 0)
+            get_leb(gb); // subblock_duration
+
+        switch (type) {
+        case AV_IAMF_PARAMETER_DEFINITION_MIX_GAIN:
+            break;
+        case AV_IAMF_PARAMETER_DEFINITION_DEMIXING:
+            skip_bits(gb, 8); // dmixp_mode
+            break;
+        case AV_IAMF_PARAMETER_DEFINITION_RECON_GAIN:
+            break;
+        default:
+            av_free(param);
+            return AVERROR_INVALIDDATA;
+        }
+    }
+
+    param->parameter_id = parameter_id;
+    param->parameter_rate = parameter_rate;
+    param->duration = duration;
+    param->constant_subblock_duration = constant_subblock_duration;
+    param->nb_subblocks = nb_subblocks;
+
+    if (param_definition) {
+        if (param_definition->param_size != param_size ||
+            memcmp(param_definition->param, param, param_size)) {
+            av_log(ctx, AV_LOG_ERROR, "Incosistent parameters for parameter_id %u\n",
+                   parameter_id);
+            av_free(param);
+            return AVERROR_INVALIDDATA;
+        }
+        av_freep(&param);
+    } else {
+        ParamDefinition *tmp = av_realloc_array(c->param_definitions,
+                                                c->nb_param_definitions + 1,
+                                                sizeof(*c->param_definitions));
+        if (!tmp) {
+            av_free(param);
+            return AVERROR(ENOMEM);
+        }
+        c->param_definitions = tmp;
+
+        param_definition = &c->param_definitions[c->nb_param_definitions++];
+        param_definition->param = param;
+        param_definition->mode = !mode;
+        param_definition->param_size = param_size;
+    }
+    if (out)
+        *out = param_definition;
+
+    return 0;
+}
+
+static int scalable_channel_layout_config(AVBSFContext *ctx, GetBitContext *gb,
+                                          ParamDefinition *recon_gain)
+{
+    int nb_layers;
+
+    nb_layers = get_bits(gb, 3);
+    skip_bits(gb, 5); //reserved
+
+    if (nb_layers > 6)
+        return AVERROR_INVALIDDATA;
+
+    for (int i = 0; i < nb_layers; i++) {
+        int output_gain_is_present_flag, recon_gain_is_present;
+
+        skip_bits(gb, 4); // loudspeaker_layout
+        output_gain_is_present_flag = get_bits1(gb);
+        recon_gain_is_present = get_bits1(gb);
+        if (recon_gain)
+            recon_gain->recon_gain_present_bitmask |= recon_gain_is_present << i;
+        skip_bits(gb, 2); // reserved
+        skip_bits(gb, 8); // substream_count
+        skip_bits(gb, 8); // coupled_substream_count
+        if (output_gain_is_present_flag) {
+            skip_bits(gb, 8); // output_gain_flags & reserved
+            skip_bits(gb, 16); // output_gain
+        }
+    }
+
+    return 0;
+}
+
+static int audio_element_obu(AVBSFContext *ctx, uint8_t *buf, unsigned size)
+{
+    IAMFSplitContext *const c = ctx->priv_data;
+    GetBitContext gb;
+    ParamDefinition *recon_gain = NULL;
+    unsigned audio_element_type;
+    unsigned num_substreams, num_parameters;
+    int ret;
+
+    ret = init_get_bits8(&gb, buf, size);
+    if (ret < 0)
+        return ret;
+
+    get_leb(&gb); // audio_element_id
+    audio_element_type = get_bits(&gb, 3);
+    skip_bits(&gb, 5); // reserved
+
+    get_leb(&gb); // codec_config_id
+    num_substreams = get_leb(&gb);
+    for (unsigned i = 0; i < num_substreams; i++) {
+        unsigned *audio_substream_id = av_dynarray2_add((void **)&c->ids, &c->nb_ids,
+                                                        sizeof(*c->ids), NULL);
+        if (!audio_substream_id)
+            return AVERROR(ENOMEM);
+
+        *audio_substream_id = get_leb(&gb);
+    }
+
+    num_parameters = get_leb(&gb);
+    if (num_parameters && audio_element_type != 0) {
+        av_log(ctx, AV_LOG_ERROR, "Audio Element parameter count %u is invalid"
+                                  " for Scene representations\n", num_parameters);
+        return AVERROR_INVALIDDATA;
+    }
+
+    for (int i = 0; i < num_parameters; i++) {
+        unsigned type = get_leb(&gb);
+
+        if (type == AV_IAMF_PARAMETER_DEFINITION_MIX_GAIN)
+            return AVERROR_INVALIDDATA;
+        else if (type == AV_IAMF_PARAMETER_DEFINITION_DEMIXING) {
+            ret = param_parse(ctx, &gb, type, NULL);
+            if (ret < 0)
+                return ret;
+            skip_bits(&gb, 8); // default_w
+        } else if (type == AV_IAMF_PARAMETER_DEFINITION_RECON_GAIN) {
+            ret = param_parse(ctx, &gb, type, &recon_gain);
+            if (ret < 0)
+                return ret;
+        } else {
+            unsigned param_definition_size = get_leb(&gb);
+            skip_bits_long(&gb, param_definition_size * 8);
+        }
+    }
+
+    if (audio_element_type == AV_IAMF_AUDIO_ELEMENT_TYPE_CHANNEL) {
+        ret = scalable_channel_layout_config(ctx, &gb, recon_gain);
+        if (ret < 0)
+            return ret;
+    }
+
+    return 0;
+}
+
+static int label_string(GetBitContext *gb)
+{
+    int byte;
+
+    do {
+        byte = get_bits(gb, 8);
+    } while (byte);
+
+    return 0;
+}
+
+static int mix_presentation_obu(AVBSFContext *ctx, uint8_t *buf, unsigned size)
+{
+    GetBitContext gb;
+    unsigned mix_presentation_id, count_label;
+    unsigned nb_submixes, nb_elements;
+    int ret;
+
+    ret = init_get_bits8(&gb, buf, size);
+    if (ret < 0)
+        return ret;
+
+    mix_presentation_id = get_leb(&gb);
+    count_label = get_leb(&gb);
+
+    for (int i = 0; i < count_label; i++) {
+        ret = label_string(&gb);
+        if (ret < 0)
+            return ret;
+    }
+
+    for (int i = 0; i < count_label; i++) {
+        ret = label_string(&gb);
+        if (ret < 0)
+            return ret;
+    }
+
+    nb_submixes = get_leb(&gb);
+    for (int i = 0; i < nb_submixes; i++) {
+        unsigned nb_layouts;
+
+        nb_elements = get_leb(&gb);
+
+        for (int j = 0; j < nb_elements; j++) {
+            unsigned rendering_config_extension_size;
+
+            get_leb(&gb); // audio_element_id
+            for (int k = 0; k < count_label; k++) {
+                ret = label_string(&gb);
+                if (ret < 0)
+                    return ret;
+            }
+
+            skip_bits(&gb, 8); // headphones_rendering_mode & reserved
+            rendering_config_extension_size = get_leb(&gb);
+            skip_bits_long(&gb, rendering_config_extension_size * 8);
+
+            ret = param_parse(ctx, &gb, AV_IAMF_PARAMETER_DEFINITION_MIX_GAIN, NULL);
+            if (ret < 0)
+                return ret;
+            skip_bits(&gb, 16); // default_mix_gain
+        }
+
+        ret = param_parse(ctx, &gb, AV_IAMF_PARAMETER_DEFINITION_MIX_GAIN, NULL);
+        if (ret < 0)
+            return ret;
+        get_bits(&gb, 16); // default_mix_gain
+
+        nb_layouts = get_leb(&gb);
+        for (int j = 0; j < nb_layouts; j++) {
+            int info_type, layout_type;
+            int byte = get_bits(&gb, 8);
+
+            layout_type = byte >> 6;
+            if (layout_type < AV_IAMF_SUBMIX_LAYOUT_TYPE_LOUDSPEAKERS &&
+                layout_type > AV_IAMF_SUBMIX_LAYOUT_TYPE_BINAURAL) {
+                av_log(ctx, AV_LOG_ERROR, "Invalid Layout type %u in a submix from "
+                                          "Mix Presentation %u\n",
+                       layout_type, mix_presentation_id);
+                return AVERROR_INVALIDDATA;
+            }
+
+            info_type = get_bits(&gb, 8);
+            get_bits(&gb, 16); // integrated_loudness
+            get_bits(&gb, 16); // digital_peak
+
+            if (info_type & 1)
+                get_bits(&gb, 16); // true_peak
+
+            if (info_type & 2) {
+                unsigned int num_anchored_loudness = get_bits(&gb, 8);
+
+                for (int k = 0; k < num_anchored_loudness; k++) {
+                    get_bits(&gb, 8); // anchor_element
+                    get_bits(&gb, 16); // anchored_loudness
+                }
+            }
+
+            if (info_type & 0xFC) {
+                unsigned int info_type_size = get_leb(&gb);
+                skip_bits_long(&gb, info_type_size * 8);
+            }
+        }
+    }
+
+    return 0;
+}
+
+static int find_idx_by_id(AVBSFContext *ctx, unsigned id)
+{
+    IAMFSplitContext *const c = ctx->priv_data;
+
+    for (int i = 0; i < c->nb_ids; i++)
+        if (c->ids[i] == id)
+            return i;
+
+    av_log(ctx, AV_LOG_ERROR, "Invalid id %d\n", id);
+    return AVERROR_INVALIDDATA;
+}
+
+static int audio_frame_obu(AVBSFContext *ctx, enum IAMF_OBU_Type type, int *idx,
+                           uint8_t *buf, int *start_pos, unsigned *size,
+                           int id_in_bitstream)
+{
+    GetBitContext gb;
+    unsigned audio_substream_id;
+    int ret;
+
+    ret = init_get_bits8(&gb, buf + *start_pos, *size);
+    if (ret < 0)
+        return ret;
+
+    if (id_in_bitstream) {
+        int pos;
+        audio_substream_id = get_leb(&gb);
+        pos = get_bits_count(&gb) / 8;
+        *start_pos += pos;
+        *size -= pos;
+    } else
+        audio_substream_id = type - IAMF_OBU_IA_AUDIO_FRAME_ID0;
+
+    ret = find_idx_by_id(ctx, audio_substream_id);
+    if (ret < 0)
+        return ret;
+
+    *idx = ret;
+
+    return 0;
+}
+
+static const ParamDefinition *get_param_definition(AVBSFContext *ctx,
+                                                   unsigned int parameter_id)
+{
+    const IAMFSplitContext *const c = ctx->priv_data;
+    const ParamDefinition *param_definition = NULL;
+
+    for (int i = 0; i < c->nb_param_definitions; i++)
+        if (c->param_definitions[i].param->parameter_id == parameter_id) {
+            param_definition = &c->param_definitions[i];
+            break;
+        }
+
+    return param_definition;
+}
+
+static int parameter_block_obu(AVBSFContext *ctx, uint8_t *buf, unsigned size)
+{
+    IAMFSplitContext *const c = ctx->priv_data;
+    GetBitContext gb;
+    const ParamDefinition *param_definition;
+    const AVIAMFParamDefinition *param;
+    AVIAMFParamDefinition *out_param = NULL;
+    unsigned int duration, constant_subblock_duration;
+    unsigned int nb_subblocks;
+    unsigned int parameter_id;
+    size_t out_param_size;
+    int ret;
+
+    ret = init_get_bits8(&gb, buf, size);
+    if (ret < 0)
+        return ret;
+
+    parameter_id = get_leb(&gb);
+
+    param_definition = get_param_definition(ctx, parameter_id);
+    if (!param_definition) {
+        ret = 0;
+        goto fail;
+    }
+
+    param = param_definition->param;
+    if (!param_definition->mode) {
+        duration = get_leb(&gb);
+        constant_subblock_duration = get_leb(&gb);
+        if (constant_subblock_duration == 0)
+            nb_subblocks = get_leb(&gb);
+        else
+            nb_subblocks = duration / constant_subblock_duration;
+    } else {
+        duration = param->duration;
+        constant_subblock_duration = param->constant_subblock_duration;
+        nb_subblocks = param->nb_subblocks;
+        if (!nb_subblocks)
+            nb_subblocks = duration / constant_subblock_duration;
+    }
+
+    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 = get_leb(&gb);
+
+        switch (param->type) {
+        case AV_IAMF_PARAMETER_DEFINITION_MIX_GAIN: {
+            AVIAMFMixGain *mix = subblock;
+
+            mix->animation_type = get_leb(&gb);
+            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(get_bits(&gb, 16), 16), 1 << 8);
+            if (mix->animation_type >= AV_IAMF_ANIMATION_TYPE_LINEAR)
+                mix->end_point_value =
+                    av_make_q(sign_extend(get_bits(&gb, 16), 16), 1 << 8);
+            if (mix->animation_type == AV_IAMF_ANIMATION_TYPE_BEZIER) {
+                mix->control_point_value =
+                    av_make_q(sign_extend(get_bits(&gb, 16), 16), 1 << 8);
+                mix->control_point_relative_time =
+                    av_make_q(get_bits(&gb, 8), 1 << 8);
+            }
+            mix->subblock_duration = subblock_duration;
+            break;
+        }
+        case AV_IAMF_PARAMETER_DEFINITION_DEMIXING: {
+            AVIAMFDemixingInfo *demix = subblock;
+
+            demix->dmixp_mode = get_bits(&gb, 3);
+            skip_bits(&gb, 5); // reserved
+            demix->subblock_duration = subblock_duration;
+            break;
+        }
+        case AV_IAMF_PARAMETER_DEFINITION_RECON_GAIN: {
+            AVIAMFReconGain *recon = subblock;
+
+            for (int i = 0; i < 6; i++) {
+                if (param_definition->recon_gain_present_bitmask & (1 << i)) {
+                    unsigned int recon_gain_flags = get_leb(&gb);
+                    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] = get_bits(&gb, 8);
+                    }
+                }
+            }
+            recon->subblock_duration = subblock_duration;
+            break;
+        }
+        default:
+            av_assert0(0);
+        }
+    }
+
+    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);
+
+    return ret;
+}
+
+static int iamf_parse_obu_header(const uint8_t *buf, int buf_size,
+                                 unsigned *obu_size, int *start_pos,
+                                 enum IAMF_OBU_Type *type,
+                                 unsigned *skip, unsigned *discard)
+{
+    GetBitContext gb;
+    int ret, extension_flag, trimming, start;
+    unsigned size;
+
+    ret = init_get_bits8(&gb, buf, FFMIN(buf_size, MAX_IAMF_OBU_HEADER_SIZE));
+    if (ret < 0)
+        return ret;
+
+    *type          = get_bits(&gb, 5);
+    /*redundant      =*/ get_bits1(&gb);
+    trimming       = get_bits1(&gb);
+    extension_flag = get_bits1(&gb);
+
+    *obu_size = get_leb(&gb);
+    if (*obu_size > INT_MAX)
+        return AVERROR_INVALIDDATA;
+
+    start = get_bits_count(&gb) / 8;
+
+    if (trimming) {
+        *skip = get_leb(&gb); // num_samples_to_trim_at_end
+        *discard = get_leb(&gb); // num_samples_to_trim_at_start
+    }
+
+    if (extension_flag) {
+        unsigned extension_bytes = get_leb(&gb);
+        if (extension_bytes > INT_MAX / 8)
+            return AVERROR_INVALIDDATA;
+        skip_bits_long(&gb, extension_bytes * 8);
+    }
+
+    if (get_bits_left(&gb) < 0)
+        return AVERROR_INVALIDDATA;
+
+    size = *obu_size + start;
+    if (size > INT_MAX)
+        return AVERROR_INVALIDDATA;
+
+    *obu_size -= get_bits_count(&gb) / 8 - start;
+    *start_pos = size - *obu_size;
+
+    return size;
+}
+
+static int iamf_frame_split_filter(AVBSFContext *ctx, AVPacket *out)
+{
+    IAMFSplitContext *const c = ctx->priv_data;
+    int ret = 0;
+
+    if (!c->buffer_pkt->data) {
+        ret = ff_bsf_get_packet_ref(ctx, c->buffer_pkt);
+        if (ret < 0)
+            return ret;
+    }
+
+    while (1) {
+        enum IAMF_OBU_Type type;
+        unsigned skip_samples = 0, discard_padding = 0, obu_size;
+        int len, start_pos, idx;
+
+        len = iamf_parse_obu_header(c->buffer_pkt->data,
+                                    c->buffer_pkt->size,
+                                    &obu_size, &start_pos, &type,
+                                    &skip_samples, &discard_padding);
+        if (len < 0) {
+            av_log(ctx, AV_LOG_ERROR, "Failed to read obu\n");
+            ret = len;
+            goto fail;
+        }
+
+        if (type >= IAMF_OBU_IA_AUDIO_FRAME &&
+            type <= IAMF_OBU_IA_AUDIO_FRAME_ID17) {
+            ret = audio_frame_obu(ctx, type, &idx,
+                                  c->buffer_pkt->data, &start_pos,
+                                  &obu_size,
+                                  type == IAMF_OBU_IA_AUDIO_FRAME);
+            if (ret < 0)
+                goto fail;
+        } else {
+            switch (type) {
+            case IAMF_OBU_IA_PARAMETER_BLOCK:
+                ret = parameter_block_obu(ctx, c->buffer_pkt->data + start_pos, obu_size);
+                if (ret < 0)
+                    goto fail;
+                break;
+            case IAMF_OBU_IA_SEQUENCE_HEADER:
+                for (int i = 0; c->param_definitions && i < c->nb_param_definitions; i++)
+                    av_free(c->param_definitions[i].param);
+                av_freep(&c->param_definitions);
+                av_freep(&c->ids);
+                c->nb_param_definitions = 0;
+                c->nb_ids = 0;
+                // fall-through
+            case IAMF_OBU_IA_TEMPORAL_DELIMITER:
+                av_freep(&c->mix);
+                av_freep(&c->demix);
+                av_freep(&c->recon);
+                c->mix_size = 0;
+                c->demix_size = 0;
+                c->recon_size = 0;
+                break;
+            case IAMF_OBU_IA_AUDIO_ELEMENT:
+                if (!c->inband_descriptors)
+                    break;
+                ret = audio_element_obu(ctx, c->buffer_pkt->data + start_pos, obu_size);
+                if (ret < 0) {
+                    av_log(ctx, AV_LOG_ERROR, "Failed to read Audio Element OBU\n");
+                    return ret;
+                }
+                break;
+            case IAMF_OBU_IA_MIX_PRESENTATION:
+                if (!c->inband_descriptors)
+                    break;
+                ret = mix_presentation_obu(ctx, c->buffer_pkt->data + start_pos, obu_size);
+                if (ret < 0) {
+                    av_log(ctx, AV_LOG_ERROR, "Failed to read Mix Presentation OBU\n");
+                    return ret;
+                }
+                break;
+            }
+
+            c->buffer_pkt->data += len;
+            c->buffer_pkt->size -= len;
+
+            if (!c->buffer_pkt->size) {
+                av_packet_unref(c->buffer_pkt);
+                ret = ff_bsf_get_packet_ref(ctx, c->buffer_pkt);
+                if (ret < 0)
+                    return ret;
+            } else if (c->buffer_pkt->size < 0) {
+                ret = AVERROR_INVALIDDATA;
+                goto fail;
+            }
+            continue;
+        }
+
+        ret = av_packet_ref(out, c->buffer_pkt);
+        if (ret < 0)
+            goto fail;
+
+        if (skip_samples || discard_padding) {
+            uint8_t *side_data = av_packet_new_side_data(out,
+                                                         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(out,
+                                                         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(out,
+                                                         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(out,
+                                                         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);
+        }
+
+        out->data += start_pos;
+        out->size = obu_size;
+        out->stream_index = idx + c->first_index;
+
+        c->buffer_pkt->data += len;
+        c->buffer_pkt->size -= len;
+
+        if (!c->buffer_pkt->size)
+            av_packet_unref(c->buffer_pkt);
+        else if (c->buffer_pkt->size < 0) {
+            ret = AVERROR_INVALIDDATA;
+            goto fail;
+        }
+
+        return 0;
+    }
+
+fail:
+    if (ret < 0) {
+        av_packet_unref(out);
+        av_packet_unref(c->buffer_pkt);
+    }
+
+    return ret;
+}
+
+static int iamf_frame_split_init(AVBSFContext *ctx)
+{
+    IAMFSplitContext *const c = ctx->priv_data;
+    uint8_t *extradata;
+    int extradata_size;
+
+    c->buffer_pkt = av_packet_alloc();
+    if (!c->buffer_pkt)
+        return AVERROR(ENOMEM);
+
+    if (!c->inband_descriptors && !ctx->par_in->extradata_size) {
+        av_log(ctx, AV_LOG_ERROR, "Missing extradata\n");
+        return AVERROR_INVALIDDATA;
+    }
+
+    extradata = ctx->par_in->extradata;
+    extradata_size = ctx->par_in->extradata_size;
+
+    while (extradata_size) {
+        enum IAMF_OBU_Type type;
+        unsigned skip_samples = 0, discard_padding = 0, obu_size;
+        int ret, len, start_pos;
+
+        len = iamf_parse_obu_header(extradata,
+                                    extradata_size,
+                                    &obu_size, &start_pos, &type,
+                                    &skip_samples, &discard_padding);
+        if (len < 0) {
+            av_log(ctx, AV_LOG_ERROR, "Failed to read descriptors\n");
+            return len;
+        }
+
+        switch (type) {
+        case IAMF_OBU_IA_SEQUENCE_HEADER:
+            for (int i = 0; c->param_definitions && i < c->nb_param_definitions; i++)
+                av_free(c->param_definitions[i].param);
+            av_freep(&c->param_definitions);
+            av_freep(&c->ids);
+            c->nb_param_definitions = 0;
+            c->nb_ids = 0;
+            break;
+        case IAMF_OBU_IA_AUDIO_ELEMENT:
+            ret = audio_element_obu(ctx, extradata + start_pos, obu_size);
+            if (ret < 0) {
+                av_log(ctx, AV_LOG_ERROR, "Failed to read Audio Element OBU\n");
+                return ret;
+            }
+            break;
+        case IAMF_OBU_IA_MIX_PRESENTATION:
+            ret = mix_presentation_obu(ctx, extradata + start_pos, obu_size);
+            if (ret < 0) {
+                av_log(ctx, AV_LOG_ERROR, "Failed to read Mix Presentation OBU\n");
+                return ret;
+            }
+            break;
+        }
+
+        extradata += len;
+        extradata_size -= len;
+
+        if (extradata_size < 0)
+            return AVERROR_INVALIDDATA;
+    }
+
+    av_freep(&ctx->par_out->extradata);
+    ctx->par_out->extradata_size = 0;
+
+    return 0;
+}
+
+static void iamf_frame_split_flush(AVBSFContext *ctx)
+{
+    IAMFSplitContext *const c = ctx->priv_data;
+
+    if (c->buffer_pkt)
+        av_packet_unref(c->buffer_pkt);
+
+    av_freep(&c->mix);
+    av_freep(&c->demix);
+    av_freep(&c->recon);
+    c->mix_size = 0;
+    c->demix_size = 0;
+    c->recon_size = 0;
+}
+
+static void iamf_frame_split_close(AVBSFContext *ctx)
+{
+    IAMFSplitContext *const c = ctx->priv_data;
+
+    iamf_frame_split_flush(ctx);
+    av_packet_free(&c->buffer_pkt);
+
+    for (int i = 0; c->param_definitions && i < c->nb_param_definitions; i++)
+        av_free(c->param_definitions[i].param);
+    av_freep(&c->param_definitions);
+    c->nb_param_definitions = 0;
+
+    av_freep(&c->ids);
+    c->nb_ids = 0;
+}
+
+#define OFFSET(x) offsetof(IAMFSplitContext, x)
+#define FLAGS (AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_BSF_PARAM)
+static const AVOption iamf_frame_split_options[] = {
+    { "first_index", "First index to set stream index in output packets",
+        OFFSET(first_index), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, FLAGS },
+    { "inband_descriptors", "Parse inband descriptor OBUs",
+        OFFSET(inband_descriptors), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, FLAGS },
+    { NULL }
+};
+
+static const AVClass iamf_frame_split_class = {
+    .class_name = "iamf_frame_split_bsf",
+    .item_name  = av_default_item_name,
+    .option     = iamf_frame_split_options,
+    .version    = LIBAVUTIL_VERSION_INT,
+};
+
+const FFBitStreamFilter ff_iamf_frame_split_bsf = {
+    .p.name         = "iamf_frame_split",
+    .p.priv_class   = &iamf_frame_split_class,
+    .priv_data_size = sizeof(IAMFSplitContext),
+    .init           = iamf_frame_split_init,
+    .flush          = iamf_frame_split_flush,
+    .close          = iamf_frame_split_close,
+    .filter         = iamf_frame_split_filter,
+};
-- 
2.43.0

_______________________________________________
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] 17+ messages in thread

* [FFmpeg-devel] [PATCH 2/7 v4] avformat/demux: support inserting bitstream filters in demuxing scenarios
  2024-02-06 13:05 [FFmpeg-devel] [PATCH 1/7 v4] avcodec: add an Immersive Audio Model and Formats frame split bsf James Almer
@ 2024-02-06 13:05 ` James Almer
  2024-02-06 13:05 ` [FFmpeg-devel] [PATCH 3/7 v4] avformat/mov: make MOVStreamContext refcounted James Almer
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 17+ messages in thread
From: James Almer @ 2024-02-06 13:05 UTC (permalink / raw)
  To: ffmpeg-devel

Packets will be passed to the bsf immediately after being generated by a
demuxer, and no further data will be read from the input until all packets
have been returned by the bsf.

Signed-off-by: James Almer <jamrial@gmail.com>
---
No changes since last version

 libavformat/avformat.c |  47 ++++++++++++
 libavformat/demux.c    | 162 ++++++++++++++++++++++++++++++-----------
 libavformat/internal.h |  13 +++-
 libavformat/mux.c      |  43 -----------
 libavformat/mux.h      |  11 ---
 libavformat/rawenc.c   |   1 +
 6 files changed, 181 insertions(+), 96 deletions(-)

diff --git a/libavformat/avformat.c b/libavformat/avformat.c
index 8e8c6fbe55..0e22d47c8b 100644
--- a/libavformat/avformat.c
+++ b/libavformat/avformat.c
@@ -931,3 +931,50 @@ FF_ENABLE_DEPRECATION_WARNINGS
     *pb = NULL;
     return ret;
 }
+
+int ff_stream_add_bitstream_filter(AVStream *st, const char *name, const char *args)
+{
+    int ret;
+    const AVBitStreamFilter *bsf;
+    FFStream *const sti = ffstream(st);
+    AVBSFContext *bsfc;
+
+    av_assert0(!sti->bsfc);
+
+    if (name) {
+        bsf = av_bsf_get_by_name(name);
+        if (!bsf) {
+            av_log(NULL, AV_LOG_ERROR, "Unknown bitstream filter '%s'\n", name);
+            return AVERROR_BSF_NOT_FOUND;
+        }
+        ret = av_bsf_alloc(bsf, &bsfc);
+    } else
+        ret = av_bsf_get_null_filter(&bsfc);
+    if (ret < 0)
+        return ret;
+
+    bsfc->time_base_in = st->time_base;
+    if ((ret = avcodec_parameters_copy(bsfc->par_in, st->codecpar)) < 0) {
+        av_bsf_free(&bsfc);
+        return ret;
+    }
+
+    if (args && bsfc->filter->priv_class) {
+        if ((ret = av_set_options_string(bsfc->priv_data, args, "=", ":")) < 0) {
+            av_bsf_free(&bsfc);
+            return ret;
+        }
+    }
+
+    if ((ret = av_bsf_init(bsfc)) < 0) {
+        av_bsf_free(&bsfc);
+        return ret;
+    }
+
+    sti->bsfc = bsfc;
+
+    av_log(NULL, AV_LOG_VERBOSE,
+           "Automatically inserted bitstream filter '%s'; args='%s'\n",
+           name, args ? args : "");
+    return 1;
+}
diff --git a/libavformat/demux.c b/libavformat/demux.c
index 6f640b92b1..fb9bf9e4ac 100644
--- a/libavformat/demux.c
+++ b/libavformat/demux.c
@@ -540,6 +540,109 @@ 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);
+}
+
+static int filter_packet(AVFormatContext *s, AVStream *st, AVPacket *pkt)
+{
+    FFFormatContext *const si = ffformatcontext(s);
+    FFStream *const sti = ffstream(st);
+    const AVPacket *pkt1;
+    int err;
+
+    if (!sti->bsfc) {
+        const PacketListEntry *pktl = si->raw_packet_buffer.head;
+        if (AVPACKET_IS_EMPTY(pkt))
+            return 0;
+
+        update_timestamps(s, st, pkt);
+
+        if (!pktl && sti->request_probe <= 0)
+            return 0;
+
+        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;
+
+        return probe_codec(s, s->streams[pkt1->stream_index], pkt1);
+    }
+
+    err = av_bsf_send_packet(sti->bsfc, pkt);
+    if (err < 0) {
+        av_log(s, AV_LOG_ERROR,
+                "Failed to send packet to filter %s for stream %d\n",
+                sti->bsfc->filter->name, st->index);
+        return err;
+    }
+
+    do {
+        AVStream *out_st;
+        FFStream *out_sti;
+
+        err = av_bsf_receive_packet(sti->bsfc, pkt);
+        if (err < 0) {
+            if (err == AVERROR(EAGAIN) || err == AVERROR_EOF)
+                return 0;
+            av_log(s, AV_LOG_ERROR, "Error applying bitstream filters to an output "
+                   "packet for stream #%d: %s\n", st->index, av_err2str(err));
+            if (!(s->error_recognition & AV_EF_EXPLODE) && err != AVERROR(ENOMEM))
+                continue;
+            return err;
+        }
+        out_st = s->streams[pkt->stream_index];
+        out_sti = ffstream(out_st);
+
+        update_timestamps(s, out_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 (out_sti->request_probe <= 0)
+            continue;
+
+        err = probe_codec(s, out_st, pkt1);
+        if (err < 0)
+            return err;
+    } while (1);
+
+    return 0;
+}
+
 int ff_read_packet(AVFormatContext *s, AVPacket *pkt)
 {
     FFFormatContext *const si = ffformatcontext(s);
@@ -557,9 +660,6 @@ FF_ENABLE_DEPRECATION_WARNINGS
 
     for (;;) {
         PacketListEntry *pktl = si->raw_packet_buffer.head;
-        AVStream *st;
-        FFStream *sti;
-        const AVPacket *pkt1;
 
         if (pktl) {
             AVStream *const st = s->streams[pktl->pkt.stream_index];
@@ -582,16 +682,27 @@ FF_ENABLE_DEPRECATION_WARNINGS
                We must re-call the demuxer to get the real packet. */
             if (err == FFERROR_REDO)
                 continue;
-            if (!pktl || err == AVERROR(EAGAIN))
+            if (err == AVERROR(EAGAIN))
                 return err;
             for (unsigned i = 0; i < s->nb_streams; i++) {
                 AVStream *const st  = s->streams[i];
                 FFStream *const sti = ffstream(st);
+                int ret;
+
+                // Drain buffered packets in the bsf context on eof
+                if (err == AVERROR_EOF)
+                    if ((ret = filter_packet(s, st, pkt)) < 0)
+                        return ret;
+                pktl = si->raw_packet_buffer.head;
+                if (!pktl)
+                    continue;
                 if (sti->probe_packets || sti->request_probe > 0)
-                    if ((err = probe_codec(s, st, NULL)) < 0)
-                        return err;
+                    if ((ret = probe_codec(s, st, NULL)) < 0)
+                        return ret;
                 av_assert0(sti->request_probe <= 0);
             }
+            if (!pktl)
+                return err;
             continue;
         }
 
@@ -616,42 +727,11 @@ FF_ENABLE_DEPRECATION_WARNINGS
         av_assert0(pkt->stream_index < (unsigned)s->nb_streams &&
                    "Invalid stream index.\n");
 
-        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);
-
-        if (!pktl && sti->request_probe <= 0)
-            return 0;
-
-        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 ((err = probe_codec(s, st, pkt1)) < 0)
+        err = filter_packet(s, s->streams[pkt->stream_index], pkt);
+        if (err < 0)
             return err;
+        if (!AVPACKET_IS_EMPTY(pkt))
+            return 0;
     }
 }
 
diff --git a/libavformat/internal.h b/libavformat/internal.h
index f93832b3c4..c2738a420f 100644
--- a/libavformat/internal.h
+++ b/libavformat/internal.h
@@ -212,7 +212,7 @@ typedef struct FFStream {
     /**
      * bitstream filter to run on stream
      * - encoding: Set by muxer using ff_stream_add_bitstream_filter
-     * - decoding: unused
+     * - decoding: Set by demuxer using ff_stream_add_bitstream_filter
      */
     struct AVBSFContext *bsfc;
 
@@ -752,4 +752,15 @@ int ff_match_url_ext(const char *url, const char *extensions);
 struct FFOutputFormat;
 void avpriv_register_devices(const struct FFOutputFormat * const o[], const AVInputFormat * const i[]);
 
+/**
+ * Add a bitstream filter to a stream.
+ *
+ * @param st output stream to add a filter to
+ * @param name the name of the filter to add
+ * @param args filter-specific argument string
+ * @return  >0 on success;
+ *          AVERROR code on failure
+ */
+int ff_stream_add_bitstream_filter(AVStream *st, const char *name, const char *args);
+
 #endif /* AVFORMAT_INTERNAL_H */
diff --git a/libavformat/mux.c b/libavformat/mux.c
index de10d2c008..4bc8627617 100644
--- a/libavformat/mux.c
+++ b/libavformat/mux.c
@@ -1344,49 +1344,6 @@ int av_get_output_timestamp(struct AVFormatContext *s, int stream,
     return 0;
 }
 
-int ff_stream_add_bitstream_filter(AVStream *st, const char *name, const char *args)
-{
-    int ret;
-    const AVBitStreamFilter *bsf;
-    FFStream *const sti = ffstream(st);
-    AVBSFContext *bsfc;
-
-    av_assert0(!sti->bsfc);
-
-    if (!(bsf = av_bsf_get_by_name(name))) {
-        av_log(NULL, AV_LOG_ERROR, "Unknown bitstream filter '%s'\n", name);
-        return AVERROR_BSF_NOT_FOUND;
-    }
-
-    if ((ret = av_bsf_alloc(bsf, &bsfc)) < 0)
-        return ret;
-
-    bsfc->time_base_in = st->time_base;
-    if ((ret = avcodec_parameters_copy(bsfc->par_in, st->codecpar)) < 0) {
-        av_bsf_free(&bsfc);
-        return ret;
-    }
-
-    if (args && bsfc->filter->priv_class) {
-        if ((ret = av_set_options_string(bsfc->priv_data, args, "=", ":")) < 0) {
-            av_bsf_free(&bsfc);
-            return ret;
-        }
-    }
-
-    if ((ret = av_bsf_init(bsfc)) < 0) {
-        av_bsf_free(&bsfc);
-        return ret;
-    }
-
-    sti->bsfc = bsfc;
-
-    av_log(NULL, AV_LOG_VERBOSE,
-           "Automatically inserted bitstream filter '%s'; args='%s'\n",
-           name, args ? args : "");
-    return 1;
-}
-
 int ff_write_chained(AVFormatContext *dst, int dst_stream, AVPacket *pkt,
                      AVFormatContext *src, int interleave)
 {
diff --git a/libavformat/mux.h b/libavformat/mux.h
index b9ec75641d..ab3e8edd60 100644
--- a/libavformat/mux.h
+++ b/libavformat/mux.h
@@ -171,17 +171,6 @@ const AVPacket *ff_interleaved_peek(AVFormatContext *s, int stream);
 
 int ff_get_muxer_ts_offset(AVFormatContext *s, int stream_index, int64_t *offset);
 
-/**
- * Add a bitstream filter to a stream.
- *
- * @param st output stream to add a filter to
- * @param name the name of the filter to add
- * @param args filter-specific argument string
- * @return  >0 on success;
- *          AVERROR code on failure
- */
-int ff_stream_add_bitstream_filter(AVStream *st, const char *name, const char *args);
-
 /**
  * Write a packet to another muxer than the one the user originally
  * intended. Useful when chaining muxers, where one muxer internally
diff --git a/libavformat/rawenc.c b/libavformat/rawenc.c
index f916db13a2..ec31d76d88 100644
--- a/libavformat/rawenc.c
+++ b/libavformat/rawenc.c
@@ -25,6 +25,7 @@
 #include "libavutil/intreadwrite.h"
 
 #include "avformat.h"
+#include "internal.h"
 #include "rawenc.h"
 #include "mux.h"
 
-- 
2.43.0

_______________________________________________
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] 17+ messages in thread

* [FFmpeg-devel] [PATCH 3/7 v4] avformat/mov: make MOVStreamContext refcounted
  2024-02-06 13:05 [FFmpeg-devel] [PATCH 1/7 v4] avcodec: add an Immersive Audio Model and Formats frame split bsf James Almer
  2024-02-06 13:05 ` [FFmpeg-devel] [PATCH 2/7 v4] avformat/demux: support inserting bitstream filters in demuxing scenarios James Almer
@ 2024-02-06 13:05 ` James Almer
  2024-02-06 13:05 ` [FFmpeg-devel] [PATCH 4/7 v4] avformat/mov: add support for Immersive Audio Model and Formats in ISOBMFF James Almer
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 17+ messages in thread
From: James Almer @ 2024-02-06 13:05 UTC (permalink / raw)
  To: ffmpeg-devel

This will be useful in the next commit.

Signed-off-by: James Almer <jamrial@gmail.com>
---
No changes since last version

 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 42b0135987..8ecc4869bb 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.0

_______________________________________________
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] 17+ messages in thread

* [FFmpeg-devel] [PATCH 4/7 v4] avformat/mov: add support for Immersive Audio Model and Formats in ISOBMFF
  2024-02-06 13:05 [FFmpeg-devel] [PATCH 1/7 v4] avcodec: add an Immersive Audio Model and Formats frame split bsf James Almer
  2024-02-06 13:05 ` [FFmpeg-devel] [PATCH 2/7 v4] avformat/demux: support inserting bitstream filters in demuxing scenarios James Almer
  2024-02-06 13:05 ` [FFmpeg-devel] [PATCH 3/7 v4] avformat/mov: make MOVStreamContext refcounted James Almer
@ 2024-02-06 13:05 ` James Almer
  2024-02-06 13:05 ` [FFmpeg-devel] [PATCH 5/7 v3] avcodec: add an Immersive Audio Model and Formats frame merge bsf James Almer
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 17+ messages in thread
From: James Almer @ 2024-02-06 13:05 UTC (permalink / raw)
  To: ffmpeg-devel

Signed-off-by: James Almer <jamrial@gmail.com>
---
Now passing descriptors to iamf_frame_split as extradata.

 configure            |   2 +-
 libavformat/Makefile |   3 +-
 libavformat/isom.h   |   3 +
 libavformat/mov.c    | 274 ++++++++++++++++++++++++++++++++++++++++---
 4 files changed, 265 insertions(+), 17 deletions(-)

diff --git a/configure b/configure
index f72533b7d2..567a39b242 100755
--- a/configure
+++ b/configure
@@ -3548,7 +3548,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 iamf_frame_split_bsf"
 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/Makefile b/libavformat/Makefile
index 05b9b8a115..ab264644c6 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -364,7 +364,8 @@ OBJS-$(CONFIG_MMF_MUXER)                 += mmf.o rawenc.o
 OBJS-$(CONFIG_MODS_DEMUXER)              += mods.o
 OBJS-$(CONFIG_MOFLEX_DEMUXER)            += moflex.o
 OBJS-$(CONFIG_MOV_DEMUXER)               += mov.o mov_chan.o mov_esds.o \
-                                            qtpalette.o replaygain.o dovi_isom.o
+                                            qtpalette.o replaygain.o dovi_isom.o \
+                                            iamf.o iamf_parse.o
 OBJS-$(CONFIG_MOV_MUXER)                 += movenc.o av1.o avc.o hevc.o vvc.o vpcc.o \
                                             movenchint.o mov_chan.o rtp.o \
                                             movenccenc.o movenc_ttml.o rawutils.o \
diff --git a/libavformat/isom.h b/libavformat/isom.h
index eee94d0449..5d74edce77 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 IAMFContext *iamf;
 } MOVStreamContext;
 
 typedef struct HEIFItem {
diff --git a/libavformat/mov.c b/libavformat/mov.c
index 8ecc4869bb..ad316e49c7 100644
--- a/libavformat/mov.c
+++ b/libavformat/mov.c
@@ -58,6 +58,7 @@
 #include "internal.h"
 #include "avio_internal.h"
 #include "demux.h"
+#include "iamf_parse.h"
 #include "dovi_isom.h"
 #include "riff.h"
 #include "isom.h"
@@ -212,6 +213,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 +838,181 @@ 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;
+    char args[128];
+    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;
+
+    iamf = sc->iamf = av_mallocz(sizeof(*iamf));
+    if (!iamf)
+        return AVERROR(ENOMEM);
+
+    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;
+
+    snprintf(args, sizeof(args), "first_index=%d:inband_descriptors=false", st->index);
+
+    ret = ff_stream_add_bitstream_filter(st, "iamf_frame_split", args);
+    if (ret == AVERROR_BSF_NOT_FOUND) {
+        av_log(c->fc, AV_LOG_ERROR, "iamf_frame_split bitstream filter "
+               "not found. This is a bug, please report it.\n");
+        ret = AVERROR_BUG;
+        goto fail;
+    }
+
+    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;
+        }
+
+        stg->id = audio_element->audio_element_id;
+        stg->params.iamf_audio_element = audio_element->element;
+        audio_element->element = NULL;
+
+        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;
+        }
+
+        stg->id = mix_presentation->mix_presentation_id;
+        stg->params.iamf_mix_presentation = mix_presentation->mix;
+        mix_presentation->mix = NULL;
+
+        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 +1558,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 +1569,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 +1639,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 +3440,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 +4820,50 @@ 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;
+
+    for (int i = 0; i < sc->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 == sc->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 +4944,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 +5182,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 +5281,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 +5456,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 +5510,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 +5814,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 +6732,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 +7677,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 +8480,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 +8712,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 +8951,9 @@ static void mov_free_stream_context(AVFormatContext *s, AVStream *st)
     av_freep(&sc->mastering);
     av_freep(&sc->coll);
     av_freep(&sc->ambient);
+
+    ff_iamf_uninit_context(sc->iamf);
+    av_freep(&sc->iamf);
 }
 
 static int mov_read_close(AVFormatContext *s)
@@ -8966,9 +9208,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;
-- 
2.43.0

_______________________________________________
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] 17+ messages in thread

* [FFmpeg-devel] [PATCH 5/7 v3] avcodec: add an Immersive Audio Model and Formats frame merge bsf
  2024-02-06 13:05 [FFmpeg-devel] [PATCH 1/7 v4] avcodec: add an Immersive Audio Model and Formats frame split bsf James Almer
                   ` (2 preceding siblings ...)
  2024-02-06 13:05 ` [FFmpeg-devel] [PATCH 4/7 v4] avformat/mov: add support for Immersive Audio Model and Formats in ISOBMFF James Almer
@ 2024-02-06 13:05 ` James Almer
  2024-02-06 13:05 ` [FFmpeg-devel] [PATCH 6/7 v3] avformat/movenc: add support for Immersive Audio Model and Formats in ISOBMFF James Almer
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 17+ messages in thread
From: James Almer @ 2024-02-06 13:05 UTC (permalink / raw)
  To: ffmpeg-devel

Signed-off-by: James Almer <jamrial@gmail.com>
---
No changes since last version

 doc/bitstream_filters.texi            |  14 ++
 libavcodec/bitstream_filters.c        |   1 +
 libavcodec/bsf/Makefile               |   1 +
 libavcodec/bsf/iamf_frame_merge_bsf.c | 228 ++++++++++++++++++++++++++
 libavcodec/leb.h                      |  22 +++
 5 files changed, 266 insertions(+)
 create mode 100644 libavcodec/bsf/iamf_frame_merge_bsf.c

diff --git a/doc/bitstream_filters.texi b/doc/bitstream_filters.texi
index 3d1a5e7a24..2f3077d02c 100644
--- a/doc/bitstream_filters.texi
+++ b/doc/bitstream_filters.texi
@@ -481,6 +481,20 @@ Lowest stream index value to set in output packets
 Enable parsing in-band descriptor OBUs
 @end table
 
+@section iamf_frame_merge
+
+Encapsulate audio data packets from different streams and merge them
+into a single packet containing all Audio Frame OBUs.
+
+@table @option
+@item index_mapping
+A :-separated list of stream_index=audio_substream_id entries to set
+stream id in output Audio Frame OBUs
+
+@item out_index
+Stream index to in output packets
+@end table
+
 @section imxdump
 
 Modifies the bitstream to fit in MOV and to be usable by the Final Cut
diff --git a/libavcodec/bitstream_filters.c b/libavcodec/bitstream_filters.c
index 633187bc6e..a50488ad92 100644
--- a/libavcodec/bitstream_filters.c
+++ b/libavcodec/bitstream_filters.c
@@ -42,6 +42,7 @@ extern const FFBitStreamFilter ff_h264_redundant_pps_bsf;
 extern const FFBitStreamFilter ff_hapqa_extract_bsf;
 extern const FFBitStreamFilter ff_hevc_metadata_bsf;
 extern const FFBitStreamFilter ff_hevc_mp4toannexb_bsf;
+extern const FFBitStreamFilter ff_iamf_frame_merge_bsf;
 extern const FFBitStreamFilter ff_iamf_frame_split_bsf;
 extern const FFBitStreamFilter ff_imx_dump_header_bsf;
 extern const FFBitStreamFilter ff_media100_to_mjpegb_bsf;
diff --git a/libavcodec/bsf/Makefile b/libavcodec/bsf/Makefile
index 738b97bdd1..662c5139bc 100644
--- a/libavcodec/bsf/Makefile
+++ b/libavcodec/bsf/Makefile
@@ -20,6 +20,7 @@ OBJS-$(CONFIG_H264_REDUNDANT_PPS_BSF)     += bsf/h264_redundant_pps.o
 OBJS-$(CONFIG_HAPQA_EXTRACT_BSF)          += bsf/hapqa_extract.o
 OBJS-$(CONFIG_HEVC_METADATA_BSF)          += bsf/h265_metadata.o
 OBJS-$(CONFIG_HEVC_MP4TOANNEXB_BSF)       += bsf/hevc_mp4toannexb.o
+OBJS-$(CONFIG_IAMF_FRAME_MERGE_BSF)       += bsf/iamf_frame_merge_bsf.o
 OBJS-$(CONFIG_IAMF_FRAME_SPLIT_BSF)       += bsf/iamf_frame_split_bsf.o
 OBJS-$(CONFIG_IMX_DUMP_HEADER_BSF)        += bsf/imx_dump_header.o
 OBJS-$(CONFIG_MEDIA100_TO_MJPEGB_BSF)     += bsf/media100_to_mjpegb.o
diff --git a/libavcodec/bsf/iamf_frame_merge_bsf.c b/libavcodec/bsf/iamf_frame_merge_bsf.c
new file mode 100644
index 0000000000..98f37be653
--- /dev/null
+++ b/libavcodec/bsf/iamf_frame_merge_bsf.c
@@ -0,0 +1,228 @@
+/*
+ * 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 <stdint.h>
+#include <stddef.h>
+
+#include "libavutil/dict.h"
+#include "libavutil/fifo.h"
+#include "libavutil/opt.h"
+#include "libavformat/iamf.h"
+#include "bsf.h"
+#include "bsf_internal.h"
+#include "bytestream.h"
+#include "get_bits.h"
+#include "leb.h"
+#include "put_bits.h"
+
+typedef struct IAMFMergeContext {
+    AVClass *class;
+
+    AVFifo *fifo;
+
+    // AVOptions
+    AVDictionary *index_mapping;
+    int stream_count;
+    int out_index;
+} IAMFMergeContext;
+
+static int find_id_from_idx(AVBSFContext *ctx, int idx)
+{
+    IAMFMergeContext *const c = ctx->priv_data;
+    const AVDictionaryEntry *e = NULL;
+
+    while (e = av_dict_iterate(c->index_mapping, e)) {
+        char *endptr = NULL;
+        int id, map_idx = strtol(e->key, &endptr, 0);
+        if (!endptr || *endptr)
+            return AVERROR_INVALIDDATA;
+        endptr = NULL;
+        id = strtol(e->value, &endptr, 0);
+        if (!endptr || *endptr)
+            return AVERROR_INVALIDDATA;
+        if (map_idx == idx)
+            return id;
+    }
+
+    av_log(ctx, AV_LOG_ERROR, "Invalid stream idx %d\n", idx);
+    return AVERROR_INVALIDDATA;
+}
+
+static int iamf_frame_merge_filter(AVBSFContext *ctx, AVPacket *out)
+{
+    IAMFMergeContext *const c = ctx->priv_data;
+    AVPacket *pkt;
+    int ret;
+
+    while (av_fifo_can_write(c->fifo)) {
+        ret = ff_bsf_get_packet(ctx, &pkt);
+        if (ret < 0)
+            return ret;
+        av_fifo_write(c->fifo, &pkt, 1);
+    }
+
+    pkt = NULL;
+    while (av_fifo_can_read(c->fifo)) {
+        PutBitContext pb;
+        PutByteContext p;
+        uint8_t *side_data, header[MAX_IAMF_OBU_HEADER_SIZE], obu[8];
+        unsigned int obu_header;
+        unsigned int skip_samples = 0, discard_padding = 0;
+        size_t side_data_size;
+        int header_size, obu_size, old_out_size = out->size;
+        int id, type;
+
+        av_packet_free(&pkt);
+        av_fifo_read(c->fifo, &pkt, 1);
+        id = find_id_from_idx(ctx, pkt->stream_index);
+        if (id < 0)
+            return AVERROR_INVALIDDATA;
+
+        type = id <= 17 ? id + IAMF_OBU_IA_AUDIO_FRAME_ID0 : IAMF_OBU_IA_AUDIO_FRAME;
+
+        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);
+        }
+
+        init_put_bits(&pb, (uint8_t *)&obu_header, sizeof(obu_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);
+
+        init_put_bits(&pb, header, sizeof(header));
+        if (skip_samples || discard_padding) {
+            put_leb(&pb, discard_padding);
+            put_leb(&pb, skip_samples);
+        }
+        if (id > 17)
+            put_leb(&pb, id);
+        flush_put_bits(&pb);
+
+        header_size = put_bytes_count(&pb, 1);
+
+        init_put_bits(&pb, obu, sizeof(obu));
+        put_leb(&pb, header_size + pkt->size);
+        flush_put_bits(&pb);
+
+        obu_size = put_bytes_count(&pb, 1);
+
+        ret = av_grow_packet(out, 1 + obu_size + header_size + pkt->size);
+        if (ret < 0)
+            goto fail;
+
+        bytestream2_init_writer(&p, out->data + old_out_size, 1 + obu_size + header_size + pkt->size);
+        bytestream2_put_byteu(&p, obu_header);
+        bytestream2_put_bufferu(&p, obu, obu_size);
+        bytestream2_put_bufferu(&p, header, header_size);
+        bytestream2_put_bufferu(&p, pkt->data, pkt->size);
+    }
+
+    ret = av_packet_copy_props(out, pkt);
+    if (ret < 0)
+        goto fail;
+    out->stream_index = c->out_index;
+
+    ret = 0;
+fail:
+    av_packet_free(&pkt);
+    if (ret < 0)
+        av_packet_free(&out);
+    return ret;
+}
+
+static int iamf_frame_merge_init(AVBSFContext *ctx)
+{
+    IAMFMergeContext *const c = ctx->priv_data;
+
+    if (!c->index_mapping) {
+        av_log(ctx, AV_LOG_ERROR, "Empty index map\n");
+        return AVERROR(EINVAL);
+    }
+
+    c->fifo = av_fifo_alloc2(av_dict_count(c->index_mapping), sizeof(AVPacket*), 0);
+    if (!c->fifo)
+        return AVERROR(ENOMEM);
+
+    return 0;
+}
+
+static void iamf_frame_merge_flush(AVBSFContext *ctx)
+{
+    IAMFMergeContext *const c = ctx->priv_data;
+
+    while (av_fifo_can_read(c->fifo)) {
+        AVPacket *pkt;
+        av_fifo_read(c->fifo, &pkt, 1);
+        av_packet_free(&pkt);
+    }
+    av_fifo_reset2(c->fifo);
+}
+
+static void iamf_frame_merge_close(AVBSFContext *ctx)
+{
+    IAMFMergeContext *const c = ctx->priv_data;
+
+    if (c->fifo)
+        iamf_frame_merge_flush(ctx);
+    av_fifo_freep2(&c->fifo);
+}
+
+#define OFFSET(x) offsetof(IAMFMergeContext, x)
+#define FLAGS (AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_BSF_PARAM)
+static const AVOption iamf_frame_merge_options[] = {
+    { "index_mapping", "a :-separated list of stream_index=audio_substream_id entries "
+                       "to set stream id in output Audio Frame OBUs",
+        OFFSET(index_mapping), AV_OPT_TYPE_DICT, { .str = NULL }, 0, 0, FLAGS },
+    { "out_index", "Stream index to set in output packets",
+        OFFSET(out_index), AV_OPT_TYPE_INT, { 0 }, 0, INT_MAX, FLAGS },
+    { NULL }
+};
+
+static const AVClass iamf_frame_merge_class = {
+    .class_name = "iamf_frame_merge_bsf",
+    .item_name  = av_default_item_name,
+    .option     = iamf_frame_merge_options,
+    .version    = LIBAVUTIL_VERSION_INT,
+};
+
+static const enum AVCodecID iamf_frame_merge_codec_ids[] = {
+    AV_CODEC_ID_PCM_S16LE, AV_CODEC_ID_PCM_S16BE,
+    AV_CODEC_ID_PCM_S24LE, AV_CODEC_ID_PCM_S24BE,
+    AV_CODEC_ID_PCM_S32LE, AV_CODEC_ID_PCM_S32BE,
+    AV_CODEC_ID_OPUS,      AV_CODEC_ID_AAC,
+    AV_CODEC_ID_FLAC,      AV_CODEC_ID_NONE,
+};
+
+const FFBitStreamFilter ff_iamf_frame_merge_bsf = {
+    .p.name         = "iamf_frame_merge",
+    .p.codec_ids    = iamf_frame_merge_codec_ids,
+    .p.priv_class   = &iamf_frame_merge_class,
+    .priv_data_size = sizeof(IAMFMergeContext),
+    .init           = iamf_frame_merge_init,
+    .flush          = iamf_frame_merge_flush,
+    .close          = iamf_frame_merge_close,
+    .filter         = iamf_frame_merge_filter,
+};
diff --git a/libavcodec/leb.h b/libavcodec/leb.h
index 5159c434b1..3f00b2988d 100644
--- a/libavcodec/leb.h
+++ b/libavcodec/leb.h
@@ -25,6 +25,7 @@
 #define AVCODEC_LEB_H
 
 #include "get_bits.h"
+#include "put_bits.h"
 
 /**
  * Read a unsigned integer coded as a variable number of up to eight
@@ -67,4 +68,25 @@ static inline int64_t get_leb128(GetBitContext *gb) {
     return ret;
 }
 
+/**
+ * Write a unsigned integer coded as a variable number of up to eight
+ * little-endian bytes, where the MSB in a byte signals another byte
+ * is coded.
+ */
+static inline void put_leb(PutBitContext *s, unsigned value)
+{
+    int len;
+    uint8_t byte;
+
+    len = (av_log2(value) + 7) / 7;
+
+    for (int i = 0; i < len; i++) {
+        byte = value >> (7 * i) & 0x7f;
+        if (i < len - 1)
+            byte |= 0x80;
+
+        put_bits_no_assert(s, 8, byte);
+    }
+}
+
 #endif /* AVCODEC_LEB_H */
-- 
2.43.0

_______________________________________________
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] 17+ messages in thread

* [FFmpeg-devel] [PATCH 6/7 v3] avformat/movenc: add support for Immersive Audio Model and Formats in ISOBMFF
  2024-02-06 13:05 [FFmpeg-devel] [PATCH 1/7 v4] avcodec: add an Immersive Audio Model and Formats frame split bsf James Almer
                   ` (3 preceding siblings ...)
  2024-02-06 13:05 ` [FFmpeg-devel] [PATCH 5/7 v3] avcodec: add an Immersive Audio Model and Formats frame merge bsf James Almer
@ 2024-02-06 13:05 ` James Almer
  2024-02-06 13:05 ` [FFmpeg-devel] [PATCH] fate: add IAMF in mp4 tests James Almer
  2024-02-12 13:40 ` [FFmpeg-devel] [PATCH 1/7 v4] avcodec: add an Immersive Audio Model and Formats frame split bsf James Almer
  6 siblings, 0 replies; 17+ messages in thread
From: James Almer @ 2024-02-06 13:05 UTC (permalink / raw)
  To: ffmpeg-devel

Signed-off-by: James Almer <jamrial@gmail.com>
---
No changes since last version

 configure            |   2 +-
 libavformat/Makefile |   2 +-
 libavformat/movenc.c | 336 +++++++++++++++++++++++++++++++++++--------
 libavformat/movenc.h |   5 +
 4 files changed, 281 insertions(+), 64 deletions(-)

diff --git a/configure b/configure
index 567a39b242..552e74acf8 100755
--- a/configure
+++ b/configure
@@ -3550,7 +3550,7 @@ mlp_demuxer_select="mlp_parser"
 mmf_muxer_select="riffenc"
 mov_demuxer_select="iso_media riffdec iamf_frame_split_bsf"
 mov_demuxer_suggest="zlib"
-mov_muxer_select="iso_media riffenc rtpenc_chain vp9_superframe_bsf aac_adtstoasc_bsf ac3_parser"
+mov_muxer_select="iso_media riffenc rtpenc_chain vp9_superframe_bsf aac_adtstoasc_bsf iamf_frame_merge_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 ab264644c6..b5996b08ce 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -369,7 +369,7 @@ OBJS-$(CONFIG_MOV_DEMUXER)               += mov.o mov_chan.o mov_esds.o \
 OBJS-$(CONFIG_MOV_MUXER)                 += movenc.o av1.o avc.o hevc.o vvc.o vpcc.o \
                                             movenchint.o mov_chan.o rtp.o \
                                             movenccenc.o movenc_ttml.o rawutils.o \
-                                            dovi_isom.o evc.o
+                                            dovi_isom.o evc.o iamf_writer.o
 OBJS-$(CONFIG_MP2_MUXER)                 += rawenc.o
 OBJS-$(CONFIG_MP3_DEMUXER)               += mp3dec.o replaygain.o
 OBJS-$(CONFIG_MP3_MUXER)                 += mp3enc.o rawenc.o id3v2enc.o
diff --git a/libavformat/movenc.c b/libavformat/movenc.c
index 40473fdf56..676fba11ee 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,45 @@ static int mov_write_subtitle_end_packet(AVFormatContext *s,
     return ret;
 }
 
+static int mov_filter_packet(AVFormatContext *s, MOVTrack *track, AVPacket *pkt)
+{
+    int ret;
+
+    if (!track->bsf)
+        return 0;
+
+    ret = av_bsf_send_packet(track->bsf, pkt);
+    if (ret < 0) {
+        av_log(s, AV_LOG_ERROR,
+                "Failed to send packet to filter %s for stream %d: %s\n",
+                track->bsf->filter->name, pkt->stream_index, av_err2str(ret));
+        return ret;
+    }
+
+    return av_bsf_receive_packet(track->bsf, pkt);
+}
+
 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;
+
+    ret = mov_filter_packet(s, trk, pkt);
+    if (ret < 0) {
+        if (ret == AVERROR(EAGAIN))
+            return 0;
+        av_log(s, AV_LOG_ERROR, "Error applying bitstream filters to an output "
+                                "packet for stream #%d: %s\n", trk->st->index, av_err2str(ret));
+        return ret;
+    }
 
     if (is_cover_image(trk->st)) {
         int ret;
@@ -6817,12 +6893,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 +6906,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 +6966,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 +7001,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;
 
@@ -6955,6 +7034,7 @@ static void mov_free(AVFormatContext *s)
         ffio_free_dyn_buf(&track->mdat_buf);
 
         avpriv_packet_list_free(&track->squashed_packet_queue);
+        av_bsf_free(&track->bsf);
     }
 
     av_freep(&mov->tracks);
@@ -7027,6 +7107,92 @@ 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
+    const AVBitStreamFilter *filter;
+    AVBPrint bprint;
+    AVStream *first_st = NULL;
+    char *args;
+    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);
+
+    av_bprint_init(&bprint, 0, AV_BPRINT_SIZE_UNLIMITED);
+
+    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:
+            if (!first_st)
+                first_st = stg->streams[0];
+
+            for (int j = 0; j < stg->nb_streams; j++) {
+                av_bprintf(&bprint, "%d=%d%s", s->streams[j]->index, s->streams[j]->id,
+                                               j < (stg->nb_streams - 1) ? ":" : "");
+                s->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;
+    }
+
+    av_bprint_finalize(&bprint, &args);
+
+    filter = av_bsf_get_by_name("iamf_frame_merge");
+    if (!filter) {
+        av_log(s, AV_LOG_ERROR, "iamf_frame_merge bitstream filter "
+               "not found. This is a bug, please report it.\n");
+        return AVERROR_BUG;
+    }
+
+    ret = av_bsf_alloc(filter, &track->bsf);
+    if (ret < 0)
+        return ret;
+
+    ret = avcodec_parameters_copy(track->bsf->par_in, first_st->codecpar);
+    if (ret < 0)
+        return ret;
+
+    av_opt_set(track->bsf->priv_data, "index_mapping", args, 0);
+    av_opt_set_int(track->bsf->priv_data, "out_index", first_st->index, 0);
+
+    track->tag = MKTAG('i','a','m','f');
+
+    return av_bsf_init(track->bsf);
+}
+
 static int mov_init(AVFormatContext *s)
 {
     MOVMuxContext *mov = s->priv_data;
@@ -7164,7 +7330,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 +7386,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 +7435,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 +7653,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 +7694,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 +7758,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 +7771,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 +7781,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 +7902,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 +8042,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 +8052,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 +8074,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..2038ce9176 100644
--- a/libavformat/movenc.h
+++ b/libavformat/movenc.h
@@ -170,6 +170,10 @@ 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 AVBSFContext *bsf;
+
+    struct IAMFContext *iamf;
 } MOVTrack;
 
 typedef enum {
@@ -188,6 +192,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.0

_______________________________________________
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] 17+ messages in thread

* [FFmpeg-devel] [PATCH] fate: add IAMF in mp4 tests
  2024-02-06 13:05 [FFmpeg-devel] [PATCH 1/7 v4] avcodec: add an Immersive Audio Model and Formats frame split bsf James Almer
                   ` (4 preceding siblings ...)
  2024-02-06 13:05 ` [FFmpeg-devel] [PATCH 6/7 v3] avformat/movenc: add support for Immersive Audio Model and Formats in ISOBMFF James Almer
@ 2024-02-06 13:05 ` James Almer
  2024-02-12 13:40 ` [FFmpeg-devel] [PATCH 1/7 v4] avcodec: add an Immersive Audio Model and Formats frame split bsf James Almer
  6 siblings, 0 replies; 17+ messages in thread
From: James Almer @ 2024-02-06 13:05 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..804ab2ccc8
--- /dev/null
+++ b/tests/ref/fate/mov-mp4-iamf-5_1_4
@@ -0,0 +1,98 @@
+d2d30f38521a49d054a6dbb47135b4e3 *tests/data/fate/mov-mp4-iamf-5_1_4.mp4
+86364 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..7459a761c5
--- /dev/null
+++ b/tests/ref/fate/mov-mp4-iamf-7_1_4
@@ -0,0 +1,114 @@
+fae8adc74ecd3a7676e3473b65a23d7b *tests/data/fate/mov-mp4-iamf-7_1_4.mp4
+100614 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..0cf81ca48c
--- /dev/null
+++ b/tests/ref/fate/mov-mp4-iamf-ambisonic_1
@@ -0,0 +1,66 @@
+6c1f78b19b5084d12a5f535d9cd500e6 *tests/data/fate/mov-mp4-iamf-ambisonic_1.mp4
+57763 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..cd155fe96b
--- /dev/null
+++ b/tests/ref/fate/mov-mp4-iamf-stereo
@@ -0,0 +1,18 @@
+99d44f405eccb7161bcfe910e68c7e1b *tests/data/fate/mov-mp4-iamf-stereo.mp4
+15177 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.0

_______________________________________________
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] 17+ messages in thread

* Re: [FFmpeg-devel] [PATCH 1/7 v4] avcodec: add an Immersive Audio Model and Formats frame split bsf
  2024-02-06 13:05 [FFmpeg-devel] [PATCH 1/7 v4] avcodec: add an Immersive Audio Model and Formats frame split bsf James Almer
                   ` (5 preceding siblings ...)
  2024-02-06 13:05 ` [FFmpeg-devel] [PATCH] fate: add IAMF in mp4 tests James Almer
@ 2024-02-12 13:40 ` James Almer
  2024-02-12 16:40   ` Andreas Rheinhardt
  6 siblings, 1 reply; 17+ messages in thread
From: James Almer @ 2024-02-12 13:40 UTC (permalink / raw)
  To: ffmpeg-devel

On 2/6/2024 10:05 AM, James Almer wrote:
> Signed-off-by: James Almer <jamrial@gmail.com>
> ---
> Now reading decriptors from extradata, plus a setting to ensure any descriptors
> present inband are omitted has been added.
> 
>   doc/bitstream_filters.texi            |  16 +
>   libavcodec/bitstream_filters.c        |   1 +
>   libavcodec/bsf/Makefile               |   1 +
>   libavcodec/bsf/iamf_frame_split_bsf.c | 887 ++++++++++++++++++++++++++
>   4 files changed, 905 insertions(+)
>   create mode 100644 libavcodec/bsf/iamf_frame_split_bsf.c

Will apply the set soon if there are no objections.
_______________________________________________
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] 17+ messages in thread

* Re: [FFmpeg-devel] [PATCH 1/7 v4] avcodec: add an Immersive Audio Model and Formats frame split bsf
  2024-02-12 13:40 ` [FFmpeg-devel] [PATCH 1/7 v4] avcodec: add an Immersive Audio Model and Formats frame split bsf James Almer
@ 2024-02-12 16:40   ` Andreas Rheinhardt
  2024-02-12 16:51     ` James Almer
  0 siblings, 1 reply; 17+ messages in thread
From: Andreas Rheinhardt @ 2024-02-12 16:40 UTC (permalink / raw)
  To: ffmpeg-devel

James Almer:
> On 2/6/2024 10:05 AM, James Almer wrote:
>> Signed-off-by: James Almer <jamrial@gmail.com>
>> ---
>> Now reading decriptors from extradata, plus a setting to ensure any
>> descriptors
>> present inband are omitted has been added.
>>
>>   doc/bitstream_filters.texi            |  16 +
>>   libavcodec/bitstream_filters.c        |   1 +
>>   libavcodec/bsf/Makefile               |   1 +
>>   libavcodec/bsf/iamf_frame_split_bsf.c | 887 ++++++++++++++++++++++++++
>>   4 files changed, 905 insertions(+)
>>   create mode 100644 libavcodec/bsf/iamf_frame_split_bsf.c
> 
> Will apply the set soon if there are no objections.

I still object to the BSF in #1 existing as it just duplicates parsing
code into lavc and lavf. And the issue with creating new framings for
stuff for which no framing except raw data can't exist is still there.

- 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] 17+ messages in thread

* Re: [FFmpeg-devel] [PATCH 1/7 v4] avcodec: add an Immersive Audio Model and Formats frame split bsf
  2024-02-12 16:40   ` Andreas Rheinhardt
@ 2024-02-12 16:51     ` James Almer
  2024-02-12 17:04       ` Andreas Rheinhardt
  0 siblings, 1 reply; 17+ messages in thread
From: James Almer @ 2024-02-12 16:51 UTC (permalink / raw)
  To: ffmpeg-devel

On 2/12/2024 1:40 PM, Andreas Rheinhardt wrote:
> James Almer:
>> On 2/6/2024 10:05 AM, James Almer wrote:
>>> Signed-off-by: James Almer <jamrial@gmail.com>
>>> ---
>>> Now reading decriptors from extradata, plus a setting to ensure any
>>> descriptors
>>> present inband are omitted has been added.
>>>
>>>    doc/bitstream_filters.texi            |  16 +
>>>    libavcodec/bitstream_filters.c        |   1 +
>>>    libavcodec/bsf/Makefile               |   1 +
>>>    libavcodec/bsf/iamf_frame_split_bsf.c | 887 ++++++++++++++++++++++++++
>>>    4 files changed, 905 insertions(+)
>>>    create mode 100644 libavcodec/bsf/iamf_frame_split_bsf.c
>>
>> Will apply the set soon if there are no objections.
> 
> I still object to the BSF in #1 existing as it just duplicates parsing
> code into lavc and lavf. And the issue with creating new framings for
> stuff for which no framing except raw data can't exist is still there.

I insist on using the split bsf, but i can try to remove the merge one 
and do that within lavf, to avoid creating packets with OBU framing.
_______________________________________________
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] 17+ messages in thread

* Re: [FFmpeg-devel] [PATCH 1/7 v4] avcodec: add an Immersive Audio Model and Formats frame split bsf
  2024-02-12 16:51     ` James Almer
@ 2024-02-12 17:04       ` Andreas Rheinhardt
  2024-02-12 17:11         ` James Almer
  0 siblings, 1 reply; 17+ messages in thread
From: Andreas Rheinhardt @ 2024-02-12 17:04 UTC (permalink / raw)
  To: ffmpeg-devel

James Almer:
> On 2/12/2024 1:40 PM, Andreas Rheinhardt wrote:
>> James Almer:
>>> On 2/6/2024 10:05 AM, James Almer wrote:
>>>> Signed-off-by: James Almer <jamrial@gmail.com>
>>>> ---
>>>> Now reading decriptors from extradata, plus a setting to ensure any
>>>> descriptors
>>>> present inband are omitted has been added.
>>>>
>>>>    doc/bitstream_filters.texi            |  16 +
>>>>    libavcodec/bitstream_filters.c        |   1 +
>>>>    libavcodec/bsf/Makefile               |   1 +
>>>>    libavcodec/bsf/iamf_frame_split_bsf.c | 887
>>>> ++++++++++++++++++++++++++
>>>>    4 files changed, 905 insertions(+)
>>>>    create mode 100644 libavcodec/bsf/iamf_frame_split_bsf.c
>>>
>>> Will apply the set soon if there are no objections.
>>
>> I still object to the BSF in #1 existing as it just duplicates parsing
>> code into lavc and lavf. And the issue with creating new framings for
>> stuff for which no framing except raw data can't exist is still there.
> 
> I insist on using the split bsf, but i can try to remove the merge one
> and do that within lavf, to avoid creating packets with OBU framing.

Why is splitting not simply done inside lavf (and inside the demuxer,
not the generic code in demux.c)? What is the advantage of that? Do such
packets as the split bsf expects exist somewhere in the wild outside of
isobmff files?

- 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] 17+ messages in thread

* Re: [FFmpeg-devel] [PATCH 1/7 v4] avcodec: add an Immersive Audio Model and Formats frame split bsf
  2024-02-12 17:04       ` Andreas Rheinhardt
@ 2024-02-12 17:11         ` James Almer
  2024-02-12 17:28           ` Andreas Rheinhardt
  0 siblings, 1 reply; 17+ messages in thread
From: James Almer @ 2024-02-12 17:11 UTC (permalink / raw)
  To: ffmpeg-devel



On 2/12/2024 2:04 PM, Andreas Rheinhardt wrote:
> James Almer:
>> On 2/12/2024 1:40 PM, Andreas Rheinhardt wrote:
>>> James Almer:
>>>> On 2/6/2024 10:05 AM, James Almer wrote:
>>>>> Signed-off-by: James Almer <jamrial@gmail.com>
>>>>> ---
>>>>> Now reading decriptors from extradata, plus a setting to ensure any
>>>>> descriptors
>>>>> present inband are omitted has been added.
>>>>>
>>>>>     doc/bitstream_filters.texi            |  16 +
>>>>>     libavcodec/bitstream_filters.c        |   1 +
>>>>>     libavcodec/bsf/Makefile               |   1 +
>>>>>     libavcodec/bsf/iamf_frame_split_bsf.c | 887
>>>>> ++++++++++++++++++++++++++
>>>>>     4 files changed, 905 insertions(+)
>>>>>     create mode 100644 libavcodec/bsf/iamf_frame_split_bsf.c
>>>>
>>>> Will apply the set soon if there are no objections.
>>>
>>> I still object to the BSF in #1 existing as it just duplicates parsing
>>> code into lavc and lavf. And the issue with creating new framings for
>>> stuff for which no framing except raw data can't exist is still there.
>>
>> I insist on using the split bsf, but i can try to remove the merge one
>> and do that within lavf, to avoid creating packets with OBU framing.
> 
> Why is splitting not simply done inside lavf (and inside the demuxer,
> not the generic code in demux.c)? What is the advantage of that?

Not making a mess in mov.c's read_packet() from reiterated calls because 
one Track Sample has packets for several AVStreams.

  Do such
> packets as the split bsf expects exist somewhere in the wild outside of
> isobmff files?

Sure, it's raw iamf. Other containers may also support it in the future, 
like Matroska, mpegs and the like.

> 
> - 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".
_______________________________________________
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] 17+ messages in thread

* Re: [FFmpeg-devel] [PATCH 1/7 v4] avcodec: add an Immersive Audio Model and Formats frame split bsf
  2024-02-12 17:11         ` James Almer
@ 2024-02-12 17:28           ` Andreas Rheinhardt
  2024-02-12 18:13             ` James Almer
  0 siblings, 1 reply; 17+ messages in thread
From: Andreas Rheinhardt @ 2024-02-12 17:28 UTC (permalink / raw)
  To: ffmpeg-devel

James Almer:
> 
> 
> On 2/12/2024 2:04 PM, Andreas Rheinhardt wrote:
>> James Almer:
>>> On 2/12/2024 1:40 PM, Andreas Rheinhardt wrote:
>>>> James Almer:
>>>>> On 2/6/2024 10:05 AM, James Almer wrote:
>>>>>> Signed-off-by: James Almer <jamrial@gmail.com>
>>>>>> ---
>>>>>> Now reading decriptors from extradata, plus a setting to ensure any
>>>>>> descriptors
>>>>>> present inband are omitted has been added.
>>>>>>
>>>>>>     doc/bitstream_filters.texi            |  16 +
>>>>>>     libavcodec/bitstream_filters.c        |   1 +
>>>>>>     libavcodec/bsf/Makefile               |   1 +
>>>>>>     libavcodec/bsf/iamf_frame_split_bsf.c | 887
>>>>>> ++++++++++++++++++++++++++
>>>>>>     4 files changed, 905 insertions(+)
>>>>>>     create mode 100644 libavcodec/bsf/iamf_frame_split_bsf.c
>>>>>
>>>>> Will apply the set soon if there are no objections.
>>>>
>>>> I still object to the BSF in #1 existing as it just duplicates parsing
>>>> code into lavc and lavf. And the issue with creating new framings for
>>>> stuff for which no framing except raw data can't exist is still there.
>>>
>>> I insist on using the split bsf, but i can try to remove the merge one
>>> and do that within lavf, to avoid creating packets with OBU framing.
>>
>> Why is splitting not simply done inside lavf (and inside the demuxer,
>> not the generic code in demux.c)? What is the advantage of that?
> 
> Not making a mess in mov.c's read_packet() from reiterated calls because
> one Track Sample has packets for several AVStreams.

Mess? All you would need to do is add a check at the beginning of
read_packet whether there are any more packets buffered. If so, return
them, if not, read new data.
(I consider adding bsfs to the demuxing code to be at least a bit messy
and not worth it unless there were several users of this.)

> 
>  Do such
>> packets as the split bsf expects exist somewhere in the wild outside of
>> isobmff files?
> 
> Sure, it's raw iamf. Other containers may also support it in the future,
> like Matroska, mpegs and the like.
> 

I don't see any reason why we should any of this framing outside of lavf
(i.e. it should be treated like e.g. Matroska's wavpack framing and
repacked inside the (de)muxer).

- 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] 17+ messages in thread

* Re: [FFmpeg-devel] [PATCH 1/7 v4] avcodec: add an Immersive Audio Model and Formats frame split bsf
  2024-02-12 17:28           ` Andreas Rheinhardt
@ 2024-02-12 18:13             ` James Almer
  2024-02-12 18:24               ` Andreas Rheinhardt
  0 siblings, 1 reply; 17+ messages in thread
From: James Almer @ 2024-02-12 18:13 UTC (permalink / raw)
  To: ffmpeg-devel

On 2/12/2024 2:28 PM, Andreas Rheinhardt wrote:
> James Almer:
>>
>>
>> On 2/12/2024 2:04 PM, Andreas Rheinhardt wrote:
>>> James Almer:
>>>> On 2/12/2024 1:40 PM, Andreas Rheinhardt wrote:
>>>>> James Almer:
>>>>>> On 2/6/2024 10:05 AM, James Almer wrote:
>>>>>>> Signed-off-by: James Almer <jamrial@gmail.com>
>>>>>>> ---
>>>>>>> Now reading decriptors from extradata, plus a setting to ensure any
>>>>>>> descriptors
>>>>>>> present inband are omitted has been added.
>>>>>>>
>>>>>>>      doc/bitstream_filters.texi            |  16 +
>>>>>>>      libavcodec/bitstream_filters.c        |   1 +
>>>>>>>      libavcodec/bsf/Makefile               |   1 +
>>>>>>>      libavcodec/bsf/iamf_frame_split_bsf.c | 887
>>>>>>> ++++++++++++++++++++++++++
>>>>>>>      4 files changed, 905 insertions(+)
>>>>>>>      create mode 100644 libavcodec/bsf/iamf_frame_split_bsf.c
>>>>>>
>>>>>> Will apply the set soon if there are no objections.
>>>>>
>>>>> I still object to the BSF in #1 existing as it just duplicates parsing
>>>>> code into lavc and lavf. And the issue with creating new framings for
>>>>> stuff for which no framing except raw data can't exist is still there.
>>>>
>>>> I insist on using the split bsf, but i can try to remove the merge one
>>>> and do that within lavf, to avoid creating packets with OBU framing.
>>>
>>> Why is splitting not simply done inside lavf (and inside the demuxer,
>>> not the generic code in demux.c)? What is the advantage of that?
>>
>> Not making a mess in mov.c's read_packet() from reiterated calls because
>> one Track Sample has packets for several AVStreams.
> 
> Mess? All you would need to do is add a check at the beginning of
> read_packet whether there are any more packets buffered. If so, return
> them, if not, read new data.

Buffered where? Internally in the demuxer?

> (I consider adding bsfs to the demuxing code to be at least a bit messy
> and not worth it unless there were several users of this.)
> 
>>
>>   Do such
>>> packets as the split bsf expects exist somewhere in the wild outside of
>>> isobmff files?
>>
>> Sure, it's raw iamf. Other containers may also support it in the future,
>> like Matroska, mpegs and the like.
>>
> 
> I don't see any reason why we should any of this framing outside of lavf
> (i.e. it should be treated like e.g. Matroska's wavpack framing and
> repacked inside the (de)muxer).
> 
> - 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".
_______________________________________________
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] 17+ messages in thread

* Re: [FFmpeg-devel] [PATCH 1/7 v4] avcodec: add an Immersive Audio Model and Formats frame split bsf
  2024-02-12 18:13             ` James Almer
@ 2024-02-12 18:24               ` Andreas Rheinhardt
  2024-02-12 19:55                 ` James Almer
  0 siblings, 1 reply; 17+ messages in thread
From: Andreas Rheinhardt @ 2024-02-12 18:24 UTC (permalink / raw)
  To: ffmpeg-devel

James Almer:
> On 2/12/2024 2:28 PM, Andreas Rheinhardt wrote:
>> James Almer:
>>>
>>>
>>> On 2/12/2024 2:04 PM, Andreas Rheinhardt wrote:
>>>> James Almer:
>>>>> On 2/12/2024 1:40 PM, Andreas Rheinhardt wrote:
>>>>>> James Almer:
>>>>>>> On 2/6/2024 10:05 AM, James Almer wrote:
>>>>>>>> Signed-off-by: James Almer <jamrial@gmail.com>
>>>>>>>> ---
>>>>>>>> Now reading decriptors from extradata, plus a setting to ensure any
>>>>>>>> descriptors
>>>>>>>> present inband are omitted has been added.
>>>>>>>>
>>>>>>>>      doc/bitstream_filters.texi            |  16 +
>>>>>>>>      libavcodec/bitstream_filters.c        |   1 +
>>>>>>>>      libavcodec/bsf/Makefile               |   1 +
>>>>>>>>      libavcodec/bsf/iamf_frame_split_bsf.c | 887
>>>>>>>> ++++++++++++++++++++++++++
>>>>>>>>      4 files changed, 905 insertions(+)
>>>>>>>>      create mode 100644 libavcodec/bsf/iamf_frame_split_bsf.c
>>>>>>>
>>>>>>> Will apply the set soon if there are no objections.
>>>>>>
>>>>>> I still object to the BSF in #1 existing as it just duplicates
>>>>>> parsing
>>>>>> code into lavc and lavf. And the issue with creating new framings for
>>>>>> stuff for which no framing except raw data can't exist is still
>>>>>> there.
>>>>>
>>>>> I insist on using the split bsf, but i can try to remove the merge one
>>>>> and do that within lavf, to avoid creating packets with OBU framing.
>>>>
>>>> Why is splitting not simply done inside lavf (and inside the demuxer,
>>>> not the generic code in demux.c)? What is the advantage of that?
>>>
>>> Not making a mess in mov.c's read_packet() from reiterated calls because
>>> one Track Sample has packets for several AVStreams.
>>
>> Mess? All you would need to do is add a check at the beginning of
>> read_packet whether there are any more packets buffered. If so, return
>> them, if not, read new data.
> 
> Buffered where? Internally in the demuxer?
> 

That's it: We add a new function (may be an inline wrapper for
avpriv_packet_list_put()) for demuxers to append multiple packets to the
raw_packet_buffer queue in a single read_packet call. That way the
ordinary (already existing) logic in ff_read_frame() will take care of
everything.

- 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] 17+ messages in thread

* Re: [FFmpeg-devel] [PATCH 1/7 v4] avcodec: add an Immersive Audio Model and Formats frame split bsf
  2024-02-12 18:24               ` Andreas Rheinhardt
@ 2024-02-12 19:55                 ` James Almer
  2024-02-12 20:08                   ` Andreas Rheinhardt
  0 siblings, 1 reply; 17+ messages in thread
From: James Almer @ 2024-02-12 19:55 UTC (permalink / raw)
  To: ffmpeg-devel

On 2/12/2024 3:24 PM, Andreas Rheinhardt wrote:
> James Almer:
>> On 2/12/2024 2:28 PM, Andreas Rheinhardt wrote:
>>> James Almer:
>>>>
>>>>
>>>> On 2/12/2024 2:04 PM, Andreas Rheinhardt wrote:
>>>>> James Almer:
>>>>>> On 2/12/2024 1:40 PM, Andreas Rheinhardt wrote:
>>>>>>> James Almer:
>>>>>>>> On 2/6/2024 10:05 AM, James Almer wrote:
>>>>>>>>> Signed-off-by: James Almer <jamrial@gmail.com>
>>>>>>>>> ---
>>>>>>>>> Now reading decriptors from extradata, plus a setting to ensure any
>>>>>>>>> descriptors
>>>>>>>>> present inband are omitted has been added.
>>>>>>>>>
>>>>>>>>>       doc/bitstream_filters.texi            |  16 +
>>>>>>>>>       libavcodec/bitstream_filters.c        |   1 +
>>>>>>>>>       libavcodec/bsf/Makefile               |   1 +
>>>>>>>>>       libavcodec/bsf/iamf_frame_split_bsf.c | 887
>>>>>>>>> ++++++++++++++++++++++++++
>>>>>>>>>       4 files changed, 905 insertions(+)
>>>>>>>>>       create mode 100644 libavcodec/bsf/iamf_frame_split_bsf.c
>>>>>>>>
>>>>>>>> Will apply the set soon if there are no objections.
>>>>>>>
>>>>>>> I still object to the BSF in #1 existing as it just duplicates
>>>>>>> parsing
>>>>>>> code into lavc and lavf. And the issue with creating new framings for
>>>>>>> stuff for which no framing except raw data can't exist is still
>>>>>>> there.
>>>>>>
>>>>>> I insist on using the split bsf, but i can try to remove the merge one
>>>>>> and do that within lavf, to avoid creating packets with OBU framing.
>>>>>
>>>>> Why is splitting not simply done inside lavf (and inside the demuxer,
>>>>> not the generic code in demux.c)? What is the advantage of that?
>>>>
>>>> Not making a mess in mov.c's read_packet() from reiterated calls because
>>>> one Track Sample has packets for several AVStreams.
>>>
>>> Mess? All you would need to do is add a check at the beginning of
>>> read_packet whether there are any more packets buffered. If so, return
>>> them, if not, read new data.
>>
>> Buffered where? Internally in the demuxer?
>>
> 
> That's it: We add a new function (may be an inline wrapper for
> avpriv_packet_list_put()) for demuxers to append multiple packets to the
> raw_packet_buffer queue in a single read_packet call. That way the
> ordinary (already existing) logic in ff_read_frame() will take care of
> everything.

ff_read_packet() fills some fields on the AVPackets returned by 
iformat->read_packet() before buffering them in raw_packet_buffer. 
Doesn't look like the demuxer should be put things in raw_packet_buffer 
on its own.
_______________________________________________
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] 17+ messages in thread

* Re: [FFmpeg-devel] [PATCH 1/7 v4] avcodec: add an Immersive Audio Model and Formats frame split bsf
  2024-02-12 19:55                 ` James Almer
@ 2024-02-12 20:08                   ` Andreas Rheinhardt
  0 siblings, 0 replies; 17+ messages in thread
From: Andreas Rheinhardt @ 2024-02-12 20:08 UTC (permalink / raw)
  To: ffmpeg-devel

James Almer:
> On 2/12/2024 3:24 PM, Andreas Rheinhardt wrote:
>> James Almer:
>>> On 2/12/2024 2:28 PM, Andreas Rheinhardt wrote:
>>>> James Almer:
>>>>>
>>>>>
>>>>> On 2/12/2024 2:04 PM, Andreas Rheinhardt wrote:
>>>>>> James Almer:
>>>>>>> On 2/12/2024 1:40 PM, Andreas Rheinhardt wrote:
>>>>>>>> James Almer:
>>>>>>>>> On 2/6/2024 10:05 AM, James Almer wrote:
>>>>>>>>>> Signed-off-by: James Almer <jamrial@gmail.com>
>>>>>>>>>> ---
>>>>>>>>>> Now reading decriptors from extradata, plus a setting to
>>>>>>>>>> ensure any
>>>>>>>>>> descriptors
>>>>>>>>>> present inband are omitted has been added.
>>>>>>>>>>
>>>>>>>>>>       doc/bitstream_filters.texi            |  16 +
>>>>>>>>>>       libavcodec/bitstream_filters.c        |   1 +
>>>>>>>>>>       libavcodec/bsf/Makefile               |   1 +
>>>>>>>>>>       libavcodec/bsf/iamf_frame_split_bsf.c | 887
>>>>>>>>>> ++++++++++++++++++++++++++
>>>>>>>>>>       4 files changed, 905 insertions(+)
>>>>>>>>>>       create mode 100644 libavcodec/bsf/iamf_frame_split_bsf.c
>>>>>>>>>
>>>>>>>>> Will apply the set soon if there are no objections.
>>>>>>>>
>>>>>>>> I still object to the BSF in #1 existing as it just duplicates
>>>>>>>> parsing
>>>>>>>> code into lavc and lavf. And the issue with creating new
>>>>>>>> framings for
>>>>>>>> stuff for which no framing except raw data can't exist is still
>>>>>>>> there.
>>>>>>>
>>>>>>> I insist on using the split bsf, but i can try to remove the
>>>>>>> merge one
>>>>>>> and do that within lavf, to avoid creating packets with OBU framing.
>>>>>>
>>>>>> Why is splitting not simply done inside lavf (and inside the demuxer,
>>>>>> not the generic code in demux.c)? What is the advantage of that?
>>>>>
>>>>> Not making a mess in mov.c's read_packet() from reiterated calls
>>>>> because
>>>>> one Track Sample has packets for several AVStreams.
>>>>
>>>> Mess? All you would need to do is add a check at the beginning of
>>>> read_packet whether there are any more packets buffered. If so, return
>>>> them, if not, read new data.
>>>
>>> Buffered where? Internally in the demuxer?
>>>
>>
>> That's it: We add a new function (may be an inline wrapper for
>> avpriv_packet_list_put()) for demuxers to append multiple packets to the
>> raw_packet_buffer queue in a single read_packet call. That way the
>> ordinary (already existing) logic in ff_read_frame() will take care of
>> everything.
> 
> ff_read_packet() fills some fields on the AVPackets returned by
> iformat->read_packet() before buffering them in raw_packet_buffer.
> Doesn't look like the demuxer should be put things in raw_packet_buffer
> on its own.

Yes, I noticed that a bit after writing my mail. It means this thing
will not be inline. (And as long as it is done by an explicit function
in demux.[ch], it is not the demuxer who does this on its own.)

- 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] 17+ messages in thread

end of thread, other threads:[~2024-02-12 20:22 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-02-06 13:05 [FFmpeg-devel] [PATCH 1/7 v4] avcodec: add an Immersive Audio Model and Formats frame split bsf James Almer
2024-02-06 13:05 ` [FFmpeg-devel] [PATCH 2/7 v4] avformat/demux: support inserting bitstream filters in demuxing scenarios James Almer
2024-02-06 13:05 ` [FFmpeg-devel] [PATCH 3/7 v4] avformat/mov: make MOVStreamContext refcounted James Almer
2024-02-06 13:05 ` [FFmpeg-devel] [PATCH 4/7 v4] avformat/mov: add support for Immersive Audio Model and Formats in ISOBMFF James Almer
2024-02-06 13:05 ` [FFmpeg-devel] [PATCH 5/7 v3] avcodec: add an Immersive Audio Model and Formats frame merge bsf James Almer
2024-02-06 13:05 ` [FFmpeg-devel] [PATCH 6/7 v3] avformat/movenc: add support for Immersive Audio Model and Formats in ISOBMFF James Almer
2024-02-06 13:05 ` [FFmpeg-devel] [PATCH] fate: add IAMF in mp4 tests James Almer
2024-02-12 13:40 ` [FFmpeg-devel] [PATCH 1/7 v4] avcodec: add an Immersive Audio Model and Formats frame split bsf James Almer
2024-02-12 16:40   ` Andreas Rheinhardt
2024-02-12 16:51     ` James Almer
2024-02-12 17:04       ` Andreas Rheinhardt
2024-02-12 17:11         ` James Almer
2024-02-12 17:28           ` Andreas Rheinhardt
2024-02-12 18:13             ` James Almer
2024-02-12 18:24               ` Andreas Rheinhardt
2024-02-12 19:55                 ` James Almer
2024-02-12 20:08                   ` Andreas Rheinhardt

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