diff options
author | Einar Johan Trøan Sømåen | 2012-06-01 02:34:32 +0200 |
---|---|---|
committer | Einar Johan Trøan Sømåen | 2012-06-02 13:07:22 +0200 |
commit | 2f02bec21fcbffc18964443c2f59a59ebe50f724 (patch) | |
tree | cff982bd9747b9befa6936581554c07bd23c0e1f /engines/wintermute | |
parent | 0cee40306710b0ddae57e962797fd8d40299558b (diff) | |
download | scummvm-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.cpp | 74 | ||||
-rw-r--r-- | engines/wintermute/BGame.h | 3 | ||||
-rw-r--r-- | engines/wintermute/VidPlayer.cpp | 467 | ||||
-rw-r--r-- | engines/wintermute/VidPlayer.h | 93 | ||||
-rw-r--r-- | engines/wintermute/VidTheoraPlayer.cpp | 957 | ||||
-rw-r--r-- | engines/wintermute/VidTheoraPlayer.h | 175 | ||||
-rw-r--r-- | engines/wintermute/module.mk | 4 |
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 += \ |