From eeadcdfd1a6f3089b6bf6e194d6ece8d3f113123 Mon Sep 17 00:00:00 2001 From: Paul B Mahol Date: Tue, 11 Mar 2014 16:24:55 +0100 Subject: LucasArts SMUSH demuxer Signed-off-by: Vittorio Giovara --- libavformat/smush.c | 256 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 256 insertions(+) create mode 100644 libavformat/smush.c (limited to 'libavformat/smush.c') diff --git a/libavformat/smush.c b/libavformat/smush.c new file mode 100644 index 0000000000..1615b45417 --- /dev/null +++ b/libavformat/smush.c @@ -0,0 +1,256 @@ +/* + * LucasArts Smush demuxer + * Copyright (c) 2006 Cyril Zorin + * + * This file is part of Libav. + * + * Libav 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.1 of the License, or (at your option) any later version. + * + * Libav 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 Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/intreadwrite.h" + +#include "avformat.h" +#include "avio.h" +#include "internal.h" + +typedef struct SMUSHContext { + int version; + int audio_stream_index; + int video_stream_index; +} SMUSHContext; + +static int smush_read_probe(AVProbeData *p) +{ + if (((AV_RL32(p->buf) == MKTAG('S', 'A', 'N', 'M') && + AV_RL32(p->buf + 8) == MKTAG('S', 'H', 'D', 'R')) || + (AV_RL32(p->buf) == MKTAG('A', 'N', 'I', 'M') && + AV_RL32(p->buf + 8) == MKTAG('A', 'H', 'D', 'R')))) { + return AVPROBE_SCORE_MAX; + } + + return 0; +} + +static int smush_read_header(AVFormatContext *ctx) +{ + SMUSHContext *smush = ctx->priv_data; + AVIOContext *pb = ctx->pb; + AVStream *vst, *ast; + uint32_t magic, nframes, size, subversion, i; + uint32_t width = 0, height = 0, got_audio = 0, read = 0; + uint32_t sample_rate, channels, palette[256]; + + magic = avio_rb32(pb); + avio_skip(pb, 4); // skip movie size + + if (magic == MKBETAG('A', 'N', 'I', 'M')) { + if (avio_rb32(pb) != MKBETAG('A', 'H', 'D', 'R')) + return AVERROR_INVALIDDATA; + + size = avio_rb32(pb); + if (size < 3 * 256 + 6) + return AVERROR_INVALIDDATA; + + smush->version = 0; + subversion = avio_rl16(pb); + nframes = avio_rl16(pb); + if (!nframes) + return AVERROR_INVALIDDATA; + + avio_skip(pb, 2); // skip pad + + for (i = 0; i < 256; i++) + palette[i] = avio_rb24(pb); + + avio_skip(pb, size - (3 * 256 + 6)); + } else if (magic == MKBETAG('S', 'A', 'N', 'M')) { + if (avio_rb32(pb) != MKBETAG('S', 'H', 'D', 'R')) + return AVERROR_INVALIDDATA; + + size = avio_rb32(pb); + if (size < 14) + return AVERROR_INVALIDDATA; + + smush->version = 1; + subversion = avio_rl16(pb); + nframes = avio_rl32(pb); + if (!nframes) + return AVERROR_INVALIDDATA; + + avio_skip(pb, 2); // skip pad + width = avio_rl16(pb); + height = avio_rl16(pb); + avio_skip(pb, 2); // skip pad + avio_skip(pb, size - 14); + + if (avio_rb32(pb) != MKBETAG('F', 'L', 'H', 'D')) + return AVERROR_INVALIDDATA; + + size = avio_rb32(pb); + while (!got_audio && ((read + 8) < size)) { + uint32_t sig, chunk_size; + + if (pb->eof_reached) + return AVERROR_EOF; + + sig = avio_rb32(pb); + chunk_size = avio_rb32(pb); + read += 8; + switch (sig) { + case MKBETAG('W', 'a', 'v', 'e'): + got_audio = 1; + sample_rate = avio_rl32(pb); + if (!sample_rate) + return AVERROR_INVALIDDATA; + + channels = avio_rl32(pb); + if (!channels) + return AVERROR_INVALIDDATA; + + avio_skip(pb, chunk_size - 8); + read += chunk_size; + break; + case MKBETAG('B', 'l', '1', '6'): + case MKBETAG('A', 'N', 'N', 'O'): + avio_skip(pb, chunk_size); + read += chunk_size; + break; + default: + return AVERROR_INVALIDDATA; + break; + } + } + + avio_skip(pb, size - read); + } else { + av_log(ctx, AV_LOG_ERROR, "Wrong magic\n"); + return AVERROR_INVALIDDATA; + } + + vst = avformat_new_stream(ctx, 0); + if (!vst) + return AVERROR(ENOMEM); + + smush->video_stream_index = vst->index; + + avpriv_set_pts_info(vst, 64, 1, 15); + + vst->start_time = 0; + vst->duration = + vst->nb_frames = nframes; + vst->avg_frame_rate = av_inv_q(vst->time_base); + vst->codec->codec_type = AVMEDIA_TYPE_VIDEO; + vst->codec->codec_id = AV_CODEC_ID_SANM; + vst->codec->codec_tag = 0; + vst->codec->width = width; + vst->codec->height = height; + + if (!smush->version) { + av_free(vst->codec->extradata); + vst->codec->extradata_size = 1024 + 2; + vst->codec->extradata = av_malloc(vst->codec->extradata_size + + FF_INPUT_BUFFER_PADDING_SIZE); + if (!vst->codec->extradata) + return AVERROR(ENOMEM); + + AV_WL16(vst->codec->extradata, subversion); + for (i = 0; i < 256; i++) + AV_WL32(vst->codec->extradata + 2 + i * 4, palette[i]); + } + + if (got_audio) { + ast = avformat_new_stream(ctx, 0); + if (!ast) + return AVERROR(ENOMEM); + + smush->audio_stream_index = ast->index; + + ast->start_time = 0; + ast->codec->codec_type = AVMEDIA_TYPE_AUDIO; + ast->codec->codec_id = AV_CODEC_ID_ADPCM_VIMA; + ast->codec->codec_tag = 0; + ast->codec->sample_rate = sample_rate; + ast->codec->channels = channels; + + avpriv_set_pts_info(ast, 64, 1, ast->codec->sample_rate); + } + + return 0; +} + +static int smush_read_packet(AVFormatContext *ctx, AVPacket *pkt) +{ + SMUSHContext *smush = ctx->priv_data; + AVIOContext *pb = ctx->pb; + int done = 0; + int ret; + + while (!done) { + uint32_t sig, size; + + if (pb->eof_reached) + return AVERROR_EOF; + + sig = avio_rb32(pb); + size = avio_rb32(pb); + + switch (sig) { + case MKBETAG('F', 'R', 'M', 'E'): + if (smush->version) + break; + if ((ret = av_get_packet(pb, pkt, size)) < 0) + return ret; + + pkt->stream_index = smush->video_stream_index; + done = 1; + break; + case MKBETAG('B', 'l', '1', '6'): + if ((ret = av_get_packet(pb, pkt, size)) < 0) + return ret; + + pkt->stream_index = smush->video_stream_index; + pkt->duration = 1; + done = 1; + break; + case MKBETAG('W', 'a', 'v', 'e'): + if (size < 13) + return AVERROR_INVALIDDATA; + if (av_get_packet(pb, pkt, size) < 13) + return AVERROR(EIO); + + pkt->stream_index = smush->audio_stream_index; + pkt->flags |= AV_PKT_FLAG_KEY; + pkt->duration = AV_RB32(pkt->data); + if (pkt->duration == 0xFFFFFFFFu) + pkt->duration = AV_RB32(pkt->data + 8); + done = 1; + break; + default: + avio_skip(pb, size); + break; + } + } + + return 0; +} + +AVInputFormat ff_smush_demuxer = { + .name = "smush", + .long_name = NULL_IF_CONFIG_SMALL("LucasArts Smush"), + .priv_data_size = sizeof(SMUSHContext), + .read_probe = smush_read_probe, + .read_header = smush_read_header, + .read_packet = smush_read_packet, +}; -- cgit v1.2.3