diff options
41 files changed, 1108 insertions, 1215 deletions
diff --git a/engines/agos/animation.cpp b/engines/agos/animation.cpp index eccb51c732..1b3ac9fd65 100644 --- a/engines/agos/animation.cpp +++ b/engines/agos/animation.cpp @@ -241,29 +241,46 @@ MoviePlayerDXA::MoviePlayerDXA(AGOSEngine_Feeble *vm, const char *name) } bool MoviePlayerDXA::load() { - char videoName[20]; - uint i; - if ((_vm->getPlatform() == Common::kPlatformAmiga || _vm->getPlatform() == Common::kPlatformMacintosh) && _vm->_language != Common::EN_ANY) { _sequenceNum = 0; - for (i = 0; i < 90; i++) { + for (uint i = 0; i < 90; i++) { if (!scumm_stricmp(baseName, _sequenceList[i])) _sequenceNum = i; } } - sprintf(videoName, "%s.dxa", baseName); + Common::String videoName = Common::String::printf("%s.dxa", baseName); if (!loadFile(videoName)) - error("Failed to load video file %s", videoName); + error("Failed to load video file %s", videoName.c_str()); - debug(0, "Playing video %s", videoName); + debug(0, "Playing video %s", videoName.c_str()); CursorMan.showMouse(false); + _firstFrameOffset = _fileStream->pos(); + return true; } +void MoviePlayerDXA::copyFrameToBuffer(byte *dst, uint x, uint y, uint pitch) { + uint h = getHeight(); + uint w = getWidth(); + + Graphics::Surface *surface = decodeNextFrame(); + byte *src = (byte *)surface->pixels; + dst += y * pitch + x; + + do { + memcpy(dst, src, w); + dst += pitch; + src += w; + } while (--h); + + if (hasDirtyPalette()) + setSystemPalette(); +} + void MoviePlayerDXA::playVideo() { // Most of the videos included in the Amiga version, reduced the // resoluton to 384 x 280, so require the screen to be cleared, @@ -277,7 +294,7 @@ void MoviePlayerDXA::playVideo() { } void MoviePlayerDXA::stopVideo() { - closeFile(); + close(); _mixer->stopHandle(_bgSound); } @@ -318,70 +335,56 @@ void MoviePlayerDXA::startSound() { } void MoviePlayerDXA::nextFrame() { - if (_bgSoundStream && _vm->_mixer->isSoundHandleActive(_bgSound) && (_vm->_mixer->getSoundElapsedTime(_bgSound) * getFrameRate()) / 1000 <= (uint32)getCurFrame()) { + if (_bgSoundStream && _vm->_mixer->isSoundHandleActive(_bgSound) && needsUpdate()) { copyFrameToBuffer(_vm->getBackBuf(), 465, 222, _vm->_screenWidth); return; } if (_vm->_interactiveVideo == TYPE_LOOPING && endOfVideo()) { - _fileStream->seek(_videoInfo.firstframeOffset); - _videoInfo.currentFrame = -1; + _fileStream->seek(_firstFrameOffset); + _curFrame = -1; startSound(); } if (!endOfVideo()) { - decodeNextFrame(); if (_vm->_interactiveVideo == TYPE_OMNITV) { copyFrameToBuffer(_vm->getBackBuf(), 465, 222, _vm->_screenWidth); } else if (_vm->_interactiveVideo == TYPE_LOOPING) { copyFrameToBuffer(_vm->getBackBuf(), (_vm->_screenWidth - getWidth()) / 2, (_vm->_screenHeight - getHeight()) / 2, _vm->_screenWidth); } } else if (_vm->_interactiveVideo == TYPE_OMNITV) { - closeFile(); + close(); _vm->_interactiveVideo = 0; _vm->_variableArray[254] = 6747; } } void MoviePlayerDXA::handleNextFrame() { - decodeNextFrame(); if (processFrame()) _vm->_system->updateScreen(); MoviePlayer::handleNextFrame(); } -void MoviePlayerDXA::setPalette(byte *pal) { - byte palette[1024]; - byte *p = palette; - - for (int i = 0; i < 256; i++) { - *p++ = *pal++; - *p++ = *pal++; - *p++ = *pal++; - *p++ = 0; - } - - _vm->_system->setPalette(palette, 0, 256); -} - bool MoviePlayerDXA::processFrame() { Graphics::Surface *screen = _vm->_system->lockScreen(); copyFrameToBuffer((byte *)screen->pixels, (_vm->_screenWidth - getWidth()) / 2, (_vm->_screenHeight - getHeight()) / 2, _vm->_screenWidth); _vm->_system->unlockScreen(); - if ((_bgSoundStream == NULL) || ((int)(_mixer->getSoundElapsedTime(_bgSound) * getFrameRate()) / 1000 <= getCurFrame())) { + Common::Rational soundTime(_mixer->getSoundElapsedTime(_bgSound), 1000); + if ((_bgSoundStream == NULL) || ((int)(soundTime * getFrameRate()) / 1000 < getCurFrame() + 1)) { if (_bgSoundStream && _mixer->isSoundHandleActive(_bgSound)) { - while (_mixer->isSoundHandleActive(_bgSound) && (_mixer->getSoundElapsedTime(_bgSound) * getFrameRate()) / 1000 <= (uint32)getCurFrame()) { + while (_mixer->isSoundHandleActive(_bgSound) && ((int) (soundTime * getFrameRate())) < getCurFrame()) { _vm->_system->delayMillis(10); + soundTime = Common::Rational(_mixer->getSoundElapsedTime(_bgSound), 1000); } // In case the background sound ends prematurely, update // _ticks so that we can still fall back on the no-sound // sync case for the subsequent frames. _ticks = _vm->_system->getMillis(); } else { - _ticks += getFrameWaitTime(); + _ticks += getTimeToNextFrame(); while (_vm->_system->getMillis() < _ticks) _vm->_system->delayMillis(10); } @@ -407,33 +410,51 @@ MoviePlayerSMK::MoviePlayerSMK(AGOSEngine_Feeble *vm, const char *name) } bool MoviePlayerSMK::load() { - char videoName[20]; + Common::String videoName = Common::String::printf("%s.smk", baseName); - sprintf(videoName, "%s.smk", baseName); if (!loadFile(videoName)) - error("Failed to load video file %s", videoName); + error("Failed to load video file %s", videoName.c_str()); - debug(0, "Playing video %s", videoName); + debug(0, "Playing video %s", videoName.c_str()); CursorMan.showMouse(false); + _firstFrameOffset = _fileStream->pos(); + return true; } +void MoviePlayerSMK::copyFrameToBuffer(byte *dst, uint x, uint y, uint pitch) { + uint h = getHeight(); + uint w = getWidth(); + + Graphics::Surface *surface = decodeNextFrame(); + byte *src = (byte *)surface->pixels; + dst += y * pitch + x; + + do { + memcpy(dst, src, w); + dst += pitch; + src += w; + } while (--h); + + if (hasDirtyPalette()) + setSystemPalette(); +} + void MoviePlayerSMK::playVideo() { while (!endOfVideo() && !_skipMovie && !_vm->shouldQuit()) handleNextFrame(); } void MoviePlayerSMK::stopVideo() { - closeFile(); + close(); } void MoviePlayerSMK::startSound() { } void MoviePlayerSMK::handleNextFrame() { - decodeNextFrame(); processFrame(); MoviePlayer::handleNextFrame(); @@ -441,8 +462,8 @@ void MoviePlayerSMK::handleNextFrame() { void MoviePlayerSMK::nextFrame() { if (_vm->_interactiveVideo == TYPE_LOOPING && endOfVideo()) { - _fileStream->seek(_videoInfo.firstframeOffset); - _videoInfo.currentFrame = -1; + _fileStream->seek(_firstFrameOffset); + _curFrame = -1; } if (!endOfVideo()) { @@ -453,32 +474,18 @@ void MoviePlayerSMK::nextFrame() { copyFrameToBuffer(_vm->getBackBuf(), (_vm->_screenWidth - getWidth()) / 2, (_vm->_screenHeight - getHeight()) / 2, _vm->_screenWidth); } } else if (_vm->_interactiveVideo == TYPE_OMNITV) { - closeFile(); + close(); _vm->_interactiveVideo = 0; _vm->_variableArray[254] = 6747; } } -void MoviePlayerSMK::setPalette(byte *pal) { - byte palette[1024]; - byte *p = palette; - - for (int i = 0; i < 256; i++) { - *p++ = *pal++; - *p++ = *pal++; - *p++ = *pal++; - *p++ = 0; - } - - _vm->_system->setPalette(palette, 0, 256); -} - bool MoviePlayerSMK::processFrame() { Graphics::Surface *screen = _vm->_system->lockScreen(); copyFrameToBuffer((byte *)screen->pixels, (_vm->_screenWidth - getWidth()) / 2, (_vm->_screenHeight - getHeight()) / 2, _vm->_screenWidth); _vm->_system->unlockScreen(); - uint32 waitTime = getFrameWaitTime(); + uint32 waitTime = getTimeToNextFrame(); if (!waitTime) { warning("dropped frame %i", getCurFrame()); diff --git a/engines/agos/animation.h b/engines/agos/animation.h index 6a1bdccadf..68a76e1f88 100644 --- a/engines/agos/animation.h +++ b/engines/agos/animation.h @@ -72,6 +72,9 @@ private: virtual void handleNextFrame(); virtual bool processFrame() = 0; virtual void startSound() {} + +protected: + uint32 _firstFrameOffset; }; class MoviePlayerDXA : public MoviePlayer, ::Graphics::DXADecoder { @@ -84,13 +87,12 @@ public: void playVideo(); void nextFrame(); virtual void stopVideo(); -protected: - void setPalette(byte *pal); private: void handleNextFrame(); bool processFrame(); void startSound(); + void copyFrameToBuffer(byte *dst, uint x, uint y, uint pitch); }; class MoviePlayerSMK : public MoviePlayer, ::Graphics::SmackerDecoder { @@ -101,12 +103,12 @@ public: void playVideo(); void nextFrame(); virtual void stopVideo(); -protected: - void setPalette(byte *pal); + private: void handleNextFrame(); bool processFrame(); void startSound(); + void copyFrameToBuffer(byte *dst, uint x, uint y, uint pitch); }; MoviePlayer *makeMoviePlayer(AGOSEngine_Feeble *vm, const char *name); diff --git a/engines/mohawk/jpeg.h b/engines/mohawk/jpeg.h index 086e78332f..ec87b1e7af 100644 --- a/engines/mohawk/jpeg.h +++ b/engines/mohawk/jpeg.h @@ -45,6 +45,7 @@ public: ~JPEGDecoder(); Graphics::Surface *decodeImage(Common::SeekableReadStream *stream); + Graphics::PixelFormat getPixelFormat() const { return _pixelFormat; } private: Graphics::PixelFormat _pixelFormat; diff --git a/engines/mohawk/video/cinepak.h b/engines/mohawk/video/cinepak.h index 43cc22bfc9..3f4cbba17c 100644 --- a/engines/mohawk/video/cinepak.h +++ b/engines/mohawk/video/cinepak.h @@ -65,6 +65,7 @@ public: ~CinepakDecoder(); Graphics::Surface *decodeImage(Common::SeekableReadStream *stream); + Graphics::PixelFormat getPixelFormat() const { return _pixelFormat; } private: CinepakFrame _curFrame; diff --git a/engines/mohawk/video/qtrle.h b/engines/mohawk/video/qtrle.h index fdccf626a6..2832bd6b24 100644 --- a/engines/mohawk/video/qtrle.h +++ b/engines/mohawk/video/qtrle.h @@ -37,6 +37,7 @@ public: ~QTRLEDecoder(); Graphics::Surface *decodeImage(Common::SeekableReadStream *stream); + Graphics::PixelFormat getPixelFormat() const { return _pixelFormat; } private: byte _bitsPerPixel; diff --git a/engines/mohawk/video/rpza.h b/engines/mohawk/video/rpza.h index b9522ec2e3..c6d0ada6f5 100644 --- a/engines/mohawk/video/rpza.h +++ b/engines/mohawk/video/rpza.h @@ -37,6 +37,7 @@ public: ~RPZADecoder() { delete _surface; } Graphics::Surface *decodeImage(Common::SeekableReadStream *stream); + Graphics::PixelFormat getPixelFormat() const { return _pixelFormat; } private: Graphics::Surface *_surface; diff --git a/engines/mohawk/video/smc.h b/engines/mohawk/video/smc.h index 331fddb9a5..c52226100e 100644 --- a/engines/mohawk/video/smc.h +++ b/engines/mohawk/video/smc.h @@ -43,6 +43,7 @@ public: ~SMCDecoder() { delete _surface; } Graphics::Surface *decodeImage(Common::SeekableReadStream *stream); + Graphics::PixelFormat getPixelFormat() const { return Graphics::PixelFormat::createFormatCLUT8(); } private: Graphics::Surface *_surface; diff --git a/engines/saga/introproc_saga2.cpp b/engines/saga/introproc_saga2.cpp index 0af31dae61..7491815303 100644 --- a/engines/saga/introproc_saga2.cpp +++ b/engines/saga/introproc_saga2.cpp @@ -40,13 +40,7 @@ namespace Saga { int Scene::DinoStartProc() { _vm->_gfx->showCursor(false); - Graphics::SmackerDecoder *smkDecoder = new Graphics::SmackerDecoder(_vm->_mixer); - Graphics::VideoPlayer *player = new Graphics::VideoPlayer(smkDecoder); - if (smkDecoder->loadFile("testvid.smk")) - player->playVideo(); // Play introduction - smkDecoder->closeFile(); - delete player; - delete smkDecoder; + playMovie("testvid.smk"); // HACK: Forcibly quit here _vm->quitGame(); @@ -57,16 +51,8 @@ int Scene::DinoStartProc() { int Scene::FTA2StartProc() { _vm->_gfx->showCursor(false); - Graphics::SmackerDecoder *smkDecoder = new Graphics::SmackerDecoder(_vm->_mixer); - Graphics::VideoPlayer *player = new Graphics::VideoPlayer(smkDecoder); - if (smkDecoder->loadFile("trimark.smk")) - player->playVideo(); // Show Ignite logo - smkDecoder->closeFile(); - if (smkDecoder->loadFile("intro.smk")) - player->playVideo(); // Play introduction - smkDecoder->closeFile(); - delete player; - delete smkDecoder; + playMovie("trimark.smk"); + playMovie("intro.smk"); // HACK: Forcibly quit here _vm->quitGame(); @@ -100,18 +86,41 @@ int Scene::FTA2EndProc(FTA2Endings whichEnding) { _vm->_gfx->showCursor(false); // Play ending - Graphics::SmackerDecoder *smkDecoder = new Graphics::SmackerDecoder(_vm->_mixer); - Graphics::VideoPlayer *player = new Graphics::VideoPlayer(smkDecoder); - if (smkDecoder->loadFile(videoName)) { - player->playVideo(); - smkDecoder->closeFile(); - } - delete player; - delete smkDecoder; + playMovie(videoName); return SUCCESS; } +void Scene::playMovie(const char *filename) { + Graphics::SmackerDecoder *smkDecoder = new Graphics::SmackerDecoder(_vm->_mixer); + + if (!smkDecoder->loadFile(filename)) + return; + + uint16 x = (g_system->getWidth() - smkDecoder->getWidth()) / 2; + uint16 y = (g_system->getHeight() - smkDecoder->getHeight()) / 2; + + while (!_vm->shouldQuit() && !smkDecoder->endOfVideo()) { + if (smkDecoder->needsUpdate()) { + Graphics::Surface *frame = smkDecoder->decodeNextFrame(); + if (frame) { + _vm->_system->copyRectToScreen((byte *)frame->pixels, frame->pitch, x, y, frame->w, frame->h); + + if (smkDecoder->hasDirtyPalette()) + smkDecoder->setSystemPalette(); + + _vm->_system->updateScreen(); + } + } + + Common::Event event; + while (_vm->_system->getEventManager()->pollEvent(event)) + ; + + _vm->_system->delayMillis(10); + } +} + } // End of namespace Saga #endif diff --git a/engines/saga/scene.h b/engines/saga/scene.h index 4fbde560b5..0131e01abb 100644 --- a/engines/saga/scene.h +++ b/engines/saga/scene.h @@ -424,6 +424,7 @@ class Scene { int DinoStartProc(); int FTA2StartProc(); int FTA2EndProc(FTA2Endings whichEnding); + void playMovie(const char *filename); void IHNMLoadCutaways(); bool checkKey(); diff --git a/engines/sci/console.cpp b/engines/sci/console.cpp index 73b9788ca0..f2e5601c9e 100644 --- a/engines/sci/console.cpp +++ b/engines/sci/console.cpp @@ -222,42 +222,48 @@ void Console::postEnter() { if (!_videoFile.empty()) { _engine->_gfxCursor->kernelHide(); + Graphics::VideoDecoder *videoDecoder = 0; + if (_videoFile.hasSuffix(".seq")) { - SeqDecoder *seqDecoder = new SeqDecoder(); - Graphics::VideoPlayer *player = new Graphics::VideoPlayer(seqDecoder); - if (seqDecoder->loadFile(_videoFile.c_str(), _videoFrameDelay)) - player->playVideo(); - else - DebugPrintf("Failed to open movie file %s\n", _videoFile.c_str()); - seqDecoder->closeFile(); - delete player; - delete seqDecoder; - } else if (_videoFile.hasSuffix(".avi")) { - Graphics::AviDecoder *aviDecoder = new Graphics::AviDecoder(g_system->getMixer()); - Graphics::VideoPlayer *player = new Graphics::VideoPlayer(aviDecoder); - if (aviDecoder->loadFile(_videoFile.c_str())) - player->playVideo(); - else - DebugPrintf("Failed to open movie file %s\n", _videoFile.c_str()); - aviDecoder->closeFile(); - delete player; - delete aviDecoder; - } else if (_videoFile.hasSuffix(".vmd")) { + videoDecoder = new SeqDecoder(); + ((SeqDecoder *)videoDecoder)->setFrameDelay(_videoFrameDelay); #ifdef ENABLE_SCI32 - VMDDecoder *vmdDecoder = new VMDDecoder(g_system->getMixer()); - Graphics::VideoPlayer *player = new Graphics::VideoPlayer(vmdDecoder); - if (vmdDecoder->loadFile(_videoFile.c_str())) - player->playVideo(); - else - DebugPrintf("Failed to open movie file %s\n", _videoFile.c_str()); - vmdDecoder->closeFile(); - delete player; - delete vmdDecoder; + } else if (_videoFile.hasSuffix(".vmd")) { + videoDecoder = new VMDDecoder(g_system->getMixer()); #endif + } else if (_videoFile.hasSuffix(".avi")) { + videoDecoder = new Graphics::AviDecoder(g_system->getMixer()); } - _engine->_gfxCursor->kernelShow(); + if (videoDecoder && videoDecoder->loadFile(_videoFile)) { + uint16 x = (g_system->getWidth() - videoDecoder->getWidth()) / 2; + uint16 y = (g_system->getHeight() - videoDecoder->getHeight()) / 2; + + while (!g_engine->shouldQuit() && !videoDecoder->endOfVideo()) { + if (videoDecoder->needsUpdate()) { + Graphics::Surface *frame = videoDecoder->decodeNextFrame(); + if (frame) { + g_system->copyRectToScreen((byte *)frame->pixels, frame->pitch, x, y, frame->w, frame->h); + if (videoDecoder->hasDirtyPalette()) + videoDecoder->setSystemPalette(); + + g_system->updateScreen(); + } + } + + Common::Event event; + while (g_system->getEventManager()->pollEvent(event)) + ; + + g_system->delayMillis(10); + } + + delete videoDecoder; + } else + warning("Could not play video %s\n", _videoFile.c_str()); + + _engine->_gfxCursor->kernelShow(); _videoFile.clear(); _videoFrameDelay = 0; } diff --git a/engines/sci/engine/kgraphics.cpp b/engines/sci/engine/kgraphics.cpp index da18bc7e03..d587790b6c 100644 --- a/engines/sci/engine/kgraphics.cpp +++ b/engines/sci/engine/kgraphics.cpp @@ -1078,8 +1078,6 @@ reg_t kDisplay(EngineState *s, int argc, reg_t *argv) { } reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) { - bool playedVideo = false; - // Hide the cursor if it's showing and then show it again if it was // previously visible. bool reshowCursor; @@ -1087,30 +1085,29 @@ reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) { reshowCursor = g_sci->_gfxCursor->isVisible(); if (reshowCursor) g_sci->_gfxCursor->kernelHide(); + + Graphics::VideoDecoder *videoDecoder = 0; if (argv[0].segment != 0) { + Common::String filename = s->_segMan->getString(argv[0]); + if (g_sci->getPlatform() == Common::kPlatformMacintosh) { // Mac QuickTime // The only argument is the string for the video - warning("TODO: Play QuickTime movie '%s'", s->_segMan->getString(argv[0]).c_str()); + warning("TODO: Play QuickTime movie '%s'", filename.c_str()); return s->r_acc; } else { // DOS SEQ // SEQ's are called with no subops, just the string and delay - Common::String filename = s->_segMan->getString(argv[0]); - int delay = argv[1].toUint16(); // Time between frames in ticks - SeqDecoder *seqDecoder = new SeqDecoder(); - Graphics::VideoPlayer *player = new Graphics::VideoPlayer(seqDecoder); - if (seqDecoder->loadFile(filename.c_str(), delay)) { - player->playVideo(); - playedVideo = true; - } else { + seqDecoder->setFrameDelay(argv[1].toUint16()); // Time between frames in ticks + videoDecoder = seqDecoder; + + if (!videoDecoder->loadFile(filename)) { warning("Failed to open movie file %s", filename.c_str()); + delete videoDecoder; + videoDecoder = 0; } - seqDecoder->closeFile(); - delete player; - delete seqDecoder; } } else { // Windows AVI (Macintosh QuickTime? Need to check KQ6 Macintosh) @@ -1130,17 +1127,13 @@ reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) { switch (argv[0].toUint16()) { case 0: { Common::String filename = s->_segMan->getString(argv[1]); - Graphics::AviDecoder *aviDecoder = new Graphics::AviDecoder(g_system->getMixer()); - Graphics::VideoPlayer *player = new Graphics::VideoPlayer(aviDecoder); - if (aviDecoder->loadFile(filename.c_str())) { - player->playVideo(); - playedVideo = true; - } else { + videoDecoder = new Graphics::AviDecoder(g_system->getMixer()); + + if (!videoDecoder->loadFile(filename.c_str())) { warning("Failed to open movie file %s", filename.c_str()); + delete videoDecoder; + videoDecoder = 0; } - aviDecoder->closeFile(); - delete player; - delete aviDecoder; break; } default: @@ -1148,8 +1141,33 @@ reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) { } } - if (playedVideo) + if (videoDecoder) { + uint16 x = (g_system->getWidth() - videoDecoder->getWidth()) / 2; + uint16 y = (g_system->getHeight() - videoDecoder->getHeight()) / 2; + + while (!g_engine->shouldQuit() && !videoDecoder->endOfVideo()) { + if (videoDecoder->needsUpdate()) { + Graphics::Surface *frame = videoDecoder->decodeNextFrame(); + if (frame) { + g_system->copyRectToScreen((byte *)frame->pixels, frame->pitch, x, y, frame->w, frame->h); + + if (videoDecoder->hasDirtyPalette()) + videoDecoder->setSystemPalette(); + + g_system->updateScreen(); + } + } + + Common::Event event; + while (g_system->getEventManager()->pollEvent(event)) + ; + + g_system->delayMillis(10); + } + + delete videoDecoder; g_sci->_gfxScreen->kernelSyncWithFramebuffer(); + } if (reshowCursor) g_sci->_gfxCursor->kernelShow(); diff --git a/engines/sci/video/seq_decoder.cpp b/engines/sci/video/seq_decoder.cpp index ccce873a15..2c117ae329 100644 --- a/engines/sci/video/seq_decoder.cpp +++ b/engines/sci/video/seq_decoder.cpp @@ -35,10 +35,6 @@ namespace Sci { -// SEQ videos always run at 320x200 -#define SCREEN_WIDTH 320 -#define SCREEN_HEIGHT 200 - enum seqPalTypes { kSeqPalVariable = 0, kSeqPalConstant = 1 @@ -49,26 +45,24 @@ enum seqFrameTypes { kSeqFrameDiff = 1 }; -SeqDecoder::~SeqDecoder() { - closeFile(); +SeqDecoder::SeqDecoder() { + _fileStream = 0; + _surface = 0; + _dirtyPalette = false; } -bool SeqDecoder::loadFile(const char *fileName, int frameDelay) { - closeFile(); +SeqDecoder::~SeqDecoder() { + close(); +} - _fileStream = SearchMan.createReadStreamForMember(fileName); - if (!_fileStream) - return false; +bool SeqDecoder::load(Common::SeekableReadStream &stream) { + close(); - // Seek to the first frame - _videoInfo.currentFrame = -1; + _fileStream = &stream; + _surface = new Graphics::Surface(); + _surface->create(SEQ_SCREEN_WIDTH, SEQ_SCREEN_HEIGHT, 1); - _videoInfo.width = SCREEN_WIDTH; - _videoInfo.height = SCREEN_HEIGHT; - _videoInfo.frameCount = _fileStream->readUint16LE(); - // Our frameDelay is calculated in 1/100 ms, so we convert it here - _videoInfo.frameDelay = 100 * frameDelay * 1000 / 60; - _videoFrameBuffer = new byte[_videoInfo.width * _videoInfo.height]; + _frameCount = _fileStream->readUint16LE(); // Set palette int paletteSize = _fileStream->readUint32LE(); @@ -81,39 +75,38 @@ bool SeqDecoder::loadFile(const char *fileName, int frameDelay) { uint16 palColorStart = READ_LE_UINT16(paletteData + 25); uint16 palColorCount = READ_LE_UINT16(paletteData + 29); - byte palette[256 * 4]; int palOffset = 37; for (uint16 colorNo = palColorStart; colorNo < palColorStart + palColorCount; colorNo++) { if (palFormat == kSeqPalVariable) palOffset++; - palette[colorNo * 4 + 0] = paletteData[palOffset++]; - palette[colorNo * 4 + 1] = paletteData[palOffset++]; - palette[colorNo * 4 + 2] = paletteData[palOffset++]; - palette[colorNo * 4 + 3] = 0; + _palette[colorNo * 3 + 0] = paletteData[palOffset++]; + _palette[colorNo * 3 + 1] = paletteData[palOffset++]; + _palette[colorNo * 3 + 2] = paletteData[palOffset++]; } - g_system->setPalette(palette, 0, 256); - + _dirtyPalette = true; delete[] paletteData; - - _videoInfo.firstframeOffset = _fileStream->pos(); - return true; } -void SeqDecoder::closeFile() { +void SeqDecoder::close() { if (!_fileStream) return; + _frameDelay = 0; + delete _fileStream; _fileStream = 0; - delete[] _videoFrameBuffer; - _videoFrameBuffer = 0; + _surface->free(); + delete _surface; + _surface = 0; + + reset(); } -bool SeqDecoder::decodeNextFrame() { +Graphics::Surface *SeqDecoder::decodeNextFrame() { int16 frameWidth = _fileStream->readUint16LE(); int16 frameHeight = _fileStream->readUint16LE(); int16 frameLeft = _fileStream->readUint16LE(); @@ -129,42 +122,41 @@ bool SeqDecoder::decodeNextFrame() { _fileStream->seek(offset); - _videoInfo.currentFrame++; - - if (_videoInfo.currentFrame == 0) - _videoInfo.startTime = g_system->getMillis(); - if (frameType == kSeqFrameFull) { - byte *dst = _videoFrameBuffer + frameTop * SCREEN_WIDTH + frameLeft; + byte *dst = (byte *)_surface->pixels + frameTop * SEQ_SCREEN_WIDTH + frameLeft; byte *linebuf = new byte[frameWidth]; do { _fileStream->read(linebuf, frameWidth); memcpy(dst, linebuf, frameWidth); - dst += SCREEN_WIDTH; + dst += SEQ_SCREEN_WIDTH; } while (--frameHeight); delete[] linebuf; } else { byte *buf = new byte[frameSize]; _fileStream->read(buf, frameSize); - decodeFrame(buf, rleSize, buf + rleSize, frameSize - rleSize, _videoFrameBuffer + SCREEN_WIDTH * frameTop, frameLeft, frameWidth, frameHeight, colorKey); + decodeFrame(buf, rleSize, buf + rleSize, frameSize - rleSize, (byte *)_surface->pixels + SEQ_SCREEN_WIDTH * frameTop, frameLeft, frameWidth, frameHeight, colorKey); delete[] buf; } - return !endOfVideo(); + if (_curFrame == -1) + _startTime = g_system->getMillis(); + + _curFrame++; + return _surface; } #define WRITE_TO_BUFFER(n) \ - if (writeRow * SCREEN_WIDTH + writeCol + (n) > SCREEN_WIDTH * height) { \ + if (writeRow * SEQ_SCREEN_WIDTH + writeCol + (n) > SEQ_SCREEN_WIDTH * height) { \ warning("SEQ player: writing out of bounds, aborting"); \ return false; \ } \ if (litPos + (n) > litSize) { \ warning("SEQ player: reading out of bounds, aborting"); \ } \ - memcpy(dest + writeRow * SCREEN_WIDTH + writeCol, litData + litPos, n); + memcpy(dest + writeRow * SEQ_SCREEN_WIDTH + writeCol, litData + litPos, n); bool SeqDecoder::decodeFrame(byte *rleData, int rleSize, byte *litData, int litSize, byte *dest, int left, int width, int height, int colorKey) { int writeRow = 0; diff --git a/engines/sci/video/seq_decoder.h b/engines/sci/video/seq_decoder.h index 7c810db05d..416abb78fa 100644 --- a/engines/sci/video/seq_decoder.h +++ b/engines/sci/video/seq_decoder.h @@ -26,40 +26,50 @@ #ifndef SEQ_DECODER_H #define SEQ_DECODER_H -#include "graphics/video/video_player.h" +#include "graphics/video/video_decoder.h" namespace Sci { /** * Implementation of the Sierra SEQ decoder, used in KQ6 DOS floppy/CD and GK1 DOS */ -class SeqDecoder : public Graphics::VideoDecoder { +class SeqDecoder : public Graphics::FixedRateVideoDecoder { public: - SeqDecoder() {} + SeqDecoder(); virtual ~SeqDecoder(); - /** - * Load a SEQ encoded video file - * @param filename the filename to load - */ - bool loadFile(const char *fileName) { return loadFile(fileName, 10); } + bool load(Common::SeekableReadStream &stream); + void close(); - /** - * Load a SEQ encoded video file - * @param filename the filename to load - * @param frameDelay the delay between frames, in ticks - */ - bool loadFile(const char *fileName, int frameDelay); + void setFrameDelay(int frameDelay) { _frameDelay = frameDelay; } - /** - * Close a SEQ encoded video file - */ - void closeFile(); - - bool decodeNextFrame(); + bool isVideoLoaded() const { return _fileStream != 0; } + uint16 getWidth() const { return SEQ_SCREEN_WIDTH; } + uint16 getHeight() const { return SEQ_SCREEN_HEIGHT; } + uint32 getFrameCount() const { return _frameCount; } + Graphics::Surface *decodeNextFrame(); + Graphics::PixelFormat getPixelFormat() const { return Graphics::PixelFormat::createFormatCLUT8(); } + byte *getPalette() { _dirtyPalette = false; return _palette; } + bool hasDirtyPalette() const { return _dirtyPalette; } + +protected: + Common::Rational getFrameRate() const { assert(_frameDelay); return Common::Rational(60, _frameDelay); } private: + enum { + SEQ_SCREEN_WIDTH = 320, + SEQ_SCREEN_HEIGHT = 200 + }; + bool decodeFrame(byte *rleData, int rleSize, byte *litData, int litSize, byte *dest, int left, int width, int height, int colorKey); + + uint16 _width, _height; + uint16 _frameDelay; + Common::SeekableReadStream *_fileStream; + byte _palette[256 * 3]; + bool _dirtyPalette; + uint32 _frameCount; + Graphics::Surface *_surface; }; } // End of namespace Sci diff --git a/engines/sci/video/vmd_decoder.cpp b/engines/sci/video/vmd_decoder.cpp index 9e95521ebb..93132bc5d6 100644 --- a/engines/sci/video/vmd_decoder.cpp +++ b/engines/sci/video/vmd_decoder.cpp @@ -27,7 +27,6 @@ #include "sci/video/vmd_decoder.h" -#include "common/archive.h" #include "common/endian.h" #include "common/util.h" #include "common/stream.h" @@ -42,54 +41,39 @@ namespace Sci { VMDDecoder::VMDDecoder(Audio::Mixer *mixer) : _mixer(mixer) { _vmdDecoder = new Graphics::Vmd(new Graphics::PaletteLUT(5, Graphics::PaletteLUT::kPaletteYUV)); + _surface = 0; + _dirtyPalette = false; + _fileStream = 0; } VMDDecoder::~VMDDecoder() { - closeFile(); -} - -uint32 VMDDecoder::getFrameWaitTime() { - return _vmdDecoder->getFrameWaitTime(); + close(); } -bool VMDDecoder::loadFile(const char *fileName) { - closeFile(); +bool VMDDecoder::load(Common::SeekableReadStream &stream) { + close(); - _fileStream = SearchMan.createReadStreamForMember(fileName); - if (!_fileStream) + if (!_vmdDecoder->load(stream)) return false; - if (!_vmdDecoder->load(*_fileStream)) - return false; + _fileStream = &stream; - if (_vmdDecoder->getFeatures() & Graphics::CoktelVideo::kFeaturesPalette) { - getPalette(); - setPalette(_palette); - } + if (_vmdDecoder->getFeatures() & Graphics::CoktelVideo::kFeaturesPalette) + loadPaletteFromVMD(); if (_vmdDecoder->getFeatures() & Graphics::CoktelVideo::kFeaturesSound) _vmdDecoder->enableSound(*_mixer); - _videoInfo.width = _vmdDecoder->getWidth(); - _videoInfo.height = _vmdDecoder->getHeight(); - _videoInfo.frameCount = _vmdDecoder->getFramesCount(); - _videoInfo.frameRate = _vmdDecoder->getFrameRate(); - _videoInfo.frameDelay = _videoInfo.frameRate * 100; - _videoInfo.currentFrame = -1; - _videoInfo.firstframeOffset = 0; // not really necessary for VMDs - if (_vmdDecoder->hasExtraData()) warning("This VMD video has extra embedded data, which is currently not handled"); - _videoFrameBuffer = new byte[_videoInfo.width * _videoInfo.height]; - memset(_videoFrameBuffer, 0, _videoInfo.width * _videoInfo.height); - - _vmdDecoder->setVideoMemory(_videoFrameBuffer, _videoInfo.width, _videoInfo.height); - + _surface = new Graphics::Surface(); + _surface->create(_vmdDecoder->getWidth(), _vmdDecoder->getHeight(), 1); + _vmdDecoder->setVideoMemory((byte *)_surface->pixels, _surface->w, _surface->h); return true; } -void VMDDecoder::closeFile() { +void VMDDecoder::close() { if (!_fileStream) return; @@ -98,27 +82,27 @@ void VMDDecoder::closeFile() { delete _fileStream; _fileStream = 0; - delete[] _videoFrameBuffer; - _videoFrameBuffer = 0; -} - -bool VMDDecoder::decodeNextFrame() { - _videoInfo.currentFrame++; + _surface->free(); + delete _surface; + _surface = 0; - if (_videoInfo.currentFrame == 0) - _videoInfo.startTime = g_system->getMillis(); + reset(); +} +Graphics::Surface *VMDDecoder::decodeNextFrame() { Graphics::CoktelVideo::State state = _vmdDecoder->nextFrame(); - if (state.flags & Graphics::CoktelVideo::kStatePalette) { - getPalette(); - setPalette(_palette); - } + if (state.flags & Graphics::CoktelVideo::kStatePalette) + loadPaletteFromVMD(); + + if (_curFrame == -1) + _startTime = g_system->getMillis(); - return !endOfVideo(); + _curFrame++; + return _surface; } -void VMDDecoder::getPalette() { +void VMDDecoder::loadPaletteFromVMD() { const byte *pal = _vmdDecoder->getPalette(); for (int i = 0; i < 256; i++) { @@ -126,6 +110,8 @@ void VMDDecoder::getPalette() { _palette[i * 3 + 1] = pal[i * 3 + 1] << 2; _palette[i * 3 + 2] = pal[i * 3 + 2] << 2; } + + _dirtyPalette = true; } } // End of namespace Graphics diff --git a/engines/sci/video/vmd_decoder.h b/engines/sci/video/vmd_decoder.h index 628be24e82..231da9202e 100644 --- a/engines/sci/video/vmd_decoder.h +++ b/engines/sci/video/vmd_decoder.h @@ -29,7 +29,7 @@ #define GRAPHICS_VIDEO_VMD_DECODER_H #include "graphics/video/coktelvideo/coktelvideo.h" -#include "graphics/video/video_player.h" +#include "graphics/video/video_decoder.h" #include "sound/mixer.h" namespace Sci { @@ -49,32 +49,37 @@ namespace Sci { * - Shivers 2: Harvest of Souls * - Torin's Passage */ -class VMDDecoder : public Graphics::VideoDecoder { +class VMDDecoder : public Graphics::FixedRateVideoDecoder { public: VMDDecoder(Audio::Mixer *mixer); virtual ~VMDDecoder(); uint32 getFrameWaitTime(); - /** - * Load a VMD encoded video file - * @param filename the filename to load - */ - bool loadFile(const char *filename); + bool load(Common::SeekableReadStream &stream); + void close(); - /** - * Close a VMD encoded video file - */ - void closeFile(); + bool isVideoLoaded() const { return _fileStream != 0; } + uint16 getWidth() const { return _surface->w; } + uint16 getHeight() const { return _surface->h; } + uint32 getFrameCount() const { return _vmdDecoder->getFramesCount(); } + Graphics::Surface *decodeNextFrame(); + Graphics::PixelFormat getPixelFormat() const { return Graphics::PixelFormat::createFormatCLUT8(); } + byte *getPalette() { _dirtyPalette = false; return _palette; } + bool hasDirtyPalette() const { return _dirtyPalette; } - bool decodeNextFrame(); +protected: + Common::Rational getFrameRate() const { return _vmdDecoder->getFrameRate(); } private: Graphics::Vmd *_vmdDecoder; Audio::Mixer *_mixer; + Graphics::Surface *_surface; + Common::SeekableReadStream *_fileStream; byte _palette[256 * 3]; + bool _dirtyPalette; - void getPalette(); + void loadPaletteFromVMD(); }; } // End of namespace Graphics diff --git a/engines/scumm/he/animation_he.cpp b/engines/scumm/he/animation_he.cpp index 5bd60d32e3..9f1bf22c38 100644 --- a/engines/scumm/he/animation_he.cpp +++ b/engines/scumm/he/animation_he.cpp @@ -46,23 +46,21 @@ int MoviePlayer::getImageNum() { } int MoviePlayer::load(const char *filename, int flags, int image) { - if (isVideoLoaded()) { - closeFile(); - } + if (isVideoLoaded()) + close(); if (!loadFile(filename)) { warning("Failed to load video file %s", filename); return -1; } + debug(1, "Playing video %s", filename); - if (flags & 2) { + if (flags & 2) _vm->_wiz->createWizEmptyImage(image, 0, 0, getWidth(), getHeight()); - } _flags = flags; _wizResNum = image; - return 0; } @@ -70,7 +68,11 @@ void MoviePlayer::copyFrameToBuffer(byte *dst, int dstType, uint x, uint y, uint uint h = getHeight(); uint w = getWidth(); - byte *src = _videoFrameBuffer; + Graphics::Surface *surface = decodeNextFrame(); + byte *src = (byte *)surface->pixels; + + if (hasDirtyPalette()) + _vm->setPaletteFromPtr(getPalette(), 256); if (_vm->_game.features & GF_16BIT_COLOR) { dst += y * pitch + x * 2; @@ -102,14 +104,11 @@ void MoviePlayer::copyFrameToBuffer(byte *dst, int dstType, uint x, uint y, uint } void MoviePlayer::handleNextFrame() { - if (!isVideoLoaded()) { + if (!isVideoLoaded()) return; - } VirtScreen *pvs = &_vm->_virtscr[kMainVirtScreen]; - decodeNextFrame(); - if (_flags & 2) { uint8 *dstPtr = _vm->getResourceAddress(rtImage, _wizResNum); assert(dstPtr); @@ -129,11 +128,7 @@ void MoviePlayer::handleNextFrame() { } if (endOfVideo()) - closeFile(); -} - -void MoviePlayer::setPalette(byte *pal) { - _vm->setPaletteFromPtr(pal, 256); + close(); } } // End of namespace Scumm diff --git a/engines/scumm/he/animation_he.h b/engines/scumm/he/animation_he.h index 86ded31940..34794b35ac 100644 --- a/engines/scumm/he/animation_he.h +++ b/engines/scumm/he/animation_he.h @@ -56,9 +56,6 @@ public: void copyFrameToBuffer(byte *dst, int dstType, uint x, uint y, uint pitch); void handleNextFrame(); - -protected: - virtual void setPalette(byte *pal); }; } // End of namespace Scumm diff --git a/engines/scumm/he/script_v100he.cpp b/engines/scumm/he/script_v100he.cpp index e32409fe85..3555f55d95 100644 --- a/engines/scumm/he/script_v100he.cpp +++ b/engines/scumm/he/script_v100he.cpp @@ -2254,7 +2254,7 @@ void ScummEngine_v100he::o100_videoOps() { } } else if (_videoParams.status == 19) { // Stop video - _moviePlay->closeFile(); + _moviePlay->close(); } break; default: diff --git a/engines/scumm/he/script_v90he.cpp b/engines/scumm/he/script_v90he.cpp index 091bf5027b..6acc16a804 100644 --- a/engines/scumm/he/script_v90he.cpp +++ b/engines/scumm/he/script_v90he.cpp @@ -1437,7 +1437,7 @@ void ScummEngine_v90he::o90_videoOps() { } } else if (_videoParams.status == 165) { // Stop video - _moviePlay->closeFile(); + _moviePlay->close(); } break; default: diff --git a/engines/sword1/animation.cpp b/engines/sword1/animation.cpp index 221a20ba45..c0e7be7758 100644 --- a/engines/sword1/animation.cpp +++ b/engines/sword1/animation.cpp @@ -68,9 +68,10 @@ static const char *sequenceList[20] = { /////////////////////////////////////////////////////////////////////////////// MoviePlayer::MoviePlayer(SwordEngine *vm, Text *textMan, Audio::Mixer *snd, OSystem *system, Audio::SoundHandle *bgSoundHandle, Graphics::VideoDecoder *decoder, DecoderType decoderType) - : _vm(vm), _textMan(textMan), _snd(snd), _bgSoundHandle(bgSoundHandle), _system(system), VideoPlayer(decoder) { + : _vm(vm), _textMan(textMan), _snd(snd), _bgSoundHandle(bgSoundHandle), _system(system) { _bgSoundStream = NULL; _decoderType = decoderType; + _decoder = decoder; } MoviePlayer::~MoviePlayer() { @@ -86,11 +87,10 @@ bool MoviePlayer::load(uint32 id) { Common::File f; char filename[20]; - if (_decoderType == kVideoDecoderDXA) { + if (_decoderType == kVideoDecoderDXA) _bgSoundStream = Audio::SeekableAudioStream::openStreamFile(sequenceList[id]); - } else { + else _bgSoundStream = NULL; - } if (SwordEngine::_systemVars.showText) { sprintf(filename, "%s.txt", sequenceList[id]); @@ -146,9 +146,9 @@ bool MoviePlayer::load(uint32 id) { } void MoviePlayer::play() { - if (_bgSoundStream) { + if (_bgSoundStream) _snd->playStream(Audio::Mixer::kSFXSoundType, _bgSoundHandle, _bgSoundStream); - } + bool terminated = false; _textX = 0; @@ -179,7 +179,7 @@ void MoviePlayer::play() { void MoviePlayer::performPostProcessing(byte *screen) { if (!_movieTexts.empty()) { - if (_decoder->getCurFrame() + 1 == _movieTexts[0]->_startFrame) { + if (_decoder->getCurFrame() == _movieTexts[0]->_startFrame) { _textMan->makeTextSprite(2, (uint8 *)_movieTexts[0]->_text, 600, LETTER_COL); FrameHeader *frame = _textMan->giveSpriteData(2); @@ -188,7 +188,7 @@ void MoviePlayer::performPostProcessing(byte *screen) { _textX = 320 - _textWidth / 2; _textY = 420 - _textHeight; } - if (_decoder->getCurFrame() + 1 == _movieTexts[0]->_endFrame) { + if (_decoder->getCurFrame() == _movieTexts[0]->_endFrame) { _textMan->releaseText(2, false); delete _movieTexts.remove_at(0); } @@ -205,10 +205,10 @@ void MoviePlayer::performPostProcessing(byte *screen) { for (x = 0; x < _textWidth; x++) { switch (src[x]) { case BORDER_COL: - dst[x] = _decoder->getBlack(); + dst[x] = findBlackPalIndex(); break; case LETTER_COL: - dst[x] = _decoder->getWhite(); + dst[x] = findWhitePalIndex(); break; } } @@ -228,12 +228,12 @@ void MoviePlayer::performPostProcessing(byte *screen) { for (y = 0; y < _textHeight; y++) { if (_textY + y < frameY || _textY + y >= frameY + frameHeight) { - memset(dst + _textX, _decoder->getBlack(), _textWidth); + memset(dst + _textX, findBlackPalIndex(), _textWidth); } else { if (frameX > _textX) - memset(dst + _textX, _decoder->getBlack(), frameX - _textX); + memset(dst + _textX, findBlackPalIndex(), frameX - _textX); if (frameX + frameWidth < _textX + _textWidth) - memset(dst + frameX + frameWidth, _decoder->getBlack(), _textX + _textWidth - (frameX + frameWidth)); + memset(dst + frameX + frameWidth, findBlackPalIndex(), _textX + _textWidth - (frameX + frameWidth)); } dst += _system->getWidth(); @@ -244,25 +244,51 @@ void MoviePlayer::performPostProcessing(byte *screen) { } } -DXADecoderWithSound::DXADecoderWithSound(Audio::Mixer *mixer, Audio::SoundHandle *bgSoundHandle) - : _mixer(mixer), _bgSoundHandle(bgSoundHandle) { +bool MoviePlayer::playVideo() { + uint16 x = (g_system->getWidth() - _decoder->getWidth()) / 2; + uint16 y = (g_system->getHeight() - _decoder->getHeight()) / 2; + + while (!_vm->shouldQuit() && !_decoder->endOfVideo()) { + if (_decoder->needsUpdate()) { + Graphics::Surface *frame = _decoder->decodeNextFrame(); + if (frame) + _vm->_system->copyRectToScreen((byte *)frame->pixels, frame->pitch, x, y, frame->w, frame->h); + + if (_decoder->hasDirtyPalette()) + _decoder->setSystemPalette(); + + Graphics::Surface *screen = _vm->_system->lockScreen(); + performPostProcessing((byte *)screen->pixels); + _vm->_system->unlockScreen(); + _vm->_system->updateScreen(); + } + + Common::Event event; + while (_vm->_system->getEventManager()->pollEvent(event)) + if ((event.type == Common::EVENT_KEYDOWN && event.kbd.keycode == Common::KEYCODE_ESCAPE) || event.type == Common::EVENT_LBUTTONUP) + return false; + } + + return !_vm->shouldQuit(); } -int32 DXADecoderWithSound::getAudioLag() { - if (!_fileStream) - return 0; +byte MoviePlayer::findBlackPalIndex() { + return 0; +} - if (!_mixer->isSoundHandleActive(*_bgSoundHandle)) - return 0; +byte MoviePlayer::findWhitePalIndex() { + return 0xff; +} - int32 frameDelay = getFrameDelay(); - int32 videoTime = (_videoInfo.currentFrame + 1) * frameDelay; - int32 audioTime; +DXADecoderWithSound::DXADecoderWithSound(Audio::Mixer *mixer, Audio::SoundHandle *bgSoundHandle) + : _mixer(mixer), _bgSoundHandle(bgSoundHandle) { +} - const Audio::Timestamp ts = _mixer->getElapsedTime(*_bgSoundHandle); - audioTime = ts.convertToFramerate(100000).totalNumberOfFrames(); +uint32 DXADecoderWithSound::getElapsedTime() const { + if (_mixer->isSoundHandleActive(*_bgSoundHandle)) + return _mixer->getSoundElapsedTime(*_bgSoundHandle); - return videoTime - audioTime; + return VideoDecoder::getElapsedTime(); } /////////////////////////////////////////////////////////////////////////////// diff --git a/engines/sword1/animation.h b/engines/sword1/animation.h index e97fc083f6..82343f2800 100644 --- a/engines/sword1/animation.h +++ b/engines/sword1/animation.h @@ -28,7 +28,7 @@ #include "graphics/video/dxa_decoder.h" #include "graphics/video/smk_decoder.h" -#include "graphics/video/video_player.h" +#include "graphics/video/video_decoder.h" #include "common/array.h" @@ -64,18 +64,20 @@ public: DXADecoderWithSound(Audio::Mixer *mixer, Audio::SoundHandle *bgSoundHandle); ~DXADecoderWithSound() {} - int32 getAudioLag(); + uint32 getElapsedTime() const; + private: Audio::Mixer *_mixer; Audio::SoundHandle *_bgSoundHandle; }; -class MoviePlayer : public Graphics::VideoPlayer { +class MoviePlayer { public: MoviePlayer(SwordEngine *vm, Text *textMan, Audio::Mixer *snd, OSystem *system, Audio::SoundHandle *bgSoundHandle, Graphics::VideoDecoder *decoder, DecoderType decoderType); virtual ~MoviePlayer(); bool load(uint32 id); void play(); + protected: SwordEngine *_vm; Text *_textMan; @@ -85,10 +87,15 @@ protected: int _textX, _textY, _textWidth, _textHeight; DecoderType _decoderType; + Graphics::VideoDecoder *_decoder; Audio::SoundHandle *_bgSoundHandle; Audio::AudioStream *_bgSoundStream; + bool playVideo(); void performPostProcessing(byte *screen); + + byte findBlackPalIndex(); + byte findWhitePalIndex(); }; MoviePlayer *makeMoviePlayer(uint32 id, SwordEngine *vm, Text *textMan, Audio::Mixer *snd, OSystem *system); diff --git a/engines/sword2/animation.cpp b/engines/sword2/animation.cpp index 1c5c2e5f8c..c3f3e796b2 100644 --- a/engines/sword2/animation.cpp +++ b/engines/sword2/animation.cpp @@ -47,9 +47,10 @@ namespace Sword2 { /////////////////////////////////////////////////////////////////////////////// MoviePlayer::MoviePlayer(Sword2Engine *vm, Audio::Mixer *snd, OSystem *system, Audio::SoundHandle *bgSoundHandle, Graphics::VideoDecoder *decoder, DecoderType decoderType) - : _vm(vm), _snd(snd), _bgSoundHandle(bgSoundHandle), _system(system), VideoPlayer(decoder) { + : _vm(vm), _snd(snd), _bgSoundHandle(bgSoundHandle), _system(system) { _bgSoundStream = NULL; _decoderType = decoderType; + _decoder = decoder; } MoviePlayer:: ~MoviePlayer() { @@ -62,11 +63,10 @@ MoviePlayer:: ~MoviePlayer() { * @param id the id of the file */ bool MoviePlayer::load(const char *name) { - if (_decoderType == kVideoDecoderDXA) { + if (_decoderType == kVideoDecoderDXA) _bgSoundStream = Audio::SeekableAudioStream::openStreamFile(name); - } else { + else _bgSoundStream = NULL; - } _textSurface = NULL; @@ -97,13 +97,11 @@ void MoviePlayer::play(MovieText *movieTexts, uint32 numMovieTexts, uint32 leadI _currentMovieText = 0; _leadOut = leadOut; - if (leadIn) { + if (leadIn) _vm->_sound->playMovieSound(leadIn, kLeadInSound); - } - if (_bgSoundStream) { + if (_bgSoundStream) _snd->playStream(Audio::Mixer::kSFXSoundType, _bgSoundHandle, _bgSoundStream); - } bool terminated = false; @@ -186,12 +184,12 @@ void MoviePlayer::closeTextObject(uint32 index, byte *screen) { for (int y = 0; y < text->_textSprite.h; y++) { if (_textY + y < frameY || _textY + y >= frameY + frameHeight) { - memset(dst + _textX, _decoder->getBlack(), text->_textSprite.w); + memset(dst + _textX, findBlackPalIndex(), text->_textSprite.w); } else { if (frameX > _textX) - memset(dst + _textX, _decoder->getBlack(), frameX - _textX); + memset(dst + _textX, findBlackPalIndex(), frameX - _textX); if (frameX + frameWidth < _textX + text->_textSprite.w) - memset(dst + frameX + frameWidth, _decoder->getBlack(), _textX + text->_textSprite.w - (frameX + frameWidth)); + memset(dst + frameX + frameWidth, findBlackPalIndex(), _textX + text->_textSprite.w - (frameX + frameWidth)); } dst += _system->getWidth(); @@ -207,8 +205,8 @@ void MoviePlayer::closeTextObject(uint32 index, byte *screen) { void MoviePlayer::drawTextObject(uint32 index, byte *screen) { MovieText *text = &_movieTexts[index]; - byte white = _decoder->getWhite(); - byte black = _decoder->getBlack(); + byte white = findWhitePalIndex(); + byte black = findBlackPalIndex(); if (text->_textMem && _textSurface) { byte *src = text->_textSprite.data; @@ -240,7 +238,7 @@ void MoviePlayer::drawTextObject(uint32 index, byte *screen) { void MoviePlayer::performPostProcessing(byte *screen) { MovieText *text; - int frame = _decoder->getCurFrame() + 1; + int frame = _decoder->getCurFrame(); if (_currentMovieText < _numMovieTexts) { text = &_movieTexts[_currentMovieText]; @@ -272,25 +270,51 @@ void MoviePlayer::performPostProcessing(byte *screen) { } } -DXADecoderWithSound::DXADecoderWithSound(Audio::Mixer *mixer, Audio::SoundHandle *bgSoundHandle) - : _mixer(mixer), _bgSoundHandle(bgSoundHandle) { +bool MoviePlayer::playVideo() { + uint16 x = (g_system->getWidth() - _decoder->getWidth()) / 2; + uint16 y = (g_system->getHeight() - _decoder->getHeight()) / 2; + + while (!_vm->shouldQuit() && !_decoder->endOfVideo()) { + if (_decoder->needsUpdate()) { + Graphics::Surface *frame = _decoder->decodeNextFrame(); + if (frame) + _vm->_system->copyRectToScreen((byte *)frame->pixels, frame->pitch, x, y, frame->w, frame->h); + + if (_decoder->hasDirtyPalette()) + _decoder->setSystemPalette(); + + Graphics::Surface *screen = _vm->_system->lockScreen(); + performPostProcessing((byte *)screen->pixels); + _vm->_system->unlockScreen(); + _vm->_system->updateScreen(); + } + + Common::Event event; + while (_vm->_system->getEventManager()->pollEvent(event)) + if ((event.type == Common::EVENT_KEYDOWN && event.kbd.keycode == Common::KEYCODE_ESCAPE) || event.type == Common::EVENT_LBUTTONUP) + return false; + } + + return !_vm->shouldQuit(); } -int32 DXADecoderWithSound::getAudioLag() { - if (!_fileStream) - return 0; +byte MoviePlayer::findBlackPalIndex() { + return 0; +} - if (!_mixer->isSoundHandleActive(*_bgSoundHandle)) - return 0; +byte MoviePlayer::findWhitePalIndex() { + return 0xff; +} - int32 frameDelay = getFrameDelay(); - int32 videoTime = _videoInfo.currentFrame * frameDelay; - int32 audioTime; +DXADecoderWithSound::DXADecoderWithSound(Audio::Mixer *mixer, Audio::SoundHandle *bgSoundHandle) + : _mixer(mixer), _bgSoundHandle(bgSoundHandle) { +} - const Audio::Timestamp ts = _mixer->getElapsedTime(*_bgSoundHandle); - audioTime = ts.convertToFramerate(100000).totalNumberOfFrames(); +uint32 DXADecoderWithSound::getElapsedTime() const { + if (_mixer->isSoundHandleActive(*_bgSoundHandle)) + return _mixer->getSoundElapsedTime(*_bgSoundHandle); - return videoTime - audioTime; + return VideoDecoder::getElapsedTime(); } /////////////////////////////////////////////////////////////////////////////// diff --git a/engines/sword2/animation.h b/engines/sword2/animation.h index 7f9ae0ff2d..bbf83e264c 100644 --- a/engines/sword2/animation.h +++ b/engines/sword2/animation.h @@ -30,7 +30,7 @@ #include "graphics/video/dxa_decoder.h" #include "graphics/video/smk_decoder.h" -#include "graphics/video/video_player.h" +#include "graphics/video/video_decoder.h" #include "sound/mixer.h" #include "sword2/screen.h" @@ -63,13 +63,13 @@ public: DXADecoderWithSound(Audio::Mixer *mixer, Audio::SoundHandle *bgSoundHandle); ~DXADecoderWithSound() {} - int32 getAudioLag(); + uint32 getElapsedTime() const; private: Audio::Mixer *_mixer; Audio::SoundHandle *_bgSoundHandle; }; -class MoviePlayer : public Graphics::VideoPlayer { +class MoviePlayer { public: MoviePlayer(Sword2Engine *vm, Audio::Mixer *snd, OSystem *system, Audio::SoundHandle *bgSoundHandle, Graphics::VideoDecoder *decoder, DecoderType decoderType); virtual ~MoviePlayer(); @@ -89,6 +89,7 @@ protected: int _textX, _textY; DecoderType _decoderType; + Graphics::VideoDecoder *_decoder; Audio::SoundHandle *_bgSoundHandle; Audio::AudioStream *_bgSoundStream; @@ -96,10 +97,14 @@ protected: int _leadOutFrame; void performPostProcessing(byte *screen); + bool playVideo(); void openTextObject(uint32 index); void closeTextObject(uint32 index, byte *screen); void drawTextObject(uint32 index, byte *screen); + + byte findBlackPalIndex(); + byte findWhitePalIndex(); }; MoviePlayer *makeMoviePlayer(const char *name, Sword2Engine *vm, Audio::Mixer *snd, OSystem *system); diff --git a/engines/tucker/sequences.cpp b/engines/tucker/sequences.cpp index 02cc695e7c..68f5301a80 100644 --- a/engines/tucker/sequences.cpp +++ b/engines/tucker/sequences.cpp @@ -537,9 +537,9 @@ void AnimationSequencePlayer::mainLoop() { } // budttle2.flc is shorter in french version ; start the background music // earlier and skip any sounds effects - if (_seqNum == 19 && _flicPlayer[0].getFrameCount() == 126) { + if (_seqNum == 19 && _flicPlayer[0].getFrameCount() == 127) { _soundSeqDataIndex = 6; - _frameCounter = 80; + _frameCounter = 79; } } (this->*(_updateFunc[_updateFuncIndex].play))(); @@ -766,19 +766,21 @@ void AnimationSequencePlayer::openAnimation(int index, const char *fileName) { } bool AnimationSequencePlayer::decodeNextAnimationFrame(int index) { - bool framesLeft = _flicPlayer[index].decodeNextFrame(); + ::Graphics::Surface *surface = _flicPlayer[index].decodeNextFrame(); + if (_seqNum == 19) { - _flicPlayer[index].copyFrameToBuffer(_offscreenBuffer, 0, 0, kScreenWidth); + for (uint16 y = 0; (y < surface->h) && (y < kScreenHeight); y++) + memcpy(_offscreenBuffer + y * kScreenWidth, (byte *)surface->pixels + y * surface->pitch, surface->w); } else { _flicPlayer[index].copyDirtyRectsToBuffer(_offscreenBuffer, kScreenWidth); } + ++_frameCounter; - if (index == 0) { - if (_flicPlayer[index].paletteChanged()) { - getRGBPalette(index); - } - } - return framesLeft; + + if (index == 0 && _flicPlayer[index].hasDirtyPalette()) + getRGBPalette(index); + + return !_flicPlayer[index].endOfVideo(); } void AnimationSequencePlayer::loadIntroSeq17_18() { @@ -803,20 +805,23 @@ void AnimationSequencePlayer::playIntroSeq19_20() { // The intro credits animation. This uses 2 animations: the foreground one, which // is the actual intro credits, and the background one, which is an animation of // cogs, and is being replayed when an intro credit appears - if (_flicPlayer[0].getCurFrame() >= 116) { - if (!_flicPlayer[1].decodeNextFrame()) { + ::Graphics::Surface *surface = 0; + + if (_flicPlayer[0].getCurFrame() >= 117) { + surface = _flicPlayer[1].decodeNextFrame(); + if (_flicPlayer[1].endOfVideo()) _flicPlayer[1].reset(); - } } + bool framesLeft = decodeNextAnimationFrame(0); - for (int i = 0; i < kScreenWidth * kScreenHeight; ++i) { - if (_offscreenBuffer[i] == 0) { - _offscreenBuffer[i] = _flicPlayer[1].getPixel(i); - } - } - if (!framesLeft) { + + if (surface) + for (int i = 0; i < kScreenWidth * kScreenHeight; ++i) + if (_offscreenBuffer[i] == 0) + _offscreenBuffer[i] = *((byte *)surface->pixels + i); + + if (!framesLeft) _changeToNextSequence = true; - } } void AnimationSequencePlayer::displayLoadingScreen() { @@ -870,7 +875,7 @@ void AnimationSequencePlayer::loadIntroSeq3_4() { void AnimationSequencePlayer::playIntroSeq3_4() { if (!_updateScreenPicture) { bool framesLeft = decodeNextAnimationFrame(0); - if (_flicPlayer[0].getCurFrame() == 706) { + if (_flicPlayer[0].getCurFrame() == 707) { initPicPart4(); } if (!framesLeft) { @@ -909,13 +914,21 @@ void AnimationSequencePlayer::drawPic2Part10() { } void AnimationSequencePlayer::drawPic1Part10() { + ::Graphics::Surface *surface = _flicPlayer[0].decodeNextFrame(); + _flicPlayer[0].copyDirtyRectsToBuffer(_offscreenBuffer, kScreenWidth); + ++_frameCounter; + + if (_flicPlayer[0].hasDirtyPalette()) + getRGBPalette(0); + int offset = 0; for (int y = 0; y < kScreenHeight; ++y) { for (int x = 0; x < kScreenWidth; ++x) { - byte color = _flicPlayer[0].getPixel(offset); - if (color == 0) { + byte color = *((byte *)surface->pixels + offset); + + if (color == 0) color = _picBufPtr[800 + y * 640 + _updateScreenWidth + x]; - } + _offscreenBuffer[offset++] = color; } } @@ -930,22 +943,22 @@ void AnimationSequencePlayer::loadIntroSeq9_10() { } void AnimationSequencePlayer::playIntroSeq9_10() { - bool framesLeft = decodeNextAnimationFrame(0); - if (_flicPlayer[0].getCurFrame() >= 264 && _flicPlayer[0].getCurFrame() <= 295) { + if (_flicPlayer[0].getCurFrame() >= 265 && _flicPlayer[0].getCurFrame() <= 296) { drawPic1Part10(); _updateScreenWidth += 6; - } else if (_flicPlayer[0].getCurFrame() == 984) { + } else if (_flicPlayer[0].getCurFrame() == 985) { + decodeNextAnimationFrame(0); drawPic2Part10(); - } else if (_flicPlayer[0].getCurFrame() >= 988 && _flicPlayer[0].getCurFrame() <= 996) { + } else if (_flicPlayer[0].getCurFrame() >= 989 && _flicPlayer[0].getCurFrame() <= 997) { drawPic1Part10(); _updateScreenWidth -= 25; if (_updateScreenWidth < 0) { _updateScreenWidth = 0; } } - if (!framesLeft) { + + if (_flicPlayer[0].endOfVideo()) _changeToNextSequence = true; - } } void AnimationSequencePlayer::loadIntroSeq21_22() { diff --git a/graphics/module.mk b/graphics/module.mk index 15fbf092b3..ff6d7f8f60 100644 --- a/graphics/module.mk +++ b/graphics/module.mk @@ -26,7 +26,7 @@ MODULE_OBJS := \ video/flic_decoder.o \ video/mpeg_player.o \ video/smk_decoder.o \ - video/video_player.o \ + video/video_decoder.o \ video/codecs/msrle.o \ video/codecs/msvideo1.o \ video/coktelvideo/indeo3.o \ diff --git a/graphics/video/avi_decoder.cpp b/graphics/video/avi_decoder.cpp index 1e0fb389b3..944c9700bd 100644 --- a/graphics/video/avi_decoder.cpp +++ b/graphics/video/avi_decoder.cpp @@ -23,7 +23,6 @@ * */ -#include "common/archive.h" #include "common/endian.h" #include "common/file.h" #include "common/stream.h" @@ -49,6 +48,7 @@ AviDecoder::AviDecoder(Audio::Mixer *mixer, Audio::Mixer::SoundType soundType) : _audStream = NULL; _fileStream = NULL; _audHandle = new Audio::SoundHandle(); + _dirtyPalette = false; memset(_palette, 0, sizeof(_palette)); memset(&_wvInfo, 0, sizeof(PCMWAVEFORMAT)); memset(&_bmInfo, 0, sizeof(BITMAPINFOHEADER)); @@ -58,7 +58,7 @@ AviDecoder::AviDecoder(Audio::Mixer *mixer, Audio::Mixer::SoundType soundType) : } AviDecoder::~AviDecoder() { - closeFile(); + close(); delete _audHandle; } @@ -190,7 +190,7 @@ void AviDecoder::handleStreamHeader() { /*_palette[i * 4 + 3] = */_fileStream->readByte(); } - setPalette(_palette); + _dirtyPalette = true; } } else if (sHeader.streamType == ID_AUDS) { _audsHeader = sHeader; @@ -204,24 +204,16 @@ void AviDecoder::handleStreamHeader() { } } -bool AviDecoder::loadFile(const char *fileName) { - closeFile(); - - _fileStream = SearchMan.createReadStreamForMember(fileName); - if (!_fileStream) - return false; +bool AviDecoder::load(Common::SeekableReadStream &stream) { + close(); + _fileStream = &stream; _decodedHeader = false; - // Seek to the first frame - _videoInfo.currentFrame = -1; // Read chunks until we have decoded the header while (!_decodedHeader) runHandle(_fileStream->readUint32BE()); - _videoFrameBuffer = new byte[_header.width * _header.height]; - memset(_videoFrameBuffer, 0, _header.width * _header.height); - uint32 nextTag = _fileStream->readUint32BE(); // Throw out any JUNK section @@ -247,34 +239,24 @@ bool AviDecoder::loadFile(const char *fileName) { _mixer->playStream(_soundType, _audHandle, _audStream); debug (0, "Frames = %d, Dimensions = %d x %d", _header.totalFrames, _header.width, _header.height); - debug (0, "Frame Rate = %d", getFrameRate()); + debug (0, "Frame Rate = %d", _vidsHeader.rate / _vidsHeader.scale); if ((_audsHeader.scale != 0) && (_header.flags & AVIF_ISINTERLEAVED)) debug (0, "Sound Rate = %d", AUDIO_RATE); debug (0, "Video Codec = \'%s\'", tag2str(_vidsHeader.streamHandler)); - _videoInfo.firstframeOffset = _fileStream->pos(); - _videoInfo.width = _header.width; - _videoInfo.height = _header.height; - _videoInfo.frameCount = _header.totalFrames; - // Our frameDelay is calculated in 1/100 ms, so we convert it here - _videoInfo.frameDelay = _header.microSecondsPerFrame / 10; - if (!_videoCodec) return false; return true; } -void AviDecoder::closeFile() { +void AviDecoder::close() { if (!_fileStream) return; delete _fileStream; _fileStream = 0; - delete[] _videoFrameBuffer; - _videoFrameBuffer = 0; - // Deinitialize sound _mixer->stopHandle(*_audHandle); _audStream = 0; @@ -293,14 +275,26 @@ void AviDecoder::closeFile() { memset(&_vidsHeader, 0, sizeof(AVIStreamHeader)); memset(&_audsHeader, 0, sizeof(AVIStreamHeader)); memset(&_ixInfo, 0, sizeof(AVIOLDINDEX)); + + reset(); } -Surface *AviDecoder::getNextFrame() { +uint32 AviDecoder::getElapsedTime() const { + if (_audStream) + return _mixer->getSoundElapsedTime(*_audHandle); + + return VideoDecoder::getElapsedTime(); +} + +Surface *AviDecoder::decodeNextFrame() { uint32 nextTag = _fileStream->readUint32BE(); if (_fileStream->eos()) return NULL; + if (_curFrame == -1) + _startTime = g_system->getMillis(); + if (nextTag == ID_LIST) { // A list of audio/video chunks uint32 listSize = _fileStream->readUint32LE() - 4; @@ -312,7 +306,7 @@ Surface *AviDecoder::getNextFrame() { // Decode chunks in the list and see if we get a frame Surface *frame = NULL; while (_fileStream->pos() < startPos + (int32)listSize) { - Surface *temp = getNextFrame(); + Surface *temp = decodeNextFrame(); if (temp) frame = temp; } @@ -335,7 +329,7 @@ Surface *AviDecoder::getNextFrame() { } else if (getStreamType(nextTag) == 'dc' || getStreamType(nextTag) == 'id' || getStreamType(nextTag) == 'AM' || getStreamType(nextTag) == '32') { // Compressed Frame - _videoInfo.currentFrame++; + _curFrame++; uint32 chunkSize = _fileStream->readUint32LE(); if (chunkSize == 0) // Keep last frame on screen @@ -364,7 +358,7 @@ Surface *AviDecoder::getNextFrame() { _fileStream->readByte(); // Flags that don't serve us any purpose } - setPalette(_palette); + _dirtyPalette = true; // No alignment necessary. It's always even. } else if (nextTag == ID_JUNK) { @@ -372,56 +366,11 @@ Surface *AviDecoder::getNextFrame() { } else if (nextTag == ID_IDX1) { runHandle(ID_IDX1); } else - error ("Tag = \'%s\', %d", tag2str(nextTag), _fileStream->pos()); + error("Tag = \'%s\', %d", tag2str(nextTag), _fileStream->pos()); return NULL; } -bool AviDecoder::decodeNextFrame() { - Surface *surface = NULL; - - int32 curFrame = _videoInfo.currentFrame; - - while (!surface && !endOfVideo() && !_fileStream->eos()) - surface = getNextFrame(); - - if (curFrame == _videoInfo.currentFrame) { - warning("No video frame found"); - _videoInfo.currentFrame++; - } - - if (surface) - memcpy(_videoFrameBuffer, surface->pixels, _header.width * _header.height); - - if (_videoInfo.currentFrame == 0) - _videoInfo.startTime = g_system->getMillis(); - - return !endOfVideo(); -} - -int32 AviDecoder::getAudioLag() { - if (!_fileStream) - return 0; - - int32 frameDelay = getFrameDelay(); - int32 videoTime = (_videoInfo.currentFrame + 1) * frameDelay; - int32 audioTime; - - if (!_audStream) { - /* No audio. - Calculate the lag by how much time has gone by since the first frame - and how much time *should* have passed. - */ - - audioTime = (g_system->getMillis() - _videoInfo.startTime) * 100; - } else { - const Audio::Timestamp ts = _mixer->getElapsedTime(*_audHandle); - audioTime = ts.convertToFramerate(100000).totalNumberOfFrames(); - } - - return videoTime - audioTime; -} - Codec *AviDecoder::createCodec() { switch (_vidsHeader.streamHandler) { case ID_CRAM: @@ -437,11 +386,14 @@ Codec *AviDecoder::createCodec() { return NULL; } -Audio::QueuingAudioStream *AviDecoder::createAudioStream() { +PixelFormat AviDecoder::getPixelFormat() const { + assert(_videoCodec); + return _videoCodec->getPixelFormat(); +} - if (_wvInfo.tag == AVI_WAVE_FORMAT_PCM) { +Audio::QueuingAudioStream *AviDecoder::createAudioStream() { + if (_wvInfo.tag == AVI_WAVE_FORMAT_PCM) return Audio::makeQueuingAudioStream(AUDIO_RATE, false); - } if (_wvInfo.tag != 0) // No sound warning ("Unsupported AVI audio format %d", _wvInfo.tag); diff --git a/graphics/video/avi_decoder.h b/graphics/video/avi_decoder.h index 0f0431c70b..5f09992647 100644 --- a/graphics/video/avi_decoder.h +++ b/graphics/video/avi_decoder.h @@ -26,7 +26,7 @@ #ifndef GRAPHICS_AVI_PLAYER_H #define GRAPHICS_AVI_PLAYER_H -#include "graphics/video/video_player.h" +#include "graphics/video/video_decoder.h" #include "graphics/video/codecs/codec.h" #include "sound/audiostream.h" #include "sound/mixer.h" @@ -172,26 +172,27 @@ struct AVIStreamHeader { Common::Rect frame; }; -class AviDecoder : public VideoDecoder { +class AviDecoder : public FixedRateVideoDecoder { public: AviDecoder(Audio::Mixer *mixer, Audio::Mixer::SoundType soundType = Audio::Mixer::kPlainSoundType); virtual ~AviDecoder(); - /** - * Load an AVI video file - * @param filename the filename to load - */ - bool loadFile(const char *fileName); + bool load(Common::SeekableReadStream &stream); + void close(); - /** - * Close an AVI video file - */ - void closeFile(); + bool isVideoLoaded() const { return _fileStream != 0; } + uint16 getWidth() const { return _header.width; } + uint16 getHeight() const { return _header.height; } + uint32 getFrameCount() const { return _header.totalFrames; } + uint32 getElapsedTime() const; + Surface *decodeNextFrame(); + PixelFormat getPixelFormat() const; + byte *getPalette() { _dirtyPalette = false; return _palette; } + bool hasDirtyPalette() const { return _dirtyPalette; } - bool decodeNextFrame(); - int32 getAudioLag(); - int32 getFrameRate() { return _vidsHeader.rate / _vidsHeader.scale; } +protected: + Common::Rational getFrameRate() const { return Common::Rational(_vidsHeader.rate, _vidsHeader.scale); } private: Audio::Mixer *_mixer; @@ -202,7 +203,9 @@ private: AVIStreamHeader _vidsHeader; AVIStreamHeader _audsHeader; byte _palette[3 * 256]; + bool _dirtyPalette; + Common::SeekableReadStream *_fileStream; bool _decodedHeader; Codec *_videoCodec; @@ -223,8 +226,6 @@ private: static byte char2num(char c); static byte getStreamNum(uint32 tag); static uint16 getStreamType(uint32 tag); - - Surface *getNextFrame(); }; } // End of namespace Graphics diff --git a/graphics/video/codecs/codec.h b/graphics/video/codecs/codec.h index 124d1bc49d..4a280a81df 100644 --- a/graphics/video/codecs/codec.h +++ b/graphics/video/codecs/codec.h @@ -28,6 +28,7 @@ #include "common/stream.h" #include "graphics/surface.h" +#include "graphics/pixelformat.h" namespace Graphics { @@ -35,7 +36,9 @@ class Codec { public: Codec() {} virtual ~Codec() {} + virtual Surface *decodeImage(Common::SeekableReadStream *stream) = 0; + virtual PixelFormat getPixelFormat() const = 0; }; } // End of namespace Graphics diff --git a/graphics/video/codecs/msrle.h b/graphics/video/codecs/msrle.h index 634fe754ba..819e66a0bd 100644 --- a/graphics/video/codecs/msrle.h +++ b/graphics/video/codecs/msrle.h @@ -36,6 +36,7 @@ public: ~MSRLEDecoder(); Surface *decodeImage(Common::SeekableReadStream *stream); + PixelFormat getPixelFormat() const { return PixelFormat::createFormatCLUT8(); } private: byte _bitsPerPixel; diff --git a/graphics/video/codecs/msvideo1.cpp b/graphics/video/codecs/msvideo1.cpp index 16596926fd..f37bdbbf95 100644 --- a/graphics/video/codecs/msvideo1.cpp +++ b/graphics/video/codecs/msvideo1.cpp @@ -125,7 +125,7 @@ void MSVideo1Decoder::decode8(Common::SeekableReadStream *stream) { } } -Graphics::Surface *MSVideo1Decoder::decodeImage(Common::SeekableReadStream *stream) { +Surface *MSVideo1Decoder::decodeImage(Common::SeekableReadStream *stream) { if (_bitsPerPixel == 8) decode8(stream); else { diff --git a/graphics/video/codecs/msvideo1.h b/graphics/video/codecs/msvideo1.h index ff6ffc6549..e07267476d 100644 --- a/graphics/video/codecs/msvideo1.h +++ b/graphics/video/codecs/msvideo1.h @@ -36,6 +36,7 @@ public: ~MSVideo1Decoder(); Surface *decodeImage(Common::SeekableReadStream *stream); + PixelFormat getPixelFormat() const { return PixelFormat::createFormatCLUT8(); } private: byte _bitsPerPixel; diff --git a/graphics/video/dxa_decoder.cpp b/graphics/video/dxa_decoder.cpp index 86f2415922..3f26012f5e 100644 --- a/graphics/video/dxa_decoder.cpp +++ b/graphics/video/dxa_decoder.cpp @@ -39,11 +39,12 @@ namespace Graphics { DXADecoder::DXADecoder() { _fileStream = 0; + _surface = 0; + _dirtyPalette = false; _frameBuffer1 = 0; _frameBuffer2 = 0; _scaledBuffer = 0; - _videoFrameBuffer = 0; _inBuffer = 0; _inBufferSize = 0; @@ -51,67 +52,59 @@ DXADecoder::DXADecoder() { _decompBuffer = 0; _decompBufferSize = 0; - _videoInfo.width = 0; - _videoInfo.height = 0; + _width = 0; + _height = 0; _frameSize = 0; - _videoInfo.frameCount = 0; - _videoInfo.currentFrame = -1; - _videoInfo.frameRate = 0; - _videoInfo.frameDelay = 0; + _frameCount = 0; + _frameRate = 0; _scaleMode = S_NONE; } DXADecoder::~DXADecoder() { - closeFile(); + close(); } -bool DXADecoder::loadFile(const char *fileName) { - uint32 tag; - int32 frameRate; +bool DXADecoder::load(Common::SeekableReadStream &stream) { + close(); - closeFile(); + _fileStream = &stream; - _fileStream = SearchMan.createReadStreamForMember(fileName); - if (!_fileStream) - return false; - - tag = _fileStream->readUint32BE(); + uint32 tag = _fileStream->readUint32BE(); assert(tag == MKID_BE('DEXA')); uint8 flags = _fileStream->readByte(); - _videoInfo.frameCount = _fileStream->readUint16BE(); - frameRate = _fileStream->readSint32BE(); - - if (frameRate > 0) { - _videoInfo.frameRate = 1000 / frameRate; - _videoInfo.frameDelay = frameRate * 100; - } else if (frameRate < 0) { - _videoInfo.frameRate = 100000 / (-frameRate); - _videoInfo.frameDelay = -frameRate; - } else { - _videoInfo.frameRate = 10; - _videoInfo.frameDelay = 10000; - } + _frameCount = _fileStream->readUint16BE(); + int32 frameRate = _fileStream->readSint32BE(); + + if (frameRate > 0) + _frameRate = 1000 / frameRate; + else if (frameRate < 0) + _frameRate = 100000 / (-frameRate); + else + _frameRate = 10; - _videoInfo.width = _fileStream->readUint16BE(); - _videoInfo.height = _fileStream->readUint16BE(); + _width = _fileStream->readUint16BE(); + _height = _fileStream->readUint16BE(); if (flags & 0x80) { _scaleMode = S_INTERLACED; - _curHeight = _videoInfo.height / 2; + _curHeight = _height / 2; } else if (flags & 0x40) { _scaleMode = S_DOUBLE; - _curHeight = _videoInfo.height / 2; + _curHeight = _height / 2; } else { _scaleMode = S_NONE; - _curHeight = _videoInfo.height; + _curHeight = _height; } - debug(2, "flags 0x0%x framesCount %d width %d height %d rate %d ticks %d", flags, getFrameCount(), getWidth(), getHeight(), getFrameRate(), getFrameDelay()); + _surface = new Graphics::Surface(); + _surface->bytesPerPixel = 1; - _frameSize = _videoInfo.width * _videoInfo.height; + debug(2, "flags 0x0%x framesCount %d width %d height %d rate %d", flags, getFrameCount(), getWidth(), getHeight(), getFrameRate().toInt()); + + _frameSize = _width * _height; _decompBufferSize = _frameSize; _frameBuffer1 = (uint8 *)malloc(_frameSize); memset(_frameBuffer1, 0, _frameSize); @@ -135,9 +128,10 @@ bool DXADecoder::loadFile(const char *fileName) { do { tag = _fileStream->readUint32BE(); - if (tag != 0) { + + if (tag != 0) size = _fileStream->readUint32BE(); - } + switch (tag) { case 0: // No more tags break; @@ -159,20 +153,19 @@ bool DXADecoder::loadFile(const char *fileName) { // Read the sound header _soundTag = _fileStream->readUint32BE(); - _videoInfo.currentFrame = -1; - - _videoInfo.firstframeOffset = _fileStream->pos(); - return true; } -void DXADecoder::closeFile() { +void DXADecoder::close() { if (!_fileStream) return; delete _fileStream; _fileStream = 0; + delete _surface; + _surface = 0; + free(_frameBuffer1); free(_frameBuffer2); free(_scaledBuffer); @@ -181,6 +174,8 @@ void DXADecoder::closeFile() { _inBuffer = 0; _decompBuffer = 0; + + reset(); } void DXADecoder::decodeZlib(byte *data, int size, int totalSize) { @@ -208,10 +203,10 @@ void DXADecoder::decode12(int size) { memcpy(_frameBuffer2, _frameBuffer1, _frameSize); - for (uint32 by = 0; by < _videoInfo.height; by += BLOCKH) { - for (uint32 bx = 0; bx < _videoInfo.width; bx += BLOCKW) { + for (uint32 by = 0; by < _height; by += BLOCKH) { + for (uint32 bx = 0; bx < _width; bx += BLOCKW) { byte type = *dat++; - byte *b2 = _frameBuffer1 + bx + by * _videoInfo.width; + byte *b2 = _frameBuffer1 + bx + by * _width; switch (type) { case 0: @@ -243,7 +238,7 @@ void DXADecoder::decode12(int size) { } diffMap <<= 1; } - b2 += _videoInfo.width; + b2 += _width; } break; } @@ -254,7 +249,7 @@ void DXADecoder::decode12(int size) { for (int xc = 0; xc < BLOCKW; xc++) { b2[xc] = color; } - b2 += _videoInfo.width; + b2 += _width; } break; } @@ -263,7 +258,7 @@ void DXADecoder::decode12(int size) { for (int xc = 0; xc < BLOCKW; xc++) { b2[xc] = *dat++; } - b2 += _videoInfo.width; + b2 += _width; } break; } @@ -275,11 +270,11 @@ void DXADecoder::decode12(int size) { int my = mbyte & 0x07; if (mbyte & 0x08) my = -my; - byte *b1 = _frameBuffer2 + (bx+mx) + (by+my) * _videoInfo.width; + byte *b1 = _frameBuffer2 + (bx+mx) + (by+my) * _width; for (int yc = 0; yc < BLOCKH; yc++) { memcpy(b2, b1, BLOCKW); - b1 += _videoInfo.width; - b2 += _videoInfo.width; + b1 += _width; + b2 += _width; } break; } @@ -309,7 +304,7 @@ void DXADecoder::decode13(int size) { memcpy(_frameBuffer2, _frameBuffer1, _frameSize); - int codeSize = _videoInfo.width * _curHeight / 16; + int codeSize = _width * _curHeight / 16; int dataSize, motSize, maskSize; dataSize = READ_BE_UINT32(&_decompBuffer[0]); @@ -322,9 +317,9 @@ void DXADecoder::decode13(int size) { maskBuf = &motBuf[motSize]; for (uint32 by = 0; by < _curHeight; by += BLOCKH) { - for (uint32 bx = 0; bx < _videoInfo.width; bx += BLOCKW) { + for (uint32 bx = 0; bx < _width; bx += BLOCKW) { uint8 type = *codeBuf++; - uint8 *b2 = (uint8*)_frameBuffer1 + bx + by * _videoInfo.width; + uint8 *b2 = (uint8*)_frameBuffer1 + bx + by * _width; switch (type) { case 0: @@ -341,7 +336,7 @@ void DXADecoder::decode13(int size) { } diffMap <<= 1; } - b2 += _videoInfo.width; + b2 += _width; } break; } @@ -352,7 +347,7 @@ void DXADecoder::decode13(int size) { for (int xc = 0; xc < BLOCKW; xc++) { b2[xc] = color; } - b2 += _videoInfo.width; + b2 += _width; } break; } @@ -361,7 +356,7 @@ void DXADecoder::decode13(int size) { for (int xc = 0; xc < BLOCKW; xc++) { b2[xc] = *dataBuf++; } - b2 += _videoInfo.width; + b2 += _width; } break; } @@ -375,11 +370,11 @@ void DXADecoder::decode13(int size) { if (mbyte & 0x08) my = -my; - uint8 *b1 = (uint8*)_frameBuffer2 + (bx+mx) + (by+my) * _videoInfo.width; + uint8 *b1 = (uint8*)_frameBuffer2 + (bx+mx) + (by+my) * _width; for (int yc = 0; yc < BLOCKH; yc++) { memcpy(b2, b1, BLOCKW); - b1 += _videoInfo.width; - b2 += _videoInfo.width; + b1 += _width; + b2 += _width; } break; } @@ -391,7 +386,7 @@ void DXADecoder::decode13(int size) { for (int subBlock = 0; subBlock < 4; subBlock++) { int sx = bx + subX[subBlock], sy = by + subY[subBlock]; - b2 = (uint8*)_frameBuffer1 + sx + sy * _videoInfo.width; + b2 = (uint8*)_frameBuffer1 + sx + sy * _width; switch (subMask & 0xC0) { // 00: skip case 0x00: @@ -403,7 +398,7 @@ void DXADecoder::decode13(int size) { for (int xc = 0; xc < BLOCKW / 2; xc++) { b2[xc] = subColor; } - b2 += _videoInfo.width; + b2 += _width; } break; } @@ -419,11 +414,11 @@ void DXADecoder::decode13(int size) { if (mbyte & 0x08) my = -my; - uint8 *b1 = (uint8*)_frameBuffer2 + (sx+mx) + (sy+my) * _videoInfo.width; + uint8 *b1 = (uint8*)_frameBuffer2 + (sx+mx) + (sy+my) * _width; for (int yc = 0; yc < BLOCKH / 2; yc++) { memcpy(b2, b1, BLOCKW / 2); - b1 += _videoInfo.width; - b2 += _videoInfo.width; + b1 += _width; + b2 += _width; } break; } @@ -433,7 +428,7 @@ void DXADecoder::decode13(int size) { for (int xc = 0; xc < BLOCKW / 2; xc++) { b2[xc] = *dataBuf++; } - b2 += _videoInfo.width; + b2 += _width; } break; } @@ -458,7 +453,7 @@ void DXADecoder::decode13(int size) { b2[xc] = pixels[code & 1]; code >>= 1; } - b2 += _videoInfo.width; + b2 += _width; } } else { uint32 code = READ_BE_UINT32(maskBuf); @@ -468,7 +463,7 @@ void DXADecoder::decode13(int size) { b2[xc] = pixels[code & 3]; code >>= 2; } - b2 += _videoInfo.width; + b2 += _width; } } break; @@ -481,20 +476,11 @@ void DXADecoder::decode13(int size) { #endif } -bool DXADecoder::decodeNextFrame() { - uint32 tag; - - _videoInfo.currentFrame++; - - if (_videoInfo.currentFrame == 0) - _videoInfo.startTime = g_system->getMillis(); - - tag = _fileStream->readUint32BE(); +Surface *DXADecoder::decodeNextFrame() { + uint32 tag = _fileStream->readUint32BE(); if (tag == MKID_BE('CMAP')) { - byte rgb[768]; - - _fileStream->read(rgb, ARRAYSIZE(rgb)); - setPalette(rgb); + _fileStream->read(_palette, 256 * 3); + _dirtyPalette = true; } tag = _fileStream->readUint32BE(); @@ -531,8 +517,8 @@ bool DXADecoder::decodeNextFrame() { if (type == 3) { for (uint32 j = 0; j < _curHeight; ++j) { - for (uint32 i = 0; i < _videoInfo.width; ++i) { - const int offs = j * _videoInfo.width + i; + for (uint32 i = 0; i < _width; ++i) { + const int offs = j * _width + i; _frameBuffer1[offs] ^= _frameBuffer2[offs]; } } @@ -542,24 +528,34 @@ bool DXADecoder::decodeNextFrame() { switch (_scaleMode) { case S_INTERLACED: for (int cy = 0; cy < _curHeight; cy++) { - memcpy(&_scaledBuffer[2 * cy * _videoInfo.width], &_frameBuffer1[cy * _videoInfo.width], _videoInfo.width); - memset(&_scaledBuffer[((2 * cy) + 1) * _videoInfo.width], 0, _videoInfo.width); + memcpy(&_scaledBuffer[2 * cy * _width], &_frameBuffer1[cy * _width], _width); + memset(&_scaledBuffer[((2 * cy) + 1) * _width], 0, _width); } - _videoFrameBuffer = _scaledBuffer; + _surface->pixels = _scaledBuffer; break; case S_DOUBLE: for (int cy = 0; cy < _curHeight; cy++) { - memcpy(&_scaledBuffer[2 * cy * _videoInfo.width], &_frameBuffer1[cy * _videoInfo.width], _videoInfo.width); - memcpy(&_scaledBuffer[((2 * cy) + 1) * _videoInfo.width], &_frameBuffer1[cy * _videoInfo.width], _videoInfo.width); + memcpy(&_scaledBuffer[2 * cy * _width], &_frameBuffer1[cy * _width], _width); + memcpy(&_scaledBuffer[((2 * cy) + 1) * _width], &_frameBuffer1[cy * _width], _width); } - _videoFrameBuffer = _scaledBuffer; + _surface->pixels = _scaledBuffer; break; case S_NONE: - _videoFrameBuffer = _frameBuffer1; + _surface->pixels = _frameBuffer1; break; } - return !endOfVideo(); + // Copy in the relevant info to the Surface + _surface->w = getWidth(); + _surface->h = getHeight(); + _surface->pitch = getWidth(); + + _curFrame++; + + if (_curFrame == 0) + _startTime = g_system->getMillis(); + + return _surface; } } // End of namespace Graphics diff --git a/graphics/video/dxa_decoder.h b/graphics/video/dxa_decoder.h index 3edcc75ca9..0312828195 100644 --- a/graphics/video/dxa_decoder.h +++ b/graphics/video/dxa_decoder.h @@ -26,7 +26,7 @@ #ifndef GRAPHICS_VIDEO_DXA_PLAYER_H #define GRAPHICS_VIDEO_DXA_PLAYER_H -#include "graphics/video/video_player.h" +#include "graphics/video/video_decoder.h" namespace Graphics { @@ -38,29 +38,33 @@ namespace Graphics { * - sword1 * - sword2 */ -class DXADecoder : public VideoDecoder { +class DXADecoder : public FixedRateVideoDecoder { public: DXADecoder(); virtual ~DXADecoder(); - /** - * Load a DXA encoded video file - * @param filename the filename to load - */ - bool loadFile(const char *fileName); - - /** - * Close a DXA encoded video file - */ - void closeFile(); - - bool decodeNextFrame(); + bool load(Common::SeekableReadStream &stream); + void close(); + + bool isVideoLoaded() const { return _fileStream != 0; } + uint16 getWidth() const { return _width; } + uint16 getHeight() const { return _height; } + uint32 getFrameCount() const { return _frameCount; } + Surface *decodeNextFrame(); + PixelFormat getPixelFormat() const { return PixelFormat::createFormatCLUT8(); } + byte *getPalette() { _dirtyPalette = false; return _palette; } + bool hasDirtyPalette() const { return _dirtyPalette; } /** * Get the sound chunk tag of the loaded DXA file */ uint32 getSoundTag() { return _soundTag; } +protected: + Common::Rational getFrameRate() const { return _frameRate; } + + Common::SeekableReadStream *_fileStream; + private: void decodeZlib(byte *data, int size, int totalSize); void decode12(int size); @@ -72,6 +76,10 @@ private: S_DOUBLE }; + Graphics::Surface *_surface; + byte _palette[256 * 3]; + bool _dirtyPalette; + byte *_frameBuffer1; byte *_frameBuffer2; byte *_scaledBuffer; @@ -83,6 +91,9 @@ private: uint32 _frameSize; ScaleMode _scaleMode; uint32 _soundTag; + uint16 _width, _height; + uint32 _frameRate; + uint32 _frameCount; }; } // End of namespace Graphics diff --git a/graphics/video/flic_decoder.cpp b/graphics/video/flic_decoder.cpp index 14d062562f..bb5b4f219b 100644 --- a/graphics/video/flic_decoder.cpp +++ b/graphics/video/flic_decoder.cpp @@ -34,20 +34,17 @@ namespace Graphics { FlicDecoder::FlicDecoder() { _paletteChanged = false; _fileStream = 0; - _videoFrameBuffer = 0; - memset(&_videoInfo, 0, sizeof(_videoInfo)); + _surface = 0; } FlicDecoder::~FlicDecoder() { - closeFile(); + close(); } -bool FlicDecoder::loadFile(const char *fileName) { - closeFile(); +bool FlicDecoder::load(Common::SeekableReadStream &stream) { + close(); - _fileStream = SearchMan.createReadStreamForMember(fileName); - if (!_fileStream) - return false; + _fileStream = &stream; /* uint32 frameSize = */ _fileStream->readUint32LE(); uint16 frameType = _fileStream->readUint16LE(); @@ -60,9 +57,10 @@ bool FlicDecoder::loadFile(const char *fileName) { return false; } - _videoInfo.frameCount = _fileStream->readUint16LE(); - _videoInfo.width = _fileStream->readUint16LE(); - _videoInfo.height = _fileStream->readUint16LE(); + + _frameCount = _fileStream->readUint16LE(); + uint16 width = _fileStream->readUint16LE(); + uint16 height = _fileStream->readUint16LE(); uint16 colorDepth = _fileStream->readUint16LE(); if (colorDepth != 8) { warning("FlicDecoder::FlicDecoder(): attempted to load an FLC with a palette of color depth %d. Only 8-bit color palettes are supported", frameType); @@ -70,45 +68,48 @@ bool FlicDecoder::loadFile(const char *fileName) { _fileStream = 0; return false; } + _fileStream->readUint16LE(); // flags // Note: The normal delay is a 32-bit integer (dword), whereas the overriden delay is a 16-bit integer (word) // frameDelay is the FLIC "speed", in milliseconds. Our frameDelay is calculated in 1/100 ms, so we convert it here - _videoInfo.frameDelay = 100 * _fileStream->readUint32LE(); - _videoInfo.frameRate = 100 * 1000 / _videoInfo.frameDelay; + uint32 frameDelay = 100 * _fileStream->readUint32LE(); + _frameRate = 100 * 1000 / frameDelay; _fileStream->seek(80); _offsetFrame1 = _fileStream->readUint32LE(); _offsetFrame2 = _fileStream->readUint32LE(); - _videoFrameBuffer = new byte[_videoInfo.width * _videoInfo.height]; + _surface = new Graphics::Surface(); + _surface->create(width, height, 1); _palette = (byte *)malloc(3 * 256); memset(_palette, 0, 3 * 256); _paletteChanged = false; // Seek to the first frame - _videoInfo.currentFrame = -1; _fileStream->seek(_offsetFrame1); return true; } -void FlicDecoder::closeFile() { +void FlicDecoder::close() { if (!_fileStream) return; delete _fileStream; _fileStream = 0; - delete[] _videoFrameBuffer; - _videoFrameBuffer = 0; + _surface->free(); + delete _surface; + _surface = 0; free(_palette); - _dirtyRects.clear(); + + reset(); } void FlicDecoder::decodeByteRun(uint8 *data) { - byte *ptr = (uint8 *)_videoFrameBuffer; - while ((uint32)(ptr - _videoFrameBuffer) < (_videoInfo.width * _videoInfo.height)) { + byte *ptr = (byte *)_surface->pixels; + while ((int32)(ptr - (byte *)_surface->pixels) < (getWidth() * getHeight())) { int chunks = *data++; while (chunks--) { int count = (int8)*data++; @@ -125,7 +126,7 @@ void FlicDecoder::decodeByteRun(uint8 *data) { // Redraw _dirtyRects.clear(); - _dirtyRects.push_back(Common::Rect(0, 0, _videoInfo.width, _videoInfo.height)); + _dirtyRects.push_back(Common::Rect(0, 0, getWidth(), getHeight())); } #define OP_PACKETCOUNT 0 @@ -152,8 +153,8 @@ void FlicDecoder::decodeDeltaFLC(uint8 *data) { case OP_UNDEFINED: break; case OP_LASTPIXEL: - _videoFrameBuffer[currentLine * _videoInfo.width + _videoInfo.width - 1] = (opcode & 0xFF); - _dirtyRects.push_back(Common::Rect(_videoInfo.width - 1, currentLine, _videoInfo.width, currentLine + 1)); + *((byte *)_surface->pixels + currentLine * getWidth() + getWidth() - 1) = (opcode & 0xFF); + _dirtyRects.push_back(Common::Rect(getWidth() - 1, currentLine, getWidth(), currentLine + 1)); break; case OP_LINESKIPCOUNT: currentLine += -(int16)opcode; @@ -168,14 +169,14 @@ void FlicDecoder::decodeDeltaFLC(uint8 *data) { column += *data++; int rleCount = (int8)*data++; if (rleCount > 0) { - memcpy(_videoFrameBuffer + (currentLine * _videoInfo.width) + column, data, rleCount * 2); + memcpy((byte *)_surface->pixels + (currentLine * getWidth()) + column, data, rleCount * 2); data += rleCount * 2; _dirtyRects.push_back(Common::Rect(column, currentLine, column + rleCount * 2, currentLine + 1)); } else if (rleCount < 0) { rleCount = -rleCount; uint16 dataWord = READ_UINT16(data); data += 2; for (int i = 0; i < rleCount; ++i) { - WRITE_UINT16(_videoFrameBuffer + currentLine * _videoInfo.width + column + i * 2, dataWord); + WRITE_UINT16((byte *)_surface->pixels + currentLine * getWidth() + column + i * 2, dataWord); } _dirtyRects.push_back(Common::Rect(column, currentLine, column + rleCount * 2, currentLine + 1)); } else { // End of cutscene ? @@ -194,34 +195,40 @@ void FlicDecoder::decodeDeltaFLC(uint8 *data) { #define PSTAMP 18 #define FRAME_TYPE 0xF1FA -bool FlicDecoder::decodeNextFrame() { +Surface *FlicDecoder::decodeNextFrame() { // Read chunk uint32 frameSize = _fileStream->readUint32LE(); uint16 frameType = _fileStream->readUint16LE(); uint16 chunkCount = 0; switch (frameType) { - case FRAME_TYPE: { - chunkCount = _fileStream->readUint16LE(); - // Note: The overriden delay is a 16-bit integer (word), whereas the normal delay is a 32-bit integer (dword) - // frameDelay is the FLIC "speed", in milliseconds. Our frameDelay is calculated in 1/100 ms, so we convert it here - uint16 newFrameDelay = _fileStream->readUint16LE(); // "speed", in milliseconds - if (newFrameDelay > 0) { - _videoInfo.frameDelay = 100 * newFrameDelay; - _videoInfo.frameRate = 100 * 1000 / _videoInfo.frameDelay; - } - _fileStream->readUint16LE(); // reserved, always 0 - uint16 newWidth = _fileStream->readUint16LE(); - uint16 newHeight = _fileStream->readUint16LE(); - if (newWidth > 0) - _videoInfo.width = newWidth; - if (newHeight > 0) - _videoInfo.height = newHeight; - - _videoInfo.currentFrame++; - - if (_videoInfo.currentFrame == 0) - _videoInfo.startTime = g_system->getMillis(); + case FRAME_TYPE: + { + // FIXME: FLIC should be switched over to a variable frame rate VideoDecoder to handle + // this properly. + + chunkCount = _fileStream->readUint16LE(); + // Note: The overriden delay is a 16-bit integer (word), whereas the normal delay is a 32-bit integer (dword) + // frameDelay is the FLIC "speed", in milliseconds. Our frameDelay is calculated in 1/100 ms, so we convert it here + uint16 newFrameDelay = _fileStream->readUint16LE(); // "speed", in milliseconds + if (newFrameDelay > 0) + _frameRate = 1000 / newFrameDelay; + + _fileStream->readUint16LE(); // reserved, always 0 + uint16 newWidth = _fileStream->readUint16LE(); + uint16 newHeight = _fileStream->readUint16LE(); + + if ((newWidth != 0) && (newHeight != 0)) { + if (newWidth == 0) + newWidth = _surface->w; + if (newHeight == 0) + newHeight = _surface->h; + + _surface->free(); + delete _surface; + _surface = new Graphics::Surface(); + _surface->create(newWidth, newHeight, 1); + } } break; default: @@ -239,7 +246,6 @@ bool FlicDecoder::decodeNextFrame() { switch (frameType) { case FLI_SETPAL: unpackPalette(data); - setPalette(_palette); _paletteChanged = true; break; case FLI_SS2: @@ -259,19 +265,25 @@ bool FlicDecoder::decodeNextFrame() { delete[] data; } } + + _curFrame++; + + if (_curFrame == 0) + _startTime = g_system->getMillis(); // If we just processed the ring frame, set the next frame - if (_videoInfo.currentFrame == (int32)_videoInfo.frameCount) { - _videoInfo.currentFrame = 0; + if (_curFrame == (int32)_frameCount) { + _curFrame = 0; _fileStream->seek(_offsetFrame2); } - return !endOfVideo(); + return _surface; } void FlicDecoder::reset() { - _videoInfo.currentFrame = -1; - _fileStream->seek(_offsetFrame1); + VideoDecoder::reset(); + if (_fileStream) + _fileStream->seek(_offsetFrame1); } void FlicDecoder::unpackPalette(uint8 *data) { @@ -303,7 +315,7 @@ void FlicDecoder::copyDirtyRectsToBuffer(uint8 *dst, uint pitch) { for (Common::List<Common::Rect>::const_iterator it = _dirtyRects.begin(); it != _dirtyRects.end(); ++it) { for (int y = (*it).top; y < (*it).bottom; ++y) { const int x = (*it).left; - memcpy(dst + y * pitch + x, _videoFrameBuffer + y * _videoInfo.width + x, (*it).right - x); + memcpy(dst + y * pitch + x, (byte *)_surface->pixels + y * getWidth() + x, (*it).right - x); } } _dirtyRects.clear(); diff --git a/graphics/video/flic_decoder.h b/graphics/video/flic_decoder.h index b0a2b771b1..60d68889a2 100644 --- a/graphics/video/flic_decoder.h +++ b/graphics/video/flic_decoder.h @@ -23,10 +23,10 @@ * */ -#ifndef GRAPHICS_VIDEO_FlicDecoder_H -#define GRAPHICS_VIDEO_FlicDecoder_H +#ifndef GRAPHICS_VIDEO_FLICDECODER_H +#define GRAPHICS_VIDEO_FLICDECODER_H -#include "graphics/video/video_player.h" +#include "graphics/video/video_decoder.h" #include "common/list.h" #include "common/rect.h" @@ -42,35 +42,42 @@ namespace Graphics { * Video decoder used in engines: * - tucker */ -class FlicDecoder : public VideoDecoder { +class FlicDecoder : public FixedRateVideoDecoder { public: FlicDecoder(); virtual ~FlicDecoder(); /** - * Load a FLIC encoded video file - * @param filename the filename to load + * Load a video file + * @param stream the stream to load */ - bool loadFile(const char *fileName); + bool load(Common::SeekableReadStream &stream); + void close(); /** - * Close a FLIC encoded video file + * Decode the next frame and return the frame's surface + * @note the return surface should *not* be freed + * @note this may return 0, in which case the last frame should be kept on screen */ - void closeFile(); + Surface *decodeNextFrame(); - /** - * Decode the next frame - */ - bool decodeNextFrame(); + bool isVideoLoaded() const { return _fileStream != 0; } + uint16 getWidth() const { return _surface->w; } + uint16 getHeight() const { return _surface->h; } + uint32 getFrameCount() const { return _frameCount; } + PixelFormat getPixelFormat() const { return PixelFormat::createFormatCLUT8(); } const Common::List<Common::Rect> *getDirtyRects() const { return &_dirtyRects; } void clearDirtyRects() { _dirtyRects.clear(); } void copyDirtyRectsToBuffer(uint8 *dst, uint pitch); - const byte *getPalette() { _paletteChanged = false; return _palette; } - bool paletteChanged() { return _paletteChanged; } + byte *getPalette() { _paletteChanged = false; return _palette; } + bool hasDirtyPalette() { return _paletteChanged; } void reset(); +protected: + Common::Rational getFrameRate() const { return _frameRate; } + private: uint16 _offsetFrame1; uint16 _offsetFrame2; @@ -81,8 +88,12 @@ private: void decodeDeltaFLC(uint8 *data); void unpackPalette(uint8 *mem); - Common::List<Common::Rect> _dirtyRects; + Common::SeekableReadStream *_fileStream; + Surface *_surface; + uint32 _frameCount; + uint32 _frameRate; + Common::List<Common::Rect> _dirtyRects; }; } // End of namespace Graphics diff --git a/graphics/video/smk_decoder.cpp b/graphics/video/smk_decoder.cpp index 2f796c4a65..68fac32aae 100644 --- a/graphics/video/smk_decoder.cpp +++ b/graphics/video/smk_decoder.cpp @@ -351,52 +351,28 @@ uint32 BigHuffmanTree::getCode(BitStream &bs) { SmackerDecoder::SmackerDecoder(Audio::Mixer *mixer, Audio::Mixer::SoundType soundType) : _audioStarted(false), _audioStream(0), _mixer(mixer), _soundType(soundType) { + _surface = 0; + _fileStream = 0; + _dirtyPalette = false; } SmackerDecoder::~SmackerDecoder() { - closeFile(); + close(); } -int SmackerDecoder::getHeight() { - if (!_fileStream) - return 0; - return (_header.flags ? 2 : 1) * _videoInfo.height; -} - -int32 SmackerDecoder::getAudioLag() { - if (!_fileStream) - return 0; +uint32 SmackerDecoder::getElapsedTime() const { + if (_audioStream) + return _mixer->getSoundElapsedTime(_audioHandle); - int32 frameDelay = getFrameDelay(); - int32 videoTime = (_videoInfo.currentFrame + 1) * frameDelay; - int32 audioTime; - - if (!_audioStream) { - /* No audio. - Calculate the lag by how much time has gone by since the first frame - and how much time *should* have passed. - */ - - audioTime = (g_system->getMillis() - _videoInfo.startTime) * 100; - } else { - const Audio::Timestamp ts = _mixer->getElapsedTime(_audioHandle); - audioTime = ts.convertToFramerate(100000).totalNumberOfFrames(); - } - - return videoTime - audioTime; + return VideoDecoder::getElapsedTime(); } -bool SmackerDecoder::loadFile(const char *fileName) { - int32 frameRate; - - closeFile(); +bool SmackerDecoder::load(Common::SeekableReadStream &stream) { + close(); - _fileStream = SearchMan.createReadStreamForMember(fileName); - if (!_fileStream) - return false; + _fileStream = &stream; // Seek to the first frame - _videoInfo.currentFrame = -1; _header.signature = _fileStream->readUint32BE(); // No BINK support available @@ -408,21 +384,17 @@ bool SmackerDecoder::loadFile(const char *fileName) { assert(_header.signature == MKID_BE('SMK2') || _header.signature == MKID_BE('SMK4')); - _videoInfo.width = _fileStream->readUint32LE(); - _videoInfo.height = _fileStream->readUint32LE(); - _videoInfo.frameCount = _fileStream->readUint32LE(); - frameRate = _fileStream->readSint32LE(); - - if (frameRate > 0) { - _videoInfo.frameRate = 1000 / frameRate; - _videoInfo.frameDelay = frameRate * 100; - } else if (frameRate < 0) { - _videoInfo.frameRate = 100000 / (-frameRate); - _videoInfo.frameDelay = -frameRate; - } else { - _videoInfo.frameRate = 10; - _videoInfo.frameDelay = 10000; - } + uint32 width = _fileStream->readUint32LE(); + uint32 height = _fileStream->readUint32LE(); + _frameCount = _fileStream->readUint32LE(); + int32 frameRate = _fileStream->readSint32LE(); + + if (frameRate > 0) + _frameRate = 1000 / frameRate; + else if (frameRate < 0) + _frameRate = 100000 / (-frameRate); + else + _frameRate = 10; // Flags are determined by which bit is set, which can be one of the following: // 0 - set to 1 if file contains a ring frame. @@ -447,7 +419,6 @@ bool SmackerDecoder::loadFile(const char *fileName) { _header.fullSize = _fileStream->readUint32LE(); _header.typeSize = _fileStream->readUint32LE(); - uint32 audioInfo; for (i = 0; i < 7; ++i) { // AudioRate - Frequency and format information for each sound track, up to 7 audio tracks. // The 32 constituent bits have the following meaning: @@ -458,7 +429,7 @@ bool SmackerDecoder::loadFile(const char *fileName) { // * bits 27-26 - if both set to zero - use v2 sound decompression // * bits 25-24 - unused // * bits 23-0 - audio sample rate - audioInfo = _fileStream->readUint32LE(); + uint32 audioInfo = _fileStream->readUint32LE(); _header.audioInfo[i].isCompressed = audioInfo & 0x80000000; _header.audioInfo[i].hasAudio = audioInfo & 0x40000000; _header.audioInfo[i].is16Bits = audioInfo & 0x20000000; @@ -467,19 +438,18 @@ bool SmackerDecoder::loadFile(const char *fileName) { !(audioInfo & 0x4000000); _header.audioInfo[i].sampleRate = audioInfo & 0xFFFFFF; - if (_header.audioInfo[i].hasAudio && i == 0) { + if (_header.audioInfo[i].hasAudio && i == 0) _audioStream = Audio::makeQueuingAudioStream(_header.audioInfo[0].sampleRate, _header.audioInfo[0].isStereo); - } } _header.dummy = _fileStream->readUint32LE(); - _frameSizes = (uint32 *)malloc(_videoInfo.frameCount * sizeof(uint32)); - for (i = 0; i < _videoInfo.frameCount; ++i) + _frameSizes = new uint32[_frameCount]; + for (i = 0; i < _frameCount; ++i) _frameSizes[i] = _fileStream->readUint32LE(); - _frameTypes = (byte *)malloc(_videoInfo.frameCount); - for (i = 0; i < _videoInfo.frameCount; ++i) + _frameTypes = new byte[_frameCount]; + for (i = 0; i < _frameCount; ++i) _frameTypes[i] = _fileStream->readByte(); byte *huffmanTrees = new byte[_header.treesSize]; @@ -494,17 +464,17 @@ bool SmackerDecoder::loadFile(const char *fileName) { delete[] huffmanTrees; - _videoFrameBuffer = (byte *)malloc(2 * _videoInfo.width * _videoInfo.height); - memset(_videoFrameBuffer, 0, 2 * _videoInfo.width * _videoInfo.height); - _palette = (byte *)malloc(3 * 256); - memset(_palette, 0, 3 * 256); + _surface = new Graphics::Surface(); - _videoInfo.firstframeOffset = _fileStream->pos(); + // Height needs to be doubled if we have flags (Y-interlaced or Y-doubled) + _surface->create(width, height * (_header.flags ? 2 : 1), 1); + _palette = (byte *)malloc(3 * 256); + memset(_palette, 0, 3 * 256); return true; } -void SmackerDecoder::closeFile() { +void SmackerDecoder::close() { if (!_fileStream) return; @@ -517,40 +487,42 @@ void SmackerDecoder::closeFile() { delete _fileStream; _fileStream = 0; + _surface->free(); + delete _surface; + _surface = 0; + delete _MMapTree; delete _MClrTree; delete _FullTree; delete _TypeTree; - free(_frameSizes); - free(_frameTypes); - free(_videoFrameBuffer); + delete[] _frameSizes; + delete[] _frameTypes; free(_palette); + + reset(); } -bool SmackerDecoder::decodeNextFrame() { +Surface *SmackerDecoder::decodeNextFrame() { uint i; uint32 chunkSize = 0; uint32 dataSizeUnpacked = 0; uint32 startPos = _fileStream->pos(); - _videoInfo.currentFrame++; - - if (_videoInfo.currentFrame == 0) - _videoInfo.startTime = g_system->getMillis(); + _curFrame++; // Check if we got a frame with palette data, and // call back the virtual setPalette function to set // the current palette - if (_frameTypes[_videoInfo.currentFrame] & 1) { + if (_frameTypes[_curFrame] & 1) { unpackPalette(); - setPalette(_palette); + _dirtyPalette = true; } // Load audio tracks for (i = 0; i < 7; ++i) { - if (!(_frameTypes[_videoInfo.currentFrame] & (2 << i))) + if (!(_frameTypes[_curFrame] & (2 << i))) continue; chunkSize = _fileStream->readUint32LE(); @@ -598,10 +570,10 @@ bool SmackerDecoder::decodeNextFrame() { } } - uint32 frameSize = _frameSizes[_videoInfo.currentFrame] & ~3; + uint32 frameSize = _frameSizes[_curFrame] & ~3; if (_fileStream->pos() - startPos > frameSize) - exit(1); + error("Smacker actual frame size exceeds recorded frame size"); uint32 frameDataSize = frameSize - (_fileStream->pos() - startPos); @@ -615,13 +587,14 @@ bool SmackerDecoder::decodeNextFrame() { _FullTree->reset(); _TypeTree->reset(); - uint bw = _videoInfo.width / 4; - uint bh = _videoInfo.height / 4; - uint stride = _videoInfo.width; - uint block = 0, blocks = bw*bh; - + // Height needs to be doubled if we have flags (Y-interlaced or Y-doubled) uint doubleY = _header.flags ? 2 : 1; + uint bw = getWidth() / 4; + uint bh = getHeight() / doubleY / 4; + uint stride = getWidth(); + uint block = 0, blocks = bw*bh; + byte *out; uint type, run, j, mode; uint32 p1, p2, clr, map; @@ -636,7 +609,7 @@ bool SmackerDecoder::decodeNextFrame() { while (run-- && block < blocks) { clr = _MClrTree->getCode(bs); map = _MMapTree->getCode(bs); - out = _videoFrameBuffer + (block / bw) * (stride * 4 * doubleY) + (block % bw) * 4; + out = (byte *)_surface->pixels + (block / bw) * (stride * 4 * doubleY) + (block % bw) * 4; hi = clr >> 8; lo = clr & 0xff; for (i = 0; i < 4; i++) { @@ -669,7 +642,7 @@ bool SmackerDecoder::decodeNextFrame() { } while (run-- && block < blocks) { - out = _videoFrameBuffer + (block / bw) * (stride * 4 * doubleY) + (block % bw) * 4; + out = (byte *)_surface->pixels + (block / bw) * (stride * 4 * doubleY) + (block % bw) * 4; switch (mode) { case 0: for (i = 0; i < 4; ++i) { @@ -735,7 +708,7 @@ bool SmackerDecoder::decodeNextFrame() { uint32 col; mode = type >> 8; while (run-- && block < blocks) { - out = _videoFrameBuffer + (block / bw) * (stride * 4 * doubleY) + (block % bw) * 4; + out = (byte *)_surface->pixels + (block / bw) * (stride * 4 * doubleY) + (block % bw) * 4; col = mode * 0x01010101; for (i = 0; i < 4 * doubleY; ++i) { out[0] = out[1] = out[2] = out[3] = col; @@ -751,7 +724,10 @@ bool SmackerDecoder::decodeNextFrame() { free(_frameData); - return !endOfVideo(); + if (_curFrame == 0) + _startTime = g_system->getMillis(); + + return _surface; } void SmackerDecoder::queueCompressedBuffer(byte *buffer, uint32 bufferSize, @@ -806,15 +782,12 @@ void SmackerDecoder::queueCompressedBuffer(byte *buffer, uint32 bufferSize, // If the sample is stereo, the data is stored for the left and right channel, respectively // (the exact opposite to the base values) if (!is16Bits) { - for (int k = 0; k < (isStereo ? 2 : 1); k++) { bases[k] += (int8) ((int16) audioTrees[k]->getCode(audioBS)); *curPointer++ = CLIP<int>(bases[k], 0, 255) ^ 0x80; curPos++; } - } else { - for (int k = 0; k < (isStereo ? 2 : 1); k++) { bases[k] += (int16) (audioTrees[k * 2]->getCode(audioBS) | (audioTrees[k * 2 + 1]->getCode(audioBS) << 8)); @@ -887,7 +860,6 @@ void SmackerDecoder::unpackPalette() { } _fileStream->seek(startPos + len); - free(chunk); } diff --git a/graphics/video/smk_decoder.h b/graphics/video/smk_decoder.h index 443d063138..437f47f2d6 100644 --- a/graphics/video/smk_decoder.h +++ b/graphics/video/smk_decoder.h @@ -26,7 +26,7 @@ #ifndef GRAPHICS_VIDEO_SMK_PLAYER_H #define GRAPHICS_VIDEO_SMK_PLAYER_H -#include "graphics/video/video_player.h" +#include "graphics/video/video_decoder.h" #include "sound/mixer.h" namespace Audio { @@ -51,27 +51,28 @@ class BigHuffmanTree; * - sword1 * - sword2 */ -class SmackerDecoder : public VideoDecoder { +class SmackerDecoder : public FixedRateVideoDecoder { public: SmackerDecoder(Audio::Mixer *mixer, Audio::Mixer::SoundType soundType = Audio::Mixer::kSFXSoundType); virtual ~SmackerDecoder(); - int getHeight(); - int32 getAudioLag(); + bool load(Common::SeekableReadStream &stream); + void close(); - /** - * Load an SMK encoded video file - * @param filename the filename to load - */ - bool loadFile(const char *filename); + bool isVideoLoaded() const { return _fileStream != 0; } + uint16 getWidth() const { return _surface->w; } + uint16 getHeight() const { return _surface->h; } + uint32 getFrameCount() const { return _frameCount; } + uint32 getElapsedTime() const; + Surface *decodeNextFrame(); + PixelFormat getPixelFormat() const { return PixelFormat::createFormatCLUT8(); } + byte *getPalette() { _dirtyPalette = false; return _palette; } + bool hasDirtyPalette() const { return _dirtyPalette; } - /** - * Close an SMK encoded video file - */ - void closeFile(); - - bool decodeNextFrame(); +protected: + Common::Rational getFrameRate() const { return _frameRate; } + Common::SeekableReadStream *_fileStream; private: void unpackPalette(); @@ -111,6 +112,11 @@ private: byte *_frameData; // The RGB palette byte *_palette; + bool _dirtyPalette; + + uint32 _frameRate; + uint32 _frameCount; + Surface *_surface; Audio::Mixer::SoundType _soundType; Audio::Mixer *_mixer; diff --git a/graphics/video/video_decoder.cpp b/graphics/video/video_decoder.cpp new file mode 100644 index 0000000000..a3f63921a8 --- /dev/null +++ b/graphics/video/video_decoder.cpp @@ -0,0 +1,101 @@ +/* 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. + * + * $URL$ + * $Id$ + * + */ + +#include "graphics/video/video_decoder.h" + +#include "common/file.h" +#include "common/system.h" + +namespace Graphics { + +VideoDecoder::VideoDecoder() { + reset(); +} + +bool VideoDecoder::loadFile(const Common::String &filename) { + Common::File *file = new Common::File(); + + if (!file->open(filename)) { + delete file; + return false; + } + + return load(*file); +} + +uint32 VideoDecoder::getElapsedTime() const { + return g_system->getMillis() - _startTime; +} + +void VideoDecoder::setSystemPalette() { + byte *vidPalette = getPalette(); + byte *sysPalette = new byte[256 * 4]; + + for (uint16 i = 0; i < 256; i++) { + sysPalette[i * 4] = vidPalette[i * 3]; + sysPalette[i * 4 + 1] = vidPalette[i * 3 + 1]; + sysPalette[i * 4 + 2] = vidPalette[i * 3 + 2]; + sysPalette[i * 4 + 3] = 0; + } + + g_system->setPalette(sysPalette, 0, 256); + delete[] sysPalette; +} + +bool VideoDecoder::needsUpdate() const { + return !endOfVideo() && getTimeToNextFrame() == 0; +} + +void VideoDecoder::reset() { + _curFrame = -1; + _startTime = 0; +} + +bool VideoDecoder::endOfVideo() const { + return !isVideoLoaded() || (getCurFrame() >= (int32)getFrameCount() - 1); +} + +uint32 FixedRateVideoDecoder::getTimeToNextFrame() const { + if (endOfVideo() || _curFrame < 0) + return 0; + + uint32 elapsedTime = getElapsedTime(); + uint32 nextFrameStartTime = getFrameBeginTime(_curFrame + 1); + + // If the time that the next frame should be shown has past + // the frame should be shown ASAP. + if (nextFrameStartTime <= elapsedTime) + return 0; + + return nextFrameStartTime - elapsedTime; +} + +uint32 FixedRateVideoDecoder::getFrameBeginTime(uint32 frame) const { + Common::Rational beginTime = frame * 1000; + beginTime /= getFrameRate(); + return beginTime.toInt(); +} + +} // End of namespace Graphics diff --git a/graphics/video/video_decoder.h b/graphics/video/video_decoder.h new file mode 100644 index 0000000000..62aa496148 --- /dev/null +++ b/graphics/video/video_decoder.h @@ -0,0 +1,184 @@ +/* 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. + * + * $URL$ + * $Id$ + * + */ + +#ifndef GRAPHICS_VIDEO_PLAYER_H +#define GRAPHICS_VIDEO_PLAYER_H + +#include "common/events.h" +#include "common/list.h" +#include "common/rational.h" +#include "common/stream.h" + +#include "graphics/surface.h" +#include "graphics/pixelformat.h" + +namespace Common { + class SeekableReadStream; +} + +namespace Graphics { + +/** + * Implementation of a generic video decoder + */ +class VideoDecoder { +public: + VideoDecoder(); + virtual ~VideoDecoder() {} + + /** + * Returns the width of the video + * @return the width of the video + */ + virtual uint16 getWidth() const = 0; + + /** + * Returns the height of the video + * @return the height of the video + */ + virtual uint16 getHeight() const = 0; + + /** + * Returns the current frame number of the video + * @return the last frame decoded by the video + */ + virtual int32 getCurFrame() const { return _curFrame; } + + /** + * Returns the amount of frames in the video + * @return the amount of frames in the video + */ + virtual uint32 getFrameCount() const = 0; + + /** + * Returns the time (in ms) that the video has been running + */ + virtual uint32 getElapsedTime() const; + + /** + * Returns whether a frame should be decoded or not + * @return whether a frame should be decoded or not + */ + virtual bool needsUpdate() const; + + /** + * Load a video file + * @param filename the filename to load + */ + virtual bool loadFile(const Common::String &filename); + + /** + * Load a video file + * @param stream the stream to load + */ + virtual bool load(Common::SeekableReadStream &stream) = 0; + + /** + * Close a video file + */ + virtual void close() = 0; + + /** + * Returns if a video file is loaded or not + */ + virtual bool isVideoLoaded() const = 0; + + /** + * Decode the next frame and return the frame's surface + * @note the return surface should *not* be freed + * @note this may return 0, in which case the last frame should be kept on screen + */ + virtual Surface *decodeNextFrame() = 0; + + /** + * Get the pixel format of the video + */ + virtual PixelFormat getPixelFormat() const = 0; + + /** + * Get the palette for the video in RGB format (if 8bpp or less) + */ + virtual byte *getPalette() { return 0; } + + /** + * Returns if the palette is dirty or not + */ + virtual bool hasDirtyPalette() const { return false; } + + /** + * Add the time the video has been paused to maintain sync + */ + virtual void addPauseTime(uint32 ms) { _startTime += ms; } + + /** + * Returns if the video is finished or not + */ + virtual bool endOfVideo() const; + + /** + * Set the current palette to the system palette + */ + void setSystemPalette(); + + /** + * Return the time until the next frame (in ms) + */ + virtual uint32 getTimeToNextFrame() const = 0; + +protected: + /** + * Resets _curFrame and _startTime. Should be called from every close() function. + */ + void reset(); + + int32 _curFrame; + uint32 _startTime; +}; + +/** + * A VideoDecoder wrapper that implements getTimeToNextFrame() based on getFrameRate(). + */ +class FixedRateVideoDecoder : public VideoDecoder { +public: + FixedRateVideoDecoder() {} + virtual ~FixedRateVideoDecoder() {} + + uint32 getTimeToNextFrame() const; + +protected: + /** + * Return the frame rate in frames per second + * This returns a Rational because videos can have rates that are not integers and + * there are some videos with frame rates < 1. + */ + virtual Common::Rational getFrameRate() const = 0; + +private: + uint32 getFrameBeginTime(uint32 frame) const; +}; + +} // End of namespace Graphics + +#endif diff --git a/graphics/video/video_player.cpp b/graphics/video/video_player.cpp deleted file mode 100644 index a8676ed594..0000000000 --- a/graphics/video/video_player.cpp +++ /dev/null @@ -1,245 +0,0 @@ -/* 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. - * - * $URL$ - * $Id$ - * - */ - -#include "common/archive.h" -#include "common/debug.h" -#include "common/events.h" -#include "common/system.h" -#include "common/util.h" -#include "common/array.h" -#include "common/endian.h" - -#include "graphics/video/video_player.h" -#include "graphics/surface.h" - -namespace Graphics { - -VideoDecoder::VideoDecoder() : _fileStream(0) { - _curFrameBlack = 0; - _curFrameWhite = 255; - _videoInfo.currentFrame = -1; -} - -VideoDecoder::~VideoDecoder() { -} - -int VideoDecoder::getWidth() { - if (!_fileStream) - return 0; - return _videoInfo.width; -} - -int VideoDecoder::getHeight() { - if (!_fileStream) - return 0; - return _videoInfo.height; -} - -int32 VideoDecoder::getCurFrame() const { - return _videoInfo.currentFrame; -} - -int32 VideoDecoder::getFrameCount() const { - if (!_fileStream) - return 0; - return _videoInfo.frameCount; -} - -int32 VideoDecoder::getFrameRate() { - if (!_fileStream) - return 0; - return _videoInfo.frameRate; -} - -int32 VideoDecoder::getFrameDelay() { - if (!_fileStream) - return 0; - return _videoInfo.frameDelay; -} - -int32 VideoDecoder::getAudioLag() { - if (!_fileStream) - return 0; - - /* No audio. - Calculate the lag by how much time has gone by since the first frame - and how much time *should* have passed. - */ - int32 audioTime = (g_system->getMillis() - _videoInfo.startTime) * 100; - int32 videoTime = _videoInfo.currentFrame * getFrameDelay(); - - return videoTime - audioTime; -} - -uint32 VideoDecoder::getFrameWaitTime() { - int32 waitTime = (getFrameDelay() + getAudioLag()) / 100; - - if (waitTime < 0) - return 0; - - return waitTime; -} - -void VideoDecoder::copyFrameToBuffer(byte *dst, uint x, uint y, uint pitch) { - uint h = getHeight(); - uint w = getWidth(); - - byte *src = _videoFrameBuffer; - dst += y * pitch + x; - - do { - memcpy(dst, src, w); - dst += pitch; - src += w; - } while (--h); -} - -void VideoDecoder::setPalette(byte *pal) { - byte videoPalette[256 * 4]; - - uint32 maxWeight = 0; - uint32 minWeight = 0xFFFFFFFF; - uint32 weight = 0; - byte r, g, b; - - for (int i = 0; i < 256; i++) { - videoPalette[i * 4 + 0] = *pal++; - videoPalette[i * 4 + 1] = *pal++; - videoPalette[i * 4 + 2] = *pal++; - videoPalette[i * 4 + 3] = 0; - - // Try and find the white and black colors for the current palette - r = videoPalette[i * 4 + 0]; - g = videoPalette[i * 4 + 1]; - b = videoPalette[i * 4 + 2]; - - weight = 3 * r * r + 6 * g * g + 2 * b * b; - - if (weight >= maxWeight) { - _curFrameWhite = i; - maxWeight = weight; - } - - if (weight <= minWeight) { - _curFrameBlack = i; - minWeight = i; - } - } - - g_system->setPalette(videoPalette, 0, 256); -} - -bool VideoDecoder::endOfVideo() const { - return !isVideoLoaded() || getCurFrame() >= (int32)getFrameCount() - 1; -} - - -/* - * VideoPlayer - */ - -void VideoPlayer::processVideoEvents(Common::List<Common::Event> &stopEvents) { - Common::Event curEvent; - Common::EventManager *eventMan = g_system->getEventManager(); - - // Process events, and skip video if esc is pressed - while (eventMan->pollEvent(curEvent)) { - if (curEvent.type == Common::EVENT_RTL || curEvent.type == Common::EVENT_QUIT) { - _skipVideo = true; - } - - for (Common::List<Common::Event>::const_iterator iter = stopEvents.begin(); iter != stopEvents.end(); ++iter) { - if (curEvent.type == iter->type) { - if (iter->type == Common::EVENT_KEYDOWN || iter->type == Common::EVENT_KEYUP) { - if (curEvent.kbd.keycode == iter->kbd.keycode) { - _skipVideo = true; - break; - } - } else { - _skipVideo = true; - break; - } - } - } - } -} - -bool VideoPlayer::playVideo(Common::List<Common::Event> &stopEvents) { - _skipVideo = false; - debug(0, "Playing video"); - - g_system->fillScreen(0); - - int frameX = (g_system->getWidth() - _decoder->getWidth()) / 2; - int frameY = (g_system->getHeight() - _decoder->getHeight()) / 2; - - while (!_decoder->endOfVideo() && !_skipVideo) { - processVideoEvents(stopEvents); - - uint32 startTime = 0; - _decoder->decodeNextFrame(); - - Graphics::Surface *screen = g_system->lockScreen(); - _decoder->copyFrameToBuffer((byte *)screen->pixels, frameX, frameY, g_system->getWidth()); - performPostProcessing((byte *)screen->pixels); - g_system->unlockScreen(); - - uint32 waitTime = _decoder->getFrameWaitTime(); - - if (!waitTime) { - warning("dropped frame %i", _decoder->getCurFrame()); - continue; - } - - // Update the screen - g_system->updateScreen(); - - startTime = g_system->getMillis(); - - // Wait before showing the next frame - while (g_system->getMillis() < startTime + waitTime && !_skipVideo) { - processVideoEvents(stopEvents); - g_system->delayMillis(10); - } - } - - return !_skipVideo; -} - -bool VideoPlayer::playVideo() { - Common::Event stopEvent; - Common::List<Common::Event> stopEvents; - stopEvents.clear(); - stopEvent.type = Common::EVENT_KEYDOWN; - stopEvent.kbd = Common::KEYCODE_ESCAPE; - stopEvents.push_back(stopEvent); - - return playVideo(stopEvents); -} - -void VideoPlayer::performPostProcessing(byte *screen) { -} - -} // End of namespace Graphics diff --git a/graphics/video/video_player.h b/graphics/video/video_player.h deleted file mode 100644 index 4f38bd9740..0000000000 --- a/graphics/video/video_player.h +++ /dev/null @@ -1,221 +0,0 @@ -/* 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. - * - * $URL$ - * $Id$ - * - */ - -#ifndef GRAPHICS_VIDEO_PLAYER_H -#define GRAPHICS_VIDEO_PLAYER_H - -#include "common/events.h" -#include "common/list.h" -#include "common/stream.h" - -namespace Common { - class SeekableReadStream; -} - -namespace Graphics { - -/** - * Implementation of a generic video decoder - */ -class VideoDecoder { -public: - VideoDecoder(); - virtual ~VideoDecoder(); - - /** - * Returns the width of the video - * @return the width of the video - */ - virtual int getWidth(); - - /** - * Returns the height of the video - * @return the height of the video - */ - virtual int getHeight(); - - /** - * Returns the current frame number of the video - * @return the current frame number of the video - */ - virtual int32 getCurFrame() const; - - /** - * Returns the amount of frames in the video - * @return the amount of frames in the video - */ - virtual int32 getFrameCount() const; - - /** - * Returns the frame rate of the video - * @return the frame rate of the video - */ - virtual int32 getFrameRate(); - - /** - * Returns the time to wait for each frame in 1/100 ms (to avoid rounding errors) - * @return the time to wait for each frame in 1/100 ms (to avoid rounding errors) - */ - virtual int32 getFrameDelay(); - - /** - * Returns the current A/V lag in 1/100 ms (to avoid rounding errors) - * If > 0, audio lags behind - * If < 0, video lags behind - * @return the current A/V lag in 1/100 ms (to avoid rounding errors) - */ - virtual int32 getAudioLag(); - - /** - * Returns the time to wait until the next frame in ms, minding any lag - * @return the time to wait until the next frame in ms - */ - virtual uint32 getFrameWaitTime(); - - /** - * Load a video file - * @param filename the filename to load - */ - virtual bool loadFile(const char *filename) = 0; - - /** - * Close a video file - */ - virtual void closeFile() = 0; - - /** - * Returns if a video file is loaded or not - */ - bool isVideoLoaded() const { return (_fileStream != NULL); } - - /** - * Set RGB palette, based on current frame - * @param pal the RGB palette data - */ - virtual void setPalette(byte *pal); - - /** - * Gets the value of the pixel at the specified x and y coordinates - * Note: This method assumes that the video's pitch equals its width, and that - * the video has an 8bpp palette - * @param x the x coordinate of the pixel - * @param y the y coordinate of the pixel - */ - byte getPixel(int x, int y) { - return *(_videoFrameBuffer + y * _videoInfo.width + x * 1); - } - - /** - * Gets the value of the pixel at the specified offset - * @param offset the offset of the pixel in the video buffer - */ - byte getPixel(int offset) { return getPixel(offset, 0); } - - /** - * Return the black palette color for the current frame - */ - byte getBlack() { return _curFrameBlack; } - - /** - * Return the white palette color for the current frame - */ - byte getWhite() { return _curFrameWhite; } - - /** - * Copy current frame into the specified position of the destination - * buffer. - * @param dst the buffer - * @param x the x position of the buffer - * @param y the y position of the buffer - * @param pitch the pitch of buffer - */ - void copyFrameToBuffer(byte *dst, uint x, uint y, uint pitch); - - /** - * Decode the next frame to _videoFrameBuffer - */ - virtual bool decodeNextFrame() = 0; - - /** - * Returns if the video is finished or not - */ - virtual bool endOfVideo() const; - -protected: - struct { - uint32 width; - uint32 height; - uint32 frameCount; - int32 frameRate; - int32 frameDelay; // 1/100 ms (to avoid rounding errors) - uint32 firstframeOffset; - int32 currentFrame; - uint32 startTime; - } _videoInfo; - - byte _curFrameBlack, _curFrameWhite; - - Common::SeekableReadStream *_fileStream; - byte *_videoFrameBuffer; -}; - -class VideoPlayer { -public: - VideoPlayer(VideoDecoder* decoder) : _skipVideo(false), _decoder(decoder) - { } - virtual ~VideoPlayer() { } - /** - * A default implementation of a video player - * Plays a non-interactive full screen video till it's stopped by a - * specific event - * @param filename the name of the file to play - * @param stopEvents a list of events that can stop the video - * - * Returns true if the video was played to the end, false if skipped - */ - bool playVideo(Common::List<Common::Event> &stopEvents); - - /** - * Provides the same functionality as the video player, and it adds the - * event of skipping the video with the escape key by default - */ - bool playVideo(); - -protected: - /** - * Perform postprocessing once the frame data is copied to the screen, - * right before the frame is drawn. Called by playVideo() - */ - virtual void performPostProcessing(byte *screen); - - bool _skipVideo; - VideoDecoder* _decoder; - - void processVideoEvents(Common::List<Common::Event> &stopEvents); -}; - -} // End of namespace Graphics - -#endif |