Libav 0.7.1
|
00001 /* 00002 * ARMovie/RPL demuxer 00003 * Copyright (c) 2007 Christian Ohm, 2008 Eli Friedman 00004 * 00005 * This file is part of Libav. 00006 * 00007 * Libav is free software; you can redistribute it and/or 00008 * modify it under the terms of the GNU Lesser General Public 00009 * License as published by the Free Software Foundation; either 00010 * version 2.1 of the License, or (at your option) any later version. 00011 * 00012 * Libav is distributed in the hope that it will be useful, 00013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00015 * Lesser General Public License for more details. 00016 * 00017 * You should have received a copy of the GNU Lesser General Public 00018 * License along with Libav; if not, write to the Free Software 00019 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00020 */ 00021 00022 #include "libavutil/avstring.h" 00023 #include "libavutil/dict.h" 00024 #include "avformat.h" 00025 #include <stdlib.h> 00026 00027 #define RPL_SIGNATURE "ARMovie\x0A" 00028 #define RPL_SIGNATURE_SIZE 8 00029 00031 #define RPL_LINE_LENGTH 256 00032 00033 static int rpl_probe(AVProbeData *p) 00034 { 00035 if (memcmp(p->buf, RPL_SIGNATURE, RPL_SIGNATURE_SIZE)) 00036 return 0; 00037 00038 return AVPROBE_SCORE_MAX; 00039 } 00040 00041 typedef struct RPLContext { 00042 // RPL header data 00043 int32_t frames_per_chunk; 00044 00045 // Stream position data 00046 uint32_t chunk_number; 00047 uint32_t chunk_part; 00048 uint32_t frame_in_part; 00049 } RPLContext; 00050 00051 static int read_line(AVIOContext * pb, char* line, int bufsize) 00052 { 00053 int i; 00054 for (i = 0; i < bufsize - 1; i++) { 00055 int b = avio_r8(pb); 00056 if (b == 0) 00057 break; 00058 if (b == '\n') { 00059 line[i] = '\0'; 00060 return 0; 00061 } 00062 line[i] = b; 00063 } 00064 line[i] = '\0'; 00065 return -1; 00066 } 00067 00068 static int32_t read_int(const char* line, const char** endptr, int* error) 00069 { 00070 unsigned long result = 0; 00071 for (; *line>='0' && *line<='9'; line++) { 00072 if (result > (0x7FFFFFFF - 9) / 10) 00073 *error = -1; 00074 result = 10 * result + *line - '0'; 00075 } 00076 *endptr = line; 00077 return result; 00078 } 00079 00080 static int32_t read_line_and_int(AVIOContext * pb, int* error) 00081 { 00082 char line[RPL_LINE_LENGTH]; 00083 const char *endptr; 00084 *error |= read_line(pb, line, sizeof(line)); 00085 return read_int(line, &endptr, error); 00086 } 00087 00092 static AVRational read_fps(const char* line, int* error) 00093 { 00094 int64_t num, den = 1; 00095 AVRational result; 00096 num = read_int(line, &line, error); 00097 if (*line == '.') 00098 line++; 00099 for (; *line>='0' && *line<='9'; line++) { 00100 // Truncate any numerator too large to fit into an int64_t 00101 if (num > (INT64_MAX - 9) / 10 || den > INT64_MAX / 10) 00102 break; 00103 num = 10 * num + *line - '0'; 00104 den *= 10; 00105 } 00106 if (!num) 00107 *error = -1; 00108 av_reduce(&result.num, &result.den, num, den, 0x7FFFFFFF); 00109 return result; 00110 } 00111 00112 static int rpl_read_header(AVFormatContext *s, AVFormatParameters *ap) 00113 { 00114 AVIOContext *pb = s->pb; 00115 RPLContext *rpl = s->priv_data; 00116 AVStream *vst = NULL, *ast = NULL; 00117 int total_audio_size; 00118 int error = 0; 00119 00120 uint32_t i; 00121 00122 int32_t audio_format, chunk_catalog_offset, number_of_chunks; 00123 AVRational fps; 00124 00125 char line[RPL_LINE_LENGTH]; 00126 00127 // The header for RPL/ARMovie files is 21 lines of text 00128 // containing the various header fields. The fields are always 00129 // in the same order, and other text besides the first 00130 // number usually isn't important. 00131 // (The spec says that there exists some significance 00132 // for the text in a few cases; samples needed.) 00133 error |= read_line(pb, line, sizeof(line)); // ARMovie 00134 error |= read_line(pb, line, sizeof(line)); // movie name 00135 av_dict_set(&s->metadata, "title" , line, 0); 00136 error |= read_line(pb, line, sizeof(line)); // date/copyright 00137 av_dict_set(&s->metadata, "copyright", line, 0); 00138 error |= read_line(pb, line, sizeof(line)); // author and other 00139 av_dict_set(&s->metadata, "author" , line, 0); 00140 00141 // video headers 00142 vst = av_new_stream(s, 0); 00143 if (!vst) 00144 return AVERROR(ENOMEM); 00145 vst->codec->codec_type = AVMEDIA_TYPE_VIDEO; 00146 vst->codec->codec_tag = read_line_and_int(pb, &error); // video format 00147 vst->codec->width = read_line_and_int(pb, &error); // video width 00148 vst->codec->height = read_line_and_int(pb, &error); // video height 00149 vst->codec->bits_per_coded_sample = read_line_and_int(pb, &error); // video bits per sample 00150 error |= read_line(pb, line, sizeof(line)); // video frames per second 00151 fps = read_fps(line, &error); 00152 av_set_pts_info(vst, 32, fps.den, fps.num); 00153 00154 // Figure out the video codec 00155 switch (vst->codec->codec_tag) { 00156 #if 0 00157 case 122: 00158 vst->codec->codec_id = CODEC_ID_ESCAPE122; 00159 break; 00160 #endif 00161 case 124: 00162 vst->codec->codec_id = CODEC_ID_ESCAPE124; 00163 // The header is wrong here, at least sometimes 00164 vst->codec->bits_per_coded_sample = 16; 00165 break; 00166 #if 0 00167 case 130: 00168 vst->codec->codec_id = CODEC_ID_ESCAPE130; 00169 break; 00170 #endif 00171 default: 00172 av_log(s, AV_LOG_WARNING, 00173 "RPL video format %i not supported yet!\n", 00174 vst->codec->codec_tag); 00175 vst->codec->codec_id = CODEC_ID_NONE; 00176 } 00177 00178 // Audio headers 00179 00180 // ARMovie supports multiple audio tracks; I don't have any 00181 // samples, though. This code will ignore additional tracks. 00182 audio_format = read_line_and_int(pb, &error); // audio format ID 00183 if (audio_format) { 00184 ast = av_new_stream(s, 0); 00185 if (!ast) 00186 return AVERROR(ENOMEM); 00187 ast->codec->codec_type = AVMEDIA_TYPE_AUDIO; 00188 ast->codec->codec_tag = audio_format; 00189 ast->codec->sample_rate = read_line_and_int(pb, &error); // audio bitrate 00190 ast->codec->channels = read_line_and_int(pb, &error); // number of audio channels 00191 ast->codec->bits_per_coded_sample = read_line_and_int(pb, &error); // audio bits per sample 00192 // At least one sample uses 0 for ADPCM, which is really 4 bits 00193 // per sample. 00194 if (ast->codec->bits_per_coded_sample == 0) 00195 ast->codec->bits_per_coded_sample = 4; 00196 00197 ast->codec->bit_rate = ast->codec->sample_rate * 00198 ast->codec->bits_per_coded_sample * 00199 ast->codec->channels; 00200 00201 ast->codec->codec_id = CODEC_ID_NONE; 00202 switch (audio_format) { 00203 case 1: 00204 if (ast->codec->bits_per_coded_sample == 16) { 00205 // 16-bit audio is always signed 00206 ast->codec->codec_id = CODEC_ID_PCM_S16LE; 00207 break; 00208 } 00209 // There are some other formats listed as legal per the spec; 00210 // samples needed. 00211 break; 00212 case 101: 00213 if (ast->codec->bits_per_coded_sample == 8) { 00214 // The samples with this kind of audio that I have 00215 // are all unsigned. 00216 ast->codec->codec_id = CODEC_ID_PCM_U8; 00217 break; 00218 } else if (ast->codec->bits_per_coded_sample == 4) { 00219 ast->codec->codec_id = CODEC_ID_ADPCM_IMA_EA_SEAD; 00220 break; 00221 } 00222 break; 00223 } 00224 if (ast->codec->codec_id == CODEC_ID_NONE) { 00225 av_log(s, AV_LOG_WARNING, 00226 "RPL audio format %i not supported yet!\n", 00227 audio_format); 00228 } 00229 av_set_pts_info(ast, 32, 1, ast->codec->bit_rate); 00230 } else { 00231 for (i = 0; i < 3; i++) 00232 error |= read_line(pb, line, sizeof(line)); 00233 } 00234 00235 rpl->frames_per_chunk = read_line_and_int(pb, &error); // video frames per chunk 00236 if (rpl->frames_per_chunk > 1 && vst->codec->codec_tag != 124) 00237 av_log(s, AV_LOG_WARNING, 00238 "Don't know how to split frames for video format %i. " 00239 "Video stream will be broken!\n", vst->codec->codec_tag); 00240 00241 number_of_chunks = read_line_and_int(pb, &error); // number of chunks in the file 00242 // The number in the header is actually the index of the last chunk. 00243 number_of_chunks++; 00244 00245 error |= read_line(pb, line, sizeof(line)); // "even" chunk size in bytes 00246 error |= read_line(pb, line, sizeof(line)); // "odd" chunk size in bytes 00247 chunk_catalog_offset = // offset of the "chunk catalog" 00248 read_line_and_int(pb, &error); // (file index) 00249 error |= read_line(pb, line, sizeof(line)); // offset to "helpful" sprite 00250 error |= read_line(pb, line, sizeof(line)); // size of "helpful" sprite 00251 error |= read_line(pb, line, sizeof(line)); // offset to key frame list 00252 00253 // Read the index 00254 avio_seek(pb, chunk_catalog_offset, SEEK_SET); 00255 total_audio_size = 0; 00256 for (i = 0; i < number_of_chunks; i++) { 00257 int64_t offset, video_size, audio_size; 00258 error |= read_line(pb, line, sizeof(line)); 00259 if (3 != sscanf(line, "%"PRId64" , %"PRId64" ; %"PRId64, 00260 &offset, &video_size, &audio_size)) 00261 error = -1; 00262 av_add_index_entry(vst, offset, i * rpl->frames_per_chunk, 00263 video_size, rpl->frames_per_chunk, 0); 00264 if (ast) 00265 av_add_index_entry(ast, offset + video_size, total_audio_size, 00266 audio_size, audio_size * 8, 0); 00267 total_audio_size += audio_size * 8; 00268 } 00269 00270 if (error) return AVERROR(EIO); 00271 00272 return 0; 00273 } 00274 00275 static int rpl_read_packet(AVFormatContext *s, AVPacket *pkt) 00276 { 00277 RPLContext *rpl = s->priv_data; 00278 AVIOContext *pb = s->pb; 00279 AVStream* stream; 00280 AVIndexEntry* index_entry; 00281 uint32_t ret; 00282 00283 if (rpl->chunk_part == s->nb_streams) { 00284 rpl->chunk_number++; 00285 rpl->chunk_part = 0; 00286 } 00287 00288 stream = s->streams[rpl->chunk_part]; 00289 00290 if (rpl->chunk_number >= stream->nb_index_entries) 00291 return -1; 00292 00293 index_entry = &stream->index_entries[rpl->chunk_number]; 00294 00295 if (rpl->frame_in_part == 0) 00296 if (avio_seek(pb, index_entry->pos, SEEK_SET) < 0) 00297 return AVERROR(EIO); 00298 00299 if (stream->codec->codec_type == AVMEDIA_TYPE_VIDEO && 00300 stream->codec->codec_tag == 124) { 00301 // We have to split Escape 124 frames because there are 00302 // multiple frames per chunk in Escape 124 samples. 00303 uint32_t frame_size; 00304 00305 avio_skip(pb, 4); /* flags */ 00306 frame_size = avio_rl32(pb); 00307 if (avio_seek(pb, -8, SEEK_CUR) < 0) 00308 return AVERROR(EIO); 00309 00310 ret = av_get_packet(pb, pkt, frame_size); 00311 if (ret != frame_size) { 00312 av_free_packet(pkt); 00313 return AVERROR(EIO); 00314 } 00315 pkt->duration = 1; 00316 pkt->pts = index_entry->timestamp + rpl->frame_in_part; 00317 pkt->stream_index = rpl->chunk_part; 00318 00319 rpl->frame_in_part++; 00320 if (rpl->frame_in_part == rpl->frames_per_chunk) { 00321 rpl->frame_in_part = 0; 00322 rpl->chunk_part++; 00323 } 00324 } else { 00325 ret = av_get_packet(pb, pkt, index_entry->size); 00326 if (ret != index_entry->size) { 00327 av_free_packet(pkt); 00328 return AVERROR(EIO); 00329 } 00330 00331 if (stream->codec->codec_type == AVMEDIA_TYPE_VIDEO) { 00332 // frames_per_chunk should always be one here; the header 00333 // parsing will warn if it isn't. 00334 pkt->duration = rpl->frames_per_chunk; 00335 } else { 00336 // All the audio codecs supported in this container 00337 // (at least so far) are constant-bitrate. 00338 pkt->duration = ret * 8; 00339 } 00340 pkt->pts = index_entry->timestamp; 00341 pkt->stream_index = rpl->chunk_part; 00342 rpl->chunk_part++; 00343 } 00344 00345 // None of the Escape formats have keyframes, and the ADPCM 00346 // format used doesn't have keyframes. 00347 if (rpl->chunk_number == 0 && rpl->frame_in_part == 0) 00348 pkt->flags |= AV_PKT_FLAG_KEY; 00349 00350 return ret; 00351 } 00352 00353 AVInputFormat ff_rpl_demuxer = { 00354 "rpl", 00355 NULL_IF_CONFIG_SMALL("RPL/ARMovie format"), 00356 sizeof(RPLContext), 00357 rpl_probe, 00358 rpl_read_header, 00359 rpl_read_packet, 00360 };