Mercurial > libavformat.hg
changeset 1019:a9d8381ff40d libavformat
Smacker demuxer and decoder.
author | kostya |
---|---|
date | Tue, 21 Mar 2006 17:27:47 +0000 |
parents | bddcb32bde33 |
children | 32cfbb9cdbdd |
files | Makefile allformats.c avformat.h smacker.c |
diffstat | 4 files changed, 350 insertions(+), 3 deletions(-) [+] |
line wrap: on
line diff
--- a/Makefile Tue Mar 21 11:29:50 2006 +0000 +++ b/Makefile Tue Mar 21 17:27:47 2006 +0000 @@ -18,7 +18,7 @@ nut.o wc3movie.o mp3.o westwood.o segafilm.o idcin.o flic.o \ sierravmd.o matroska.o sol.o electronicarts.o nsvdec.o asf.o \ ogg2.o oggparsevorbis.o oggparsetheora.o oggparseflac.o daud.o aiff.o \ - voc.o tta.o mm.o avs.o + voc.o tta.o mm.o avs.o smacker.o # muxers ifeq ($(CONFIG_MUXERS),yes)
--- a/allformats.c Tue Mar 21 11:29:50 2006 +0000 +++ b/allformats.c Tue Mar 21 17:27:47 2006 +0000 @@ -80,6 +80,7 @@ flic_init(); vmd_init(); mm_init(); + smacker_init(); #if defined(AMR_NB) || defined(AMR_NB_FIXED) || defined(AMR_WB) amr_init();
--- a/avformat.h Tue Mar 21 11:29:50 2006 +0000 +++ b/avformat.h Tue Mar 21 17:27:47 2006 +0000 @@ -5,8 +5,8 @@ extern "C" { #endif -#define LIBAVFORMAT_VERSION_INT ((50<<16)+(3<<8)+0) -#define LIBAVFORMAT_VERSION 50.3.0 +#define LIBAVFORMAT_VERSION_INT ((50<<16)+(4<<8)+0) +#define LIBAVFORMAT_VERSION 50.4.0 #define LIBAVFORMAT_BUILD LIBAVFORMAT_VERSION_INT #define LIBAVFORMAT_IDENT "Lavf" AV_STRINGIFY(LIBAVFORMAT_VERSION) @@ -567,6 +567,9 @@ /* avs.c */ int avs_init(void); +/* smacker.c */ +int smacker_init(void); + #include "rtp.h" #include "rtsp.h"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/smacker.c Tue Mar 21 17:27:47 2006 +0000 @@ -0,0 +1,343 @@ +/* + * Smacker decoder + * Copyright (c) 2006 Konstantin Shishkov. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * Based on http://wiki.multimedia.cx/index.php?title=Smacker + */ + +#include "avformat.h" +#include "avi.h" +#include "bswap.h" + +#define SMACKER_PAL 0x01 + +enum SAudFlags { + SMK_AUD_PACKED = 0x80000000, + SMK_AUD_16BITS = 0x20000000, + SMK_AUD_STEREO = 0x10000000, + SMK_AUD_BINKAUD = 0x08000000, + SMK_AUD_USEDCT = 0x04000000 +}; + +typedef struct SmackerContext { + /* Smacker file header */ + uint32_t magic; + uint32_t width, height; + uint32_t frames; + int pts_inc; + uint32_t flags; + uint32_t audio[7]; + uint32_t treesize; + uint32_t mmap_size, mclr_size, full_size, type_size; + uint32_t rates[7]; + uint32_t pad; + /* frame info */ + uint32_t *frm_size; + uint8_t *frm_flags; + /* internal variables */ + int cur_frame; + int is_ver4; + int64_t cur_pts; + /* current frame for demuxing */ + uint8_t pal[768]; + int indexes[7]; + int videoindex; + uint8_t *bufs[7]; + int buf_sizes[7]; + int stream_id[7]; + int curstream; + offset_t nextpos; +} SmackerContext; + +typedef struct SmackerFrame { + int64_t pts; + int stream; +} SmackerFrame; + +/* palette used in Smacker */ +static const uint8_t smk_pal[64] = { + 0x00, 0x04, 0x08, 0x0C, 0x10, 0x14, 0x18, 0x1C, + 0x20, 0x24, 0x28, 0x2C, 0x30, 0x34, 0x38, 0x3C, + 0x41, 0x45, 0x49, 0x4D, 0x51, 0x55, 0x59, 0x5D, + 0x61, 0x65, 0x69, 0x6D, 0x71, 0x75, 0x79, 0x7D, + 0x82, 0x86, 0x8A, 0x8E, 0x92, 0x96, 0x9A, 0x9E, + 0xA2, 0xA6, 0xAA, 0xAE, 0xB2, 0xB6, 0xBA, 0xBE, + 0xC3, 0xC7, 0xCB, 0xCF, 0xD3, 0xD7, 0xDB, 0xDF, + 0xE3, 0xE7, 0xEB, 0xEF, 0xF3, 0xF7, 0xFB, 0xFF +}; + + +static int smacker_probe(AVProbeData *p) +{ + if (p->buf_size < 4) + return 0; + if(p->buf[0] == 'S' && p->buf[1] == 'M' && p->buf[2] == 'K' + && (p->buf[3] == '2' || p->buf[3] == '4')) + return AVPROBE_SCORE_MAX; + else + return 0; +} + +static int smacker_read_header(AVFormatContext *s, AVFormatParameters *ap) +{ + ByteIOContext *pb = &s->pb; + SmackerContext *smk = (SmackerContext *)s->priv_data; + AVStream *st, *ast[7]; + int i, ret; + int tbase; + + /* read and check header */ + smk->magic = get_le32(pb); + if (smk->magic != MKTAG('S', 'M', 'K', '2') && smk->magic != MKTAG('S', 'M', 'K', '4')) + return -1; + smk->width = get_le32(pb); + smk->height = get_le32(pb); + smk->frames = get_le32(pb); + smk->pts_inc = (int32_t)get_le32(pb); + smk->flags = get_le32(pb); + for(i = 0; i < 7; i++) + smk->audio[i] = get_le32(pb); + smk->treesize = get_le32(pb); + smk->mmap_size = get_le32(pb); + smk->mclr_size = get_le32(pb); + smk->full_size = get_le32(pb); + smk->type_size = get_le32(pb); + for(i = 0; i < 7; i++) + smk->rates[i] = get_le32(pb); + smk->pad = get_le32(pb); + /* setup data */ + if(smk->frames > 0xFFFFFF) { + av_log(s, AV_LOG_ERROR, "Too many frames: %i\n", smk->frames); + return -1; + } + smk->frm_size = av_malloc(smk->frames * 4); + smk->frm_flags = av_malloc(smk->frames); + + smk->is_ver4 = (smk->magic != MKTAG('S', 'M', 'K', '2')); + + /* read frame info */ + for(i = 0; i < smk->frames; i++) { + smk->frm_size[i] = get_le32(pb); + } + for(i = 0; i < smk->frames; i++) { + smk->frm_flags[i] = get_byte(pb); + } + + /* init video codec */ + st = av_new_stream(s, 0); + if (!st) + return -1; + smk->videoindex = st->index; + st->codec->width = smk->width; + st->codec->height = smk->height; + st->codec->pix_fmt = PIX_FMT_PAL8; + st->codec->codec_type = CODEC_TYPE_VIDEO; + st->codec->codec_id = CODEC_ID_SMACKVIDEO; + st->codec->codec_tag = smk->is_ver4; + /* Smacker uses 100000 as internal timebase */ + if(smk->pts_inc < 0) + smk->pts_inc = -smk->pts_inc; + else + smk->pts_inc *= 100; + tbase = 100000; + av_reduce(&tbase, &smk->pts_inc, tbase, smk->pts_inc, (1UL<<31)-1); + av_set_pts_info(st, 33, smk->pts_inc, tbase); + /* handle possible audio streams */ + for(i = 0; i < 7; i++) { + smk->indexes[i] = -1; + if((smk->rates[i] & 0xFFFFFF) && !(smk->rates[i] & SMK_AUD_BINKAUD)){ + ast[i] = av_new_stream(s, 0); + smk->indexes[i] = ast[i]->index; + av_set_pts_info(ast[i], 33, smk->pts_inc, tbase); + ast[i]->codec->codec_type = CODEC_TYPE_AUDIO; + ast[i]->codec->codec_id = (smk->rates[i] & SMK_AUD_PACKED) ? CODEC_ID_SMACKAUDIO : CODEC_ID_PCM_U8; + ast[i]->codec->codec_tag = 0; + ast[i]->codec->channels = (smk->rates[i] & SMK_AUD_STEREO) ? 2 : 1; + ast[i]->codec->sample_rate = smk->rates[i] & 0xFFFFFF; + ast[i]->codec->bits_per_sample = (smk->rates[i] & SMK_AUD_16BITS) ? 16 : 8; + if(ast[i]->codec->bits_per_sample == 16 && ast[i]->codec->codec_id == CODEC_ID_PCM_U8) + ast[i]->codec->codec_id = CODEC_ID_PCM_S16LE; + } + } + + + /* load trees to extradata, they will be unpacked by decoder */ + st->codec->extradata = av_malloc(smk->treesize + 16); + st->codec->extradata_size = smk->treesize + 16; + if(!st->codec->extradata){ + av_log(s, AV_LOG_ERROR, "Cannot allocate %i bytes of extradata\n", smk->treesize + 16); + av_free(smk->frm_size); + av_free(smk->frm_flags); + return -1; + } + ret = get_buffer(pb, st->codec->extradata + 16, st->codec->extradata_size - 16); + if(ret != st->codec->extradata_size - 16){ + av_free(smk->frm_size); + av_free(smk->frm_flags); + return AVERROR_IO; + } + ((int32_t*)st->codec->extradata)[0] = le2me_32(smk->mmap_size); + ((int32_t*)st->codec->extradata)[1] = le2me_32(smk->mclr_size); + ((int32_t*)st->codec->extradata)[2] = le2me_32(smk->full_size); + ((int32_t*)st->codec->extradata)[3] = le2me_32(smk->type_size); + + smk->curstream = -1; + smk->nextpos = url_ftell(pb); + + return 0; +} + + +static int smacker_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + SmackerContext *smk = (SmackerContext *)s->priv_data; + int flags; + int ret; + int i; + int frame_size = 0; + int palchange = 0; + int pos; + + if (url_feof(&s->pb) || smk->cur_frame >= smk->frames) + return -EIO; + + /* if we demuxed all streams, pass another frame */ + if(smk->curstream < 0) { + url_fseek(&s->pb, smk->nextpos, 0); + frame_size = smk->frm_size[smk->cur_frame] & (~3); + flags = smk->frm_flags[smk->cur_frame]; + /* handle palette change event */ + pos = url_ftell(&s->pb); + if(flags & SMACKER_PAL){ + int size, sz, t, off, j, pos; + uint8_t *pal = smk->pal; + uint8_t oldpal[768]; + + memcpy(oldpal, pal, 768); + size = get_byte(&s->pb); + size = size * 4 - 1; + frame_size -= size; + frame_size--; + sz = 0; + pos = url_ftell(&s->pb) + size; + while(sz < 256){ + t = get_byte(&s->pb); + if(t & 0x80){ /* skip palette entries */ + sz += (t & 0x7F) + 1; + pal += ((t & 0x7F) + 1) * 3; + } else if(t & 0x40){ /* copy with offset */ + off = get_byte(&s->pb) * 3; + j = (t & 0x3F) + 1; + while(j-- && sz < 256) { + *pal++ = oldpal[off + 0]; + *pal++ = oldpal[off + 1]; + *pal++ = oldpal[off + 2]; + sz++; + off += 3; + } + } else { /* new entries */ + *pal++ = smk_pal[t]; + *pal++ = smk_pal[get_byte(&s->pb) & 0x3F]; + *pal++ = smk_pal[get_byte(&s->pb) & 0x3F]; + sz++; + } + } + url_fseek(&s->pb, pos, 0); + palchange |= 1; + } + flags >>= 1; + smk->curstream = -1; + /* if audio chunks are present, put them to stack and retrieve later */ + for(i = 0; i < 7; i++) { + if(flags & 1) { + int size; + size = get_le32(&s->pb) - 4; + frame_size -= size; + frame_size -= 4; + smk->curstream++; + smk->bufs[smk->curstream] = av_realloc(smk->bufs[smk->curstream], size); + smk->buf_sizes[smk->curstream] = size; + ret = get_buffer(&s->pb, smk->bufs[smk->curstream], size); + if(ret != size) + return AVERROR_IO; + smk->stream_id[smk->curstream] = smk->indexes[i]; + } + flags >>= 1; + } + if (av_new_packet(pkt, frame_size + 768)) + return AVERROR_NOMEM; + if(smk->frm_size[smk->cur_frame] & 1) + palchange |= 2; + pkt->data[0] = palchange; + memcpy(pkt->data + 1, smk->pal, 768); + ret = get_buffer(&s->pb, pkt->data + 769, frame_size); + if(ret != frame_size) + return AVERROR_IO; + pkt->stream_index = smk->videoindex; + pkt->size = ret + 769; + smk->cur_frame++; + smk->nextpos = url_ftell(&s->pb); + } else { + if (av_new_packet(pkt, smk->buf_sizes[smk->curstream])) + return AVERROR_NOMEM; + memcpy(pkt->data, smk->bufs[smk->curstream], smk->buf_sizes[smk->curstream]); + pkt->size = smk->buf_sizes[smk->curstream]; + pkt->stream_index = smk->stream_id[smk->curstream]; + smk->curstream--; + } + + return 0; +} + +static int smacker_read_close(AVFormatContext *s) +{ + SmackerContext *smk = (SmackerContext *)s->priv_data; + int i; + + for(i = 0; i < 7; i++) + if(smk->bufs[i]) + av_free(smk->bufs[i]); + if(smk->frm_size) + av_free(smk->frm_size); + if(smk->frm_flags) + av_free(smk->frm_flags); + + for(i=0;i<s->nb_streams;i++) { + AVStream *st = s->streams[i]; + if(st->codec->extradata) + av_free(st->codec->extradata); + } + return 0; +} + +static AVInputFormat smacker_iformat = { + "smk", + "Smacker Video", + sizeof(SmackerContext), + smacker_probe, + smacker_read_header, + smacker_read_packet, + smacker_read_close, +}; + +int smacker_init(void) +{ + av_register_input_format(&smacker_iformat); + return 0; +}