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