Go to most recent revision | Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
4349 | Serge | 1 | /* |
2 | * Monkey's Audio APE demuxer |
||
3 | * Copyright (c) 2007 Benjamin Zores |
||
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 |
||
24 | |||
25 | #include "libavutil/intreadwrite.h" |
||
26 | #include "avformat.h" |
||
27 | #include "internal.h" |
||
28 | #include "apetag.h" |
||
29 | |||
30 | /* The earliest and latest file formats supported by this library */ |
||
31 | #define APE_MIN_VERSION 3800 |
||
32 | #define APE_MAX_VERSION 3990 |
||
33 | |||
34 | #define MAC_FORMAT_FLAG_8_BIT 1 // is 8-bit [OBSOLETE] |
||
35 | #define MAC_FORMAT_FLAG_CRC 2 // uses the new CRC32 error detection [OBSOLETE] |
||
36 | #define MAC_FORMAT_FLAG_HAS_PEAK_LEVEL 4 // uint32 nPeakLevel after the header [OBSOLETE] |
||
37 | #define MAC_FORMAT_FLAG_24_BIT 8 // is 24-bit [OBSOLETE] |
||
38 | #define MAC_FORMAT_FLAG_HAS_SEEK_ELEMENTS 16 // has the number of seek elements after the peak level |
||
39 | #define MAC_FORMAT_FLAG_CREATE_WAV_HEADER 32 // create the wave header on decompression (not stored) |
||
40 | |||
41 | #define APE_EXTRADATA_SIZE 6 |
||
42 | |||
43 | typedef struct { |
||
44 | int64_t pos; |
||
45 | int nblocks; |
||
46 | int size; |
||
47 | int skip; |
||
48 | int64_t pts; |
||
49 | } APEFrame; |
||
50 | |||
51 | typedef struct { |
||
52 | /* Derived fields */ |
||
53 | uint32_t junklength; |
||
54 | uint32_t firstframe; |
||
55 | uint32_t totalsamples; |
||
56 | int currentframe; |
||
57 | APEFrame *frames; |
||
58 | |||
59 | /* Info from Descriptor Block */ |
||
60 | char magic[4]; |
||
61 | int16_t fileversion; |
||
62 | int16_t padding1; |
||
63 | uint32_t descriptorlength; |
||
64 | uint32_t headerlength; |
||
65 | uint32_t seektablelength; |
||
66 | uint32_t wavheaderlength; |
||
67 | uint32_t audiodatalength; |
||
68 | uint32_t audiodatalength_high; |
||
69 | uint32_t wavtaillength; |
||
70 | uint8_t md5[16]; |
||
71 | |||
72 | /* Info from Header Block */ |
||
73 | uint16_t compressiontype; |
||
74 | uint16_t formatflags; |
||
75 | uint32_t blocksperframe; |
||
76 | uint32_t finalframeblocks; |
||
77 | uint32_t totalframes; |
||
78 | uint16_t bps; |
||
79 | uint16_t channels; |
||
80 | uint32_t samplerate; |
||
81 | |||
82 | /* Seektable */ |
||
83 | uint32_t *seektable; |
||
84 | uint8_t *bittable; |
||
85 | } APEContext; |
||
86 | |||
87 | static int ape_probe(AVProbeData * p) |
||
88 | { |
||
89 | if (p->buf[0] == 'M' && p->buf[1] == 'A' && p->buf[2] == 'C' && p->buf[3] == ' ') |
||
90 | return AVPROBE_SCORE_MAX; |
||
91 | |||
92 | return 0; |
||
93 | } |
||
94 | |||
95 | static void ape_dumpinfo(AVFormatContext * s, APEContext * ape_ctx) |
||
96 | { |
||
97 | #ifdef DEBUG |
||
98 | int i; |
||
99 | |||
100 | av_log(s, AV_LOG_DEBUG, "Descriptor Block:\n\n"); |
||
101 | av_log(s, 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]); |
||
102 | av_log(s, AV_LOG_DEBUG, "fileversion = %"PRId16"\n", ape_ctx->fileversion); |
||
103 | av_log(s, AV_LOG_DEBUG, "descriptorlength = %"PRIu32"\n", ape_ctx->descriptorlength); |
||
104 | av_log(s, AV_LOG_DEBUG, "headerlength = %"PRIu32"\n", ape_ctx->headerlength); |
||
105 | av_log(s, AV_LOG_DEBUG, "seektablelength = %"PRIu32"\n", ape_ctx->seektablelength); |
||
106 | av_log(s, AV_LOG_DEBUG, "wavheaderlength = %"PRIu32"\n", ape_ctx->wavheaderlength); |
||
107 | av_log(s, AV_LOG_DEBUG, "audiodatalength = %"PRIu32"\n", ape_ctx->audiodatalength); |
||
108 | av_log(s, AV_LOG_DEBUG, "audiodatalength_high = %"PRIu32"\n", ape_ctx->audiodatalength_high); |
||
109 | av_log(s, AV_LOG_DEBUG, "wavtaillength = %"PRIu32"\n", ape_ctx->wavtaillength); |
||
110 | av_log(s, AV_LOG_DEBUG, "md5 = "); |
||
111 | for (i = 0; i < 16; i++) |
||
112 | av_log(s, AV_LOG_DEBUG, "%02x", ape_ctx->md5[i]); |
||
113 | av_log(s, AV_LOG_DEBUG, "\n"); |
||
114 | |||
115 | av_log(s, AV_LOG_DEBUG, "\nHeader Block:\n\n"); |
||
116 | |||
117 | av_log(s, AV_LOG_DEBUG, "compressiontype = %"PRIu16"\n", ape_ctx->compressiontype); |
||
118 | av_log(s, AV_LOG_DEBUG, "formatflags = %"PRIu16"\n", ape_ctx->formatflags); |
||
119 | av_log(s, AV_LOG_DEBUG, "blocksperframe = %"PRIu32"\n", ape_ctx->blocksperframe); |
||
120 | av_log(s, AV_LOG_DEBUG, "finalframeblocks = %"PRIu32"\n", ape_ctx->finalframeblocks); |
||
121 | av_log(s, AV_LOG_DEBUG, "totalframes = %"PRIu32"\n", ape_ctx->totalframes); |
||
122 | av_log(s, AV_LOG_DEBUG, "bps = %"PRIu16"\n", ape_ctx->bps); |
||
123 | av_log(s, AV_LOG_DEBUG, "channels = %"PRIu16"\n", ape_ctx->channels); |
||
124 | av_log(s, AV_LOG_DEBUG, "samplerate = %"PRIu32"\n", ape_ctx->samplerate); |
||
125 | |||
126 | av_log(s, AV_LOG_DEBUG, "\nSeektable\n\n"); |
||
127 | if ((ape_ctx->seektablelength / sizeof(uint32_t)) != ape_ctx->totalframes) { |
||
128 | av_log(s, AV_LOG_DEBUG, "No seektable\n"); |
||
129 | } else { |
||
130 | for (i = 0; i < ape_ctx->seektablelength / sizeof(uint32_t); i++) { |
||
131 | if (i < ape_ctx->totalframes - 1) { |
||
132 | av_log(s, AV_LOG_DEBUG, "%8d %"PRIu32" (%"PRIu32" bytes)", |
||
133 | i, ape_ctx->seektable[i], |
||
134 | ape_ctx->seektable[i + 1] - ape_ctx->seektable[i]); |
||
135 | if (ape_ctx->bittable) |
||
136 | av_log(s, AV_LOG_DEBUG, " + %2d bits\n", |
||
137 | ape_ctx->bittable[i]); |
||
138 | av_log(s, AV_LOG_DEBUG, "\n"); |
||
139 | } else { |
||
140 | av_log(s, AV_LOG_DEBUG, "%8d %"PRIu32"\n", i, ape_ctx->seektable[i]); |
||
141 | } |
||
142 | } |
||
143 | } |
||
144 | |||
145 | av_log(s, AV_LOG_DEBUG, "\nFrames\n\n"); |
||
146 | for (i = 0; i < ape_ctx->totalframes; i++) |
||
147 | av_log(s, AV_LOG_DEBUG, "%8d %8"PRId64" %8d (%d samples)\n", i, |
||
148 | ape_ctx->frames[i].pos, ape_ctx->frames[i].size, |
||
149 | ape_ctx->frames[i].nblocks); |
||
150 | |||
151 | av_log(s, AV_LOG_DEBUG, "\nCalculated information:\n\n"); |
||
152 | av_log(s, AV_LOG_DEBUG, "junklength = %"PRIu32"\n", ape_ctx->junklength); |
||
153 | av_log(s, AV_LOG_DEBUG, "firstframe = %"PRIu32"\n", ape_ctx->firstframe); |
||
154 | av_log(s, AV_LOG_DEBUG, "totalsamples = %"PRIu32"\n", ape_ctx->totalsamples); |
||
155 | #endif |
||
156 | } |
||
157 | |||
158 | static int ape_read_header(AVFormatContext * s) |
||
159 | { |
||
160 | AVIOContext *pb = s->pb; |
||
161 | APEContext *ape = s->priv_data; |
||
162 | AVStream *st; |
||
163 | uint32_t tag; |
||
164 | int i; |
||
165 | int total_blocks, final_size = 0; |
||
166 | int64_t pts, file_size; |
||
167 | |||
168 | /* Skip any leading junk such as id3v2 tags */ |
||
169 | ape->junklength = avio_tell(pb); |
||
170 | |||
171 | tag = avio_rl32(pb); |
||
172 | if (tag != MKTAG('M', 'A', 'C', ' ')) |
||
173 | return AVERROR_INVALIDDATA; |
||
174 | |||
175 | ape->fileversion = avio_rl16(pb); |
||
176 | |||
177 | if (ape->fileversion < APE_MIN_VERSION || ape->fileversion > APE_MAX_VERSION) { |
||
178 | av_log(s, AV_LOG_ERROR, "Unsupported file version - %d.%02d\n", |
||
179 | ape->fileversion / 1000, (ape->fileversion % 1000) / 10); |
||
180 | return AVERROR_PATCHWELCOME; |
||
181 | } |
||
182 | |||
183 | if (ape->fileversion >= 3980) { |
||
184 | ape->padding1 = avio_rl16(pb); |
||
185 | ape->descriptorlength = avio_rl32(pb); |
||
186 | ape->headerlength = avio_rl32(pb); |
||
187 | ape->seektablelength = avio_rl32(pb); |
||
188 | ape->wavheaderlength = avio_rl32(pb); |
||
189 | ape->audiodatalength = avio_rl32(pb); |
||
190 | ape->audiodatalength_high = avio_rl32(pb); |
||
191 | ape->wavtaillength = avio_rl32(pb); |
||
192 | avio_read(pb, ape->md5, 16); |
||
193 | |||
194 | /* Skip any unknown bytes at the end of the descriptor. |
||
195 | This is for future compatibility */ |
||
196 | if (ape->descriptorlength > 52) |
||
197 | avio_skip(pb, ape->descriptorlength - 52); |
||
198 | |||
199 | /* Read header data */ |
||
200 | ape->compressiontype = avio_rl16(pb); |
||
201 | ape->formatflags = avio_rl16(pb); |
||
202 | ape->blocksperframe = avio_rl32(pb); |
||
203 | ape->finalframeblocks = avio_rl32(pb); |
||
204 | ape->totalframes = avio_rl32(pb); |
||
205 | ape->bps = avio_rl16(pb); |
||
206 | ape->channels = avio_rl16(pb); |
||
207 | ape->samplerate = avio_rl32(pb); |
||
208 | } else { |
||
209 | ape->descriptorlength = 0; |
||
210 | ape->headerlength = 32; |
||
211 | |||
212 | ape->compressiontype = avio_rl16(pb); |
||
213 | ape->formatflags = avio_rl16(pb); |
||
214 | ape->channels = avio_rl16(pb); |
||
215 | ape->samplerate = avio_rl32(pb); |
||
216 | ape->wavheaderlength = avio_rl32(pb); |
||
217 | ape->wavtaillength = avio_rl32(pb); |
||
218 | ape->totalframes = avio_rl32(pb); |
||
219 | ape->finalframeblocks = avio_rl32(pb); |
||
220 | |||
221 | if (ape->formatflags & MAC_FORMAT_FLAG_HAS_PEAK_LEVEL) { |
||
222 | avio_skip(pb, 4); /* Skip the peak level */ |
||
223 | ape->headerlength += 4; |
||
224 | } |
||
225 | |||
226 | if (ape->formatflags & MAC_FORMAT_FLAG_HAS_SEEK_ELEMENTS) { |
||
227 | ape->seektablelength = avio_rl32(pb); |
||
228 | ape->headerlength += 4; |
||
229 | ape->seektablelength *= sizeof(int32_t); |
||
230 | } else |
||
231 | ape->seektablelength = ape->totalframes * sizeof(int32_t); |
||
232 | |||
233 | if (ape->formatflags & MAC_FORMAT_FLAG_8_BIT) |
||
234 | ape->bps = 8; |
||
235 | else if (ape->formatflags & MAC_FORMAT_FLAG_24_BIT) |
||
236 | ape->bps = 24; |
||
237 | else |
||
238 | ape->bps = 16; |
||
239 | |||
240 | if (ape->fileversion >= 3950) |
||
241 | ape->blocksperframe = 73728 * 4; |
||
242 | else if (ape->fileversion >= 3900 || (ape->fileversion >= 3800 && ape->compressiontype >= 4000)) |
||
243 | ape->blocksperframe = 73728; |
||
244 | else |
||
245 | ape->blocksperframe = 9216; |
||
246 | |||
247 | /* Skip any stored wav header */ |
||
248 | if (!(ape->formatflags & MAC_FORMAT_FLAG_CREATE_WAV_HEADER)) |
||
249 | avio_skip(pb, ape->wavheaderlength); |
||
250 | } |
||
251 | |||
252 | if(!ape->totalframes){ |
||
253 | av_log(s, AV_LOG_ERROR, "No frames in the file!\n"); |
||
254 | return AVERROR(EINVAL); |
||
255 | } |
||
256 | if(ape->totalframes > UINT_MAX / sizeof(APEFrame)){ |
||
257 | av_log(s, AV_LOG_ERROR, "Too many frames: %"PRIu32"\n", |
||
258 | ape->totalframes); |
||
259 | return AVERROR_INVALIDDATA; |
||
260 | } |
||
261 | if (ape->seektablelength / sizeof(*ape->seektable) < ape->totalframes) { |
||
262 | av_log(s, AV_LOG_ERROR, |
||
263 | "Number of seek entries is less than number of frames: %zu vs. %"PRIu32"\n", |
||
264 | ape->seektablelength / sizeof(*ape->seektable), ape->totalframes); |
||
265 | return AVERROR_INVALIDDATA; |
||
266 | } |
||
267 | ape->frames = av_malloc(ape->totalframes * sizeof(APEFrame)); |
||
268 | if(!ape->frames) |
||
269 | return AVERROR(ENOMEM); |
||
270 | ape->firstframe = ape->junklength + ape->descriptorlength + ape->headerlength + ape->seektablelength + ape->wavheaderlength; |
||
271 | if (ape->fileversion < 3810) |
||
272 | ape->firstframe += ape->totalframes; |
||
273 | ape->currentframe = 0; |
||
274 | |||
275 | |||
276 | ape->totalsamples = ape->finalframeblocks; |
||
277 | if (ape->totalframes > 1) |
||
278 | ape->totalsamples += ape->blocksperframe * (ape->totalframes - 1); |
||
279 | |||
280 | if (ape->seektablelength > 0) { |
||
281 | ape->seektable = av_malloc(ape->seektablelength); |
||
282 | if (!ape->seektable) |
||
283 | return AVERROR(ENOMEM); |
||
284 | for (i = 0; i < ape->seektablelength / sizeof(uint32_t) && !pb->eof_reached; i++) |
||
285 | ape->seektable[i] = avio_rl32(pb); |
||
286 | if (ape->fileversion < 3810) { |
||
287 | ape->bittable = av_malloc(ape->totalframes); |
||
288 | if (!ape->bittable) |
||
289 | return AVERROR(ENOMEM); |
||
290 | for (i = 0; i < ape->totalframes && !pb->eof_reached; i++) |
||
291 | ape->bittable[i] = avio_r8(pb); |
||
292 | } |
||
293 | } |
||
294 | |||
295 | ape->frames[0].pos = ape->firstframe; |
||
296 | ape->frames[0].nblocks = ape->blocksperframe; |
||
297 | ape->frames[0].skip = 0; |
||
298 | for (i = 1; i < ape->totalframes; i++) { |
||
299 | ape->frames[i].pos = ape->seektable[i] + ape->junklength; |
||
300 | ape->frames[i].nblocks = ape->blocksperframe; |
||
301 | ape->frames[i - 1].size = ape->frames[i].pos - ape->frames[i - 1].pos; |
||
302 | ape->frames[i].skip = (ape->frames[i].pos - ape->frames[0].pos) & 3; |
||
303 | } |
||
304 | ape->frames[ape->totalframes - 1].nblocks = ape->finalframeblocks; |
||
305 | /* calculate final packet size from total file size, if available */ |
||
306 | file_size = avio_size(pb); |
||
307 | if (file_size > 0) { |
||
308 | final_size = file_size - ape->frames[ape->totalframes - 1].pos - |
||
309 | ape->wavtaillength; |
||
310 | final_size -= final_size & 3; |
||
311 | } |
||
312 | if (file_size <= 0 || final_size <= 0) |
||
313 | final_size = ape->finalframeblocks * 8; |
||
314 | ape->frames[ape->totalframes - 1].size = final_size; |
||
315 | |||
316 | for (i = 0; i < ape->totalframes; i++) { |
||
317 | if(ape->frames[i].skip){ |
||
318 | ape->frames[i].pos -= ape->frames[i].skip; |
||
319 | ape->frames[i].size += ape->frames[i].skip; |
||
320 | } |
||
321 | ape->frames[i].size = (ape->frames[i].size + 3) & ~3; |
||
322 | } |
||
323 | if (ape->fileversion < 3810) { |
||
324 | for (i = 0; i < ape->totalframes; i++) { |
||
325 | if (i < ape->totalframes - 1 && ape->bittable[i + 1]) |
||
326 | ape->frames[i].size += 4; |
||
327 | ape->frames[i].skip <<= 3; |
||
328 | ape->frames[i].skip += ape->bittable[i]; |
||
329 | } |
||
330 | } |
||
331 | |||
332 | ape_dumpinfo(s, ape); |
||
333 | |||
334 | av_log(s, AV_LOG_DEBUG, "Decoding file - v%d.%02d, compression level %"PRIu16"\n", |
||
335 | ape->fileversion / 1000, (ape->fileversion % 1000) / 10, |
||
336 | ape->compressiontype); |
||
337 | |||
338 | /* now we are ready: build format streams */ |
||
339 | st = avformat_new_stream(s, NULL); |
||
340 | if (!st) |
||
341 | return AVERROR(ENOMEM); |
||
342 | |||
343 | total_blocks = (ape->totalframes == 0) ? 0 : ((ape->totalframes - 1) * ape->blocksperframe) + ape->finalframeblocks; |
||
344 | |||
345 | st->codec->codec_type = AVMEDIA_TYPE_AUDIO; |
||
346 | st->codec->codec_id = AV_CODEC_ID_APE; |
||
347 | st->codec->codec_tag = MKTAG('A', 'P', 'E', ' '); |
||
348 | st->codec->channels = ape->channels; |
||
349 | st->codec->sample_rate = ape->samplerate; |
||
350 | st->codec->bits_per_coded_sample = ape->bps; |
||
351 | |||
352 | st->nb_frames = ape->totalframes; |
||
353 | st->start_time = 0; |
||
354 | st->duration = total_blocks; |
||
355 | avpriv_set_pts_info(st, 64, 1, ape->samplerate); |
||
356 | |||
357 | if (ff_alloc_extradata(st->codec, APE_EXTRADATA_SIZE)) |
||
358 | return AVERROR(ENOMEM); |
||
359 | AV_WL16(st->codec->extradata + 0, ape->fileversion); |
||
360 | AV_WL16(st->codec->extradata + 2, ape->compressiontype); |
||
361 | AV_WL16(st->codec->extradata + 4, ape->formatflags); |
||
362 | |||
363 | pts = 0; |
||
364 | for (i = 0; i < ape->totalframes; i++) { |
||
365 | ape->frames[i].pts = pts; |
||
366 | av_add_index_entry(st, ape->frames[i].pos, ape->frames[i].pts, 0, 0, AVINDEX_KEYFRAME); |
||
367 | pts += ape->blocksperframe; |
||
368 | } |
||
369 | |||
370 | /* try to read APE tags */ |
||
371 | if (pb->seekable) { |
||
372 | ff_ape_parse_tag(s); |
||
373 | avio_seek(pb, 0, SEEK_SET); |
||
374 | } |
||
375 | |||
376 | return 0; |
||
377 | } |
||
378 | |||
379 | static int ape_read_packet(AVFormatContext * s, AVPacket * pkt) |
||
380 | { |
||
381 | int ret; |
||
382 | int nblocks; |
||
383 | APEContext *ape = s->priv_data; |
||
384 | uint32_t extra_size = 8; |
||
385 | |||
386 | if (url_feof(s->pb)) |
||
387 | return AVERROR_EOF; |
||
388 | if (ape->currentframe >= ape->totalframes) |
||
389 | return AVERROR_EOF; |
||
390 | |||
391 | if (avio_seek(s->pb, ape->frames[ape->currentframe].pos, SEEK_SET) < 0) |
||
392 | return AVERROR(EIO); |
||
393 | |||
394 | /* Calculate how many blocks there are in this frame */ |
||
395 | if (ape->currentframe == (ape->totalframes - 1)) |
||
396 | nblocks = ape->finalframeblocks; |
||
397 | else |
||
398 | nblocks = ape->blocksperframe; |
||
399 | |||
400 | if (ape->frames[ape->currentframe].size <= 0 || |
||
401 | ape->frames[ape->currentframe].size > INT_MAX - extra_size) { |
||
402 | av_log(s, AV_LOG_ERROR, "invalid packet size: %d\n", |
||
403 | ape->frames[ape->currentframe].size); |
||
404 | ape->currentframe++; |
||
405 | return AVERROR(EIO); |
||
406 | } |
||
407 | |||
408 | if (av_new_packet(pkt, ape->frames[ape->currentframe].size + extra_size) < 0) |
||
409 | return AVERROR(ENOMEM); |
||
410 | |||
411 | AV_WL32(pkt->data , nblocks); |
||
412 | AV_WL32(pkt->data + 4, ape->frames[ape->currentframe].skip); |
||
413 | ret = avio_read(s->pb, pkt->data + extra_size, ape->frames[ape->currentframe].size); |
||
414 | if (ret < 0) |
||
415 | return ret; |
||
416 | |||
417 | pkt->pts = ape->frames[ape->currentframe].pts; |
||
418 | pkt->stream_index = 0; |
||
419 | |||
420 | /* note: we need to modify the packet size here to handle the last |
||
421 | packet */ |
||
422 | pkt->size = ret + extra_size; |
||
423 | |||
424 | ape->currentframe++; |
||
425 | |||
426 | return 0; |
||
427 | } |
||
428 | |||
429 | static int ape_read_close(AVFormatContext * s) |
||
430 | { |
||
431 | APEContext *ape = s->priv_data; |
||
432 | |||
433 | av_freep(&ape->frames); |
||
434 | av_freep(&ape->seektable); |
||
435 | av_freep(&ape->bittable); |
||
436 | return 0; |
||
437 | } |
||
438 | |||
439 | static int ape_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags) |
||
440 | { |
||
441 | AVStream *st = s->streams[stream_index]; |
||
442 | APEContext *ape = s->priv_data; |
||
443 | int index = av_index_search_timestamp(st, timestamp, flags); |
||
444 | |||
445 | if (index < 0) |
||
446 | return -1; |
||
447 | |||
448 | if (avio_seek(s->pb, st->index_entries[index].pos, SEEK_SET) < 0) |
||
449 | return -1; |
||
450 | ape->currentframe = index; |
||
451 | return 0; |
||
452 | } |
||
453 | |||
454 | AVInputFormat ff_ape_demuxer = { |
||
455 | .name = "ape", |
||
456 | .long_name = NULL_IF_CONFIG_SMALL("Monkey's Audio"), |
||
457 | .priv_data_size = sizeof(APEContext), |
||
458 | .read_probe = ape_probe, |
||
459 | .read_header = ape_read_header, |
||
460 | .read_packet = ape_read_packet, |
||
461 | .read_close = ape_read_close, |
||
462 | .read_seek = ape_read_seek, |
||
463 | .extensions = "ape,apl,mac", |
||
464 | };>>>>=>>>=><=>>>>>=>=>>>>>>>>>>>> |