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