3 * Copyright (c) 2023 James Almer
5 * This file is part of FFmpeg.
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
26 #include "iamf_writer.h"
30 typedef struct IAMFMuxContext
{
33 int64_t descriptors_offset
;
39 static int iamf_init(AVFormatContext
*s
)
41 IAMFMuxContext
*const c
= s
->priv_data
;
42 IAMFContext
*const iamf
= &c
->iamf
;
43 int nb_audio_elements
= 0, nb_mix_presentations
= 0;
46 for (int i
= 0; i
< s
->nb_streams
; i
++) {
47 if (s
->streams
[i
]->codecpar
->codec_type
!= AVMEDIA_TYPE_AUDIO
||
48 (s
->streams
[i
]->codecpar
->codec_tag
!= MKTAG('m','p','4','a') &&
49 s
->streams
[i
]->codecpar
->codec_tag
!= MKTAG('O','p','u','s') &&
50 s
->streams
[i
]->codecpar
->codec_tag
!= MKTAG('f','L','a','C') &&
51 s
->streams
[i
]->codecpar
->codec_tag
!= MKTAG('i','p','c','m'))) {
52 av_log(s
, AV_LOG_ERROR
, "Unsupported codec id %s\n",
53 avcodec_get_name(s
->streams
[i
]->codecpar
->codec_id
));
54 return AVERROR(EINVAL
);
57 if (s
->streams
[i
]->codecpar
->ch_layout
.nb_channels
> 2) {
58 av_log(s
, AV_LOG_ERROR
, "Unsupported channel layout on stream #%d\n", i
);
59 return AVERROR(EINVAL
);
62 for (int j
= 0; j
< i
; j
++) {
63 if (s
->streams
[i
]->id
== s
->streams
[j
]->id
) {
64 av_log(s
, AV_LOG_ERROR
, "Duplicated stream id %d\n", s
->streams
[j
]->id
);
65 return AVERROR(EINVAL
);
70 if (s
->nb_stream_groups
<= 1) {
71 av_log(s
, AV_LOG_ERROR
, "There must be at least two stream groups\n");
72 return AVERROR(EINVAL
);
75 for (int i
= 0; i
< s
->nb_stream_groups
; i
++) {
76 const AVStreamGroup
*stg
= s
->stream_groups
[i
];
78 if (stg
->type
== AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT
)
80 if (stg
->type
== AV_STREAM_GROUP_PARAMS_IAMF_MIX_PRESENTATION
)
81 nb_mix_presentations
++;
83 if ((nb_audio_elements
< 1 || nb_audio_elements
> 2) || nb_mix_presentations
< 1) {
84 av_log(s
, AV_LOG_ERROR
, "There must be >= 1 and <= 2 IAMF_AUDIO_ELEMENT and at least "
85 "one IAMF_MIX_PRESENTATION stream groups\n");
86 return AVERROR(EINVAL
);
89 for (int i
= 0; i
< s
->nb_stream_groups
; i
++) {
90 const AVStreamGroup
*stg
= s
->stream_groups
[i
];
91 if (stg
->type
!= AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT
)
94 ret
= ff_iamf_add_audio_element(iamf
, stg
, s
);
99 for (int i
= 0; i
< s
->nb_stream_groups
; i
++) {
100 const AVStreamGroup
*stg
= s
->stream_groups
[i
];
101 if (stg
->type
!= AV_STREAM_GROUP_PARAMS_IAMF_MIX_PRESENTATION
)
104 ret
= ff_iamf_add_mix_presentation(iamf
, stg
, s
);
109 c
->first_stream_id
= s
->streams
[0]->id
;
114 static int iamf_write_header(AVFormatContext
*s
)
116 IAMFMuxContext
*const c
= s
->priv_data
;
117 IAMFContext
*const iamf
= &c
->iamf
;
120 c
->descriptors_offset
= avio_tell(s
->pb
);
121 ret
= ff_iamf_write_descriptors(iamf
, s
->pb
, s
);
125 c
->first_stream_id
= s
->streams
[0]->id
;
130 static int iamf_write_packet(AVFormatContext
*s
, AVPacket
*pkt
)
132 IAMFMuxContext
*const c
= s
->priv_data
;
133 AVStream
*st
= s
->streams
[pkt
->stream_index
];
136 if (st
->id
== c
->first_stream_id
)
137 ret
= ff_iamf_write_parameter_blocks(&c
->iamf
, s
->pb
, pkt
, s
);
139 ret
= ff_iamf_write_audio_frame(&c
->iamf
, s
->pb
, st
->id
, pkt
);
140 if (!ret
&& !pkt
->size
)
141 c
->update_extradata
= 1;
146 static int iamf_write_trailer(AVFormatContext
*s
)
148 const IAMFMuxContext
*const c
= s
->priv_data
;
149 const IAMFContext
*const iamf
= &c
->iamf
;
153 if (!c
->update_extradata
|| !(s
->pb
->seekable
& AVIO_SEEKABLE_NORMAL
))
156 pos
= avio_tell(s
->pb
);
157 avio_seek(s
->pb
, c
->descriptors_offset
, SEEK_SET
);
158 ret
= ff_iamf_write_descriptors(iamf
, s
->pb
, s
);
162 avio_seek(s
->pb
, pos
, SEEK_SET
);
167 static void iamf_deinit(AVFormatContext
*s
)
169 IAMFMuxContext
*const c
= s
->priv_data
;
170 IAMFContext
*const iamf
= &c
->iamf
;
172 ff_iamf_uninit_context(iamf
);
175 static const AVCodecTag iamf_codec_tags
[] = {
176 { AV_CODEC_ID_AAC
, MKTAG('m','p','4','a') },
177 { AV_CODEC_ID_FLAC
, MKTAG('f','L','a','C') },
178 { AV_CODEC_ID_OPUS
, MKTAG('O','p','u','s') },
179 { AV_CODEC_ID_PCM_S16LE
, MKTAG('i','p','c','m') },
180 { AV_CODEC_ID_PCM_S16BE
, MKTAG('i','p','c','m') },
181 { AV_CODEC_ID_PCM_S24LE
, MKTAG('i','p','c','m') },
182 { AV_CODEC_ID_PCM_S24BE
, MKTAG('i','p','c','m') },
183 { AV_CODEC_ID_PCM_S32LE
, MKTAG('i','p','c','m') },
184 { AV_CODEC_ID_PCM_S32BE
, MKTAG('i','p','c','m') },
185 { AV_CODEC_ID_NONE
, MKTAG('i','p','c','m') }
188 const FFOutputFormat ff_iamf_muxer
= {
190 .p
.long_name
= NULL_IF_CONFIG_SMALL("Raw Immersive Audio Model and Formats"),
191 .p
.extensions
= "iamf",
192 .priv_data_size
= sizeof(IAMFMuxContext
),
193 .p
.audio_codec
= AV_CODEC_ID_OPUS
,
195 .deinit
= iamf_deinit
,
196 .write_header
= iamf_write_header
,
197 .write_packet
= iamf_write_packet
,
198 .write_trailer
= iamf_write_trailer
,
199 .p
.codec_tag
= (const AVCodecTag
* const []){ iamf_codec_tags
, NULL
},
200 .p
.flags
= AVFMT_GLOBALHEADER
| AVFMT_NOTIMESTAMPS
,