Ticket #11627: 0002-fix-allow-demuxing-iamf-to-iamf-mp4-without-specifyi.patch

File 0002-fix-allow-demuxing-iamf-to-iamf-mp4-without-specifyi.patch, 13.5 KB (added by Luiz Stangarlin, 7 months ago)

patch to allow copying the stream groups from iamf to iamf/mp4

  • fftools/ffmpeg_mux_init.c

    From 098be1ee59daf5aaf78300d859d5e984bcb9b062 Mon Sep 17 00:00:00 2001
    From: Luiz Felipe Stangarlin <luizfelipestang@gmail.com>
    Date: Tue, 10 Feb 2026 08:26:35 -0600
    Subject: [PATCH 2/2] fix: allow demuxing iamf to iamf/mp4 without specifying
     groups
    
    ---
     fftools/ffmpeg_mux_init.c |  50 +++++++++++++
     libavformat/iamf.h        |   3 +
     libavformat/iamfdec.c     | 148 ++++++++++++++++++++++++++++++++++++++
     libavformat/iamfenc.c     |  61 +---------------
     4 files changed, 203 insertions(+), 59 deletions(-)
    
    diff --git a/fftools/ffmpeg_mux_init.c b/fftools/ffmpeg_mux_init.c
    index 4cb8f91d6e..bf0103cde8 100644
    a b  
    2828
    2929#include "libavformat/avformat.h"
    3030#include "libavformat/avio.h"
     31#include "libavformat/iamf.h"
    3132
    3233#include "libavcodec/avcodec.h"
    3334
    static int copy_meta(Muxer *mux, const OptionsContext *o)  
    30853086    return 0;
    30863087}
    30873088
     3089static int copy_groups_if_empty(Muxer* mux, const OptionsContext* o)
     3090{
     3091    AVFormatContext *oc = mux->fc;
     3092    OutputFile *of = &mux->of;
     3093    int ret = 0;
     3094
     3095    /* Only proceed if user hasn't manually specified stream groups */
     3096    if (o->stream_groups.nb_opt > 0)
     3097        return 0;
     3098
     3099    for (int file_idx = 0; file_idx < nb_input_files; file_idx++) {
     3100        InputFile *ifile = input_files[file_idx];
     3101        AVFormatContext *ic = ifile->ctx;
     3102
     3103        if (ic->nb_stream_groups == 0)
     3104            continue;
     3105
     3106        if (ic->nb_streams == 0)
     3107            continue;
     3108
     3109        /* Look for IAMF descriptors in the first stream's side data */
     3110        const AVPacketSideData* sd =
     3111            av_packet_side_data_get(ic->streams[0]->codecpar->coded_side_data,
     3112                                    ic->streams[0]->codecpar->nb_coded_side_data,
     3113                                    AV_PKT_DATA_IAMF_DESCRIPTORS);
     3114        if (!sd)
     3115            continue;
     3116
     3117        av_log(mux, AV_LOG_VERBOSE,
     3118            "Found IAMF descriptors in input file %d, creating stream groups\n",
     3119            file_idx);
     3120
     3121        ret = ff_iamf_decode_side_data(oc, sd);
     3122        if (ret < 0) {
     3123            av_log(mux, AV_LOG_ERROR,
     3124                "Failed to parse IAMF descriptors from input file %d: %s\n",
     3125                file_idx, av_err2str(ret));
     3126            return ret;
     3127        }
     3128    }
     3129
     3130    return 0;
     3131}
     3132
    30883133static int set_dispositions(Muxer *mux, const OptionsContext *o)
    30893134{
    30903135    OutputFile                    *of = &mux->of;
    int of_open(const OptionsContext *o, const char *filename, Scheduler *sch)  
    34473492    if (err < 0)
    34483493        return err;
    34493494
     3495    /* copy existing groups if not informed by the user, used by IAMF. */
     3496    err = copy_groups_if_empty(mux, o);
     3497    if (err < 0)
     3498        return err;
     3499
    34503500    err = of_add_groups(mux, o);
    34513501    if (err < 0)
    34523502        return err;
  • libavformat/iamf.h

    diff --git a/libavformat/iamf.h b/libavformat/iamf.h
    index ad874c1fa8..fd66a749f7 100644
    a b  
    3030#include "libavutil/iamf.h"
    3131#include "libavcodec/codec_id.h"
    3232#include "libavcodec/codec_par.h"
     33#include "libavformat/avformat.h"
    3334
    3435#define MAX_IAMF_OBU_HEADER_SIZE (1 + 8 * 3)
    3536
    static inline IAMFParamDefinition *ff_iamf_get_param_definition(const IAMFContex  
    196197    return param_definition;
    197198}
    198199
     200int ff_iamf_decode_side_data(AVFormatContext *c, const AVPacketSideData* sd);
     201
    199202void ff_iamf_free_audio_element(IAMFAudioElement **paudio_element);
    200203void ff_iamf_free_mix_presentation(IAMFMixPresentation **pmix_presentation);
    201204void ff_iamf_uninit_context(IAMFContext *c);
  • libavformat/iamfdec.c

    diff --git a/libavformat/iamfdec.c b/libavformat/iamfdec.c
    index 02f01abc6c..99c4b433be 100644
    a b  
    2121
    2222#include "libavutil/avassert.h"
    2323#include "libavutil/intreadwrite.h"
     24#include "libavutil/mem.h"
    2425#include "avformat.h"
    2526#include "demux.h"
    2627#include "iamf.h"
    2728#include "iamf_reader.h"
    2829#include "iamf_parse.h"
    2930#include "internal.h"
     31#include "avio_internal.h"
    3032
    3133//return < 0 if we need more data
    3234static int get_score(const uint8_t *buf, int buf_size, enum IAMF_OBU_Type type, int *seq)
    static int iamf_read_header(AVFormatContext *s)  
    7577    IAMFContext *const iamf = &c->iamf;
    7678    int ret;
    7779
     80    int64_t old_offset = avio_tell(s->pb);
     81
    7882    ret = ff_iamfdec_read_descriptors(iamf, s->pb, INT_MAX, s);
    7983    if (ret < 0)
    8084        return ret;
    8185
     86    int64_t cur_offset = avio_tell(s->pb);
     87    size_t descriptors_size = cur_offset - old_offset;
     88
    8289    for (int i = 0; i < iamf->nb_audio_elements; i++) {
    8390        IAMFAudioElement *audio_element = iamf->audio_elements[i];
    8491        AVStreamGroup *stg = avformat_stream_group_create(s, AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT, NULL);
    static int iamf_read_header(AVFormatContext *s)  
    113120                st->disposition |= AV_DISPOSITION_DEPENDENT;
    114121            st->id = substream->audio_substream_id;
    115122            avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate);
     123       
     124            // save a copy of the descriptors on the side data for later use when encoding raw IAMF
     125            if (i == 0 && j == 0) {
     126                //we only look at the first stream later
     127                if (!av_packet_side_data_new(&st->codecpar->coded_side_data,
     128                    &st->codecpar->nb_coded_side_data,
     129                    AV_PKT_DATA_IAMF_DESCRIPTORS,
     130                    descriptors_size, 0)) {
     131                    return AVERROR(ENOMEM);
     132                }
     133                avio_seek(s->pb, old_offset, SEEK_SET);
     134                ret = avio_read(s->pb, st->codecpar->coded_side_data->data, descriptors_size);
     135                if (ret != descriptors_size)
     136                    return ret < 0 ? ret : AVERROR_INVALIDDATA;
     137                avio_seek(s->pb, cur_offset, SEEK_SET);
     138            }
    116139        }
    117140    }
    118141
    static int iamf_read_close(AVFormatContext *s)  
    181204    return 0;
    182205}
    183206
     207int ff_iamf_decode_side_data(AVFormatContext *target, const AVPacketSideData *sd)
     208{
     209    IAMFDemuxContext dmux = { 0 };
     210    FFIOContext b = { 0 };
     211    AVIOContext *pbc;
     212    int ret;
     213    int has_stream_ids = 0;
     214
     215    if (!sd || sd->type != AV_PKT_DATA_IAMF_DESCRIPTORS)
     216        return AVERROR(EINVAL);
     217
     218    ffio_init_read_context(&b, sd->data, sd->size);
     219    pbc = &b.pub;
     220
     221    /* Parse descriptors */
     222    ret = ff_iamfdec_read_descriptors(&dmux.iamf, &b.pub, sd->size, target);
     223    if (ret < 0) {
     224        goto fail;
     225    }
     226   
     227    for (int k = 0; k < target->nb_streams; k++) {
     228        if (target->streams[k]->id != 0) has_stream_ids = 1;
     229    }
     230
     231    for (int i = 0; i < dmux.iamf.nb_audio_elements; i++) {
     232        IAMFAudioElement *audio_element = dmux.iamf.audio_elements[i];
     233        const AVIAMFAudioElement *element;
     234
     235        AVStreamGroup *stg =
     236            avformat_stream_group_create(target, AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT, NULL);
     237        if (!stg) {
     238            ret = AVERROR(ENOMEM);
     239            goto fail;
     240        }
     241
     242        av_iamf_audio_element_free(&stg->params.iamf_audio_element);
     243        stg->id = audio_element->audio_element_id;
     244        /* Transfer ownership */
     245        element = stg->params.iamf_audio_element = audio_element->element;
     246        audio_element->element = NULL;
     247
     248        for (int j = 0; j < audio_element->nb_substreams; j++) {
     249            IAMFSubStream *substream = &audio_element->substreams[j];
     250            AVStream *stream = NULL;
     251            int found = 0;
     252            int sid;
     253
     254            /* Find the output stream by matching the substream id */
     255            for (int k = 0; k < target->nb_streams; k++) {
     256                AVStream *ost = target->streams[k];
     257                int id = has_stream_ids ? ost->id : ost->index;
     258                if (id == substream->audio_substream_id) {
     259                    stream = ost;
     260                    found = 1;
     261                    break;
     262                }
     263            }
     264            if (!found) {
     265                continue;
     266            }
     267
     268            ret = avcodec_parameters_copy(stream->codecpar, substream->codecpar);
     269            if (ret < 0)
     270                goto fail;
     271
     272            if (!i && !j && audio_element->layers[0].substream_count == 1)
     273                stream->disposition |= AV_DISPOSITION_DEFAULT;
     274            else if (audio_element->nb_layers > 1 || audio_element->layers[0].substream_count > 1)
     275                stream->disposition |= AV_DISPOSITION_DEPENDENT;
     276            stream->id = substream->audio_substream_id;
     277            avpriv_set_pts_info(stream, 64, 1, stream->codecpar->sample_rate);
     278
     279            ret = avformat_stream_group_add_stream(stg, stream);
     280            if (ret < 0)
     281                goto fail;
     282        }
     283    }
     284
     285    for (int i = 0; i < dmux.iamf.nb_mix_presentations; i++) {
     286        IAMFMixPresentation *mix_presentation = dmux.iamf.mix_presentations[i];
     287        const AVIAMFMixPresentation *mix = mix_presentation->cmix;
     288
     289        AVStreamGroup *stg =
     290            avformat_stream_group_create(target, AV_STREAM_GROUP_PARAMS_IAMF_MIX_PRESENTATION, NULL);
     291        if (!stg) {
     292            ret = AVERROR(ENOMEM);
     293            goto fail;
     294        }
     295
     296        av_iamf_mix_presentation_free(&stg->params.iamf_mix_presentation);
     297        stg->id = mix_presentation->mix_presentation_id;
     298        /* Transfer ownership */
     299        stg->params.iamf_mix_presentation = mix_presentation->mix;
     300        mix_presentation->mix = NULL;
     301
     302        for (int j = 0; j < mix->nb_submixes; j++) {
     303            const AVIAMFSubmix *submix = mix->submixes[j];
     304
     305            for (int k = 0; k < submix->nb_elements; k++) {
     306                const AVIAMFSubmixElement *submix_element = submix->elements[k];
     307                const AVStreamGroup *audio_element = NULL;
     308
     309                for (int l = 0; l < target->nb_stream_groups; l++)
     310                    if (target->stream_groups[l]->type == AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT &&
     311                        target->stream_groups[l]->id   == submix_element->audio_element_id) {
     312                        audio_element = target->stream_groups[l];
     313                        break;
     314                    }
     315                av_assert0(audio_element);
     316
     317                for (int l = 0; l < audio_element->nb_streams; l++) {
     318                    ret = avformat_stream_group_add_stream(stg, audio_element->streams[l]);
     319                    if (ret < 0 && ret != AVERROR(EEXIST))
     320                        goto fail;
     321                }
     322            }
     323        }
     324    }
     325
     326
     327fail:
     328    ff_iamf_uninit_context(&dmux.iamf);
     329    return ret;
     330}
     331
    184332const FFInputFormat ff_iamf_demuxer = {
    185333    .p.name         = "iamf",
    186334    .p.long_name    = NULL_IF_CONFIG_SMALL("Raw Immersive Audio Model and Formats"),
  • libavformat/iamfenc.c

    diff --git a/libavformat/iamfenc.c b/libavformat/iamfenc.c
    index bc21988907..1acfda5e3c 100644
    a b static int iamf_init(AVFormatContext *s)  
    4444{
    4545    IAMFMuxContext *const c = s->priv_data;
    4646    IAMFContext *const iamf = &c->iamf;
    47     FFIOContext b;
    4847    int nb_audio_elements = 0, nb_mix_presentations = 0;
    4948    int ret;
    5049
    static int iamf_init(AVFormatContext *s)  
    7372    }
    7473
    7574    if (s->nb_stream_groups <= 1) {
    76         av_log(s, AV_LOG_WARNING,
    77                "Less than two stream groups provided; falling back to copying IAMF descriptors.\n");
    78 
    79         /* Find the descriptors, by default only stored on the first stream */
    80         const AVPacketSideData *sd =
    81             av_packet_side_data_get(s->streams[0]->codecpar->coded_side_data,
    82                                     s->streams[0]->codecpar->nb_coded_side_data,
    83                                     AV_PKT_DATA_IAMF_DESCRIPTORS);
    84         if (!sd) {
    85             av_log(s, AV_LOG_ERROR,
    86                 "No IAMF extradata found on the first input stream; cannot rewrap.\n");
    87             return AVERROR_INVALIDDATA;
    88         }
    89 
    90         av_log(s, AV_LOG_TRACE,
    91             "Writing IAMF descriptors from extradata (size=%d)\n", sd->size);
    92        
    93         ffio_init_read_context(&b, sd->data, sd->size);
    94         ret = ff_iamfdec_read_descriptors(iamf, &b.pub, sd->size, s);
    95         if (ret < 0)
    96             return ret;
    97        
    98         /* patch the iamf codec struct like the ff_iamf_add_audio_element does */
    99 
    100         for (int i = 0; i < iamf->nb_codec_configs; i++) {
    101             av_free(iamf->codec_configs[i]->extradata);
    102             av_free(iamf->codec_configs[i]);
    103         }
    104         av_freep(&iamf->codec_configs);
    105         iamf->nb_codec_configs = 0;
    106 
    107         for (int i = 0; i < iamf->nb_audio_elements; i++) {
    108             IAMFAudioElement *audio_element = iamf->audio_elements[i];
    109 
    110             /* Create a synthetic stream group */
    111             AVStreamGroup *stg = avformat_stream_group_create(
    112                     s, AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT, NULL);
    113             if (!stg)
    114                 return AVERROR(ENOMEM);
    115 
    116             int sid = audio_element->substreams[0].audio_substream_id;
    117             for (int j = 0; j < s->nb_streams; j++) {
    118                 AVStream *st = s->streams[j];
    119                 if (st->id != sid) continue;
    120                 avformat_stream_group_add_stream(stg, st);
    121                 break;
    122             }
    123 
    124             IAMFCodecConfig *cc = av_mallocz(sizeof(*cc));
    125             if (!cc)
    126                 return AVERROR(ENOMEM);
    127 
    128             ret = ff_iamf_fill_codec_config(iamf, stg, cc);
    129             if (ret < 0)
    130                 return ret;
    131         }
    132 
    133         return 0;
     75        av_log(s, AV_LOG_ERROR, "There must be at least two stream groups\n");
     76        return AVERROR(EINVAL);
    13477    }
    13578
    13679    for (int i = 0; i < s->nb_stream_groups; i++) {