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