Signed-off-by: Peter Ross <pross@xvid.org>
Signed-off-by: Michael Niedermayer <michaelni@gmx.at>
... | ... |
@@ -36,6 +36,7 @@ |
36 | 36 |
#include "libavutil/dict.h" |
37 | 37 |
#include "libavcodec/bytestream.h" |
38 | 38 |
#include "avformat.h" |
39 |
+#include "id3v2.h" |
|
39 | 40 |
#include "internal.h" |
40 | 41 |
|
41 | 42 |
#define ID_8SVX MKTAG('8','S','V','X') |
... | ... |
@@ -57,8 +58,10 @@ |
57 | 57 |
#define ID_DEEP MKTAG('D','E','E','P') |
58 | 58 |
#define ID_RGB8 MKTAG('R','G','B','8') |
59 | 59 |
#define ID_RGBN MKTAG('R','G','B','N') |
60 |
+#define ID_DSD MKTAG('D','S','D',' ') |
|
60 | 61 |
|
61 | 62 |
#define ID_FORM MKTAG('F','O','R','M') |
63 |
+#define ID_FRM8 MKTAG('F','R','M','8') |
|
62 | 64 |
#define ID_ANNO MKTAG('A','N','N','O') |
63 | 65 |
#define ID_AUTH MKTAG('A','U','T','H') |
64 | 66 |
#define ID_CHRS MKTAG('C','H','R','S') |
... | ... |
@@ -95,6 +98,7 @@ typedef enum { |
95 | 95 |
} svx8_compression_type; |
96 | 96 |
|
97 | 97 |
typedef struct { |
98 |
+ int is_64bit; ///< chunk size is 64-bit |
|
98 | 99 |
int64_t body_pos; |
99 | 100 |
int64_t body_end; |
100 | 101 |
uint32_t body_size; |
... | ... |
@@ -133,7 +137,7 @@ static int iff_probe(AVProbeData *p) |
133 | 133 |
{ |
134 | 134 |
const uint8_t *d = p->buf; |
135 | 135 |
|
136 |
- if ( AV_RL32(d) == ID_FORM && |
|
136 |
+ if ( (AV_RL32(d) == ID_FORM && |
|
137 | 137 |
(AV_RL32(d+8) == ID_8SVX || |
138 | 138 |
AV_RL32(d+8) == ID_16SV || |
139 | 139 |
AV_RL32(d+8) == ID_MAUD || |
... | ... |
@@ -142,11 +146,188 @@ static int iff_probe(AVProbeData *p) |
142 | 142 |
AV_RL32(d+8) == ID_DEEP || |
143 | 143 |
AV_RL32(d+8) == ID_ILBM || |
144 | 144 |
AV_RL32(d+8) == ID_RGB8 || |
145 |
- AV_RL32(d+8) == ID_RGBN) ) |
|
145 |
+ AV_RL32(d+8) == ID_RGB8 || |
|
146 |
+ AV_RL32(d+8) == ID_RGBN)) || |
|
147 |
+ (AV_RL32(d) == ID_FRM8 && AV_RL32(d+12) == ID_DSD)) |
|
146 | 148 |
return AVPROBE_SCORE_MAX; |
147 | 149 |
return 0; |
148 | 150 |
} |
149 | 151 |
|
152 |
+static const AVCodecTag dsd_codec_tags[] = { |
|
153 |
+ { AV_CODEC_ID_DSD_MSBF, ID_DSD }, |
|
154 |
+ { AV_CODEC_ID_NONE, 0 }, |
|
155 |
+}; |
|
156 |
+ |
|
157 |
+ |
|
158 |
+#define DSD_SLFT MKTAG('S','L','F','T') |
|
159 |
+#define DSD_SRGT MKTAG('S','R','G','T') |
|
160 |
+#define DSD_MLFT MKTAG('M','L','F','T') |
|
161 |
+#define DSD_MRGT MKTAG('M','R','G','T') |
|
162 |
+#define DSD_C MKTAG('C',' ',' ',' ') |
|
163 |
+#define DSD_LS MKTAG('L','S',' ',' ') |
|
164 |
+#define DSD_RS MKTAG('R','S',' ',' ') |
|
165 |
+#define DSD_LFE MKTAG('L','F','E',' ') |
|
166 |
+ |
|
167 |
+static const uint32_t dsd_stereo[] = { DSD_SLFT, DSD_SRGT }; |
|
168 |
+static const uint32_t dsd_5point0[] = { DSD_MLFT, DSD_MRGT, DSD_C, DSD_LS, DSD_RS }; |
|
169 |
+static const uint32_t dsd_5point1[] = { DSD_MLFT, DSD_MRGT, DSD_C, DSD_LFE, DSD_LS, DSD_RS }; |
|
170 |
+ |
|
171 |
+typedef struct { |
|
172 |
+ uint64_t layout; |
|
173 |
+ const uint32_t * dsd_layout; |
|
174 |
+} DSDLayoutDesc; |
|
175 |
+ |
|
176 |
+static const DSDLayoutDesc dsd_channel_layout[] = { |
|
177 |
+ { AV_CH_LAYOUT_STEREO, dsd_stereo }, |
|
178 |
+ { AV_CH_LAYOUT_5POINT0, dsd_5point0 }, |
|
179 |
+ { AV_CH_LAYOUT_5POINT1, dsd_5point1 }, |
|
180 |
+}; |
|
181 |
+ |
|
182 |
+static const uint64_t dsd_loudspeaker_config[] = { |
|
183 |
+ AV_CH_LAYOUT_STEREO, |
|
184 |
+ 0, 0, |
|
185 |
+ AV_CH_LAYOUT_5POINT0, AV_CH_LAYOUT_5POINT1, |
|
186 |
+}; |
|
187 |
+ |
|
188 |
+static const char * dsd_source_comment[] = { |
|
189 |
+ "dsd_source_comment", |
|
190 |
+ "analogue_source_comment", |
|
191 |
+ "pcm_source_comment", |
|
192 |
+}; |
|
193 |
+ |
|
194 |
+static const char * dsd_history_comment[] = { |
|
195 |
+ "general_remark", |
|
196 |
+ "operator_name", |
|
197 |
+ "creating_machine", |
|
198 |
+ "timezone", |
|
199 |
+ "file_revision" |
|
200 |
+}; |
|
201 |
+ |
|
202 |
+static int parse_dsd_diin(AVFormatContext *s, AVStream *st, uint64_t eof) |
|
203 |
+{ |
|
204 |
+ AVIOContext *pb = s->pb; |
|
205 |
+ |
|
206 |
+ while (avio_tell(pb) + 12 <= eof) { |
|
207 |
+ uint32_t tag = avio_rl32(pb); |
|
208 |
+ uint64_t size = avio_rb64(pb); |
|
209 |
+ uint64_t orig_pos = avio_tell(pb); |
|
210 |
+ const char * metadata_tag = NULL; |
|
211 |
+ |
|
212 |
+ switch(tag) { |
|
213 |
+ case MKTAG('D','I','A','R'): metadata_tag = "artist"; break; |
|
214 |
+ case MKTAG('D','I','T','I'): metadata_tag = "title"; break; |
|
215 |
+ } |
|
216 |
+ |
|
217 |
+ if (metadata_tag && size > 4) { |
|
218 |
+ unsigned int tag_size = avio_rb32(pb); |
|
219 |
+ int ret = get_metadata(s, metadata_tag, FFMIN(tag_size, size - 4)); |
|
220 |
+ if (ret < 0) { |
|
221 |
+ av_log(s, AV_LOG_ERROR, "cannot allocate metadata tag %s!\n", metadata_tag); |
|
222 |
+ return ret; |
|
223 |
+ } |
|
224 |
+ } |
|
225 |
+ |
|
226 |
+ avio_skip(pb, size - (avio_tell(pb) - orig_pos) + (size & 1)); |
|
227 |
+ } |
|
228 |
+ |
|
229 |
+ return 0; |
|
230 |
+} |
|
231 |
+ |
|
232 |
+static int parse_dsd_prop(AVFormatContext *s, AVStream *st, uint64_t eof) |
|
233 |
+{ |
|
234 |
+ AVIOContext *pb = s->pb; |
|
235 |
+ char abss[24]; |
|
236 |
+ int hour, min, sec, i, ret, config; |
|
237 |
+ int dsd_layout[6]; |
|
238 |
+ ID3v2ExtraMeta *id3v2_extra_meta; |
|
239 |
+ |
|
240 |
+ while (avio_tell(pb) + 12 <= eof) { |
|
241 |
+ uint32_t tag = avio_rl32(pb); |
|
242 |
+ uint64_t size = avio_rb64(pb); |
|
243 |
+ uint64_t orig_pos = avio_tell(pb); |
|
244 |
+ |
|
245 |
+ switch(tag) { |
|
246 |
+ case MKTAG('A','B','S','S'): |
|
247 |
+ if (size < 8) |
|
248 |
+ return AVERROR_INVALIDDATA; |
|
249 |
+ hour = avio_rb16(pb); |
|
250 |
+ min = avio_r8(pb); |
|
251 |
+ sec = avio_r8(pb); |
|
252 |
+ snprintf(abss, sizeof(abss), "%02dh:%02dm:%02ds:%d", hour, min, sec, avio_rb32(pb)); |
|
253 |
+ av_dict_set(&st->metadata, "absolute_start_time", abss, 0); |
|
254 |
+ break; |
|
255 |
+ |
|
256 |
+ case MKTAG('C','H','N','L'): |
|
257 |
+ if (size < 2) |
|
258 |
+ return AVERROR_INVALIDDATA; |
|
259 |
+ st->codec->channels = avio_rb16(pb); |
|
260 |
+ if (size < 2 + st->codec->channels * 4) |
|
261 |
+ return AVERROR_INVALIDDATA; |
|
262 |
+ st->codec->channel_layout = 0; |
|
263 |
+ if (st->codec->channels > FF_ARRAY_ELEMS(dsd_layout)) { |
|
264 |
+ avpriv_request_sample(s, "channel layout"); |
|
265 |
+ break; |
|
266 |
+ } |
|
267 |
+ for (i = 0; i < st->codec->channels; i++) |
|
268 |
+ dsd_layout[i] = avio_rl32(pb); |
|
269 |
+ for (i = 0; i < FF_ARRAY_ELEMS(dsd_channel_layout); i++) { |
|
270 |
+ const DSDLayoutDesc * d = &dsd_channel_layout[i]; |
|
271 |
+ if (av_get_channel_layout_nb_channels(d->layout) == st->codec->channels && |
|
272 |
+ !memcmp(d->dsd_layout, dsd_layout, st->codec->channels * sizeof(uint32_t))) { |
|
273 |
+ st->codec->channel_layout = d->layout; |
|
274 |
+ break; |
|
275 |
+ } |
|
276 |
+ } |
|
277 |
+ break; |
|
278 |
+ |
|
279 |
+ case MKTAG('C','M','P','R'): |
|
280 |
+ if (size < 4) |
|
281 |
+ return AVERROR_INVALIDDATA; |
|
282 |
+ st->codec->codec_id = ff_codec_get_id(dsd_codec_tags, avio_rl32(pb)); |
|
283 |
+ break; |
|
284 |
+ |
|
285 |
+ case MKTAG('F','S',' ',' '): |
|
286 |
+ if (size < 4) |
|
287 |
+ return AVERROR_INVALIDDATA; |
|
288 |
+ st->codec->sample_rate = avio_rb32(pb) / 8; |
|
289 |
+ break; |
|
290 |
+ |
|
291 |
+ case MKTAG('I','D','3',' '): |
|
292 |
+ id3v2_extra_meta = NULL; |
|
293 |
+ ff_id3v2_read(s, ID3v2_DEFAULT_MAGIC, &id3v2_extra_meta, size); |
|
294 |
+ if (id3v2_extra_meta) { |
|
295 |
+ if ((ret = ff_id3v2_parse_apic(s, &id3v2_extra_meta)) < 0) { |
|
296 |
+ ff_id3v2_free_extra_meta(&id3v2_extra_meta); |
|
297 |
+ return ret; |
|
298 |
+ } |
|
299 |
+ ff_id3v2_free_extra_meta(&id3v2_extra_meta); |
|
300 |
+ } |
|
301 |
+ |
|
302 |
+ if (size < avio_tell(pb) - orig_pos) { |
|
303 |
+ av_log(s, AV_LOG_ERROR, "id3 exceeds chunk size\n"); |
|
304 |
+ return AVERROR_INVALIDDATA; |
|
305 |
+ } |
|
306 |
+ break; |
|
307 |
+ |
|
308 |
+ case MKTAG('L','S','C','O'): |
|
309 |
+ if (size < 2) |
|
310 |
+ return AVERROR_INVALIDDATA; |
|
311 |
+ config = avio_rb16(pb); |
|
312 |
+ if (config != 0xFFFF) { |
|
313 |
+ if (config < FF_ARRAY_ELEMS(dsd_loudspeaker_config)) |
|
314 |
+ st->codec->channel_layout = dsd_loudspeaker_config[config]; |
|
315 |
+ if (!st->codec->channel_layout) |
|
316 |
+ avpriv_request_sample(s, "loudspeaker configuration %d", config); |
|
317 |
+ } |
|
318 |
+ break; |
|
319 |
+ } |
|
320 |
+ |
|
321 |
+ avio_skip(pb, size - (avio_tell(pb) - orig_pos) + (size & 1)); |
|
322 |
+ } |
|
323 |
+ |
|
324 |
+ return 0; |
|
325 |
+} |
|
326 |
+ |
|
150 | 327 |
static const uint8_t deep_rgb24[] = {0, 0, 0, 3, 0, 1, 0, 8, 0, 2, 0, 8, 0, 3, 0, 8}; |
151 | 328 |
static const uint8_t deep_rgba[] = {0, 0, 0, 4, 0, 1, 0, 8, 0, 2, 0, 8, 0, 3, 0, 8}; |
152 | 329 |
static const uint8_t deep_bgra[] = {0, 0, 0, 4, 0, 3, 0, 8, 0, 2, 0, 8, 0, 1, 0, 8}; |
... | ... |
@@ -159,7 +340,8 @@ static int iff_read_header(AVFormatContext *s) |
159 | 159 |
AVIOContext *pb = s->pb; |
160 | 160 |
AVStream *st; |
161 | 161 |
uint8_t *buf; |
162 |
- uint32_t chunk_id, data_size; |
|
162 |
+ uint32_t chunk_id; |
|
163 |
+ uint64_t data_size; |
|
163 | 164 |
uint32_t screenmode = 0, num, den; |
164 | 165 |
unsigned transparency = 0; |
165 | 166 |
unsigned masking = 0; // no mask |
... | ... |
@@ -172,7 +354,8 @@ static int iff_read_header(AVFormatContext *s) |
172 | 172 |
|
173 | 173 |
st->codec->channels = 1; |
174 | 174 |
st->codec->channel_layout = AV_CH_LAYOUT_MONO; |
175 |
- avio_skip(pb, 8); |
|
175 |
+ iff->is_64bit = avio_rl32(pb) == ID_FRM8; |
|
176 |
+ avio_skip(pb, iff->is_64bit ? 8 : 4); |
|
176 | 177 |
// codec_tag used by ByteRun1 decoder to distinguish progressive (PBM) and interlaced (ILBM) content |
177 | 178 |
st->codec->codec_tag = avio_rl32(pb); |
178 | 179 |
iff->bitmap_compression = -1; |
... | ... |
@@ -184,8 +367,9 @@ static int iff_read_header(AVFormatContext *s) |
184 | 184 |
uint64_t orig_pos; |
185 | 185 |
int res; |
186 | 186 |
const char *metadata_tag = NULL; |
187 |
+ int version, nb_comments, i; |
|
187 | 188 |
chunk_id = avio_rl32(pb); |
188 |
- data_size = avio_rb32(pb); |
|
189 |
+ data_size = iff->is_64bit ? avio_rb64(pb) : avio_rb32(pb); |
|
189 | 190 |
orig_pos = avio_tell(pb); |
190 | 191 |
|
191 | 192 |
switch(chunk_id) { |
... | ... |
@@ -227,6 +411,7 @@ static int iff_read_header(AVFormatContext *s) |
227 | 227 |
case ID_ABIT: |
228 | 228 |
case ID_BODY: |
229 | 229 |
case ID_DBOD: |
230 |
+ case ID_DSD: |
|
230 | 231 |
case ID_MDAT: |
231 | 232 |
iff->body_pos = avio_tell(pb); |
232 | 233 |
iff->body_end = iff->body_pos + data_size; |
... | ... |
@@ -253,7 +438,7 @@ static int iff_read_header(AVFormatContext *s) |
253 | 253 |
|
254 | 254 |
case ID_CMAP: |
255 | 255 |
if (data_size < 3 || data_size > 768 || data_size % 3) { |
256 |
- av_log(s, AV_LOG_ERROR, "Invalid CMAP chunk size %"PRIu32"\n", |
|
256 |
+ av_log(s, AV_LOG_ERROR, "Invalid CMAP chunk size %"PRIu64"\n", |
|
257 | 257 |
data_size); |
258 | 258 |
return AVERROR_INVALIDDATA; |
259 | 259 |
} |
... | ... |
@@ -340,6 +525,84 @@ static int iff_read_header(AVFormatContext *s) |
340 | 340 |
case ID_AUTH: metadata_tag = "artist"; break; |
341 | 341 |
case ID_COPYRIGHT: metadata_tag = "copyright"; break; |
342 | 342 |
case ID_NAME: metadata_tag = "title"; break; |
343 |
+ |
|
344 |
+ /* DSD tags */ |
|
345 |
+ |
|
346 |
+ case MKTAG('F','V','E','R'): |
|
347 |
+ if (data_size < 4) |
|
348 |
+ return AVERROR_INVALIDDATA; |
|
349 |
+ version = avio_rb32(pb); |
|
350 |
+ av_log(s, AV_LOG_DEBUG, "DSIFF v%d.%d.%d.%d\n",version >> 24, (version >> 16) & 0xFF, (version >> 8) & 0xFF, version & 0xFF); |
|
351 |
+ st->codec->codec_type = AVMEDIA_TYPE_AUDIO; |
|
352 |
+ break; |
|
353 |
+ |
|
354 |
+ case MKTAG('D','I','I','N'): |
|
355 |
+ res = parse_dsd_diin(s, st, orig_pos + data_size); |
|
356 |
+ if (res < 0) |
|
357 |
+ return res; |
|
358 |
+ break; |
|
359 |
+ |
|
360 |
+ case MKTAG('P','R','O','P'): |
|
361 |
+ if (data_size < 4) |
|
362 |
+ return AVERROR_INVALIDDATA; |
|
363 |
+ if (avio_rl32(pb) != MKTAG('S','N','D',' ')) { |
|
364 |
+ avpriv_request_sample(s, "unknown property type"); |
|
365 |
+ break; |
|
366 |
+ } |
|
367 |
+ res = parse_dsd_prop(s, st, orig_pos + data_size); |
|
368 |
+ if (res < 0) |
|
369 |
+ return res; |
|
370 |
+ break; |
|
371 |
+ |
|
372 |
+ case MKTAG('C','O','M','T'): |
|
373 |
+ if (data_size < 2) |
|
374 |
+ return AVERROR_INVALIDDATA; |
|
375 |
+ nb_comments = avio_rb16(pb); |
|
376 |
+ for (i = 0; i < nb_comments; i++) { |
|
377 |
+ int year, mon, day, hour, min, type, ref; |
|
378 |
+ char tmp[24]; |
|
379 |
+ const char *tag; |
|
380 |
+ int metadata_size; |
|
381 |
+ |
|
382 |
+ year = avio_rb16(pb); |
|
383 |
+ mon = avio_r8(pb); |
|
384 |
+ day = avio_r8(pb); |
|
385 |
+ hour = avio_r8(pb); |
|
386 |
+ min = avio_r8(pb); |
|
387 |
+ snprintf(tmp, sizeof(tmp), "%04d-%02d-%02d %02d:%02d", year, mon, day, hour, min); |
|
388 |
+ av_dict_set(&st->metadata, "comment_time", tmp, 0); |
|
389 |
+ |
|
390 |
+ type = avio_rb16(pb); |
|
391 |
+ ref = avio_rb16(pb); |
|
392 |
+ switch (type) { |
|
393 |
+ case 1: |
|
394 |
+ if (!i) |
|
395 |
+ tag = "channel_comment"; |
|
396 |
+ else { |
|
397 |
+ snprintf(tmp, sizeof(tmp), "channel%d_comment", ref); |
|
398 |
+ tag = tmp; |
|
399 |
+ } |
|
400 |
+ break; |
|
401 |
+ case 2: |
|
402 |
+ tag = ref < FF_ARRAY_ELEMS(dsd_source_comment) ? dsd_history_comment[ref] : "source_comment"; |
|
403 |
+ break; |
|
404 |
+ case 3: |
|
405 |
+ tag = ref < FF_ARRAY_ELEMS(dsd_history_comment) ? dsd_history_comment[ref] : "file_history"; |
|
406 |
+ break; |
|
407 |
+ default: |
|
408 |
+ tag = "comment"; |
|
409 |
+ } |
|
410 |
+ |
|
411 |
+ metadata_size = avio_rb32(pb); |
|
412 |
+ if ((res = get_metadata(s, tag, metadata_size)) < 0) { |
|
413 |
+ av_log(s, AV_LOG_ERROR, "cannot allocate metadata tag %s!\n", tag); |
|
414 |
+ return res; |
|
415 |
+ } |
|
416 |
+ |
|
417 |
+ if (metadata_size & 1) |
|
418 |
+ avio_skip(pb, 1); |
|
419 |
+ } |
|
420 |
+ break; |
|
343 | 421 |
} |
344 | 422 |
|
345 | 423 |
if (metadata_tag) { |
... | ... |
@@ -372,7 +635,7 @@ static int iff_read_header(AVFormatContext *s) |
372 | 372 |
avpriv_request_sample(s, "compression %d and bit depth %d", iff->maud_compression, iff->maud_bits); |
373 | 373 |
return AVERROR_PATCHWELCOME; |
374 | 374 |
} |
375 |
- } else { |
|
375 |
+ } else if (st->codec->codec_tag != ID_DSD) { |
|
376 | 376 |
switch (iff->svx8_compression) { |
377 | 377 |
case COMP_NONE: |
378 | 378 |
st->codec->codec_id = AV_CODEC_ID_PCM_S8_PLANAR; |
... | ... |
@@ -443,7 +706,7 @@ static int iff_read_packet(AVFormatContext *s, |
443 | 443 |
return AVERROR_EOF; |
444 | 444 |
|
445 | 445 |
if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { |
446 |
- if (st->codec->codec_tag == ID_MAUD) { |
|
446 |
+ if (st->codec->codec_tag == ID_DSD || st->codec->codec_tag == ID_MAUD) { |
|
447 | 447 |
ret = av_get_packet(pb, pkt, FFMIN(iff->body_end - pos, 1024 * st->codec->block_align)); |
448 | 448 |
} else { |
449 | 449 |
ret = av_get_packet(pb, pkt, iff->body_size); |