• Main Page
  • Related Pages
  • Modules
  • Data Structures
  • Files
  • Examples
  • 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 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) * 3;
00269                     j = (t & 0x3F) + 1;
00270                     while(j-- && sz < 256) {
00271                         *pal++ = oldpal[off + 0];
00272                         *pal++ = oldpal[off + 1];
00273                         *pal++ = oldpal[off + 2];
00274                         sz++;
00275                         off += 3;
00276                     }
00277                 } else { /* new entries */
00278                     *pal++ = smk_pal[t];
00279                     *pal++ = smk_pal[avio_r8(s->pb) & 0x3F];
00280                     *pal++ = smk_pal[avio_r8(s->pb) & 0x3F];
00281                     sz++;
00282                 }
00283             }
00284             avio_seek(s->pb, pos, 0);
00285             palchange |= 1;
00286         }
00287         flags >>= 1;
00288         smk->curstream = -1;
00289         /* if audio chunks are present, put them to stack and retrieve later */
00290         for(i = 0; i < 7; i++) {
00291             if(flags & 1) {
00292                 int size;
00293                 uint8_t *tmpbuf;
00294 
00295                 size = avio_rl32(s->pb) - 4;
00296                 frame_size -= size;
00297                 frame_size -= 4;
00298                 smk->curstream++;
00299                 tmpbuf = av_realloc(smk->bufs[smk->curstream], size);
00300                 if (!tmpbuf)
00301                     return AVERROR(ENOMEM);
00302                 smk->bufs[smk->curstream] = tmpbuf;
00303                 smk->buf_sizes[smk->curstream] = size;
00304                 ret = avio_read(s->pb, smk->bufs[smk->curstream], size);
00305                 if(ret != size)
00306                     return AVERROR(EIO);
00307                 smk->stream_id[smk->curstream] = smk->indexes[i];
00308             }
00309             flags >>= 1;
00310         }
00311         if (frame_size < 0)
00312             return AVERROR_INVALIDDATA;
00313         if (av_new_packet(pkt, frame_size + 769))
00314             return AVERROR(ENOMEM);
00315         if(smk->frm_size[smk->cur_frame] & 1)
00316             palchange |= 2;
00317         pkt->data[0] = palchange;
00318         memcpy(pkt->data + 1, smk->pal, 768);
00319         ret = avio_read(s->pb, pkt->data + 769, frame_size);
00320         if(ret != frame_size)
00321             return AVERROR(EIO);
00322         pkt->stream_index = smk->videoindex;
00323         pkt->size = ret + 769;
00324         smk->cur_frame++;
00325         smk->nextpos = avio_tell(s->pb);
00326     } else {
00327         if (av_new_packet(pkt, smk->buf_sizes[smk->curstream]))
00328             return AVERROR(ENOMEM);
00329         memcpy(pkt->data, smk->bufs[smk->curstream], smk->buf_sizes[smk->curstream]);
00330         pkt->size = smk->buf_sizes[smk->curstream];
00331         pkt->stream_index = smk->stream_id[smk->curstream];
00332         pkt->pts = smk->aud_pts[smk->curstream];
00333         smk->aud_pts[smk->curstream] += AV_RL32(pkt->data);
00334         smk->curstream--;
00335     }
00336 
00337     return 0;
00338 }
00339 
00340 static int smacker_read_close(AVFormatContext *s)
00341 {
00342     SmackerContext *smk = s->priv_data;
00343     int i;
00344 
00345     for(i = 0; i < 7; i++)
00346         av_free(smk->bufs[i]);
00347     av_free(smk->frm_size);
00348     av_free(smk->frm_flags);
00349 
00350     return 0;
00351 }
00352 
00353 AVInputFormat ff_smacker_demuxer = {
00354     .name           = "smk",
00355     .long_name      = NULL_IF_CONFIG_SMALL("Smacker video"),
00356     .priv_data_size = sizeof(SmackerContext),
00357     .read_probe     = smacker_probe,
00358     .read_header    = smacker_read_header,
00359     .read_packet    = smacker_read_packet,
00360     .read_close     = smacker_read_close,
00361 };
Generated on Sat Mar 17 2012 12:57:49 for Libav by doxygen 1.7.1