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