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