348efc18 |
/* |
7fbde343 |
* Smacker demuxer |
406792e7 |
* Copyright (c) 2006 Konstantin Shishkov |
348efc18 |
* |
b78e7197 |
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or |
348efc18 |
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either |
b78e7197 |
* version 2.1 of the License, or (at your option) any later version. |
348efc18 |
* |
b78e7197 |
* FFmpeg is distributed in the hope that it will be useful, |
348efc18 |
* 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 |
b78e7197 |
* License along with FFmpeg; if not, write to the Free Software |
348efc18 |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
* Based on http://wiki.multimedia.cx/index.php?title=Smacker
*/
|
d92024f1 |
#include <inttypes.h>
|
245976da |
#include "libavutil/bswap.h" |
bfccd76a |
#include "libavutil/channel_layout.h" |
6a5d31ac |
#include "libavutil/intreadwrite.h" |
348efc18 |
#include "avformat.h" |
c3f9ebf7 |
#include "internal.h" |
348efc18 |
#define SMACKER_PAL 0x01 |
2b71ddd9 |
#define SMACKER_FLAG_RING_FRAME 0x01 |
348efc18 |
enum SAudFlags { |
a7984a6a |
SMK_AUD_PACKED = 0x80,
SMK_AUD_16BITS = 0x20,
SMK_AUD_STEREO = 0x10,
SMK_AUD_BINKAUD = 0x08,
SMK_AUD_USEDCT = 0x04 |
348efc18 |
};
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; |
a7984a6a |
uint8_t aflags[7]; |
348efc18 |
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; |
bc5c918e |
int64_t nextpos; |
386b9b5f |
int64_t aud_pts[7]; |
348efc18 |
} 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)
{ |
0062869a |
if ( AV_RL32(p->buf) != MKTAG('S', 'M', 'K', '2')
&& AV_RL32(p->buf) != MKTAG('S', 'M', 'K', '4')) |
348efc18 |
return 0; |
0062869a |
if (AV_RL32(p->buf+4) > 32768U || AV_RL32(p->buf+8) > 32768U)
return AVPROBE_SCORE_MAX/4;
return AVPROBE_SCORE_MAX; |
348efc18 |
}
|
6e9651d1 |
static int smacker_read_header(AVFormatContext *s) |
348efc18 |
{ |
471fe57e |
AVIOContext *pb = s->pb; |
e4141433 |
SmackerContext *smk = s->priv_data; |
348efc18 |
AVStream *st, *ast[7];
int i, ret;
int tbase;
/* read and check header */ |
e63a3628 |
smk->magic = avio_rl32(pb); |
348efc18 |
if (smk->magic != MKTAG('S', 'M', 'K', '2') && smk->magic != MKTAG('S', 'M', 'K', '4')) |
547b8aee |
return AVERROR_INVALIDDATA; |
e63a3628 |
smk->width = avio_rl32(pb);
smk->height = avio_rl32(pb);
smk->frames = avio_rl32(pb);
smk->pts_inc = (int32_t)avio_rl32(pb); |
7ed47e97 |
if (smk->pts_inc > INT_MAX / 100) {
av_log(s, AV_LOG_ERROR, "pts_inc %d is too large\n", smk->pts_inc);
return AVERROR_INVALIDDATA;
}
|
e63a3628 |
smk->flags = avio_rl32(pb); |
2b71ddd9 |
if(smk->flags & SMACKER_FLAG_RING_FRAME)
smk->frames++; |
348efc18 |
for(i = 0; i < 7; i++) |
e63a3628 |
smk->audio[i] = avio_rl32(pb);
smk->treesize = avio_rl32(pb); |
a443a253 |
if(smk->treesize >= UINT_MAX/4){ // smk->treesize + 16 must not overflow (this check is probably redundant)
av_log(s, AV_LOG_ERROR, "treesize too large\n"); |
547b8aee |
return AVERROR_INVALIDDATA; |
a443a253 |
}
//FIXME remove extradata "rebuilding" |
e63a3628 |
smk->mmap_size = avio_rl32(pb);
smk->mclr_size = avio_rl32(pb);
smk->full_size = avio_rl32(pb);
smk->type_size = avio_rl32(pb); |
a7984a6a |
for(i = 0; i < 7; i++) {
smk->rates[i] = avio_rl24(pb);
smk->aflags[i] = avio_r8(pb);
} |
e63a3628 |
smk->pad = avio_rl32(pb); |
348efc18 |
/* setup data */
if(smk->frames > 0xFFFFFF) { |
d92024f1 |
av_log(s, AV_LOG_ERROR, "Too many frames: %"PRIu32"\n", smk->frames); |
547b8aee |
return AVERROR_INVALIDDATA; |
348efc18 |
} |
78f680cb |
smk->frm_size = av_malloc_array(smk->frames, sizeof(*smk->frm_size)); |
348efc18 |
smk->frm_flags = av_malloc(smk->frames); |
78f680cb |
if (!smk->frm_size || !smk->frm_flags) {
av_freep(&smk->frm_size);
av_freep(&smk->frm_flags);
return AVERROR(ENOMEM);
} |
348efc18 |
smk->is_ver4 = (smk->magic != MKTAG('S', 'M', 'K', '2'));
/* read frame info */
for(i = 0; i < smk->frames; i++) { |
e63a3628 |
smk->frm_size[i] = avio_rl32(pb); |
348efc18 |
}
for(i = 0; i < smk->frames; i++) { |
e63a3628 |
smk->frm_flags[i] = avio_r8(pb); |
348efc18 |
}
/* init video codec */ |
3b3bbdd3 |
st = avformat_new_stream(s, NULL); |
348efc18 |
if (!st) |
547b8aee |
return AVERROR(ENOMEM); |
348efc18 |
smk->videoindex = st->index; |
9200514a |
st->codecpar->width = smk->width;
st->codecpar->height = smk->height;
st->codecpar->format = AV_PIX_FMT_PAL8;
st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
st->codecpar->codec_id = AV_CODEC_ID_SMACKVIDEO;
st->codecpar->codec_tag = smk->magic; |
348efc18 |
/* 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); |
c3f9ebf7 |
avpriv_set_pts_info(st, 33, smk->pts_inc, tbase); |
2a33c673 |
st->duration = smk->frames; |
348efc18 |
/* handle possible audio streams */
for(i = 0; i < 7; i++) {
smk->indexes[i] = -1; |
a7984a6a |
if (smk->rates[i]) { |
3b3bbdd3 |
ast[i] = avformat_new_stream(s, NULL); |
9e505485 |
if (!ast[i])
return AVERROR(ENOMEM); |
348efc18 |
smk->indexes[i] = ast[i]->index; |
9200514a |
ast[i]->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; |
a7984a6a |
if (smk->aflags[i] & SMK_AUD_BINKAUD) { |
9200514a |
ast[i]->codecpar->codec_id = AV_CODEC_ID_BINKAUDIO_RDFT; |
a7984a6a |
} else if (smk->aflags[i] & SMK_AUD_USEDCT) { |
9200514a |
ast[i]->codecpar->codec_id = AV_CODEC_ID_BINKAUDIO_DCT; |
a7984a6a |
} else if (smk->aflags[i] & SMK_AUD_PACKED){ |
9200514a |
ast[i]->codecpar->codec_id = AV_CODEC_ID_SMACKAUDIO;
ast[i]->codecpar->codec_tag = MKTAG('S', 'M', 'K', 'A'); |
895ab748 |
} else { |
9200514a |
ast[i]->codecpar->codec_id = AV_CODEC_ID_PCM_U8; |
895ab748 |
} |
bfccd76a |
if (smk->aflags[i] & SMK_AUD_STEREO) { |
9200514a |
ast[i]->codecpar->channels = 2;
ast[i]->codecpar->channel_layout = AV_CH_LAYOUT_STEREO; |
bfccd76a |
} else { |
9200514a |
ast[i]->codecpar->channels = 1;
ast[i]->codecpar->channel_layout = AV_CH_LAYOUT_MONO; |
bfccd76a |
} |
9200514a |
ast[i]->codecpar->sample_rate = smk->rates[i];
ast[i]->codecpar->bits_per_coded_sample = (smk->aflags[i] & SMK_AUD_16BITS) ? 16 : 8;
if(ast[i]->codecpar->bits_per_coded_sample == 16 && ast[i]->codecpar->codec_id == AV_CODEC_ID_PCM_U8)
ast[i]->codecpar->codec_id = AV_CODEC_ID_PCM_S16LE;
avpriv_set_pts_info(ast[i], 64, 1, ast[i]->codecpar->sample_rate
* ast[i]->codecpar->channels * ast[i]->codecpar->bits_per_coded_sample / 8); |
348efc18 |
}
}
/* load trees to extradata, they will be unpacked by decoder */ |
6f69f7a8 |
if(ff_alloc_extradata(st->codecpar, smk->treesize + 16)){ |
d92024f1 |
av_log(s, AV_LOG_ERROR,
"Cannot allocate %"PRIu32" bytes of extradata\n",
smk->treesize + 16); |
d2d96f9e |
av_freep(&smk->frm_size);
av_freep(&smk->frm_flags); |
547b8aee |
return AVERROR(ENOMEM); |
348efc18 |
} |
9200514a |
ret = avio_read(pb, st->codecpar->extradata + 16, st->codecpar->extradata_size - 16);
if(ret != st->codecpar->extradata_size - 16){ |
d2d96f9e |
av_freep(&smk->frm_size);
av_freep(&smk->frm_flags); |
6f3e0b21 |
return AVERROR(EIO); |
348efc18 |
} |
9200514a |
((int32_t*)st->codecpar->extradata)[0] = av_le2ne32(smk->mmap_size);
((int32_t*)st->codecpar->extradata)[1] = av_le2ne32(smk->mclr_size);
((int32_t*)st->codecpar->extradata)[2] = av_le2ne32(smk->full_size);
((int32_t*)st->codecpar->extradata)[3] = av_le2ne32(smk->type_size); |
348efc18 |
smk->curstream = -1; |
384c9c2f |
smk->nextpos = avio_tell(pb); |
348efc18 |
return 0;
}
static int smacker_read_packet(AVFormatContext *s, AVPacket *pkt)
{ |
e4141433 |
SmackerContext *smk = s->priv_data; |
348efc18 |
int flags;
int ret;
int i;
int frame_size = 0;
int palchange = 0;
|
d34ec64a |
if (avio_feof(s->pb) || smk->cur_frame >= smk->frames) |
3bb96f91 |
return AVERROR_EOF; |
348efc18 |
/* if we demuxed all streams, pass another frame */
if(smk->curstream < 0) { |
f59d8ff8 |
avio_seek(s->pb, smk->nextpos, 0); |
348efc18 |
frame_size = smk->frm_size[smk->cur_frame] & (~3);
flags = smk->frm_flags[smk->cur_frame];
/* handle palette change event */
if(flags & SMACKER_PAL){
int size, sz, t, off, j, pos;
uint8_t *pal = smk->pal;
uint8_t oldpal[768];
memcpy(oldpal, pal, 768); |
e63a3628 |
size = avio_r8(s->pb); |
348efc18 |
size = size * 4 - 1; |
c402c1c9 |
if(size + 1 > frame_size)
return AVERROR_INVALIDDATA; |
348efc18 |
frame_size -= size;
frame_size--;
sz = 0; |
384c9c2f |
pos = avio_tell(s->pb) + size; |
348efc18 |
while(sz < 256){ |
e63a3628 |
t = avio_r8(s->pb); |
348efc18 |
if(t & 0x80){ /* skip palette entries */
sz += (t & 0x7F) + 1;
pal += ((t & 0x7F) + 1) * 3;
} else if(t & 0x40){ /* copy with offset */ |
a93b572a |
off = avio_r8(s->pb); |
348efc18 |
j = (t & 0x3F) + 1; |
c3c08bae |
if (off + j > 0x100) { |
a93b572a |
av_log(s, AV_LOG_ERROR,
"Invalid palette update, offset=%d length=%d extends beyond palette size\n",
off, j);
return AVERROR_INVALIDDATA;
}
off *= 3; |
348efc18 |
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]; |
e63a3628 |
*pal++ = smk_pal[avio_r8(s->pb) & 0x3F];
*pal++ = smk_pal[avio_r8(s->pb) & 0x3F]; |
348efc18 |
sz++;
}
} |
f59d8ff8 |
avio_seek(s->pb, pos, 0); |
348efc18 |
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) { |
ee16a0ce |
uint32_t size; |
5626f994 |
int err; |
47a8589f |
|
e63a3628 |
size = avio_rl32(s->pb) - 4; |
465f3705 |
if (!size || size + 4LL > frame_size) { |
ee16a0ce |
av_log(s, AV_LOG_ERROR, "Invalid audio part size\n"); |
c402c1c9 |
return AVERROR_INVALIDDATA; |
ee16a0ce |
} |
348efc18 |
frame_size -= size;
frame_size -= 4;
smk->curstream++; |
d872fb0f |
if ((err = av_reallocp(&smk->bufs[smk->curstream], size)) < 0) {
smk->buf_sizes[smk->curstream] = 0; |
5626f994 |
return err; |
d872fb0f |
} |
348efc18 |
smk->buf_sizes[smk->curstream] = size; |
e63a3628 |
ret = avio_read(s->pb, smk->bufs[smk->curstream], size); |
348efc18 |
if(ret != size) |
6f3e0b21 |
return AVERROR(EIO); |
348efc18 |
smk->stream_id[smk->curstream] = smk->indexes[i];
}
flags >>= 1;
} |
710b0e27 |
if (frame_size < 0 || frame_size >= INT_MAX/2) |
d0121e8d |
return AVERROR_INVALIDDATA;
if (av_new_packet(pkt, frame_size + 769)) |
769e10f0 |
return AVERROR(ENOMEM); |
348efc18 |
if(smk->frm_size[smk->cur_frame] & 1)
palchange |= 2;
pkt->data[0] = palchange;
memcpy(pkt->data + 1, smk->pal, 768); |
e63a3628 |
ret = avio_read(s->pb, pkt->data + 769, frame_size); |
348efc18 |
if(ret != frame_size) |
6f3e0b21 |
return AVERROR(EIO); |
348efc18 |
pkt->stream_index = smk->videoindex; |
851bc1d6 |
pkt->pts = smk->cur_frame; |
348efc18 |
pkt->size = ret + 769;
smk->cur_frame++; |
384c9c2f |
smk->nextpos = avio_tell(s->pb); |
348efc18 |
} else { |
8be56e46 |
if (smk->stream_id[smk->curstream] < 0 || !smk->bufs[smk->curstream]) |
8d928023 |
return AVERROR_INVALIDDATA; |
348efc18 |
if (av_new_packet(pkt, smk->buf_sizes[smk->curstream])) |
769e10f0 |
return AVERROR(ENOMEM); |
348efc18 |
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]; |
386b9b5f |
pkt->pts = smk->aud_pts[smk->curstream]; |
fead30d4 |
smk->aud_pts[smk->curstream] += AV_RL32(pkt->data); |
348efc18 |
smk->curstream--;
}
return 0;
}
static int smacker_read_close(AVFormatContext *s)
{ |
e4141433 |
SmackerContext *smk = s->priv_data; |
348efc18 |
int i;
for(i = 0; i < 7; i++) |
d2d96f9e |
av_freep(&smk->bufs[i]);
av_freep(&smk->frm_size);
av_freep(&smk->frm_flags); |
348efc18 |
return 0;
}
|
66355be3 |
AVInputFormat ff_smacker_demuxer = { |
dfc2c4d9 |
.name = "smk", |
29ba3aac |
.long_name = NULL_IF_CONFIG_SMALL("Smacker"), |
dfc2c4d9 |
.priv_data_size = sizeof(SmackerContext),
.read_probe = smacker_probe,
.read_header = smacker_read_header,
.read_packet = smacker_read_packet,
.read_close = smacker_read_close, |
348efc18 |
}; |