00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
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
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
00056 uint32_t *frm_size;
00057 uint8_t *frm_flags;
00058
00059 int cur_frame;
00060 int is_ver4;
00061 int64_t cur_pts;
00062
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
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
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){
00125 av_log(s, AV_LOG_ERROR, "treesize too large\n");
00126 return -1;
00127 }
00128
00129
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
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
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
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
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
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
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 (s->pb->eof_reached || smk->cur_frame >= smk->frames)
00242 return AVERROR_EOF;
00243
00244
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
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 frame_size -= size;
00259 frame_size--;
00260 sz = 0;
00261 pos = avio_tell(s->pb) + size;
00262 while(sz < 256){
00263 t = avio_r8(s->pb);
00264 if(t & 0x80){
00265 sz += (t & 0x7F) + 1;
00266 pal += ((t & 0x7F) + 1) * 3;
00267 } else if(t & 0x40){
00268 off = avio_r8(s->pb);
00269 j = (t & 0x3F) + 1;
00270 if (off + j > 0xff) {
00271 av_log(s, AV_LOG_ERROR,
00272 "Invalid palette update, offset=%d length=%d extends beyond palette size\n",
00273 off, j);
00274 return AVERROR_INVALIDDATA;
00275 }
00276 off *= 3;
00277 while(j-- && sz < 256) {
00278 *pal++ = oldpal[off + 0];
00279 *pal++ = oldpal[off + 1];
00280 *pal++ = oldpal[off + 2];
00281 sz++;
00282 off += 3;
00283 }
00284 } else {
00285 *pal++ = smk_pal[t];
00286 *pal++ = smk_pal[avio_r8(s->pb) & 0x3F];
00287 *pal++ = smk_pal[avio_r8(s->pb) & 0x3F];
00288 sz++;
00289 }
00290 }
00291 avio_seek(s->pb, pos, 0);
00292 palchange |= 1;
00293 }
00294 flags >>= 1;
00295 smk->curstream = -1;
00296
00297 for(i = 0; i < 7; i++) {
00298 if(flags & 1) {
00299 int size;
00300 uint8_t *tmpbuf;
00301
00302 size = avio_rl32(s->pb) - 4;
00303 frame_size -= size;
00304 frame_size -= 4;
00305 smk->curstream++;
00306 tmpbuf = av_realloc(smk->bufs[smk->curstream], size);
00307 if (!tmpbuf)
00308 return AVERROR(ENOMEM);
00309 smk->bufs[smk->curstream] = tmpbuf;
00310 smk->buf_sizes[smk->curstream] = size;
00311 ret = avio_read(s->pb, smk->bufs[smk->curstream], size);
00312 if(ret != size)
00313 return AVERROR(EIO);
00314 smk->stream_id[smk->curstream] = smk->indexes[i];
00315 }
00316 flags >>= 1;
00317 }
00318 if (frame_size < 0)
00319 return AVERROR_INVALIDDATA;
00320 if (av_new_packet(pkt, frame_size + 769))
00321 return AVERROR(ENOMEM);
00322 if(smk->frm_size[smk->cur_frame] & 1)
00323 palchange |= 2;
00324 pkt->data[0] = palchange;
00325 memcpy(pkt->data + 1, smk->pal, 768);
00326 ret = avio_read(s->pb, pkt->data + 769, frame_size);
00327 if(ret != frame_size)
00328 return AVERROR(EIO);
00329 pkt->stream_index = smk->videoindex;
00330 pkt->size = ret + 769;
00331 smk->cur_frame++;
00332 smk->nextpos = avio_tell(s->pb);
00333 } else {
00334 if (av_new_packet(pkt, smk->buf_sizes[smk->curstream]))
00335 return AVERROR(ENOMEM);
00336 memcpy(pkt->data, smk->bufs[smk->curstream], smk->buf_sizes[smk->curstream]);
00337 pkt->size = smk->buf_sizes[smk->curstream];
00338 pkt->stream_index = smk->stream_id[smk->curstream];
00339 pkt->pts = smk->aud_pts[smk->curstream];
00340 smk->aud_pts[smk->curstream] += AV_RL32(pkt->data);
00341 smk->curstream--;
00342 }
00343
00344 return 0;
00345 }
00346
00347 static int smacker_read_close(AVFormatContext *s)
00348 {
00349 SmackerContext *smk = s->priv_data;
00350 int i;
00351
00352 for(i = 0; i < 7; i++)
00353 av_free(smk->bufs[i]);
00354 av_free(smk->frm_size);
00355 av_free(smk->frm_flags);
00356
00357 return 0;
00358 }
00359
00360 AVInputFormat ff_smacker_demuxer = {
00361 .name = "smk",
00362 .long_name = NULL_IF_CONFIG_SMALL("Smacker video"),
00363 .priv_data_size = sizeof(SmackerContext),
00364 .read_probe = smacker_probe,
00365 .read_header = smacker_read_header,
00366 .read_packet = smacker_read_packet,
00367 .read_close = smacker_read_close,
00368 };