Mercurial > libavformat.hg
annotate cafdec.c @ 5836:2997c88028cd libavformat
Move the probe loop from av_open_input_file() into its own method
av_probe_input_buffer() so that it can be reused. Here are a few
differences to the original way things were probed:
- maximum probe buffer size can be specified as a parameter.
- offset within the stream to probe from can be specified as a parameter.
- instead of seeking back to the start each time a probe fails, stream
data is appended to the reallocated buffer. This lowers the amount
of data read from the stream (there is no repetition) and results in
fewer closed and reopened streams (when seeking fails).
New attempt after r22296, which was revert in r22315 due to a FATE
failure.
See the thread:
Subject: [FFmpeg-devel] [PATCH] Move av_open_input_file probe loop to its own method
Date: 2010-03-05 03:23:57 GMT
Patch by Micah F. Galizia printf("%s%s@%s.%s", "micah", "galizia", "gmail", "com").
author | stefano |
---|---|
date | Sun, 14 Mar 2010 22:40:16 +0000 |
parents | 134741dc8327 |
children | 536e5527c1e0 |
rev | line source |
---|---|
5206 | 1 /* |
2 * Core Audio Format demuxer | |
3 * Copyright (c) 2007 Justin Ruggles | |
4 * Copyright (c) 2009 Peter Ross | |
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 /** | |
24 * @file libavformat/cafdec.c | |
25 * Core Audio Format demuxer | |
26 */ | |
27 | |
28 #include "avformat.h" | |
29 #include "riff.h" | |
30 #include "isom.h" | |
31 #include "libavutil/intreadwrite.h" | |
32 #include "caf.h" | |
33 | |
34 typedef struct { | |
35 int bytes_per_packet; ///< bytes in a packet, or 0 if variable | |
36 int frames_per_packet; ///< frames in a packet, or 0 if variable | |
37 int64_t num_bytes; ///< total number of bytes in stream | |
38 | |
39 int64_t packet_cnt; ///< packet counter | |
40 int64_t frame_cnt; ///< frame counter | |
41 | |
42 int64_t data_start; ///< data start position, in bytes | |
43 int64_t data_size; ///< raw data size, in bytes | |
44 } CaffContext; | |
45 | |
46 static int probe(AVProbeData *p) | |
47 { | |
48 if (AV_RB32(p->buf) == MKBETAG('c','a','f','f') && AV_RB16(&p->buf[4]) == 1) | |
49 return AVPROBE_SCORE_MAX; | |
50 return 0; | |
51 } | |
52 | |
53 /** Read audio description chunk */ | |
54 static int read_desc_chunk(AVFormatContext *s) | |
55 { | |
56 ByteIOContext *pb = s->pb; | |
57 CaffContext *caf = s->priv_data; | |
58 AVStream *st; | |
59 int flags; | |
60 | |
61 /* new audio stream */ | |
62 st = av_new_stream(s, 0); | |
63 if (!st) | |
64 return AVERROR_NOMEM; | |
65 | |
66 /* parse format description */ | |
67 st->codec->codec_type = CODEC_TYPE_AUDIO; | |
68 st->codec->sample_rate = av_int2dbl(get_be64(pb)); | |
69 st->codec->codec_tag = get_be32(pb); | |
70 flags = get_be32(pb); | |
71 caf->bytes_per_packet = get_be32(pb); | |
72 st->codec->block_align = caf->bytes_per_packet; | |
73 caf->frames_per_packet = get_be32(pb); | |
74 st->codec->channels = get_be32(pb); | |
75 st->codec->bits_per_coded_sample = get_be32(pb); | |
76 | |
77 /* calculate bit rate for constant size packets */ | |
78 if (caf->frames_per_packet > 0 && caf->bytes_per_packet > 0) { | |
79 st->codec->bit_rate = (uint64_t)st->codec->sample_rate * (uint64_t)caf->bytes_per_packet * 8 | |
80 / (uint64_t)caf->frames_per_packet; | |
81 } else { | |
82 st->codec->bit_rate = 0; | |
83 } | |
84 | |
85 /* determine codec */ | |
86 if (st->codec->codec_tag == MKBETAG('l','p','c','m')) | |
87 st->codec->codec_id = ff_mov_get_lpcm_codec_id(st->codec->bits_per_coded_sample, (flags ^ 0x2) | 0x4); | |
88 else | |
89 st->codec->codec_id = ff_codec_get_id(ff_codec_caf_tags, st->codec->codec_tag); | |
90 return 0; | |
91 } | |
92 | |
93 /** Read magic cookie chunk */ | |
94 static int read_kuki_chunk(AVFormatContext *s, int64_t size) | |
95 { | |
96 ByteIOContext *pb = s->pb; | |
97 AVStream *st = s->streams[0]; | |
98 | |
99 if (size < 0 || size > INT_MAX - FF_INPUT_BUFFER_PADDING_SIZE) | |
100 return -1; | |
101 | |
102 if (st->codec->codec_id == CODEC_ID_AAC) { | |
103 /* The magic cookie format for AAC is an mp4 esds atom. | |
104 The lavc AAC decoder requires the data from the codec specific | |
105 description as extradata input. */ | |
106 int strt, skip; | |
107 MOVAtom atom; | |
108 | |
109 strt = url_ftell(pb); | |
110 ff_mov_read_esds(s, pb, atom); | |
111 skip = size - (url_ftell(pb) - strt); | |
112 if (skip < 0 || !st->codec->extradata || | |
113 st->codec->codec_id != CODEC_ID_AAC) { | |
114 av_log(s, AV_LOG_ERROR, "invalid AAC magic cookie\n"); | |
115 return AVERROR_INVALIDDATA; | |
116 } | |
117 url_fskip(pb, skip); | |
118 } else { | |
119 st->codec->extradata = av_mallocz(size + FF_INPUT_BUFFER_PADDING_SIZE); | |
120 if (!st->codec->extradata) | |
121 return AVERROR(ENOMEM); | |
122 get_buffer(pb, st->codec->extradata, size); | |
123 st->codec->extradata_size = size; | |
124 } | |
125 | |
126 return 0; | |
127 } | |
128 | |
129 /** Read packet table chunk */ | |
130 static int read_pakt_chunk(AVFormatContext *s, int64_t size) | |
131 { | |
132 ByteIOContext *pb = s->pb; | |
133 AVStream *st = s->streams[0]; | |
134 CaffContext *caf = s->priv_data; | |
135 int64_t pos = 0, ccount; | |
136 int num_packets, i; | |
137 | |
138 ccount = url_ftell(pb); | |
139 | |
140 num_packets = get_be64(pb); | |
141 if (num_packets < 0 || INT32_MAX / sizeof(AVIndexEntry) < num_packets) | |
142 return AVERROR_INVALIDDATA; | |
143 | |
144 st->nb_frames = get_be64(pb); /* valid frames */ | |
145 st->nb_frames += get_be32(pb); /* priming frames */ | |
146 st->nb_frames += get_be32(pb); /* remainder frames */ | |
147 | |
148 st->duration = 0; | |
149 for (i = 0; i < num_packets; i++) { | |
150 av_add_index_entry(s->streams[0], pos, st->duration, 0, 0, AVINDEX_KEYFRAME); | |
151 pos += caf->bytes_per_packet ? caf->bytes_per_packet : ff_mp4_read_descr_len(pb); | |
152 st->duration += caf->frames_per_packet ? caf->frames_per_packet : ff_mp4_read_descr_len(pb); | |
153 } | |
154 | |
155 if (url_ftell(pb) - ccount != size) { | |
156 av_log(s, AV_LOG_ERROR, "error reading packet table\n"); | |
157 return -1; | |
158 } | |
159 | |
160 caf->num_bytes = pos; | |
161 return 0; | |
162 } | |
163 | |
164 /** Read information chunk */ | |
165 static void read_info_chunk(AVFormatContext *s, int64_t size) | |
166 { | |
167 ByteIOContext *pb = s->pb; | |
168 unsigned int i; | |
169 unsigned int nb_entries = get_be32(pb); | |
170 for (i = 0; i < nb_entries; i++) { | |
171 char key[32]; | |
172 char value[1024]; | |
173 get_strz(pb, key, sizeof(key)); | |
174 get_strz(pb, value, sizeof(value)); | |
175 av_metadata_set(&s->metadata, key, value); | |
176 } | |
177 } | |
178 | |
179 static int read_header(AVFormatContext *s, | |
180 AVFormatParameters *ap) | |
181 { | |
182 ByteIOContext *pb = s->pb; | |
183 CaffContext *caf = s->priv_data; | |
184 AVStream *st; | |
185 uint32_t tag = 0; | |
186 int found_data, ret; | |
187 int64_t size; | |
188 | |
189 url_fskip(pb, 8); /* magic, version, file flags */ | |
190 | |
191 /* audio description chunk */ | |
192 if (get_be32(pb) != MKBETAG('d','e','s','c')) { | |
193 av_log(s, AV_LOG_ERROR, "desc chunk not present\n"); | |
194 return AVERROR_INVALIDDATA; | |
195 } | |
196 size = get_be64(pb); | |
197 if (size != 32) | |
198 return AVERROR_INVALIDDATA; | |
199 | |
200 ret = read_desc_chunk(s); | |
201 if (ret) | |
202 return ret; | |
203 st = s->streams[0]; | |
204 | |
205 /* parse each chunk */ | |
206 found_data = 0; | |
207 while (!url_feof(pb)) { | |
208 | |
209 /* stop at data chunk if seeking is not supported or | |
210 data chunk size is unknown */ | |
211 if (found_data && (caf->data_size < 0 || url_is_streamed(pb))) | |
212 break; | |
213 | |
214 tag = get_be32(pb); | |
215 size = get_be64(pb); | |
216 if (url_feof(pb)) | |
217 break; | |
218 | |
219 switch (tag) { | |
220 case MKBETAG('d','a','t','a'): | |
221 url_fskip(pb, 4); /* edit count */ | |
222 caf->data_start = url_ftell(pb); | |
223 caf->data_size = size < 0 ? -1 : size - 4; | |
224 if (caf->data_size > 0 && !url_is_streamed(pb)) | |
225 url_fskip(pb, caf->data_size); | |
226 found_data = 1; | |
227 break; | |
228 | |
229 /* magic cookie chunk */ | |
230 case MKBETAG('k','u','k','i'): | |
231 if (read_kuki_chunk(s, size)) | |
232 return AVERROR_INVALIDDATA; | |
233 break; | |
234 | |
235 /* packet table chunk */ | |
236 case MKBETAG('p','a','k','t'): | |
237 if (read_pakt_chunk(s, size)) | |
238 return AVERROR_INVALIDDATA; | |
239 break; | |
240 | |
241 case MKBETAG('i','n','f','o'): | |
242 read_info_chunk(s, size); | |
243 break; | |
244 | |
245 default: | |
246 #define _(x) ((x) >= ' ' ? (x) : ' ') | |
247 av_log(s, AV_LOG_WARNING, "skipping CAF chunk: %08X (%c%c%c%c)\n", | |
248 tag, _(tag>>24), _((tag>>16)&0xFF), _((tag>>8)&0xFF), _(tag&0xFF)); | |
249 #undef _ | |
250 case MKBETAG('f','r','e','e'): | |
251 if (size < 0) | |
252 return AVERROR_INVALIDDATA; | |
253 url_fskip(pb, size); | |
254 break; | |
255 } | |
256 } | |
257 | |
258 if (!found_data) | |
259 return AVERROR_INVALIDDATA; | |
260 | |
261 if (caf->bytes_per_packet > 0 && caf->frames_per_packet > 0) { | |
262 if (caf->data_size > 0) | |
263 st->nb_frames = (caf->data_size / caf->bytes_per_packet) * caf->frames_per_packet; | |
264 } else if (st->nb_index_entries) { | |
265 st->codec->bit_rate = st->codec->sample_rate * caf->data_size * 8 / | |
266 st->duration; | |
267 } else { | |
268 av_log(s, AV_LOG_ERROR, "Missing packet table. It is required when " | |
269 "block size or frame size are variable.\n"); | |
270 return AVERROR_INVALIDDATA; | |
271 } | |
272 s->file_size = url_fsize(pb); | |
273 s->file_size = FFMAX(0, s->file_size); | |
274 | |
275 av_set_pts_info(st, 64, 1, st->codec->sample_rate); | |
276 st->start_time = 0; | |
277 | |
278 /* position the stream at the start of data */ | |
279 if (caf->data_size >= 0) | |
280 url_fseek(pb, caf->data_start, SEEK_SET); | |
281 | |
282 return 0; | |
283 } | |
284 | |
285 #define CAF_MAX_PKT_SIZE 4096 | |
286 | |
287 static int read_packet(AVFormatContext *s, AVPacket *pkt) | |
288 { | |
289 ByteIOContext *pb = s->pb; | |
290 AVStream *st = s->streams[0]; | |
291 CaffContext *caf = s->priv_data; | |
292 int res, pkt_size = 0, pkt_frames = 0; | |
293 int64_t left = CAF_MAX_PKT_SIZE; | |
294 | |
295 if (url_feof(pb)) | |
5834
134741dc8327
Replace all the occurrences of AVERROR_EIO with AVERROR(EIO), and mark
stefano
parents:
5206
diff
changeset
|
296 return AVERROR(EIO); |
5206 | 297 |
298 /* don't read past end of data chunk */ | |
299 if (caf->data_size > 0) { | |
300 left = (caf->data_start + caf->data_size) - url_ftell(pb); | |
301 if (left <= 0) | |
5834
134741dc8327
Replace all the occurrences of AVERROR_EIO with AVERROR(EIO), and mark
stefano
parents:
5206
diff
changeset
|
302 return AVERROR(EIO); |
5206 | 303 } |
304 | |
305 pkt_frames = caf->frames_per_packet; | |
306 pkt_size = caf->bytes_per_packet; | |
307 | |
308 if (pkt_size > 0 && pkt_frames == 1) { | |
309 pkt_size = (CAF_MAX_PKT_SIZE / pkt_size) * pkt_size; | |
310 pkt_size = FFMIN(pkt_size, left); | |
311 pkt_frames = pkt_size / caf->bytes_per_packet; | |
312 } else if (st->nb_index_entries) { | |
313 if (caf->packet_cnt < st->nb_index_entries - 1) { | |
314 pkt_size = st->index_entries[caf->packet_cnt + 1].pos - st->index_entries[caf->packet_cnt].pos; | |
315 pkt_frames = st->index_entries[caf->packet_cnt + 1].timestamp - st->index_entries[caf->packet_cnt].timestamp; | |
316 } else if (caf->packet_cnt == st->nb_index_entries - 1) { | |
317 pkt_size = caf->num_bytes - st->index_entries[caf->packet_cnt].pos; | |
318 pkt_frames = st->duration - st->index_entries[caf->packet_cnt].timestamp; | |
319 } else { | |
5834
134741dc8327
Replace all the occurrences of AVERROR_EIO with AVERROR(EIO), and mark
stefano
parents:
5206
diff
changeset
|
320 return AVERROR(EIO); |
5206 | 321 } |
322 } | |
323 | |
324 if (pkt_size == 0 || pkt_frames == 0 || pkt_size > left) | |
5834
134741dc8327
Replace all the occurrences of AVERROR_EIO with AVERROR(EIO), and mark
stefano
parents:
5206
diff
changeset
|
325 return AVERROR(EIO); |
5206 | 326 |
327 res = av_get_packet(pb, pkt, pkt_size); | |
328 if (res < 0) | |
329 return res; | |
330 | |
331 pkt->size = res; | |
332 pkt->stream_index = 0; | |
333 pkt->dts = pkt->pts = caf->frame_cnt; | |
334 | |
335 caf->packet_cnt++; | |
336 caf->frame_cnt += pkt_frames; | |
337 | |
338 return 0; | |
339 } | |
340 | |
341 static int read_seek(AVFormatContext *s, int stream_index, | |
342 int64_t timestamp, int flags) | |
343 { | |
344 AVStream *st = s->streams[0]; | |
345 CaffContext *caf = s->priv_data; | |
346 int64_t pos; | |
347 | |
348 timestamp = FFMAX(timestamp, 0); | |
349 | |
350 if (caf->frames_per_packet > 0 && caf->bytes_per_packet > 0) { | |
351 /* calculate new byte position based on target frame position */ | |
352 pos = caf->bytes_per_packet * timestamp / caf->frames_per_packet; | |
353 if (caf->data_size > 0) | |
354 pos = FFMIN(pos, caf->data_size); | |
355 caf->packet_cnt = pos / caf->bytes_per_packet; | |
356 caf->frame_cnt = caf->frames_per_packet * caf->packet_cnt; | |
357 } else if (st->nb_index_entries) { | |
358 caf->packet_cnt = av_index_search_timestamp(st, timestamp, flags); | |
359 caf->frame_cnt = st->index_entries[caf->packet_cnt].timestamp; | |
360 pos = st->index_entries[caf->packet_cnt].pos; | |
361 } else { | |
362 return -1; | |
363 } | |
364 | |
365 url_fseek(s->pb, pos + caf->data_start, SEEK_SET); | |
366 return 0; | |
367 } | |
368 | |
369 AVInputFormat caf_demuxer = { | |
370 "caf", | |
371 NULL_IF_CONFIG_SMALL("Apple Core Audio Format"), | |
372 sizeof(CaffContext), | |
373 probe, | |
374 read_header, | |
375 read_packet, | |
376 NULL, | |
377 read_seek, | |
378 .codec_tag = (const AVCodecTag*[]){ff_codec_caf_tags, 0}, | |
379 }; |