FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavformat/aiffenc.c
Date: 2026-08-29 16:27:19
Exec Total Coverage
Lines: 119 139 85.6%
Functions: 6 6 100.0%
Branches: 44 68 64.7%

Line Branch Exec Source
1 /*
2 * AIFF/AIFF-C muxer
3 * Copyright (c) 2006 Patrick Guimond
4 *
5 * This file is part of FFmpeg.
6 *
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.
11 *
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.
16 *
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
20 */
21
22 #include <stdint.h>
23
24 #include "libavutil/intfloat.h"
25 #include "libavutil/opt.h"
26 #include "packet_internal.h"
27 #include "avformat.h"
28 #include "internal.h"
29 #include "aiff.h"
30 #include "avio_internal.h"
31 #include "isom.h"
32 #include "id3v2.h"
33 #include "mux.h"
34
35 typedef struct AIFFOutputContext {
36 const AVClass *class;
37 int64_t form;
38 int64_t frames;
39 int64_t ssnd;
40 int audio_stream_idx;
41 PacketList pict_list;
42 int write_id3v2;
43 int id3v2_version;
44 } AIFFOutputContext;
45
46 3 static int put_id3v2_tags(AVFormatContext *s, AIFFOutputContext *aiff)
47 {
48 int ret;
49 uint64_t pos, end, size;
50 3 ID3v2EncContext id3v2 = { 0 };
51 3 AVIOContext *pb = s->pb;
52 3 PacketListEntry *list_entry = aiff->pict_list.head;
53
54
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
3 if (!s->metadata && !s->nb_chapters && !list_entry)
55 return 0;
56
57 3 avio_wb32(pb, MKBETAG('I', 'D', '3', ' '));
58 3 avio_wb32(pb, 0);
59 3 pos = avio_tell(pb);
60
61 3 ff_id3v2_start(&id3v2, pb, aiff->id3v2_version, ID3v2_DEFAULT_MAGIC);
62 3 ff_id3v2_write_metadata(s, pb, &id3v2);
63
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 3 times.
8 while (list_entry) {
64
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
5 if ((ret = ff_id3v2_write_apic(s, pb, &id3v2, &list_entry->pkt)) < 0)
65 return ret;
66 5 list_entry = list_entry->next;
67 }
68 3 ff_id3v2_finish(&id3v2, pb, s->metadata_header_padding);
69
70 3 end = avio_tell(pb);
71 3 size = end - pos;
72
73 /* Update chunk size */
74 3 avio_seek(pb, pos - 4, SEEK_SET);
75 3 avio_wb32(pb, size);
76 3 avio_seek(pb, end, SEEK_SET);
77
78
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
3 if (size & 1)
79 2 avio_w8(pb, 0);
80
81 3 return 0;
82 }
83
84 24 static void put_meta(AVFormatContext *s, const char *key, uint32_t id)
85 {
86 AVDictionaryEntry *tag;
87 24 AVIOContext *pb = s->pb;
88
89
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 20 times.
24 if (tag = av_dict_get(s->metadata, key, NULL, 0)) {
90 4 size_t size = strlen(tag->value);
91
92 // AIFF tags are zero-padded to an even length.
93 // So simply copy the terminating \0 if the length is odd.
94 4 size = FFALIGN(size, 2);
95
96 4 avio_wb32(pb, id);
97 4 avio_wb32(pb, size);
98 4 avio_write(pb, tag->value, size);
99 }
100 24 }
101
102 6 static int aiff_write_header(AVFormatContext *s)
103 {
104 6 AIFFOutputContext *aiff = s->priv_data;
105 6 AVIOContext *pb = s->pb;
106 AVCodecParameters *par;
107 uint64_t sample_rate;
108 6 int i, aifc = 0;
109
110 6 aiff->audio_stream_idx = -1;
111
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 6 times.
17 for (i = 0; i < s->nb_streams; i++) {
112 11 AVStream *st = s->streams[i];
113
3/4
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
11 if (aiff->audio_stream_idx < 0 && st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
114 6 aiff->audio_stream_idx = i;
115
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 } else if (st->codecpar->codec_type != AVMEDIA_TYPE_VIDEO) {
116 av_log(s, AV_LOG_ERROR, "AIFF allows only one audio stream and a picture.\n");
117 return AVERROR(EINVAL);
118 }
119 }
120
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (aiff->audio_stream_idx < 0) {
121 av_log(s, AV_LOG_ERROR, "No audio stream present.\n");
122 return AVERROR(EINVAL);
123 }
124
125 6 par = s->streams[aiff->audio_stream_idx]->codecpar;
126
127 /* First verify if format is ok */
128
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (!par->codec_tag)
129 return AVERROR(EINVAL);
130
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
6 if (par->codec_tag != MKTAG('N','O','N','E'))
131 3 aifc = 1;
132
133 /* FORM AIFF header */
134 6 ffio_wfourcc(pb, "FORM");
135 6 aiff->form = avio_tell(pb);
136 6 avio_wb32(pb, 0); /* file length */
137
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
6 ffio_wfourcc(pb, aifc ? "AIFC" : "AIFF");
138
139
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
6 if (aifc) { // compressed audio
140
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (!par->block_align) {
141 av_log(s, AV_LOG_ERROR, "block align not set\n");
142 return AVERROR(EINVAL);
143 }
144 /* Version chunk */
145 3 ffio_wfourcc(pb, "FVER");
146 3 avio_wb32(pb, 4);
147 3 avio_wb32(pb, 0xA2805140);
148 }
149
150 6 put_meta(s, "title", MKBETAG('N', 'A', 'M', 'E'));
151 6 put_meta(s, "author", MKBETAG('A', 'U', 'T', 'H'));
152 6 put_meta(s, "copyright", MKBETAG('(', 'c', ')', ' '));
153 6 put_meta(s, "comment", MKBETAG('A', 'N', 'N', 'O'));
154
155 /* Common chunk */
156 6 ffio_wfourcc(pb, "COMM");
157
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
6 avio_wb32(pb, aifc ? 24 : 18); /* size */
158 6 avio_wb16(pb, par->ch_layout.nb_channels); /* Number of channels */
159
160 6 aiff->frames = avio_tell(pb);
161 6 avio_wb32(pb, 0); /* Number of frames */
162
163
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (!par->bits_per_coded_sample)
164 par->bits_per_coded_sample = av_get_bits_per_sample(par->codec_id);
165
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (!par->bits_per_coded_sample) {
166 av_log(s, AV_LOG_ERROR, "could not compute bits per sample\n");
167 return AVERROR(EINVAL);
168 }
169
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (!par->block_align)
170 par->block_align = (par->bits_per_coded_sample * par->ch_layout.nb_channels) >> 3;
171
172 6 avio_wb16(pb, par->bits_per_coded_sample); /* Sample size */
173
174 6 sample_rate = av_double2int(par->sample_rate);
175 6 avio_wb16(pb, (sample_rate >> 52) + (16383 - 1023));
176 6 avio_wb64(pb, UINT64_C(1) << 63 | sample_rate << 11);
177
178
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
6 if (aifc) {
179 3 avio_wl32(pb, par->codec_tag);
180 3 avio_wb16(pb, 0);
181 }
182
183
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 if ( (par->codec_tag == MKTAG('Q','D','M','2')
184
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
6 || par->codec_tag == MKTAG('Q','c','l','p')) && par->extradata_size) {
185 ffio_wfourcc(pb, "wave");
186 avio_wb32(pb, par->extradata_size);
187 avio_write(pb, par->extradata, par->extradata_size);
188 }
189
190 /* CHAN chunk; a decoder may use the channel count when parsing this chunk,
191 * so let's write it after the COMM chunk which indicates said channel count. */
192
3/4
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 5 times.
6 if (par->ch_layout.order == AV_CHANNEL_ORDER_NATIVE && par->ch_layout.nb_channels > 2) {
193 1 ffio_wfourcc(pb, "CHAN");
194 1 avio_wb32(pb, 12);
195 1 ff_mov_write_chan(pb, par->ch_layout.u.mask);
196 }
197
198 /* Sound data chunk */
199 6 ffio_wfourcc(pb, "SSND");
200 6 aiff->ssnd = avio_tell(pb); /* Sound chunk size */
201 6 avio_wb32(pb, 0); /* Sound samples data size */
202 6 avio_wb32(pb, 0); /* Data offset */
203 6 avio_wb32(pb, 0); /* Block-size (block align) */
204
205 6 avpriv_set_pts_info(s->streams[aiff->audio_stream_idx], 64, 1,
206 6 s->streams[aiff->audio_stream_idx]->codecpar->sample_rate);
207
208 6 return 0;
209 }
210
211 8450 static int aiff_write_packet(AVFormatContext *s, AVPacket *pkt)
212 {
213 8450 AIFFOutputContext *aiff = s->priv_data;
214 8450 AVIOContext *pb = s->pb;
215
2/2
✓ Branch 0 taken 8445 times.
✓ Branch 1 taken 5 times.
8450 if (pkt->stream_index == aiff->audio_stream_idx)
216 8445 avio_write(pb, pkt->data, pkt->size);
217 else {
218 /* warn only once for each stream */
219
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if (s->streams[pkt->stream_index]->nb_frames == 1) {
220 av_log(s, AV_LOG_WARNING, "Got more than one picture in stream %d,"
221 " ignoring.\n", pkt->stream_index);
222 }
223
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if (s->streams[pkt->stream_index]->nb_frames >= 1)
224 return 0;
225
226 5 return ff_packet_list_put(&aiff->pict_list, pkt, NULL, 0);
227 }
228
229 8445 return 0;
230 }
231
232 6 static int aiff_write_trailer(AVFormatContext *s)
233 {
234 6 int ret = 0;
235 6 AVIOContext *pb = s->pb;
236 6 AIFFOutputContext *aiff = s->priv_data;
237 6 AVCodecParameters *par = s->streams[aiff->audio_stream_idx]->codecpar;
238
239 /* Chunks sizes must be even */
240 int64_t file_size, data_size;
241 6 data_size = avio_tell(pb);
242
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (data_size & 1)
243 avio_w8(pb, 0);
244
245
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 if (s->pb->seekable & AVIO_SEEKABLE_NORMAL) {
246 /* Write ID3 tags */
247
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
6 if (aiff->write_id3v2)
248
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
3 if ((ret = put_id3v2_tags(s, aiff)) < 0)
249 return ret;
250
251 /* File length */
252 6 file_size = avio_tell(pb);
253 6 avio_seek(pb, aiff->form, SEEK_SET);
254 6 avio_wb32(pb, file_size - aiff->form - 4);
255
256 /* Number of sample frames */
257 6 avio_seek(pb, aiff->frames, SEEK_SET);
258 6 avio_wb32(pb, (data_size - aiff->ssnd - 12) / par->block_align);
259
260 /* Sound Data chunk size */
261 6 avio_seek(pb, aiff->ssnd, SEEK_SET);
262 6 avio_wb32(pb, data_size - aiff->ssnd - 4);
263 }
264
265 6 return ret;
266 }
267
268 6 static void aiff_deinit(AVFormatContext *s)
269 {
270 6 AIFFOutputContext *aiff = s->priv_data;
271
272 6 ff_packet_list_free(&aiff->pict_list);
273 6 }
274
275 #define OFFSET(x) offsetof(AIFFOutputContext, x)
276 #define ENC AV_OPT_FLAG_ENCODING_PARAM
277 static const AVOption options[] = {
278 { "write_id3v2", "Enable ID3 tags writing.",
279 OFFSET(write_id3v2), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, ENC },
280 { "id3v2_version", "Select ID3v2 version to write. Currently 3 and 4 are supported.",
281 OFFSET(id3v2_version), AV_OPT_TYPE_INT, {.i64 = 4}, 3, 4, ENC },
282 { NULL },
283 };
284
285 static const AVClass aiff_muxer_class = {
286 .class_name = "AIFF muxer",
287 .item_name = av_default_item_name,
288 .option = options,
289 .version = LIBAVUTIL_VERSION_INT,
290 };
291
292 const FFOutputFormat ff_aiff_muxer = {
293 .p.name = "aiff",
294 .p.long_name = NULL_IF_CONFIG_SMALL("Audio IFF"),
295 .p.mime_type = "audio/aiff",
296 .p.extensions = "aif,aiff,afc,aifc",
297 .priv_data_size = sizeof(AIFFOutputContext),
298 .p.audio_codec = AV_CODEC_ID_PCM_S16BE,
299 .p.video_codec = AV_CODEC_ID_PNG,
300 .write_header = aiff_write_header,
301 .write_packet = aiff_write_packet,
302 .write_trailer = aiff_write_trailer,
303 .deinit = aiff_deinit,
304 .p.codec_tag = ff_aiff_codec_tags_list,
305 .p.priv_class = &aiff_muxer_class,
306 };
307