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 , 7 months ago) |
|---|
-
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 28 28 29 29 #include "libavformat/avformat.h" 30 30 #include "libavformat/avio.h" 31 #include "libavformat/iamf.h" 31 32 32 33 #include "libavcodec/avcodec.h" 33 34 … … static int copy_meta(Muxer *mux, const OptionsContext *o) 3085 3086 return 0; 3086 3087 } 3087 3088 3089 static 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 3088 3133 static int set_dispositions(Muxer *mux, const OptionsContext *o) 3089 3134 { 3090 3135 OutputFile *of = &mux->of; … … int of_open(const OptionsContext *o, const char *filename, Scheduler *sch) 3447 3492 if (err < 0) 3448 3493 return err; 3449 3494 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 3450 3500 err = of_add_groups(mux, o); 3451 3501 if (err < 0) 3452 3502 return err; -
libavformat/iamf.h
diff --git a/libavformat/iamf.h b/libavformat/iamf.h index ad874c1fa8..fd66a749f7 100644
a b 30 30 #include "libavutil/iamf.h" 31 31 #include "libavcodec/codec_id.h" 32 32 #include "libavcodec/codec_par.h" 33 #include "libavformat/avformat.h" 33 34 34 35 #define MAX_IAMF_OBU_HEADER_SIZE (1 + 8 * 3) 35 36 … … static inline IAMFParamDefinition *ff_iamf_get_param_definition(const IAMFContex 196 197 return param_definition; 197 198 } 198 199 200 int ff_iamf_decode_side_data(AVFormatContext *c, const AVPacketSideData* sd); 201 199 202 void ff_iamf_free_audio_element(IAMFAudioElement **paudio_element); 200 203 void ff_iamf_free_mix_presentation(IAMFMixPresentation **pmix_presentation); 201 204 void 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 21 21 22 22 #include "libavutil/avassert.h" 23 23 #include "libavutil/intreadwrite.h" 24 #include "libavutil/mem.h" 24 25 #include "avformat.h" 25 26 #include "demux.h" 26 27 #include "iamf.h" 27 28 #include "iamf_reader.h" 28 29 #include "iamf_parse.h" 29 30 #include "internal.h" 31 #include "avio_internal.h" 30 32 31 33 //return < 0 if we need more data 32 34 static int get_score(const uint8_t *buf, int buf_size, enum IAMF_OBU_Type type, int *seq) … … static int iamf_read_header(AVFormatContext *s) 75 77 IAMFContext *const iamf = &c->iamf; 76 78 int ret; 77 79 80 int64_t old_offset = avio_tell(s->pb); 81 78 82 ret = ff_iamfdec_read_descriptors(iamf, s->pb, INT_MAX, s); 79 83 if (ret < 0) 80 84 return ret; 81 85 86 int64_t cur_offset = avio_tell(s->pb); 87 size_t descriptors_size = cur_offset - old_offset; 88 82 89 for (int i = 0; i < iamf->nb_audio_elements; i++) { 83 90 IAMFAudioElement *audio_element = iamf->audio_elements[i]; 84 91 AVStreamGroup *stg = avformat_stream_group_create(s, AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT, NULL); … … static int iamf_read_header(AVFormatContext *s) 113 120 st->disposition |= AV_DISPOSITION_DEPENDENT; 114 121 st->id = substream->audio_substream_id; 115 122 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 } 116 139 } 117 140 } 118 141 … … static int iamf_read_close(AVFormatContext *s) 181 204 return 0; 182 205 } 183 206 207 int 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 327 fail: 328 ff_iamf_uninit_context(&dmux.iamf); 329 return ret; 330 } 331 184 332 const FFInputFormat ff_iamf_demuxer = { 185 333 .p.name = "iamf", 186 334 .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) 44 44 { 45 45 IAMFMuxContext *const c = s->priv_data; 46 46 IAMFContext *const iamf = &c->iamf; 47 FFIOContext b;48 47 int nb_audio_elements = 0, nb_mix_presentations = 0; 49 48 int ret; 50 49 … … static int iamf_init(AVFormatContext *s) 73 72 } 74 73 75 74 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); 134 77 } 135 78 136 79 for (int i = 0; i < s->nb_stream_groups; i++) {
