aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--engines/sword25/fmv/movieplayer.cpp43
-rw-r--r--engines/sword25/fmv/movieplayer.h22
-rw-r--r--engines/sword25/fmv/movieplayer_script.cpp196
-rw-r--r--engines/sword25/fmv/theora_decoder.cpp434
-rwxr-xr-xengines/sword25/fmv/theora_decoder.h120
-rw-r--r--engines/sword25/module.mk9
6 files changed, 700 insertions, 124 deletions
diff --git a/engines/sword25/fmv/movieplayer.cpp b/engines/sword25/fmv/movieplayer.cpp
index bfba6d1b7a..a031fc8283 100644
--- a/engines/sword25/fmv/movieplayer.cpp
+++ b/engines/sword25/fmv/movieplayer.cpp
@@ -38,11 +38,52 @@ namespace Sword25 {
#define BS_LOG_PREFIX "MOVIEPLAYER"
-BS_MoviePlayer::BS_MoviePlayer(BS_Kernel * pKernel) : BS_Service(pKernel) {
+BS_Service *BS_OggTheora_CreateObject(BS_Kernel *pKernel) { return new BS_MoviePlayer(pKernel); }
+
+BS_MoviePlayer::BS_MoviePlayer(BS_Kernel *pKernel) : BS_Service(pKernel) {
if (!_RegisterScriptBindings())
BS_LOG_ERRORLN("Script bindings could not be registered.");
else
BS_LOGLN("Script bindings registered.");
}
+bool BS_MoviePlayer::LoadMovie(const Common::String &Filename, unsigned int Z) {
+ return true;
+}
+
+bool BS_MoviePlayer::UnloadMovie() {
+ return true;
+}
+
+bool BS_MoviePlayer::Play() {
+ return true;
+}
+
+bool BS_MoviePlayer::Pause() {
+ return true;
+}
+
+void BS_MoviePlayer::Update() {
+}
+
+bool BS_MoviePlayer::IsMovieLoaded() {
+ return true;
+}
+
+bool BS_MoviePlayer::IsPaused() {
+ return true;
+}
+
+float BS_MoviePlayer::GetScaleFactor() {
+ return 1.0f;
+}
+
+void BS_MoviePlayer::SetScaleFactor(float ScaleFactor) {
+}
+
+double BS_MoviePlayer::GetTime() {
+ return 1.0;
+}
+
+
} // End of namespace Sword25
diff --git a/engines/sword25/fmv/movieplayer.h b/engines/sword25/fmv/movieplayer.h
index 9988b5e182..be841c51a9 100644
--- a/engines/sword25/fmv/movieplayer.h
+++ b/engines/sword25/fmv/movieplayer.h
@@ -55,7 +55,7 @@ public:
// -----------------------------------------------------------------------------
BS_MoviePlayer(BS_Kernel *pKernel);
- virtual ~BS_MoviePlayer() {};
+ ~BS_MoviePlayer() {};
// -----------------------------------------------------------------------------
// Abstract interface must be implemented by each Movie Player
@@ -71,14 +71,14 @@ public:
* @param Z Z indicates the position of the film on the main graphics layer
* @return Returns false if an error occured while loading, otherwise true.
*/
- virtual bool LoadMovie(const Common::String &Filename, unsigned int Z) = 0;
+ bool LoadMovie(const Common::String &Filename, unsigned int Z);
/**
* Unloads the currently loaded movie file.
* @return Returns false if an error occurred while unloading, otherwise true.
* @remark This method can only be called when IsMovieLoaded() returns true.
*/
- virtual bool UnloadMovie() = 0;
+ bool UnloadMovie();
/**
* Plays the loaded movie.
@@ -88,7 +88,7 @@ public:
* @return Returns false if an error occurred while starting, otherwise true.
* @remark This method can only be called when IsMovieLoaded() returns true.
*/
- virtual bool Play() = 0;
+ bool Play();
/**
* Pauses movie playback.
@@ -97,23 +97,23 @@ public:
* @return Returns false if an error occurred while pausing, otherwise true.
* @remark This method can only be called when IsMovieLoaded() returns true.
*/
- virtual bool Pause() = 0;
+ bool Pause();
/**
* This function must be called once per frame.
*/
- virtual void Update() = 0;
+ void Update();
/**
* Returns whether a film is loaded for playback.
*/
- virtual bool IsMovieLoaded() = 0;
+ bool IsMovieLoaded();
/**
* Returns whether the movie playback is paused.
* @remark This method can only be called when IsMovieLoaded() returns true.
*/
- virtual bool IsPaused() = 0;
+ bool IsPaused();
/**
* Returns the scaling factor for the loaded film.
@@ -123,20 +123,20 @@ public:
* @return Returns the scaling factor of the film.
* @remark This method can only be called when IsMovieLoaded() returns true.
*/
- virtual float GetScaleFactor() = 0;
+ float GetScaleFactor();
/**
* Sets the factor by which the loaded film is to be scaled.
* @param ScaleFactor The desired scale factor.
* @remark This method can only be called when IsMovieLoaded() returns true.
*/
- virtual void SetScaleFactor(float ScaleFactor) = 0;
+ void SetScaleFactor(float ScaleFactor);
/**
* Returns the current playing position in seconds.
* @remark This method can only be called when IsMovieLoaded() returns true.
*/
- virtual double GetTime() = 0;
+ double GetTime();
private:
bool _RegisterScriptBindings();
diff --git a/engines/sword25/fmv/movieplayer_script.cpp b/engines/sword25/fmv/movieplayer_script.cpp
index f95dc90b1d..523cc2d4f3 100644
--- a/engines/sword25/fmv/movieplayer_script.cpp
+++ b/engines/sword25/fmv/movieplayer_script.cpp
@@ -23,7 +23,7 @@
*
*/
-/*
+/*
* This code is based on Broken Sword 2.5 engine
*
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
@@ -45,150 +45,138 @@
namespace Sword25 {
- int LoadMovie(lua_State * L)
- {
- BS_MoviePlayer * FMVPtr = BS_Kernel::GetInstance()->GetFMV();
- BS_ASSERT(FMVPtr);
+int LoadMovie(lua_State *L) {
+ BS_MoviePlayer *FMVPtr = BS_Kernel::GetInstance()->GetFMV();
+ BS_ASSERT(FMVPtr);
- lua_pushbooleancpp(L, FMVPtr->LoadMovie(luaL_checkstring(L, 1), lua_gettop(L) == 2 ? static_cast<unsigned int>(luaL_checknumber(L, 2)) : 10));
+ lua_pushbooleancpp(L, FMVPtr->LoadMovie(luaL_checkstring(L, 1), lua_gettop(L) == 2 ? static_cast<unsigned int>(luaL_checknumber(L, 2)) : 10));
- return 1;
- }
-
- // -------------------------------------------------------------------------
-
- int UnloadMovie(lua_State * L)
- {
- BS_MoviePlayer * FMVPtr = BS_Kernel::GetInstance()->GetFMV();
- BS_ASSERT(FMVPtr);
-
- lua_pushbooleancpp(L, FMVPtr->UnloadMovie());
+ return 1;
+}
- return 1;
- }
+// -------------------------------------------------------------------------
- // -------------------------------------------------------------------------
+int UnloadMovie(lua_State *L) {
+ BS_MoviePlayer *FMVPtr = BS_Kernel::GetInstance()->GetFMV();
+ BS_ASSERT(FMVPtr);
- int Play(lua_State * L)
- {
- BS_MoviePlayer * FMVPtr = BS_Kernel::GetInstance()->GetFMV();
- BS_ASSERT(FMVPtr);
+ lua_pushbooleancpp(L, FMVPtr->UnloadMovie());
- lua_pushbooleancpp(L, FMVPtr->Play());
+ return 1;
+}
- return 1;
- }
+// -------------------------------------------------------------------------
- // -------------------------------------------------------------------------
+int Play(lua_State *L) {
+ BS_MoviePlayer *FMVPtr = BS_Kernel::GetInstance()->GetFMV();
+ BS_ASSERT(FMVPtr);
- int Pause(lua_State * L)
- {
- BS_MoviePlayer * FMVPtr = BS_Kernel::GetInstance()->GetFMV();
- BS_ASSERT(FMVPtr);
+ lua_pushbooleancpp(L, FMVPtr->Play());
- lua_pushbooleancpp(L, FMVPtr->Pause());
+ return 1;
+}
- return 1;
- }
+// -------------------------------------------------------------------------
- // -------------------------------------------------------------------------
+int Pause(lua_State *L) {
+ BS_MoviePlayer *FMVPtr = BS_Kernel::GetInstance()->GetFMV();
+ BS_ASSERT(FMVPtr);
- int Update(lua_State * L)
- {
- BS_MoviePlayer * FMVPtr = BS_Kernel::GetInstance()->GetFMV();
- BS_ASSERT(FMVPtr);
+ lua_pushbooleancpp(L, FMVPtr->Pause());
- FMVPtr->Update();
+ return 1;
+}
- return 0;
- }
+// -------------------------------------------------------------------------
- // -------------------------------------------------------------------------
+int Update(lua_State *L) {
+ BS_MoviePlayer *FMVPtr = BS_Kernel::GetInstance()->GetFMV();
+ BS_ASSERT(FMVPtr);
- int IsMovieLoaded(lua_State * L)
- {
- BS_MoviePlayer * FMVPtr = BS_Kernel::GetInstance()->GetFMV();
- BS_ASSERT(FMVPtr);
+ FMVPtr->Update();
- lua_pushbooleancpp(L, FMVPtr->IsMovieLoaded());
+ return 0;
+}
- return 1;
- }
+// -------------------------------------------------------------------------
- // -------------------------------------------------------------------------
+int IsMovieLoaded(lua_State *L) {
+ BS_MoviePlayer *FMVPtr = BS_Kernel::GetInstance()->GetFMV();
+ BS_ASSERT(FMVPtr);
- int IsPaused(lua_State * L)
- {
- BS_MoviePlayer * FMVPtr = BS_Kernel::GetInstance()->GetFMV();
- BS_ASSERT(FMVPtr);
+ lua_pushbooleancpp(L, FMVPtr->IsMovieLoaded());
- lua_pushbooleancpp(L, FMVPtr->IsPaused());
+ return 1;
+}
- return 1;
- }
+// -------------------------------------------------------------------------
- // -------------------------------------------------------------------------
+int IsPaused(lua_State *L) {
+ BS_MoviePlayer *FMVPtr = BS_Kernel::GetInstance()->GetFMV();
+ BS_ASSERT(FMVPtr);
- int GetScaleFactor(lua_State * L)
- {
- BS_MoviePlayer * FMVPtr = BS_Kernel::GetInstance()->GetFMV();
- BS_ASSERT(FMVPtr);
+ lua_pushbooleancpp(L, FMVPtr->IsPaused());
- lua_pushnumber(L, FMVPtr->GetScaleFactor());
+ return 1;
+}
- return 1;
- }
+// -------------------------------------------------------------------------
- // -------------------------------------------------------------------------
+int GetScaleFactor(lua_State *L) {
+ BS_MoviePlayer *FMVPtr = BS_Kernel::GetInstance()->GetFMV();
+ BS_ASSERT(FMVPtr);
- int SetScaleFactor(lua_State * L)
- {
- BS_MoviePlayer * FMVPtr = BS_Kernel::GetInstance()->GetFMV();
- BS_ASSERT(FMVPtr);
+ lua_pushnumber(L, FMVPtr->GetScaleFactor());
- FMVPtr->SetScaleFactor(static_cast<float>(luaL_checknumber(L, 1)));
+ return 1;
+}
- return 0;
- }
+// -------------------------------------------------------------------------
- // -------------------------------------------------------------------------
+int SetScaleFactor(lua_State *L) {
+ BS_MoviePlayer *FMVPtr = BS_Kernel::GetInstance()->GetFMV();
+ BS_ASSERT(FMVPtr);
- int GetTime(lua_State * L)
- {
- BS_MoviePlayer * FMVPtr = BS_Kernel::GetInstance()->GetFMV();
- BS_ASSERT(FMVPtr);
+ FMVPtr->SetScaleFactor(static_cast<float>(luaL_checknumber(L, 1)));
- lua_pushnumber(L, FMVPtr->GetTime());
+ return 0;
+}
- return 1;
- }
+// -------------------------------------------------------------------------
- // -------------------------------------------------------------------------
+int GetTime(lua_State *L) {
+ BS_MoviePlayer *FMVPtr = BS_Kernel::GetInstance()->GetFMV();
+ BS_ASSERT(FMVPtr);
- const char * LIBRARY_NAME = "Movieplayer";
+ lua_pushnumber(L, FMVPtr->GetTime());
- const luaL_reg LIBRARY_FUNCTIONS[] =
- {
- { "LoadMovie", LoadMovie },
- { "UnloadMovie", UnloadMovie },
- { "Play", Play },
- { "Pause", Pause },
- { "Update", Update },
- { "IsMovieLoaded", IsMovieLoaded },
- { "IsPaused", IsPaused },
- { "GetScaleFactor", GetScaleFactor },
- { "SetScaleFactor", SetScaleFactor },
- { "GetTime", GetTime },
- { 0, 0 }
- };
+ return 1;
+}
-bool BS_MoviePlayer::_RegisterScriptBindings()
-{
- BS_Kernel * pKernel = BS_Kernel::GetInstance();
+// -------------------------------------------------------------------------
+
+const char *LIBRARY_NAME = "Movieplayer";
+
+const luaL_reg LIBRARY_FUNCTIONS[] = {
+ { "LoadMovie", LoadMovie },
+ { "UnloadMovie", UnloadMovie },
+ { "Play", Play },
+ { "Pause", Pause },
+ { "Update", Update },
+ { "IsMovieLoaded", IsMovieLoaded },
+ { "IsPaused", IsPaused },
+ { "GetScaleFactor", GetScaleFactor },
+ { "SetScaleFactor", SetScaleFactor },
+ { "GetTime", GetTime },
+ { 0, 0 }
+};
+
+bool BS_MoviePlayer::_RegisterScriptBindings() {
+ BS_Kernel *pKernel = BS_Kernel::GetInstance();
BS_ASSERT(pKernel);
- BS_ScriptEngine * pScript = static_cast<BS_ScriptEngine *>(pKernel->GetService("script"));
+ BS_ScriptEngine *pScript = static_cast<BS_ScriptEngine *>(pKernel->GetService("script"));
BS_ASSERT(pScript);
- lua_State * L = static_cast<lua_State *>(pScript->GetScriptObject());
+ lua_State *L = static_cast<lua_State *>(pScript->GetScriptObject());
BS_ASSERT(L);
if (!BS_LuaBindhelper::AddFunctionsToLib(L, LIBRARY_NAME, LIBRARY_FUNCTIONS)) return false;
diff --git a/engines/sword25/fmv/theora_decoder.cpp b/engines/sword25/fmv/theora_decoder.cpp
new file mode 100644
index 0000000000..0a4de4e649
--- /dev/null
+++ b/engines/sword25/fmv/theora_decoder.cpp
@@ -0,0 +1,434 @@
+/* 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$
+ *
+ */
+
+/*
+ * Source is based on the player example from libvorbis package
+ *
+ * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE.
+ * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS
+ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE
+ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING.
+ *
+ * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009
+ * by the Xiph.Org Foundation and contributors http://www.xiph.org/
+ *
+ */
+
+#include "sword25/fmv/theora_decoder.h"
+#include "common/system.h"
+
+namespace Sword25 {
+
+TheoraDecoder::TheoraDecoder() {
+ _fileStream = 0;
+ _surface = 0;
+
+ _theoraPacket = 0;
+ _vorbisPacket = 0;
+ _stateFlag = 0;
+}
+
+TheoraDecoder::~TheoraDecoder() {
+ close();
+}
+
+void TheoraDecoder::queuePage(ogg_page *page) {
+ if (_theoraPacket)
+ ogg_stream_pagein(&_theoraOut, page);
+
+ if (_vorbisPacket)
+ ogg_stream_pagein(&_vorbisOut, page);
+}
+
+int TheoraDecoder::bufferData() {
+ char *buffer = ogg_sync_buffer(&_oggSync, 4096);
+ int bytes = _fileStream->read(buffer, 4096);
+
+ ogg_sync_wrote(&_oggSync, bytes);
+
+ return(bytes);
+}
+
+bool TheoraDecoder::load(Common::SeekableReadStream &stream) {
+ close();
+
+ _fileStream = &stream;
+
+ // start up Ogg stream synchronization layer
+ ogg_sync_init(&_oggSync);
+
+ // init supporting Vorbis structures needed in header parsing
+ vorbis_info_init(&_vorbisInfo);
+ vorbis_comment_init(&_vorbisComment);
+
+ // init supporting Theora structures needed in header parsing
+ th_comment_init(&_theoraComment);
+ th_info_init(&_theoraInfo);
+
+ // Ogg file open; parse the headers
+ // Only interested in Vorbis/Theora streams
+ while (!_stateFlag) {
+ int ret = bufferData();
+
+ if (ret == 0)
+ break;
+
+ while (ogg_sync_pageout(&_oggSync, &_oggPage) > 0) {
+ ogg_stream_state test;
+
+ // is this a mandated initial header? If not, stop parsing
+ if (!ogg_page_bos(&_oggPage)) {
+ // don't leak the page; get it into the appropriate stream
+ queuePage(&_oggPage);
+ _stateFlag = 1;
+ break;
+ }
+
+ ogg_stream_init(&test, ogg_page_serialno(&_oggPage));
+ ogg_stream_pagein(&test, &_oggPage);
+ ogg_stream_packetout(&test, &_oggPacket);
+
+ // identify the codec: try theora
+ if (!_theoraPacket && th_decode_headerin(&_theoraInfo, &_theoraComment, &_theoraSetup, &_oggPacket) >= 0) {
+ // it is theora
+ memcpy(&_theoraOut, &test, sizeof(test));
+ _theoraPacket = 1;
+ } else if (!_vorbisPacket && vorbis_synthesis_headerin(&_vorbisInfo, &_vorbisComment, &_oggPacket) >= 0) {
+ // it is vorbis
+ memcpy(&_vorbisOut, &test, sizeof(test));
+ _vorbisPacket = 1;
+ } else {
+ // whatever it is, we don't care about it
+ ogg_stream_clear(&test);
+ }
+ }
+ // fall through to non-bos page parsing
+ }
+
+ // we're expecting more header packets.
+ while ((_theoraPacket && _theoraPacket < 3) || (_vorbisPacket && _vorbisPacket < 3)) {
+ int ret;
+
+ // look for further theora headers
+ while (_theoraPacket && (_theoraPacket < 3) && (ret = ogg_stream_packetout(&_theoraOut, &_oggPacket))) {
+ if (ret < 0)
+ error("Error parsing Theora stream headers; corrupt stream?");
+
+ if (!th_decode_headerin(&_theoraInfo, &_theoraComment, &_theoraSetup, &_oggPacket))
+ error("Error parsing Theora stream headers; corrupt stream?");
+
+ _theoraPacket++;
+ }
+
+ // look for more vorbis header packets
+ while (_vorbisPacket && (_vorbisPacket < 3) && (ret = ogg_stream_packetout(&_vorbisOut, &_oggPacket))) {
+ if (ret < 0)
+ error("Error parsing Vorbis stream headers; corrupt stream?");
+
+ if (vorbis_synthesis_headerin(&_vorbisInfo, &_vorbisComment, &_oggPacket))
+ error("Error parsing Vorbis stream headers; corrupt stream?");
+
+ _vorbisPacket++;
+
+ if (_vorbisPacket == 3)
+ break;
+ }
+
+ // The header pages/packets will arrive before anything else we
+ // care about, or the stream is not obeying spec
+
+ if (ogg_sync_pageout(&_oggSync, &_oggPage) > 0) {
+ queuePage(&_oggPage); // demux into the appropriate stream
+ } else {
+ ret = bufferData(); // someone needs more data
+
+ if (ret == 0)
+ error("End of file while searching for codec headers.");
+ }
+ }
+
+ // and now we have it all. initialize decoders
+ if (_theoraPacket) {
+ _theoraDecode = th_decode_alloc(&_theoraInfo, _theoraSetup);
+ debugN(1, "Ogg logical stream %lx is Theora %dx%d %.02f fps",
+ _theoraOut.serialno, _theoraInfo.pic_width, _theoraInfo.pic_height,
+ (double)_theoraInfo.fps_numerator / _theoraInfo.fps_denominator);
+ switch (_theoraInfo.pixel_fmt) {
+ case TH_PF_420:
+ debug(1, " 4:2:0 video");
+ break;
+ case TH_PF_422:
+ debug(1, " 4:2:2 video");
+ break;
+ case TH_PF_444:
+ debug(1, " 4:4:4 video");
+ break;
+ case TH_PF_RSVD:
+ default:
+ debug(1, " video\n (UNKNOWN Chroma sampling!)");
+ break;
+ }
+
+ if (_theoraInfo.pic_width != _theoraInfo.frame_width || _theoraInfo.pic_height != _theoraInfo.frame_height)
+ debug(1, " Frame content is %dx%d with offset (%d,%d).",
+ _theoraInfo.frame_width, _theoraInfo.frame_height, _theoraInfo.pic_x, _theoraInfo.pic_y);
+
+ th_decode_ctl(_theoraDecode, TH_DECCTL_GET_PPLEVEL_MAX, &_ppLevelMax, sizeof(_ppLevelMax));
+ _ppLevel = _ppLevelMax;
+ th_decode_ctl(_theoraDecode, TH_DECCTL_SET_PPLEVEL, &_ppLevel, sizeof(_ppLevel));
+ _ppInc = 0;
+
+ } else {
+ // tear down the partial theora setup
+ th_info_clear(&_theoraInfo);
+ th_comment_clear(&_theoraComment);
+ }
+
+ th_setup_free(_theoraSetup);
+
+ if (_vorbisPacket) {
+ vorbis_synthesis_init(&_vorbisDSP, &_vorbisInfo);
+ vorbis_block_init(&_vorbisDSP, &_vorbisBlock);
+ debug(3, "Ogg logical stream %lx is Vorbis %d channel %ld Hz audio.",
+ _vorbisOut.serialno, _vorbisInfo.channels, _vorbisInfo.rate);
+ } else {
+ // tear down the partial vorbis setup
+ vorbis_info_clear(&_vorbisInfo);
+ vorbis_comment_clear(&_vorbisComment);
+ }
+
+ // open audio
+ if (_vorbisPacket)
+ open_audio();
+
+ return true;
+}
+
+void TheoraDecoder::close() {
+ if (_vorbisPacket) {
+ ogg_stream_clear(&_vorbisOut);
+ vorbis_block_clear(&_vorbisBlock);
+ vorbis_dsp_clear(&_vorbisDSP);
+ vorbis_comment_clear(&_vorbisComment);
+ vorbis_info_clear(&_vorbisInfo);
+ }
+ if (_theoraPacket) {
+ ogg_stream_clear(&_theoraOut);
+ th_decode_free(_theoraDecode);
+ th_comment_clear(&_theoraComment);
+ th_info_clear(&_theoraInfo);
+ }
+ ogg_sync_clear(&_oggSync);
+
+ if (!_fileStream)
+ return;
+
+ delete _fileStream;
+ _fileStream = 0;
+
+ _surface->free();
+ delete _surface;
+ _surface = 0;
+
+ reset();
+}
+
+Graphics::Surface *TheoraDecoder::decodeNextFrame() {
+ int i, j;
+
+ _stateFlag = 0; // playback has not begun
+
+ // we want a video and audio frame ready to go at all times. If
+ // we have to buffer incoming, buffer the compressed data (ie, let
+ // ogg do the buffering)
+ while (_vorbisPacket && !_audiobufReady) {
+ int ret;
+ float **pcm;
+
+ // if there's pending, decoded audio, grab it
+ if ((ret = vorbis_synthesis_pcmout(&_vorbisDSP, &pcm)) > 0) {
+ int count = _audiobufFill / 2;
+ int maxsamples = (audiofd_fragsize - _audiobufFill) / 2 / _vorbisInfo.channels;
+ for (i = 0; i < ret && i < maxsamples; i++)
+ for (j = 0; j < _vorbisInfo.channels; j++) {
+ int val = CLIP(rint(pcm[j][i] * 32767.f), -32768, 32768);
+ _audiobuf[count++] = val;
+ }
+
+ vorbis_synthesis_read(&_vorbisDSP, i);
+ _audiobufFill += i * _vorbisInfo.channels * 2;
+
+ if (_audiobufFill == audiofd_fragsize)
+ _audiobufReady = 1;
+
+ if (_vorbisDSP.granulepos >= 0)
+ _audiobufGranulePos = _vorbisDSP.granulepos - ret + i;
+ else
+ _audiobufGranulePos += i;
+ } else {
+
+ // no pending audio; is there a pending packet to decode?
+ if (ogg_stream_packetout(&_vorbisOut, &_oggPacket) > 0) {
+ if (vorbis_synthesis(&_vorbisBlock, &_oggPacket) == 0) // test for success!
+ vorbis_synthesis_blockin(&_vorbisDSP, &_vorbisBlock);
+ } else // we need more data; break out to suck in another page
+ break;
+ }
+ }
+
+ while (_theoraPacket && !_videobufReady) {
+ // theora is one in, one out...
+ if (ogg_stream_packetout(&_theoraOut, &_oggPacket) > 0) {
+
+ if (_ppInc) {
+ _ppLevel += _ppInc;
+ th_decode_ctl(_theoraDecode, TH_DECCTL_SET_PPLEVEL, &_ppLevel, sizeof(_ppLevel));
+ _ppInc = 0;
+ }
+ // HACK: This should be set after a seek or a gap, but we might not have
+ // a granulepos for the first packet (we only have them for the last
+ // packet on a page), so we just set it as often as we get it.
+ // To do this right, we should back-track from the last packet on the
+ // page and compute the correct granulepos for the first packet after
+ // a seek or a gap.
+ if (_oggPacket.granulepos >= 0) {
+ th_decode_ctl(_theoraDecode, TH_DECCTL_SET_GRANPOS, &_oggPacket.granulepos, sizeof(_oggPacket.granulepos));
+ }
+ if (th_decode_packetin(_theoraDecode, &_oggPacket, &_videobufGranulePos) == 0) {
+ _videobufTime = th_granule_time(_theoraDecode, _videobufGranulePos);
+ _curFrame++;
+
+ // is it already too old to be useful? This is only actually
+ // useful cosmetically after a SIGSTOP. Note that we have to
+ // decode the frame even if we don't show it (for now) due to
+ // keyframing. Soon enough libtheora will be able to deal
+ // with non-keyframe seeks.
+
+ if (_videobufTime >= get_time())
+ _videobufReady = 1;
+ else {
+ // If we are too slow, reduce the pp level.
+ _ppInc = _ppLevel > 0 ? -1 : 0;
+ dropped++;
+ }
+ }
+ } else
+ break;
+ }
+
+ if (!_videobufReady && !_audiobufReady && _fileStream->eos())
+ break;
+
+ if (!_videobufReady || !_audiobufReady) {
+ // no data yet for somebody. Grab another page
+ bufferData();
+ while (ogg_sync_pageout(&_oggSync, &_oggPage) > 0) {
+ queuePage(&_oggPage);
+ }
+ }
+
+ // If playback has begun, top audio buffer off immediately.
+ if (_stateFlag) audio_write_nonblocking();
+
+ // are we at or past time for this video frame?
+ if (_stateFlag && _videobufReady && _videobufTime <= get_time()) {
+ video_write();
+ _videobufReady = 0;
+ }
+
+ if (_stateFlag &&
+ (_audiobufReady || !_vorbisPacket) &&
+ (_videobufReady || !_theoraPacket) &&
+ !got_sigint) {
+ // we have an audio frame ready (which means the audio buffer is
+ // full), it's not time to play video, so wait until one of the
+ // audio buffer is ready or it's near time to play video
+
+ // set up select wait on the audiobuffer and a timeout for video
+ struct timeval timeout;
+ fd_set writefs;
+ fd_set empty;
+ int n = 0;
+
+ FD_ZERO(&writefs);
+ FD_ZERO(&empty);
+ if (audiofd >= 0) {
+ FD_SET(audiofd, &writefs);
+ n = audiofd + 1;
+ }
+
+ if (_theoraPacket) {
+ double tdiff;
+ long milliseconds;
+ tdiff = _videobufTime - get_time();
+
+ // If we have lots of extra time, increase the post-processing level.
+ if (tdiff > _theoraInfo.fps_denominator * 0.25 / _theoraInfo.fps_numerator) {
+ _ppInc = _ppLevel < _ppLevelMax ? 1 : 0;
+ } else if (tdiff < _theoraInfo.fps_denominator * 0.05 / _theoraInfo.fps_numerator) {
+ _ppInc = _ppLevel > 0 ? -1 : 0;
+ }
+ milliseconds = tdiff * 1000 - 5;
+ if (milliseconds > 500)
+ milliseconds = 500;
+ if (milliseconds > 0) {
+ timeout.tv_sec = milliseconds / 1000;
+ timeout.tv_usec = (milliseconds % 1000) * 1000;
+
+ n = select(n, &empty, &writefs, &empty, &timeout);
+ if (n)
+ audio_calibrate_timer(0);
+ }
+ } else {
+ select(n, &empty, &writefs, &empty, NULL);
+ }
+ }
+
+ // if our buffers either don't exist or are ready to go,
+ // we can begin playback
+ if ((!_theoraPacket || _videobufReady) &&
+ (!_vorbisPacket || _audiobufReady))
+ _stateFlag = 1;
+
+ // same if we've run out of input
+ if (_fileStream->eos())
+ _stateFlag = 1;
+}
+
+void TheoraDecoder::reset() {
+ VideoDecoder::reset();
+ if (_fileStream)
+ _fileStream->seek(0);
+
+ _videobufReady = 0;
+ _videobufGranulePos = -1;
+ _videobufTime = 0;
+
+ _audiobufFill = 0;
+ _audiobufReady = 0;
+ _audiobufGranulePos = 0;
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/fmv/theora_decoder.h b/engines/sword25/fmv/theora_decoder.h
new file mode 100755
index 0000000000..cda1c8a6cb
--- /dev/null
+++ b/engines/sword25/fmv/theora_decoder.h
@@ -0,0 +1,120 @@
+/* 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 SWORD25_THEORADECODER_H
+#define SWORD25_THEORADECODER_H
+
+#include "graphics/video/video_decoder.h"
+
+#include <theora/theoradec.h>
+#include <vorbis/codec.h>
+
+namespace Common {
+ class SeekableReadStream;
+}
+
+namespace Sword25 {
+
+/**
+ *
+ * Decoder for Theora videos.
+ * Video decoder used in engines:
+ * - sword25
+ */
+class TheoraDecoder : public Graphics::FixedRateVideoDecoder {
+public:
+ TheoraDecoder();
+ virtual ~TheoraDecoder();
+
+ /**
+ * Load a video file
+ * @param stream the stream to load
+ */
+ bool load(Common::SeekableReadStream &stream);
+ void close();
+
+ /**
+ * 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
+ */
+ Graphics::Surface *decodeNextFrame();
+
+ bool isVideoLoaded() const { return _fileStream != 0; }
+ uint16 getWidth() const { return _surface->w; }
+ uint16 getHeight() const { return _surface->h; }
+ uint32 getFrameCount() const { return _frameCount; }
+ Graphics::PixelFormat getPixelFormat() const { return Graphics::PixelFormat(3, 8, 8, 8, 0, 0, 0, 0, 0); }
+
+protected:
+ Common::Rational getFrameRate() const { return _frameRate; }
+
+private:
+ void queuePage(ogg_page *page);
+ int bufferData();
+
+private:
+ Common::SeekableReadStream *_fileStream;
+ Graphics::Surface *_surface;
+ Common::Rational _frameRate;
+ uint32 _frameCount;
+
+ ogg_sync_state _oggSync;
+ ogg_page _oggPage;
+ ogg_packet _oggPacket;
+ ogg_stream_state _vorbisOut;
+ ogg_stream_state _theoraOut;
+ th_info _theoraInfo;
+ th_comment _theoraComment;
+ th_dec_ctx *_theoraDecode;
+ th_setup_info *_theoraSetup;
+ vorbis_info _vorbisInfo;
+ vorbis_dsp_state _vorbisDSP;
+ vorbis_block _vorbisBlock;
+ vorbis_comment _vorbisComment;
+
+ int _theoraPacket;
+ int _vorbisPacket;
+ int _stateFlag;
+
+ int _ppLevelMax;
+ int _ppLevel;
+ int _ppInc;
+
+ // single frame video buffering
+ int _videobufReady;
+ ogg_int64_t _videobufGranulePos;
+ double _videobufTime;
+
+ // single audio fragment audio buffering
+ int _audiobufFill;
+ int _audiobufReady;
+ ogg_int16_t *_audiobuf;
+ ogg_int64_t _audiobufGranulePos; // time position of last sample
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/module.mk b/engines/sword25/module.mk
index e1320252b1..f61a86a262 100644
--- a/engines/sword25/module.mk
+++ b/engines/sword25/module.mk
@@ -5,14 +5,7 @@ MODULE_OBJS := \
sword25.o \
fmv/movieplayer.o \
fmv/movieplayer_script.o \
- fmv/oggtheora/audiobuffer.o \
- fmv/oggtheora/moviefile.o \
- fmv/oggtheora/oggstate.o \
- fmv/oggtheora/oggstreamstate.o \
- fmv/oggtheora/oggtheora.o \
- fmv/oggtheora/theorastate.o \
- fmv/oggtheora/vorbisstate.o \
- fmv/oggtheora/yuvtorgba.o \
+ fmv/theora_decoder.o \
gfx/animation.o \
gfx/animationdescription.o \
gfx/animationresource.o \