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 #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 (s->pb->eof_reached || 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             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){ /* skip palette entries */
00265                     sz += (t & 0x7F) + 1;
00266                     pal += ((t & 0x7F) + 1) * 3;
00267                 } else if(t & 0x40){ /* copy with offset */
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 { /* new entries */
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         /* if audio chunks are present, put them to stack and retrieve later */
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 };