FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavformat/aiffenc.c
Date: 2024-03-28 14:59:00
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 "libavcodec/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, &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, &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 1 times.
✓ Branch 1 taken 2 times.
3 if (size & 1)
79 1 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
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) {
151 1 ffio_wfourcc(pb, "CHAN");
152 1 avio_wb32(pb, 12);
153 1 ff_mov_write_chan(pb, par->ch_layout.u.mask);
154 }
155
156 6 put_meta(s, "title", MKBETAG('N', 'A', 'M', 'E'));
157 6 put_meta(s, "author", MKBETAG('A', 'U', 'T', 'H'));
158 6 put_meta(s, "copyright", MKBETAG('(', 'c', ')', ' '));
159 6 put_meta(s, "comment", MKBETAG('A', 'N', 'N', 'O'));
160
161 /* Common chunk */
162 6 ffio_wfourcc(pb, "COMM");
163
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
6 avio_wb32(pb, aifc ? 24 : 18); /* size */
164 6 avio_wb16(pb, par->ch_layout.nb_channels); /* Number of channels */
165
166 6 aiff->frames = avio_tell(pb);
167 6 avio_wb32(pb, 0); /* Number of frames */
168
169
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (!par->bits_per_coded_sample)
170 par->bits_per_coded_sample = av_get_bits_per_sample(par->codec_id);
171
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (!par->bits_per_coded_sample) {
172 av_log(s, AV_LOG_ERROR, "could not compute bits per sample\n");
173 return AVERROR(EINVAL);
174 }
175
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (!par->block_align)
176 par->block_align = (par->bits_per_coded_sample * par->ch_layout.nb_channels) >> 3;
177
178 6 avio_wb16(pb, par->bits_per_coded_sample); /* Sample size */
179
180 6 sample_rate = av_double2int(par->sample_rate);
181 6 avio_wb16(pb, (sample_rate >> 52) + (16383 - 1023));
182 6 avio_wb64(pb, UINT64_C(1) << 63 | sample_rate << 11);
183
184
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
6 if (aifc) {
185 3 avio_wl32(pb, par->codec_tag);
186 3 avio_wb16(pb, 0);
187 }
188
189
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 if ( (par->codec_tag == MKTAG('Q','D','M','2')
190
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) {
191 ffio_wfourcc(pb, "wave");
192 avio_wb32(pb, par->extradata_size);
193 avio_write(pb, par->extradata, par->extradata_size);
194 }
195
196 /* Sound data chunk */
197 6 ffio_wfourcc(pb, "SSND");
198 6 aiff->ssnd = avio_tell(pb); /* Sound chunk size */
199 6 avio_wb32(pb, 0); /* Sound samples data size */
200 6 avio_wb32(pb, 0); /* Data offset */
201 6 avio_wb32(pb, 0); /* Block-size (block align) */
202
203 6 avpriv_set_pts_info(s->streams[aiff->audio_stream_idx], 64, 1,
204 6 s->streams[aiff->audio_stream_idx]->codecpar->sample_rate);
205
206 6 return 0;
207 }
208
209 8450 static int aiff_write_packet(AVFormatContext *s, AVPacket *pkt)
210 {
211 8450 AIFFOutputContext *aiff = s->priv_data;
212 8450 AVIOContext *pb = s->pb;
213
2/2
✓ Branch 0 taken 8445 times.
✓ Branch 1 taken 5 times.
8450 if (pkt->stream_index == aiff->audio_stream_idx)
214 8445 avio_write(pb, pkt->data, pkt->size);
215 else {
216 /* warn only once for each stream */
217
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if (s->streams[pkt->stream_index]->nb_frames == 1) {
218 av_log(s, AV_LOG_WARNING, "Got more than one picture in stream %d,"
219 " ignoring.\n", pkt->stream_index);
220 }
221
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if (s->streams[pkt->stream_index]->nb_frames >= 1)
222 return 0;
223
224 5 return avpriv_packet_list_put(&aiff->pict_list, pkt, NULL, 0);
225 }
226
227 8445 return 0;
228 }
229
230 6 static int aiff_write_trailer(AVFormatContext *s)
231 {
232 6 int ret = 0;
233 6 AVIOContext *pb = s->pb;
234 6 AIFFOutputContext *aiff = s->priv_data;
235 6 AVCodecParameters *par = s->streams[aiff->audio_stream_idx]->codecpar;
236
237 /* Chunks sizes must be even */
238 int64_t file_size, data_size;
239 6 data_size = avio_tell(pb);
240
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (data_size & 1)
241 avio_w8(pb, 0);
242
243
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 if (s->pb->seekable & AVIO_SEEKABLE_NORMAL) {
244 /* Write ID3 tags */
245
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
6 if (aiff->write_id3v2)
246
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
3 if ((ret = put_id3v2_tags(s, aiff)) < 0)
247 return ret;
248
249 /* File length */
250 6 file_size = avio_tell(pb);
251 6 avio_seek(pb, aiff->form, SEEK_SET);
252 6 avio_wb32(pb, file_size - aiff->form - 4);
253
254 /* Number of sample frames */
255 6 avio_seek(pb, aiff->frames, SEEK_SET);
256 6 avio_wb32(pb, (data_size - aiff->ssnd - 12) / par->block_align);
257
258 /* Sound Data chunk size */
259 6 avio_seek(pb, aiff->ssnd, SEEK_SET);
260 6 avio_wb32(pb, data_size - aiff->ssnd - 4);
261 }
262
263 6 return ret;
264 }
265
266 6 static void aiff_deinit(AVFormatContext *s)
267 {
268 6 AIFFOutputContext *aiff = s->priv_data;
269
270 6 avpriv_packet_list_free(&aiff->pict_list);
271 6 }
272
273 #define OFFSET(x) offsetof(AIFFOutputContext, x)
274 #define ENC AV_OPT_FLAG_ENCODING_PARAM
275 static const AVOption options[] = {
276 { "write_id3v2", "Enable ID3 tags writing.",
277 OFFSET(write_id3v2), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, ENC },
278 { "id3v2_version", "Select ID3v2 version to write. Currently 3 and 4 are supported.",
279 OFFSET(id3v2_version), AV_OPT_TYPE_INT, {.i64 = 4}, 3, 4, ENC },
280 { NULL },
281 };
282
283 static const AVClass aiff_muxer_class = {
284 .class_name = "AIFF muxer",
285 .item_name = av_default_item_name,
286 .option = options,
287 .version = LIBAVUTIL_VERSION_INT,
288 };
289
290 const FFOutputFormat ff_aiff_muxer = {
291 .p.name = "aiff",
292 .p.long_name = NULL_IF_CONFIG_SMALL("Audio IFF"),
293 .p.mime_type = "audio/aiff",
294 .p.extensions = "aif,aiff,afc,aifc",
295 .priv_data_size = sizeof(AIFFOutputContext),
296 .p.audio_codec = AV_CODEC_ID_PCM_S16BE,
297 .p.video_codec = AV_CODEC_ID_PNG,
298 .write_header = aiff_write_header,
299 .write_packet = aiff_write_packet,
300 .write_trailer = aiff_write_trailer,
301 .deinit = aiff_deinit,
302 .p.codec_tag = ff_aiff_codec_tags_list,
303 .p.priv_class = &aiff_muxer_class,
304 };
305