mirror of
https://github.com/libretro/scummvm.git
synced 2025-02-03 17:33:05 +00:00
WINTERMUTE: Add in more of the missing Video-pieces, also, copy over the TheoraPlayer from SWORD25
This commit is contained in:
parent
74ff79e90b
commit
cafdcd1c8a
@ -57,6 +57,8 @@
|
||||
#include "engines/wintermute/Base/BSprite.h"
|
||||
#include "engines/wintermute/Base/BFileManager.h"
|
||||
#include "engines/wintermute/utils/utils.h"
|
||||
#include "engines/wintermute/video/VidPlayer.h"
|
||||
#include "engines/wintermute/video/VidTheoraPlayer.h"
|
||||
#include "common/str.h"
|
||||
|
||||
namespace WinterMute {
|
||||
@ -1689,40 +1691,58 @@ HRESULT CAdGame::DisplayContent(bool Update, bool DisplayAll) {
|
||||
// fill black
|
||||
_renderer->Fill(0, 0, 0);
|
||||
if (!_editorMode) _renderer->SetScreenViewport();
|
||||
|
||||
// process scripts
|
||||
if (Update) _scEngine->Tick();
|
||||
|
||||
POINT p;
|
||||
GetMousePos(&p);
|
||||
|
||||
_scene->Update();
|
||||
_scene->Display();
|
||||
|
||||
|
||||
// display in-game windows
|
||||
DisplayWindows(true);
|
||||
if (_inventoryBox) _inventoryBox->Display();
|
||||
if (_stateEx == GAME_WAITING_RESPONSE) _responseBox->Display();
|
||||
if (_indicatorDisplay) DisplayIndicator();
|
||||
|
||||
|
||||
if (Update || DisplayAll) {
|
||||
// display normal windows
|
||||
DisplayWindows(false);
|
||||
|
||||
SetActiveObject(Game->_renderer->GetObjectAt(p.x, p.y));
|
||||
|
||||
// textual info
|
||||
DisplaySentences(_state == GAME_FROZEN);
|
||||
|
||||
ShowCursor();
|
||||
|
||||
if (_fader) _fader->Display();
|
||||
_transMgr->Update();
|
||||
|
||||
// playing exclusive video?
|
||||
if(_videoPlayer->isPlaying())
|
||||
{
|
||||
if(Update) _videoPlayer->update();
|
||||
_videoPlayer->display();
|
||||
}
|
||||
else if(_theoraPlayer)
|
||||
{
|
||||
if(_theoraPlayer->isPlaying()) {
|
||||
if(Update) _theoraPlayer->update();
|
||||
_theoraPlayer->display();
|
||||
}
|
||||
if(_theoraPlayer->IsFinished()) {
|
||||
delete _theoraPlayer;
|
||||
_theoraPlayer = NULL;
|
||||
}
|
||||
} else {
|
||||
|
||||
// process scripts
|
||||
if (Update) _scEngine->Tick();
|
||||
|
||||
POINT p;
|
||||
GetMousePos(&p);
|
||||
|
||||
_scene->Update();
|
||||
_scene->Display();
|
||||
|
||||
|
||||
// display in-game windows
|
||||
DisplayWindows(true);
|
||||
if (_inventoryBox) _inventoryBox->Display();
|
||||
if (_stateEx == GAME_WAITING_RESPONSE) _responseBox->Display();
|
||||
if (_indicatorDisplay) DisplayIndicator();
|
||||
|
||||
|
||||
if (Update || DisplayAll) {
|
||||
// display normal windows
|
||||
DisplayWindows(false);
|
||||
|
||||
SetActiveObject(Game->_renderer->GetObjectAt(p.x, p.y));
|
||||
|
||||
// textual info
|
||||
DisplaySentences(_state == GAME_FROZEN);
|
||||
|
||||
ShowCursor();
|
||||
|
||||
if (_fader) _fader->Display();
|
||||
_transMgr->Update();
|
||||
}
|
||||
|
||||
}
|
||||
if (_loadingIcon) {
|
||||
_loadingIcon->Display(_loadingIconX, _loadingIconY);
|
||||
if (!_loadingIconPersistent) {
|
||||
|
@ -67,6 +67,8 @@
|
||||
#include "engines/wintermute/Base/scriptables/SXMath.h"
|
||||
#include "engines/wintermute/Base/scriptables/SXStore.h"
|
||||
#include "engines/wintermute/Base/scriptables/SXString.h"
|
||||
#include "engines/wintermute/video/VidPlayer.h"
|
||||
#include "engines/wintermute/video/VidTheoraPlayer.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/util.h"
|
||||
#include "common/keyboard.h"
|
||||
@ -115,6 +117,9 @@ CBGame::CBGame(): CBObject(this) {
|
||||
_systemFont = NULL;
|
||||
_videoFont = NULL;
|
||||
|
||||
_videoPlayer = NULL;
|
||||
_theoraPlayer = NULL;
|
||||
|
||||
_mainObject = NULL;
|
||||
_activeObject = NULL;
|
||||
|
||||
@ -304,6 +309,8 @@ CBGame::~CBGame() {
|
||||
delete _scEngine;
|
||||
delete _fontStorage;
|
||||
delete _surfaceStorage;
|
||||
delete _videoPlayer;
|
||||
delete _theoraPlayer;
|
||||
delete _soundMgr;
|
||||
delete _debugMgr;
|
||||
//SAFE_DELETE(_keyboardState);
|
||||
@ -326,6 +333,8 @@ CBGame::~CBGame() {
|
||||
_scEngine = NULL;
|
||||
_fontStorage = NULL;
|
||||
_surfaceStorage = NULL;
|
||||
_videoPlayer = NULL;
|
||||
_theoraPlayer = NULL;
|
||||
_soundMgr = NULL;
|
||||
_debugMgr = NULL;
|
||||
|
||||
@ -445,6 +454,9 @@ HRESULT CBGame::Initialize1() {
|
||||
|
||||
_scEngine = new CScEngine(this);
|
||||
if (_scEngine == NULL) goto init_fail;
|
||||
|
||||
_videoPlayer = new CVidPlayer(this);
|
||||
if(_videoPlayer==NULL) goto init_fail;
|
||||
|
||||
_transMgr = new CBTransitionMgr(this);
|
||||
if (_transMgr == NULL) goto init_fail;
|
||||
@ -473,6 +485,7 @@ init_fail:
|
||||
if (_soundMgr) delete _soundMgr;
|
||||
if (_fileManager) delete _fileManager;
|
||||
if (_scEngine) delete _scEngine;
|
||||
if (_videoPlayer) delete _videoPlayer;
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
@ -1399,16 +1412,16 @@ HRESULT CBGame::ScCallMethod(CScScript *Script, CScStack *Stack, CScStack *ThisS
|
||||
|
||||
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->_videoPlayer->initialize(Filename, SubtitleFile)))
|
||||
{
|
||||
if(SUCCEEDED(Game->m_VideoPlayer->Play((TVideoPlayback)Type, X, Y, FreezeMusic)))
|
||||
if(SUCCEEDED(Game->_videoPlayer->play((TVideoPlayback)Type, X, Y, FreezeMusic)))
|
||||
{
|
||||
Stack->PushBool(true);
|
||||
Script->Sleep(0);
|
||||
}
|
||||
else Stack->PushBool(false);
|
||||
}
|
||||
else */Stack->PushBool(false);
|
||||
else Stack->PushBool(false);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
@ -1441,20 +1454,20 @@ HRESULT CBGame::ScCallMethod(CScScript *Script, CScStack *Stack, CScStack *ThisS
|
||||
|
||||
|
||||
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)))
|
||||
|
||||
delete _theoraPlayer;
|
||||
_theoraPlayer = new CVidTheoraPlayer(this);
|
||||
if(_theoraPlayer && SUCCEEDED(_theoraPlayer->initialize(Filename, SubtitleFile)))
|
||||
{
|
||||
m_TheoraPlayer->m_DontDropFrames = !DropFrames;
|
||||
if(SUCCEEDED(m_TheoraPlayer->Play((TVideoPlayback)Type, X, Y, true, FreezeMusic)))
|
||||
_theoraPlayer->_dontDropFrames = !DropFrames;
|
||||
if(SUCCEEDED(_theoraPlayer->play((TVideoPlayback)Type, X, Y, true, FreezeMusic)))
|
||||
{
|
||||
Stack->PushBool(true);
|
||||
Script->Sleep(0);
|
||||
}
|
||||
else Stack->PushBool(false);
|
||||
}
|
||||
else */Stack->PushBool(false);
|
||||
else Stack->PushBool(false);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
@ -4195,6 +4208,27 @@ HRESULT CBGame::SetWaitCursor(const char *Filename) {
|
||||
} else return S_OK;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool CBGame::IsVideoPlaying()
|
||||
{
|
||||
if(_videoPlayer->isPlaying()) return true;
|
||||
if(_theoraPlayer && _theoraPlayer->isPlaying()) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
HRESULT CBGame::StopVideo()
|
||||
{
|
||||
if(_videoPlayer->isPlaying()) _videoPlayer->stop();
|
||||
if(_theoraPlayer && _theoraPlayer->isPlaying())
|
||||
{
|
||||
_theoraPlayer->stop();
|
||||
delete _theoraPlayer;
|
||||
_theoraPlayer = NULL;
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
HRESULT CBGame::DrawCursor(CBSprite *Cursor) {
|
||||
|
@ -61,6 +61,8 @@ class CBSurfaceStorage;
|
||||
class CSXStore;
|
||||
class CSXMath;
|
||||
class CBKeyboardState;
|
||||
class CVidPlayer;
|
||||
class CVidTheoraPlayer;
|
||||
|
||||
#define NUM_MUSIC_CHANNELS 5
|
||||
|
||||
@ -263,8 +265,8 @@ public:
|
||||
HRESULT Unfreeze();
|
||||
HRESULT Freeze(bool IncludingMusic = true);
|
||||
HRESULT FocusWindow(CUIWindow *Window);
|
||||
/* CVidPlayer* _videoPlayer;
|
||||
CVidTheoraPlayer* _theoraPlayer;*/
|
||||
CVidPlayer* _videoPlayer;
|
||||
CVidTheoraPlayer* _theoraPlayer;
|
||||
bool _loadInProgress;
|
||||
CUIWindow *_focusedWindow;
|
||||
bool _editorForceScripts;
|
||||
@ -323,6 +325,9 @@ public:
|
||||
uint32 _fps;
|
||||
HRESULT UpdateMusicCrossfade();
|
||||
|
||||
bool IsVideoPlaying();
|
||||
HRESULT StopVideo();
|
||||
|
||||
CBArray<CBObject *, CBObject *> _regObjects;
|
||||
public:
|
||||
virtual HRESULT DisplayContent(bool Update = true, bool DisplayAll = false);
|
||||
|
@ -86,12 +86,12 @@ HRESULT CVidPlayer::SetDefaults() {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
CVidPlayer::~CVidPlayer() {
|
||||
Cleanup();
|
||||
cleanup();
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
HRESULT CVidPlayer::Cleanup() {
|
||||
HRESULT CVidPlayer::cleanup() {
|
||||
#if 0
|
||||
if (_sound) _sound->Stop();
|
||||
if (_videoPGF) AVIStreamGetFrameClose(_videoPGF);
|
||||
@ -124,7 +124,7 @@ HRESULT CVidPlayer::Cleanup() {
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
HRESULT CVidPlayer::Initialize(char *inFilename, char *SubtitleFile) {
|
||||
HRESULT CVidPlayer::initialize(const char *inFilename, const char *SubtitleFile) {
|
||||
#if 0
|
||||
Cleanup();
|
||||
|
||||
@ -204,7 +204,7 @@ HRESULT CVidPlayer::Initialize(char *inFilename, char *SubtitleFile) {
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
HRESULT CVidPlayer::Update() {
|
||||
HRESULT CVidPlayer::update() {
|
||||
#if 0
|
||||
if (!m_Playing) return S_OK;
|
||||
|
||||
@ -274,7 +274,7 @@ HRESULT CVidPlayer::Update() {
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
HRESULT CVidPlayer::Display() {
|
||||
HRESULT CVidPlayer::display() {
|
||||
#if 0
|
||||
if (!m_Playing) return S_OK;
|
||||
|
||||
@ -298,7 +298,7 @@ HRESULT CVidPlayer::Display() {
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
HRESULT CVidPlayer::Play(TVideoPlayback Type, int X, int Y, bool FreezeMusic) {
|
||||
HRESULT CVidPlayer::play(TVideoPlayback Type, int X, int Y, bool FreezeMusic) {
|
||||
#if 0
|
||||
if (!_videoStream || !_vidRenderer) return E_FAIL;
|
||||
|
||||
@ -359,7 +359,7 @@ HRESULT CVidPlayer::Play(TVideoPlayback Type, int X, int Y, bool FreezeMusic) {
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
HRESULT CVidPlayer::Stop() {
|
||||
HRESULT CVidPlayer::stop() {
|
||||
#if 0
|
||||
if (!_playing) return S_OK;
|
||||
|
||||
@ -372,13 +372,13 @@ HRESULT CVidPlayer::Stop() {
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool CVidPlayer::IsPlaying() {
|
||||
bool CVidPlayer::isPlaying() {
|
||||
return _playing;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
HRESULT CVidPlayer::LoadSubtitles(char *Filename, char *SubtitleFile) {
|
||||
HRESULT CVidPlayer::loadSubtitles(const char *Filename, const char *SubtitleFile) {
|
||||
#if 0
|
||||
if (!Filename) return S_OK;
|
||||
|
||||
|
@ -46,12 +46,12 @@ class CVidPlayer : public CBBase {
|
||||
public:
|
||||
bool _showSubtitle;
|
||||
int _currentSubtitle;
|
||||
HRESULT LoadSubtitles(char *Filename, char *SubtitleFile);
|
||||
HRESULT loadSubtitles(const char *Filename, const char *SubtitleFile);
|
||||
bool _slowRendering;
|
||||
bool IsPlaying();
|
||||
bool isPlaying();
|
||||
char *_filename;
|
||||
HRESULT Stop();
|
||||
HRESULT Play(TVideoPlayback Type = VID_PLAY_CENTER, int X = 0, int Y = 0, bool FreezeMusic = true);
|
||||
HRESULT stop();
|
||||
HRESULT play(TVideoPlayback Type = VID_PLAY_CENTER, int X = 0, int Y = 0, bool FreezeMusic = true);
|
||||
uint32 _totalVideoTime;
|
||||
uint32 _startTime;
|
||||
//CVidRenderer *_vidRenderer;
|
||||
@ -59,10 +59,10 @@ public:
|
||||
bool _soundAvailable;
|
||||
HRESULT SetDefaults();
|
||||
bool _playing;
|
||||
HRESULT Display();
|
||||
HRESULT Update();
|
||||
HRESULT Initialize(char *inFilename, char *SubtitleFile = NULL);
|
||||
HRESULT Cleanup();
|
||||
HRESULT display();
|
||||
HRESULT update();
|
||||
HRESULT initialize(const char *inFilename, const char *SubtitleFile = NULL);
|
||||
HRESULT cleanup();
|
||||
CVidPlayer(CBGame *inGame);
|
||||
virtual ~CVidPlayer();
|
||||
|
||||
|
@ -159,7 +159,7 @@ void CVidTheoraPlayer::Cleanup() {
|
||||
}*/
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
HRESULT CVidTheoraPlayer::Initialize(char *Filename, char *SubtitleFile) {
|
||||
HRESULT CVidTheoraPlayer::initialize(const char *Filename, const char *SubtitleFile) {
|
||||
#if 0
|
||||
Cleanup();
|
||||
|
||||
@ -328,7 +328,7 @@ HRESULT CVidTheoraPlayer::Initialize(char *Filename, char *SubtitleFile) {
|
||||
|
||||
return Res;
|
||||
#endif
|
||||
return 0;
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
|
||||
@ -345,7 +345,7 @@ HRESULT CVidTheoraPlayer::ResetStream() {
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
HRESULT CVidTheoraPlayer::Play(TVideoPlayback Type, int X, int Y, bool FreezeGame, bool FreezeMusic, bool Looping, uint32 StartTime, float ForceZoom, int Volume) {
|
||||
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);
|
||||
@ -397,11 +397,11 @@ HRESULT CVidTheoraPlayer::Play(TVideoPlayback Type, int X, int Y, bool FreezeGam
|
||||
|
||||
Update();
|
||||
#endif
|
||||
return S_OK;
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
HRESULT CVidTheoraPlayer::Stop() {
|
||||
HRESULT CVidTheoraPlayer::stop() {
|
||||
#if 0
|
||||
if (m_Sound) m_Sound->Stop();
|
||||
m_State = THEORA_STATE_FINISHED;
|
||||
@ -411,7 +411,7 @@ HRESULT CVidTheoraPlayer::Stop() {
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
HRESULT CVidTheoraPlayer::Update() {
|
||||
HRESULT CVidTheoraPlayer::update() {
|
||||
#if 0
|
||||
m_CurrentTime = m_FreezeGame ? Game->m_LiveTimer : Game->m_Timer;
|
||||
|
||||
@ -637,7 +637,7 @@ HRESULT CVidTheoraPlayer::WriteVideo() {
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
HRESULT CVidTheoraPlayer::Display(uint32 Alpha) {
|
||||
HRESULT CVidTheoraPlayer::display(uint32 Alpha) {
|
||||
|
||||
RECT rc;
|
||||
HRESULT Res;
|
||||
@ -890,7 +890,7 @@ finish:
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
HRESULT CVidTheoraPlayer::Pause() {
|
||||
HRESULT CVidTheoraPlayer::pause() {
|
||||
#if 0
|
||||
if (m_State == THEORA_STATE_PLAYING) {
|
||||
m_State = THEORA_STATE_PAUSED;
|
||||
@ -902,7 +902,7 @@ HRESULT CVidTheoraPlayer::Pause() {
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
HRESULT CVidTheoraPlayer::Resume() {
|
||||
HRESULT CVidTheoraPlayer::resume() {
|
||||
#if 0
|
||||
if (_state == THEORA_STATE_PAUSED) {
|
||||
_state = THEORA_STATE_PLAYING;
|
||||
@ -945,7 +945,7 @@ HRESULT CVidTheoraPlayer::Resume() {
|
||||
}
|
||||
*/
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
HRESULT CVidTheoraPlayer::InitializeSimple() {
|
||||
HRESULT CVidTheoraPlayer::initializeSimple() {
|
||||
#if 0
|
||||
if (SUCCEEDED(Initialize(m_Filename))) {
|
||||
if (m_AlphaFilename) SetAlphaImage(m_AlphaFilename);
|
||||
|
@ -71,21 +71,22 @@ public:
|
||||
//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 initialize(const char *Filename, const 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();
|
||||
HRESULT pause();
|
||||
HRESULT resume();
|
||||
|
||||
bool IsPlaying() {
|
||||
bool isPlaying() {
|
||||
return _state == THEORA_STATE_PLAYING;
|
||||
};
|
||||
bool IsFinished() {
|
||||
return true; // HACK
|
||||
return _state == THEORA_STATE_FINISHED;
|
||||
};
|
||||
bool IsPaused() {
|
||||
|
566
engines/wintermute/video/decoders/theora_decoder.cpp
Normal file
566
engines/wintermute/video/decoders/theora_decoder.cpp
Normal file
@ -0,0 +1,566 @@
|
||||
/* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Source is based on the player example from libvorbis package,
|
||||
* available at: http://svn.xiph.org/trunk/theora/examples/player_example.c
|
||||
*
|
||||
* THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE.
|
||||
* USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS
|
||||
* GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE
|
||||
* IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING.
|
||||
*
|
||||
* THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009
|
||||
* by the Xiph.Org Foundation and contributors http://www.xiph.org/
|
||||
*
|
||||
*/
|
||||
|
||||
#include "engines/wintermute/video/decoders/theora_decoder.h"
|
||||
|
||||
#ifdef USE_THEORADEC
|
||||
#include "common/system.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/util.h"
|
||||
#include "graphics/yuv_to_rgb.h"
|
||||
#include "audio/decoders/raw.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/debug.h"
|
||||
|
||||
namespace WinterMute {
|
||||
|
||||
#define AUDIOFD_FRAGSIZE 10240
|
||||
|
||||
static double rint(double v) {
|
||||
return floor(v + 0.5);
|
||||
}
|
||||
|
||||
TheoraDecoder::TheoraDecoder(Audio::Mixer::SoundType soundType) {
|
||||
_fileStream = 0;
|
||||
|
||||
_theoraPacket = 0;
|
||||
_vorbisPacket = 0;
|
||||
_theoraDecode = 0;
|
||||
_theoraSetup = 0;
|
||||
_nextFrameStartTime = 0.0;
|
||||
|
||||
_soundType = soundType;
|
||||
_audStream = 0;
|
||||
_audHandle = new Audio::SoundHandle();
|
||||
|
||||
ogg_sync_init(&_oggSync);
|
||||
|
||||
_curFrame = -1;
|
||||
_audiobuf = (ogg_int16_t *)malloc(AUDIOFD_FRAGSIZE * sizeof(ogg_int16_t));
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
TheoraDecoder::~TheoraDecoder() {
|
||||
close();
|
||||
delete _fileStream;
|
||||
delete _audHandle;
|
||||
free(_audiobuf);
|
||||
}
|
||||
|
||||
void TheoraDecoder::queuePage(ogg_page *page) {
|
||||
if (_theoraPacket)
|
||||
ogg_stream_pagein(&_theoraOut, page);
|
||||
|
||||
if (_vorbisPacket)
|
||||
ogg_stream_pagein(&_vorbisOut, page);
|
||||
}
|
||||
|
||||
int TheoraDecoder::bufferData() {
|
||||
char *buffer = ogg_sync_buffer(&_oggSync, 4096);
|
||||
int bytes = _fileStream->read(buffer, 4096);
|
||||
|
||||
ogg_sync_wrote(&_oggSync, bytes);
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
bool TheoraDecoder::loadStream(Common::SeekableReadStream *stream) {
|
||||
close();
|
||||
|
||||
_endOfAudio = false;
|
||||
_endOfVideo = false;
|
||||
_fileStream = stream;
|
||||
|
||||
// start up Ogg stream synchronization layer
|
||||
ogg_sync_init(&_oggSync);
|
||||
|
||||
// init supporting Vorbis structures needed in header parsing
|
||||
vorbis_info_init(&_vorbisInfo);
|
||||
vorbis_comment_init(&_vorbisComment);
|
||||
|
||||
// init supporting Theora structures needed in header parsing
|
||||
th_comment_init(&_theoraComment);
|
||||
th_info_init(&_theoraInfo);
|
||||
|
||||
// Ogg file open; parse the headers
|
||||
// Only interested in Vorbis/Theora streams
|
||||
bool foundHeader = false;
|
||||
while (!foundHeader) {
|
||||
int ret = bufferData();
|
||||
|
||||
if (ret == 0)
|
||||
break;
|
||||
|
||||
while (ogg_sync_pageout(&_oggSync, &_oggPage) > 0) {
|
||||
ogg_stream_state test;
|
||||
|
||||
// is this a mandated initial header? If not, stop parsing
|
||||
if (!ogg_page_bos(&_oggPage)) {
|
||||
// don't leak the page; get it into the appropriate stream
|
||||
queuePage(&_oggPage);
|
||||
foundHeader = true;
|
||||
break;
|
||||
}
|
||||
|
||||
ogg_stream_init(&test, ogg_page_serialno(&_oggPage));
|
||||
ogg_stream_pagein(&test, &_oggPage);
|
||||
ogg_stream_packetout(&test, &_oggPacket);
|
||||
|
||||
// identify the codec: try theora
|
||||
if (!_theoraPacket && th_decode_headerin(&_theoraInfo, &_theoraComment, &_theoraSetup, &_oggPacket) >= 0) {
|
||||
// it is theora
|
||||
memcpy(&_theoraOut, &test, sizeof(test));
|
||||
_theoraPacket = 1;
|
||||
} else if (!_vorbisPacket && vorbis_synthesis_headerin(&_vorbisInfo, &_vorbisComment, &_oggPacket) >= 0) {
|
||||
// it is vorbis
|
||||
memcpy(&_vorbisOut, &test, sizeof(test));
|
||||
_vorbisPacket = 1;
|
||||
} else {
|
||||
// whatever it is, we don't care about it
|
||||
ogg_stream_clear(&test);
|
||||
}
|
||||
}
|
||||
// fall through to non-bos page parsing
|
||||
}
|
||||
|
||||
// we're expecting more header packets.
|
||||
while ((_theoraPacket && _theoraPacket < 3) || (_vorbisPacket && _vorbisPacket < 3)) {
|
||||
int ret;
|
||||
|
||||
// look for further theora headers
|
||||
while (_theoraPacket && (_theoraPacket < 3) && (ret = ogg_stream_packetout(&_theoraOut, &_oggPacket))) {
|
||||
if (ret < 0)
|
||||
error("Error parsing Theora stream headers; corrupt stream?");
|
||||
|
||||
if (!th_decode_headerin(&_theoraInfo, &_theoraComment, &_theoraSetup, &_oggPacket))
|
||||
error("Error parsing Theora stream headers; corrupt stream?");
|
||||
|
||||
_theoraPacket++;
|
||||
}
|
||||
|
||||
// look for more vorbis header packets
|
||||
while (_vorbisPacket && (_vorbisPacket < 3) && (ret = ogg_stream_packetout(&_vorbisOut, &_oggPacket))) {
|
||||
if (ret < 0)
|
||||
error("Error parsing Vorbis stream headers; corrupt stream?");
|
||||
|
||||
if (vorbis_synthesis_headerin(&_vorbisInfo, &_vorbisComment, &_oggPacket))
|
||||
error("Error parsing Vorbis stream headers; corrupt stream?");
|
||||
|
||||
_vorbisPacket++;
|
||||
|
||||
if (_vorbisPacket == 3)
|
||||
break;
|
||||
}
|
||||
|
||||
// The header pages/packets will arrive before anything else we
|
||||
// care about, or the stream is not obeying spec
|
||||
|
||||
if (ogg_sync_pageout(&_oggSync, &_oggPage) > 0) {
|
||||
queuePage(&_oggPage); // demux into the appropriate stream
|
||||
} else {
|
||||
ret = bufferData(); // someone needs more data
|
||||
|
||||
if (ret == 0)
|
||||
error("End of file while searching for codec headers.");
|
||||
}
|
||||
}
|
||||
|
||||
// and now we have it all. initialize decoders
|
||||
if (_theoraPacket) {
|
||||
_theoraDecode = th_decode_alloc(&_theoraInfo, _theoraSetup);
|
||||
debugN(1, "Ogg logical stream %lx is Theora %dx%d %.02f fps",
|
||||
_theoraOut.serialno, _theoraInfo.pic_width, _theoraInfo.pic_height,
|
||||
(double)_theoraInfo.fps_numerator / _theoraInfo.fps_denominator);
|
||||
|
||||
switch (_theoraInfo.pixel_fmt) {
|
||||
case TH_PF_420:
|
||||
debug(1, " 4:2:0 video");
|
||||
break;
|
||||
case TH_PF_422:
|
||||
debug(1, " 4:2:2 video");
|
||||
break;
|
||||
case TH_PF_444:
|
||||
debug(1, " 4:4:4 video");
|
||||
break;
|
||||
case TH_PF_RSVD:
|
||||
default:
|
||||
debug(1, " video\n (UNKNOWN Chroma sampling!)");
|
||||
break;
|
||||
}
|
||||
|
||||
if (_theoraInfo.pic_width != _theoraInfo.frame_width || _theoraInfo.pic_height != _theoraInfo.frame_height)
|
||||
debug(1, " Frame content is %dx%d with offset (%d,%d).",
|
||||
_theoraInfo.frame_width, _theoraInfo.frame_height, _theoraInfo.pic_x, _theoraInfo.pic_y);
|
||||
|
||||
switch (_theoraInfo.colorspace){
|
||||
case TH_CS_UNSPECIFIED:
|
||||
/* nothing to report */
|
||||
break;
|
||||
case TH_CS_ITU_REC_470M:
|
||||
debug(1, " encoder specified ITU Rec 470M (NTSC) color.");
|
||||
break;
|
||||
case TH_CS_ITU_REC_470BG:
|
||||
debug(1, " encoder specified ITU Rec 470BG (PAL) color.");
|
||||
break;
|
||||
default:
|
||||
debug(1, "warning: encoder specified unknown colorspace (%d).", _theoraInfo.colorspace);
|
||||
break;
|
||||
}
|
||||
|
||||
debug(1, "Encoded by %s", _theoraComment.vendor);
|
||||
if (_theoraComment.comments) {
|
||||
debug(1, "theora comment header:");
|
||||
for (int i = 0; i < _theoraComment.comments; i++) {
|
||||
if (_theoraComment.user_comments[i]) {
|
||||
int len = _theoraComment.comment_lengths[i];
|
||||
char *value = (char *)malloc(len + 1);
|
||||
if (value) {
|
||||
memcpy(value, _theoraComment.user_comments[i], len);
|
||||
value[len] = '\0';
|
||||
debug(1, "\t%s", value);
|
||||
free(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
th_decode_ctl(_theoraDecode, TH_DECCTL_GET_PPLEVEL_MAX, &_ppLevelMax, sizeof(_ppLevelMax));
|
||||
_ppLevel = _ppLevelMax;
|
||||
th_decode_ctl(_theoraDecode, TH_DECCTL_SET_PPLEVEL, &_ppLevel, sizeof(_ppLevel));
|
||||
_ppInc = 0;
|
||||
} else {
|
||||
// tear down the partial theora setup
|
||||
th_info_clear(&_theoraInfo);
|
||||
th_comment_clear(&_theoraComment);
|
||||
}
|
||||
|
||||
th_setup_free(_theoraSetup);
|
||||
_theoraSetup = 0;
|
||||
|
||||
if (_vorbisPacket) {
|
||||
vorbis_synthesis_init(&_vorbisDSP, &_vorbisInfo);
|
||||
vorbis_block_init(&_vorbisDSP, &_vorbisBlock);
|
||||
debug(3, "Ogg logical stream %lx is Vorbis %d channel %ld Hz audio.",
|
||||
_vorbisOut.serialno, _vorbisInfo.channels, _vorbisInfo.rate);
|
||||
|
||||
_audStream = Audio::makeQueuingAudioStream(_vorbisInfo.rate, _vorbisInfo.channels);
|
||||
|
||||
// Get enough audio data to start us off
|
||||
while (_audStream->numQueuedStreams() == 0) {
|
||||
// Queue more data
|
||||
bufferData();
|
||||
while (ogg_sync_pageout(&_oggSync, &_oggPage) > 0)
|
||||
queuePage(&_oggPage);
|
||||
|
||||
queueAudio();
|
||||
}
|
||||
|
||||
if (_audStream)
|
||||
g_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, _audHandle, _audStream, -1, getVolume(), getBalance());
|
||||
} else {
|
||||
// tear down the partial vorbis setup
|
||||
vorbis_info_clear(&_vorbisInfo);
|
||||
vorbis_comment_clear(&_vorbisComment);
|
||||
_endOfAudio = true;
|
||||
}
|
||||
|
||||
_surface.create(_theoraInfo.frame_width, _theoraInfo.frame_height, g_system->getScreenFormat());
|
||||
|
||||
// Set up a display surface
|
||||
_displaySurface.pixels = _surface.getBasePtr(_theoraInfo.pic_x, _theoraInfo.pic_y);
|
||||
_displaySurface.w = _theoraInfo.pic_width;
|
||||
_displaySurface.h = _theoraInfo.pic_height;
|
||||
_displaySurface.format = _surface.format;
|
||||
_displaySurface.pitch = _surface.pitch;
|
||||
|
||||
// Set the frame rate
|
||||
_frameRate = Common::Rational(_theoraInfo.fps_numerator, _theoraInfo.fps_denominator);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void TheoraDecoder::close() {
|
||||
if (_vorbisPacket) {
|
||||
ogg_stream_clear(&_vorbisOut);
|
||||
vorbis_block_clear(&_vorbisBlock);
|
||||
vorbis_dsp_clear(&_vorbisDSP);
|
||||
vorbis_comment_clear(&_vorbisComment);
|
||||
vorbis_info_clear(&_vorbisInfo);
|
||||
|
||||
g_system->getMixer()->stopHandle(*_audHandle);
|
||||
|
||||
_audStream = 0;
|
||||
_vorbisPacket = false;
|
||||
}
|
||||
if (_theoraPacket) {
|
||||
ogg_stream_clear(&_theoraOut);
|
||||
th_decode_free(_theoraDecode);
|
||||
th_comment_clear(&_theoraComment);
|
||||
th_info_clear(&_theoraInfo);
|
||||
_theoraDecode = 0;
|
||||
_theoraPacket = false;
|
||||
}
|
||||
|
||||
if (!_fileStream)
|
||||
return;
|
||||
|
||||
ogg_sync_clear(&_oggSync);
|
||||
|
||||
delete _fileStream;
|
||||
_fileStream = 0;
|
||||
|
||||
_surface.free();
|
||||
_displaySurface.pixels = 0;
|
||||
_displaySurface.free();
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
const Graphics::Surface *TheoraDecoder::decodeNextFrame() {
|
||||
// First, let's get our frame
|
||||
while (_theoraPacket) {
|
||||
// theora is one in, one out...
|
||||
if (ogg_stream_packetout(&_theoraOut, &_oggPacket) > 0) {
|
||||
|
||||
if (_ppInc) {
|
||||
_ppLevel += _ppInc;
|
||||
th_decode_ctl(_theoraDecode, TH_DECCTL_SET_PPLEVEL, &_ppLevel, sizeof(_ppLevel));
|
||||
_ppInc = 0;
|
||||
}
|
||||
|
||||
if (th_decode_packetin(_theoraDecode, &_oggPacket, NULL) == 0) {
|
||||
_curFrame++;
|
||||
|
||||
// Convert YUV data to RGB data
|
||||
th_ycbcr_buffer yuv;
|
||||
th_decode_ycbcr_out(_theoraDecode, yuv);
|
||||
translateYUVtoRGBA(yuv);
|
||||
|
||||
if (_curFrame == 0)
|
||||
_startTime = g_system->getMillis();
|
||||
|
||||
double time = th_granule_time(_theoraDecode, _oggPacket.granulepos);
|
||||
|
||||
// We need to calculate when the next frame should be shown
|
||||
// This is all in floating point because that's what the Ogg code gives us
|
||||
// Ogg is a lossy container format, so it doesn't always list the time to the
|
||||
// next frame. In such cases, we need to calculate it ourselves.
|
||||
if (time == -1.0)
|
||||
_nextFrameStartTime += _frameRate.getInverse().toDouble();
|
||||
else
|
||||
_nextFrameStartTime = time;
|
||||
|
||||
// break out
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// If we can't get any more frames, we're done.
|
||||
if (_theoraOut.e_o_s || _fileStream->eos()) {
|
||||
_endOfVideo = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Queue more data
|
||||
bufferData();
|
||||
while (ogg_sync_pageout(&_oggSync, &_oggPage) > 0)
|
||||
queuePage(&_oggPage);
|
||||
}
|
||||
|
||||
// Update audio if we can
|
||||
queueAudio();
|
||||
}
|
||||
|
||||
// Force at least some audio to be buffered
|
||||
// TODO: 5 is very arbitrary. We probably should do something like QuickTime does.
|
||||
while (!_endOfAudio && _audStream->numQueuedStreams() < 5) {
|
||||
bufferData();
|
||||
while (ogg_sync_pageout(&_oggSync, &_oggPage) > 0)
|
||||
queuePage(&_oggPage);
|
||||
|
||||
bool queuedAudio = queueAudio();
|
||||
if ((_vorbisOut.e_o_s || _fileStream->eos()) && !queuedAudio) {
|
||||
_endOfAudio = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return &_displaySurface;
|
||||
}
|
||||
|
||||
bool TheoraDecoder::queueAudio() {
|
||||
if (!_audStream)
|
||||
return false;
|
||||
|
||||
// An audio buffer should have been allocated (either in the constructor or after queuing the current buffer)
|
||||
if (!_audiobuf) {
|
||||
warning("[TheoraDecoder::queueAudio] Invalid audio buffer");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool queuedAudio = false;
|
||||
|
||||
for (;;) {
|
||||
float **pcm;
|
||||
|
||||
// if there's pending, decoded audio, grab it
|
||||
int ret = vorbis_synthesis_pcmout(&_vorbisDSP, &pcm);
|
||||
if (ret > 0) {
|
||||
int count = _audiobufFill / 2;
|
||||
int maxsamples = ((AUDIOFD_FRAGSIZE - _audiobufFill) / _vorbisInfo.channels) >> 1;
|
||||
int i;
|
||||
for (i = 0; i < ret && i < maxsamples; i++)
|
||||
for (int j = 0; j < _vorbisInfo.channels; j++) {
|
||||
int val = CLIP((int)rint(pcm[j][i] * 32767.f), -32768, 32767);
|
||||
_audiobuf[count++] = val;
|
||||
}
|
||||
|
||||
vorbis_synthesis_read(&_vorbisDSP, i);
|
||||
_audiobufFill += (i * _vorbisInfo.channels) << 1;
|
||||
|
||||
if (_audiobufFill == AUDIOFD_FRAGSIZE) {
|
||||
byte flags = Audio::FLAG_16BITS | Audio::FLAG_STEREO;
|
||||
#ifdef SCUMM_LITTLE_ENDIAN
|
||||
flags |= Audio::FLAG_LITTLE_ENDIAN;
|
||||
#endif
|
||||
_audStream->queueBuffer((byte *)_audiobuf, AUDIOFD_FRAGSIZE, DisposeAfterUse::NO, flags);
|
||||
|
||||
// The audio mixer is now responsible for the old audio buffer.
|
||||
// We need to create a new one.
|
||||
_audiobuf = (ogg_int16_t *)malloc(AUDIOFD_FRAGSIZE * sizeof(ogg_int16_t));
|
||||
if (!_audiobuf) {
|
||||
warning("[TheoraDecoder::queueAudio] Cannot allocate memory for audio buffer");
|
||||
return false;
|
||||
}
|
||||
|
||||
_audiobufFill = 0;
|
||||
queuedAudio = true;
|
||||
}
|
||||
} else {
|
||||
// no pending audio; is there a pending packet to decode?
|
||||
if (ogg_stream_packetout(&_vorbisOut, &_oggPacket) > 0) {
|
||||
if (vorbis_synthesis(&_vorbisBlock, &_oggPacket) == 0) // test for success!
|
||||
vorbis_synthesis_blockin(&_vorbisDSP, &_vorbisBlock);
|
||||
} else // we've buffered all we have, break out for now
|
||||
return queuedAudio;
|
||||
}
|
||||
}
|
||||
|
||||
// Unreachable
|
||||
return false;
|
||||
}
|
||||
|
||||
void TheoraDecoder::reset() {
|
||||
VideoDecoder::reset();
|
||||
|
||||
// FIXME: This does a rewind() instead of a reset()!
|
||||
|
||||
if (_fileStream)
|
||||
_fileStream->seek(0);
|
||||
|
||||
_audiobufFill = 0;
|
||||
_audiobufReady = false;
|
||||
|
||||
_curFrame = -1;
|
||||
|
||||
_theoraPacket = 0;
|
||||
_vorbisPacket = 0;
|
||||
}
|
||||
|
||||
bool TheoraDecoder::endOfVideo() const {
|
||||
return !isVideoLoaded() || (_endOfVideo && (!_audStream || (_audStream->endOfData() && _endOfAudio)));
|
||||
}
|
||||
|
||||
uint32 TheoraDecoder::getTimeToNextFrame() const {
|
||||
if (endOfVideo() || _curFrame < 0)
|
||||
return 0;
|
||||
|
||||
uint32 elapsedTime = getTime();
|
||||
uint32 nextFrameStartTime = (uint32)(_nextFrameStartTime * 1000);
|
||||
|
||||
if (nextFrameStartTime <= elapsedTime)
|
||||
return 0;
|
||||
|
||||
return nextFrameStartTime - elapsedTime;
|
||||
}
|
||||
|
||||
uint32 TheoraDecoder::getTime() const {
|
||||
if (_audStream)
|
||||
return g_system->getMixer()->getSoundElapsedTime(*_audHandle);
|
||||
|
||||
return VideoDecoder::getTime();
|
||||
}
|
||||
|
||||
void TheoraDecoder::pauseVideoIntern(bool pause) {
|
||||
if (_audStream)
|
||||
g_system->getMixer()->pauseHandle(*_audHandle, pause);
|
||||
}
|
||||
|
||||
enum TheoraYUVBuffers {
|
||||
kBufferY = 0,
|
||||
kBufferU = 1,
|
||||
kBufferV = 2
|
||||
};
|
||||
|
||||
void TheoraDecoder::translateYUVtoRGBA(th_ycbcr_buffer &YUVBuffer) {
|
||||
// Width and height of all buffers have to be divisible by 2.
|
||||
assert((YUVBuffer[kBufferY].width & 1) == 0);
|
||||
assert((YUVBuffer[kBufferY].height & 1) == 0);
|
||||
assert((YUVBuffer[kBufferU].width & 1) == 0);
|
||||
assert((YUVBuffer[kBufferV].width & 1) == 0);
|
||||
|
||||
// UV images have to have a quarter of the Y image resolution
|
||||
assert(YUVBuffer[kBufferU].width == YUVBuffer[kBufferY].width >> 1);
|
||||
assert(YUVBuffer[kBufferV].width == YUVBuffer[kBufferY].width >> 1);
|
||||
assert(YUVBuffer[kBufferU].height == YUVBuffer[kBufferY].height >> 1);
|
||||
assert(YUVBuffer[kBufferV].height == YUVBuffer[kBufferY].height >> 1);
|
||||
|
||||
Graphics::convertYUV420ToRGB(&_surface, YUVBuffer[kBufferY].data, YUVBuffer[kBufferU].data, YUVBuffer[kBufferV].data, YUVBuffer[kBufferY].width, YUVBuffer[kBufferY].height, YUVBuffer[kBufferY].stride, YUVBuffer[kBufferU].stride);
|
||||
}
|
||||
|
||||
void TheoraDecoder::updateVolume() {
|
||||
if (g_system->getMixer()->isSoundHandleActive(*_audHandle))
|
||||
g_system->getMixer()->setChannelVolume(*_audHandle, getVolume());
|
||||
}
|
||||
|
||||
void TheoraDecoder::updateBalance() {
|
||||
if (g_system->getMixer()->isSoundHandleActive(*_audHandle))
|
||||
g_system->getMixer()->setChannelBalance(*_audHandle, getBalance());
|
||||
}
|
||||
|
||||
} // End of namespace Sword25
|
||||
|
||||
#endif
|
144
engines/wintermute/video/decoders/theora_decoder.h
Normal file
144
engines/wintermute/video/decoders/theora_decoder.h
Normal file
@ -0,0 +1,144 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef WINTERMUTE_THEORADECODER_H
|
||||
#define WINTERMUTE_THEORADECODER_H
|
||||
|
||||
#include "common/scummsys.h" // for USE_THEORADEC
|
||||
|
||||
#ifdef USE_THEORADEC
|
||||
|
||||
#include "common/rational.h"
|
||||
#include "video/video_decoder.h"
|
||||
#include "audio/audiostream.h"
|
||||
#include "audio/mixer.h"
|
||||
#include "graphics/pixelformat.h"
|
||||
#include "graphics/surface.h"
|
||||
|
||||
#include <theora/theoradec.h>
|
||||
#include <vorbis/codec.h>
|
||||
|
||||
namespace Common {
|
||||
class SeekableReadStream;
|
||||
}
|
||||
|
||||
namespace WinterMute {
|
||||
|
||||
/**
|
||||
*
|
||||
* Decoder for Theora videos.
|
||||
* Video decoder used in engines:
|
||||
* - sword25
|
||||
*/
|
||||
class TheoraDecoder : public Video::VideoDecoder {
|
||||
public:
|
||||
TheoraDecoder(Audio::Mixer::SoundType soundType = Audio::Mixer::kMusicSoundType);
|
||||
virtual ~TheoraDecoder();
|
||||
|
||||
/**
|
||||
* Load a video file
|
||||
* @param stream the stream to load
|
||||
*/
|
||||
bool loadStream(Common::SeekableReadStream *stream);
|
||||
void close();
|
||||
void reset();
|
||||
|
||||
/**
|
||||
* Decode the next frame and return the frame's surface
|
||||
* @note the return surface should *not* be freed
|
||||
* @note this may return 0, in which case the last frame should be kept on screen
|
||||
*/
|
||||
const Graphics::Surface *decodeNextFrame();
|
||||
|
||||
bool isVideoLoaded() const { return _fileStream != 0; }
|
||||
uint16 getWidth() const { return _displaySurface.w; }
|
||||
uint16 getHeight() const { return _displaySurface.h; }
|
||||
|
||||
uint32 getFrameCount() const {
|
||||
// It is not possible to get frame count easily
|
||||
// I.e. seeking is required
|
||||
assert(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Graphics::PixelFormat getPixelFormat() const { return _displaySurface.format; }
|
||||
uint32 getTime() const;
|
||||
uint32 getTimeToNextFrame() const;
|
||||
|
||||
bool endOfVideo() const;
|
||||
|
||||
protected:
|
||||
// VideoDecoder API
|
||||
void updateVolume();
|
||||
void updateBalance();
|
||||
void pauseVideoIntern(bool pause);
|
||||
|
||||
private:
|
||||
void queuePage(ogg_page *page);
|
||||
bool queueAudio();
|
||||
int bufferData();
|
||||
void translateYUVtoRGBA(th_ycbcr_buffer &YUVBuffer);
|
||||
|
||||
Common::SeekableReadStream *_fileStream;
|
||||
Graphics::Surface _surface;
|
||||
Graphics::Surface _displaySurface;
|
||||
Common::Rational _frameRate;
|
||||
double _nextFrameStartTime;
|
||||
bool _endOfVideo;
|
||||
bool _endOfAudio;
|
||||
|
||||
Audio::Mixer::SoundType _soundType;
|
||||
Audio::SoundHandle *_audHandle;
|
||||
Audio::QueuingAudioStream *_audStream;
|
||||
|
||||
ogg_sync_state _oggSync;
|
||||
ogg_page _oggPage;
|
||||
ogg_packet _oggPacket;
|
||||
ogg_stream_state _vorbisOut;
|
||||
ogg_stream_state _theoraOut;
|
||||
th_info _theoraInfo;
|
||||
th_comment _theoraComment;
|
||||
th_dec_ctx *_theoraDecode;
|
||||
th_setup_info *_theoraSetup;
|
||||
vorbis_info _vorbisInfo;
|
||||
vorbis_dsp_state _vorbisDSP;
|
||||
vorbis_block _vorbisBlock;
|
||||
vorbis_comment _vorbisComment;
|
||||
|
||||
int _theoraPacket;
|
||||
int _vorbisPacket;
|
||||
|
||||
int _ppLevelMax;
|
||||
int _ppLevel;
|
||||
int _ppInc;
|
||||
|
||||
// single audio fragment audio buffering
|
||||
int _audiobufFill;
|
||||
bool _audiobufReady;
|
||||
ogg_int16_t *_audiobuf;
|
||||
};
|
||||
|
||||
} // End of namespace Sword25
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
Loading…
x
Reference in New Issue
Block a user