mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-04 16:26:53 +00:00
ACCESS: movie player for Noctropolis+Synnergist
accessible via debug command "playmovie"
This commit is contained in:
parent
5137d0d3f9
commit
d69ffaa0e4
@ -313,6 +313,8 @@ public:
|
|||||||
|
|
||||||
void SPRINTCHR(char c, int fontNum);
|
void SPRINTCHR(char c, int fontNum);
|
||||||
void PRINTCHR(Common::String msg, int fontNum);
|
void PRINTCHR(Common::String msg, int fontNum);
|
||||||
|
|
||||||
|
bool playMovie(const Common::String &filename, const Common::Point &pos);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // End of namespace Access
|
} // End of namespace Access
|
||||||
|
@ -258,7 +258,7 @@ void ASurface::transBlitFrom(ASurface &src) {
|
|||||||
blitFrom(src);
|
blitFrom(src);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ASurface::blitFrom(Graphics::Surface &src) {
|
void ASurface::blitFrom(const Graphics::Surface &src) {
|
||||||
assert(w >= src.w && h >= src.h);
|
assert(w >= src.w && h >= src.h);
|
||||||
for (int y = 0; y < src.h; ++y) {
|
for (int y = 0; y < src.h; ++y) {
|
||||||
const byte *srcP = (const byte *)src.getBasePtr(0, y);
|
const byte *srcP = (const byte *)src.getBasePtr(0, y);
|
||||||
|
@ -107,7 +107,7 @@ public:
|
|||||||
|
|
||||||
virtual void transBlitFrom(ASurface &src);
|
virtual void transBlitFrom(ASurface &src);
|
||||||
|
|
||||||
virtual void blitFrom(Graphics::Surface &src);
|
virtual void blitFrom(const Graphics::Surface &src);
|
||||||
|
|
||||||
virtual void copyBuffer(Graphics::Surface *src);
|
virtual void copyBuffer(Graphics::Surface *src);
|
||||||
|
|
||||||
|
@ -52,12 +52,23 @@ Debugger *Debugger::init(AccessEngine *vm) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Debugger::postEnter() {
|
||||||
|
if (!_playMovieFile.empty()) {
|
||||||
|
_vm->playMovie(_playMovieFile, Common::Point(0, 0));
|
||||||
|
|
||||||
|
_playMovieFile.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
_vm->pauseEngine(false);
|
||||||
|
}
|
||||||
|
|
||||||
/*------------------------------------------------------------------------*/
|
/*------------------------------------------------------------------------*/
|
||||||
|
|
||||||
Debugger::Debugger(AccessEngine *vm) : GUI::Debugger(), _vm(vm) {
|
Debugger::Debugger(AccessEngine *vm) : GUI::Debugger(), _vm(vm) {
|
||||||
registerCmd("continue", WRAP_METHOD(Debugger, cmdExit));
|
registerCmd("continue", WRAP_METHOD(Debugger, cmdExit));
|
||||||
registerCmd("scene", WRAP_METHOD(Debugger, Cmd_LoadScene));
|
registerCmd("scene", WRAP_METHOD(Debugger, Cmd_LoadScene));
|
||||||
registerCmd("cheat", WRAP_METHOD(Debugger, Cmd_Cheat));
|
registerCmd("cheat", WRAP_METHOD(Debugger, Cmd_Cheat));
|
||||||
|
registerCmd("playmovie", WRAP_METHOD(Debugger, Cmd_PlayMovie));
|
||||||
|
|
||||||
switch (vm->getGameID()) {
|
switch (vm->getGameID()) {
|
||||||
case GType_Amazon:
|
case GType_Amazon:
|
||||||
@ -133,6 +144,19 @@ bool Debugger::Cmd_Cheat(int argc, const char **argv) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Debugger::Cmd_PlayMovie(int argc, const char **argv) {
|
||||||
|
if (argc != 2) {
|
||||||
|
debugPrintf("Format: playmovie <movie-file>\n");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// play gets postboned until debugger is closed
|
||||||
|
Common::String filename = argv[1];
|
||||||
|
_playMovieFile = filename;
|
||||||
|
|
||||||
|
return cmdExit(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
/*------------------------------------------------------------------------*/
|
/*------------------------------------------------------------------------*/
|
||||||
|
|
||||||
namespace Amazon {
|
namespace Amazon {
|
||||||
|
@ -35,13 +35,16 @@ class AccessEngine;
|
|||||||
class Debugger : public GUI::Debugger {
|
class Debugger : public GUI::Debugger {
|
||||||
protected:
|
protected:
|
||||||
AccessEngine *_vm;
|
AccessEngine *_vm;
|
||||||
|
Common::String _playMovieFile;
|
||||||
|
|
||||||
bool Cmd_LoadScene(int argc, const char **argv);
|
bool Cmd_LoadScene(int argc, const char **argv);
|
||||||
bool Cmd_Cheat(int argc, const char **argv);
|
bool Cmd_Cheat(int argc, const char **argv);
|
||||||
|
bool Cmd_PlayMovie(int argc, const char **argv);
|
||||||
Common::String *_sceneDescr;
|
Common::String *_sceneDescr;
|
||||||
int _sceneNumb;
|
int _sceneNumb;
|
||||||
public:
|
public:
|
||||||
static Debugger *init(AccessEngine *vm);
|
static Debugger *init(AccessEngine *vm);
|
||||||
|
void postEnter();
|
||||||
public:
|
public:
|
||||||
Debugger(AccessEngine *vm);
|
Debugger(AccessEngine *vm);
|
||||||
virtual ~Debugger();
|
virtual ~Debugger();
|
||||||
|
@ -21,6 +21,7 @@ MODULE_OBJS := \
|
|||||||
scripts.o \
|
scripts.o \
|
||||||
sound.o \
|
sound.o \
|
||||||
video.o \
|
video.o \
|
||||||
|
video/movie_decoder.o \
|
||||||
amazon/amazon_game.o \
|
amazon/amazon_game.o \
|
||||||
amazon/amazon_logic.o \
|
amazon/amazon_logic.o \
|
||||||
amazon/amazon_player.o \
|
amazon/amazon_player.o \
|
||||||
|
@ -296,7 +296,7 @@ void Screen::transBlitFrom(ASurface *src, const Common::Rect &bounds) {
|
|||||||
ASurface::transBlitFrom(src, bounds);
|
ASurface::transBlitFrom(src, bounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Screen::blitFrom(Graphics::Surface &src) {
|
void Screen::blitFrom(const Graphics::Surface &src) {
|
||||||
addDirtyRect(Common::Rect(0, 0, src.w, src.h));
|
addDirtyRect(Common::Rect(0, 0, src.w, src.h));
|
||||||
ASurface::blitFrom(src);
|
ASurface::blitFrom(src);
|
||||||
}
|
}
|
||||||
|
@ -98,7 +98,7 @@ public:
|
|||||||
|
|
||||||
virtual void transBlitFrom(ASurface *src, const Common::Rect &bounds);
|
virtual void transBlitFrom(ASurface *src, const Common::Rect &bounds);
|
||||||
|
|
||||||
virtual void blitFrom(Graphics::Surface &src);
|
virtual void blitFrom(const Graphics::Surface &src);
|
||||||
|
|
||||||
virtual void copyBuffer(Graphics::Surface *src);
|
virtual void copyBuffer(Graphics::Surface *src);
|
||||||
|
|
||||||
|
739
engines/access/video/movie_decoder.cpp
Normal file
739
engines/access/video/movie_decoder.cpp
Normal file
@ -0,0 +1,739 @@
|
|||||||
|
/* ScummVM - Graphic Adventure Engine
|
||||||
|
*
|
||||||
|
* ScummVM 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 program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "common/scummsys.h"
|
||||||
|
#include "common/stream.h"
|
||||||
|
#include "common/textconsole.h"
|
||||||
|
|
||||||
|
#include "audio/audiostream.h"
|
||||||
|
#include "audio/decoders/raw.h"
|
||||||
|
|
||||||
|
#include "access/access.h"
|
||||||
|
#include "access/video/movie_decoder.h"
|
||||||
|
|
||||||
|
// for Test-Code
|
||||||
|
#include "common/system.h"
|
||||||
|
#include "common/events.h"
|
||||||
|
#include "common/keyboard.h"
|
||||||
|
#include "engines/engine.h"
|
||||||
|
#include "engines/util.h"
|
||||||
|
#include "graphics/palette.h"
|
||||||
|
#include "graphics/pixelformat.h"
|
||||||
|
#include "graphics/surface.h"
|
||||||
|
|
||||||
|
namespace Access {
|
||||||
|
|
||||||
|
AccessVIDMovieDecoder::AccessVIDMovieDecoder()
|
||||||
|
: _stream(0), _videoTrack(0), _audioTrack(0) {
|
||||||
|
_streamSeekOffset = 0;
|
||||||
|
_streamVideoIndex = 0;
|
||||||
|
_streamAudioIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
AccessVIDMovieDecoder::~AccessVIDMovieDecoder() {
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AccessVIDMovieDecoder::loadStream(Common::SeekableReadStream *stream) {
|
||||||
|
uint32 videoCodecTag = 0;
|
||||||
|
uint32 videoHeight = 0;
|
||||||
|
uint32 videoWidth = 0;
|
||||||
|
uint16 regularDelay = 0;
|
||||||
|
uint32 audioSampleRate = 0;
|
||||||
|
|
||||||
|
close();
|
||||||
|
|
||||||
|
_stream = stream;
|
||||||
|
_streamSeekOffset = 15; // offset of first chunk
|
||||||
|
_streamVideoIndex = 0;
|
||||||
|
_streamAudioIndex = 0;
|
||||||
|
|
||||||
|
// read header
|
||||||
|
// ID [dword] "VID"
|
||||||
|
// ?? [byte]
|
||||||
|
// ?? [word]
|
||||||
|
// width [word]
|
||||||
|
// height [word]
|
||||||
|
// regular delay between frames (60 per second) [word]
|
||||||
|
// ?? [word]
|
||||||
|
|
||||||
|
videoCodecTag = _stream->readUint32BE();
|
||||||
|
if (videoCodecTag != MKTAG('V','I','D',0x00)) {
|
||||||
|
warning("AccessVIDMoviePlay: bad codec tag, not a video file?");
|
||||||
|
close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
_stream->skip(3);
|
||||||
|
videoWidth = _stream->readUint16LE();
|
||||||
|
videoHeight = _stream->readUint16LE();
|
||||||
|
regularDelay = _stream->readUint16LE();
|
||||||
|
_stream->skip(2);
|
||||||
|
|
||||||
|
if (!regularDelay) {
|
||||||
|
warning("AccessVIDMoviePlay: delay between frames is zero?");
|
||||||
|
close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create video track
|
||||||
|
_videoTrack = new StreamVideoTrack(videoWidth, videoHeight, regularDelay);
|
||||||
|
addTrack(_videoTrack);
|
||||||
|
|
||||||
|
//warning("width %d, height %d", videoWidth, videoHeight);
|
||||||
|
|
||||||
|
// Look through the first few packets
|
||||||
|
static const int maxPacketCheckCount = 10;
|
||||||
|
|
||||||
|
for (int i = 0; i < maxPacketCheckCount; i++) {
|
||||||
|
byte chunkId = _stream->readByte();
|
||||||
|
|
||||||
|
// Bail out if done
|
||||||
|
if (_stream->eos())
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Bail also in case end of file chunk was found
|
||||||
|
if (chunkId == kVIDMovieChunkId_EndOfFile)
|
||||||
|
break;
|
||||||
|
|
||||||
|
uint32 chunkStartOffset = _stream->pos();
|
||||||
|
//warning("data chunk at %x", chunkStartOffset);
|
||||||
|
|
||||||
|
switch (chunkId) {
|
||||||
|
case kVIDMovieChunkId_FullFrame:
|
||||||
|
case kVIDMovieChunkId_FullFrameCompressed:
|
||||||
|
case kVIDMovieChunkId_PartialFrameCompressed:
|
||||||
|
case kVIDMovieChunkId_FullFrameCompressedFill: {
|
||||||
|
if (!_videoTrack->skipOverFrame(_stream, chunkId)) {
|
||||||
|
close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case kVIDMovieChunkId_Palette: {
|
||||||
|
if (!_videoTrack->skipOverPalette(_stream)) {
|
||||||
|
close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case kVIDMovieChunkId_AudioFirstChunk:
|
||||||
|
case kVIDMovieChunkId_Audio: {
|
||||||
|
// sync [word]
|
||||||
|
// sampling rate [byte]
|
||||||
|
// size of audio data [word]
|
||||||
|
// sample data [] (mono, 8-bit, unsigned)
|
||||||
|
//
|
||||||
|
// Only first chunk has sync + sampling rate
|
||||||
|
if (chunkId == kVIDMovieChunkId_AudioFirstChunk) {
|
||||||
|
byte soundblasterRate;
|
||||||
|
|
||||||
|
_stream->skip(2); // skip over sync
|
||||||
|
soundblasterRate = _stream->readByte();
|
||||||
|
audioSampleRate = 1000000 / (256 - soundblasterRate);
|
||||||
|
|
||||||
|
_audioTrack = new StreamAudioTrack(audioSampleRate);
|
||||||
|
addTrack(_audioTrack);
|
||||||
|
|
||||||
|
_stream->seek(chunkStartOffset); // seek back
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_audioTrack) {
|
||||||
|
warning("AccessVIDMoviePlay: regular audio chunk, before audio chunk w/ header");
|
||||||
|
close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!_audioTrack->skipOverAudio(_stream, chunkId)) {
|
||||||
|
close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
warning("AccessVIDMoviePlay: Unknown chunk-id '%x' inside VID movie", chunkId);
|
||||||
|
close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remember this chunk inside our cache
|
||||||
|
IndexCacheEntry indexCacheEntry;
|
||||||
|
|
||||||
|
indexCacheEntry.chunkId = chunkId;
|
||||||
|
indexCacheEntry.offset = chunkStartOffset;
|
||||||
|
|
||||||
|
_indexCacheTable.push_back(indexCacheEntry);
|
||||||
|
|
||||||
|
// Got an audio chunk now? -> exit b/c we are done
|
||||||
|
if (audioSampleRate)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remember offset of latest not-indexed-yet chunk
|
||||||
|
_streamSeekOffset = _stream->pos();
|
||||||
|
|
||||||
|
// If sample rate was found, create an audio track
|
||||||
|
if (audioSampleRate) {
|
||||||
|
_audioTrack = new StreamAudioTrack(audioSampleRate);
|
||||||
|
addTrack(_audioTrack);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rewind back to the beginning right to the first chunk
|
||||||
|
_stream->seek(15);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AccessVIDMovieDecoder::close() {
|
||||||
|
Video::VideoDecoder::close();
|
||||||
|
|
||||||
|
delete _stream; _stream = 0;
|
||||||
|
_videoTrack = 0;
|
||||||
|
|
||||||
|
_indexCacheTable.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// We try to at least decode 1 frame
|
||||||
|
// and also try to get at least 0.5 seconds of audio queued up
|
||||||
|
void AccessVIDMovieDecoder::readNextPacket() {
|
||||||
|
uint32 currentMovieTime = getTime();
|
||||||
|
uint32 wantedAudioQueued = currentMovieTime + 500; // always try to be 0.500 seconds in front of movie time
|
||||||
|
|
||||||
|
uint32 streamIndex = 0;
|
||||||
|
IndexCacheEntry indexEntry;
|
||||||
|
bool currentlySeeking = false;
|
||||||
|
|
||||||
|
bool videoDone = false;
|
||||||
|
bool audioDone = false;
|
||||||
|
|
||||||
|
// Seek to smallest stream offset
|
||||||
|
if ((_streamVideoIndex <= _streamAudioIndex) || (!_audioTrack)) {
|
||||||
|
streamIndex = _streamVideoIndex;
|
||||||
|
} else {
|
||||||
|
streamIndex = _streamAudioIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_audioTrack) {
|
||||||
|
if (wantedAudioQueued <= _audioTrack->getTotalAudioQueued()) {
|
||||||
|
// already got enough audio queued up
|
||||||
|
audioDone = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// no audio track, audio is always done
|
||||||
|
audioDone = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
// Check, if stream-index is already cached
|
||||||
|
if (streamIndex < _indexCacheTable.size()) {
|
||||||
|
indexEntry.chunkId = _indexCacheTable[streamIndex].chunkId;
|
||||||
|
indexEntry.offset = _indexCacheTable[streamIndex].offset;
|
||||||
|
currentlySeeking = false;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// read from file
|
||||||
|
_stream->seek(_streamSeekOffset);
|
||||||
|
indexEntry.chunkId = _stream->readByte();
|
||||||
|
indexEntry.offset = _stream->pos();
|
||||||
|
currentlySeeking = true;
|
||||||
|
|
||||||
|
// and store that as well
|
||||||
|
_indexCacheTable.push_back(indexEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
// end of stream -> exit
|
||||||
|
if (_stream->eos())
|
||||||
|
break;
|
||||||
|
|
||||||
|
// end of file chunk -> exit
|
||||||
|
if (indexEntry.chunkId == kVIDMovieChunkId_EndOfFile)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// warning("chunk %x", indexEntry.chunkId);
|
||||||
|
|
||||||
|
switch (indexEntry.chunkId) {
|
||||||
|
case kVIDMovieChunkId_FullFrame:
|
||||||
|
case kVIDMovieChunkId_FullFrameCompressed:
|
||||||
|
case kVIDMovieChunkId_PartialFrameCompressed:
|
||||||
|
case kVIDMovieChunkId_FullFrameCompressedFill: {
|
||||||
|
if ((_streamVideoIndex <= streamIndex) && (!videoDone)) {
|
||||||
|
// We are at an index, that is still relevant for video decoding
|
||||||
|
// and we are not done with video yet
|
||||||
|
if (!currentlySeeking) {
|
||||||
|
// seek to stream position in case we used the cache
|
||||||
|
_stream->seek(indexEntry.offset);
|
||||||
|
}
|
||||||
|
//warning("video decode chunk %x at %lx", indexEntry.chunkId, _stream->pos());
|
||||||
|
_videoTrack->decodeFrame(_stream, indexEntry.chunkId);
|
||||||
|
videoDone = true;
|
||||||
|
_streamVideoIndex = streamIndex + 1;
|
||||||
|
} else {
|
||||||
|
if (currentlySeeking) {
|
||||||
|
// currently seeking, so we have to skip the frame bytes manually
|
||||||
|
_videoTrack->skipOverFrame(_stream, indexEntry.chunkId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case kVIDMovieChunkId_Palette: {
|
||||||
|
if ((_streamVideoIndex <= streamIndex) && (!videoDone)) {
|
||||||
|
// We are at an index, that is still relevant for video decoding
|
||||||
|
// and we are not done with video yet
|
||||||
|
if (!currentlySeeking) {
|
||||||
|
// seek to stream position in case we used the cache
|
||||||
|
_stream->seek(indexEntry.offset);
|
||||||
|
}
|
||||||
|
_videoTrack->decodePalette(_stream);
|
||||||
|
_streamVideoIndex = streamIndex + 1;
|
||||||
|
} else {
|
||||||
|
if (currentlySeeking) {
|
||||||
|
// currently seeking, so we have to skip the frame bytes manually
|
||||||
|
_videoTrack->skipOverPalette(_stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case kVIDMovieChunkId_AudioFirstChunk:
|
||||||
|
case kVIDMovieChunkId_Audio: {
|
||||||
|
if ((_streamAudioIndex <= streamIndex) && (!audioDone)) {
|
||||||
|
// We are at an index that is still relevant for audio decoding
|
||||||
|
if (!currentlySeeking) {
|
||||||
|
// seek to stream position in case we used the cache
|
||||||
|
_stream->seek(indexEntry.offset);
|
||||||
|
}
|
||||||
|
_audioTrack->queueAudio(_stream, indexEntry.chunkId);
|
||||||
|
_streamAudioIndex = streamIndex + 1;
|
||||||
|
|
||||||
|
if (wantedAudioQueued <= _audioTrack->getTotalAudioQueued()) {
|
||||||
|
// Got enough audio
|
||||||
|
audioDone = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!_audioTrack) {
|
||||||
|
error("AccessVIDMoviePlay: audio chunks found without audio track active");
|
||||||
|
}
|
||||||
|
if (currentlySeeking) {
|
||||||
|
// currently seeking, so we have to skip the audio bytes manually
|
||||||
|
_audioTrack->skipOverAudio(_stream, indexEntry.chunkId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
error("AccessVIDMoviePlay: Unknown chunk-id '%x' inside VID movie", indexEntry.chunkId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentlySeeking) {
|
||||||
|
// remember currently stream offset in case we are seeking
|
||||||
|
_streamSeekOffset = _stream->pos();
|
||||||
|
}
|
||||||
|
|
||||||
|
// go to next index
|
||||||
|
streamIndex++;
|
||||||
|
|
||||||
|
if ((videoDone) && (audioDone)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!videoDone) {
|
||||||
|
// no more video frames? set end of video track
|
||||||
|
_videoTrack->setEndOfTrack();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AccessVIDMovieDecoder::StreamVideoTrack::StreamVideoTrack(uint32 width, uint32 height, uint16 regularFrameDelay) {
|
||||||
|
_width = width;
|
||||||
|
_height = height;
|
||||||
|
_regularFrameDelay = regularFrameDelay;
|
||||||
|
_curFrame = -1;
|
||||||
|
_nextFrameStartTime = 0;
|
||||||
|
_endOfTrack = false;
|
||||||
|
|
||||||
|
memset(&_palette, 0, sizeof(_palette));
|
||||||
|
|
||||||
|
_surface = new Graphics::Surface();
|
||||||
|
_surface->create(_width, _height, Graphics::PixelFormat::createFormatCLUT8());
|
||||||
|
}
|
||||||
|
|
||||||
|
AccessVIDMovieDecoder::StreamVideoTrack::~StreamVideoTrack() {
|
||||||
|
delete _surface;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AccessVIDMovieDecoder::StreamVideoTrack::endOfTrack() const {
|
||||||
|
return _endOfTrack;
|
||||||
|
}
|
||||||
|
|
||||||
|
Graphics::PixelFormat AccessVIDMovieDecoder::StreamVideoTrack::getPixelFormat() const {
|
||||||
|
return _surface->format;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AccessVIDMovieDecoder::StreamVideoTrack::decodeFrame(Common::SeekableReadStream *stream, byte chunkId) {
|
||||||
|
byte *framePixelsPtr = (byte *)_surface->getPixels();
|
||||||
|
byte *pixelsPtr = framePixelsPtr;
|
||||||
|
byte rleByte = 0;
|
||||||
|
uint16 additionalDelay = 0;
|
||||||
|
int32 expectedPixels = 0;
|
||||||
|
|
||||||
|
switch (chunkId) {
|
||||||
|
case kVIDMovieChunkId_FullFrame: {
|
||||||
|
// Full frame is:
|
||||||
|
// data [width * height]
|
||||||
|
additionalDelay = stream->readUint16LE();
|
||||||
|
stream->read(framePixelsPtr, _width * _height);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case kVIDMovieChunkId_FullFrameCompressed:
|
||||||
|
case kVIDMovieChunkId_PartialFrameCompressed: {
|
||||||
|
// Skip manually over compressed data
|
||||||
|
// Full frame compressed is:
|
||||||
|
// additional delay [word]
|
||||||
|
// REPEAT:
|
||||||
|
// RLE [byte]
|
||||||
|
// RLE upper bit set: skip over RLE & 0x7F pixels
|
||||||
|
// RLE upper bit not set: draw RLE amount of pixels (those pixels follow right after RLE byte)
|
||||||
|
//
|
||||||
|
// Partial frame compressed is:
|
||||||
|
// sync [word]
|
||||||
|
// horizontal start position [word]
|
||||||
|
// REPEAT:
|
||||||
|
// see full frame compressed
|
||||||
|
uint16 horizontalStartPosition = 0;
|
||||||
|
|
||||||
|
additionalDelay = stream->readUint16LE();
|
||||||
|
|
||||||
|
if (chunkId == kVIDMovieChunkId_PartialFrameCompressed) {
|
||||||
|
horizontalStartPosition = stream->readUint16LE();
|
||||||
|
if (horizontalStartPosition >= _height) {
|
||||||
|
error("AccessVIDMoviePlay: starting position larger than height during partial frame compressed, data corrupt?");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
byte rleByte = 0;
|
||||||
|
expectedPixels = _width * (_height - horizontalStartPosition);
|
||||||
|
|
||||||
|
// adjust frame destination pointer
|
||||||
|
pixelsPtr += (horizontalStartPosition * _width);
|
||||||
|
|
||||||
|
while (expectedPixels >= 0) {
|
||||||
|
rleByte = stream->readByte();
|
||||||
|
if (!rleByte) // NUL means end of stream
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (rleByte & 0x80) {
|
||||||
|
rleByte = rleByte & 0x7F;
|
||||||
|
expectedPixels -= rleByte;
|
||||||
|
} else {
|
||||||
|
// skip over pixels
|
||||||
|
expectedPixels -= rleByte;
|
||||||
|
stream->read(pixelsPtr, rleByte); // read pixel data into frame
|
||||||
|
}
|
||||||
|
pixelsPtr += rleByte;
|
||||||
|
}
|
||||||
|
// expectedPixels may be positive here in case stream got terminated with a NUL
|
||||||
|
if (expectedPixels < 0) {
|
||||||
|
error("AccessVIDMoviePlay: pixel count mismatch during full/partial frame compressed, data corrupt?");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case kVIDMovieChunkId_FullFrameCompressedFill: {
|
||||||
|
// Full frame compressed fill is:
|
||||||
|
// additional delay [word]
|
||||||
|
// REPEAT:
|
||||||
|
// RLE [byte]
|
||||||
|
// RLE upper bit set: draw RLE amount (& 0x7F) of pixels with specified color (color byte follows after RLE byte)
|
||||||
|
// RLE upper bit not set: draw RLE amount of pixels (those pixels follow right after RLE byte)
|
||||||
|
additionalDelay = stream->readUint16LE();
|
||||||
|
expectedPixels = _width * _height;
|
||||||
|
|
||||||
|
while (expectedPixels > 0) {
|
||||||
|
rleByte = stream->readByte();
|
||||||
|
|
||||||
|
if (rleByte & 0x80) {
|
||||||
|
rleByte = rleByte & 0x7F;
|
||||||
|
expectedPixels -= rleByte;
|
||||||
|
|
||||||
|
byte fillColor = stream->readByte();
|
||||||
|
memset(pixelsPtr, fillColor, rleByte);
|
||||||
|
} else {
|
||||||
|
// skip over pixels
|
||||||
|
expectedPixels -= rleByte;
|
||||||
|
stream->read(pixelsPtr, rleByte); // read pixel data into frame
|
||||||
|
}
|
||||||
|
pixelsPtr += rleByte;
|
||||||
|
}
|
||||||
|
if (expectedPixels < 0) {
|
||||||
|
error("AccessVIDMoviePlay: pixel count mismatch during full frame compressed fill, data corrupt?");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
assert(0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
_curFrame++;
|
||||||
|
|
||||||
|
// TODO: not sure, if additionalDelay is supposed to affect the follow-up frame or the current frame
|
||||||
|
// the videos, that I found, don't have it set
|
||||||
|
uint32 currentFrameStartTime = getNextFrameStartTime();
|
||||||
|
uint32 nextFrameStartTime = (_regularFrameDelay * _curFrame) * 1000 / 60;
|
||||||
|
if (additionalDelay) {
|
||||||
|
nextFrameStartTime += additionalDelay * 1000 / 60;
|
||||||
|
}
|
||||||
|
assert(currentFrameStartTime <= nextFrameStartTime);
|
||||||
|
setNextFrameStartTime(nextFrameStartTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AccessVIDMovieDecoder::StreamVideoTrack::skipOverFrame(Common::SeekableReadStream *stream, byte chunkId) {
|
||||||
|
byte rleByte = 0;
|
||||||
|
int32 expectedPixels = 0;
|
||||||
|
|
||||||
|
switch (chunkId) {
|
||||||
|
case kVIDMovieChunkId_FullFrame: {
|
||||||
|
// Full frame is:
|
||||||
|
// additional delay [word]
|
||||||
|
// data [width * height]
|
||||||
|
stream->skip(2);
|
||||||
|
stream->skip(_width * _height);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case kVIDMovieChunkId_FullFrameCompressed:
|
||||||
|
case kVIDMovieChunkId_PartialFrameCompressed: {
|
||||||
|
// Skip manually over compressed data
|
||||||
|
// Full frame compressed is:
|
||||||
|
// additional delay [word]
|
||||||
|
// REPEAT:
|
||||||
|
// RLE [byte]
|
||||||
|
// RLE upper bit set: skip over RLE & 0x7F pixels
|
||||||
|
// RLE upper bit not set: draw RLE amount of pixels (those pixels follow right after RLE byte)
|
||||||
|
//
|
||||||
|
// Partial frame compressed is:
|
||||||
|
// sync [word]
|
||||||
|
// horizontal start position [word]
|
||||||
|
// REPEAT:
|
||||||
|
// see full frame compressed
|
||||||
|
uint16 horizontalStartPosition = 0;
|
||||||
|
|
||||||
|
stream->skip(2);
|
||||||
|
|
||||||
|
if (chunkId == kVIDMovieChunkId_PartialFrameCompressed) {
|
||||||
|
horizontalStartPosition = stream->readUint16LE();
|
||||||
|
if (horizontalStartPosition >= _height) {
|
||||||
|
warning("AccessVIDMoviePlay: starting position larger than height during partial frame compressed, data corrupt?");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
byte rleByte = 0;
|
||||||
|
expectedPixels = _width * (_height - horizontalStartPosition);
|
||||||
|
|
||||||
|
while (expectedPixels >= 0) {
|
||||||
|
rleByte = stream->readByte();
|
||||||
|
if (!rleByte) // NUL means end of stream
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (rleByte & 0x80) {
|
||||||
|
expectedPixels -= rleByte & 0x7F;
|
||||||
|
} else {
|
||||||
|
// skip over pixels
|
||||||
|
expectedPixels -= rleByte;
|
||||||
|
stream->skip(rleByte); // skip over pixel data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// expectedPixels may be positive here in case stream got terminated with a NUL
|
||||||
|
if (expectedPixels < 0) {
|
||||||
|
warning("AccessVIDMoviePlay: pixel count mismatch during full/partial frame compressed, data corrupt?");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case kVIDMovieChunkId_FullFrameCompressedFill: {
|
||||||
|
// Full frame compressed fill is:
|
||||||
|
// additional delay [word]
|
||||||
|
// REPEAT:
|
||||||
|
// RLE [byte]
|
||||||
|
// RLE upper bit set: draw RLE amount (& 0x7F) of pixels with specified color (color byte follows after RLE byte)
|
||||||
|
// RLE upper bit not set: draw RLE amount of pixels (those pixels follow right after RLE byte)
|
||||||
|
stream->skip(2);
|
||||||
|
expectedPixels = _width * _height;
|
||||||
|
|
||||||
|
while (expectedPixels > 0) {
|
||||||
|
rleByte = stream->readByte();
|
||||||
|
|
||||||
|
if (rleByte & 0x80) {
|
||||||
|
expectedPixels -= rleByte & 0x7F;
|
||||||
|
stream->skip(1);
|
||||||
|
} else {
|
||||||
|
// skip over pixels
|
||||||
|
expectedPixels -= rleByte;
|
||||||
|
stream->skip(rleByte); // skip over pixel data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (expectedPixels < 0) {
|
||||||
|
warning("AccessVIDMoviePlay: pixel count mismatch during full frame compressed fill, data corrupt?");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
assert(0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AccessVIDMovieDecoder::StreamVideoTrack::skipOverPalette(Common::SeekableReadStream *stream) {
|
||||||
|
stream->skip(0x300); // 3 bytes per color, 256 colors
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AccessVIDMovieDecoder::StreamVideoTrack::decodePalette(Common::SeekableReadStream *stream) {
|
||||||
|
assert(stream);
|
||||||
|
|
||||||
|
for (uint16 curColor = 0; curColor < 256; curColor++) {
|
||||||
|
_palette[curColor * 3] = stream->readByte();
|
||||||
|
_palette[curColor * 3 + 1] = stream->readByte();
|
||||||
|
_palette[curColor * 3 + 2] = stream->readByte();
|
||||||
|
}
|
||||||
|
|
||||||
|
_dirtyPalette = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const byte *AccessVIDMovieDecoder::StreamVideoTrack::getPalette() const {
|
||||||
|
_dirtyPalette = false;
|
||||||
|
return _palette;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AccessVIDMovieDecoder::StreamVideoTrack::hasDirtyPalette() const {
|
||||||
|
return _dirtyPalette;
|
||||||
|
}
|
||||||
|
|
||||||
|
AccessVIDMovieDecoder::StreamAudioTrack::StreamAudioTrack(uint32 sampleRate) {
|
||||||
|
_totalAudioQueued = 0; // currently 0 milliseconds queued
|
||||||
|
|
||||||
|
_sampleRate = sampleRate;
|
||||||
|
_stereo = false; // always mono
|
||||||
|
|
||||||
|
_audioStream = Audio::makeQueuingAudioStream(sampleRate, _stereo);
|
||||||
|
}
|
||||||
|
|
||||||
|
AccessVIDMovieDecoder::StreamAudioTrack::~StreamAudioTrack() {
|
||||||
|
delete _audioStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AccessVIDMovieDecoder::StreamAudioTrack::queueAudio(Common::SeekableReadStream *stream, byte chunkId) {
|
||||||
|
Common::SeekableReadStream *rawAudioStream = 0;
|
||||||
|
Audio::RewindableAudioStream *audioStream = 0;
|
||||||
|
uint32 audioLengthMSecs = 0;
|
||||||
|
|
||||||
|
if (chunkId == kVIDMovieChunkId_AudioFirstChunk) {
|
||||||
|
stream->skip(3); // skip over additional delay + sample rate
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 audioSize = stream->readUint16LE();
|
||||||
|
|
||||||
|
// Read the specified chunk into memory
|
||||||
|
rawAudioStream = stream->readStream(audioSize);
|
||||||
|
audioLengthMSecs = audioSize * 1000 / _sampleRate; // 1 byte == 1 8-bit sample
|
||||||
|
|
||||||
|
audioStream = Audio::makeRawStream(rawAudioStream, _sampleRate, Audio::FLAG_UNSIGNED | Audio::FLAG_LITTLE_ENDIAN, DisposeAfterUse::YES);
|
||||||
|
if (audioStream) {
|
||||||
|
_totalAudioQueued += audioLengthMSecs;
|
||||||
|
_audioStream->queueAudioStream(audioStream, DisposeAfterUse::YES);
|
||||||
|
} else {
|
||||||
|
// in case there was an error
|
||||||
|
delete rawAudioStream;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AccessVIDMovieDecoder::StreamAudioTrack::skipOverAudio(Common::SeekableReadStream *stream, byte chunkId) {
|
||||||
|
if (chunkId == kVIDMovieChunkId_AudioFirstChunk) {
|
||||||
|
stream->skip(3); // skip over additional delay + sample rate
|
||||||
|
}
|
||||||
|
uint32 audioSize = stream->readUint16LE();
|
||||||
|
stream->skip(audioSize);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Audio::AudioStream *AccessVIDMovieDecoder::StreamAudioTrack::getAudioStream() const {
|
||||||
|
return _audioStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AccessEngine::playMovie(const Common::String &filename, const Common::Point &pos) {
|
||||||
|
AccessVIDMovieDecoder *videoDecoder = new AccessVIDMovieDecoder();
|
||||||
|
|
||||||
|
Common::Point framePos(pos.x, pos.y);
|
||||||
|
|
||||||
|
if (!videoDecoder->loadFile(filename)) {
|
||||||
|
warning("AccessVIDMoviePlay: could not open '%s'", filename.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool skipVideo = false;
|
||||||
|
uint16 width = videoDecoder->getWidth();
|
||||||
|
uint16 height = videoDecoder->getHeight();
|
||||||
|
|
||||||
|
_events->clearEvents();
|
||||||
|
videoDecoder->start();
|
||||||
|
|
||||||
|
while (!shouldQuit() && !videoDecoder->endOfVideo() && !skipVideo) {
|
||||||
|
if (videoDecoder->needsUpdate()) {
|
||||||
|
const Graphics::Surface *frame = videoDecoder->decodeNextFrame();
|
||||||
|
|
||||||
|
if (frame) {
|
||||||
|
_screen->blitFrom(*frame);
|
||||||
|
|
||||||
|
if (videoDecoder->hasDirtyPalette()) {
|
||||||
|
const byte *palette = videoDecoder->getPalette();
|
||||||
|
g_system->getPaletteManager()->setPalette(palette, 0, 256);
|
||||||
|
}
|
||||||
|
|
||||||
|
_screen->updateScreen();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_events->pollEventsAndWait();
|
||||||
|
|
||||||
|
Common::KeyState keyState;
|
||||||
|
if (_events->getKey(keyState)) {
|
||||||
|
if (keyState.keycode == Common::KEYCODE_ESCAPE)
|
||||||
|
skipVideo = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
videoDecoder->close();
|
||||||
|
delete videoDecoder;
|
||||||
|
|
||||||
|
return !skipVideo;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // End of namespace Access
|
158
engines/access/video/movie_decoder.h
Normal file
158
engines/access/video/movie_decoder.h
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
/* ScummVM - Graphic Adventure Engine
|
||||||
|
*
|
||||||
|
* ScummVM 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 program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ACCESS_VIDEO_MOVIE_DECODER_H
|
||||||
|
#define ACCESS_VIDEO_MOVIE_DECODER_H
|
||||||
|
|
||||||
|
#include "common/rect.h"
|
||||||
|
#include "video/video_decoder.h"
|
||||||
|
#include "audio/decoders/raw.h"
|
||||||
|
|
||||||
|
namespace Audio {
|
||||||
|
class QueuingAudioStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Common {
|
||||||
|
class SeekableReadStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Image {
|
||||||
|
class Codec;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Access {
|
||||||
|
|
||||||
|
enum kDebugLevels {
|
||||||
|
kVIDMovieChunkId_FullFrame = 0x00,
|
||||||
|
kVIDMovieChunkId_FullFrameCompressed = 0x01,
|
||||||
|
kVIDMovieChunkId_Palette = 0x02,
|
||||||
|
kVIDMovieChunkId_FullFrameCompressedFill = 0x03,
|
||||||
|
kVIDMovieChunkId_PartialFrameCompressed = 0x04,
|
||||||
|
kVIDMovieChunkId_EndOfFile = 0x14,
|
||||||
|
kVIDMovieChunkId_AudioFirstChunk = 0x7C,
|
||||||
|
kVIDMovieChunkId_Audio = 0x7D
|
||||||
|
};
|
||||||
|
|
||||||
|
// This video format is used in at least the following Access engine games:
|
||||||
|
// - Noctropolis
|
||||||
|
// - Synnergist
|
||||||
|
|
||||||
|
class AccessVIDMovieDecoder : public Video::VideoDecoder {
|
||||||
|
public:
|
||||||
|
AccessVIDMovieDecoder();
|
||||||
|
~AccessVIDMovieDecoder();
|
||||||
|
|
||||||
|
bool loadStream(Common::SeekableReadStream *stream);
|
||||||
|
void close();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void readNextPacket();
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool streamSkipFullFrameCompressedFill();
|
||||||
|
|
||||||
|
private:
|
||||||
|
int32 _streamSeekOffset; /* current stream offset, pointing to not-yet-indexed stream position */
|
||||||
|
uint32 _streamVideoIndex; /* current stream index for video decoding */
|
||||||
|
uint32 _streamAudioIndex; /* current stream index for audio decoding */
|
||||||
|
|
||||||
|
struct IndexCacheEntry {
|
||||||
|
byte chunkId;
|
||||||
|
int32 offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
Common::Array<IndexCacheEntry> _indexCacheTable;
|
||||||
|
|
||||||
|
private:
|
||||||
|
class StreamVideoTrack : public VideoTrack {
|
||||||
|
public:
|
||||||
|
StreamVideoTrack(uint32 width, uint32 height, uint16 regularFrameDelay);
|
||||||
|
~StreamVideoTrack();
|
||||||
|
|
||||||
|
bool endOfTrack() const;
|
||||||
|
|
||||||
|
uint16 getWidth() const { return _width; }
|
||||||
|
uint16 getHeight() const { return _height; }
|
||||||
|
Graphics::PixelFormat getPixelFormat() const;
|
||||||
|
int getCurFrame() const { return _curFrame; }
|
||||||
|
void setNextFrameStartTime(uint32 nextFrameStartTime) { _nextFrameStartTime = nextFrameStartTime; }
|
||||||
|
uint32 getNextFrameStartTime() const { return _nextFrameStartTime; }
|
||||||
|
const Graphics::Surface *decodeNextFrame() { return _surface; }
|
||||||
|
|
||||||
|
const byte *getPalette() const;
|
||||||
|
bool hasDirtyPalette() const;
|
||||||
|
|
||||||
|
void decodePalette(Common::SeekableReadStream *stream);
|
||||||
|
void decodeFrame(Common::SeekableReadStream *stream, byte chunkId);
|
||||||
|
bool skipOverFrame(Common::SeekableReadStream *stream, byte chunkId);
|
||||||
|
bool skipOverPalette(Common::SeekableReadStream *stream);
|
||||||
|
|
||||||
|
void setEndOfTrack() { _endOfTrack = true; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Graphics::Surface *_surface;
|
||||||
|
|
||||||
|
int _curFrame;
|
||||||
|
uint32 _nextFrameStartTime;
|
||||||
|
|
||||||
|
byte _palette[3 * 256];
|
||||||
|
mutable bool _dirtyPalette;
|
||||||
|
uint16 _width, _height;
|
||||||
|
|
||||||
|
uint16 _regularFrameDelay; // delay between frames (1 = 1/60 of a second)
|
||||||
|
bool _endOfTrack;
|
||||||
|
};
|
||||||
|
|
||||||
|
class StreamAudioTrack : public AudioTrack {
|
||||||
|
public:
|
||||||
|
StreamAudioTrack(uint32 sampleRate);
|
||||||
|
~StreamAudioTrack();
|
||||||
|
|
||||||
|
void queueAudio(Common::SeekableReadStream *stream, byte chunkId);
|
||||||
|
bool skipOverAudio(Common::SeekableReadStream *stream, byte chunkId);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Audio::AudioStream *getAudioStream() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Audio::QueuingAudioStream *_audioStream;
|
||||||
|
uint32 _totalAudioQueued; /* total amount of milliseconds of audio, that we queued up already */
|
||||||
|
|
||||||
|
public:
|
||||||
|
uint32 getTotalAudioQueued() const { return _totalAudioQueued; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
int16 decodeSample(uint8 dataNibble);
|
||||||
|
|
||||||
|
uint32 _codecTag;
|
||||||
|
uint16 _sampleRate;
|
||||||
|
bool _stereo;
|
||||||
|
};
|
||||||
|
|
||||||
|
Common::SeekableReadStream *_stream;
|
||||||
|
StreamVideoTrack *_videoTrack;
|
||||||
|
StreamAudioTrack *_audioTrack;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // End of namespace Access
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue
Block a user