From bef8fd7099edfac9c487e6eb75d7e6116ad80465 Mon Sep 17 00:00:00 2001 From: Paul B Mahol Date: Mon, 19 Mar 2012 02:55:26 +0000 Subject: LucasArts SMUSH demuxer Signed-off-by: Paul B Mahol --- Changelog | 1 + doc/general.texi | 2 + libavformat/Makefile | 1 + libavformat/allformats.c | 1 + libavformat/smush.c | 232 +++++++++++++++++++++++++++++++++++++++++++++++ libavformat/version.h | 2 +- 6 files changed, 238 insertions(+), 1 deletion(-) create mode 100644 libavformat/smush.c diff --git a/Changelog b/Changelog index 63260aeb36..b1dc7fa53f 100644 --- a/Changelog +++ b/Changelog @@ -13,6 +13,7 @@ version next: - iLBC encoding/decoding via libilbc - Microsoft Screen 1 decoder - showwaves filter +- LucasArts SMUSH playback support version 0.11: diff --git a/doc/general.texi b/doc/general.texi index 77a82c804a..49b665d617 100644 --- a/doc/general.texi +++ b/doc/general.texi @@ -335,6 +335,8 @@ library: @tab Multimedia format used by many games. @item SMJPEG @tab X @tab X @tab Used in certain Loki game ports. +@item Smush + @tab Multimedia format used in some LucasArts games. @item Sony OpenMG (OMA) @tab X @tab X @tab Audio format used in Sony Sonic Stage and Sony Vegas. @item Sony PlayStation STR @tab @tab X diff --git a/libavformat/Makefile b/libavformat/Makefile index f38b8a575c..74257392cd 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -306,6 +306,7 @@ OBJS-$(CONFIG_SIFF_DEMUXER) += siff.o OBJS-$(CONFIG_SMACKER_DEMUXER) += smacker.o OBJS-$(CONFIG_SMJPEG_DEMUXER) += smjpegdec.o smjpeg.o OBJS-$(CONFIG_SMJPEG_MUXER) += smjpegenc.o smjpeg.o +OBJS-$(CONFIG_SMUSH_DEMUXER) += smush.o OBJS-$(CONFIG_SOL_DEMUXER) += sol.o pcm.o OBJS-$(CONFIG_SOX_DEMUXER) += soxdec.o pcm.o OBJS-$(CONFIG_SOX_MUXER) += soxenc.o diff --git a/libavformat/allformats.c b/libavformat/allformats.c index b5738e4d97..c3b8bb479f 100644 --- a/libavformat/allformats.c +++ b/libavformat/allformats.c @@ -219,6 +219,7 @@ void av_register_all(void) REGISTER_DEMUXER (SIFF, siff); REGISTER_DEMUXER (SMACKER, smacker); REGISTER_MUXDEMUX (SMJPEG, smjpeg); + REGISTER_DEMUXER (SMUSH, smush); REGISTER_DEMUXER (SOL, sol); REGISTER_MUXDEMUX (SOX, sox); REGISTER_MUXDEMUX (SPDIF, spdif); diff --git a/libavformat/smush.c b/libavformat/smush.c new file mode 100644 index 0000000000..4d3e1380e4 --- /dev/null +++ b/libavformat/smush.c @@ -0,0 +1,232 @@ +/* + * LucasArts Smush demuxer + * Copyright (c) 2006 Cyril Zorin + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; 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 "internal.h" +#include "avio.h" + +typedef struct { + 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) == MKTAG('A', 'N', 'I', 'M'))) { + 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); + + 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; + avio_skip(pb, 2); // skip version + nframes = avio_rl32(pb); + 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 (url_feof(pb)) + 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); + channels = avio_rl32(pb); + 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; + + vst->start_time = 0; + vst->duration = + vst->nb_frames = nframes; + vst->codec->codec_type = AVMEDIA_TYPE_VIDEO; + vst->codec->codec_id = CODEC_ID_SANM; + vst->codec->codec_tag = 0; + vst->codec->width = width; + vst->codec->height = height; + + avpriv_set_pts_info(vst, 64, 66667, 1000000); + + if (!smush->version) { + vst->codec->extradata = av_malloc(1024 + 2 + FF_INPUT_BUFFER_PADDING_SIZE); + if (!vst->codec->extradata) + return AVERROR(ENOMEM); + + vst->codec->extradata_size = 1024 + 2; + 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 = CODEC_ID_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; + + while (!done) { + uint32_t sig, size; + + if (url_feof(pb)) + return AVERROR_EOF; + + sig = avio_rb32(pb); + size = avio_rb32(pb); + + switch (sig) { + case MKBETAG('F', 'R', 'M', 'E'): + if (smush->version) + break; + if (av_get_packet(pb, pkt, size) < 0) + return AVERROR(EIO); + + pkt->stream_index = smush->video_stream_index; + done = 1; + break; + case MKBETAG('B', 'l', '1', '6'): + if (av_get_packet(pb, pkt, size) < 0) + return AVERROR(EIO); + + pkt->stream_index = smush->video_stream_index; + pkt->duration = 1; + done = 1; + break; + case MKBETAG('W', 'a', 'v', 'e'): + if (av_get_packet(pb, pkt, size) < 0) + return AVERROR(EIO); + + pkt->stream_index = smush->audio_stream_index; + 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, +}; diff --git a/libavformat/version.h b/libavformat/version.h index 8d567d9fce..9a8b9aa130 100644 --- a/libavformat/version.h +++ b/libavformat/version.h @@ -30,7 +30,7 @@ #include "libavutil/avutil.h" #define LIBAVFORMAT_VERSION_MAJOR 54 -#define LIBAVFORMAT_VERSION_MINOR 9 +#define LIBAVFORMAT_VERSION_MINOR 10 #define LIBAVFORMAT_VERSION_MICRO 100 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ -- cgit v1.2.3