... | ... |
@@ -30,7 +30,7 @@ |
30 | 30 |
#include "libavutil/avutil.h" |
31 | 31 |
|
32 | 32 |
#define LIBAVFORMAT_VERSION_MAJOR 54 |
33 |
-#define LIBAVFORMAT_VERSION_MINOR 62 |
|
33 |
+#define LIBAVFORMAT_VERSION_MINOR 63 |
|
34 | 34 |
#define LIBAVFORMAT_VERSION_MICRO 100 |
35 | 35 |
|
36 | 36 |
#define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ |
... | ... |
@@ -5,6 +5,9 @@ |
5 | 5 |
* Sony Wave64 muxer |
6 | 6 |
* Copyright (c) 2012 Paul B Mahol |
7 | 7 |
* |
8 |
+ * WAV muxer RF64 support |
|
9 |
+ * Copyright (c) 2013 Daniel Verkamp <daniel@drv.nu> |
|
10 |
+ * |
|
8 | 11 |
* This file is part of FFmpeg. |
9 | 12 |
* |
10 | 13 |
* FFmpeg is free software; you can redistribute it and/or |
... | ... |
@@ -36,14 +39,20 @@ |
36 | 36 |
#include "internal.h" |
37 | 37 |
#include "riff.h" |
38 | 38 |
|
39 |
+#define RF64_AUTO (-1) |
|
40 |
+#define RF64_NEVER 0 |
|
41 |
+#define RF64_ALWAYS 1 |
|
42 |
+ |
|
39 | 43 |
typedef struct WAVMuxContext { |
40 | 44 |
const AVClass *class; |
41 | 45 |
int64_t data; |
42 | 46 |
int64_t fact_pos; |
47 |
+ int64_t ds64; |
|
43 | 48 |
int64_t minpts; |
44 | 49 |
int64_t maxpts; |
45 | 50 |
int last_duration; |
46 | 51 |
int write_bext; |
52 |
+ int rf64; |
|
47 | 53 |
} WAVMuxContext; |
48 | 54 |
|
49 | 55 |
#if CONFIG_WAV_MUXER |
... | ... |
@@ -107,10 +116,24 @@ static int wav_write_header(AVFormatContext *s) |
107 | 107 |
AVIOContext *pb = s->pb; |
108 | 108 |
int64_t fmt; |
109 | 109 |
|
110 |
+ if (wav->rf64 == RF64_ALWAYS) { |
|
111 |
+ ffio_wfourcc(pb, "RF64"); |
|
112 |
+ avio_wl32(pb, -1); /* RF64 chunk size: use size in ds64 */ |
|
113 |
+ } else { |
|
110 | 114 |
ffio_wfourcc(pb, "RIFF"); |
111 | 115 |
avio_wl32(pb, 0); /* file length */ |
116 |
+ } |
|
117 |
+ |
|
112 | 118 |
ffio_wfourcc(pb, "WAVE"); |
113 | 119 |
|
120 |
+ if (wav->rf64 != RF64_NEVER) { |
|
121 |
+ /* write empty ds64 chunk or JUNK chunk to reserve space for ds64 */ |
|
122 |
+ ffio_wfourcc(pb, wav->rf64 == RF64_ALWAYS ? "ds64" : "JUNK"); |
|
123 |
+ avio_wl32(pb, 28); /* chunk size */ |
|
124 |
+ wav->ds64 = avio_tell(pb); |
|
125 |
+ ffio_fill(pb, 0, 28); |
|
126 |
+ } |
|
127 |
+ |
|
114 | 128 |
/* format header */ |
115 | 129 |
fmt = ff_start_tag(pb, "fmt "); |
116 | 130 |
if (ff_put_wav_header(pb, s->streams[0]->codec) < 0) { |
... | ... |
@@ -163,31 +186,65 @@ static int wav_write_trailer(AVFormatContext *s) |
163 | 163 |
{ |
164 | 164 |
AVIOContext *pb = s->pb; |
165 | 165 |
WAVMuxContext *wav = s->priv_data; |
166 |
- int64_t file_size; |
|
166 |
+ int64_t file_size, data_size; |
|
167 |
+ int64_t number_of_samples = 0; |
|
168 |
+ int rf64 = 0; |
|
167 | 169 |
|
168 | 170 |
avio_flush(pb); |
169 | 171 |
|
170 | 172 |
if (s->pb->seekable) { |
171 |
- ff_end_tag(pb, wav->data); |
|
172 |
- |
|
173 | 173 |
/* update file size */ |
174 | 174 |
file_size = avio_tell(pb); |
175 |
+ data_size = file_size - wav->data; |
|
176 |
+ if (wav->rf64 == RF64_ALWAYS || (wav->rf64 == RF64_AUTO && file_size - 8 > UINT32_MAX)) { |
|
177 |
+ rf64 = 1; |
|
178 |
+ } else { |
|
175 | 179 |
avio_seek(pb, 4, SEEK_SET); |
176 | 180 |
avio_wl32(pb, (uint32_t)(file_size - 8)); |
177 | 181 |
avio_seek(pb, file_size, SEEK_SET); |
178 | 182 |
|
183 |
+ ff_end_tag(pb, wav->data); |
|
179 | 184 |
avio_flush(pb); |
185 |
+ } |
|
180 | 186 |
|
181 |
- if(s->streams[0]->codec->codec_tag != 0x01) { |
|
182 |
- /* Update num_samps in fact chunk */ |
|
183 |
- int number_of_samples; |
|
184 | 187 |
number_of_samples = av_rescale(wav->maxpts - wav->minpts + wav->last_duration, |
185 | 188 |
s->streams[0]->codec->sample_rate * (int64_t)s->streams[0]->time_base.num, |
186 | 189 |
s->streams[0]->time_base.den); |
190 |
+ |
|
191 |
+ if(s->streams[0]->codec->codec_tag != 0x01) { |
|
192 |
+ /* Update num_samps in fact chunk */ |
|
187 | 193 |
avio_seek(pb, wav->fact_pos, SEEK_SET); |
194 |
+ if (rf64 || (wav->rf64 == RF64_AUTO && number_of_samples > UINT32_MAX)) { |
|
195 |
+ rf64 = 1; |
|
196 |
+ avio_wl32(pb, -1); |
|
197 |
+ } else { |
|
188 | 198 |
avio_wl32(pb, number_of_samples); |
189 | 199 |
avio_seek(pb, file_size, SEEK_SET); |
190 | 200 |
avio_flush(pb); |
201 |
+ } |
|
202 |
+ } |
|
203 |
+ |
|
204 |
+ if (rf64) { |
|
205 |
+ /* overwrite RIFF with RF64 */ |
|
206 |
+ avio_seek(pb, 0, SEEK_SET); |
|
207 |
+ ffio_wfourcc(pb, "RF64"); |
|
208 |
+ avio_wl32(pb, -1); |
|
209 |
+ |
|
210 |
+ /* write ds64 chunk (overwrite JUNK if rf64 == RF64_AUTO) */ |
|
211 |
+ avio_seek(pb, wav->ds64 - 8, SEEK_SET); |
|
212 |
+ ffio_wfourcc(pb, "ds64"); |
|
213 |
+ avio_wl32(pb, 28); /* ds64 chunk size */ |
|
214 |
+ avio_wl64(pb, file_size - 8); /* RF64 chunk size */ |
|
215 |
+ avio_wl64(pb, data_size); /* data chunk size */ |
|
216 |
+ avio_wl64(pb, number_of_samples); /* fact chunk number of samples */ |
|
217 |
+ avio_wl32(pb, 0); /* number of table entries for non-'data' chunks */ |
|
218 |
+ |
|
219 |
+ /* write -1 in data chunk size */ |
|
220 |
+ avio_seek(pb, wav->data - 4, SEEK_SET); |
|
221 |
+ avio_wl32(pb, -1); |
|
222 |
+ |
|
223 |
+ avio_seek(pb, file_size, SEEK_SET); |
|
224 |
+ avio_flush(pb); |
|
191 | 225 |
} |
192 | 226 |
} |
193 | 227 |
return 0; |
... | ... |
@@ -197,6 +254,10 @@ static int wav_write_trailer(AVFormatContext *s) |
197 | 197 |
#define ENC AV_OPT_FLAG_ENCODING_PARAM |
198 | 198 |
static const AVOption options[] = { |
199 | 199 |
{ "write_bext", "Write BEXT chunk.", OFFSET(write_bext), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, ENC }, |
200 |
+ { "rf64", "Use RF64 header rather than RIFF for large files.", OFFSET(rf64), AV_OPT_TYPE_INT, { .i64 = RF64_NEVER },-1, 1, ENC, "rf64" }, |
|
201 |
+ { "auto", "Write RF64 header if file grows large enough.", 0, AV_OPT_TYPE_CONST, { .i64 = RF64_AUTO }, 0, 0, ENC, "rf64" }, |
|
202 |
+ { "always", "Always write RF64 header regardless of file size.", 0, AV_OPT_TYPE_CONST, { .i64 = RF64_ALWAYS }, 0, 0, ENC, "rf64" }, |
|
203 |
+ { "never", "Never write RF64 header regardless of file size.", 0, AV_OPT_TYPE_CONST, { .i64 = RF64_NEVER }, 0, 0, ENC, "rf64" }, |
|
200 | 204 |
{ NULL }, |
201 | 205 |
}; |
202 | 206 |
|