Libav
|
00001 /* 00002 * FLI/FLC Animation File Demuxer 00003 * Copyright (c) 2003 The ffmpeg Project 00004 * 00005 * This file is part of FFmpeg. 00006 * 00007 * FFmpeg 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 * FFmpeg 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 FFmpeg; if not, write to the Free Software 00019 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00020 */ 00021 00034 #include "libavutil/intreadwrite.h" 00035 #include "avformat.h" 00036 00037 #define FLIC_FILE_MAGIC_1 0xAF11 00038 #define FLIC_FILE_MAGIC_2 0xAF12 00039 #define FLIC_FILE_MAGIC_3 0xAF44 /* Flic Type for Extended FLX Format which 00040 originated in Dave's Targa Animator (DTA) */ 00041 #define FLIC_CHUNK_MAGIC_1 0xF1FA 00042 #define FLIC_CHUNK_MAGIC_2 0xF5FA 00043 #define FLIC_MC_SPEED 5 /* speed for Magic Carpet game FLIs */ 00044 #define FLIC_DEFAULT_SPEED 5 /* for FLIs that have 0 speed */ 00045 #define FLIC_TFTD_CHUNK_AUDIO 0xAAAA /* Audio chunk. Used in Terror from the Deep. 00046 Has 10 B extra header not accounted for in the chunk header */ 00047 #define FLIC_TFTD_SAMPLE_RATE 22050 00048 00049 #define FLIC_HEADER_SIZE 128 00050 #define FLIC_PREAMBLE_SIZE 6 00051 00052 typedef struct FlicDemuxContext { 00053 int video_stream_index; 00054 int audio_stream_index; 00055 int frame_number; 00056 } FlicDemuxContext; 00057 00058 static int flic_probe(AVProbeData *p) 00059 { 00060 int magic_number; 00061 00062 if(p->buf_size < FLIC_HEADER_SIZE) 00063 return 0; 00064 00065 magic_number = AV_RL16(&p->buf[4]); 00066 if ((magic_number != FLIC_FILE_MAGIC_1) && 00067 (magic_number != FLIC_FILE_MAGIC_2) && 00068 (magic_number != FLIC_FILE_MAGIC_3)) 00069 return 0; 00070 00071 if(AV_RL16(&p->buf[0x10]) != FLIC_CHUNK_MAGIC_1){ 00072 if(AV_RL32(&p->buf[0x10]) > 2000) 00073 return 0; 00074 } 00075 00076 if( AV_RL16(&p->buf[0x08]) > 4096 00077 || AV_RL16(&p->buf[0x0A]) > 4096) 00078 return 0; 00079 00080 00081 return AVPROBE_SCORE_MAX; 00082 } 00083 00084 static int flic_read_header(AVFormatContext *s, 00085 AVFormatParameters *ap) 00086 { 00087 FlicDemuxContext *flic = s->priv_data; 00088 ByteIOContext *pb = s->pb; 00089 unsigned char header[FLIC_HEADER_SIZE]; 00090 AVStream *st, *ast; 00091 int speed; 00092 int magic_number; 00093 unsigned char preamble[FLIC_PREAMBLE_SIZE]; 00094 00095 flic->frame_number = 0; 00096 00097 /* load the whole header and pull out the width and height */ 00098 if (get_buffer(pb, header, FLIC_HEADER_SIZE) != FLIC_HEADER_SIZE) 00099 return AVERROR(EIO); 00100 00101 magic_number = AV_RL16(&header[4]); 00102 speed = AV_RL32(&header[0x10]); 00103 if (speed == 0) 00104 speed = FLIC_DEFAULT_SPEED; 00105 00106 /* initialize the decoder streams */ 00107 st = av_new_stream(s, 0); 00108 if (!st) 00109 return AVERROR(ENOMEM); 00110 flic->video_stream_index = st->index; 00111 st->codec->codec_type = AVMEDIA_TYPE_VIDEO; 00112 st->codec->codec_id = CODEC_ID_FLIC; 00113 st->codec->codec_tag = 0; /* no fourcc */ 00114 st->codec->width = AV_RL16(&header[0x08]); 00115 st->codec->height = AV_RL16(&header[0x0A]); 00116 00117 if (!st->codec->width || !st->codec->height) { 00118 /* Ugly hack needed for the following sample: */ 00119 /* http://samples.mplayerhq.hu/fli-flc/fli-bugs/specular.flc */ 00120 av_log(s, AV_LOG_WARNING, 00121 "File with no specified width/height. Trying 640x480.\n"); 00122 st->codec->width = 640; 00123 st->codec->height = 480; 00124 } 00125 00126 /* send over the whole 128-byte FLIC header */ 00127 st->codec->extradata_size = FLIC_HEADER_SIZE; 00128 st->codec->extradata = av_malloc(FLIC_HEADER_SIZE); 00129 memcpy(st->codec->extradata, header, FLIC_HEADER_SIZE); 00130 00131 /* peek at the preamble to detect TFTD videos - they seem to always start with an audio chunk */ 00132 if (get_buffer(pb, preamble, FLIC_PREAMBLE_SIZE) != FLIC_PREAMBLE_SIZE) { 00133 av_log(s, AV_LOG_ERROR, "Failed to peek at preamble\n"); 00134 return AVERROR(EIO); 00135 } 00136 00137 url_fseek(pb, -FLIC_PREAMBLE_SIZE, SEEK_CUR); 00138 00139 /* Time to figure out the framerate: 00140 * If the first preamble's magic number is 0xAAAA then this file is from 00141 * X-COM: Terror from the Deep. If on the other hand there is a FLIC chunk 00142 * magic number at offset 0x10 assume this file is from Magic Carpet instead. 00143 * If neither of the above is true then this is a normal FLIC file. 00144 */ 00145 if (AV_RL16(&preamble[4]) == FLIC_TFTD_CHUNK_AUDIO) { 00146 /* TFTD videos have an extra 22050 Hz 8-bit mono audio stream */ 00147 ast = av_new_stream(s, 1); 00148 if (!ast) 00149 return AVERROR(ENOMEM); 00150 00151 flic->audio_stream_index = ast->index; 00152 00153 /* all audio frames are the same size, so use the size of the first chunk for block_align */ 00154 ast->codec->block_align = AV_RL32(&preamble[0]); 00155 ast->codec->codec_type = CODEC_TYPE_AUDIO; 00156 ast->codec->codec_id = CODEC_ID_PCM_U8; 00157 ast->codec->codec_tag = 0; 00158 ast->codec->sample_rate = FLIC_TFTD_SAMPLE_RATE; 00159 ast->codec->channels = 1; 00160 ast->codec->sample_fmt = SAMPLE_FMT_U8; 00161 ast->codec->bit_rate = st->codec->sample_rate * 8; 00162 ast->codec->bits_per_coded_sample = 8; 00163 ast->codec->channel_layout = CH_LAYOUT_MONO; 00164 ast->codec->extradata_size = 0; 00165 00166 /* Since the header information is incorrect we have to figure out the 00167 * framerate using block_align and the fact that the audio is 22050 Hz. 00168 * We usually have two cases: 2205 -> 10 fps and 1470 -> 15 fps */ 00169 av_set_pts_info(st, 64, ast->codec->block_align, FLIC_TFTD_SAMPLE_RATE); 00170 av_set_pts_info(ast, 64, 1, FLIC_TFTD_SAMPLE_RATE); 00171 } else if (AV_RL16(&header[0x10]) == FLIC_CHUNK_MAGIC_1) { 00172 av_set_pts_info(st, 64, FLIC_MC_SPEED, 70); 00173 00174 /* rewind the stream since the first chunk is at offset 12 */ 00175 url_fseek(pb, 12, SEEK_SET); 00176 00177 /* send over abbreviated FLIC header chunk */ 00178 av_free(st->codec->extradata); 00179 st->codec->extradata_size = 12; 00180 st->codec->extradata = av_malloc(12); 00181 memcpy(st->codec->extradata, header, 12); 00182 00183 } else if (magic_number == FLIC_FILE_MAGIC_1) { 00184 av_set_pts_info(st, 64, speed, 70); 00185 } else if ((magic_number == FLIC_FILE_MAGIC_2) || 00186 (magic_number == FLIC_FILE_MAGIC_3)) { 00187 av_set_pts_info(st, 64, speed, 1000); 00188 } else { 00189 av_log(s, AV_LOG_INFO, "Invalid or unsupported magic chunk in file\n"); 00190 return AVERROR_INVALIDDATA; 00191 } 00192 00193 return 0; 00194 } 00195 00196 static int flic_read_packet(AVFormatContext *s, 00197 AVPacket *pkt) 00198 { 00199 FlicDemuxContext *flic = s->priv_data; 00200 ByteIOContext *pb = s->pb; 00201 int packet_read = 0; 00202 unsigned int size; 00203 int magic; 00204 int ret = 0; 00205 unsigned char preamble[FLIC_PREAMBLE_SIZE]; 00206 00207 while (!packet_read) { 00208 00209 if ((ret = get_buffer(pb, preamble, FLIC_PREAMBLE_SIZE)) != 00210 FLIC_PREAMBLE_SIZE) { 00211 ret = AVERROR(EIO); 00212 break; 00213 } 00214 00215 size = AV_RL32(&preamble[0]); 00216 magic = AV_RL16(&preamble[4]); 00217 00218 if (((magic == FLIC_CHUNK_MAGIC_1) || (magic == FLIC_CHUNK_MAGIC_2)) && size > FLIC_PREAMBLE_SIZE) { 00219 if (av_new_packet(pkt, size)) { 00220 ret = AVERROR(EIO); 00221 break; 00222 } 00223 pkt->stream_index = flic->video_stream_index; 00224 pkt->pts = flic->frame_number++; 00225 pkt->pos = url_ftell(pb); 00226 memcpy(pkt->data, preamble, FLIC_PREAMBLE_SIZE); 00227 ret = get_buffer(pb, pkt->data + FLIC_PREAMBLE_SIZE, 00228 size - FLIC_PREAMBLE_SIZE); 00229 if (ret != size - FLIC_PREAMBLE_SIZE) { 00230 av_free_packet(pkt); 00231 ret = AVERROR(EIO); 00232 } 00233 packet_read = 1; 00234 } else if (magic == FLIC_TFTD_CHUNK_AUDIO) { 00235 if (av_new_packet(pkt, size)) { 00236 ret = AVERROR(EIO); 00237 break; 00238 } 00239 00240 /* skip useless 10B sub-header (yes, it's not accounted for in the chunk header) */ 00241 url_fseek(pb, 10, SEEK_CUR); 00242 00243 pkt->stream_index = flic->audio_stream_index; 00244 pkt->pos = url_ftell(pb); 00245 ret = get_buffer(pb, pkt->data, size); 00246 00247 if (ret != size) { 00248 av_free_packet(pkt); 00249 ret = AVERROR(EIO); 00250 } 00251 00252 packet_read = 1; 00253 } else { 00254 /* not interested in this chunk */ 00255 url_fseek(pb, size - 6, SEEK_CUR); 00256 } 00257 } 00258 00259 return ret; 00260 } 00261 00262 AVInputFormat flic_demuxer = { 00263 "flic", 00264 NULL_IF_CONFIG_SMALL("FLI/FLC/FLX animation format"), 00265 sizeof(FlicDemuxContext), 00266 flic_probe, 00267 flic_read_header, 00268 flic_read_packet, 00269 };