/src/ffmpeg/libavformat/rpl.c
Line | Count | Source |
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 | | |
22 | | #include <inttypes.h> |
23 | | #include <stdlib.h> |
24 | | |
25 | | #include "libavutil/avstring.h" |
26 | | #include "libavutil/dict.h" |
27 | | #include "avformat.h" |
28 | | #include "demux.h" |
29 | | #include "internal.h" |
30 | | |
31 | 958k | #define RPL_SIGNATURE "ARMovie\x0A" |
32 | 958k | #define RPL_SIGNATURE_SIZE 8 |
33 | | |
34 | | /** 256 is arbitrary, but should be big enough for any reasonable file. */ |
35 | 10.0k | #define RPL_LINE_LENGTH 256 |
36 | | |
37 | | static int rpl_probe(const AVProbeData *p) |
38 | 958k | { |
39 | 958k | if (memcmp(p->buf, RPL_SIGNATURE, RPL_SIGNATURE_SIZE)) |
40 | 957k | return 0; |
41 | | |
42 | 728 | return AVPROBE_SCORE_MAX; |
43 | 958k | } |
44 | | |
45 | | typedef struct RPLContext { |
46 | | // RPL header data |
47 | | int32_t frames_per_chunk; |
48 | | |
49 | | // Stream position data |
50 | | uint32_t chunk_number; |
51 | | uint32_t chunk_part; |
52 | | uint32_t frame_in_part; |
53 | | } RPLContext; |
54 | | |
55 | | static int read_line(AVIOContext * pb, char* line, int bufsize) |
56 | 240k | { |
57 | 240k | int i; |
58 | 1.94M | for (i = 0; i < bufsize - 1; i++) { |
59 | 1.94M | int b = avio_r8(pb); |
60 | 1.94M | if (b == 0) |
61 | 43.9k | break; |
62 | 1.90M | if (b == '\n') { |
63 | 196k | line[i] = '\0'; |
64 | 196k | return avio_feof(pb) ? -1 : 0; |
65 | 196k | } |
66 | 1.70M | line[i] = b; |
67 | 1.70M | } |
68 | 44.2k | line[i] = '\0'; |
69 | 44.2k | return -1; |
70 | 240k | } |
71 | | |
72 | | static int32_t read_int(const char* line, const char** endptr, int* error) |
73 | 62.0k | { |
74 | 62.0k | unsigned long result = 0; |
75 | 155k | for (; *line>='0' && *line<='9'; line++) { |
76 | 93.0k | if (result > (0x7FFFFFFF - 9) / 10) |
77 | 18.8k | *error = -1; |
78 | 93.0k | result = 10 * result + *line - '0'; |
79 | 93.0k | } |
80 | 62.0k | *endptr = line; |
81 | 62.0k | return result; |
82 | 62.0k | } |
83 | | |
84 | | static int32_t read_line_and_int(AVIOContext * pb, int* error) |
85 | 44.8k | { |
86 | 44.8k | char line[RPL_LINE_LENGTH]; |
87 | 44.8k | const char *endptr; |
88 | 44.8k | *error |= read_line(pb, line, sizeof(line)); |
89 | 44.8k | return read_int(line, &endptr, error); |
90 | 44.8k | } |
91 | | |
92 | | /** Parsing for fps, which can be a fraction. Unfortunately, |
93 | | * the spec for the header leaves out a lot of details, |
94 | | * so this is mostly guessing. |
95 | | */ |
96 | | static AVRational read_fps(const char* line, int* error) |
97 | 7.29k | { |
98 | 7.29k | int64_t num, den = 1; |
99 | 7.29k | AVRational result; |
100 | 7.29k | num = read_int(line, &line, error); |
101 | 7.29k | if (*line == '.') |
102 | 1.26k | line++; |
103 | 17.2k | for (; *line>='0' && *line<='9'; line++) { |
104 | | // Truncate any numerator too large to fit into an int64_t |
105 | 9.95k | if (num > (INT64_MAX - 9) / 10ULL || den > INT64_MAX / 10ULL) |
106 | 12 | break; |
107 | 9.94k | num = 10 * num + (*line - '0'); |
108 | 9.94k | den *= 10; |
109 | 9.94k | } |
110 | 7.29k | if (!num) |
111 | 2.25k | *error = -1; |
112 | 7.29k | av_reduce(&result.num, &result.den, num, den, 0x7FFFFFFF); |
113 | 7.29k | return result; |
114 | 7.29k | } |
115 | | |
116 | | static int rpl_read_header(AVFormatContext *s) |
117 | 7.29k | { |
118 | 7.29k | AVIOContext *pb = s->pb; |
119 | 7.29k | RPLContext *rpl = s->priv_data; |
120 | 7.29k | AVStream *vst = NULL, *ast = NULL; |
121 | 7.29k | int64_t total_audio_size; |
122 | 7.29k | int error = 0; |
123 | 7.29k | const char *endptr; |
124 | 7.29k | char audio_type[RPL_LINE_LENGTH]; |
125 | 7.29k | char audio_codec[RPL_LINE_LENGTH]; |
126 | | |
127 | 7.29k | uint32_t i; |
128 | | |
129 | 7.29k | int32_t video_format, audio_format, chunk_catalog_offset, number_of_chunks; |
130 | 7.29k | AVRational fps; |
131 | | |
132 | 7.29k | char line[RPL_LINE_LENGTH]; |
133 | | |
134 | | // The header for RPL/ARMovie files is 21 lines of text |
135 | | // containing the various header fields. The fields are always |
136 | | // in the same order, and other text besides the first |
137 | | // number usually isn't important. |
138 | | // (The spec says that there exists some significance |
139 | | // for the text in a few cases; samples needed.) |
140 | 7.29k | error |= read_line(pb, line, sizeof(line)); // ARMovie |
141 | 7.29k | error |= read_line(pb, line, sizeof(line)); // movie name |
142 | 7.29k | av_dict_set(&s->metadata, "title" , line, 0); |
143 | 7.29k | error |= read_line(pb, line, sizeof(line)); // date/copyright |
144 | 7.29k | av_dict_set(&s->metadata, "copyright", line, 0); |
145 | 7.29k | error |= read_line(pb, line, sizeof(line)); // author and other |
146 | 7.29k | av_dict_set(&s->metadata, "author" , line, 0); |
147 | | |
148 | | // video headers |
149 | 7.29k | video_format = read_line_and_int(pb, &error); |
150 | 7.29k | if (video_format) { |
151 | 4.46k | vst = avformat_new_stream(s, NULL); |
152 | 4.46k | if (!vst) |
153 | 0 | return AVERROR(ENOMEM); |
154 | 4.46k | vst->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; |
155 | 4.46k | vst->codecpar->codec_tag = video_format; |
156 | 4.46k | vst->codecpar->width = read_line_and_int(pb, &error); // video width |
157 | 4.46k | vst->codecpar->height = read_line_and_int(pb, &error); // video height |
158 | 4.46k | vst->codecpar->bits_per_coded_sample = read_line_and_int(pb, &error); // video bits per sample |
159 | | |
160 | | // Figure out the video codec |
161 | 4.46k | switch (vst->codecpar->codec_tag) { |
162 | | #if 0 |
163 | | case 122: |
164 | | vst->codecpar->codec_id = AV_CODEC_ID_ESCAPE122; |
165 | | break; |
166 | | #endif |
167 | 692 | case 124: |
168 | 692 | vst->codecpar->codec_id = AV_CODEC_ID_ESCAPE124; |
169 | | // The header is wrong here, at least sometimes |
170 | 692 | vst->codecpar->bits_per_coded_sample = 16; |
171 | 692 | break; |
172 | 152 | case 130: |
173 | 152 | vst->codecpar->codec_id = AV_CODEC_ID_ESCAPE130; |
174 | 152 | break; |
175 | 3.61k | default: |
176 | 3.61k | avpriv_report_missing_feature(s, "Video format %s", |
177 | 3.61k | av_fourcc2str(vst->codecpar->codec_tag)); |
178 | 3.61k | vst->codecpar->codec_id = AV_CODEC_ID_NONE; |
179 | 4.46k | } |
180 | 4.46k | } else { |
181 | 11.3k | for (i = 0; i < 3; i++) |
182 | 8.49k | error |= read_line(pb, line, sizeof(line)); |
183 | 2.83k | } |
184 | | |
185 | 7.29k | error |= read_line(pb, line, sizeof(line)); // video frames per second |
186 | 7.29k | fps = read_fps(line, &error); |
187 | 7.29k | if (vst) |
188 | 4.46k | avpriv_set_pts_info(vst, 32, fps.den, fps.num); |
189 | | |
190 | | // Audio headers |
191 | | |
192 | | // ARMovie supports multiple audio tracks; I don't have any |
193 | | // samples, though. This code will ignore additional tracks. |
194 | 7.29k | error |= read_line(pb, line, sizeof(line)); |
195 | 7.29k | audio_format = read_int(line, &endptr, &error); // audio format ID |
196 | 7.29k | av_strlcpy(audio_codec, endptr, RPL_LINE_LENGTH); |
197 | 7.29k | if (audio_format) { |
198 | 3.14k | int channels; |
199 | 3.14k | ast = avformat_new_stream(s, NULL); |
200 | 3.14k | if (!ast) |
201 | 0 | return AVERROR(ENOMEM); |
202 | 3.14k | ast->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; |
203 | 3.14k | ast->codecpar->codec_tag = audio_format; |
204 | 3.14k | ast->codecpar->sample_rate = read_line_and_int(pb, &error); // audio bitrate |
205 | 3.14k | if (ast->codecpar->sample_rate < 0) |
206 | 69 | return AVERROR_INVALIDDATA; |
207 | 3.07k | channels = read_line_and_int(pb, &error); // number of audio channels |
208 | 3.07k | if (channels <= 0) |
209 | 360 | return AVERROR_INVALIDDATA; |
210 | 2.71k | error |= read_line(pb, line, sizeof(line)); |
211 | 2.71k | ast->codecpar->bits_per_coded_sample = read_int(line, &endptr, &error); // audio bits per sample |
212 | 2.71k | av_strlcpy(audio_type, endptr, RPL_LINE_LENGTH); |
213 | 2.71k | ast->codecpar->ch_layout.nb_channels = channels; |
214 | | // At least one sample uses 0 for ADPCM, which is really 4 bits |
215 | | // per sample. |
216 | 2.71k | if (ast->codecpar->bits_per_coded_sample == 0) |
217 | 1.38k | ast->codecpar->bits_per_coded_sample = 4; |
218 | | |
219 | 2.71k | ast->codecpar->bit_rate = ast->codecpar->sample_rate * |
220 | 2.71k | (int64_t)ast->codecpar->ch_layout.nb_channels; |
221 | 2.71k | if (ast->codecpar->bit_rate > INT64_MAX / ast->codecpar->bits_per_coded_sample) |
222 | 92 | return AVERROR_INVALIDDATA; |
223 | 2.61k | ast->codecpar->bit_rate *= ast->codecpar->bits_per_coded_sample; |
224 | | |
225 | 2.61k | ast->codecpar->codec_id = AV_CODEC_ID_NONE; |
226 | 2.61k | switch (audio_format) { |
227 | 946 | case 1: |
228 | 946 | if (ast->codecpar->bits_per_coded_sample == 16) { |
229 | | // 16-bit audio is always signed |
230 | 10 | ast->codecpar->codec_id = AV_CODEC_ID_PCM_S16LE; |
231 | 936 | } else if (ast->codecpar->bits_per_coded_sample == 8) { |
232 | 353 | if (av_stristr(audio_type, "unsigned") != NULL) |
233 | 0 | ast->codecpar->codec_id = AV_CODEC_ID_PCM_U8; |
234 | 353 | else if (av_stristr(audio_type, "linear") != NULL) |
235 | 6 | ast->codecpar->codec_id = AV_CODEC_ID_PCM_S8; |
236 | 347 | else |
237 | 347 | ast->codecpar->codec_id = AV_CODEC_ID_PCM_VIDC; |
238 | 353 | } |
239 | | // There are some other formats listed as legal per the spec; |
240 | | // samples needed. |
241 | 946 | break; |
242 | 367 | case 2: |
243 | 367 | if (av_stristr(audio_codec, "adpcm") != NULL) { |
244 | 0 | ast->codecpar->codec_id = AV_CODEC_ID_ADPCM_IMA_ACORN; |
245 | 0 | } |
246 | 367 | break; |
247 | 82 | case 101: |
248 | 82 | if (ast->codecpar->bits_per_coded_sample == 8) { |
249 | | // The samples with this kind of audio that I have |
250 | | // are all unsigned. |
251 | 12 | ast->codecpar->codec_id = AV_CODEC_ID_PCM_U8; |
252 | 70 | } else if (ast->codecpar->bits_per_coded_sample == 4) { |
253 | 28 | ast->codecpar->codec_id = AV_CODEC_ID_ADPCM_IMA_ESCAPE; |
254 | 28 | } |
255 | 82 | break; |
256 | 2.61k | } |
257 | 2.61k | if (ast->codecpar->codec_id == AV_CODEC_ID_NONE) |
258 | 2.21k | avpriv_request_sample(s, "Audio format %"PRId32" (%s)", |
259 | 2.21k | audio_format, audio_codec); |
260 | 2.61k | avpriv_set_pts_info(ast, 32, 1, ast->codecpar->bit_rate); |
261 | 4.15k | } else { |
262 | 16.6k | for (i = 0; i < 3; i++) |
263 | 12.4k | error |= read_line(pb, line, sizeof(line)); |
264 | 4.15k | } |
265 | | |
266 | 6.77k | if (s->nb_streams == 0) |
267 | 800 | return AVERROR_INVALIDDATA; |
268 | | |
269 | 5.97k | rpl->frames_per_chunk = read_line_and_int(pb, &error); // video frames per chunk |
270 | 5.97k | if (vst && rpl->frames_per_chunk > 1 && vst->codecpar->codec_tag != 124) |
271 | 1.78k | av_log(s, AV_LOG_WARNING, |
272 | 1.78k | "Don't know how to split frames for video format %s. " |
273 | 1.78k | "Video stream will be broken!\n", av_fourcc2str(vst->codecpar->codec_tag)); |
274 | | |
275 | 5.97k | number_of_chunks = read_line_and_int(pb, &error); // number of chunks in the file |
276 | 5.97k | if (number_of_chunks == INT_MAX) |
277 | 2 | return AVERROR_INVALIDDATA; |
278 | | |
279 | | // The number in the header is actually the index of the last chunk. |
280 | 5.96k | number_of_chunks++; |
281 | | |
282 | 5.96k | error |= read_line(pb, line, sizeof(line)); // "even" chunk size in bytes |
283 | 5.96k | error |= read_line(pb, line, sizeof(line)); // "odd" chunk size in bytes |
284 | 5.96k | chunk_catalog_offset = // offset of the "chunk catalog" |
285 | 5.96k | read_line_and_int(pb, &error); // (file index) |
286 | 5.96k | error |= read_line(pb, line, sizeof(line)); // offset to "helpful" sprite |
287 | 5.96k | error |= read_line(pb, line, sizeof(line)); // size of "helpful" sprite |
288 | 5.96k | if (vst) { |
289 | 4.42k | error |= read_line(pb, line, sizeof(line)); // offset to key frame list |
290 | 4.42k | vst->duration = number_of_chunks * (int64_t)rpl->frames_per_chunk; |
291 | 4.42k | } |
292 | | |
293 | | // Read the index |
294 | 5.96k | avio_seek(pb, chunk_catalog_offset, SEEK_SET); |
295 | 5.96k | total_audio_size = 0; |
296 | 105k | for (i = 0; !error && i < number_of_chunks; i++) { |
297 | 99.8k | int64_t offset, video_size, audio_size; |
298 | 99.8k | error |= read_line(pb, line, sizeof(line)); |
299 | 99.8k | if (3 != sscanf(line, "%"SCNd64" , %"SCNd64" ; %"SCNd64, |
300 | 99.8k | &offset, &video_size, &audio_size)) { |
301 | 241 | error = -1; |
302 | 241 | continue; |
303 | 241 | } |
304 | 99.5k | if (vst) |
305 | 38.5k | av_add_index_entry(vst, offset, i * rpl->frames_per_chunk, |
306 | 38.5k | video_size, rpl->frames_per_chunk, 0); |
307 | 99.5k | if (ast) |
308 | 90.0k | av_add_index_entry(ast, offset + video_size, total_audio_size, |
309 | 90.0k | audio_size, audio_size * 8, 0); |
310 | 99.5k | if (total_audio_size/8 + (uint64_t)audio_size >= INT64_MAX/8) |
311 | 9 | return AVERROR_INVALIDDATA; |
312 | 99.5k | total_audio_size += audio_size * 8; |
313 | 99.5k | } |
314 | | |
315 | 5.96k | if (error) |
316 | 1.93k | return AVERROR_INVALIDDATA; |
317 | | |
318 | 4.02k | return 0; |
319 | 5.96k | } |
320 | | |
321 | | static int rpl_read_packet(AVFormatContext *s, AVPacket *pkt) |
322 | 78.3k | { |
323 | 78.3k | RPLContext *rpl = s->priv_data; |
324 | 78.3k | AVIOContext *pb = s->pb; |
325 | 78.3k | AVStream* stream; |
326 | 78.3k | FFStream *sti; |
327 | 78.3k | AVIndexEntry* index_entry; |
328 | 78.3k | int ret; |
329 | | |
330 | 78.3k | if (rpl->chunk_part == s->nb_streams) { |
331 | 41.7k | rpl->chunk_number++; |
332 | 41.7k | rpl->chunk_part = 0; |
333 | 41.7k | } |
334 | | |
335 | 78.3k | stream = s->streams[rpl->chunk_part]; |
336 | 78.3k | sti = ffstream(stream); |
337 | | |
338 | 78.3k | if (rpl->chunk_number >= sti->nb_index_entries) |
339 | 4.62k | return AVERROR_EOF; |
340 | | |
341 | 73.7k | index_entry = &sti->index_entries[rpl->chunk_number]; |
342 | | |
343 | 73.7k | if (rpl->frame_in_part == 0) { |
344 | 54.6k | int64_t ret64 = avio_seek(pb, index_entry->pos, SEEK_SET); |
345 | 54.6k | if (ret64 < 0) |
346 | 801 | return (int)ret64; |
347 | 54.6k | } |
348 | | |
349 | 72.9k | if (stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && |
350 | 37.7k | stream->codecpar->codec_tag == 124) { |
351 | | // We have to split Escape 124 frames because there are |
352 | | // multiple frames per chunk in Escape 124 samples. |
353 | 20.0k | uint32_t frame_size; |
354 | 20.0k | int64_t ret64; |
355 | | |
356 | 20.0k | avio_skip(pb, 4); /* flags */ |
357 | 20.0k | frame_size = avio_rl32(pb); |
358 | 20.0k | if (avio_feof(pb) || !frame_size) |
359 | 284 | return AVERROR_INVALIDDATA; |
360 | 19.8k | if ((ret64 = avio_seek(pb, -8, SEEK_CUR)) < 0) |
361 | 55 | return (int)ret64; |
362 | | |
363 | 19.7k | ret = av_get_packet(pb, pkt, frame_size); |
364 | 19.7k | if (ret < 0) |
365 | 152 | return ret; |
366 | 19.6k | if (ret != frame_size) |
367 | 637 | return AVERROR_INVALIDDATA; |
368 | | |
369 | 18.9k | pkt->duration = 1; |
370 | 18.9k | pkt->pts = index_entry->timestamp + rpl->frame_in_part; |
371 | 18.9k | pkt->stream_index = rpl->chunk_part; |
372 | | |
373 | 18.9k | rpl->frame_in_part++; |
374 | 18.9k | if (rpl->frame_in_part == rpl->frames_per_chunk) { |
375 | 15 | rpl->frame_in_part = 0; |
376 | 15 | rpl->chunk_part++; |
377 | 15 | } |
378 | 52.8k | } else { |
379 | 52.8k | ret = av_get_packet(pb, pkt, index_entry->size); |
380 | 52.8k | if (ret < 0) |
381 | 346 | return ret; |
382 | 52.5k | if (ret != index_entry->size) |
383 | 586 | return AVERROR_INVALIDDATA; |
384 | | |
385 | 51.9k | if (stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { |
386 | | // frames_per_chunk should always be one here; the header |
387 | | // parsing will warn if it isn't. |
388 | 17.3k | pkt->duration = rpl->frames_per_chunk; |
389 | 34.6k | } else { |
390 | | // All the audio codecs supported in this container |
391 | | // (at least so far) are constant-bitrate. |
392 | 34.6k | pkt->duration = ret * 8; |
393 | 34.6k | } |
394 | 51.9k | pkt->pts = index_entry->timestamp; |
395 | 51.9k | pkt->stream_index = rpl->chunk_part; |
396 | 51.9k | rpl->chunk_part++; |
397 | 51.9k | } |
398 | | |
399 | | // None of the Escape formats have keyframes, and the ADPCM |
400 | | // format used doesn't have keyframes. |
401 | 70.8k | if (rpl->chunk_number == 0 && rpl->frame_in_part == 0) |
402 | 3.44k | pkt->flags |= AV_PKT_FLAG_KEY; |
403 | | |
404 | 70.8k | return ret; |
405 | 72.9k | } |
406 | | |
407 | | const FFInputFormat ff_rpl_demuxer = { |
408 | | .p.name = "rpl", |
409 | | .p.long_name = NULL_IF_CONFIG_SMALL("RPL / ARMovie"), |
410 | | .priv_data_size = sizeof(RPLContext), |
411 | | .read_probe = rpl_probe, |
412 | | .read_header = rpl_read_header, |
413 | | .read_packet = rpl_read_packet, |
414 | | }; |