aboutsummaryrefslogtreecommitdiff
path: root/engines/wintermute
diff options
context:
space:
mode:
authorEinar Johan Trøan Sømåen2012-06-01 02:34:32 +0200
committerEinar Johan Trøan Sømåen2012-06-02 13:07:22 +0200
commit2f02bec21fcbffc18964443c2f59a59ebe50f724 (patch)
treecff982bd9747b9befa6936581554c07bd23c0e1f /engines/wintermute
parent0cee40306710b0ddae57e962797fd8d40299558b (diff)
downloadscummvm-rg350-2f02bec21fcbffc18964443c2f59a59ebe50f724.tar.gz
scummvm-rg350-2f02bec21fcbffc18964443c2f59a59ebe50f724.tar.bz2
scummvm-rg350-2f02bec21fcbffc18964443c2f59a59ebe50f724.zip
WINTERMUTE: Add in, and stub the VideoPlayer-files
Diffstat (limited to 'engines/wintermute')
-rw-r--r--engines/wintermute/BGame.cpp74
-rw-r--r--engines/wintermute/BGame.h3
-rw-r--r--engines/wintermute/VidPlayer.cpp467
-rw-r--r--engines/wintermute/VidPlayer.h93
-rw-r--r--engines/wintermute/VidTheoraPlayer.cpp957
-rw-r--r--engines/wintermute/VidTheoraPlayer.h175
-rw-r--r--engines/wintermute/module.mk4
7 files changed, 1770 insertions, 3 deletions
diff --git a/engines/wintermute/BGame.cpp b/engines/wintermute/BGame.cpp
index 6758d2e71e..59d8f2b4fe 100644
--- a/engines/wintermute/BGame.cpp
+++ b/engines/wintermute/BGame.cpp
@@ -1373,20 +1373,90 @@ HRESULT CBGame::ScCallMethod(CScScript *Script, CScStack *Stack, CScStack *ThisS
// PlayVideo
//////////////////////////////////////////////////////////////////////////
else if (strcmp(Name, "PlayVideo") == 0) {
- Stack->CorrectParams(0);
+/* Stack->CorrectParams(0);
Stack->PushBool(false);
return S_OK;
+ // TODO: ADDVIDEO
+ */
+
+ Game->LOG(0, "Warning: Game.PlayVideo() is now deprecated. Use Game.PlayTheora() instead.");
+
+ Stack->CorrectParams(6);
+ const char* Filename = Stack->Pop()->GetString();
+ warning("PlayVideo: %s - not implemented yet", Filename);
+ CScValue* valType = Stack->Pop();
+ int Type;
+ if(valType->IsNULL()) Type = (int)VID_PLAY_STRETCH;
+ else Type = valType->GetInt();
+
+ int X = Stack->Pop()->GetInt();
+ int Y = Stack->Pop()->GetInt();
+ bool FreezeMusic = Stack->Pop()->GetBool(true);
+
+ CScValue* valSub = Stack->Pop();
+ const char* SubtitleFile = valSub->IsNULL()?NULL:valSub->GetString();
+
+ if(Type < (int)VID_PLAY_POS || Type > (int)VID_PLAY_CENTER) Type = (int)VID_PLAY_STRETCH;
+
+ /*if(SUCCEEDED(Game->m_VideoPlayer->Initialize(Filename, SubtitleFile)))
+ {
+ if(SUCCEEDED(Game->m_VideoPlayer->Play((TVideoPlayback)Type, X, Y, FreezeMusic)))
+ {
+ Stack->PushBool(true);
+ Script->Sleep(0);
+ }
+ else Stack->PushBool(false);
+ }
+ else */Stack->PushBool(false);
+
+ return S_OK;
}
//////////////////////////////////////////////////////////////////////////
// PlayTheora
//////////////////////////////////////////////////////////////////////////
else if (strcmp(Name, "PlayTheora") == 0) {
- Stack->CorrectParams(0);
+ /* Stack->CorrectParams(0);
Stack->PushBool(false);
return S_OK;
+ // TODO: ADDVIDEO
+ */
+ Stack->CorrectParams(7);
+ const char* Filename = Stack->Pop()->GetString();
+ warning("PlayTheora: %s - not implemented yet", Filename);
+ CScValue* valType = Stack->Pop();
+ int Type;
+ if(valType->IsNULL()) Type = (int)VID_PLAY_STRETCH;
+ else Type = valType->GetInt();
+
+ int X = Stack->Pop()->GetInt();
+ int Y = Stack->Pop()->GetInt();
+ bool FreezeMusic = Stack->Pop()->GetBool(true);
+ bool DropFrames = Stack->Pop()->GetBool(true);
+
+ CScValue* valSub = Stack->Pop();
+ const char* SubtitleFile = valSub->IsNULL()?NULL:valSub->GetString();
+
+
+ if(Type < (int)VID_PLAY_POS || Type > (int)VID_PLAY_CENTER) Type = (int)VID_PLAY_STRETCH;
+
+ /*SAFE_DELETE(m_TheoraPlayer);
+ m_TheoraPlayer = new CVidTheoraPlayer(this);
+ if(m_TheoraPlayer && SUCCEEDED(m_TheoraPlayer->Initialize(Filename, SubtitleFile)))
+ {
+ m_TheoraPlayer->m_DontDropFrames = !DropFrames;
+ if(SUCCEEDED(m_TheoraPlayer->Play((TVideoPlayback)Type, X, Y, true, FreezeMusic)))
+ {
+ Stack->PushBool(true);
+ Script->Sleep(0);
+ }
+ else Stack->PushBool(false);
+ }
+ else */Stack->PushBool(false);
+
+ return S_OK;
}
//////////////////////////////////////////////////////////////////////////
diff --git a/engines/wintermute/BGame.h b/engines/wintermute/BGame.h
index 8b0797825d..3a7e165b2c 100644
--- a/engines/wintermute/BGame.h
+++ b/engines/wintermute/BGame.h
@@ -263,7 +263,8 @@ public:
HRESULT Unfreeze();
HRESULT Freeze(bool IncludingMusic = true);
HRESULT FocusWindow(CUIWindow *Window);
-
+/* CVidPlayer* _videoPlayer;
+ CVidTheoraPlayer* _theoraPlayer;*/
bool _loadInProgress;
CUIWindow *_focusedWindow;
bool _editorForceScripts;
diff --git a/engines/wintermute/VidPlayer.cpp b/engines/wintermute/VidPlayer.cpp
new file mode 100644
index 0000000000..40d953f67b
--- /dev/null
+++ b/engines/wintermute/VidPlayer.cpp
@@ -0,0 +1,467 @@
+/* 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.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+
+#include "dcgf.h"
+#include "VidPlayer.h"
+
+//#pragma comment(lib, "vfw32.lib")
+
+namespace WinterMute {
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+
+//////////////////////////////////////////////////////////////////////////
+CVidPlayer::CVidPlayer(CBGame *inGame): CBBase(inGame) {
+ SetDefaults();
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CVidPlayer::SetDefaults() {
+ _playing = false;
+
+/* _aviFile = NULL;
+
+ _audioStream = NULL;
+ _audioFormat = NULL;
+
+ _videoStream = NULL;
+ _videoFormat = NULL;
+ _videoPGF = NULL;*/
+ _videoEndTime = 0;
+
+ //_sound = NULL;
+ _soundAvailable = false;
+
+ //_vidRenderer = NULL;
+
+ _startTime = 0;
+ _totalVideoTime = 0;
+
+ //_lastSample = -1;
+
+ //_targetFormat = NULL;
+
+ _playPosX = _playPosY = 0;
+ _playZoom = 0.0f;
+
+ _filename = NULL;
+
+ _slowRendering = false;
+
+ _currentSubtitle = 0;
+ _showSubtitle = false;
+
+ return S_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CVidPlayer::~CVidPlayer() {
+ Cleanup();
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CVidPlayer::Cleanup() {
+#if 0
+ if (_sound) _sound->Stop();
+ if (_videoPGF) AVIStreamGetFrameClose(_videoPGF);
+ _videoPGF = NULL;
+
+ _playing = false;
+
+
+ if (_aviFile) AVIFileRelease(m_AviFile);
+
+ if (_audioStream) AVIStreamRelease(m_AudioStream);
+ if (_videoStream) AVIStreamRelease(m_VideoStream);
+
+ if (_audioFormat) delete [](byte *)m_AudioFormat;
+ if (_videoFormat) delete [](byte *)m_VideoFormat;
+ if (_targetFormat) delete [](byte *)m_TargetFormat;
+
+ SAFE_DELETE(_sound);
+ SAFE_DELETE(_vidRenderer);
+
+ SAFE_DELETE_ARRAY(_filename);
+
+ for (int i = 0; i < _subtitles.GetSize(); i++) delete _subtitles[i];
+ _subtitles.RemoveAll();
+
+ return SetDefaults();
+#endif
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CVidPlayer::Initialize(char *inFilename, char *SubtitleFile) {
+#if 0
+ Cleanup();
+
+ char Filename[MAX_PATH];
+ Game->_fileManager->GetFullPath(inFilename, Filename);
+
+ // open file
+ if (AVIFileOpen(&_aviFile, Filename, OF_READ, NULL) != 0) {
+ Game->LOG(0, "Error opening AVI file '%s'", Filename);
+ return E_FAIL;
+ }
+
+ // get video stream
+ if (AVIFileGetStream(_aviFile, &_videoStream, streamtypeVIDEO, 0) != 0) {
+ Game->LOG(0, "Error finding video stream in AVI file '%s'", Filename);
+ return E_FAIL;
+ }
+ _totalVideoTime = AVIStreamEndTime(_videoStream);
+
+ // get audio stream
+ if (Game->m_SoundMgr->_soundAvailable && AVIFileGetStream(_aviFile, &_audioStream, streamtypeAUDIO, 0) == 0)
+ _soundAvailable = true;
+ else
+ _soundAvailable = false;
+
+
+ LONG Size;
+
+ // get video format
+ if (AVIStreamReadFormat(m_VideoStream, 0, NULL, &Size)) {
+ Game->LOG(0, "Error obtaining video stream format in AVI file '%s'", Filename);
+ return E_FAIL;
+ }
+ _videoFormat = (LPBITMAPINFO)new BYTE[Size];
+ AVIStreamReadFormat(m_VideoStream, 0, m_VideoFormat, &Size);
+
+ // initialize optimal target format
+ m_TargetFormat = (LPBITMAPV4HEADER)new BYTE[max(Size, sizeof(BITMAPV4HEADER))];
+ memset(m_TargetFormat, 0, sizeof(BITMAPV4HEADER));
+ memcpy(m_TargetFormat, m_VideoFormat, Size);
+ m_TargetFormat->bV4Size = max(Size, sizeof(BITMAPV4HEADER));
+
+ m_TargetFormat->bV4BitCount = 24;
+ m_TargetFormat->bV4V4Compression = BI_RGB;
+
+ if (Game->m_UseD3D)
+ m_VidRenderer = new CVidRendererD3D(Game);
+ else
+ m_VidRenderer = new CVidRendererDD(Game);
+
+ if (!m_VidRenderer || FAILED(m_VidRenderer->Initialize(m_VideoFormat, m_TargetFormat))) {
+ Game->LOG(0, "Error initializing video renderer for AVI file '%s'", Filename);
+ SAFE_DELETE(m_VidRenderer);
+ return E_FAIL;
+ }
+
+
+ // create sound buffer
+ HRESULT res;
+
+ if (_soundAvailable) {
+ _sound = new CBSoundAVI(Game);
+ if (FAILED(res = _sound->InitializeBuffer(_audioStream))) {
+ SAFE_DELETE(_sound);
+ _soundAvailable = false;
+ Game->LOG(res, "Error initializing sound buffer for AVI file '%s'", Filename);
+ }
+ }
+
+ if (Game->_videoSubtitles) LoadSubtitles(inFilename, SubtitleFile);
+
+ _filename = new char[strlen(Filename) + 1];
+ if (_filename) strcpy(_filename, Filename);
+#endif
+ return S_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CVidPlayer::Update() {
+#if 0
+ if (!m_Playing) return S_OK;
+
+ HRESULT res;
+
+ if (_soundAvailable && m_Sound) {
+ res = _sound->Update();
+ if (FAILED(res)) return res;
+ }
+
+
+ DWORD CurrentTime; // current playing time (in ms)
+ /*
+ if(m_SoundAvailable && m_Sound){
+ CurrentTime = m_Sound->GetPosition(); // in samples
+ CurrentTime /= (m_Sound->m_Format.wf.nSamplesPerSec / 1000);
+
+ if(!m_Sound->IsPlaying()) CurrentTime = m_TotalVideoTime;
+ }
+ else
+ CurrentTime = timeGetTime() - m_StartTime;
+ */
+ CurrentTime = timeGetTime() - _startTime;
+
+ if (CurrentTime >= _totalVideoTime) {
+ Stop();
+ return S_OK;
+ }
+
+
+ // get and render frame
+ DWORD sample = AVIStreamTimeToSample(_videoStream, CurrentTime);
+ if (sample != _lastSample) {
+ _lastSample = sample;
+
+ // process subtitles
+ _showSubtitle = false;
+ while (_currentSubtitle < _subtitles.GetSize()) {
+ int End = _subtitles[_currentSubtitle]->m_EndFrame;
+
+ bool NextFrameOK = (_currentSubtitle < _subtitles.GetSize() - 1 && _subtitles[_currentSubtitle + 1]->_startFrame <= sample);
+
+ if (sample > End) {
+ if (NextFrameOK) {
+ _currentSubtitle++;
+ } else {
+ _showSubtitle = (End == 0);
+ break;
+ }
+ } else {
+ _showSubtitle = true;
+ break;
+ }
+ }
+
+
+ // render frame
+ LPBITMAPINFOHEADER FrameData = (LPBITMAPINFOHEADER)AVIStreamGetFrame(m_VideoPGF, sample);
+ if (FrameData) {
+ if (_slowRendering) return _vidRenderer->ProcessFrameSlow(FrameData);
+ else return _vidRenderer->ProcessFrame(FrameData);
+ } else return E_FAIL;
+ } else return S_OK;
+#endif
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CVidPlayer::Display() {
+#if 0
+ if (!m_Playing) return S_OK;
+
+ HRESULT res;
+ if (_vidRenderer) res = _vidRenderer->Display(m_PlayPosX, m_PlayPosY, m_PlayZoom);
+ else res = E_FAIL;
+
+ // display subtitle
+ if (m_ShowSubtitle) {
+ CBFont *font = Game->_videoFont ? Game->_videoFont : Game->_systemFont;
+ int Height = font->GetTextHeight((BYTE *)m_Subtitles[_currentSubtitle]->_text, Game->_renderer->_width);
+ font->DrawText((byte *)_subtitles[m_CurrentSubtitle]->_text, 0, Game->_renderer->_height - Height - 5, Game->_renderer->_width, TAL_CENTER);
+ }
+
+ return res;
+#endif
+}
+
+
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CVidPlayer::Play(TVideoPlayback Type, int X, int Y, bool FreezeMusic) {
+#if 0
+ if (!_videoStream || !_vidRenderer) return E_FAIL;
+
+ switch (Type) {
+ case VID_PLAY_POS:
+ _playZoom = 100.0f;
+ _playPosX = X;
+ _playPosY = Y;
+ break;
+
+ case VID_PLAY_STRETCH: {
+ float ZoomX = (float)((float)Game->_renderer->m_Width / (float)_videoFormat->bmiHeader.biWidth * 100);
+ float ZoomY = (float)((float)Game->_renderer->m_Height / (float)_videoFormat->bmiHeader.biHeight * 100);
+ _playZoom = min(ZoomX, ZoomY);
+ _playPosX = (Game->_renderer->_width - _videoFormat->bmiHeader.biWidth * (_playZoom / 100)) / 2;
+ _playPosY = (Game->_renderer->_height - _videoFormat->bmiHeader.biHeight * (_playZoom / 100)) / 2;
+ }
+ break;
+
+ case VID_PLAY_CENTER:
+ _playZoom = 100.0f;
+ _playPosX = (Game->_renderer->_width - _videoFormat->bmiHeader.biWidth) / 2;
+ _playPosY = (Game->_renderer->_height - _videoFormat->bmiHeader.biHeight) / 2;
+ break;
+ }
+
+ _targetFormat->bV4BitCount = 24;
+ _targetFormat->bV4V4Compression = BI_RGB;
+
+
+ _videoPGF = AVIStreamGetFrameOpen(_videoStream, (LPBITMAPINFOHEADER)m_TargetFormat);
+ if (!_videoPGF) {
+ _videoPGF = AVIStreamGetFrameOpen(_videoStream, NULL);
+ if (!_videoPGF) {
+ Game->LOG(0, "Error: Unsupported AVI format (file '%s')", m_Filename);
+ Cleanup();
+ return E_FAIL;
+ } else {
+ Game->LOG(0, "Performance warning: non-optimal AVI format, using generic (i.e. slow) rendering routines (file '%s')", m_Filename);
+ _slowRendering = true;
+ }
+ } else _slowRendering = false;
+
+ // HACK!!!
+ _slowRendering = true;
+
+
+ _currentSubtitle = 0;
+
+ Game->Freeze(FreezeMusic);
+
+ _playing = true;
+ if (_sound) _sound->Play();
+ _startTime = timeGetTime();
+#endif
+ return S_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CVidPlayer::Stop() {
+#if 0
+ if (!_playing) return S_OK;
+
+ Cleanup();
+
+ Game->Unfreeze();
+#endif
+ return S_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CVidPlayer::IsPlaying() {
+ return _playing;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CVidPlayer::LoadSubtitles(char *Filename, char *SubtitleFile) {
+#if 0
+ if (!Filename) return S_OK;
+
+ char NewFile[MAX_PATH];
+ char drive[_MAX_DRIVE];
+ char dir[_MAX_DIR];
+ char fname[_MAX_FNAME];
+
+ if (SubtitleFile) {
+ strcpy(NewFile, SubtitleFile);
+ } else {
+ _splitpath(Filename, drive, dir, fname, NULL);
+ _makepath(NewFile, drive, dir, fname, ".SUB");
+ }
+
+ DWORD Size;
+ BYTE *Buffer = Game->m_FileManager->ReadWholeFile(NewFile, &Size, false);
+ if (Buffer == NULL) return S_OK; // no subtitles
+
+
+ LONG Start, End;
+ bool InToken;
+ char *TokenStart;
+ int TokenLength;
+ int TokenPos;
+ int TextLength;
+
+ int Pos = 0;
+ int LineLength = 0;
+ while (Pos < Size) {
+ Start = End = -1;
+ InToken = false;
+ TokenPos = -1;
+ TextLength = 0;
+
+ LineLength = 0;
+ while (Pos + LineLength < Size && Buffer[Pos + LineLength] != '\n' && Buffer[Pos + LineLength] != '\0') LineLength++;
+
+ int RealLength = LineLength - (Pos + LineLength >= Size ? 0 : 1);
+ char *Text = new char[RealLength + 1];
+ char *line = (char *)&Buffer[Pos];
+
+ for (int i = 0; i < RealLength; i++) {
+ if (line[i] == '{') {
+ if (!InToken) {
+ InToken = true;
+ TokenStart = line + i + 1;
+ TokenLength = 0;
+ TokenPos++;
+ } else TokenLength++;
+ } else if (line[i] == '}') {
+ if (InToken) {
+ InToken = false;
+ char *Token = new char[TokenLength + 1];
+ strncpy(Token, TokenStart, TokenLength);
+ Token[TokenLength] = '\0';
+ if (TokenPos == 0) Start = atoi(Token);
+ else if (TokenPos == 1) End = atoi(Token);
+
+ delete [] Token;
+ } else {
+ Text[TextLength] = line[i];
+ TextLength++;
+ }
+ } else {
+ if (InToken) {
+ TokenLength++;
+ } else {
+ Text[TextLength] = line[i];
+ if (Text[TextLength] == '|') Text[TextLength] = '\n';
+ TextLength++;
+ }
+ }
+ }
+ Text[TextLength] = '\0';
+
+ if (Start != -1 && TextLength > 0) _subtitles.Add(new CVidSubtitle(Game, Text, Start, End));
+
+ delete [] Text;
+
+ Pos += LineLength + 1;
+ }
+
+ delete [] Buffer;
+#endif
+ return S_OK;
+}
+
+} // end of namespace WinterMute
diff --git a/engines/wintermute/VidPlayer.h b/engines/wintermute/VidPlayer.h
new file mode 100644
index 0000000000..e38baae413
--- /dev/null
+++ b/engines/wintermute/VidPlayer.h
@@ -0,0 +1,93 @@
+/* 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.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_VIDPLAYER_H
+#define WINTERMUTE_VIDPLAYER_H
+
+#include "dctypes.h" // Added by ClassView
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#include "engines/wintermute/BBase.h"
+
+#define MAX_AUDIO_STREAMS 5
+#define MAX_VIDEO_STREAMS 5
+
+
+namespace WinterMute {
+
+class CVidPlayer : public CBBase {
+public:
+ bool _showSubtitle;
+ int _currentSubtitle;
+ HRESULT LoadSubtitles(char *Filename, char *SubtitleFile);
+ bool _slowRendering;
+ bool IsPlaying();
+ char *_filename;
+ HRESULT Stop();
+ HRESULT Play(TVideoPlayback Type = VID_PLAY_CENTER, int X = 0, int Y = 0, bool FreezeMusic = true);
+ uint32 _totalVideoTime;
+ uint32 _startTime;
+ //CVidRenderer *_vidRenderer;
+ //CBSoundAVI *_sound;
+ bool _soundAvailable;
+ HRESULT SetDefaults();
+ bool _playing;
+ HRESULT Display();
+ HRESULT Update();
+ HRESULT Initialize(char *inFilename, char *SubtitleFile = NULL);
+ HRESULT Cleanup();
+ CVidPlayer(CBGame *inGame);
+ virtual ~CVidPlayer();
+
+ /*PAVIFILE _aviFile;
+
+ LONG _lastSample;
+
+ PAVISTREAM _audioStream;
+ PAVISTREAM _videoStream;
+
+ LPWAVEFORMAT _audioFormat;
+
+ LPBITMAPINFO _videoFormat;
+ PGETFRAME _videoPGF;*/
+ uint32 _videoEndTime;
+
+ int _playPosX;
+ int _playPosY;
+ float _playZoom;
+
+/* LPBITMAPV4HEADER _targetFormat;
+
+ CBArray<CVidSubtitle *, CVidSubtitle *> _subtitles;*/
+};
+
+} // end of namespace WinterMute
+
+#endif
diff --git a/engines/wintermute/VidTheoraPlayer.cpp b/engines/wintermute/VidTheoraPlayer.cpp
new file mode 100644
index 0000000000..f27a27b724
--- /dev/null
+++ b/engines/wintermute/VidTheoraPlayer.cpp
@@ -0,0 +1,957 @@
+// Copyright 2009, 2010 Jan Nedoma
+//
+// This file is part of Wintermute Engine.
+//
+// Wintermute Engine is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Wintermute Engine is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with Wintermute Engine. If not, see <http://www.gnu.org/licenses/>.
+//////////////////////////////////////////////////////////////////////////
+
+
+#include "dcgf.h"
+#include "engines/wintermute/vidtheoraplayer.h"
+
+//#pragma comment(lib, "libtheora.lib")
+
+namespace WinterMute {
+
+//IMPLEMENT_PERSISTENT(CVidTheoraPlayer, false);
+
+//////////////////////////////////////////////////////////////////////////
+CVidTheoraPlayer::CVidTheoraPlayer(CBGame *inGame): CBBase(inGame) {
+ SetDefaults();
+}
+
+//////////////////////////////////////////////////////////////////////////
+void CVidTheoraPlayer::SetDefaults() {
+#if 0
+ _file = NULL;
+ _filename = NULL;
+ _startTime = 0;
+ _looping = false;
+ _sound = NULL;
+ _audiobufGranulepos = 0;
+ _freezeGame = false;
+ _currentTime = 0;
+
+ _state = THEORA_STATE_NONE;
+
+ _videoFrameReady = false;
+ _audioFrameReady = false;
+ _videobufTime = 0;
+
+ _audioBuf = NULL;
+ _audioBufFill = 0;
+ _audioBufSize = 0;
+ _playbackStarted = false;
+ _dontDropFrames = false;
+
+ _texture = NULL;
+ _alphaImage = NULL;
+ _alphaFilename = NULL;
+
+ _frameRendered = false;
+
+
+/* memset(&m_OggSyncState, 0, sizeof(ogg_sync_state));
+ memset(&m_OggPage, 0, sizeof(ogg_page));
+ memset(&m_VorbisStreamState, 0 , sizeof(ogg_stream_state));
+ memset(&m_TheoraStreamState, 0 , sizeof(ogg_stream_state));
+
+ memset(&m_TheoraInfo, 0, sizeof(theora_info));
+ memset(&m_TheoraComment, 0, sizeof(theora_comment));
+ memset(&m_TheoraState, 0, sizeof(theora_state));
+
+ memset(&m_VorbisInfo, 0, sizeof(vorbis_info));
+ memset(&m_VorbisDSPState, 0, sizeof(vorbis_dsp_state));
+ memset(&m_VorbisBlock, 0, sizeof(vorbis_block));
+ memset(&m_VorbisComment, 0, sizeof(vorbis_comment));*/
+
+ _vorbisStreams = _theoraStreams = 0;
+
+ GenLookupTables();
+
+ _seekingKeyframe = false;
+ _timeOffset = 0.0f;
+
+ _posX = _posY = 0;
+ _playbackType = VID_PLAY_CENTER;
+ _playZoom = 0.0f;
+
+ _subtitler = NULL;
+
+ _savedState = THEORA_STATE_NONE;
+ _savedPos = 0;
+ _volume = 100;
+#endif
+}
+
+//////////////////////////////////////////////////////////////////////////
+CVidTheoraPlayer::~CVidTheoraPlayer(void) {
+ Cleanup();
+
+ SAFE_DELETE_ARRAY(_filename);
+ SAFE_DELETE_ARRAY(_alphaFilename);
+ SAFE_DELETE(_texture);
+ SAFE_DELETE(_alphaImage);
+// SAFE_DELETE(_subtitler);
+}
+
+//////////////////////////////////////////////////////////////////////////
+void CVidTheoraPlayer::Cleanup() {
+#if 0
+ if (_vorbisStreams) {
+ ogg_stream_clear(&m_VorbisStreamState);
+ vorbis_block_clear(&m_VorbisBlock);
+ vorbis_dsp_clear(&m_VorbisDSPState);
+ vorbis_comment_clear(&m_VorbisComment);
+ vorbis_info_clear(&m_VorbisInfo);
+
+ _vorbisStreams = 0;
+ }
+ if (m_TheoraStreams) {
+ ogg_stream_clear(&m_TheoraStreamState);
+ theora_clear(&m_TheoraState);
+ theora_comment_clear(&m_TheoraComment);
+ theora_info_clear(&m_TheoraInfo);
+
+ m_TheoraStreams = 0;
+ }
+ ogg_sync_clear(&m_OggSyncState);
+
+
+ if (m_File) Game->m_FileManager->CloseFile(m_File);
+ m_File = NULL;
+
+ if (m_Sound) {
+ Game->m_SoundMgr->RemoveSound(m_Sound);
+ m_Sound = NULL;
+ }
+
+
+ SAFE_DELETE_ARRAY(m_AudioBuf);
+ m_AudioBufFill = 0;
+ m_AudioBufSize = 0;
+#endif
+}
+
+//////////////////////////////////////////////////////////////////////////
+/*int CVidTheoraPlayer::BufferData(ogg_sync_state *OggSyncState) {
+ if (!_file) return 0;
+
+ DWORD Size = 4096;
+ if (m_File->GetSize() - m_File->GetPos() < Size) Size = m_File->GetSize() - m_File->GetPos();
+
+ char *Buffer = ogg_sync_buffer(OggSyncState, Size);
+ m_File->Read(Buffer, Size);
+ ogg_sync_wrote(OggSyncState, Size);
+
+ return Size;
+}*/
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CVidTheoraPlayer::Initialize(char *Filename, char *SubtitleFile) {
+#if 0
+ Cleanup();
+
+ _file = Game->_fileManager->OpenFile(Filename);
+ if (!_file) return E_FAIL;
+
+ if (Filename != _filename) CBUtils::SetString(&_filename, Filename);
+
+ // start up Ogg stream synchronization layer
+ ogg_sync_init(&m_OggSyncState);
+
+ // init supporting Vorbis structures needed in header parsing
+ vorbis_comment_init(&m_VorbisComment);
+ vorbis_info_init(&m_VorbisInfo);
+
+ // init supporting Theora structures needed in header parsing
+ theora_comment_init(&m_TheoraComment);
+ theora_info_init(&m_TheoraInfo);
+
+
+
+ // Ogg file open; parse the headers
+ // Only interested in Vorbis/Theora streams
+ ogg_packet TempOggPacket;
+ bool IsDone = false;
+ while (!IsDone) {
+ int BytesRead = BufferData(&m_OggSyncState);
+ if (BytesRead == 0) break;
+
+ while (ogg_sync_pageout(&m_OggSyncState, &m_OggPage) > 0) {
+ ogg_stream_state OggStateTest;
+
+ // is this a mandated initial header? If not, stop parsing
+ if (!ogg_page_bos(&m_OggPage)) {
+ // don't leak the page; get it into the appropriate stream
+ if (m_TheoraStreams)
+ ogg_stream_pagein(&m_TheoraStreamState, &m_OggPage);
+ if (m_VorbisStreams)
+ ogg_stream_pagein(&m_VorbisStreamState, &m_OggPage);
+
+ IsDone = true;
+ break;
+ }
+
+ ogg_stream_init(&OggStateTest, ogg_page_serialno(&m_OggPage));
+ ogg_stream_pagein(&OggStateTest, &m_OggPage);
+ ogg_stream_packetout(&OggStateTest, &TempOggPacket);
+
+ // identify the codec: try theora
+ if (!m_TheoraStreams && theora_decode_header(&m_TheoraInfo, &m_TheoraComment, &TempOggPacket) >= 0) {
+ // it is theora
+ memcpy(&m_TheoraStreamState, &OggStateTest, sizeof(OggStateTest));
+ m_TheoraStreams = 1;
+ } else if (!m_VorbisStreams && vorbis_synthesis_headerin(&m_VorbisInfo, &m_VorbisComment, &TempOggPacket) >= 0) {
+ // it is vorbis
+ memcpy(&m_VorbisStreamState, &OggStateTest, sizeof(OggStateTest));
+ m_VorbisStreams = 1;
+ } else {
+ // whatever it is, we don't care about it
+ ogg_stream_clear(&OggStateTest);
+ }
+ }
+ }
+
+ // we're expecting more header packets
+ while ((m_TheoraStreams && m_TheoraStreams < 3) || (m_VorbisStreams && m_VorbisStreams < 3)) {
+ int Ret;
+
+ // look for further theora headers
+ while (m_TheoraStreams && (m_TheoraStreams < 3) && (Ret = ogg_stream_packetout(&m_TheoraStreamState, &TempOggPacket))) {
+ if (Ret < 0) {
+ Game->LOG(0, "Error parsing Theora stream headers; corrupt stream?");
+ return E_FAIL;
+ }
+ if (theora_decode_header(&m_TheoraInfo, &m_TheoraComment, &TempOggPacket)) {
+ Game->LOG(0, "Error parsing Theora stream headers; corrupt stream?");
+ return E_FAIL;
+ }
+ m_TheoraStreams++;
+ if (m_TheoraStreams == 3) break;
+ }
+
+ /* look for more vorbis header packets */
+ while (m_VorbisStreams && (m_VorbisStreams < 3) && (Ret = ogg_stream_packetout(&m_VorbisStreamState, &TempOggPacket))) {
+ if (Ret < 0) {
+ Game->LOG(0, "Error parsing Vorbis stream headers; corrupt stream?");
+ return E_FAIL;
+ }
+ if (vorbis_synthesis_headerin(&m_VorbisInfo, &m_VorbisComment, &TempOggPacket)) {
+ Game->LOG(0, "Error parsing Vorbis stream headers; corrupt stream?");
+ return E_FAIL;
+ }
+ m_VorbisStreams++;
+ if (m_VorbisStreams == 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(&m_OggSyncState, &m_OggPage) > 0) {
+ if (m_TheoraStreams)
+ ogg_stream_pagein(&m_TheoraStreamState, &m_OggPage);
+ if (m_VorbisStreams)
+ ogg_stream_pagein(&m_VorbisStreamState, &m_OggPage);
+ } else {
+ int Ret = BufferData(&m_OggSyncState); // someone needs more data
+ if (Ret == 0) {
+ Game->LOG(0, "End of file while searching for codec headers");
+ return E_FAIL;
+ }
+ }
+ }
+
+
+
+ // and now we have it all. initialize decoders
+ if (m_TheoraStreams) {
+ theora_decode_init(&m_TheoraState, &m_TheoraInfo);
+ } else {
+ // tear down the partial theora setup
+ theora_info_clear(&m_TheoraInfo);
+ theora_comment_clear(&m_TheoraComment);
+ }
+
+ if (m_VorbisStreams) {
+ vorbis_synthesis_init(&m_VorbisDSPState, &m_VorbisInfo);
+ vorbis_block_init(&m_VorbisDSPState, &m_VorbisBlock);
+
+ } else {
+ // tear down the partial vorbis setup
+ vorbis_info_clear(&m_VorbisInfo);
+ vorbis_comment_clear(&m_VorbisComment);
+ }
+
+ HRESULT Res = S_OK;
+
+ // create sound buffer
+ if (m_VorbisStreams && Game->m_SoundMgr->m_SoundAvailable) {
+ m_Sound = new CBSoundTheora(Game);
+ Game->m_SoundMgr->AddSound(m_Sound);
+ if (FAILED(Res = m_Sound->InitializeBuffer(this))) {
+ Game->m_SoundMgr->RemoveSound(m_Sound);
+ m_Sound = NULL;
+ Game->LOG(Res, "Error initializing sound buffer for Theora file '%s'", Filename);
+ } else {
+ SAFE_DELETE_ARRAY(m_AudioBuf);
+ m_AudioBufSize = m_Sound->m_StreamBlockSize;
+ m_AudioBuf = new ogg_int16_t[m_AudioBufSize];
+ }
+ }
+
+ // create texture
+ if (m_TheoraStreams && !m_Texture) {
+ if (Game->m_UseD3D)
+ m_Texture = new CBSurfaceD3D(Game);
+ else
+ m_Texture = new CBSurfaceDD(Game);
+
+ if (!m_Texture || FAILED(Res = m_Texture->Create(m_TheoraInfo.width, m_TheoraInfo.height))) {
+ SAFE_DELETE(m_Texture);
+ }
+ }
+
+
+ if (!m_Subtitler) m_Subtitler = new CVidSubtitler(Game);
+ if (m_Subtitler && Game->m_VideoSubtitles) m_Subtitler->LoadSubtitles(Filename, SubtitleFile);
+
+ return Res;
+#endif
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CVidTheoraPlayer::ResetStream() {
+#if 0
+ if (_sound) _sound->Stop();
+
+ m_TimeOffset = 0.0f;
+ Initialize(m_Filename);
+ Play(m_PlaybackType, m_PosX, m_PosY, false, false, m_Looping, 0, m_PlayZoom);
+#endif
+ return S_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CVidTheoraPlayer::Play(TVideoPlayback Type, int X, int Y, bool FreezeGame, bool FreezeMusic, bool Looping, uint32 StartTime, float ForceZoom, int Volume) {
+#if 0
+ if (ForceZoom < 0.0f) ForceZoom = 100.0f;
+ if (Volume < 0) m_Volume = Game->m_SoundMgr->GetVolumePercent(SOUND_SFX);
+ else m_Volume = Volume;
+
+ m_FreezeGame = FreezeGame;
+ if (!m_PlaybackStarted && m_FreezeGame) Game->Freeze(FreezeMusic);
+
+ m_PlaybackStarted = false;
+ m_State = THEORA_STATE_PLAYING;
+
+ m_Looping = Looping;
+ m_PlaybackType = Type;
+
+ float Width, Height;
+ if (m_TheoraStreams) {
+ Width = (float)m_TheoraInfo.width;
+ Height = (float)m_TheoraInfo.height;
+ } else {
+ Width = (float)Game->m_Renderer->m_Width;
+ Height = (float)Game->m_Renderer->m_Height;
+ }
+
+ switch (Type) {
+ case VID_PLAY_POS:
+ m_PlayZoom = ForceZoom;
+ m_PosX = X;
+ m_PosY = Y;
+ break;
+
+ case VID_PLAY_STRETCH: {
+ float ZoomX = (float)((float)Game->m_Renderer->m_Width / Width * 100);
+ float ZoomY = (float)((float)Game->m_Renderer->m_Height / Height * 100);
+ m_PlayZoom = min(ZoomX, ZoomY);
+ m_PosX = (Game->m_Renderer->m_Width - Width * (m_PlayZoom / 100)) / 2;
+ m_PosY = (Game->m_Renderer->m_Height - Height * (m_PlayZoom / 100)) / 2;
+ }
+ break;
+
+ case VID_PLAY_CENTER:
+ m_PlayZoom = 100.0f;
+ m_PosX = (Game->m_Renderer->m_Width - Width) / 2;
+ m_PosY = (Game->m_Renderer->m_Height - Height) / 2;
+ break;
+ }
+
+
+ if (StartTime) SeekToTime(StartTime);
+
+ Update();
+#endif
+ return S_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CVidTheoraPlayer::Stop() {
+#if 0
+ if (m_Sound) m_Sound->Stop();
+ m_State = THEORA_STATE_FINISHED;
+ if (m_FreezeGame) Game->Unfreeze();
+#endif
+ return S_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CVidTheoraPlayer::Update() {
+#if 0
+ m_CurrentTime = m_FreezeGame ? Game->m_LiveTimer : Game->m_Timer;
+
+ if (!IsPlaying()) return S_OK;
+
+ if (m_PlaybackStarted && m_Sound && !m_Sound->IsPlaying()) return S_OK;
+
+ if (m_PlaybackStarted && !m_FreezeGame && Game->m_State == GAME_FROZEN) return S_OK;
+
+ int Counter = 0;
+ while (true) {
+ if (m_Sound) DecodeVorbis();
+ else m_AudioFrameReady = true;
+
+ if (m_Texture) DecodeTheora();
+ else m_VideoFrameReady = true;
+
+ if ((!m_Sound || !m_AudioFrameReady) && (!m_Texture || !m_VideoFrameReady) && m_File->IsEOF()) {
+ // end playback
+ if (!m_Looping) {
+ m_State = THEORA_STATE_FINISHED;
+ if (m_Sound) m_Sound->Stop();
+ if (m_FreezeGame) Game->Unfreeze();
+ break;
+ } else {
+ ResetStream();
+ return S_OK;
+ }
+ }
+
+
+ if (!m_VideoFrameReady || !m_AudioFrameReady) {
+ Counter++;
+ if (StreamInData() == 0) break;
+ } else break;
+ }
+
+
+ // If playback has begun, top audio buffer off immediately.
+ //if(m_Sound) WriteAudio();
+
+ // are we at or past time for this video frame?
+ if (m_PlaybackStarted && m_VideoFrameReady && (!m_FrameRendered || m_VideobufTime <= GetMovieTime())) {
+ //Game->LOG(0, "%f %f", m_VideobufTime, GetMovieTime());
+ if (m_Texture) WriteVideo();
+ m_VideoFrameReady = false;
+
+ if (m_SavedState == THEORA_STATE_PAUSED) {
+ Pause();
+ m_SavedState = THEORA_STATE_NONE;
+ }
+ }
+
+ // if our buffers either don't exist or are ready to go,
+ // we can begin playback
+ bool StartNow = false;
+ if ((!m_TheoraStreams || m_VideoFrameReady) &&
+ (!m_VorbisStreams || m_AudioFrameReady)) StartNow = true;
+ // same if we've run out of input
+ if (m_File->IsEOF()) StartNow = true;
+
+
+ if (m_Sound) WriteAudio();
+
+
+ if (!m_PlaybackStarted && StartNow && !m_SeekingKeyframe) {
+ //m_StartTime = timeGetTime();
+ m_StartTime = m_CurrentTime;
+ if (m_Sound) {
+ m_Sound->SetPrivateVolume(m_Volume);
+ m_Sound->Play();
+ }
+ m_PlaybackStarted = true;
+ }
+
+ if (m_Subtitler && Game->m_VideoSubtitles) m_Subtitler->Update(GetMovieFrame());
+#endif
+ return S_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+/*int CVidTheoraPlayer::StreamInData() {
+#if 0
+ // no data yet for somebody. Grab another page
+ int BytesRead = BufferData(&m_OggSyncState);
+ while (ogg_sync_pageout(&m_OggSyncState, &m_OggPage) > 0) {
+ if (m_TheoraStreams)
+ ogg_stream_pagein(&m_TheoraStreamState, &m_OggPage);
+ if (m_VorbisStreams)
+ ogg_stream_pagein(&m_VorbisStreamState, &m_OggPage);
+ }
+ return BytesRead;
+#endif
+}*/
+
+//////////////////////////////////////////////////////////////////////////
+void CVidTheoraPlayer::DecodeVorbis() {
+#if 0
+ if (!m_Sound) return;
+
+ while (m_VorbisStreams && !m_AudioFrameReady) {
+ int ret;
+ float **pcm;
+
+ // if there's pending, decoded audio, grab it
+ if ((ret = vorbis_synthesis_pcmout(&m_VorbisDSPState, &pcm)) > 0 && !m_SeekingKeyframe) {
+ int count = m_AudioBufFill / 2;
+ int maxsamples = (m_AudioBufSize - m_AudioBufFill) / 2 / m_VorbisInfo.channels;
+
+ int i;
+ for (i = 0; i < ret && i < maxsamples; i++)
+ for (int j = 0; j < m_VorbisInfo.channels; j++) {
+ int val = (int)(pcm[j][i] * 32767.f);
+ if (val > 32767) val = 32767;
+ if (val < -32768) val = -32768;
+
+ m_AudioBuf[count++] = val;
+ }
+ vorbis_synthesis_read(&m_VorbisDSPState, i);
+ m_AudioBufFill += i * m_VorbisInfo.channels * 2;
+ if (m_AudioBufFill == m_AudioBufSize) m_AudioFrameReady = true;
+ if (m_VorbisDSPState.granulepos >= 0)
+ m_AudiobufGranulepos = m_VorbisDSPState.granulepos - ret + i;
+ else
+ m_AudiobufGranulepos += i;
+ } else {
+ ogg_packet opVorbis;
+
+ //no pending audio; is there a pending packet to decode?
+ if (ogg_stream_packetout(&m_VorbisStreamState, &opVorbis) > 0) {
+ //test for success!
+ if (vorbis_synthesis(&m_VorbisBlock, &opVorbis) == 0)
+ vorbis_synthesis_blockin(&m_VorbisDSPState, &m_VorbisBlock);
+ } else { //we need more data; break out to suck in another page
+ break;
+ }
+ }
+ } // while
+#endif
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CVidTheoraPlayer::DecodeTheora() {
+#if 0
+ ogg_packet opTheora;
+
+ while (m_TheoraStreams && !m_VideoFrameReady) {
+ // theora is one in, one out...
+ if (ogg_stream_packetout(&m_TheoraStreamState, &opTheora) > 0) {
+ theora_decode_packetin(&m_TheoraState, &opTheora);
+ m_VideobufTime = theora_granule_time(&m_TheoraState, m_TheoraState.granulepos);
+
+ if (m_SeekingKeyframe) {
+ if (!theora_packet_iskeyframe(&opTheora)) continue;
+ else {
+ m_SeekingKeyframe = false;
+ m_TimeOffset = m_VideobufTime;
+ }
+ }
+
+ if (m_VideobufTime >= GetMovieTime() || m_DontDropFrames) m_VideoFrameReady = true;
+ } else {
+ break;
+ }
+ }
+#endif
+}
+
+//////////////////////////////////////////////////////////////////////////
+float CVidTheoraPlayer::GetMovieTime() {
+#if 0
+ if (!m_PlaybackStarted) return 0.0f;
+ else if (m_Sound) return (float)(m_Sound->GetPosition()) / 1000.0f + m_TimeOffset;
+ else return (float)(m_CurrentTime - m_StartTime) / 1000.0f + m_TimeOffset;
+#endif
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+int CVidTheoraPlayer::GetMovieFrame() {
+#if 0
+ if (!m_TheoraStreams) return 0;
+ float Time = GetMovieTime();
+
+ return Time / ((double)m_TheoraInfo.fps_denominator / m_TheoraInfo.fps_numerator);
+#endif
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CVidTheoraPlayer::WriteAudio() {
+#if 0
+ if (m_AudioFrameReady) {
+ if (m_Sound->WriteBlock((BYTE *)m_AudioBuf, m_AudioBufSize)) {
+ m_AudioBufFill = 0;
+ m_AudioFrameReady = false;
+ }
+ } else if (m_File->IsEOF()) {
+ memset(m_AudioBuf, 0, m_AudioBufSize);
+ m_Sound->WriteBlock((BYTE *)m_AudioBuf, m_AudioBufSize);
+ }
+#endif
+ return S_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CVidTheoraPlayer::WriteVideo() {
+#if 0
+ if (!m_Texture) return E_FAIL;
+
+ yuv_buffer yuv;
+ theora_decode_YUVout(&m_TheoraState, &yuv);
+
+ m_Texture->StartPixelOp();
+ RenderFrame(m_Texture, &yuv);
+ m_Texture->EndPixelOp();
+#endif
+ return S_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CVidTheoraPlayer::Display(uint32 Alpha) {
+
+ RECT rc;
+ HRESULT Res;
+#if 0
+ if (m_Texture) {
+ SetRect(&rc, 0, 0, m_Texture->GetWidth(), m_Texture->GetHeight());
+ if (m_PlayZoom == 100.0f) Res = m_Texture->DisplayTrans(m_PosX, m_PosY, rc, Alpha);
+ else Res = m_Texture->DisplayTransZoom(m_PosX, m_PosY, rc, m_PlayZoom, m_PlayZoom, Alpha);
+ } else Res = E_FAIL;
+
+ if (m_Subtitler && Game->m_VideoSubtitles) m_Subtitler->Display();
+#endif
+ return Res;
+}
+
+//////////////////////////////////////////////////////////////////////////
+void CVidTheoraPlayer::GenLookupTables() {
+ //used to bring the table into the high side (scale up) so we
+ //can maintain high precision and not use floats (FIXED POINT)
+ int scale = 1L << 13;
+ int temp;
+
+ for (unsigned int i = 0; i < 256; i++) {
+ temp = i - 128;
+
+ _yTable[i] = (unsigned int)((1.164 * scale + 0.5) * (i - 16)); //Calc Y component
+
+ _rVTable[i] = (unsigned int)((1.596 * scale + 0.5) * temp); //Calc R component
+
+ _gUTable[i] = (unsigned int)((0.391 * scale + 0.5) * temp); //Calc G u & v components
+ _gVTable[i] = (unsigned int)((0.813 * scale + 0.5) * temp);
+
+ _bUTable[i] = (unsigned int)((2.018 * scale + 0.5) * temp); //Calc B component
+ }
+}
+
+#define CLIP_RGB_COLOR( rgb_color_test ) max( min(rgb_color_test, 255), 0 )
+//////////////////////////////////////////////////////////////////////////
+#if 0
+HRESULT CVidTheoraPlayer::RenderFrame(CBSurface *Texture, yuv_buffer *yuv) {
+ //Convert 4:2:0 YUV YCrCb to an RGB24 Bitmap
+ //convenient pointers
+ int TargetX1 = 0;
+ int TargetX2 = 1;
+ int TargetY1 = 0;
+ int TargetY2 = 1;
+
+ unsigned char *ySrc = (unsigned char *)yuv->y;
+ unsigned char *uSrc = (unsigned char *)yuv->u;
+ unsigned char *vSrc = (unsigned char *)yuv->v;
+ unsigned char *ySrc2 = ySrc + yuv->y_stride;
+
+ //Calculate buffer offset
+ int yOff = (yuv->y_stride * 2) - yuv->y_width;
+
+
+ //Check if upside down, if so, reverse buffers and offsets
+ if (yuv->y_height < 0) {
+ yuv->y_height = -yuv->y_height;
+ ySrc += (yuv->y_height - 1) * yuv->y_stride;
+
+ uSrc += ((yuv->y_height / 2) - 1) * yuv->uv_stride;
+ vSrc += ((yuv->y_height / 2) - 1) * yuv->uv_stride;
+
+ ySrc2 = ySrc - yuv->y_stride;
+ yOff = -yuv->y_width - (yuv->y_stride * 2);
+
+ yuv->uv_stride = -yuv->uv_stride;
+ }
+
+ //Cut width and height in half (uv field is only half y field)
+ yuv->y_height = yuv->y_height >> 1;
+ yuv->y_width = yuv->y_width >> 1;
+
+ //Convientient temp vars
+ signed int r, g, b, u, v, bU, gUV, rV, rgbY;
+ int x;
+
+ //Loop does four blocks per iteration (2 rows, 2 pixels at a time)
+ for (int y = yuv->y_height; y > 0; --y) {
+ for (x = 0; x < yuv->y_width; ++x) {
+ //Get uv pointers for row
+ u = uSrc[x];
+ v = vSrc[x];
+
+ //get corresponding lookup values
+ rgbY = m_YTable[*ySrc];
+ rV = m_RVTable[v];
+ gUV = m_GUTable[u] + m_GVTable[v];
+ bU = m_BUTable[u];
+ ++ySrc;
+
+ //scale down - brings are values back into the 8 bits of a byte
+ r = CLIP_RGB_COLOR((rgbY + rV) >> 13);
+ g = CLIP_RGB_COLOR((rgbY - gUV) >> 13);
+ b = CLIP_RGB_COLOR((rgbY + bU) >> 13);
+ Texture->PutPixel(TargetX1, TargetY1, r, g, b, GetAlphaAt(TargetX1, TargetY1));
+
+ //And repeat for other pixels (note, y is unique for each
+ //pixel, while uv are not)
+ rgbY = m_YTable[*ySrc];
+ r = CLIP_RGB_COLOR((rgbY + rV) >> 13);
+ g = CLIP_RGB_COLOR((rgbY - gUV) >> 13);
+ b = CLIP_RGB_COLOR((rgbY + bU) >> 13);
+ Texture->PutPixel(TargetX2, TargetY1, r, g, b, GetAlphaAt(TargetX2, TargetY1));
+ ++ySrc;
+
+ rgbY = m_YTable[*ySrc2];
+ r = CLIP_RGB_COLOR((rgbY + rV) >> 13);
+ g = CLIP_RGB_COLOR((rgbY - gUV) >> 13);
+ b = CLIP_RGB_COLOR((rgbY + bU) >> 13);
+ Texture->PutPixel(TargetX1, TargetY2, r, g, b, GetAlphaAt(TargetX1, TargetY2));
+ ++ySrc2;
+
+ rgbY = m_YTable[*ySrc2];
+ r = CLIP_RGB_COLOR((rgbY + rV) >> 13);
+ g = CLIP_RGB_COLOR((rgbY - gUV) >> 13);
+ b = CLIP_RGB_COLOR((rgbY + bU) >> 13);
+ Texture->PutPixel(TargetX2, TargetY2, r, g, b, GetAlphaAt(TargetX2, TargetY2));
+ ++ySrc2;
+
+ /*
+ Texture->PutPixel(TargetX1, TargetY1, 255, 0, 0, GetAlphaAt(TargetX1, TargetY1));
+ Texture->PutPixel(TargetX2, TargetY1, 255, 0, 0, GetAlphaAt(TargetX2, TargetY1));
+ Texture->PutPixel(TargetX1, TargetY2, 255, 0, 0, GetAlphaAt(TargetX1, TargetY2));
+ Texture->PutPixel(TargetX2, TargetY2, 255, 0, 0, GetAlphaAt(TargetX2, TargetY2));
+ */
+
+
+ //Advance inner loop offsets
+ TargetX1 += 2;
+ TargetX2 += 2;
+ } // end for x
+
+ //Advance destination pointers by offsets
+ TargetX1 = 0;
+ TargetX2 = 1;
+ TargetY1 += 2;
+ TargetY2 += 2;
+
+ ySrc += yOff;
+ ySrc2 += yOff;
+ uSrc += yuv->uv_stride;
+ vSrc += yuv->uv_stride;
+ } //end for y
+
+ m_FrameRendered = true;
+
+ return S_OK;
+}
+#endif
+//////////////////////////////////////////////////////////////////////////
+HRESULT CVidTheoraPlayer::SetAlphaImage(const char *Filename) {
+#if 0
+ SAFE_DELETE(m_AlphaImage);
+ m_AlphaImage = new CBImage(Game);
+ if (!m_AlphaImage || FAILED(m_AlphaImage->LoadFile(Filename))) {
+ SAFE_DELETE(m_AlphaImage);
+ SAFE_DELETE_ARRAY(m_AlphaFilename);
+ return E_FAIL;
+ }
+ if (m_AlphaFilename != Filename) CBUtils::SetString(&m_AlphaFilename, Filename);
+ m_AlphaImage->Convert(IMG_TRUECOLOR);
+#endif
+ return S_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+byte CVidTheoraPlayer::GetAlphaAt(int X, int Y) {
+#if 0
+ if (_alphaImage) return _alphaImage->GetAlphaAt(X, Y);
+ else return 0xFF;
+#endif
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+inline int intlog(int num) {
+ int r = 0;
+ while (num > 0) {
+ num = num / 2;
+ r = r + 1;
+ }
+
+ return r;
+}
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CVidTheoraPlayer::SeekToTime(uint32 Time) {
+#if 0
+ if (!m_TheoraStreams) return E_FAIL;
+
+
+ float TargetTime = Time / 1000.0f;
+
+
+ ogg_page page;
+ int read = 1;
+ ogg_int64_t gran;
+ float movieLength = 0;
+ DWORD LastPos = 0;
+
+ int keyframe_granule_shift = intlog(m_TheoraInfo.keyframe_frequency_force - 1);
+
+ while (!m_File->IsEOF() && read != 0) {
+ read = BufferData(&m_OggSyncState);
+
+ while (ogg_sync_pageout(&m_OggSyncState, &page) > 0) {
+ int serno = ogg_page_serialno(&page);
+ //This is theora stream we were searching for
+ if (m_TheoraStreamState.serialno == serno) {
+ //Calculate a rough time estimate
+ gran = ogg_page_granulepos(&page);
+ if (gran >= 0) {
+ ogg_int64_t iframe = gran >> keyframe_granule_shift;
+ ogg_int64_t pframe = gran - (iframe << keyframe_granule_shift);
+ movieLength = (iframe + pframe) *
+ ((double)m_TheoraInfo.fps_denominator / m_TheoraInfo.fps_numerator);
+
+ if (movieLength >= TargetTime) {
+ m_TimeOffset = movieLength;
+ //m_TimeOffset = TargetTime;
+ //m_File->Seek(LastPos);
+
+ goto finish;
+ }
+ LastPos = m_File->GetPos();
+ }
+ }
+ }
+ }
+
+finish:
+ ogg_sync_reset(&m_OggSyncState);
+
+ ogg_stream_reset(&m_TheoraStreamState);
+ ogg_stream_reset(&m_VorbisStreamState);
+
+ theora_clear(&m_TheoraState);
+ theora_decode_init(&m_TheoraState, &m_TheoraInfo);
+ vorbis_synthesis_restart(&m_VorbisDSPState);
+
+ m_SeekingKeyframe = true;
+
+ //theora_packet_iskeyframe
+
+#endif
+ return S_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CVidTheoraPlayer::Pause() {
+#if 0
+ if (m_State == THEORA_STATE_PLAYING) {
+ m_State = THEORA_STATE_PAUSED;
+ if (m_Sound) m_Sound->Pause();
+ return S_OK;
+ } else return E_FAIL;
+#endif
+}
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CVidTheoraPlayer::Resume() {
+#if 0
+ if (_state == THEORA_STATE_PAUSED) {
+ _state = THEORA_STATE_PLAYING;
+ if (_sound) _sound->Resume();
+ return S_OK;
+ } else return E_FAIL;
+#endif
+}
+
+//////////////////////////////////////////////////////////////////////////
+/*HRESULT CVidTheoraPlayer::Persist(CBPersistMgr *PersistMgr) {
+ //CBBase::Persist(PersistMgr);
+#if 0
+ if (PersistMgr->m_Saving) {
+ m_SavedPos = GetMovieTime() * 1000;
+ m_SavedState = m_State;
+ } else {
+ SetDefaults();
+ }
+
+ PersistMgr->Transfer(TMEMBER(Game));
+ PersistMgr->Transfer(TMEMBER(m_SavedPos));
+ PersistMgr->Transfer(TMEMBER(m_SavedState));
+ PersistMgr->Transfer(TMEMBER(m_Filename));
+ PersistMgr->Transfer(TMEMBER(m_AlphaFilename));
+ PersistMgr->Transfer(TMEMBER(m_PosX));
+ PersistMgr->Transfer(TMEMBER(m_PosY));
+ PersistMgr->Transfer(TMEMBER(m_PlayZoom));
+ PersistMgr->Transfer(TMEMBER_INT(m_PlaybackType));
+ PersistMgr->Transfer(TMEMBER(m_Looping));
+
+ if (PersistMgr->CheckVersion(1, 7, 3)) {
+ PersistMgr->Transfer(TMEMBER(m_Volume));
+ } else {
+ m_Volume = 100;
+ }
+#endif
+ return S_OK;
+}
+*/
+//////////////////////////////////////////////////////////////////////////
+HRESULT CVidTheoraPlayer::InitializeSimple() {
+#if 0
+ if (SUCCEEDED(Initialize(m_Filename))) {
+ if (m_AlphaFilename) SetAlphaImage(m_AlphaFilename);
+ Play(m_PlaybackType, m_PosX, m_PosY, false, false, m_Looping, m_SavedPos, m_PlayZoom);
+ } else m_State = THEORA_STATE_FINISHED;
+#endif
+ return S_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+CBSurface *CVidTheoraPlayer::GetTexture() {
+ return _texture;
+}
+
+} // end of namespace WinterMute
diff --git a/engines/wintermute/VidTheoraPlayer.h b/engines/wintermute/VidTheoraPlayer.h
new file mode 100644
index 0000000000..6a32c307ad
--- /dev/null
+++ b/engines/wintermute/VidTheoraPlayer.h
@@ -0,0 +1,175 @@
+// Copyright 2009, 2010 Jan Nedoma
+//
+// This file is part of Wintermute Engine.
+//
+// Wintermute Engine is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Wintermute Engine is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with Wintermute Engine. If not, see <http://www.gnu.org/licenses/>.
+//////////////////////////////////////////////////////////////////////////
+
+
+#ifndef WINTERMUTE_VIDTHEORAPLAYER_H
+#define WINTERMUTE_VIDTHEORAPLAYER_H
+
+#include "engines/wintermute/BBase.h"
+#include "engines/wintermute/BFile.h"
+#include "engines/wintermute/BSurface.h"
+#include "engines/wintermute/BImage.h"
+//#include <theora/theora.h>
+
+namespace WinterMute {
+
+class CVidTheoraPlayer : public CBBase {
+private:
+ enum { THEORA_STATE_NONE = 0, THEORA_STATE_PLAYING = 1, THEORA_STATE_PAUSED = 2, THEORA_STATE_FINISHED = 3 };
+public:
+ //DECLARE_PERSISTENT(CVidTheoraPlayer, CBBase);
+
+ CVidTheoraPlayer(CBGame *inGame);
+ virtual ~CVidTheoraPlayer(void);
+
+ // Vorbis/Theora structs
+ /*ogg_sync_state m_OggSyncState;
+ ogg_page m_OggPage;
+ ogg_stream_state m_VorbisStreamState;
+ ogg_stream_state m_TheoraStreamState;
+
+ theora_info m_TheoraInfo;
+ theora_comment m_TheoraComment;
+ theora_state m_TheoraState;
+
+ vorbis_info m_VorbisInfo;
+ vorbis_dsp_state m_VorbisDSPState;
+ vorbis_block m_VorbisBlock;
+ vorbis_comment m_VorbisComment;*/
+
+ int _theoraStreams;
+ int _vorbisStreams;
+
+ //ogg_int64_t m_AudiobufGranulepos; //time position of last sample
+
+
+ // external objects
+ CBFile *_file;
+ char *_filename;
+
+ //CBSoundTheora *_sound;
+ //ogg_int16_t *_audioBuf;
+ int _audioBufSize;
+ int _audioBufFill;
+
+ CBSurface *_texture;
+ //CVidSubtitler *_subtitler;
+
+ // control methods
+ HRESULT Initialize(char *Filename, char *SubtitleFile = NULL);
+ HRESULT InitializeSimple();
+ HRESULT Update();
+ HRESULT Play(TVideoPlayback Type = VID_PLAY_CENTER, int X = 0, int Y = 0, bool FreezeGame = false, bool FreezeMusic = true, bool Looping = false, uint32 StartTime = 0, float ForceZoom = -1.0f, int Volume = -1);
+ HRESULT Stop();
+ HRESULT Display(uint32 Alpha = 0xFFFFFFFF);
+ //HRESULT RenderFrame(CBSurface *Texture, yuv_buffer *yuv);
+
+ HRESULT Pause();
+ HRESULT Resume();
+
+ bool IsPlaying() {
+ return _state == THEORA_STATE_PLAYING;
+ };
+ bool IsFinished() {
+ return _state == THEORA_STATE_FINISHED;
+ };
+ bool IsPaused() {
+ return _state == THEORA_STATE_PAUSED;
+ };
+
+ float GetMovieTime();
+ int GetMovieFrame();
+
+ CBSurface *GetTexture();
+
+ int _state;
+ uint32 _startTime;
+
+ int _savedState;
+ uint32 _savedPos;
+
+
+ // alpha related
+ CBImage *_alphaImage;
+ char *_alphaFilename;
+ HRESULT SetAlphaImage(const char *Filename);
+ __inline byte GetAlphaAt(int X, int Y);
+
+ HRESULT SeekToTime(uint32 Time);
+
+
+ void Cleanup();
+ HRESULT ResetStream();
+
+ // video properties
+ TVideoPlayback m_PlaybackType;
+ int _posX;
+ int _posY;
+ float _playZoom;
+ int _volume;
+
+ bool _looping;
+ bool _dontDropFrames;
+ bool _freezeGame;
+ uint32 _currentTime;
+
+
+private:
+ // data streaming
+ //int BufferData(ogg_sync_state *OggSyncState);
+ //int StreamInData();
+
+
+ // lookup tables
+ unsigned int _yTable[256];
+ unsigned int _bUTable[256];
+ unsigned int _gUTable[256];
+ unsigned int _gVTable[256];
+ unsigned int _rVTable[256];
+
+ void GenLookupTables();
+
+
+ // seeking support
+ bool _seekingKeyframe;
+ float _timeOffset;
+
+ bool _frameRendered;
+
+
+ // decoding
+ void DecodeVorbis();
+ void DecodeTheora();
+
+ bool _audioFrameReady;
+ bool _videoFrameReady;
+ float _videobufTime;
+
+ HRESULT WriteAudio();
+ HRESULT WriteVideo();
+
+ bool _playbackStarted;
+
+ // helpers
+ void SetDefaults();
+
+};
+
+} // end of namespace WinterMute
+
+#endif
diff --git a/engines/wintermute/module.mk b/engines/wintermute/module.mk
index 6faab5d506..70acdb5dec 100644
--- a/engines/wintermute/module.mk
+++ b/engines/wintermute/module.mk
@@ -92,6 +92,8 @@ MODULE_OBJS := \
crc.o \
detection.o \
FontGlyphCache.o \
+ graphics/transparentSurface.o \
+ graphics/tga.o \
MathUtil.o \
Matrix4.o \
PathUtil.o \
@@ -116,6 +118,8 @@ MODULE_OBJS := \
UIWindow.o \
utils.o \
Vector2.o \
+ VidPlayer.o \
+ VidTheoraPlayer.o \
wintermute.o
MODULE_DIRS += \