Go to most recent revision | Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
4349 | Serge | 1 | /* |
2 | * WAV muxer |
||
3 | * Copyright (c) 2001, 2002 Fabrice Bellard |
||
4 | * |
||
5 | * Sony Wave64 muxer |
||
6 | * Copyright (c) 2012 Paul B Mahol |
||
7 | * |
||
8 | * WAV muxer RF64 support |
||
9 | * Copyright (c) 2013 Daniel Verkamp |
||
10 | * |
||
11 | * This file is part of FFmpeg. |
||
12 | * |
||
13 | * FFmpeg is free software; you can redistribute it and/or |
||
14 | * modify it under the terms of the GNU Lesser General Public |
||
15 | * License as published by the Free Software Foundation; either |
||
16 | * version 2.1 of the License, or (at your option) any later version. |
||
17 | * |
||
18 | * FFmpeg is distributed in the hope that it will be useful, |
||
19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||
21 | * Lesser General Public License for more details. |
||
22 | * |
||
23 | * You should have received a copy of the GNU Lesser General Public |
||
24 | * License along with FFmpeg; if not, write to the Free Software |
||
25 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||
26 | */ |
||
27 | |||
28 | #include |
||
29 | #include |
||
30 | |||
31 | #include "libavutil/dict.h" |
||
32 | #include "libavutil/common.h" |
||
33 | #include "libavutil/mathematics.h" |
||
34 | #include "libavutil/opt.h" |
||
35 | |||
36 | #include "avformat.h" |
||
37 | #include "avio.h" |
||
38 | #include "avio_internal.h" |
||
39 | #include "internal.h" |
||
40 | #include "riff.h" |
||
41 | |||
42 | #define RF64_AUTO (-1) |
||
43 | #define RF64_NEVER 0 |
||
44 | #define RF64_ALWAYS 1 |
||
45 | |||
46 | typedef struct WAVMuxContext { |
||
47 | const AVClass *class; |
||
48 | int64_t data; |
||
49 | int64_t fact_pos; |
||
50 | int64_t ds64; |
||
51 | int64_t minpts; |
||
52 | int64_t maxpts; |
||
53 | int last_duration; |
||
54 | int write_bext; |
||
55 | int rf64; |
||
56 | } WAVMuxContext; |
||
57 | |||
58 | #if CONFIG_WAV_MUXER |
||
59 | static inline void bwf_write_bext_string(AVFormatContext *s, const char *key, int maxlen) |
||
60 | { |
||
61 | AVDictionaryEntry *tag; |
||
62 | int len = 0; |
||
63 | |||
64 | if (tag = av_dict_get(s->metadata, key, NULL, 0)) { |
||
65 | len = strlen(tag->value); |
||
66 | len = FFMIN(len, maxlen); |
||
67 | avio_write(s->pb, tag->value, len); |
||
68 | } |
||
69 | |||
70 | ffio_fill(s->pb, 0, maxlen - len); |
||
71 | } |
||
72 | |||
73 | static void bwf_write_bext_chunk(AVFormatContext *s) |
||
74 | { |
||
75 | AVDictionaryEntry *tmp_tag; |
||
76 | uint64_t time_reference = 0; |
||
77 | int64_t bext = ff_start_tag(s->pb, "bext"); |
||
78 | |||
79 | bwf_write_bext_string(s, "description", 256); |
||
80 | bwf_write_bext_string(s, "originator", 32); |
||
81 | bwf_write_bext_string(s, "originator_reference", 32); |
||
82 | bwf_write_bext_string(s, "origination_date", 10); |
||
83 | bwf_write_bext_string(s, "origination_time", 8); |
||
84 | |||
85 | if (tmp_tag = av_dict_get(s->metadata, "time_reference", NULL, 0)) |
||
86 | time_reference = strtoll(tmp_tag->value, NULL, 10); |
||
87 | avio_wl64(s->pb, time_reference); |
||
88 | avio_wl16(s->pb, 1); // set version to 1 |
||
89 | |||
90 | if (tmp_tag = av_dict_get(s->metadata, "umid", NULL, 0)) { |
||
91 | unsigned char umidpart_str[17] = {0}; |
||
92 | int i; |
||
93 | uint64_t umidpart; |
||
94 | int len = strlen(tmp_tag->value+2); |
||
95 | |||
96 | for (i = 0; i < len/16; i++) { |
||
97 | memcpy(umidpart_str, tmp_tag->value + 2 + (i*16), 16); |
||
98 | umidpart = strtoll(umidpart_str, NULL, 16); |
||
99 | avio_wb64(s->pb, umidpart); |
||
100 | } |
||
101 | ffio_fill(s->pb, 0, 64 - i*8); |
||
102 | } else |
||
103 | ffio_fill(s->pb, 0, 64); // zero UMID |
||
104 | |||
105 | ffio_fill(s->pb, 0, 190); // Reserved |
||
106 | |||
107 | if (tmp_tag = av_dict_get(s->metadata, "coding_history", NULL, 0)) |
||
108 | avio_put_str(s->pb, tmp_tag->value); |
||
109 | |||
110 | ff_end_tag(s->pb, bext); |
||
111 | } |
||
112 | |||
113 | static int wav_write_header(AVFormatContext *s) |
||
114 | { |
||
115 | WAVMuxContext *wav = s->priv_data; |
||
116 | AVIOContext *pb = s->pb; |
||
117 | int64_t fmt; |
||
118 | |||
119 | if (wav->rf64 == RF64_ALWAYS) { |
||
120 | ffio_wfourcc(pb, "RF64"); |
||
121 | avio_wl32(pb, -1); /* RF64 chunk size: use size in ds64 */ |
||
122 | } else { |
||
123 | ffio_wfourcc(pb, "RIFF"); |
||
124 | avio_wl32(pb, 0); /* file length */ |
||
125 | } |
||
126 | |||
127 | ffio_wfourcc(pb, "WAVE"); |
||
128 | |||
129 | if (wav->rf64 != RF64_NEVER) { |
||
130 | /* write empty ds64 chunk or JUNK chunk to reserve space for ds64 */ |
||
131 | ffio_wfourcc(pb, wav->rf64 == RF64_ALWAYS ? "ds64" : "JUNK"); |
||
132 | avio_wl32(pb, 28); /* chunk size */ |
||
133 | wav->ds64 = avio_tell(pb); |
||
134 | ffio_fill(pb, 0, 28); |
||
135 | } |
||
136 | |||
137 | /* format header */ |
||
138 | fmt = ff_start_tag(pb, "fmt "); |
||
139 | if (ff_put_wav_header(pb, s->streams[0]->codec) < 0) { |
||
140 | av_log(s, AV_LOG_ERROR, "%s codec not supported in WAVE format\n", |
||
141 | s->streams[0]->codec->codec ? s->streams[0]->codec->codec->name : "NONE"); |
||
142 | return -1; |
||
143 | } |
||
144 | ff_end_tag(pb, fmt); |
||
145 | |||
146 | if (s->streams[0]->codec->codec_tag != 0x01 /* hence for all other than PCM */ |
||
147 | && s->pb->seekable) { |
||
148 | wav->fact_pos = ff_start_tag(pb, "fact"); |
||
149 | avio_wl32(pb, 0); |
||
150 | ff_end_tag(pb, wav->fact_pos); |
||
151 | } |
||
152 | |||
153 | if (wav->write_bext) |
||
154 | bwf_write_bext_chunk(s); |
||
155 | |||
156 | avpriv_set_pts_info(s->streams[0], 64, 1, s->streams[0]->codec->sample_rate); |
||
157 | wav->maxpts = wav->last_duration = 0; |
||
158 | wav->minpts = INT64_MAX; |
||
159 | |||
160 | /* info header */ |
||
161 | ff_riff_write_info(s); |
||
162 | |||
163 | /* data header */ |
||
164 | wav->data = ff_start_tag(pb, "data"); |
||
165 | |||
166 | avio_flush(pb); |
||
167 | |||
168 | return 0; |
||
169 | } |
||
170 | |||
171 | static int wav_write_packet(AVFormatContext *s, AVPacket *pkt) |
||
172 | { |
||
173 | AVIOContext *pb = s->pb; |
||
174 | WAVMuxContext *wav = s->priv_data; |
||
175 | avio_write(pb, pkt->data, pkt->size); |
||
176 | if(pkt->pts != AV_NOPTS_VALUE) { |
||
177 | wav->minpts = FFMIN(wav->minpts, pkt->pts); |
||
178 | wav->maxpts = FFMAX(wav->maxpts, pkt->pts); |
||
179 | wav->last_duration = pkt->duration; |
||
180 | } else |
||
181 | av_log(s, AV_LOG_ERROR, "wav_write_packet: NOPTS\n"); |
||
182 | return 0; |
||
183 | } |
||
184 | |||
185 | static int wav_write_trailer(AVFormatContext *s) |
||
186 | { |
||
187 | AVIOContext *pb = s->pb; |
||
188 | WAVMuxContext *wav = s->priv_data; |
||
189 | int64_t file_size, data_size; |
||
190 | int64_t number_of_samples = 0; |
||
191 | int rf64 = 0; |
||
192 | |||
193 | avio_flush(pb); |
||
194 | |||
195 | if (s->pb->seekable) { |
||
196 | /* update file size */ |
||
197 | file_size = avio_tell(pb); |
||
198 | data_size = file_size - wav->data; |
||
199 | if (wav->rf64 == RF64_ALWAYS || (wav->rf64 == RF64_AUTO && file_size - 8 > UINT32_MAX)) { |
||
200 | rf64 = 1; |
||
201 | } else { |
||
202 | avio_seek(pb, 4, SEEK_SET); |
||
203 | avio_wl32(pb, (uint32_t)(file_size - 8)); |
||
204 | avio_seek(pb, file_size, SEEK_SET); |
||
205 | |||
206 | ff_end_tag(pb, wav->data); |
||
207 | avio_flush(pb); |
||
208 | } |
||
209 | |||
210 | number_of_samples = av_rescale(wav->maxpts - wav->minpts + wav->last_duration, |
||
211 | s->streams[0]->codec->sample_rate * (int64_t)s->streams[0]->time_base.num, |
||
212 | s->streams[0]->time_base.den); |
||
213 | |||
214 | if(s->streams[0]->codec->codec_tag != 0x01) { |
||
215 | /* Update num_samps in fact chunk */ |
||
216 | avio_seek(pb, wav->fact_pos, SEEK_SET); |
||
217 | if (rf64 || (wav->rf64 == RF64_AUTO && number_of_samples > UINT32_MAX)) { |
||
218 | rf64 = 1; |
||
219 | avio_wl32(pb, -1); |
||
220 | } else { |
||
221 | avio_wl32(pb, number_of_samples); |
||
222 | avio_seek(pb, file_size, SEEK_SET); |
||
223 | avio_flush(pb); |
||
224 | } |
||
225 | } |
||
226 | |||
227 | if (rf64) { |
||
228 | /* overwrite RIFF with RF64 */ |
||
229 | avio_seek(pb, 0, SEEK_SET); |
||
230 | ffio_wfourcc(pb, "RF64"); |
||
231 | avio_wl32(pb, -1); |
||
232 | |||
233 | /* write ds64 chunk (overwrite JUNK if rf64 == RF64_AUTO) */ |
||
234 | avio_seek(pb, wav->ds64 - 8, SEEK_SET); |
||
235 | ffio_wfourcc(pb, "ds64"); |
||
236 | avio_wl32(pb, 28); /* ds64 chunk size */ |
||
237 | avio_wl64(pb, file_size - 8); /* RF64 chunk size */ |
||
238 | avio_wl64(pb, data_size); /* data chunk size */ |
||
239 | avio_wl64(pb, number_of_samples); /* fact chunk number of samples */ |
||
240 | avio_wl32(pb, 0); /* number of table entries for non-'data' chunks */ |
||
241 | |||
242 | /* write -1 in data chunk size */ |
||
243 | avio_seek(pb, wav->data - 4, SEEK_SET); |
||
244 | avio_wl32(pb, -1); |
||
245 | |||
246 | avio_seek(pb, file_size, SEEK_SET); |
||
247 | avio_flush(pb); |
||
248 | } |
||
249 | } |
||
250 | return 0; |
||
251 | } |
||
252 | |||
253 | #define OFFSET(x) offsetof(WAVMuxContext, x) |
||
254 | #define ENC AV_OPT_FLAG_ENCODING_PARAM |
||
255 | static const AVOption options[] = { |
||
256 | { "write_bext", "Write BEXT chunk.", OFFSET(write_bext), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, ENC }, |
||
257 | { "rf64", "Use RF64 header rather than RIFF for large files.", OFFSET(rf64), AV_OPT_TYPE_INT, { .i64 = RF64_NEVER },-1, 1, ENC, "rf64" }, |
||
258 | { "auto", "Write RF64 header if file grows large enough.", 0, AV_OPT_TYPE_CONST, { .i64 = RF64_AUTO }, 0, 0, ENC, "rf64" }, |
||
259 | { "always", "Always write RF64 header regardless of file size.", 0, AV_OPT_TYPE_CONST, { .i64 = RF64_ALWAYS }, 0, 0, ENC, "rf64" }, |
||
260 | { "never", "Never write RF64 header regardless of file size.", 0, AV_OPT_TYPE_CONST, { .i64 = RF64_NEVER }, 0, 0, ENC, "rf64" }, |
||
261 | { NULL }, |
||
262 | }; |
||
263 | |||
264 | static const AVClass wav_muxer_class = { |
||
265 | .class_name = "WAV muxer", |
||
266 | .item_name = av_default_item_name, |
||
267 | .option = options, |
||
268 | .version = LIBAVUTIL_VERSION_INT, |
||
269 | }; |
||
270 | |||
271 | AVOutputFormat ff_wav_muxer = { |
||
272 | .name = "wav", |
||
273 | .long_name = NULL_IF_CONFIG_SMALL("WAV / WAVE (Waveform Audio)"), |
||
274 | .mime_type = "audio/x-wav", |
||
275 | .extensions = "wav", |
||
276 | .priv_data_size = sizeof(WAVMuxContext), |
||
277 | .audio_codec = AV_CODEC_ID_PCM_S16LE, |
||
278 | .video_codec = AV_CODEC_ID_NONE, |
||
279 | .write_header = wav_write_header, |
||
280 | .write_packet = wav_write_packet, |
||
281 | .write_trailer = wav_write_trailer, |
||
282 | .flags = AVFMT_TS_NONSTRICT, |
||
283 | .codec_tag = (const AVCodecTag* const []){ ff_codec_wav_tags, 0 }, |
||
284 | .priv_class = &wav_muxer_class, |
||
285 | }; |
||
286 | #endif /* CONFIG_WAV_MUXER */ |
||
287 | |||
288 | #if CONFIG_W64_MUXER |
||
289 | #include "w64.h" |
||
290 | |||
291 | static void start_guid(AVIOContext *pb, const uint8_t *guid, int64_t *pos) |
||
292 | { |
||
293 | *pos = avio_tell(pb); |
||
294 | |||
295 | avio_write(pb, guid, 16); |
||
296 | avio_wl64(pb, INT64_MAX); |
||
297 | } |
||
298 | |||
299 | static void end_guid(AVIOContext *pb, int64_t start) |
||
300 | { |
||
301 | int64_t end, pos = avio_tell(pb); |
||
302 | |||
303 | end = FFALIGN(pos, 8); |
||
304 | ffio_fill(pb, 0, end - pos); |
||
305 | avio_seek(pb, start + 16, SEEK_SET); |
||
306 | avio_wl64(pb, end - start); |
||
307 | avio_seek(pb, end, SEEK_SET); |
||
308 | } |
||
309 | |||
310 | static int w64_write_header(AVFormatContext *s) |
||
311 | { |
||
312 | WAVMuxContext *wav = s->priv_data; |
||
313 | AVIOContext *pb = s->pb; |
||
314 | int64_t start; |
||
315 | int ret; |
||
316 | |||
317 | avio_write(pb, ff_w64_guid_riff, sizeof(ff_w64_guid_riff)); |
||
318 | avio_wl64(pb, -1); |
||
319 | avio_write(pb, ff_w64_guid_wave, sizeof(ff_w64_guid_wave)); |
||
320 | start_guid(pb, ff_w64_guid_fmt, &start); |
||
321 | if ((ret = ff_put_wav_header(pb, s->streams[0]->codec)) < 0) { |
||
322 | av_log(s, AV_LOG_ERROR, "%s codec not supported\n", |
||
323 | s->streams[0]->codec->codec ? s->streams[0]->codec->codec->name : "NONE"); |
||
324 | return ret; |
||
325 | } |
||
326 | end_guid(pb, start); |
||
327 | |||
328 | if (s->streams[0]->codec->codec_tag != 0x01 /* hence for all other than PCM */ |
||
329 | && s->pb->seekable) { |
||
330 | start_guid(pb, ff_w64_guid_fact, &wav->fact_pos); |
||
331 | avio_wl64(pb, 0); |
||
332 | end_guid(pb, wav->fact_pos); |
||
333 | } |
||
334 | |||
335 | start_guid(pb, ff_w64_guid_data, &wav->data); |
||
336 | |||
337 | return 0; |
||
338 | } |
||
339 | |||
340 | static int w64_write_trailer(AVFormatContext *s) |
||
341 | { |
||
342 | AVIOContext *pb = s->pb; |
||
343 | WAVMuxContext *wav = s->priv_data; |
||
344 | int64_t file_size; |
||
345 | |||
346 | if (pb->seekable) { |
||
347 | end_guid(pb, wav->data); |
||
348 | |||
349 | file_size = avio_tell(pb); |
||
350 | avio_seek(pb, 16, SEEK_SET); |
||
351 | avio_wl64(pb, file_size); |
||
352 | |||
353 | if (s->streams[0]->codec->codec_tag != 0x01) { |
||
354 | int64_t number_of_samples; |
||
355 | |||
356 | number_of_samples = av_rescale(wav->maxpts - wav->minpts + wav->last_duration, |
||
357 | s->streams[0]->codec->sample_rate * (int64_t)s->streams[0]->time_base.num, |
||
358 | s->streams[0]->time_base.den); |
||
359 | avio_seek(pb, wav->fact_pos + 24, SEEK_SET); |
||
360 | avio_wl64(pb, number_of_samples); |
||
361 | } |
||
362 | |||
363 | avio_seek(pb, file_size, SEEK_SET); |
||
364 | avio_flush(pb); |
||
365 | } |
||
366 | |||
367 | return 0; |
||
368 | } |
||
369 | |||
370 | AVOutputFormat ff_w64_muxer = { |
||
371 | .name = "w64", |
||
372 | .long_name = NULL_IF_CONFIG_SMALL("Sony Wave64"), |
||
373 | .extensions = "w64", |
||
374 | .priv_data_size = sizeof(WAVMuxContext), |
||
375 | .audio_codec = AV_CODEC_ID_PCM_S16LE, |
||
376 | .video_codec = AV_CODEC_ID_NONE, |
||
377 | .write_header = w64_write_header, |
||
378 | .write_packet = wav_write_packet, |
||
379 | .write_trailer = w64_write_trailer, |
||
380 | .flags = AVFMT_TS_NONSTRICT, |
||
381 | .codec_tag = (const AVCodecTag* const []){ ff_codec_wav_tags, 0 }, |
||
382 | }; |
||
383 | #endif /* CONFIG_W64_MUXER */>>> |