Mercurial > libavformat.hg
annotate rpl.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 | 8c9ba1c9bcbc |
children | 536e5527c1e0 |
rev | line source |
---|---|
3197 | 1 /* |
2 * ARMovie/RPL demuxer | |
3 * Copyright (c) 2007 Christian Ohm, 2008 Eli Friedman | |
4 * | |
5 * This file is part of FFmpeg. | |
6 * | |
7 * FFmpeg is free software; you can redistribute it and/or | |
8 * modify it under the terms of the GNU Lesser General Public | |
9 * License as published by the Free Software Foundation; either | |
10 * version 2.1 of the License, or (at your option) any later version. | |
11 * | |
12 * FFmpeg is distributed in the hope that it will be useful, | |
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 * Lesser General Public License for more details. | |
16 * | |
17 * You should have received a copy of the GNU Lesser General Public | |
18 * License along with FFmpeg; if not, write to the Free Software | |
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
20 */ | |
21 | |
3286 | 22 #include "libavutil/avstring.h" |
3197 | 23 #include "avformat.h" |
24 #include <stdlib.h> | |
25 | |
26 #define RPL_SIGNATURE "ARMovie\x0A" | |
27 #define RPL_SIGNATURE_SIZE 8 | |
28 | |
29 /** 256 is arbitrary, but should be big enough for any reasonable file. */ | |
30 #define RPL_LINE_LENGTH 256 | |
31 | |
32 static int rpl_probe(AVProbeData *p) | |
33 { | |
34 if (memcmp(p->buf, RPL_SIGNATURE, RPL_SIGNATURE_SIZE)) | |
35 return 0; | |
36 | |
37 return AVPROBE_SCORE_MAX; | |
38 } | |
39 | |
40 typedef struct RPLContext { | |
41 // RPL header data | |
42 int32_t frames_per_chunk; | |
43 | |
44 // Stream position data | |
45 uint32_t chunk_number; | |
46 uint32_t chunk_part; | |
47 uint32_t frame_in_part; | |
48 } RPLContext; | |
49 | |
50 static int read_line(ByteIOContext * pb, char* line, int bufsize) | |
51 { | |
52 int i; | |
53 for (i = 0; i < bufsize - 1; i++) { | |
54 int b = get_byte(pb); | |
55 if (b == 0) | |
56 break; | |
57 if (b == '\n') { | |
58 line[i] = '\0'; | |
59 return 0; | |
60 } | |
61 line[i] = b; | |
62 } | |
63 line[i] = '\0'; | |
64 return -1; | |
65 } | |
66 | |
67 static int32_t read_int(const char* line, const char** endptr, int* error) | |
68 { | |
69 unsigned long result = 0; | |
70 for (; *line>='0' && *line<='9'; line++) { | |
71 if (result > (0x7FFFFFFF - 9) / 10) | |
72 *error = -1; | |
73 result = 10 * result + *line - '0'; | |
74 } | |
75 *endptr = line; | |
76 return result; | |
77 } | |
78 | |
79 static int32_t read_line_and_int(ByteIOContext * pb, int* error) | |
80 { | |
81 char line[RPL_LINE_LENGTH]; | |
82 const char *endptr; | |
83 *error |= read_line(pb, line, sizeof(line)); | |
84 return read_int(line, &endptr, error); | |
85 } | |
86 | |
87 /** Parsing for fps, which can be a fraction. Unfortunately, | |
88 * the spec for the header leaves out a lot of details, | |
89 * so this is mostly guessing. | |
90 */ | |
91 static AVRational read_fps(const char* line, int* error) | |
92 { | |
93 int64_t num, den = 1; | |
94 AVRational result; | |
95 num = read_int(line, &line, error); | |
96 if (*line == '.') | |
97 line++; | |
98 for (; *line>='0' && *line<='9'; line++) { | |
99 // Truncate any numerator too large to fit into an int64_t | |
100 if (num > (INT64_MAX - 9) / 10 || den > INT64_MAX / 10) | |
101 break; | |
102 num = 10 * num + *line - '0'; | |
103 den *= 10; | |
104 } | |
105 if (!num) | |
106 *error = -1; | |
107 av_reduce(&result.num, &result.den, num, den, 0x7FFFFFFF); | |
108 return result; | |
109 } | |
110 | |
111 static int rpl_read_header(AVFormatContext *s, AVFormatParameters *ap) | |
112 { | |
113 ByteIOContext *pb = s->pb; | |
114 RPLContext *rpl = s->priv_data; | |
115 AVStream *vst = NULL, *ast = NULL; | |
116 int total_audio_size; | |
117 int error = 0; | |
118 | |
119 uint32_t i; | |
120 | |
121 int32_t audio_format, chunk_catalog_offset, number_of_chunks; | |
122 AVRational fps; | |
123 | |
124 char line[RPL_LINE_LENGTH]; | |
125 | |
126 // The header for RPL/ARMovie files is 21 lines of text | |
127 // containing the various header fields. The fields are always | |
128 // in the same order, and other text besides the first | |
129 // number usually isn't important. | |
130 // (The spec says that there exists some significance | |
131 // for the text in a few cases; samples needed.) | |
4598 | 132 error |= read_line(pb, line, sizeof(line)); // ARMovie |
4597 | 133 error |= read_line(pb, line, sizeof(line)); // movie name |
134 av_metadata_set(&s->metadata, "title" , line); | |
135 error |= read_line(pb, line, sizeof(line)); // date/copyright | |
136 av_metadata_set(&s->metadata, "copyright", line); | |
137 error |= read_line(pb, line, sizeof(line)); // author and other | |
138 av_metadata_set(&s->metadata, "author" , line); | |
3197 | 139 |
140 // video headers | |
141 vst = av_new_stream(s, 0); | |
142 if (!vst) | |
143 return AVERROR(ENOMEM); | |
144 vst->codec->codec_type = CODEC_TYPE_VIDEO; | |
145 vst->codec->codec_tag = read_line_and_int(pb, &error); // video format | |
146 vst->codec->width = read_line_and_int(pb, &error); // video width | |
147 vst->codec->height = read_line_and_int(pb, &error); // video height | |
3908
1d3d17de20ba
Bump Major version, this commit is almost just renaming bits_per_sample to
michael
parents:
3424
diff
changeset
|
148 vst->codec->bits_per_coded_sample = read_line_and_int(pb, &error); // video bits per sample |
3197 | 149 error |= read_line(pb, line, sizeof(line)); // video frames per second |
150 fps = read_fps(line, &error); | |
151 av_set_pts_info(vst, 32, fps.den, fps.num); | |
152 | |
153 // Figure out the video codec | |
154 switch (vst->codec->codec_tag) { | |
155 #if 0 | |
156 case 122: | |
157 vst->codec->codec_id = CODEC_ID_ESCAPE122; | |
158 break; | |
159 #endif | |
160 case 124: | |
161 vst->codec->codec_id = CODEC_ID_ESCAPE124; | |
162 // The header is wrong here, at least sometimes | |
3908
1d3d17de20ba
Bump Major version, this commit is almost just renaming bits_per_sample to
michael
parents:
3424
diff
changeset
|
163 vst->codec->bits_per_coded_sample = 16; |
3197 | 164 break; |
165 #if 0 | |
166 case 130: | |
167 vst->codec->codec_id = CODEC_ID_ESCAPE130; | |
168 break; | |
169 #endif | |
170 default: | |
171 av_log(s, AV_LOG_WARNING, | |
172 "RPL video format %i not supported yet!\n", | |
173 vst->codec->codec_tag); | |
174 vst->codec->codec_id = CODEC_ID_NONE; | |
175 } | |
176 | |
177 // Audio headers | |
178 | |
179 // ARMovie supports multiple audio tracks; I don't have any | |
180 // samples, though. This code will ignore additional tracks. | |
181 audio_format = read_line_and_int(pb, &error); // audio format ID | |
182 if (audio_format) { | |
183 ast = av_new_stream(s, 0); | |
184 if (!ast) | |
185 return AVERROR(ENOMEM); | |
186 ast->codec->codec_type = CODEC_TYPE_AUDIO; | |
187 ast->codec->codec_tag = audio_format; | |
188 ast->codec->sample_rate = read_line_and_int(pb, &error); // audio bitrate | |
189 ast->codec->channels = read_line_and_int(pb, &error); // number of audio channels | |
3908
1d3d17de20ba
Bump Major version, this commit is almost just renaming bits_per_sample to
michael
parents:
3424
diff
changeset
|
190 ast->codec->bits_per_coded_sample = read_line_and_int(pb, &error); // audio bits per sample |
3197 | 191 // At least one sample uses 0 for ADPCM, which is really 4 bits |
192 // per sample. | |
3908
1d3d17de20ba
Bump Major version, this commit is almost just renaming bits_per_sample to
michael
parents:
3424
diff
changeset
|
193 if (ast->codec->bits_per_coded_sample == 0) |
1d3d17de20ba
Bump Major version, this commit is almost just renaming bits_per_sample to
michael
parents:
3424
diff
changeset
|
194 ast->codec->bits_per_coded_sample = 4; |
3197 | 195 |
196 ast->codec->bit_rate = ast->codec->sample_rate * | |
3908
1d3d17de20ba
Bump Major version, this commit is almost just renaming bits_per_sample to
michael
parents:
3424
diff
changeset
|
197 ast->codec->bits_per_coded_sample * |
3197 | 198 ast->codec->channels; |
199 | |
200 ast->codec->codec_id = CODEC_ID_NONE; | |
201 switch (audio_format) { | |
202 case 1: | |
3908
1d3d17de20ba
Bump Major version, this commit is almost just renaming bits_per_sample to
michael
parents:
3424
diff
changeset
|
203 if (ast->codec->bits_per_coded_sample == 16) { |
3197 | 204 // 16-bit audio is always signed |
205 ast->codec->codec_id = CODEC_ID_PCM_S16LE; | |
206 break; | |
207 } | |
208 // There are some other formats listed as legal per the spec; | |
209 // samples needed. | |
210 break; | |
211 case 101: | |
3908
1d3d17de20ba
Bump Major version, this commit is almost just renaming bits_per_sample to
michael
parents:
3424
diff
changeset
|
212 if (ast->codec->bits_per_coded_sample == 8) { |
3197 | 213 // The samples with this kind of audio that I have |
214 // are all unsigned. | |
215 ast->codec->codec_id = CODEC_ID_PCM_U8; | |
216 break; | |
3908
1d3d17de20ba
Bump Major version, this commit is almost just renaming bits_per_sample to
michael
parents:
3424
diff
changeset
|
217 } else if (ast->codec->bits_per_coded_sample == 4) { |
3197 | 218 ast->codec->codec_id = CODEC_ID_ADPCM_IMA_EA_SEAD; |
219 break; | |
220 } | |
221 break; | |
222 } | |
223 if (ast->codec->codec_id == CODEC_ID_NONE) { | |
224 av_log(s, AV_LOG_WARNING, | |
225 "RPL audio format %i not supported yet!\n", | |
226 audio_format); | |
227 } | |
228 av_set_pts_info(ast, 32, 1, ast->codec->bit_rate); | |
229 } else { | |
230 for (i = 0; i < 3; i++) | |
231 error |= read_line(pb, line, sizeof(line)); | |
232 } | |
233 | |
234 rpl->frames_per_chunk = read_line_and_int(pb, &error); // video frames per chunk | |
235 if (rpl->frames_per_chunk > 1 && vst->codec->codec_tag != 124) | |
236 av_log(s, AV_LOG_WARNING, | |
237 "Don't know how to split frames for video format %i. " | |
238 "Video stream will be broken!\n", vst->codec->codec_tag); | |
239 | |
240 number_of_chunks = read_line_and_int(pb, &error); // number of chunks in the file | |
241 // The number in the header is actually the index of the last chunk. | |
242 number_of_chunks++; | |
243 | |
244 error |= read_line(pb, line, sizeof(line)); // "even" chunk size in bytes | |
245 error |= read_line(pb, line, sizeof(line)); // "odd" chunk size in bytes | |
246 chunk_catalog_offset = // offset of the "chunk catalog" | |
247 read_line_and_int(pb, &error); // (file index) | |
248 error |= read_line(pb, line, sizeof(line)); // offset to "helpful" sprite | |
249 error |= read_line(pb, line, sizeof(line)); // size of "helpful" sprite | |
250 error |= read_line(pb, line, sizeof(line)); // offset to key frame list | |
251 | |
252 // Read the index | |
253 url_fseek(pb, chunk_catalog_offset, SEEK_SET); | |
254 total_audio_size = 0; | |
255 for (i = 0; i < number_of_chunks; i++) { | |
256 int64_t offset, video_size, audio_size; | |
257 error |= read_line(pb, line, sizeof(line)); | |
258 if (3 != sscanf(line, "%"PRId64" , %"PRId64" ; %"PRId64, | |
259 &offset, &video_size, &audio_size)) | |
260 error = -1; | |
261 av_add_index_entry(vst, offset, i * rpl->frames_per_chunk, | |
262 video_size, rpl->frames_per_chunk, 0); | |
263 if (ast) | |
264 av_add_index_entry(ast, offset + video_size, total_audio_size, | |
265 audio_size, audio_size * 8, 0); | |
266 total_audio_size += audio_size * 8; | |
267 } | |
268 | |
269 if (error) return AVERROR(EIO); | |
270 | |
271 return 0; | |
272 } | |
273 | |
274 static int rpl_read_packet(AVFormatContext *s, AVPacket *pkt) | |
275 { | |
276 RPLContext *rpl = s->priv_data; | |
277 ByteIOContext *pb = s->pb; | |
278 AVStream* stream; | |
279 AVIndexEntry* index_entry; | |
280 uint32_t ret; | |
281 | |
282 if (rpl->chunk_part == s->nb_streams) { | |
283 rpl->chunk_number++; | |
284 rpl->chunk_part = 0; | |
285 } | |
286 | |
287 stream = s->streams[rpl->chunk_part]; | |
288 | |
289 if (rpl->chunk_number >= stream->nb_index_entries) | |
290 return -1; | |
291 | |
292 index_entry = &stream->index_entries[rpl->chunk_number]; | |
293 | |
294 if (rpl->frame_in_part == 0) | |
295 if (url_fseek(pb, index_entry->pos, SEEK_SET) < 0) | |
296 return AVERROR(EIO); | |
297 | |
298 if (stream->codec->codec_type == CODEC_TYPE_VIDEO && | |
299 stream->codec->codec_tag == 124) { | |
300 // We have to split Escape 124 frames because there are | |
301 // multiple frames per chunk in Escape 124 samples. | |
302 uint32_t frame_size, frame_flags; | |
303 | |
304 frame_flags = get_le32(pb); | |
305 frame_size = get_le32(pb); | |
306 if (url_fseek(pb, -8, SEEK_CUR) < 0) | |
307 return AVERROR(EIO); | |
308 | |
309 ret = av_get_packet(pb, pkt, frame_size); | |
310 if (ret != frame_size) { | |
311 av_free_packet(pkt); | |
312 return AVERROR(EIO); | |
313 } | |
314 pkt->duration = 1; | |
315 pkt->pts = index_entry->timestamp + rpl->frame_in_part; | |
316 pkt->stream_index = rpl->chunk_part; | |
317 | |
318 rpl->frame_in_part++; | |
319 if (rpl->frame_in_part == rpl->frames_per_chunk) { | |
320 rpl->frame_in_part = 0; | |
321 rpl->chunk_part++; | |
322 } | |
323 } else { | |
324 ret = av_get_packet(pb, pkt, index_entry->size); | |
325 if (ret != index_entry->size) { | |
326 av_free_packet(pkt); | |
327 return AVERROR(EIO); | |
328 } | |
329 | |
330 if (stream->codec->codec_type == CODEC_TYPE_VIDEO) { | |
331 // frames_per_chunk should always be one here; the header | |
332 // parsing will warn if it isn't. | |
333 pkt->duration = rpl->frames_per_chunk; | |
334 } else { | |
335 // All the audio codecs supported in this container | |
336 // (at least so far) are constant-bitrate. | |
337 pkt->duration = ret * 8; | |
338 } | |
339 pkt->pts = index_entry->timestamp; | |
340 pkt->stream_index = rpl->chunk_part; | |
341 rpl->chunk_part++; | |
342 } | |
343 | |
344 // None of the Escape formats have keyframes, and the ADPCM | |
345 // format used doesn't have keyframes. | |
346 if (rpl->chunk_number == 0 && rpl->frame_in_part == 0) | |
347 pkt->flags |= PKT_FLAG_KEY; | |
348 | |
349 return ret; | |
350 } | |
351 | |
352 AVInputFormat rpl_demuxer = { | |
353 "rpl", | |
3424
7a0230981402
Make long_names in lavf/lavdev optional depending on CONFIG_SMALL.
diego
parents:
3286
diff
changeset
|
354 NULL_IF_CONFIG_SMALL("RPL/ARMovie format"), |
3197 | 355 sizeof(RPLContext), |
356 rpl_probe, | |
357 rpl_read_header, | |
358 rpl_read_packet, | |
359 }; |