mirror of
https://github.com/libretro/scummvm.git
synced 2025-02-12 06:00:48 +00:00
Merge pull request #376 from somaen/smush_decoder
Refactor the SMUSH-decoder into a subclass of VideoDecoder
This commit is contained in:
commit
4bddcfadac
@ -37,11 +37,11 @@ enum enDebugLevels {
|
||||
DEBUG_BITMAPS = 16,
|
||||
DEBUG_MODEL = 32,
|
||||
DEBUG_STUB = 64,
|
||||
DEBUG_SMUSH = 128,
|
||||
DEBUG_MOVIE = 128,
|
||||
DEBUG_IMUSE = 256,
|
||||
DEBUG_CHORES = 512,
|
||||
DEBUG_ALL = DEBUG_NORMAL | DEBUG_WARN | DEBUG_ERROR | DEBUG_LUA | DEBUG_BITMAPS |
|
||||
DEBUG_MODEL | DEBUG_STUB | DEBUG_SMUSH | DEBUG_IMUSE | DEBUG_CHORES
|
||||
DEBUG_MODEL | DEBUG_STUB | DEBUG_MOVIE | DEBUG_IMUSE | DEBUG_CHORES
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -25,6 +25,10 @@
|
||||
|
||||
#include "math/vector3d.h"
|
||||
|
||||
namespace Graphics {
|
||||
struct Surface;
|
||||
}
|
||||
|
||||
namespace Grim {
|
||||
|
||||
struct Shadow;
|
||||
@ -194,7 +198,7 @@ public:
|
||||
* @see drawMovieFrame
|
||||
* @see releaseMovieFrame
|
||||
*/
|
||||
virtual void prepareMovieFrame(int width, int height, byte *bitmap) = 0;
|
||||
virtual void prepareMovieFrame(Graphics::Surface* frame) = 0;
|
||||
virtual void drawMovieFrame(int offsetX, int offsetY) = 0;
|
||||
|
||||
/**
|
||||
|
@ -29,6 +29,8 @@
|
||||
#include "common/endian.h"
|
||||
#include "common/system.h"
|
||||
|
||||
#include "graphics/surface.h"
|
||||
|
||||
#include "engines/grim/actor.h"
|
||||
#include "engines/grim/colormap.h"
|
||||
#include "engines/grim/font.h"
|
||||
@ -1040,7 +1042,11 @@ void GfxOpenGL::drawDepthBitmap(int x, int y, int w, int h, char *data) {
|
||||
glDepthFunc(GL_LESS);
|
||||
}
|
||||
|
||||
void GfxOpenGL::prepareMovieFrame(int width, int height, byte *bitmap) {
|
||||
void GfxOpenGL::prepareMovieFrame(Graphics::Surface* frame) {
|
||||
int height = frame->h;
|
||||
int width = frame->w;
|
||||
byte *bitmap = (byte *)frame->pixels;
|
||||
|
||||
// remove if already exist
|
||||
if (_smushNumTex > 0) {
|
||||
glDeleteTextures(_smushNumTex, _smushTexIds);
|
||||
|
@ -114,7 +114,7 @@ public:
|
||||
void drawLine(PrimitiveObject *primitive);
|
||||
void drawPolygon(PrimitiveObject *primitive);
|
||||
|
||||
void prepareMovieFrame(int width, int height, byte *bitmap);
|
||||
void prepareMovieFrame(Graphics::Surface* frame);
|
||||
void drawMovieFrame(int offsetX, int offsetY);
|
||||
void releaseMovieFrame();
|
||||
|
||||
|
@ -23,6 +23,8 @@
|
||||
#include "common/endian.h"
|
||||
#include "common/system.h"
|
||||
|
||||
#include "graphics/surface.h"
|
||||
|
||||
#include "engines/grim/actor.h"
|
||||
#include "engines/grim/colormap.h"
|
||||
#include "engines/grim/material.h"
|
||||
@ -857,10 +859,10 @@ void GfxTinyGL::destroyMaterial(Texture *material) {
|
||||
delete[] (TGLuint *)material->_texture;
|
||||
}
|
||||
|
||||
void GfxTinyGL::prepareMovieFrame(int width, int height, byte *bitmap) {
|
||||
_smushWidth = width;
|
||||
_smushHeight = height;
|
||||
_smushBitmap = bitmap;
|
||||
void GfxTinyGL::prepareMovieFrame(Graphics::Surface* frame) {
|
||||
_smushWidth = frame->w;
|
||||
_smushHeight = frame->h;
|
||||
_smushBitmap = (byte *)frame->pixels;
|
||||
}
|
||||
|
||||
void GfxTinyGL::drawMovieFrame(int offsetX, int offsetY) {
|
||||
|
@ -106,7 +106,7 @@ public:
|
||||
void drawLine(PrimitiveObject *primitive);
|
||||
void drawPolygon(PrimitiveObject *primitive);
|
||||
|
||||
void prepareMovieFrame(int width, int height, byte *bitmap);
|
||||
void prepareMovieFrame(Graphics::Surface* frame);
|
||||
void drawMovieFrame(int offsetX, int offsetY);
|
||||
void releaseMovieFrame();
|
||||
|
||||
|
@ -215,12 +215,12 @@ Common::Error GrimEngine::run() {
|
||||
g_resourceloader = new ResourceLoader();
|
||||
g_localizer = new Localizer();
|
||||
if (getGameType() == GType_GRIM)
|
||||
g_movie = CreateSmushPlayer();
|
||||
g_movie = CreateSmushPlayer(getGameFlags() & ADGF_DEMO);
|
||||
else if (getGameType() == GType_MONKEY4) {
|
||||
if (_gamePlatform == Common::kPlatformPS2)
|
||||
g_movie = CreateMpegPlayer();
|
||||
else
|
||||
g_movie = CreateBinkPlayer();
|
||||
g_movie = CreateBinkPlayer(getGameFlags() & ADGF_DEMO);
|
||||
}
|
||||
g_imuse = new Imuse(20);
|
||||
|
||||
@ -686,7 +686,7 @@ void GrimEngine::updateDisplayScene() {
|
||||
if (g_movie->isPlaying()) {
|
||||
_movieTime = g_movie->getMovieTime();
|
||||
if (g_movie->isUpdateNeeded()) {
|
||||
g_driver->prepareMovieFrame(g_movie->getWidth(), g_movie->getHeight(), g_movie->getDstPtr());
|
||||
g_driver->prepareMovieFrame(g_movie->getDstSurface());
|
||||
g_movie->clearUpdateNeeded();
|
||||
}
|
||||
int frame = g_movie->getFrame();
|
||||
@ -741,7 +741,7 @@ void GrimEngine::updateDisplayScene() {
|
||||
if (g_movie->isPlaying()) {
|
||||
_movieTime = g_movie->getMovieTime();
|
||||
if (g_movie->isUpdateNeeded()) {
|
||||
g_driver->prepareMovieFrame(g_movie->getWidth(), g_movie->getHeight(), g_movie->getDstPtr());
|
||||
g_driver->prepareMovieFrame(g_movie->getDstSurface());
|
||||
g_movie->clearUpdateNeeded();
|
||||
}
|
||||
if (g_movie->getFrame() > 0)
|
||||
|
@ -35,6 +35,7 @@ MODULE_OBJS := \
|
||||
movie/codecs/blocky8.o \
|
||||
movie/codecs/blocky16.o \
|
||||
movie/codecs/vima.o \
|
||||
movie/codecs/smush_decoder.o \
|
||||
movie/bink.o \
|
||||
movie/mpeg.o \
|
||||
movie/smush.o \
|
||||
|
@ -20,156 +20,30 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#define FORBIDDEN_SYMBOL_EXCEPTION_printf
|
||||
#define FORBIDDEN_SYMBOL_EXCEPTION_chdir
|
||||
#define FORBIDDEN_SYMBOL_EXCEPTION_getcwd
|
||||
#define FORBIDDEN_SYMBOL_EXCEPTION_getwd
|
||||
#define FORBIDDEN_SYMBOL_EXCEPTION_mkdir
|
||||
#define FORBIDDEN_SYMBOL_EXCEPTION_unlink
|
||||
|
||||
#include "common/endian.h"
|
||||
#include "common/timer.h"
|
||||
#include "common/file.h"
|
||||
#include "common/events.h"
|
||||
#include "common/system.h"
|
||||
#include "common/textconsole.h"
|
||||
|
||||
#include "audio/audiostream.h"
|
||||
#include "audio/mixer.h"
|
||||
#include "audio/decoders/raw.h"
|
||||
|
||||
#include "graphics/surface.h"
|
||||
#include "video/bink_decoder.h"
|
||||
|
||||
#include "engines/grim/movie/bink.h"
|
||||
|
||||
#include "engines/grim/debug.h"
|
||||
#include "engines/grim/grim.h"
|
||||
|
||||
#ifdef USE_BINK
|
||||
|
||||
namespace Grim {
|
||||
|
||||
MoviePlayer *CreateBinkPlayer() {
|
||||
return new BinkPlayer();
|
||||
MoviePlayer *CreateBinkPlayer(bool demo) {
|
||||
return new BinkPlayer(demo);
|
||||
}
|
||||
|
||||
void BinkPlayer::timerCallback(void *) {
|
||||
((BinkPlayer *)g_movie)->handleFrame();
|
||||
}
|
||||
|
||||
BinkPlayer::BinkPlayer() : MoviePlayer() {
|
||||
g_movie = this;
|
||||
_binkDecoder = new Video::BinkDecoder();
|
||||
_surface = new Graphics::Surface();
|
||||
_externalSurface = new Graphics::Surface();
|
||||
BinkPlayer::BinkPlayer(bool demo) : MoviePlayer(), _demo(demo) {
|
||||
_videoDecoder = new Video::BinkDecoder();
|
||||
_speed = 1000;
|
||||
}
|
||||
|
||||
BinkPlayer::~BinkPlayer() {
|
||||
deinit();
|
||||
}
|
||||
|
||||
void BinkPlayer::init() {
|
||||
_frame = 0;
|
||||
_movieTime = 0;
|
||||
_updateNeeded = false;
|
||||
_videoFinished = false;
|
||||
_width = 0;
|
||||
_height = 0;
|
||||
_x = 0;
|
||||
_y = 0;
|
||||
|
||||
assert(!_externalBuffer);
|
||||
|
||||
g_system->getTimerManager()->installTimerProc(&timerCallback, _speed, NULL);
|
||||
}
|
||||
|
||||
void BinkPlayer::deinit() {
|
||||
g_system->getTimerManager()->removeTimerProc(&timerCallback);
|
||||
_binkDecoder->close();
|
||||
_surface->free();
|
||||
|
||||
if (_externalBuffer) {
|
||||
delete[] _externalBuffer;
|
||||
_externalBuffer = NULL;
|
||||
}
|
||||
|
||||
if (_stream) {
|
||||
_stream->finish();
|
||||
_stream = NULL;
|
||||
g_system->getMixer()->stopHandle(_soundHandle);
|
||||
}
|
||||
_videoPause = false;
|
||||
}
|
||||
|
||||
void BinkPlayer::handleFrame() {
|
||||
if (_binkDecoder->endOfVideo())
|
||||
_videoFinished = true;
|
||||
|
||||
if (_videoPause)
|
||||
return;
|
||||
|
||||
if (_videoFinished) {
|
||||
g_grim->setMode(ENGINE_MODE_NORMAL);
|
||||
_videoPause = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (_binkDecoder->getTimeToNextFrame() > 0)
|
||||
return;
|
||||
|
||||
_surface->copyFrom(*_binkDecoder->decodeNextFrame());
|
||||
|
||||
_width = _surface->w;
|
||||
_height = _surface->h;
|
||||
|
||||
_internalBuffer = (byte *)_surface->pixels;
|
||||
|
||||
// Avoid updating the _externalBuffer if it's flagged as updateNeeded
|
||||
// since the draw-loop might access it then. This way, any late frames
|
||||
// will be dropped, and the sound will continue, in synch.
|
||||
if (!_updateNeeded) {
|
||||
_externalSurface->copyFrom(*_surface);
|
||||
_externalBuffer = (byte *)_externalSurface->pixels;
|
||||
_updateNeeded = true;
|
||||
}
|
||||
|
||||
_movieTime = _binkDecoder->getElapsedTime();
|
||||
_frame = _binkDecoder->getCurFrame();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void BinkPlayer::stop() {
|
||||
deinit();
|
||||
g_grim->setMode(ENGINE_MODE_NORMAL);
|
||||
}
|
||||
|
||||
bool BinkPlayer::play(const char *filename, bool looping, int x, int y) {
|
||||
deinit();
|
||||
_x = x;
|
||||
_y = y;
|
||||
_fname = filename;
|
||||
|
||||
bool BinkPlayer::loadFile(Common::String filename) {
|
||||
// The demo uses a weird .lab suffix instead of the normal .bik
|
||||
_fname += (g_grim->getGameFlags() & ADGF_DEMO) ? ".lab" : ".bik";
|
||||
_fname = filename;
|
||||
_fname += (_demo) ? ".lab" : ".bik";
|
||||
|
||||
if (!_binkDecoder->loadFile(_fname))
|
||||
return false;
|
||||
|
||||
if (gDebugLevel == DEBUG_SMUSH)
|
||||
printf("Playing video '%s'.\n", filename);
|
||||
|
||||
init();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void BinkPlayer::saveState(SaveGame *state) {
|
||||
}
|
||||
|
||||
void BinkPlayer::restoreState(SaveGame *state) {
|
||||
return MoviePlayer::loadFile(_fname);
|
||||
}
|
||||
|
||||
} // end of namespace Grim
|
||||
|
@ -23,46 +23,18 @@
|
||||
#ifndef GRIM_BINK_PLAYER_H
|
||||
#define GRIM_BINK_PLAYER_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/file.h"
|
||||
|
||||
#include "graphics/pixelformat.h"
|
||||
|
||||
#include "audio/mixer.h"
|
||||
#include "audio/audiostream.h"
|
||||
|
||||
#include "engines/grim/movie/movie.h"
|
||||
|
||||
#ifdef USE_BINK
|
||||
|
||||
namespace Video{
|
||||
class BinkDecoder;
|
||||
}
|
||||
|
||||
namespace Graphics{
|
||||
struct Surface;
|
||||
}
|
||||
|
||||
namespace Grim {
|
||||
|
||||
class BinkPlayer : public MoviePlayer {
|
||||
private:
|
||||
Video::BinkDecoder *_binkDecoder;
|
||||
Graphics::Surface *_surface, *_externalSurface;
|
||||
public:
|
||||
BinkPlayer();
|
||||
~BinkPlayer();
|
||||
|
||||
bool play(const char *filename, bool looping, int x, int y);
|
||||
void stop();
|
||||
void saveState(SaveGame *state);
|
||||
void restoreState(SaveGame *state);
|
||||
void deliverFrameFromDecode(int width, int height, uint16 *dat);
|
||||
BinkPlayer(bool demo);
|
||||
private:
|
||||
static void timerCallback(void *ptr);
|
||||
virtual void handleFrame();
|
||||
void init();
|
||||
void deinit();
|
||||
bool loadFile(Common::String filename);
|
||||
bool _demo;
|
||||
};
|
||||
|
||||
} // end of namespace Grim
|
||||
|
565
engines/grim/movie/codecs/smush_decoder.cpp
Normal file
565
engines/grim/movie/codecs/smush_decoder.cpp
Normal file
@ -0,0 +1,565 @@
|
||||
/* Residual - A 3D game interpreter
|
||||
*
|
||||
* Residual is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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.1 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
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/endian.h"
|
||||
#include "common/events.h"
|
||||
#include "common/file.h"
|
||||
#include "common/rational.h"
|
||||
#include "common/system.h"
|
||||
#include "common/timer.h"
|
||||
#include "common/zlib.h"
|
||||
|
||||
#include "audio/audiostream.h"
|
||||
#include "audio/mixer.h"
|
||||
#include "audio/decoders/raw.h"
|
||||
|
||||
#include "engines/grim/debug.h"
|
||||
|
||||
#include "engines/grim/movie/codecs/blocky8.h"
|
||||
#include "engines/grim/movie/codecs/blocky16.h"
|
||||
#include "engines/grim/movie/codecs/smush_decoder.h"
|
||||
|
||||
#ifdef USE_SMUSH
|
||||
|
||||
namespace Grim {
|
||||
|
||||
// Prototypes to avoid depending on grim.h
|
||||
void vimaInit(uint16 *destTable);
|
||||
void decompressVima(const byte *src, int16 *dest, int destLen, uint16 *destTable);
|
||||
|
||||
#define ANNO_HEADER "MakeAnim animation type 'Bl16' parameters: "
|
||||
#define BUFFER_SIZE 16385
|
||||
#define SMUSH_SPEED 66667
|
||||
|
||||
static uint16 smushDestTable[5786];
|
||||
|
||||
SmushDecoder::SmushDecoder() {
|
||||
// Set colour-format statically here for SMUSH (5650), to allow for differing PixelFormat in engine and renderer (and conversion from Surface there)
|
||||
// Which means 16 bpp, 565, shift of 11, 5, 0, 0 for RGBA
|
||||
_format = Graphics::PixelFormat(16, 5, 6, 5, 0, 11, 5, 0, 0);
|
||||
_nbframes = 0;
|
||||
_file = 0;
|
||||
_width = 0;
|
||||
_height = 0;
|
||||
_channels = -1;
|
||||
_freq = 22050;
|
||||
_videoLooping = false;
|
||||
_startPos = 0;
|
||||
_x = 0;
|
||||
_y = 0;
|
||||
_blocky8 = new Blocky8();
|
||||
_blocky16 = new Blocky16();
|
||||
init();
|
||||
}
|
||||
|
||||
SmushDecoder::~SmushDecoder() {
|
||||
delete _blocky8;
|
||||
delete _blocky16;
|
||||
}
|
||||
|
||||
void SmushDecoder::init() {
|
||||
_IACTpos = 0;
|
||||
_stream = NULL;
|
||||
_curFrame = 0;
|
||||
_videoPause = false;
|
||||
|
||||
if (!_demo) {
|
||||
_surface.create(_width, _height, _format);
|
||||
vimaInit(smushDestTable);
|
||||
}
|
||||
}
|
||||
|
||||
void SmushDecoder::close() {
|
||||
_surface.free();
|
||||
if (_stream) {
|
||||
_stream->finish();
|
||||
_stream = NULL;
|
||||
g_system->getMixer()->stopHandle(_soundHandle);
|
||||
}
|
||||
_videoLooping = false;
|
||||
_videoPause = true;
|
||||
if (_file) {
|
||||
delete _file;
|
||||
_file = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void SmushDecoder::handleWave(const byte *src, uint32 size) {
|
||||
int16 *dst = (int16 *) malloc(size * _channels * sizeof(int16));
|
||||
decompressVima(src, dst, size * _channels * 2, smushDestTable);
|
||||
|
||||
int flags = Audio::FLAG_16BITS;
|
||||
if (_channels == 2)
|
||||
flags |= Audio::FLAG_STEREO;
|
||||
|
||||
if (!_stream) {
|
||||
_stream = Audio::makeQueuingAudioStream(_freq, (_channels == 2));
|
||||
g_system->getMixer()->playStream(Audio::Mixer::kMusicSoundType, &_soundHandle, _stream);
|
||||
}
|
||||
if (g_system->getMixer()->isReady()) {
|
||||
_stream->queueBuffer((byte *)dst, size * _channels * 2, DisposeAfterUse::YES, flags);
|
||||
} else {
|
||||
free(dst);
|
||||
}
|
||||
}
|
||||
|
||||
void SmushDecoder::handleFrame() {
|
||||
uint32 tag;
|
||||
int32 size;
|
||||
int pos = 0;
|
||||
|
||||
if (_curFrame == 0)
|
||||
_startTime = g_system->getMillis();
|
||||
|
||||
if (_videoPause)
|
||||
return;
|
||||
|
||||
if (endOfVideo()) { // Looping is handled outside, by rewinding the video.
|
||||
_videoPause = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (_curFrame == _nbframes) {
|
||||
// If the video has been looping and was previously on the last
|
||||
// frame then reset the frame number and the movie time, this
|
||||
// needs to occur at the beginning so the last frame has time to
|
||||
// render appropriately
|
||||
_curFrame = 1;
|
||||
}
|
||||
|
||||
tag = _file->readUint32BE();
|
||||
if (tag == MKTAG('A','N','N','O')) {
|
||||
char *anno;
|
||||
byte *data;
|
||||
|
||||
size = _file->readUint32BE();
|
||||
data = new byte[size];
|
||||
_file->read(data, size);
|
||||
anno = (char *)data;
|
||||
if (strncmp(anno, ANNO_HEADER, sizeof(ANNO_HEADER) - 1) == 0) {
|
||||
//char *annoData = anno + sizeof(ANNO_HEADER);
|
||||
|
||||
// Examples:
|
||||
// Water streaming around boat from Manny's balcony
|
||||
// MakeAnim animation type 'Bl16' parameters: 10000;12000;100;1;0;0;0;0;25;0;
|
||||
// Water in front of the Blue Casket
|
||||
// MakeAnim animation type 'Bl16' parameters: 20000;25000;100;1;0;0;0;0;25;0;
|
||||
// Scrimshaw exterior:
|
||||
// MakeAnim animation type 'Bl16' parameters: 6000;8000;100;0;0;0;0;0;2;0;
|
||||
// Lola engine room (loops a limited number of times?):
|
||||
// MakeAnim animation type 'Bl16' parameters: 6000;8000;90;1;0;0;0;0;2;0;
|
||||
if (gDebugLevel == DEBUG_MOVIE || gDebugLevel == DEBUG_NORMAL || gDebugLevel == DEBUG_ALL)
|
||||
debug("Announcement data: %s\n", anno);
|
||||
// It looks like the announcement data is actually for setting some of the
|
||||
// header parameters, not for any looping purpose
|
||||
} else {
|
||||
if (gDebugLevel == DEBUG_MOVIE || gDebugLevel == DEBUG_NORMAL || gDebugLevel == DEBUG_ALL)
|
||||
debug("Announcement header not understood: %s\n", anno);
|
||||
}
|
||||
delete[] anno;
|
||||
tag = _file->readUint32BE();
|
||||
}
|
||||
|
||||
assert(tag == MKTAG('F','R','M','E'));
|
||||
size = _file->readUint32BE();
|
||||
byte *frame = new byte[size];
|
||||
_file->read(frame, size);
|
||||
|
||||
do {
|
||||
if (READ_BE_UINT32(frame + pos) == MKTAG('B','l','1','6')) {
|
||||
_blocky16->decode((byte *)_surface.pixels, frame + pos + 8);
|
||||
pos += READ_BE_UINT32(frame + pos + 4) + 8;
|
||||
} else if (READ_BE_UINT32(frame + pos) == MKTAG('W','a','v','e')) {
|
||||
int decompressed_size = READ_BE_UINT32(frame + pos + 8);
|
||||
if (decompressed_size < 0)
|
||||
handleWave(frame + pos + 8 + 4 + 8, READ_BE_UINT32(frame + pos + 8 + 8));
|
||||
else
|
||||
handleWave(frame + pos + 8 + 4, decompressed_size);
|
||||
pos += READ_BE_UINT32(frame + pos + 4) + 8;
|
||||
} else if (gDebugLevel == DEBUG_MOVIE || gDebugLevel == DEBUG_ERROR || gDebugLevel == DEBUG_ALL) {
|
||||
error("SmushDecoder::handleFrame() unknown tag");
|
||||
}
|
||||
} while (pos < size);
|
||||
delete[] frame;
|
||||
|
||||
_curFrame++;
|
||||
if (_curFrame == _nbframes) {
|
||||
// If we're not supposed to loop (or looping fails) then end the video
|
||||
if (!_videoLooping || !_file->seek(_startPos, SEEK_SET)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static byte delta_color(byte org_color, int16 delta_color) {
|
||||
int t = (org_color * 129 + delta_color) / 128;
|
||||
return CLIP(t, 0, 255);
|
||||
}
|
||||
|
||||
void SmushDecoder::handleDeltaPalette(byte *src, int32 size) {
|
||||
if (size == 0x300 * 3 + 4) {
|
||||
for (int i = 0; i < 0x300; i++)
|
||||
_deltaPal[i] = READ_LE_UINT16(src + (i * 2) + 4);
|
||||
memcpy(_pal, src + 0x600 + 4, 0x300);
|
||||
} else if (size == 6) {
|
||||
for (int i = 0; i < 0x300; i++)
|
||||
_pal[i] = delta_color(_pal[i], _deltaPal[i]);
|
||||
} else {
|
||||
error("SmushDecoder::handleDeltaPalette() Wrong size for DeltaPalette");
|
||||
}
|
||||
}
|
||||
|
||||
void SmushDecoder::handleIACT(const byte *src, int32 size) {
|
||||
int32 bsize = size - 18;
|
||||
const byte *d_src = src + 18;
|
||||
|
||||
while (bsize > 0) {
|
||||
if (_IACTpos >= 2) {
|
||||
int32 len = READ_BE_UINT16(_IACToutput) + 2;
|
||||
len -= _IACTpos;
|
||||
if (len > bsize) {
|
||||
memcpy(_IACToutput + _IACTpos, d_src, bsize);
|
||||
_IACTpos += bsize;
|
||||
bsize = 0;
|
||||
} else {
|
||||
byte *output_data = new byte[4096];
|
||||
memcpy(_IACToutput + _IACTpos, d_src, len);
|
||||
byte *dst = output_data;
|
||||
byte *d_src2 = _IACToutput;
|
||||
d_src2 += 2;
|
||||
int32 count = 1024;
|
||||
byte variable1 = *d_src2++;
|
||||
byte variable2 = variable1 / 16;
|
||||
variable1 &= 0x0f;
|
||||
do {
|
||||
byte value;
|
||||
value = *(d_src2++);
|
||||
if (value == 0x80) {
|
||||
*dst++ = *d_src2++;
|
||||
*dst++ = *d_src2++;
|
||||
} else {
|
||||
int16 val = (int8)value << variable2;
|
||||
*dst++ = val >> 8;
|
||||
*dst++ = (byte)(val);
|
||||
}
|
||||
value = *(d_src2++);
|
||||
if (value == 0x80) {
|
||||
*dst++ = *d_src2++;
|
||||
*dst++ = *d_src2++;
|
||||
} else {
|
||||
int16 val = (int8)value << variable1;
|
||||
*dst++ = val >> 8;
|
||||
*dst++ = (byte)(val);
|
||||
}
|
||||
} while (--count);
|
||||
|
||||
if (!_stream) {
|
||||
_stream = Audio::makeQueuingAudioStream(22050, true);
|
||||
g_system->getMixer()->playStream(Audio::Mixer::kSFXSoundType, &_soundHandle, _stream);
|
||||
}
|
||||
_stream->queueBuffer(output_data, 0x1000, DisposeAfterUse::YES, Audio::FLAG_STEREO | Audio::FLAG_16BITS);
|
||||
|
||||
bsize -= len;
|
||||
d_src += len;
|
||||
_IACTpos = 0;
|
||||
}
|
||||
} else {
|
||||
if (bsize > 1 && _IACTpos == 0) {
|
||||
*(_IACToutput + 0) = *d_src++;
|
||||
_IACTpos = 1;
|
||||
bsize--;
|
||||
}
|
||||
*(_IACToutput + _IACTpos) = *d_src++;
|
||||
_IACTpos++;
|
||||
bsize--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SmushDecoder::handleFrameDemo() {
|
||||
uint32 tag;
|
||||
int32 size;
|
||||
int pos = 0;
|
||||
|
||||
if (_videoPause)
|
||||
return;
|
||||
|
||||
if (endOfVideo()) {
|
||||
_videoPause = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (_curFrame == 0)
|
||||
_startTime = g_system->getMillis();
|
||||
|
||||
tag = _file->readUint32BE();
|
||||
assert(tag == MKTAG('F','R','M','E'));
|
||||
size = _file->readUint32BE();
|
||||
byte *frame = new byte[size];
|
||||
_file->read(frame, size);
|
||||
|
||||
do {
|
||||
if (READ_BE_UINT32(frame + pos) == MKTAG('F','O','B','J')) {
|
||||
_x = READ_LE_UINT16(frame + pos + 10);
|
||||
_y = READ_LE_UINT16(frame + pos + 12);
|
||||
int width = READ_LE_UINT16(frame + pos + 14);
|
||||
int height = READ_LE_UINT16(frame + pos + 16);
|
||||
if (width != _width || height != _height) {
|
||||
_width = width;
|
||||
_height = height;
|
||||
_surface.create(_width, _height, _format);
|
||||
_blocky8->init(_width, _height);
|
||||
}
|
||||
_blocky8->decode((byte *)_surface.pixels, frame + pos + 8 + 14);
|
||||
pos += READ_BE_UINT32(frame + pos + 4) + 8;
|
||||
} else if (READ_BE_UINT32(frame + pos) == MKTAG('I','A','C','T')) {
|
||||
handleIACT(frame + pos + 8, READ_BE_UINT32(frame + pos + 4));
|
||||
int offset = READ_BE_UINT32(frame + pos + 4) + 8;
|
||||
if (offset & 1)
|
||||
offset += 1;
|
||||
pos += offset;
|
||||
} else if (READ_BE_UINT32(frame + pos) == MKTAG('X','P','A','L')) {
|
||||
handleDeltaPalette(frame + pos + 8, READ_BE_UINT32(frame + pos + 4));
|
||||
pos += READ_BE_UINT32(frame + pos + 4) + 8;
|
||||
} else {
|
||||
error("SmushDecoder::handleFrame() unknown tag");
|
||||
}
|
||||
} while (pos < size);
|
||||
delete[] frame;
|
||||
|
||||
Graphics::Surface conversion;
|
||||
conversion.create(0, 0, _format); // Avoid issues with copyFrom, by creating an empty surface.
|
||||
conversion.copyFrom(_surface);
|
||||
|
||||
uint16 *d = (uint16 *)_surface.pixels;
|
||||
for (int l = 0; l < _width * _height; l++) {
|
||||
int index = ((byte *)conversion.pixels)[l];
|
||||
d[l] = ((_pal[(index * 3) + 0] & 0xF8) << 8) | ((_pal[(index * 3) + 1] & 0xFC) << 3) | (_pal[(index * 3) + 2] >> 3);
|
||||
}
|
||||
conversion.free();
|
||||
|
||||
_curFrame++;
|
||||
}
|
||||
|
||||
void SmushDecoder::handleFramesHeader() {
|
||||
uint32 tag;
|
||||
int32 size;
|
||||
int pos = 0;
|
||||
|
||||
tag = _file->readUint32BE();
|
||||
assert(tag == MKTAG('F','L','H','D'));
|
||||
size = _file->readUint32BE();
|
||||
byte *f_header = new byte[size];
|
||||
_file->read(f_header, size);
|
||||
|
||||
do {
|
||||
if (READ_BE_UINT32(f_header + pos) == MKTAG('B','l','1','6')) {
|
||||
pos += READ_BE_UINT32(f_header + pos + 4) + 8;
|
||||
} else if (READ_BE_UINT32(f_header + pos) == MKTAG('W','a','v','e')) {
|
||||
_freq = READ_LE_UINT32(f_header + pos + 8);
|
||||
_channels = READ_LE_UINT32(f_header + pos + 12);
|
||||
pos += 20;
|
||||
} else {
|
||||
error("SmushDecoder::handleFramesHeader() unknown tag");
|
||||
}
|
||||
} while (pos < size);
|
||||
delete[] f_header;
|
||||
}
|
||||
|
||||
bool SmushDecoder::setupAnimDemo() {
|
||||
uint32 tag;
|
||||
int32 size;
|
||||
|
||||
tag = _file->readUint32BE();
|
||||
assert(tag == MKTAG('A','N','I','M'));
|
||||
size = _file->readUint32BE();
|
||||
|
||||
tag = _file->readUint32BE();
|
||||
assert(tag == MKTAG('A','H','D','R'));
|
||||
size = _file->readUint32BE();
|
||||
|
||||
_file->readUint16BE(); // version
|
||||
_nbframes = _file->readUint16LE();
|
||||
_file->readUint16BE(); // unknown
|
||||
|
||||
for (int l = 0; l < 0x300; l++) {
|
||||
_pal[l] = _file->readByte();
|
||||
}
|
||||
_file->readUint32BE();
|
||||
_file->readUint32BE();
|
||||
_file->readUint32BE();
|
||||
_file->readUint32BE();
|
||||
_file->readUint32BE();
|
||||
|
||||
_x = -1;
|
||||
_y = -1;
|
||||
_width = -1;
|
||||
_height = -1;
|
||||
_videoLooping = false;
|
||||
_startPos = 0;
|
||||
|
||||
setMsPerFrame(SMUSH_SPEED);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SmushDecoder::setupAnim() {
|
||||
uint32 tag;
|
||||
int32 size;
|
||||
int16 flags;
|
||||
|
||||
if (!_file)
|
||||
return false;
|
||||
|
||||
tag = _file->readUint32BE();
|
||||
assert(tag == MKTAG('S','A','N','M'));
|
||||
size = _file->readUint32BE();
|
||||
|
||||
tag = _file->readUint32BE();
|
||||
assert(tag == MKTAG('S','H','D','R'));
|
||||
size = _file->readUint32BE();
|
||||
byte *s_header = new byte[size];
|
||||
_file->read(s_header, size);
|
||||
_nbframes = READ_LE_UINT32(s_header + 2);
|
||||
int width = READ_LE_UINT16(s_header + 8);
|
||||
int height = READ_LE_UINT16(s_header + 10);
|
||||
if (_width != width || _height != height) {
|
||||
_blocky16->init(width, height);
|
||||
}
|
||||
|
||||
_width = width;
|
||||
_height = height;
|
||||
|
||||
// If the video is NOT looping, setLooping will set the speed to the proper value
|
||||
setMsPerFrame(READ_LE_UINT32(s_header + 14));
|
||||
|
||||
flags = READ_LE_UINT16(s_header + 18);
|
||||
// Output information for checking out the flags
|
||||
if (gDebugLevel == DEBUG_MOVIE || gDebugLevel == DEBUG_NORMAL || gDebugLevel == DEBUG_ALL) {
|
||||
warning("SMUSH Flags:");
|
||||
for (int i = 0; i < 16; i++)
|
||||
warning(" %d", (flags & (1 << i)) != 0);
|
||||
//printf("\n");
|
||||
}
|
||||
_videoLooping = true;
|
||||
|
||||
delete[] s_header;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SmushDecoder::loadStream(Common::SeekableReadStream *stream) {
|
||||
close();
|
||||
|
||||
// Load the video
|
||||
if (_demo) {
|
||||
_file = stream;
|
||||
if (!setupAnimDemo())
|
||||
return false;
|
||||
} else {
|
||||
_file = wrapCompressedReadStream(stream);
|
||||
if (!setupAnim())
|
||||
return false;
|
||||
|
||||
handleFramesHeader();
|
||||
}
|
||||
|
||||
_startPos = _file->pos();
|
||||
|
||||
init();
|
||||
if (!_demo)
|
||||
_surface.create(_width, _height, _format);
|
||||
return true;
|
||||
}
|
||||
|
||||
const Graphics::Surface *SmushDecoder::decodeNextFrame() {
|
||||
if (_demo)
|
||||
handleFrameDemo();
|
||||
else
|
||||
handleFrame();
|
||||
|
||||
return &_surface;
|
||||
}
|
||||
|
||||
void SmushDecoder::setLooping(bool l) {
|
||||
_videoLooping = l;
|
||||
|
||||
if (!_videoLooping)
|
||||
setMsPerFrame(SMUSH_SPEED);
|
||||
}
|
||||
|
||||
void SmushDecoder::pauseVideoIntern(bool p) {
|
||||
g_system->getMixer()->pauseHandle(_soundHandle, p);
|
||||
}
|
||||
|
||||
uint32 SmushDecoder::getFrameCount() const {
|
||||
return _nbframes;
|
||||
}
|
||||
|
||||
void SmushDecoder::setMsPerFrame(int ms) {
|
||||
_frameRate = Common::Rational(1000000, ms);
|
||||
}
|
||||
|
||||
void SmushDecoder::seekToTime(Audio::Timestamp time) { // FIXME: This will be off by a second or two right now.
|
||||
int32 wantedFrame = (uint32) ((time.msecs() / 1000.0f) * getFrameRate().toDouble());
|
||||
warning("Seek to time: %d, frame: %d", time.msecs(), wantedFrame);
|
||||
warning("Current frame: %d", _curFrame);
|
||||
uint32 tag;
|
||||
int32 size;
|
||||
|
||||
if (_stream)
|
||||
_stream->finish();
|
||||
|
||||
if (wantedFrame > _nbframes)
|
||||
return;
|
||||
|
||||
if (wantedFrame < _curFrame) {
|
||||
_file->seek(_startPos, SEEK_SET);
|
||||
}
|
||||
|
||||
_videoPause = true;
|
||||
_startTime = g_system->getMillis() - time.msecs(); // This won't be correct, as we should round off to the frame-start.
|
||||
|
||||
while(_curFrame < wantedFrame) {
|
||||
tag = _file->readUint32BE();
|
||||
if (tag == MKTAG('A','N','N','O')) {
|
||||
size = _file->readUint32BE();
|
||||
_file->seek(size, SEEK_CUR);
|
||||
tag = _file->readUint32BE();
|
||||
|
||||
}
|
||||
assert(tag == MKTAG('F','R','M','E'));
|
||||
size = _file->readUint32BE();
|
||||
_file->seek(size, SEEK_CUR);
|
||||
_curFrame++;
|
||||
}
|
||||
|
||||
warning("Seek complete");
|
||||
_videoPause = false;
|
||||
}
|
||||
|
||||
uint32 SmushDecoder::getDuration() const {
|
||||
return (uint32) (getFrameCount() / getFrameRate().toDouble());
|
||||
}
|
||||
|
||||
} // end of namespace Grim
|
||||
|
||||
#endif // USE_SMUSH
|
119
engines/grim/movie/codecs/smush_decoder.h
Normal file
119
engines/grim/movie/codecs/smush_decoder.h
Normal file
@ -0,0 +1,119 @@
|
||||
/* Residual - A 3D game interpreter
|
||||
*
|
||||
* Residual is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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.1 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
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GRIM_SMUSH_DECODER_H
|
||||
#define GRIM_SMUSH_DECODER_H
|
||||
|
||||
#include "common/rational.h"
|
||||
|
||||
#include "audio/mixer.h"
|
||||
|
||||
#include "video/video_decoder.h"
|
||||
|
||||
#include "graphics/surface.h"
|
||||
|
||||
#ifdef USE_SMUSH
|
||||
|
||||
namespace Audio {
|
||||
class QueuingAudioStream;
|
||||
}
|
||||
|
||||
namespace Grim {
|
||||
|
||||
class Blocky8;
|
||||
class Blocky16;
|
||||
|
||||
class SmushDecoder : public virtual Video::SeekableVideoDecoder, public virtual Video::FixedRateVideoDecoder {
|
||||
private:
|
||||
int32 _nbframes;
|
||||
int _width, _height;
|
||||
int _x, _y;
|
||||
Blocky8 *_blocky8;
|
||||
Blocky16 *_blocky16;
|
||||
Common::SeekableReadStream *_file;
|
||||
Common::Rational _frameRate;
|
||||
Graphics::Surface _surface;
|
||||
Graphics::PixelFormat _format;
|
||||
|
||||
byte _pal[0x300];
|
||||
int16 _deltaPal[0x300];
|
||||
byte _IACToutput[4096];
|
||||
int32 _IACTpos;
|
||||
|
||||
Audio::SoundHandle _soundHandle;
|
||||
Audio::QueuingAudioStream *_stream;
|
||||
|
||||
uint32 _startPos;
|
||||
int _channels;
|
||||
int _freq;
|
||||
bool _videoPause;
|
||||
bool _videoLooping;
|
||||
bool _demo;
|
||||
public:
|
||||
SmushDecoder();
|
||||
~SmushDecoder();
|
||||
|
||||
int getX() { return _x; }
|
||||
int getY() { return _y; }
|
||||
void setLooping(bool l);
|
||||
void setDemo(bool demo) { _demo = demo; }
|
||||
|
||||
uint16 getWidth() const { return _width; }
|
||||
uint16 getHeight() const { return _height; }
|
||||
|
||||
Graphics::PixelFormat getPixelFormat() const { return _surface.format; }
|
||||
bool isVideoLoaded() const { return _file != 0; }
|
||||
|
||||
bool loadStream(Common::SeekableReadStream *stream);
|
||||
const Graphics::Surface *decodeNextFrame();
|
||||
|
||||
uint32 getFrameCount() const;
|
||||
void close();
|
||||
|
||||
// Seekable
|
||||
void seekToTime(Audio::Timestamp time);
|
||||
uint32 getDuration() const;
|
||||
|
||||
private:
|
||||
void pauseVideoIntern(bool p);
|
||||
void parseNextFrame();
|
||||
void init();
|
||||
void handleDeltaPalette(byte *src, int32 size);
|
||||
void handleFramesHeader();
|
||||
void handleFrameDemo();
|
||||
void handleFrame();
|
||||
void handleBlocky16(byte *src);
|
||||
void handleWave(const byte *src, uint32 size);
|
||||
void handleIACT(const byte *src, int32 size);
|
||||
bool setupAnim();
|
||||
bool setupAnimDemo();
|
||||
void setMsPerFrame(int ms);
|
||||
protected:
|
||||
// Fixed Rate:
|
||||
Common::Rational getFrameRate() const { return _frameRate; }
|
||||
};
|
||||
|
||||
} // end of namespace Grim
|
||||
|
||||
#endif // USE_SMUSH
|
||||
|
||||
#endif
|
@ -8,35 +8,192 @@
|
||||
* 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.
|
||||
|
||||
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
#include "engines/grim/movie/movie.h"
|
||||
#include "graphics/surface.h"
|
||||
|
||||
#if !defined(USE_MPEG2) || !defined(USE_SMUSH) || !defined(USE_BINK)
|
||||
#define NEED_NULLPLAYER
|
||||
#endif
|
||||
#include "common/system.h"
|
||||
#include "common/timer.h"
|
||||
|
||||
#include "engines/grim/movie/movie.h"
|
||||
#include "engines/grim/grim.h"
|
||||
#include "engines/grim/debug.h"
|
||||
#include "engines/grim/savegame.h"
|
||||
|
||||
namespace Grim {
|
||||
|
||||
MoviePlayer *g_movie;
|
||||
|
||||
void MoviePlayer::pause(bool p) {
|
||||
_videoPause = p;
|
||||
g_system->getMixer()->pauseHandle(_soundHandle, p);
|
||||
MoviePlayer::MoviePlayer() {
|
||||
_speed = 0;
|
||||
_channels = -1;
|
||||
_freq = 22050;
|
||||
_videoFinished = false;
|
||||
_videoLooping = false;
|
||||
_videoPause = true;
|
||||
_updateNeeded = false;
|
||||
_movieTime = 0;
|
||||
_frame = 0;
|
||||
_x = 0;
|
||||
_y = 0;
|
||||
_videoDecoder = NULL;
|
||||
_surface = new Graphics::Surface();
|
||||
_externalSurface = new Graphics::Surface();
|
||||
}
|
||||
|
||||
// Fallback for when USE_MPEG2 isnt defined, might want to do something similar
|
||||
// for USE_BINK if that comes over from ScummVM
|
||||
MoviePlayer::~MoviePlayer() {
|
||||
deinit();
|
||||
delete _videoDecoder;
|
||||
}
|
||||
|
||||
void MoviePlayer::pause(bool p) {
|
||||
_videoPause = p;
|
||||
_videoDecoder->pauseVideo(p);
|
||||
}
|
||||
|
||||
void MoviePlayer::stop() {
|
||||
deinit();
|
||||
g_grim->setMode(ENGINE_MODE_NORMAL);
|
||||
}
|
||||
|
||||
void MoviePlayer::timerCallback(void *) {
|
||||
g_movie->_frameMutex.lock();
|
||||
if (g_movie->prepareFrame())
|
||||
g_movie->handleFrame();
|
||||
g_movie->_frameMutex.unlock();
|
||||
}
|
||||
|
||||
bool MoviePlayer::prepareFrame() {
|
||||
if (_videoDecoder->endOfVideo())
|
||||
_videoFinished = true;
|
||||
|
||||
if (_videoPause)
|
||||
return false;
|
||||
|
||||
if (_videoFinished) {
|
||||
g_grim->setMode(ENGINE_MODE_NORMAL);
|
||||
_videoPause = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_videoDecoder->getTimeToNextFrame() > 0)
|
||||
return false;
|
||||
|
||||
_surface->copyFrom(*_videoDecoder->decodeNextFrame());
|
||||
|
||||
// Avoid updating the _externalBuffer if it's flagged as updateNeeded
|
||||
// since the draw-loop might access it then. This way, any late frames
|
||||
// will be dropped, and the sound will continue, in synch.
|
||||
if (!_updateNeeded) {
|
||||
_externalSurface->copyFrom(*_surface);
|
||||
_updateNeeded = true;
|
||||
}
|
||||
|
||||
_movieTime = _videoDecoder->getElapsedTime();
|
||||
_frame = _videoDecoder->getCurFrame();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Graphics::Surface *MoviePlayer::getDstSurface() {
|
||||
return _externalSurface;
|
||||
}
|
||||
|
||||
void MoviePlayer::init() {
|
||||
_frame = 0;
|
||||
_movieTime = 0;
|
||||
_updateNeeded = false;
|
||||
_videoFinished = false;
|
||||
|
||||
g_system->getTimerManager()->installTimerProc(&timerCallback, _speed, NULL);
|
||||
}
|
||||
|
||||
void MoviePlayer::deinit() {
|
||||
_frameMutex.unlock();
|
||||
g_system->getTimerManager()->removeTimerProc(&timerCallback);
|
||||
_videoDecoder->close();
|
||||
_surface->free();
|
||||
_externalSurface->free();
|
||||
|
||||
_videoPause = false;
|
||||
_videoFinished = true;
|
||||
}
|
||||
|
||||
bool MoviePlayer::play(Common::String filename, bool looping, int x, int y) {
|
||||
deinit();
|
||||
_x = x;
|
||||
_y = y;
|
||||
_fname = filename;
|
||||
_videoLooping = looping;
|
||||
|
||||
if (!loadFile(_fname))
|
||||
return false;
|
||||
|
||||
if (gDebugLevel == DEBUG_MOVIE)
|
||||
warning("Playing video '%s'.\n", filename.c_str());
|
||||
|
||||
init();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MoviePlayer::loadFile(Common::String filename) {
|
||||
return _videoDecoder->loadFile(filename);
|
||||
}
|
||||
|
||||
void MoviePlayer::saveState(SaveGame *state) {
|
||||
state->beginSection('SMUS');
|
||||
|
||||
state->writeString(_fname);
|
||||
|
||||
state->writeLESint32(_frame);
|
||||
state->writeFloat(_movieTime);
|
||||
state->writeLESint32(_videoFinished);
|
||||
state->writeLESint32(_videoLooping);
|
||||
|
||||
state->writeLESint32(_x);
|
||||
state->writeLESint32(_y);
|
||||
|
||||
state->endSection();
|
||||
}
|
||||
|
||||
void MoviePlayer::restoreState(SaveGame *state) {
|
||||
state->beginSection('SMUS');
|
||||
|
||||
_fname = state->readString();
|
||||
|
||||
int32 frame = state->readLESint32();
|
||||
float movieTime = state->readFloat();
|
||||
bool videoFinished = state->readLESint32();
|
||||
bool videoLooping = state->readLESint32();
|
||||
|
||||
int x = state->readLESint32();
|
||||
int y = state->readLESint32();
|
||||
|
||||
if (!videoFinished) {
|
||||
play(_fname.c_str(), videoLooping, x, y);
|
||||
}
|
||||
_frame = frame;
|
||||
_movieTime = movieTime;
|
||||
|
||||
state->endSection();
|
||||
}
|
||||
|
||||
#if !defined(USE_MPEG2) || !defined(USE_SMUSH) || !defined(USE_BINK)
|
||||
#define NEED_NULLPLAYER
|
||||
#endif
|
||||
|
||||
// Fallback for when USE_MPEG2 / USE_BINK / USE_SMUSH isnt defined
|
||||
|
||||
#ifdef NEED_NULLPLAYER
|
||||
class NullPlayer : public MoviePlayer {
|
||||
@ -44,7 +201,7 @@ public:
|
||||
NullPlayer(const char* codecID) {
|
||||
warning("%s-playback not compiled in, but needed", codecID);
|
||||
_videoFinished = true; // Rigs all movies to be completed.
|
||||
}
|
||||
}
|
||||
~NullPlayer() {}
|
||||
bool play(const char* filename, bool looping, int x, int y) {return true;}
|
||||
void stop() {}
|
||||
|
@ -23,31 +23,21 @@
|
||||
#ifndef GRIM_MOVIE_PLAYER_H
|
||||
#define GRIM_MOVIE_PLAYER_H
|
||||
|
||||
#include <zlib.h>
|
||||
|
||||
#include "common/file.h"
|
||||
#include "common/mutex.h"
|
||||
#include "common/system.h"
|
||||
|
||||
#include "audio/mixer.h"
|
||||
#include "audio/audiostream.h"
|
||||
#include "video/video_decoder.h"
|
||||
|
||||
namespace Grim {
|
||||
|
||||
class SaveGame;
|
||||
|
||||
struct SavePos {
|
||||
uint32 filePos;
|
||||
z_stream streamBuf;
|
||||
byte *tmpBuf;
|
||||
};
|
||||
|
||||
class MoviePlayer {
|
||||
protected:
|
||||
Common::File _f;
|
||||
Common::String _fname;
|
||||
Audio::SoundHandle _soundHandle;
|
||||
Audio::QueuingAudioStream *_stream;
|
||||
|
||||
Common::Mutex _frameMutex;
|
||||
Video::VideoDecoder *_videoDecoder; //< Initialize this to your needed subclass of VideoDecoder in the constructor
|
||||
Graphics::Surface *_surface, *_externalSurface;
|
||||
int32 _frame;
|
||||
bool _updateNeeded;
|
||||
int32 _speed;
|
||||
@ -57,63 +47,105 @@ protected:
|
||||
bool _videoFinished;
|
||||
bool _videoPause;
|
||||
bool _videoLooping;
|
||||
struct SavePos *_startPos;
|
||||
int _x, _y;
|
||||
int _width, _height;
|
||||
byte *_internalBuffer, *_externalBuffer;
|
||||
|
||||
public:
|
||||
MoviePlayer() {
|
||||
_internalBuffer = NULL;
|
||||
_externalBuffer = NULL;
|
||||
_width = 0;
|
||||
_height = 0;
|
||||
_speed = 0;
|
||||
_channels = -1;
|
||||
_freq = 22050;
|
||||
_videoFinished = false;
|
||||
_videoLooping = false;
|
||||
_videoPause = true;
|
||||
_updateNeeded = false;
|
||||
_startPos = NULL;
|
||||
_stream = NULL;
|
||||
_movieTime = 0;
|
||||
_frame = 0;
|
||||
_x = 0;
|
||||
_y = 0;
|
||||
};
|
||||
virtual ~MoviePlayer() {}
|
||||
MoviePlayer();
|
||||
virtual ~MoviePlayer();
|
||||
|
||||
virtual bool play(const char *filename, bool looping, int x, int y) = 0;
|
||||
virtual void stop() = 0;
|
||||
/**
|
||||
* Loads a file for playing, and starts playing it.
|
||||
* the default implementation calls init()/deinit() to handle
|
||||
* any necessary setup.
|
||||
*
|
||||
* @param filename the file to open
|
||||
* @param looping true if we want the video to loop, false otherwise
|
||||
* @param x the x-coordinate for the draw-position
|
||||
* @param y the y-coordinate for the draw-position
|
||||
* @see init
|
||||
* @see stop
|
||||
*/
|
||||
virtual bool play(Common::String filename, bool looping, int x, int y);
|
||||
virtual void stop();
|
||||
virtual void pause(bool p);
|
||||
virtual bool isPlaying() { return !_videoFinished; }
|
||||
virtual bool isUpdateNeeded() { return _updateNeeded; }
|
||||
virtual byte *getDstPtr() { return _externalBuffer; }
|
||||
virtual Graphics::Surface *getDstSurface();
|
||||
virtual int getX() { return _x; }
|
||||
virtual int getY() { return _y; }
|
||||
virtual int getWidth() {return _width; }
|
||||
virtual int getHeight() { return _height; }
|
||||
virtual int getFrame() { return _frame; }
|
||||
virtual void clearUpdateNeeded() { _updateNeeded = false; }
|
||||
virtual int32 getMovieTime() { return (int32)_movieTime; }
|
||||
|
||||
virtual void saveState(SaveGame *state) = 0;
|
||||
virtual void restoreState(SaveGame *state) = 0;
|
||||
/**
|
||||
* Saves the state of the video to a savegame
|
||||
*
|
||||
* If you overload this in a subclass, call this first thing in the
|
||||
* overloaded function
|
||||
*
|
||||
* @param state the state to save to
|
||||
*/
|
||||
virtual void saveState(SaveGame *state);
|
||||
virtual void restoreState(SaveGame *state);
|
||||
|
||||
protected:
|
||||
static void timerCallback(void *ptr);
|
||||
virtual void handleFrame() = 0;
|
||||
virtual void init() = 0;
|
||||
virtual void deinit() = 0;
|
||||
/**
|
||||
* Handles basic stuff per frame, like copying the latest frame to
|
||||
* _externalBuffer, and updating the frame-counters.
|
||||
*
|
||||
* @return false if a frame wasnt drawn to _externalBuffer, true otherwise.
|
||||
* @see handleFrame
|
||||
*/
|
||||
virtual bool prepareFrame();
|
||||
|
||||
/**
|
||||
* Frame-handling function.
|
||||
*
|
||||
* Perform any codec-specific per-frame operations after prepareFrame has been
|
||||
* run, this function is called whenever prepareFrame returns true.
|
||||
*
|
||||
* @see prepareFrame
|
||||
* @see clearUpdateNeeded
|
||||
* @see isUpdateNeeded
|
||||
*/
|
||||
virtual void handleFrame() {};
|
||||
|
||||
/**
|
||||
* Initialization of buffers
|
||||
* This function is called by the default-implementation of play,
|
||||
* and is expected to get the necessary datastructures set up for
|
||||
* playback, as well as initializing the callback.
|
||||
*
|
||||
* @see deinit
|
||||
*/
|
||||
virtual void init();
|
||||
|
||||
/**
|
||||
* Closes any file/codec-handles, and resets the movie-state to
|
||||
* a blank MoviePlayer.
|
||||
*
|
||||
* @see init
|
||||
*/
|
||||
virtual void deinit();
|
||||
|
||||
/**
|
||||
* Loads a file for playback, any additional setup is not done here, but in
|
||||
* the play-function. This function is supposed to handle any specifics w.r.t.
|
||||
* files vs containers (i.e. load from LAB vs load from file).
|
||||
*
|
||||
* @see play
|
||||
* @param filename The filename to be handled.
|
||||
*/
|
||||
virtual bool loadFile(Common::String filename);
|
||||
};
|
||||
|
||||
|
||||
// Factory-like functions:
|
||||
|
||||
MoviePlayer *CreateMpegPlayer();
|
||||
MoviePlayer *CreateSmushPlayer();
|
||||
MoviePlayer *CreateBinkPlayer();
|
||||
MoviePlayer *CreateSmushPlayer(bool demo);
|
||||
MoviePlayer *CreateBinkPlayer(bool demo);
|
||||
extern MoviePlayer *g_movie;
|
||||
|
||||
} // end of namespace Grim
|
||||
|
@ -38,16 +38,18 @@
|
||||
#include "audio/mixer.h"
|
||||
#include "audio/decoders/raw.h"
|
||||
|
||||
#include "graphics/surface.h"
|
||||
|
||||
#include "engines/grim/movie/mpeg.h"
|
||||
|
||||
#include "engines/grim/debug.h"
|
||||
#include "engines/grim/grim.h"
|
||||
|
||||
#ifdef USE_MPEG2
|
||||
|
||||
#define MWIDTH 640
|
||||
#define MHEIGHT 400
|
||||
|
||||
#ifdef USE_MPEG2
|
||||
|
||||
namespace Grim {
|
||||
|
||||
MoviePlayer *CreateMpegPlayer() {
|
||||
@ -67,49 +69,25 @@ protected:
|
||||
}
|
||||
};
|
||||
|
||||
void MpegPlayer::timerCallback(void *) {
|
||||
((MpegPlayer*)g_movie)->handleFrame();
|
||||
}
|
||||
|
||||
MpegPlayer::MpegPlayer() : MoviePlayer() {
|
||||
g_movie = this;
|
||||
_speed = 50;
|
||||
_videoBase = new MpegHandler(this, g_system, MWIDTH, MHEIGHT);
|
||||
}
|
||||
|
||||
MpegPlayer::~MpegPlayer() {
|
||||
deinit();
|
||||
}
|
||||
|
||||
void MpegPlayer::init() {
|
||||
_frame = 0;
|
||||
_movieTime = 0;
|
||||
_videoPause = false;
|
||||
_updateNeeded = false;
|
||||
_width = MWIDTH;
|
||||
_height = MHEIGHT;
|
||||
MoviePlayer::init();
|
||||
|
||||
assert(!_externalBuffer);
|
||||
// FIXME, deal with pixelformat differently when we get this properly tested.
|
||||
Graphics::PixelFormat format = Graphics::PixelFormat(16, 5, 6, 5, 0, 11, 5, 0, 0);
|
||||
_externalSurface->create(MWIDTH, MHEIGHT, format);
|
||||
|
||||
_externalBuffer = new byte[_width * _height * 2];
|
||||
|
||||
warning("Trying to play %s",_fname.c_str());
|
||||
_videoBase->init(_fname.c_str());
|
||||
g_system->getTimerManager()->installTimerProc(&timerCallback, _speed, NULL);
|
||||
}
|
||||
|
||||
void MpegPlayer::deinit() {
|
||||
g_system->getTimerManager()->removeTimerProc(&timerCallback);
|
||||
|
||||
if (_externalBuffer) {
|
||||
delete[] _externalBuffer;
|
||||
_externalBuffer = NULL;
|
||||
}
|
||||
if (_videoLooping && _startPos) {
|
||||
delete[] _startPos->tmpBuf;
|
||||
delete[] _startPos;
|
||||
_startPos = NULL;
|
||||
}
|
||||
if (_stream) {
|
||||
_stream->finish();
|
||||
_stream = NULL;
|
||||
@ -133,32 +111,14 @@ void MpegPlayer::handleFrame() {
|
||||
}
|
||||
|
||||
void MpegPlayer::deliverFrameFromDecode(int width, int height, uint16 *dat) {
|
||||
memcpy(_externalBuffer, dat, _width * _height * 2);
|
||||
memcpy(_externalSurface->pixels, dat, _externalSurface->w * _externalSurface->h * 2);
|
||||
_frame++;
|
||||
_updateNeeded = true;
|
||||
}
|
||||
|
||||
void MpegPlayer::stop() {
|
||||
deinit();
|
||||
g_grim->setMode(ENGINE_MODE_NORMAL);
|
||||
}
|
||||
|
||||
bool MpegPlayer::play(const char *filename, bool looping, int x, int y) {
|
||||
deinit();
|
||||
_fname = filename;
|
||||
|
||||
if (gDebugLevel == DEBUG_SMUSH)
|
||||
printf("Playing video '%s'.\n", filename);
|
||||
|
||||
init();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void MpegPlayer::saveState(SaveGame *state) {
|
||||
}
|
||||
|
||||
void MpegPlayer::restoreState(SaveGame *state) {
|
||||
bool MpegPlayer::loadFile(Common::String filename) {
|
||||
_videoBase->init(_fname.c_str());
|
||||
return true; // FIXME
|
||||
}
|
||||
|
||||
} // end of namespace Grim
|
||||
|
@ -41,23 +41,18 @@ namespace Grim {
|
||||
|
||||
class MpegPlayer : public MoviePlayer {
|
||||
private:
|
||||
Common::File _f;
|
||||
Video::BaseAnimationState *_videoBase;
|
||||
|
||||
Audio::SoundHandle _soundHandle;
|
||||
Audio::QueuingAudioStream *_stream;
|
||||
public:
|
||||
MpegPlayer();
|
||||
virtual ~MpegPlayer();
|
||||
|
||||
virtual bool play(const char *filename, bool looping, int x, int y);
|
||||
virtual void stop();
|
||||
virtual void saveState(SaveGame *state);
|
||||
virtual void restoreState(SaveGame *state);
|
||||
void deliverFrameFromDecode(int width, int height, uint16 *dat);
|
||||
private:
|
||||
static void timerCallback(void *ptr);
|
||||
virtual void handleFrame();
|
||||
void handleFrame();
|
||||
void init();
|
||||
void deinit();
|
||||
bool loadFile(Common::String filename);
|
||||
};
|
||||
|
||||
} // end of namespace Grim
|
||||
|
@ -20,572 +20,72 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#define FORBIDDEN_SYMBOL_EXCEPTION_printf
|
||||
#define FORBIDDEN_SYMBOL_EXCEPTION_chdir
|
||||
#define FORBIDDEN_SYMBOL_EXCEPTION_getcwd
|
||||
#define FORBIDDEN_SYMBOL_EXCEPTION_getwd
|
||||
#define FORBIDDEN_SYMBOL_EXCEPTION_mkdir
|
||||
#define FORBIDDEN_SYMBOL_EXCEPTION_unlink
|
||||
|
||||
#include "common/endian.h"
|
||||
#include "common/timer.h"
|
||||
#include "common/file.h"
|
||||
#include "common/events.h"
|
||||
#include "common/system.h"
|
||||
#include "common/mutex.h"
|
||||
|
||||
#include "audio/audiostream.h"
|
||||
#include "audio/mixer.h"
|
||||
#include "audio/decoders/raw.h"
|
||||
|
||||
#include "engines/grim/movie/codecs/smush_decoder.h"
|
||||
#include "engines/grim/movie/smush.h"
|
||||
#include "engines/grim/movie/codecs/vima.h"
|
||||
|
||||
#include "engines/grim/debug.h"
|
||||
#include "engines/grim/grim.h"
|
||||
#include "engines/grim/resource.h"
|
||||
#include "engines/grim/savegame.h"
|
||||
#include "engines/grim/grim.h"
|
||||
|
||||
#ifdef USE_SMUSH
|
||||
|
||||
namespace Grim {
|
||||
|
||||
#define ANNO_HEADER "MakeAnim animation type 'Bl16' parameters: "
|
||||
#define BUFFER_SIZE 16385
|
||||
|
||||
static uint16 smushDestTable[5786];
|
||||
MoviePlayer *CreateSmushPlayer() {
|
||||
return new SmushPlayer();
|
||||
MoviePlayer *CreateSmushPlayer(bool demo) {
|
||||
return new SmushPlayer(demo);
|
||||
}
|
||||
|
||||
void SmushPlayer::timerCallback(void *) {
|
||||
SmushPlayer *smush = static_cast<SmushPlayer *>(g_movie);
|
||||
// Use a mutex to protect against multiple threads running handleFrame
|
||||
// at the same time.
|
||||
Common::StackLock lock(smush->_frameMutex);
|
||||
SmushPlayer::SmushPlayer(bool demo) : MoviePlayer(), _demo(demo) {
|
||||
_speed = 5000;
|
||||
_videoDecoder = new Grim::SmushDecoder();
|
||||
getDecoder()->setDemo(_demo);
|
||||
}
|
||||
|
||||
if (g_grim->getGameFlags() & ADGF_DEMO)
|
||||
smush->handleFrameDemo();
|
||||
bool SmushPlayer::loadFile(Common::String filename) {
|
||||
if (!_demo)
|
||||
return _videoDecoder->loadStream(g_resourceloader->openNewSubStreamFile(filename.c_str()));
|
||||
else
|
||||
smush->handleFrame();
|
||||
return _videoDecoder->loadFile(filename);
|
||||
}
|
||||
|
||||
SmushPlayer::SmushPlayer() {
|
||||
g_movie = this;
|
||||
_IACTpos = 0;
|
||||
_nbframes = 0;
|
||||
_file = 0;
|
||||
}
|
||||
|
||||
SmushPlayer::~SmushPlayer() {
|
||||
deinit();
|
||||
SmushDecoder* SmushPlayer::getDecoder() {
|
||||
return dynamic_cast<SmushDecoder*>(_videoDecoder);
|
||||
}
|
||||
|
||||
void SmushPlayer::init() {
|
||||
_IACTpos = 0;
|
||||
_stream = NULL;
|
||||
_frame = 0;
|
||||
_movieTime = 0;
|
||||
_videoFinished = false;
|
||||
_videoPause = false;
|
||||
_updateNeeded = false;
|
||||
|
||||
assert(!_internalBuffer);
|
||||
assert(!_externalBuffer);
|
||||
|
||||
if (!(g_grim->getGameFlags() & ADGF_DEMO)) {
|
||||
_internalBuffer = new byte[_width * _height * 2];
|
||||
_externalBuffer = new byte[_width * _height * 2];
|
||||
vimaInit(smushDestTable);
|
||||
}
|
||||
g_system->getTimerManager()->installTimerProc(&timerCallback, _speed, NULL);
|
||||
// Get a valid frame to draw
|
||||
timerCallback(0);
|
||||
}
|
||||
|
||||
void SmushPlayer::deinit() {
|
||||
g_system->getTimerManager()->removeTimerProc(&timerCallback);
|
||||
|
||||
if (_internalBuffer) {
|
||||
delete[] _internalBuffer;
|
||||
_internalBuffer = NULL;
|
||||
}
|
||||
if (_externalBuffer) {
|
||||
delete[] _externalBuffer;
|
||||
_externalBuffer = NULL;
|
||||
}
|
||||
if (_videoLooping && _startPos) {
|
||||
delete[] _startPos->tmpBuf;
|
||||
delete _startPos;
|
||||
_startPos = NULL;
|
||||
}
|
||||
if (_stream) {
|
||||
_stream->finish();
|
||||
_stream = NULL;
|
||||
g_system->getMixer()->stopHandle(_soundHandle);
|
||||
}
|
||||
_videoLooping = false;
|
||||
_videoFinished = true;
|
||||
_videoPause = true;
|
||||
if (g_grim->getGameFlags() & ADGF_DEMO)
|
||||
_f.close();
|
||||
else if (_file) {
|
||||
delete _file;
|
||||
_file = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void SmushPlayer::handleWave(const byte *src, uint32 size) {
|
||||
int16 *dst = (int16 *) malloc(size * _channels * sizeof(int16));
|
||||
decompressVima(src, dst, size * _channels * 2, smushDestTable);
|
||||
|
||||
int flags = Audio::FLAG_16BITS;
|
||||
if (_channels == 2)
|
||||
flags |= Audio::FLAG_STEREO;
|
||||
|
||||
if (!_stream) {
|
||||
_stream = Audio::makeQueuingAudioStream(_freq, (_channels == 2));
|
||||
g_system->getMixer()->playStream(Audio::Mixer::kMusicSoundType, &_soundHandle, _stream);
|
||||
}
|
||||
if (g_system->getMixer()->isReady()) {
|
||||
_stream->queueBuffer((byte *)dst, size * _channels * 2, DisposeAfterUse::YES, flags);
|
||||
if (_demo) {
|
||||
_x = getDecoder()->getX();
|
||||
_y = getDecoder()->getY();
|
||||
} else {
|
||||
free(dst);
|
||||
getDecoder()->setLooping(_videoLooping);
|
||||
}
|
||||
MoviePlayer::init();
|
||||
}
|
||||
|
||||
void SmushPlayer::handleFrame() {
|
||||
uint32 tag;
|
||||
int32 size;
|
||||
int pos = 0;
|
||||
|
||||
if (_videoPause)
|
||||
return;
|
||||
|
||||
if (_videoFinished) {
|
||||
_videoPause = true;
|
||||
return;
|
||||
if (_demo) {
|
||||
_x = getDecoder()->getX();
|
||||
_y = getDecoder()->getY();
|
||||
}
|
||||
|
||||
if (_frame == _nbframes) {
|
||||
// If the video has been looping and was previously on the last
|
||||
// frame then reset the frame number and the movie time, this
|
||||
// needs to occur at the beginning so the last frame has time to
|
||||
// render appropriately
|
||||
_frame = 1;
|
||||
_movieTime = 0;
|
||||
}
|
||||
|
||||
tag = _file->readUint32BE();
|
||||
if (tag == MKTAG('A','N','N','O')) {
|
||||
char *anno;
|
||||
byte *data;
|
||||
|
||||
size = _file->readUint32BE();
|
||||
data = new byte[size];
|
||||
_file->read(data, size);
|
||||
anno = (char *)data;
|
||||
if (strncmp(anno, ANNO_HEADER, sizeof(ANNO_HEADER) - 1) == 0) {
|
||||
//char *annoData = anno + sizeof(ANNO_HEADER);
|
||||
|
||||
// Examples:
|
||||
// Water streaming around boat from Manny's balcony
|
||||
// MakeAnim animation type 'Bl16' parameters: 10000;12000;100;1;0;0;0;0;25;0;
|
||||
// Water in front of the Blue Casket
|
||||
// MakeAnim animation type 'Bl16' parameters: 20000;25000;100;1;0;0;0;0;25;0;
|
||||
// Scrimshaw exterior:
|
||||
// MakeAnim animation type 'Bl16' parameters: 6000;8000;100;0;0;0;0;0;2;0;
|
||||
// Lola engine room (loops a limited number of times?):
|
||||
// MakeAnim animation type 'Bl16' parameters: 6000;8000;90;1;0;0;0;0;2;0;
|
||||
if (gDebugLevel == DEBUG_SMUSH || gDebugLevel == DEBUG_NORMAL || gDebugLevel == DEBUG_ALL)
|
||||
debug("Announcement data: %s\n", anno);
|
||||
// It looks like the announcement data is actually for setting some of the
|
||||
// header parameters, not for any looping purpose
|
||||
} else {
|
||||
if (gDebugLevel == DEBUG_SMUSH || gDebugLevel == DEBUG_NORMAL || gDebugLevel == DEBUG_ALL)
|
||||
debug("Announcement header not understood: %s\n", anno);
|
||||
}
|
||||
delete[] anno;
|
||||
tag = _file->readUint32BE();
|
||||
}
|
||||
|
||||
assert(tag == MKTAG('F','R','M','E'));
|
||||
size = _file->readUint32BE();
|
||||
byte *frame = new byte[size];
|
||||
_file->read(frame, size);
|
||||
|
||||
do {
|
||||
if (READ_BE_UINT32(frame + pos) == MKTAG('B','l','1','6')) {
|
||||
_blocky16.decode(_internalBuffer, frame + pos + 8);
|
||||
pos += READ_BE_UINT32(frame + pos + 4) + 8;
|
||||
} else if (READ_BE_UINT32(frame + pos) == MKTAG('W','a','v','e')) {
|
||||
int decompressed_size = READ_BE_UINT32(frame + pos + 8);
|
||||
if (decompressed_size < 0)
|
||||
handleWave(frame + pos + 8 + 4 + 8, READ_BE_UINT32(frame + pos + 8 + 8));
|
||||
else
|
||||
handleWave(frame + pos + 8 + 4, decompressed_size);
|
||||
pos += READ_BE_UINT32(frame + pos + 4) + 8;
|
||||
} else if (gDebugLevel == DEBUG_SMUSH || gDebugLevel == DEBUG_ERROR || gDebugLevel == DEBUG_ALL) {
|
||||
error("SmushPlayer::handleFrame() unknown tag");
|
||||
}
|
||||
} while (pos < size);
|
||||
delete[] frame;
|
||||
|
||||
memcpy(_externalBuffer, _internalBuffer, _width * _height * 2);
|
||||
_updateNeeded = true;
|
||||
|
||||
_frame++;
|
||||
_movieTime += _speed / 1000.f;
|
||||
if (_frame == _nbframes) {
|
||||
if (_videoDecoder->endOfVideo()) {
|
||||
// If we're not supposed to loop (or looping fails) then end the video
|
||||
if (!_videoLooping || !_file->seek(_startPos->filePos, SEEK_SET)) {
|
||||
if (!_videoLooping ) {
|
||||
_videoFinished = true;
|
||||
g_grim->setMode(ENGINE_MODE_NORMAL);
|
||||
deinit();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static byte delta_color(byte org_color, int16 delta_color) {
|
||||
int t = (org_color * 129 + delta_color) / 128;
|
||||
return CLIP(t, 0, 255);
|
||||
}
|
||||
|
||||
void SmushPlayer::handleDeltaPalette(byte *src, int32 size) {
|
||||
if (size == 0x300 * 3 + 4) {
|
||||
for (int i = 0; i < 0x300; i++)
|
||||
_deltaPal[i] = READ_LE_UINT16(src + (i * 2) + 4);
|
||||
memcpy(_pal, src + 0x600 + 4, 0x300);
|
||||
} else if (size == 6) {
|
||||
for (int i = 0; i < 0x300; i++)
|
||||
_pal[i] = delta_color(_pal[i], _deltaPal[i]);
|
||||
} else {
|
||||
error("SmushPlayer::handleDeltaPalette() Wrong size for DeltaPalette");
|
||||
}
|
||||
}
|
||||
|
||||
void SmushPlayer::handleIACT(const byte *src, int32 size) {
|
||||
int32 bsize = size - 18;
|
||||
const byte *d_src = src + 18;
|
||||
|
||||
while (bsize > 0) {
|
||||
if (_IACTpos >= 2) {
|
||||
int32 len = READ_BE_UINT16(_IACToutput) + 2;
|
||||
len -= _IACTpos;
|
||||
if (len > bsize) {
|
||||
memcpy(_IACToutput + _IACTpos, d_src, bsize);
|
||||
_IACTpos += bsize;
|
||||
bsize = 0;
|
||||
} else {
|
||||
byte *output_data = new byte[4096];
|
||||
memcpy(_IACToutput + _IACTpos, d_src, len);
|
||||
byte *dst = output_data;
|
||||
byte *d_src2 = _IACToutput;
|
||||
d_src2 += 2;
|
||||
int32 count = 1024;
|
||||
byte variable1 = *d_src2++;
|
||||
byte variable2 = variable1 / 16;
|
||||
variable1 &= 0x0f;
|
||||
do {
|
||||
byte value;
|
||||
value = *(d_src2++);
|
||||
if (value == 0x80) {
|
||||
*dst++ = *d_src2++;
|
||||
*dst++ = *d_src2++;
|
||||
} else {
|
||||
int16 val = (int8)value << variable2;
|
||||
*dst++ = val >> 8;
|
||||
*dst++ = (byte)(val);
|
||||
}
|
||||
value = *(d_src2++);
|
||||
if (value == 0x80) {
|
||||
*dst++ = *d_src2++;
|
||||
*dst++ = *d_src2++;
|
||||
} else {
|
||||
int16 val = (int8)value << variable1;
|
||||
*dst++ = val >> 8;
|
||||
*dst++ = (byte)(val);
|
||||
}
|
||||
} while (--count);
|
||||
|
||||
if (!_stream) {
|
||||
_stream = Audio::makeQueuingAudioStream(22050, true);
|
||||
g_system->getMixer()->playStream(Audio::Mixer::kSFXSoundType, &_soundHandle, _stream);
|
||||
}
|
||||
_stream->queueBuffer(output_data, 0x1000, DisposeAfterUse::YES, Audio::FLAG_STEREO | Audio::FLAG_16BITS);
|
||||
|
||||
bsize -= len;
|
||||
d_src += len;
|
||||
_IACTpos = 0;
|
||||
}
|
||||
} else {
|
||||
if (bsize > 1 && _IACTpos == 0) {
|
||||
*(_IACToutput + 0) = *d_src++;
|
||||
_IACTpos = 1;
|
||||
bsize--;
|
||||
}
|
||||
*(_IACToutput + _IACTpos) = *d_src++;
|
||||
_IACTpos++;
|
||||
bsize--;
|
||||
getDecoder()->rewind(); // This doesnt handle if looping fails.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SmushPlayer::handleFrameDemo() {
|
||||
uint32 tag;
|
||||
int32 size;
|
||||
int pos = 0;
|
||||
|
||||
if (_videoPause)
|
||||
return;
|
||||
|
||||
if (_videoFinished) {
|
||||
_videoPause = true;
|
||||
return;
|
||||
}
|
||||
|
||||
tag = _f.readUint32BE();
|
||||
assert(tag == MKTAG('F','R','M','E'));
|
||||
size = _f.readUint32BE();
|
||||
byte *frame = new byte[size];
|
||||
_f.read(frame, size);
|
||||
|
||||
do {
|
||||
if (READ_BE_UINT32(frame + pos) == MKTAG('F','O','B','J')) {
|
||||
_x = READ_LE_UINT16(frame + pos + 10);
|
||||
_y = READ_LE_UINT16(frame + pos + 12);
|
||||
int width = READ_LE_UINT16(frame + pos + 14);
|
||||
int height = READ_LE_UINT16(frame + pos + 16);
|
||||
if (width != _width || height != _height) {
|
||||
delete[] _internalBuffer;
|
||||
delete[] _externalBuffer;
|
||||
_width = width;
|
||||
_height = height;
|
||||
_internalBuffer = new byte[_width * _height];
|
||||
_externalBuffer = new byte[_width * _height * 2];
|
||||
_blocky8.init(_width, _height);
|
||||
}
|
||||
_blocky8.decode(_internalBuffer, frame + pos + 8 + 14);
|
||||
pos += READ_BE_UINT32(frame + pos + 4) + 8;
|
||||
} else if (READ_BE_UINT32(frame + pos) == MKTAG('I','A','C','T')) {
|
||||
handleIACT(frame + pos + 8, READ_BE_UINT32(frame + pos + 4));
|
||||
int offset = READ_BE_UINT32(frame + pos + 4) + 8;
|
||||
if (offset & 1)
|
||||
offset += 1;
|
||||
pos += offset;
|
||||
} else if (READ_BE_UINT32(frame + pos) == MKTAG('X','P','A','L')) {
|
||||
handleDeltaPalette(frame + pos + 8, READ_BE_UINT32(frame + pos + 4));
|
||||
pos += READ_BE_UINT32(frame + pos + 4) + 8;
|
||||
} else {
|
||||
error("SmushPlayer::handleFrame() unknown tag");
|
||||
}
|
||||
} while (pos < size);
|
||||
delete[] frame;
|
||||
|
||||
uint16 *d = (uint16 *)_externalBuffer;
|
||||
for (int l = 0; l < _width * _height; l++) {
|
||||
int index = _internalBuffer[l];
|
||||
d[l] = ((_pal[(index * 3) + 0] & 0xF8) << 8) | ((_pal[(index * 3) + 1] & 0xFC) << 3) | (_pal[(index * 3) + 2] >> 3);
|
||||
}
|
||||
|
||||
_updateNeeded = true;
|
||||
|
||||
_frame++;
|
||||
_movieTime += _speed / 1000.f;
|
||||
if (_frame == _nbframes) {
|
||||
_videoFinished = true;
|
||||
g_grim->setMode(ENGINE_MODE_NORMAL);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void SmushPlayer::handleFramesHeader() {
|
||||
uint32 tag;
|
||||
int32 size;
|
||||
int pos = 0;
|
||||
|
||||
tag = _file->readUint32BE();
|
||||
assert(tag == MKTAG('F','L','H','D'));
|
||||
size = _file->readUint32BE();
|
||||
byte *f_header = new byte[size];
|
||||
_file->read(f_header, size);
|
||||
|
||||
do {
|
||||
if (READ_BE_UINT32(f_header + pos) == MKTAG('B','l','1','6')) {
|
||||
pos += READ_BE_UINT32(f_header + pos + 4) + 8;
|
||||
} else if (READ_BE_UINT32(f_header + pos) == MKTAG('W','a','v','e')) {
|
||||
_freq = READ_LE_UINT32(f_header + pos + 8);
|
||||
_channels = READ_LE_UINT32(f_header + pos + 12);
|
||||
pos += 20;
|
||||
} else {
|
||||
error("SmushPlayer::handleFramesHeader() unknown tag");
|
||||
}
|
||||
} while (pos < size);
|
||||
delete[] f_header;
|
||||
}
|
||||
|
||||
bool SmushPlayer::setupAnimDemo(const char *file) {
|
||||
uint32 tag;
|
||||
int32 size;
|
||||
|
||||
if (!_f.open(file))
|
||||
return false;
|
||||
|
||||
tag = _f.readUint32BE();
|
||||
assert(tag == MKTAG('A','N','I','M'));
|
||||
size = _f.readUint32BE();
|
||||
|
||||
tag = _f.readUint32BE();
|
||||
assert(tag == MKTAG('A','H','D','R'));
|
||||
size = _f.readUint32BE();
|
||||
|
||||
_f.readUint16BE(); // version
|
||||
_nbframes = _f.readUint16LE();
|
||||
_f.readUint16BE(); // unknown
|
||||
|
||||
for (int l = 0; l < 0x300; l++) {
|
||||
_pal[l] = _f.readByte();
|
||||
}
|
||||
_f.readUint32BE();
|
||||
_f.readUint32BE();
|
||||
_f.readUint32BE();
|
||||
_f.readUint32BE();
|
||||
_f.readUint32BE();
|
||||
|
||||
_x = -1;
|
||||
_y = -1;
|
||||
_width = -1;
|
||||
_height = -1;
|
||||
_videoLooping = false;
|
||||
_startPos = NULL;
|
||||
_speed = 66667;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SmushPlayer::setupAnim(const char *file, bool looping, int x, int y) {
|
||||
uint32 tag;
|
||||
int32 size;
|
||||
int16 flags;
|
||||
|
||||
_file = wrapCompressedReadStream(g_resourceloader->openNewSubStreamFile(file));
|
||||
if (!_file)
|
||||
return false;
|
||||
|
||||
tag = _file->readUint32BE();
|
||||
assert(tag == MKTAG('S','A','N','M'));
|
||||
size = _file->readUint32BE();
|
||||
|
||||
tag = _file->readUint32BE();
|
||||
assert(tag == MKTAG('S','H','D','R'));
|
||||
size = _file->readUint32BE();
|
||||
byte *s_header = new byte[size];
|
||||
_file->read(s_header, size);
|
||||
_nbframes = READ_LE_UINT32(s_header + 2);
|
||||
int width = READ_LE_UINT16(s_header + 8);
|
||||
int height = READ_LE_UINT16(s_header + 10);
|
||||
if (_width != width || _height != height) {
|
||||
_blocky16.init(width, height);
|
||||
}
|
||||
|
||||
_x = x;
|
||||
_y = y;
|
||||
_width = width;
|
||||
_height = height;
|
||||
|
||||
if (looping)
|
||||
_speed = READ_LE_UINT32(s_header + 14);
|
||||
else
|
||||
_speed = 66667;
|
||||
flags = READ_LE_UINT16(s_header + 18);
|
||||
// Output information for checking out the flags
|
||||
if (gDebugLevel == DEBUG_SMUSH || gDebugLevel == DEBUG_NORMAL || gDebugLevel == DEBUG_ALL) {
|
||||
printf("SMUSH Flags:");
|
||||
for (int i = 0; i < 16; i++)
|
||||
printf(" %d", (flags & (1 << i)) != 0);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
_videoLooping = looping;
|
||||
_startPos = NULL; // Set later
|
||||
delete[] s_header;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SmushPlayer::stop() {
|
||||
deinit();
|
||||
g_grim->setMode(ENGINE_MODE_NORMAL);
|
||||
}
|
||||
|
||||
bool SmushPlayer::play(const char *filename, bool looping, int x, int y) {
|
||||
deinit();
|
||||
_fname = filename;
|
||||
|
||||
if (gDebugLevel == DEBUG_SMUSH)
|
||||
printf("Playing video '%s'.\n", filename);
|
||||
|
||||
// Load the video
|
||||
if (g_grim->getGameFlags() & ADGF_DEMO) {
|
||||
if (!setupAnimDemo(filename))
|
||||
return false;
|
||||
} else {
|
||||
if (!setupAnim(filename, looping, x, y))
|
||||
return false;
|
||||
|
||||
handleFramesHeader();
|
||||
}
|
||||
|
||||
if (_videoLooping) {
|
||||
_startPos = new SavePos;
|
||||
_startPos->tmpBuf = NULL;
|
||||
_startPos->filePos = _file->pos();
|
||||
}
|
||||
|
||||
init();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SmushPlayer::saveState(SaveGame *state) {
|
||||
state->beginSection('SMUS');
|
||||
|
||||
state->writeString(_fname);
|
||||
|
||||
state->writeLESint32(_frame);
|
||||
state->writeFloat(_movieTime);
|
||||
state->writeLESint32(_videoFinished);
|
||||
state->writeLESint32(_videoLooping);
|
||||
|
||||
state->writeLESint32(_x);
|
||||
state->writeLESint32(_y);
|
||||
|
||||
state->endSection();
|
||||
}
|
||||
|
||||
void SmushPlayer::restoreState(SaveGame *state) {
|
||||
state->beginSection('SMUS');
|
||||
|
||||
_fname = state->readString();
|
||||
|
||||
int32 frame = state->readLESint32();
|
||||
float movieTime = state->readFloat();
|
||||
bool videoFinished = state->readLESint32();
|
||||
bool videoLooping = state->readLESint32();
|
||||
|
||||
int x = state->readLESint32();
|
||||
int y = state->readLESint32();
|
||||
|
||||
if (!videoFinished) {
|
||||
play(_fname.c_str(), videoLooping, x, y);
|
||||
}
|
||||
_frame = frame;
|
||||
_movieTime = movieTime;
|
||||
|
||||
state->endSection();
|
||||
MoviePlayer::restoreState(state);
|
||||
getDecoder()->seekToTime((uint32)_movieTime); // Currently not fully working (out of synch)
|
||||
}
|
||||
|
||||
} // end of namespace Grim
|
||||
|
||||
|
||||
#endif // USE_SMUSH
|
||||
|
@ -23,63 +23,25 @@
|
||||
#ifndef GRIM_SMUSH_PLAYER_H
|
||||
#define GRIM_SMUSH_PLAYER_H
|
||||
|
||||
#include "common/zlib.h"
|
||||
#include "common/file.h"
|
||||
|
||||
#include "engines/grim/movie/movie.h"
|
||||
#include "engines/grim/movie/codecs/blocky8.h"
|
||||
#include "engines/grim/movie/codecs/blocky16.h"
|
||||
|
||||
#include "audio/mixer.h"
|
||||
#include "audio/audiostream.h"
|
||||
|
||||
#ifdef USE_SMUSH
|
||||
|
||||
namespace Common {
|
||||
class Mutex;
|
||||
}
|
||||
|
||||
namespace Grim {
|
||||
|
||||
class SaveGame;
|
||||
class SmushDecoder;
|
||||
|
||||
class SmushPlayer : public MoviePlayer {
|
||||
private:
|
||||
int32 _nbframes;
|
||||
Blocky8 _blocky8;
|
||||
Blocky16 _blocky16;
|
||||
Common::SeekableReadStream *_file;
|
||||
Common::Mutex _frameMutex;
|
||||
|
||||
byte _pal[0x300];
|
||||
int16 _deltaPal[0x300];
|
||||
byte _IACToutput[4096];
|
||||
int32 _IACTpos;
|
||||
|
||||
public:
|
||||
SmushPlayer();
|
||||
virtual ~SmushPlayer();
|
||||
SmushPlayer(bool demo);
|
||||
|
||||
bool play(const char *filename, bool looping, int x, int y);
|
||||
void stop();
|
||||
|
||||
void saveState(SaveGame *state);
|
||||
void restoreState(SaveGame *state);
|
||||
|
||||
private:
|
||||
static void timerCallback(void *ptr);
|
||||
void parseNextFrame();
|
||||
void init();
|
||||
void deinit();
|
||||
void handleDeltaPalette(byte *src, int32 size);
|
||||
void handleFramesHeader();
|
||||
void handleFrameDemo();
|
||||
bool loadFile(Common::String filename);
|
||||
void handleFrame();
|
||||
void handleBlocky16(byte *src);
|
||||
void handleWave(const byte *src, uint32 size);
|
||||
void handleIACT(const byte *src, int32 size);
|
||||
bool setupAnim(const char *file, bool looping, int x, int y);
|
||||
bool setupAnimDemo(const char *file);
|
||||
SmushDecoder* getDecoder();
|
||||
void init();
|
||||
bool _demo;
|
||||
};
|
||||
|
||||
} // end of namespace Grim
|
||||
|
Loading…
x
Reference in New Issue
Block a user