diff --git a/Changelog b/Changelog index b417f86bf2..b3ab301179 100644 --- a/Changelog +++ b/Changelog @@ -16,6 +16,7 @@ version : - VP7 video decoder - LucasArts SMUSH SANM video decoder - LucasArts SMUSH VIMA audio decoder (ADPCM) +- LucasArts SMUSH demuxer version 10: diff --git a/doc/general.texi b/doc/general.texi index 66844a6098..f1287cf8d9 100644 --- a/doc/general.texi +++ b/doc/general.texi @@ -383,6 +383,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 e3bfd3a4e4..6784d51e98 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -311,6 +311,7 @@ OBJS-$(CONFIG_SMACKER_DEMUXER) += smacker.o OBJS-$(CONFIG_SMJPEG_DEMUXER) += smjpegdec.o smjpeg.o OBJS-$(CONFIG_SMJPEG_MUXER) += smjpegenc.o smjpeg.o OBJS-$(CONFIG_SMOOTHSTREAMING_MUXER) += smoothstreamingenc.o isom.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 f9f7c68abc..4f3acf56a3 100644 --- a/libavformat/allformats.c +++ b/libavformat/allformats.c @@ -223,6 +223,7 @@ void av_register_all(void) REGISTER_DEMUXER (SMACKER, smacker); REGISTER_MUXDEMUX(SMJPEG, smjpeg); REGISTER_MUXER (SMOOTHSTREAMING, smoothstreaming); + 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..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, +}; diff --git a/libavformat/version.h b/libavformat/version.h index 85ff700117..ba531205c5 100644 --- a/libavformat/version.h +++ b/libavformat/version.h @@ -30,7 +30,7 @@ #include "libavutil/version.h" #define LIBAVFORMAT_VERSION_MAJOR 55 -#define LIBAVFORMAT_VERSION_MINOR 14 +#define LIBAVFORMAT_VERSION_MINOR 15 #define LIBAVFORMAT_VERSION_MICRO 0 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \