Mercurial > libavformat.hg
annotate ape.c @ 3897:5aa1cbbd79e0 libavformat
matroskadec: ensure that the timecode added to the index are is the one
of the actual keyframe block, and not the one of the start of the cluster
containing this block.
author | aurel |
---|---|
date | Sat, 06 Sep 2008 23:39:59 +0000 |
parents | 7a0230981402 |
children | 1d3d17de20ba |
rev | line source |
---|---|
2548 | 1 /* |
2 * Monkey's Audio APE demuxer | |
3 * Copyright (c) 2007 Benjamin Zores <ben@geexbox.org> | |
4 * based upon libdemac from Dave Chapman. | |
5 * | |
6 * This file is part of FFmpeg. | |
7 * | |
8 * FFmpeg is free software; you can redistribute it and/or | |
9 * modify it under the terms of the GNU Lesser General Public | |
10 * License as published by the Free Software Foundation; either | |
11 * version 2.1 of the License, or (at your option) any later version. | |
12 * | |
13 * FFmpeg is distributed in the hope that it will be useful, | |
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
16 * Lesser General Public License for more details. | |
17 * | |
18 * You should have received a copy of the GNU Lesser General Public | |
19 * License along with FFmpeg; if not, write to the Free Software | |
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
21 */ | |
22 | |
23 #include <stdio.h> | |
24 | |
25 #include "avformat.h" | |
26 | |
2556
cb131102b256
disable loads of debug messages to reduce object size
aurel
parents:
2554
diff
changeset
|
27 #define ENABLE_DEBUG 0 |
cb131102b256
disable loads of debug messages to reduce object size
aurel
parents:
2554
diff
changeset
|
28 |
2548 | 29 /* The earliest and latest file formats supported by this library */ |
2568
59f7ce8ad381
Looks like this APE decoder support versions starting from 3.95
kostya
parents:
2556
diff
changeset
|
30 #define APE_MIN_VERSION 3950 |
2548 | 31 #define APE_MAX_VERSION 3990 |
32 | |
33 #define MAC_FORMAT_FLAG_8_BIT 1 // is 8-bit [OBSOLETE] | |
34 #define MAC_FORMAT_FLAG_CRC 2 // uses the new CRC32 error detection [OBSOLETE] | |
35 #define MAC_FORMAT_FLAG_HAS_PEAK_LEVEL 4 // uint32 nPeakLevel after the header [OBSOLETE] | |
36 #define MAC_FORMAT_FLAG_24_BIT 8 // is 24-bit [OBSOLETE] | |
37 #define MAC_FORMAT_FLAG_HAS_SEEK_ELEMENTS 16 // has the number of seek elements after the peak level | |
38 #define MAC_FORMAT_FLAG_CREATE_WAV_HEADER 32 // create the wave header on decompression (not stored) | |
39 | |
40 #define MAC_SUBFRAME_SIZE 4608 | |
41 | |
42 #define APE_EXTRADATA_SIZE 6 | |
43 | |
2554 | 44 /* APE tags */ |
45 #define APE_TAG_VERSION 2000 | |
46 #define APE_TAG_FOOTER_BYTES 32 | |
47 #define APE_TAG_FLAG_CONTAINS_HEADER (1 << 31) | |
48 #define APE_TAG_FLAG_IS_HEADER (1 << 29) | |
49 | |
50 #define TAG(name, field) {name, offsetof(AVFormatContext, field), sizeof(((AVFormatContext *)0)->field)} | |
51 | |
52 static const struct { | |
3007
bec20ba1e194
fix 8 "initialization discards qualifiers from pointer target type"
michael
parents:
2771
diff
changeset
|
53 const char *name; |
2554 | 54 int offset; |
55 int size; | |
56 } tags[] = { | |
57 TAG("Title" , title ), | |
58 TAG("Artist" , author ), | |
59 TAG("Copyright", copyright), | |
60 TAG("Comment" , comment ), | |
61 TAG("Album" , album ), | |
62 TAG("Year" , year ), | |
63 TAG("Track" , track ), | |
64 TAG("Genre" , genre ), | |
65 { NULL } | |
66 }; | |
67 | |
2548 | 68 typedef struct { |
69 int64_t pos; | |
70 int nblocks; | |
71 int size; | |
72 int skip; | |
73 int64_t pts; | |
74 } APEFrame; | |
75 | |
76 typedef struct { | |
77 /* Derived fields */ | |
78 uint32_t junklength; | |
79 uint32_t firstframe; | |
80 uint32_t totalsamples; | |
81 int currentframe; | |
82 APEFrame *frames; | |
83 | |
84 /* Info from Descriptor Block */ | |
85 char magic[4]; | |
86 int16_t fileversion; | |
87 int16_t padding1; | |
88 uint32_t descriptorlength; | |
89 uint32_t headerlength; | |
90 uint32_t seektablelength; | |
91 uint32_t wavheaderlength; | |
92 uint32_t audiodatalength; | |
93 uint32_t audiodatalength_high; | |
94 uint32_t wavtaillength; | |
95 uint8_t md5[16]; | |
96 | |
97 /* Info from Header Block */ | |
98 uint16_t compressiontype; | |
99 uint16_t formatflags; | |
100 uint32_t blocksperframe; | |
101 uint32_t finalframeblocks; | |
102 uint32_t totalframes; | |
103 uint16_t bps; | |
104 uint16_t channels; | |
105 uint32_t samplerate; | |
106 | |
107 /* Seektable */ | |
108 uint32_t *seektable; | |
109 } APEContext; | |
110 | |
2554 | 111 static void ape_tag_read_field(AVFormatContext *s) |
112 { | |
2771
d52c718e83f9
Use dynamically allocated ByteIOContext in AVFormatContext
andoma
parents:
2568
diff
changeset
|
113 ByteIOContext *pb = s->pb; |
2554 | 114 uint8_t buf[1024]; |
115 uint32_t size; | |
116 int i; | |
117 | |
118 memset(buf, 0, 1024); | |
119 size = get_le32(pb); /* field size */ | |
120 url_fskip(pb, 4); /* skip field flags */ | |
121 | |
122 for (i=0; pb->buf_ptr[i]!='0' && pb->buf_ptr[i]>=0x20 && pb->buf_ptr[i]<=0x7E; i++); | |
123 | |
124 get_buffer(pb, buf, FFMIN(i, 1024)); | |
125 url_fskip(pb, 1); | |
126 | |
127 for (i=0; tags[i].name; i++) | |
128 if (!strcmp (buf, tags[i].name)) { | |
129 if (tags[i].size == sizeof(int)) { | |
130 char tmp[16]; | |
131 get_buffer(pb, tmp, FFMIN(sizeof(tmp), size)); | |
132 *(int *)(((char *)s)+tags[i].offset) = atoi(tmp); | |
133 } else { | |
134 get_buffer(pb, ((char *)s) + tags[i].offset, | |
135 FFMIN(tags[i].size, size)); | |
136 } | |
137 break; | |
138 } | |
139 | |
140 if (!tags[i].name) | |
141 url_fskip(pb, size); | |
142 } | |
143 | |
144 static void ape_parse_tag(AVFormatContext *s) | |
145 { | |
2771
d52c718e83f9
Use dynamically allocated ByteIOContext in AVFormatContext
andoma
parents:
2568
diff
changeset
|
146 ByteIOContext *pb = s->pb; |
2554 | 147 int file_size = url_fsize(pb); |
148 uint32_t val, fields, tag_bytes; | |
149 uint8_t buf[8]; | |
150 int i; | |
151 | |
152 if (file_size < APE_TAG_FOOTER_BYTES) | |
153 return; | |
154 | |
155 url_fseek(pb, file_size - APE_TAG_FOOTER_BYTES, SEEK_SET); | |
156 | |
157 get_buffer(pb, buf, 8); /* APETAGEX */ | |
158 if (strncmp(buf, "APETAGEX", 8)) { | |
159 return; | |
160 } | |
161 | |
162 val = get_le32(pb); /* APE tag version */ | |
163 if (val > APE_TAG_VERSION) { | |
164 av_log(NULL, AV_LOG_ERROR, "Unsupported tag version. (>=%d)\n", APE_TAG_VERSION); | |
165 return; | |
166 } | |
167 | |
168 tag_bytes = get_le32(pb); /* tag size */ | |
169 if (tag_bytes - APE_TAG_FOOTER_BYTES > (1024 * 1024 * 16)) { | |
170 av_log(NULL, AV_LOG_ERROR, "Tag size is way too big\n"); | |
171 return; | |
172 } | |
173 | |
174 fields = get_le32(pb); /* number of fields */ | |
175 if (fields > 65536) { | |
176 av_log(NULL, AV_LOG_ERROR, "Too many tag fields (%d)\n", fields); | |
177 return; | |
178 } | |
179 | |
180 val = get_le32(pb); /* flags */ | |
181 if (val & APE_TAG_FLAG_IS_HEADER) { | |
182 av_log(NULL, AV_LOG_ERROR, "APE Tag is a header\n"); | |
183 return; | |
184 } | |
185 | |
186 if (val & APE_TAG_FLAG_CONTAINS_HEADER) | |
187 tag_bytes += 2*APE_TAG_FOOTER_BYTES; | |
188 | |
189 url_fseek(pb, file_size - tag_bytes, SEEK_SET); | |
190 | |
191 for (i=0; i<fields; i++) | |
192 ape_tag_read_field(s); | |
193 | |
2556
cb131102b256
disable loads of debug messages to reduce object size
aurel
parents:
2554
diff
changeset
|
194 #if ENABLE_DEBUG |
2554 | 195 av_log(NULL, AV_LOG_DEBUG, "\nAPE Tags:\n\n"); |
196 av_log(NULL, AV_LOG_DEBUG, "title = %s\n", s->title); | |
197 av_log(NULL, AV_LOG_DEBUG, "author = %s\n", s->author); | |
198 av_log(NULL, AV_LOG_DEBUG, "copyright = %s\n", s->copyright); | |
199 av_log(NULL, AV_LOG_DEBUG, "comment = %s\n", s->comment); | |
200 av_log(NULL, AV_LOG_DEBUG, "album = %s\n", s->album); | |
201 av_log(NULL, AV_LOG_DEBUG, "year = %d\n", s->year); | |
202 av_log(NULL, AV_LOG_DEBUG, "track = %d\n", s->track); | |
203 av_log(NULL, AV_LOG_DEBUG, "genre = %s\n", s->genre); | |
2556
cb131102b256
disable loads of debug messages to reduce object size
aurel
parents:
2554
diff
changeset
|
204 #endif |
2554 | 205 } |
206 | |
2548 | 207 static int ape_probe(AVProbeData * p) |
208 { | |
209 if (p->buf[0] == 'M' && p->buf[1] == 'A' && p->buf[2] == 'C' && p->buf[3] == ' ') | |
210 return AVPROBE_SCORE_MAX; | |
211 | |
212 return 0; | |
213 } | |
214 | |
215 static void ape_dumpinfo(APEContext * ape_ctx) | |
216 { | |
2556
cb131102b256
disable loads of debug messages to reduce object size
aurel
parents:
2554
diff
changeset
|
217 #if ENABLE_DEBUG |
2548 | 218 int i; |
219 | |
220 av_log(NULL, AV_LOG_DEBUG, "Descriptor Block:\n\n"); | |
221 av_log(NULL, AV_LOG_DEBUG, "magic = \"%c%c%c%c\"\n", ape_ctx->magic[0], ape_ctx->magic[1], ape_ctx->magic[2], ape_ctx->magic[3]); | |
222 av_log(NULL, AV_LOG_DEBUG, "fileversion = %d\n", ape_ctx->fileversion); | |
223 av_log(NULL, AV_LOG_DEBUG, "descriptorlength = %d\n", ape_ctx->descriptorlength); | |
224 av_log(NULL, AV_LOG_DEBUG, "headerlength = %d\n", ape_ctx->headerlength); | |
225 av_log(NULL, AV_LOG_DEBUG, "seektablelength = %d\n", ape_ctx->seektablelength); | |
226 av_log(NULL, AV_LOG_DEBUG, "wavheaderlength = %d\n", ape_ctx->wavheaderlength); | |
227 av_log(NULL, AV_LOG_DEBUG, "audiodatalength = %d\n", ape_ctx->audiodatalength); | |
228 av_log(NULL, AV_LOG_DEBUG, "audiodatalength_high = %d\n", ape_ctx->audiodatalength_high); | |
229 av_log(NULL, AV_LOG_DEBUG, "wavtaillength = %d\n", ape_ctx->wavtaillength); | |
230 av_log(NULL, AV_LOG_DEBUG, "md5 = "); | |
231 for (i = 0; i < 16; i++) | |
232 av_log(NULL, AV_LOG_DEBUG, "%02x", ape_ctx->md5[i]); | |
233 av_log(NULL, AV_LOG_DEBUG, "\n"); | |
234 | |
235 av_log(NULL, AV_LOG_DEBUG, "\nHeader Block:\n\n"); | |
236 | |
237 av_log(NULL, AV_LOG_DEBUG, "compressiontype = %d\n", ape_ctx->compressiontype); | |
238 av_log(NULL, AV_LOG_DEBUG, "formatflags = %d\n", ape_ctx->formatflags); | |
239 av_log(NULL, AV_LOG_DEBUG, "blocksperframe = %d\n", ape_ctx->blocksperframe); | |
240 av_log(NULL, AV_LOG_DEBUG, "finalframeblocks = %d\n", ape_ctx->finalframeblocks); | |
241 av_log(NULL, AV_LOG_DEBUG, "totalframes = %d\n", ape_ctx->totalframes); | |
242 av_log(NULL, AV_LOG_DEBUG, "bps = %d\n", ape_ctx->bps); | |
243 av_log(NULL, AV_LOG_DEBUG, "channels = %d\n", ape_ctx->channels); | |
244 av_log(NULL, AV_LOG_DEBUG, "samplerate = %d\n", ape_ctx->samplerate); | |
245 | |
246 av_log(NULL, AV_LOG_DEBUG, "\nSeektable\n\n"); | |
247 if ((ape_ctx->seektablelength / sizeof(uint32_t)) != ape_ctx->totalframes) { | |
248 av_log(NULL, AV_LOG_DEBUG, "No seektable\n"); | |
249 } else { | |
250 for (i = 0; i < ape_ctx->seektablelength / sizeof(uint32_t); i++) { | |
251 if (i < ape_ctx->totalframes - 1) { | |
252 av_log(NULL, AV_LOG_DEBUG, "%8d %d (%d bytes)\n", i, ape_ctx->seektable[i], ape_ctx->seektable[i + 1] - ape_ctx->seektable[i]); | |
253 } else { | |
254 av_log(NULL, AV_LOG_DEBUG, "%8d %d\n", i, ape_ctx->seektable[i]); | |
255 } | |
256 } | |
257 } | |
258 | |
259 av_log(NULL, AV_LOG_DEBUG, "\nFrames\n\n"); | |
260 for (i = 0; i < ape_ctx->totalframes; i++) | |
261 av_log(NULL, AV_LOG_DEBUG, "%8d %8lld %8d (%d samples)\n", i, ape_ctx->frames[i].pos, ape_ctx->frames[i].size, ape_ctx->frames[i].nblocks); | |
262 | |
263 av_log(NULL, AV_LOG_DEBUG, "\nCalculated information:\n\n"); | |
264 av_log(NULL, AV_LOG_DEBUG, "junklength = %d\n", ape_ctx->junklength); | |
265 av_log(NULL, AV_LOG_DEBUG, "firstframe = %d\n", ape_ctx->firstframe); | |
266 av_log(NULL, AV_LOG_DEBUG, "totalsamples = %d\n", ape_ctx->totalsamples); | |
2556
cb131102b256
disable loads of debug messages to reduce object size
aurel
parents:
2554
diff
changeset
|
267 #endif |
2548 | 268 } |
269 | |
270 static int ape_read_header(AVFormatContext * s, AVFormatParameters * ap) | |
271 { | |
2771
d52c718e83f9
Use dynamically allocated ByteIOContext in AVFormatContext
andoma
parents:
2568
diff
changeset
|
272 ByteIOContext *pb = s->pb; |
2548 | 273 APEContext *ape = s->priv_data; |
274 AVStream *st; | |
275 uint32_t tag; | |
276 int i; | |
277 int total_blocks; | |
278 int64_t pts; | |
279 | |
280 /* TODO: Skip any leading junk such as id3v2 tags */ | |
281 ape->junklength = 0; | |
282 | |
283 tag = get_le32(pb); | |
284 if (tag != MKTAG('M', 'A', 'C', ' ')) | |
285 return -1; | |
286 | |
287 ape->fileversion = get_le16(pb); | |
288 | |
289 if (ape->fileversion < APE_MIN_VERSION || ape->fileversion > APE_MAX_VERSION) { | |
290 av_log(s, AV_LOG_ERROR, "Unsupported file version - %d.%02d\n", ape->fileversion / 1000, (ape->fileversion % 1000) / 10); | |
291 return -1; | |
292 } | |
293 | |
294 if (ape->fileversion >= 3980) { | |
295 ape->padding1 = get_le16(pb); | |
296 ape->descriptorlength = get_le32(pb); | |
297 ape->headerlength = get_le32(pb); | |
298 ape->seektablelength = get_le32(pb); | |
299 ape->wavheaderlength = get_le32(pb); | |
300 ape->audiodatalength = get_le32(pb); | |
301 ape->audiodatalength_high = get_le32(pb); | |
302 ape->wavtaillength = get_le32(pb); | |
303 get_buffer(pb, ape->md5, 16); | |
304 | |
305 /* Skip any unknown bytes at the end of the descriptor. | |
306 This is for future compatibility */ | |
307 if (ape->descriptorlength > 52) | |
308 url_fseek(pb, ape->descriptorlength - 52, SEEK_CUR); | |
309 | |
310 /* Read header data */ | |
311 ape->compressiontype = get_le16(pb); | |
312 ape->formatflags = get_le16(pb); | |
313 ape->blocksperframe = get_le32(pb); | |
314 ape->finalframeblocks = get_le32(pb); | |
315 ape->totalframes = get_le32(pb); | |
316 ape->bps = get_le16(pb); | |
317 ape->channels = get_le16(pb); | |
318 ape->samplerate = get_le32(pb); | |
319 } else { | |
320 ape->descriptorlength = 0; | |
321 ape->headerlength = 32; | |
322 | |
323 ape->compressiontype = get_le16(pb); | |
324 ape->formatflags = get_le16(pb); | |
325 ape->channels = get_le16(pb); | |
326 ape->samplerate = get_le32(pb); | |
327 ape->wavheaderlength = get_le32(pb); | |
328 ape->wavtaillength = get_le32(pb); | |
329 ape->totalframes = get_le32(pb); | |
330 ape->finalframeblocks = get_le32(pb); | |
331 | |
332 if (ape->formatflags & MAC_FORMAT_FLAG_HAS_PEAK_LEVEL) { | |
333 url_fseek(pb, 4, SEEK_CUR); /* Skip the peak level */ | |
334 ape->headerlength += 4; | |
335 } | |
336 | |
337 if (ape->formatflags & MAC_FORMAT_FLAG_HAS_SEEK_ELEMENTS) { | |
338 ape->seektablelength = get_le32(pb); | |
339 ape->headerlength += 4; | |
340 ape->seektablelength *= sizeof(int32_t); | |
341 } else | |
342 ape->seektablelength = ape->totalframes * sizeof(int32_t); | |
343 | |
344 if (ape->formatflags & MAC_FORMAT_FLAG_8_BIT) | |
345 ape->bps = 8; | |
346 else if (ape->formatflags & MAC_FORMAT_FLAG_24_BIT) | |
347 ape->bps = 24; | |
348 else | |
349 ape->bps = 16; | |
350 | |
351 if (ape->fileversion >= 3950) | |
352 ape->blocksperframe = 73728 * 4; | |
353 else if (ape->fileversion >= 3900 || (ape->fileversion >= 3800 && ape->compressiontype >= 4000)) | |
354 ape->blocksperframe = 73728; | |
355 else | |
356 ape->blocksperframe = 9216; | |
357 | |
358 /* Skip any stored wav header */ | |
359 if (!(ape->formatflags & MAC_FORMAT_FLAG_CREATE_WAV_HEADER)) | |
360 url_fskip(pb, ape->wavheaderlength); | |
361 } | |
362 | |
363 if(ape->totalframes > UINT_MAX / sizeof(APEFrame)){ | |
364 av_log(s, AV_LOG_ERROR, "Too many frames: %d\n", ape->totalframes); | |
365 return -1; | |
366 } | |
367 ape->frames = av_malloc(ape->totalframes * sizeof(APEFrame)); | |
368 if(!ape->frames) | |
369 return AVERROR_NOMEM; | |
370 ape->firstframe = ape->junklength + ape->descriptorlength + ape->headerlength + ape->seektablelength + ape->wavheaderlength; | |
371 ape->currentframe = 0; | |
372 | |
373 | |
374 ape->totalsamples = ape->finalframeblocks; | |
375 if (ape->totalframes > 1) | |
376 ape->totalsamples += ape->blocksperframe * (ape->totalframes - 1); | |
377 | |
378 if (ape->seektablelength > 0) { | |
379 ape->seektable = av_malloc(ape->seektablelength); | |
380 for (i = 0; i < ape->seektablelength / sizeof(uint32_t); i++) | |
381 ape->seektable[i] = get_le32(pb); | |
382 } | |
383 | |
384 ape->frames[0].pos = ape->firstframe; | |
385 ape->frames[0].nblocks = ape->blocksperframe; | |
386 ape->frames[0].skip = 0; | |
387 for (i = 1; i < ape->totalframes; i++) { | |
388 ape->frames[i].pos = ape->seektable[i]; //ape->frames[i-1].pos + ape->blocksperframe; | |
389 ape->frames[i].nblocks = ape->blocksperframe; | |
390 ape->frames[i - 1].size = ape->frames[i].pos - ape->frames[i - 1].pos; | |
391 ape->frames[i].skip = (ape->frames[i].pos - ape->frames[0].pos) & 3; | |
392 } | |
393 ape->frames[ape->totalframes - 1].size = ape->finalframeblocks * 4; | |
394 ape->frames[ape->totalframes - 1].nblocks = ape->finalframeblocks; | |
395 | |
396 for (i = 0; i < ape->totalframes; i++) { | |
397 if(ape->frames[i].skip){ | |
398 ape->frames[i].pos -= ape->frames[i].skip; | |
399 ape->frames[i].size += ape->frames[i].skip; | |
400 } | |
401 ape->frames[i].size = (ape->frames[i].size + 3) & ~3; | |
402 } | |
403 | |
404 | |
405 ape_dumpinfo(ape); | |
406 | |
2554 | 407 /* try to read APE tags */ |
408 if (!url_is_streamed(pb)) { | |
409 ape_parse_tag(s); | |
410 url_fseek(pb, 0, SEEK_SET); | |
411 } | |
412 | |
2548 | 413 av_log(s, AV_LOG_DEBUG, "Decoding file - v%d.%02d, compression level %d\n", ape->fileversion / 1000, (ape->fileversion % 1000) / 10, ape->compressiontype); |
414 | |
415 /* now we are ready: build format streams */ | |
416 st = av_new_stream(s, 0); | |
417 if (!st) | |
418 return -1; | |
419 | |
420 total_blocks = (ape->totalframes == 0) ? 0 : ((ape->totalframes - 1) * ape->blocksperframe) + ape->finalframeblocks; | |
421 | |
422 st->codec->codec_type = CODEC_TYPE_AUDIO; | |
423 st->codec->codec_id = CODEC_ID_APE; | |
424 st->codec->codec_tag = MKTAG('A', 'P', 'E', ' '); | |
425 st->codec->channels = ape->channels; | |
426 st->codec->sample_rate = ape->samplerate; | |
427 st->codec->bits_per_sample = ape->bps; | |
428 st->codec->frame_size = MAC_SUBFRAME_SIZE; | |
429 | |
430 st->nb_frames = ape->totalframes; | |
431 s->start_time = 0; | |
432 s->duration = (int64_t) total_blocks * AV_TIME_BASE / ape->samplerate; | |
433 av_set_pts_info(st, 64, MAC_SUBFRAME_SIZE, ape->samplerate); | |
434 | |
435 st->codec->extradata = av_malloc(APE_EXTRADATA_SIZE); | |
436 st->codec->extradata_size = APE_EXTRADATA_SIZE; | |
437 AV_WL16(st->codec->extradata + 0, ape->fileversion); | |
438 AV_WL16(st->codec->extradata + 2, ape->compressiontype); | |
439 AV_WL16(st->codec->extradata + 4, ape->formatflags); | |
440 | |
441 pts = 0; | |
442 for (i = 0; i < ape->totalframes; i++) { | |
443 ape->frames[i].pts = pts; | |
444 av_add_index_entry(st, ape->frames[i].pos, ape->frames[i].pts, 0, 0, AVINDEX_KEYFRAME); | |
445 pts += ape->blocksperframe / MAC_SUBFRAME_SIZE; | |
446 } | |
447 | |
448 return 0; | |
449 } | |
450 | |
451 static int ape_read_packet(AVFormatContext * s, AVPacket * pkt) | |
452 { | |
453 int ret; | |
454 int nblocks; | |
455 APEContext *ape = s->priv_data; | |
456 uint32_t extra_size = 8; | |
457 | |
2771
d52c718e83f9
Use dynamically allocated ByteIOContext in AVFormatContext
andoma
parents:
2568
diff
changeset
|
458 if (url_feof(s->pb)) |
2548 | 459 return AVERROR_IO; |
460 if (ape->currentframe > ape->totalframes) | |
461 return AVERROR_IO; | |
462 | |
2771
d52c718e83f9
Use dynamically allocated ByteIOContext in AVFormatContext
andoma
parents:
2568
diff
changeset
|
463 url_fseek (s->pb, ape->frames[ape->currentframe].pos, SEEK_SET); |
2548 | 464 |
465 /* Calculate how many blocks there are in this frame */ | |
466 if (ape->currentframe == (ape->totalframes - 1)) | |
467 nblocks = ape->finalframeblocks; | |
468 else | |
469 nblocks = ape->blocksperframe; | |
470 | |
471 if (av_new_packet(pkt, ape->frames[ape->currentframe].size + extra_size) < 0) | |
472 return AVERROR_NOMEM; | |
473 | |
474 AV_WL32(pkt->data , nblocks); | |
475 AV_WL32(pkt->data + 4, ape->frames[ape->currentframe].skip); | |
2771
d52c718e83f9
Use dynamically allocated ByteIOContext in AVFormatContext
andoma
parents:
2568
diff
changeset
|
476 ret = get_buffer(s->pb, pkt->data + extra_size, ape->frames[ape->currentframe].size); |
2548 | 477 |
478 pkt->pts = ape->frames[ape->currentframe].pts; | |
479 pkt->stream_index = 0; | |
480 | |
481 /* note: we need to modify the packet size here to handle the last | |
482 packet */ | |
483 pkt->size = ret + extra_size; | |
484 | |
485 ape->currentframe++; | |
486 | |
487 return 0; | |
488 } | |
489 | |
490 static int ape_read_close(AVFormatContext * s) | |
491 { | |
492 APEContext *ape = s->priv_data; | |
493 | |
494 av_freep(&ape->frames); | |
495 av_freep(&ape->seektable); | |
496 return 0; | |
497 } | |
498 | |
499 static int ape_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags) | |
500 { | |
501 AVStream *st = s->streams[stream_index]; | |
502 APEContext *ape = s->priv_data; | |
503 int index = av_index_search_timestamp(st, timestamp, flags); | |
504 | |
505 if (index < 0) | |
506 return -1; | |
507 | |
508 ape->currentframe = index; | |
509 return 0; | |
510 } | |
511 | |
512 AVInputFormat ape_demuxer = { | |
513 "ape", | |
3424
7a0230981402
Make long_names in lavf/lavdev optional depending on CONFIG_SMALL.
diego
parents:
3266
diff
changeset
|
514 NULL_IF_CONFIG_SMALL("Monkey's Audio"), |
2548 | 515 sizeof(APEContext), |
516 ape_probe, | |
517 ape_read_header, | |
518 ape_read_packet, | |
519 ape_read_close, | |
520 ape_read_seek, | |
521 .extensions = "ape,apl,mac" | |
522 }; |