• Main Page
  • Related Pages
  • Modules
  • Data Structures
  • Files
  • File List
  • Globals

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 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 
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     ByteIOContext *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 = get_le32(pb);
00109     if (smk->magic != MKTAG('S', 'M', 'K', '2') && smk->magic != MKTAG('S', 'M', 'K', '4'))
00110         return -1;
00111     smk->width = get_le32(pb);
00112     smk->height = get_le32(pb);
00113     smk->frames = get_le32(pb);
00114     smk->pts_inc = (int32_t)get_le32(pb);
00115     smk->flags = get_le32(pb);
00116     if(smk->flags & SMACKER_FLAG_RING_FRAME)
00117         smk->frames++;
00118     for(i = 0; i < 7; i++)
00119         smk->audio[i] = get_le32(pb);
00120     smk->treesize = get_le32(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 = get_le32(pb);
00129     smk->mclr_size = get_le32(pb);
00130     smk->full_size = get_le32(pb);
00131     smk->type_size = get_le32(pb);
00132     for(i = 0; i < 7; i++)
00133         smk->rates[i] = get_le32(pb);
00134     smk->pad = get_le32(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] = get_le32(pb);
00148     }
00149     for(i = 0; i < smk->frames; i++) {
00150         smk->frm_flags[i] = get_byte(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 = get_buffer(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] = le2me_32(smk->mmap_size);
00217     ((int32_t*)st->codec->extradata)[1] = le2me_32(smk->mclr_size);
00218     ((int32_t*)st->codec->extradata)[2] = le2me_32(smk->full_size);
00219     ((int32_t*)st->codec->extradata)[3] = le2me_32(smk->type_size);
00220 
00221     smk->curstream = -1;
00222     smk->nextpos = url_ftell(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     int pos;
00237 
00238     if (url_feof(s->pb) || smk->cur_frame >= smk->frames)
00239         return AVERROR(EIO);
00240 
00241     /* if we demuxed all streams, pass another frame */
00242     if(smk->curstream < 0) {
00243         url_fseek(s->pb, smk->nextpos, 0);
00244         frame_size = smk->frm_size[smk->cur_frame] & (~3);
00245         flags = smk->frm_flags[smk->cur_frame];
00246         /* handle palette change event */
00247         pos = url_ftell(s->pb);
00248         if(flags & SMACKER_PAL){
00249             int size, sz, t, off, j, pos;
00250             uint8_t *pal = smk->pal;
00251             uint8_t oldpal[768];
00252 
00253             memcpy(oldpal, pal, 768);
00254             size = get_byte(s->pb);
00255             size = size * 4 - 1;
00256             frame_size -= size;
00257             frame_size--;
00258             sz = 0;
00259             pos = url_ftell(s->pb) + size;
00260             while(sz < 256){
00261                 t = get_byte(s->pb);
00262                 if(t & 0x80){ /* skip palette entries */
00263                     sz += (t & 0x7F) + 1;
00264                     pal += ((t & 0x7F) + 1) * 3;
00265                 } else if(t & 0x40){ /* copy with offset */
00266                     off = get_byte(s->pb) * 3;
00267                     j = (t & 0x3F) + 1;
00268                     while(j-- && sz < 256) {
00269                         *pal++ = oldpal[off + 0];
00270                         *pal++ = oldpal[off + 1];
00271                         *pal++ = oldpal[off + 2];
00272                         sz++;
00273                         off += 3;
00274                     }
00275                 } else { /* new entries */
00276                     *pal++ = smk_pal[t];
00277                     *pal++ = smk_pal[get_byte(s->pb) & 0x3F];
00278                     *pal++ = smk_pal[get_byte(s->pb) & 0x3F];
00279                     sz++;
00280                 }
00281             }
00282             url_fseek(s->pb, pos, 0);
00283             palchange |= 1;
00284         }
00285         flags >>= 1;
00286         smk->curstream = -1;
00287         /* if audio chunks are present, put them to stack and retrieve later */
00288         for(i = 0; i < 7; i++) {
00289             if(flags & 1) {
00290                 int size;
00291                 size = get_le32(s->pb) - 4;
00292                 frame_size -= size;
00293                 frame_size -= 4;
00294                 smk->curstream++;
00295                 smk->bufs[smk->curstream] = av_realloc(smk->bufs[smk->curstream], size);
00296                 smk->buf_sizes[smk->curstream] = size;
00297                 ret = get_buffer(s->pb, smk->bufs[smk->curstream], size);
00298                 if(ret != size)
00299                     return AVERROR(EIO);
00300                 smk->stream_id[smk->curstream] = smk->indexes[i];
00301             }
00302             flags >>= 1;
00303         }
00304         if (av_new_packet(pkt, frame_size + 768))
00305             return AVERROR(ENOMEM);
00306         if(smk->frm_size[smk->cur_frame] & 1)
00307             palchange |= 2;
00308         pkt->data[0] = palchange;
00309         memcpy(pkt->data + 1, smk->pal, 768);
00310         ret = get_buffer(s->pb, pkt->data + 769, frame_size);
00311         if(ret != frame_size)
00312             return AVERROR(EIO);
00313         pkt->stream_index = smk->videoindex;
00314         pkt->size = ret + 769;
00315         smk->cur_frame++;
00316         smk->nextpos = url_ftell(s->pb);
00317     } else {
00318         if (av_new_packet(pkt, smk->buf_sizes[smk->curstream]))
00319             return AVERROR(ENOMEM);
00320         memcpy(pkt->data, smk->bufs[smk->curstream], smk->buf_sizes[smk->curstream]);
00321         pkt->size = smk->buf_sizes[smk->curstream];
00322         pkt->stream_index = smk->stream_id[smk->curstream];
00323         pkt->pts = smk->aud_pts[smk->curstream];
00324         smk->aud_pts[smk->curstream] += AV_RL32(pkt->data);
00325         smk->curstream--;
00326     }
00327 
00328     return 0;
00329 }
00330 
00331 static int smacker_read_close(AVFormatContext *s)
00332 {
00333     SmackerContext *smk = s->priv_data;
00334     int i;
00335 
00336     for(i = 0; i < 7; i++)
00337         if(smk->bufs[i])
00338             av_free(smk->bufs[i]);
00339     if(smk->frm_size)
00340         av_free(smk->frm_size);
00341     if(smk->frm_flags)
00342         av_free(smk->frm_flags);
00343 
00344     return 0;
00345 }
00346 
00347 AVInputFormat smacker_demuxer = {
00348     "smk",
00349     NULL_IF_CONFIG_SMALL("Smacker video"),
00350     sizeof(SmackerContext),
00351     smacker_probe,
00352     smacker_read_header,
00353     smacker_read_packet,
00354     smacker_read_close,
00355 };

Generated on Fri Sep 16 2011 17:17:43 for FFmpeg by  doxygen 1.7.1