00001 /* 00002 * Smacker demuxer 00003 * Copyright (c) 2006 Konstantin Shishkov 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 00022 /* 00023 * Based on http://wiki.multimedia.cx/index.php?title=Smacker 00024 */ 00025 00026 #include "libavutil/bswap.h" 00027 #include "libavutil/intreadwrite.h" 00028 #include "avformat.h" 00029 #include "internal.h" 00030 00031 #define SMACKER_PAL 0x01 00032 #define SMACKER_FLAG_RING_FRAME 0x01 00033 00034 enum SAudFlags { 00035 SMK_AUD_PACKED = 0x80, 00036 SMK_AUD_16BITS = 0x20, 00037 SMK_AUD_STEREO = 0x10, 00038 SMK_AUD_BINKAUD = 0x08, 00039 SMK_AUD_USEDCT = 0x04 00040 }; 00041 00042 typedef struct SmackerContext { 00043 /* Smacker file header */ 00044 uint32_t magic; 00045 uint32_t width, height; 00046 uint32_t frames; 00047 int pts_inc; 00048 uint32_t flags; 00049 uint32_t audio[7]; 00050 uint32_t treesize; 00051 uint32_t mmap_size, mclr_size, full_size, type_size; 00052 uint8_t aflags[7]; 00053 uint32_t rates[7]; 00054 uint32_t pad; 00055 /* frame info */ 00056 uint32_t *frm_size; 00057 uint8_t *frm_flags; 00058 /* internal variables */ 00059 int cur_frame; 00060 int is_ver4; 00061 int64_t cur_pts; 00062 /* current frame for demuxing */ 00063 uint8_t pal[768]; 00064 int indexes[7]; 00065 int videoindex; 00066 uint8_t *bufs[7]; 00067 int buf_sizes[7]; 00068 int stream_id[7]; 00069 int curstream; 00070 int64_t nextpos; 00071 int64_t aud_pts[7]; 00072 } SmackerContext; 00073 00074 typedef struct SmackerFrame { 00075 int64_t pts; 00076 int stream; 00077 } SmackerFrame; 00078 00079 /* palette used in Smacker */ 00080 static const uint8_t smk_pal[64] = { 00081 0x00, 0x04, 0x08, 0x0C, 0x10, 0x14, 0x18, 0x1C, 00082 0x20, 0x24, 0x28, 0x2C, 0x30, 0x34, 0x38, 0x3C, 00083 0x41, 0x45, 0x49, 0x4D, 0x51, 0x55, 0x59, 0x5D, 00084 0x61, 0x65, 0x69, 0x6D, 0x71, 0x75, 0x79, 0x7D, 00085 0x82, 0x86, 0x8A, 0x8E, 0x92, 0x96, 0x9A, 0x9E, 00086 0xA2, 0xA6, 0xAA, 0xAE, 0xB2, 0xB6, 0xBA, 0xBE, 00087 0xC3, 0xC7, 0xCB, 0xCF, 0xD3, 0xD7, 0xDB, 0xDF, 00088 0xE3, 0xE7, 0xEB, 0xEF, 0xF3, 0xF7, 0xFB, 0xFF 00089 }; 00090 00091 00092 static int smacker_probe(AVProbeData *p) 00093 { 00094 if(p->buf[0] == 'S' && p->buf[1] == 'M' && p->buf[2] == 'K' 00095 && (p->buf[3] == '2' || p->buf[3] == '4')) 00096 return AVPROBE_SCORE_MAX; 00097 else 00098 return 0; 00099 } 00100 00101 static int smacker_read_header(AVFormatContext *s, AVFormatParameters *ap) 00102 { 00103 AVIOContext *pb = s->pb; 00104 SmackerContext *smk = s->priv_data; 00105 AVStream *st, *ast[7]; 00106 int i, ret; 00107 int tbase; 00108 00109 /* read and check header */ 00110 smk->magic = avio_rl32(pb); 00111 if (smk->magic != MKTAG('S', 'M', 'K', '2') && smk->magic != MKTAG('S', 'M', 'K', '4')) 00112 return -1; 00113 smk->width = avio_rl32(pb); 00114 smk->height = avio_rl32(pb); 00115 smk->frames = avio_rl32(pb); 00116 smk->pts_inc = (int32_t)avio_rl32(pb); 00117 smk->flags = avio_rl32(pb); 00118 if(smk->flags & SMACKER_FLAG_RING_FRAME) 00119 smk->frames++; 00120 for(i = 0; i < 7; i++) 00121 smk->audio[i] = avio_rl32(pb); 00122 smk->treesize = avio_rl32(pb); 00123 00124 if(smk->treesize >= UINT_MAX/4){ // smk->treesize + 16 must not overflow (this check is probably redundant) 00125 av_log(s, AV_LOG_ERROR, "treesize too large\n"); 00126 return -1; 00127 } 00128 00129 //FIXME remove extradata "rebuilding" 00130 smk->mmap_size = avio_rl32(pb); 00131 smk->mclr_size = avio_rl32(pb); 00132 smk->full_size = avio_rl32(pb); 00133 smk->type_size = avio_rl32(pb); 00134 for(i = 0; i < 7; i++) { 00135 smk->rates[i] = avio_rl24(pb); 00136 smk->aflags[i] = avio_r8(pb); 00137 } 00138 smk->pad = avio_rl32(pb); 00139 /* setup data */ 00140 if(smk->frames > 0xFFFFFF) { 00141 av_log(s, AV_LOG_ERROR, "Too many frames: %i\n", smk->frames); 00142 return -1; 00143 } 00144 smk->frm_size = av_malloc(smk->frames * 4); 00145 smk->frm_flags = av_malloc(smk->frames); 00146 00147 smk->is_ver4 = (smk->magic != MKTAG('S', 'M', 'K', '2')); 00148 00149 /* read frame info */ 00150 for(i = 0; i < smk->frames; i++) { 00151 smk->frm_size[i] = avio_rl32(pb); 00152 } 00153 for(i = 0; i < smk->frames; i++) { 00154 smk->frm_flags[i] = avio_r8(pb); 00155 } 00156 00157 /* init video codec */ 00158 st = avformat_new_stream(s, NULL); 00159 if (!st) 00160 return -1; 00161 smk->videoindex = st->index; 00162 st->codec->width = smk->width; 00163 st->codec->height = smk->height; 00164 st->codec->pix_fmt = PIX_FMT_PAL8; 00165 st->codec->codec_type = AVMEDIA_TYPE_VIDEO; 00166 st->codec->codec_id = CODEC_ID_SMACKVIDEO; 00167 st->codec->codec_tag = smk->magic; 00168 /* Smacker uses 100000 as internal timebase */ 00169 if(smk->pts_inc < 0) 00170 smk->pts_inc = -smk->pts_inc; 00171 else 00172 smk->pts_inc *= 100; 00173 tbase = 100000; 00174 av_reduce(&tbase, &smk->pts_inc, tbase, smk->pts_inc, (1UL<<31)-1); 00175 avpriv_set_pts_info(st, 33, smk->pts_inc, tbase); 00176 st->duration = smk->frames; 00177 /* handle possible audio streams */ 00178 for(i = 0; i < 7; i++) { 00179 smk->indexes[i] = -1; 00180 if (smk->rates[i]) { 00181 ast[i] = avformat_new_stream(s, NULL); 00182 smk->indexes[i] = ast[i]->index; 00183 ast[i]->codec->codec_type = AVMEDIA_TYPE_AUDIO; 00184 if (smk->aflags[i] & SMK_AUD_BINKAUD) { 00185 ast[i]->codec->codec_id = CODEC_ID_BINKAUDIO_RDFT; 00186 } else if (smk->aflags[i] & SMK_AUD_USEDCT) { 00187 ast[i]->codec->codec_id = CODEC_ID_BINKAUDIO_DCT; 00188 } else if (smk->aflags[i] & SMK_AUD_PACKED){ 00189 ast[i]->codec->codec_id = CODEC_ID_SMACKAUDIO; 00190 ast[i]->codec->codec_tag = MKTAG('S', 'M', 'K', 'A'); 00191 } else { 00192 ast[i]->codec->codec_id = CODEC_ID_PCM_U8; 00193 } 00194 ast[i]->codec->channels = (smk->aflags[i] & SMK_AUD_STEREO) ? 2 : 1; 00195 ast[i]->codec->sample_rate = smk->rates[i]; 00196 ast[i]->codec->bits_per_coded_sample = (smk->aflags[i] & SMK_AUD_16BITS) ? 16 : 8; 00197 if(ast[i]->codec->bits_per_coded_sample == 16 && ast[i]->codec->codec_id == CODEC_ID_PCM_U8) 00198 ast[i]->codec->codec_id = CODEC_ID_PCM_S16LE; 00199 avpriv_set_pts_info(ast[i], 64, 1, ast[i]->codec->sample_rate 00200 * ast[i]->codec->channels * ast[i]->codec->bits_per_coded_sample / 8); 00201 } 00202 } 00203 00204 00205 /* load trees to extradata, they will be unpacked by decoder */ 00206 st->codec->extradata = av_malloc(smk->treesize + 16); 00207 st->codec->extradata_size = smk->treesize + 16; 00208 if(!st->codec->extradata){ 00209 av_log(s, AV_LOG_ERROR, "Cannot allocate %i bytes of extradata\n", smk->treesize + 16); 00210 av_free(smk->frm_size); 00211 av_free(smk->frm_flags); 00212 return -1; 00213 } 00214 ret = avio_read(pb, st->codec->extradata + 16, st->codec->extradata_size - 16); 00215 if(ret != st->codec->extradata_size - 16){ 00216 av_free(smk->frm_size); 00217 av_free(smk->frm_flags); 00218 return AVERROR(EIO); 00219 } 00220 ((int32_t*)st->codec->extradata)[0] = av_le2ne32(smk->mmap_size); 00221 ((int32_t*)st->codec->extradata)[1] = av_le2ne32(smk->mclr_size); 00222 ((int32_t*)st->codec->extradata)[2] = av_le2ne32(smk->full_size); 00223 ((int32_t*)st->codec->extradata)[3] = av_le2ne32(smk->type_size); 00224 00225 smk->curstream = -1; 00226 smk->nextpos = avio_tell(pb); 00227 00228 return 0; 00229 } 00230 00231 00232 static int smacker_read_packet(AVFormatContext *s, AVPacket *pkt) 00233 { 00234 SmackerContext *smk = s->priv_data; 00235 int flags; 00236 int ret; 00237 int i; 00238 int frame_size = 0; 00239 int palchange = 0; 00240 00241 if (url_feof(s->pb) || smk->cur_frame >= smk->frames) 00242 return AVERROR_EOF; 00243 00244 /* if we demuxed all streams, pass another frame */ 00245 if(smk->curstream < 0) { 00246 avio_seek(s->pb, smk->nextpos, 0); 00247 frame_size = smk->frm_size[smk->cur_frame] & (~3); 00248 flags = smk->frm_flags[smk->cur_frame]; 00249 /* handle palette change event */ 00250 if(flags & SMACKER_PAL){ 00251 int size, sz, t, off, j, pos; 00252 uint8_t *pal = smk->pal; 00253 uint8_t oldpal[768]; 00254 00255 memcpy(oldpal, pal, 768); 00256 size = avio_r8(s->pb); 00257 size = size * 4 - 1; 00258 if(size + 1 > frame_size) 00259 return AVERROR_INVALIDDATA; 00260 frame_size -= size; 00261 frame_size--; 00262 sz = 0; 00263 pos = avio_tell(s->pb) + size; 00264 while(sz < 256){ 00265 t = avio_r8(s->pb); 00266 if(t & 0x80){ /* skip palette entries */ 00267 sz += (t & 0x7F) + 1; 00268 pal += ((t & 0x7F) + 1) * 3; 00269 } else if(t & 0x40){ /* copy with offset */ 00270 off = avio_r8(s->pb); 00271 j = (t & 0x3F) + 1; 00272 if (off + j > 0xff) { 00273 av_log(s, AV_LOG_ERROR, 00274 "Invalid palette update, offset=%d length=%d extends beyond palette size\n", 00275 off, j); 00276 return AVERROR_INVALIDDATA; 00277 } 00278 off *= 3; 00279 while(j-- && sz < 256) { 00280 *pal++ = oldpal[off + 0]; 00281 *pal++ = oldpal[off + 1]; 00282 *pal++ = oldpal[off + 2]; 00283 sz++; 00284 off += 3; 00285 } 00286 } else { /* new entries */ 00287 *pal++ = smk_pal[t]; 00288 *pal++ = smk_pal[avio_r8(s->pb) & 0x3F]; 00289 *pal++ = smk_pal[avio_r8(s->pb) & 0x3F]; 00290 sz++; 00291 } 00292 } 00293 avio_seek(s->pb, pos, 0); 00294 palchange |= 1; 00295 } 00296 flags >>= 1; 00297 smk->curstream = -1; 00298 /* if audio chunks are present, put them to stack and retrieve later */ 00299 for(i = 0; i < 7; i++) { 00300 if(flags & 1) { 00301 unsigned int size; 00302 uint8_t *tmpbuf; 00303 00304 size = avio_rl32(s->pb) - 4; 00305 if(size + 4L > frame_size) 00306 return AVERROR_INVALIDDATA; 00307 frame_size -= size; 00308 frame_size -= 4; 00309 smk->curstream++; 00310 tmpbuf = av_realloc(smk->bufs[smk->curstream], size); 00311 if (!tmpbuf) 00312 return AVERROR(ENOMEM); 00313 smk->bufs[smk->curstream] = tmpbuf; 00314 smk->buf_sizes[smk->curstream] = size; 00315 ret = avio_read(s->pb, smk->bufs[smk->curstream], size); 00316 if(ret != size) 00317 return AVERROR(EIO); 00318 smk->stream_id[smk->curstream] = smk->indexes[i]; 00319 } 00320 flags >>= 1; 00321 } 00322 if (frame_size < 0) 00323 return AVERROR_INVALIDDATA; 00324 if (av_new_packet(pkt, frame_size + 769)) 00325 return AVERROR(ENOMEM); 00326 if(smk->frm_size[smk->cur_frame] & 1) 00327 palchange |= 2; 00328 pkt->data[0] = palchange; 00329 memcpy(pkt->data + 1, smk->pal, 768); 00330 ret = avio_read(s->pb, pkt->data + 769, frame_size); 00331 if(ret != frame_size) 00332 return AVERROR(EIO); 00333 pkt->stream_index = smk->videoindex; 00334 pkt->size = ret + 769; 00335 smk->cur_frame++; 00336 smk->nextpos = avio_tell(s->pb); 00337 } else { 00338 if (av_new_packet(pkt, smk->buf_sizes[smk->curstream])) 00339 return AVERROR(ENOMEM); 00340 memcpy(pkt->data, smk->bufs[smk->curstream], smk->buf_sizes[smk->curstream]); 00341 pkt->size = smk->buf_sizes[smk->curstream]; 00342 pkt->stream_index = smk->stream_id[smk->curstream]; 00343 pkt->pts = smk->aud_pts[smk->curstream]; 00344 smk->aud_pts[smk->curstream] += AV_RL32(pkt->data); 00345 smk->curstream--; 00346 } 00347 00348 return 0; 00349 } 00350 00351 static int smacker_read_close(AVFormatContext *s) 00352 { 00353 SmackerContext *smk = s->priv_data; 00354 int i; 00355 00356 for(i = 0; i < 7; i++) 00357 av_free(smk->bufs[i]); 00358 av_free(smk->frm_size); 00359 av_free(smk->frm_flags); 00360 00361 return 0; 00362 } 00363 00364 AVInputFormat ff_smacker_demuxer = { 00365 .name = "smk", 00366 .long_name = NULL_IF_CONFIG_SMALL("Smacker video"), 00367 .priv_data_size = sizeof(SmackerContext), 00368 .read_probe = smacker_probe, 00369 .read_header = smacker_read_header, 00370 .read_packet = smacker_read_packet, 00371 .read_close = smacker_read_close, 00372 };