aboutsummaryrefslogtreecommitdiff
path: root/engines/wintermute/video
diff options
context:
space:
mode:
Diffstat (limited to 'engines/wintermute/video')
-rw-r--r--engines/wintermute/video/decoders/theora_decoder.cpp570
-rw-r--r--engines/wintermute/video/decoders/theora_decoder.h147
-rw-r--r--engines/wintermute/video/video_player.cpp109
-rw-r--r--engines/wintermute/video/video_player.h90
-rw-r--r--engines/wintermute/video/video_theora_player.cpp500
-rw-r--r--engines/wintermute/video/video_theora_player.h148
6 files changed, 1564 insertions, 0 deletions
diff --git a/engines/wintermute/video/decoders/theora_decoder.cpp b/engines/wintermute/video/decoders/theora_decoder.cpp
new file mode 100644
index 0000000000..6267e300f9
--- /dev/null
+++ b/engines/wintermute/video/decoders/theora_decoder.cpp
@@ -0,0 +1,570 @@
+/* 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::YES, 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());
+}
+
+void TheoraDecoder::rewind() {
+ reset();
+}
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/wintermute/video/decoders/theora_decoder.h b/engines/wintermute/video/decoders/theora_decoder.h
new file mode 100644
index 0000000000..fd94f52142
--- /dev/null
+++ b/engines/wintermute/video/decoders/theora_decoder.h
@@ -0,0 +1,147 @@
+/* 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
+ * - wintermute
+ */
+class TheoraDecoder : public Video::RewindableVideoDecoder {
+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;
+
+ void rewind();
+
+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
diff --git a/engines/wintermute/video/video_player.cpp b/engines/wintermute/video/video_player.cpp
new file mode 100644
index 0000000000..2577b8aedc
--- /dev/null
+++ b/engines/wintermute/video/video_player.cpp
@@ -0,0 +1,109 @@
+/* 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 "engines/wintermute/video/video_player.h"
+
+namespace Wintermute {
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+
+//////////////////////////////////////////////////////////////////////////
+VideoPlayer::VideoPlayer(BaseGame *inGame) : BaseClass(inGame) {
+ setDefaults();
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool VideoPlayer::setDefaults() {
+ _playing = false;
+ _videoEndTime = 0;
+ _soundAvailable = false;
+ _startTime = 0;
+ _totalVideoTime = 0;
+ _playPosX = _playPosY = 0;
+ _playZoom = 0.0f;
+
+ _filename = NULL;
+
+ _slowRendering = false;
+
+ _currentSubtitle = 0;
+ _showSubtitle = false;
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+VideoPlayer::~VideoPlayer() {
+ cleanup();
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool VideoPlayer::cleanup() {
+ return 0;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool VideoPlayer::initialize(const char *inFilename, const char *subtitleFile) {
+ warning("VideoPlayer: %s %s - Not implemented yet", inFilename, subtitleFile);
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool VideoPlayer::update() {
+ return 0;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool VideoPlayer::display() {
+ return 0;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool VideoPlayer::play(TVideoPlayback type, int x, int y, bool freezeMusic) {
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool VideoPlayer::stop() {
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool VideoPlayer::isPlaying() {
+ return _playing;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool VideoPlayer::loadSubtitles(const char *filename, const char *subtitleFile) {
+ return STATUS_OK;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/video/video_player.h b/engines/wintermute/video/video_player.h
new file mode 100644
index 0000000000..d5466da679
--- /dev/null
+++ b/engines/wintermute/video/video_player.h
@@ -0,0 +1,90 @@
+/* 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 "engines/wintermute/dctypes.h" // Added by ClassView
+#include "engines/wintermute/base/base.h"
+
+#define MAX_AUDIO_STREAMS 5
+#define MAX_VIDEO_STREAMS 5
+
+
+namespace Wintermute {
+
+// AVI-Video-player, currently fully stubbed
+class VideoPlayer : public BaseClass {
+public:
+ bool _showSubtitle;
+ int _currentSubtitle;
+ bool loadSubtitles(const char *filename, const char *subtitleFile);
+ bool _slowRendering;
+ bool isPlaying();
+ char *_filename;
+ bool stop();
+ bool play(TVideoPlayback Type = VID_PLAY_CENTER, int x = 0, int y = 0, bool freezeMusic = true);
+ uint32 _totalVideoTime;
+ uint32 _startTime;
+ //CVidRenderer *_vidRenderer;
+ //BaseSoundAVI *_sound;
+ bool _soundAvailable;
+ bool setDefaults();
+ bool _playing;
+ bool display();
+ bool update();
+ bool initialize(const char *inFilename, const char *subtitleFile = NULL);
+ bool cleanup();
+ VideoPlayer(BaseGame *inGame);
+ virtual ~VideoPlayer();
+
+ /*PAVIFILE _aviFile;
+
+ LONG _lastSample;
+
+ PAVISTREAM _audioStream;
+ PAVISTREAM _videoStream;
+
+ LPWAVEFORMAT _audioFormat;
+
+ LPBITMAPINFO _videoFormat;
+ PGETFRAME _videoPGF;*/
+ uint32 _videoEndTime;
+
+ int _playPosX;
+ int _playPosY;
+ float _playZoom;
+
+ /* LPBITMAPV4HEADER _targetFormat;
+
+ BaseArray<CVidSubtitle *, CVidSubtitle *> _subtitles;*/
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/video/video_theora_player.cpp b/engines/wintermute/video/video_theora_player.cpp
new file mode 100644
index 0000000000..d92a74610d
--- /dev/null
+++ b/engines/wintermute/video/video_theora_player.cpp
@@ -0,0 +1,500 @@
+/* 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 "engines/wintermute/video/video_theora_player.h"
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/base/base_file_manager.h"
+#include "engines/wintermute/base/gfx/osystem/base_surface_osystem.h"
+#include "engines/wintermute/base/gfx/base_image.h"
+#include "engines/wintermute/base/sound/base_sound_manager.h"
+#include "engines/wintermute/utils/utils.h"
+#include "engines/wintermute/platform_osystem.h"
+#include "engines/wintermute/video/decoders/theora_decoder.h"
+#include "engines/wintermute/wintermute.h"
+#include "common/system.h"
+
+namespace Wintermute {
+
+IMPLEMENT_PERSISTENT(VideoTheoraPlayer, false)
+
+//////////////////////////////////////////////////////////////////////////
+VideoTheoraPlayer::VideoTheoraPlayer(BaseGame *inGame) : BaseClass(inGame) {
+ SetDefaults();
+}
+
+//////////////////////////////////////////////////////////////////////////
+void VideoTheoraPlayer::SetDefaults() {
+
+ _file = NULL;
+ _filename = "";
+ _startTime = 0;
+ _looping = false;
+
+ _freezeGame = false;
+ _currentTime = 0;
+
+ _state = THEORA_STATE_NONE;
+
+ _videoFrameReady = false;
+ _audioFrameReady = false;
+ _videobufTime = 0;
+
+ _playbackStarted = false;
+ _dontDropFrames = false;
+
+ _texture = NULL;
+ _alphaImage = NULL;
+ _alphaFilename = "";
+
+ _frameRendered = false;
+
+ _seekingKeyframe = false;
+ _timeOffset = 0.0f;
+
+ _posX = _posY = 0;
+ _playbackType = VID_PLAY_CENTER;
+ _playZoom = 0.0f;
+
+ _savedState = THEORA_STATE_NONE;
+ _savedPos = 0;
+ _volume = 100;
+ _theoraDecoder = NULL;
+
+ // TODO: Add subtitles-support
+ //_subtitler = NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+VideoTheoraPlayer::~VideoTheoraPlayer(void) {
+ cleanup();
+// SAFE_DELETE(_subtitler);
+}
+
+//////////////////////////////////////////////////////////////////////////
+void VideoTheoraPlayer::cleanup() {
+ if (_file) {
+ BaseFileManager::getEngineInstance()->closeFile(_file);
+ _file = NULL;
+ }
+
+ _surface.free();
+ if (_theoraDecoder) {
+ _theoraDecoder->close();
+ }
+ delete _theoraDecoder;
+ _theoraDecoder = NULL;
+ delete _alphaImage;
+ _alphaImage = NULL;
+ delete _texture;
+ _texture = NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool VideoTheoraPlayer::initialize(const Common::String &filename, const Common::String &subtitleFile) {
+ cleanup();
+
+ _filename = filename;
+ _file = BaseFileManager::getEngineInstance()->openFile(filename, true, false);
+ if (!_file) {
+ return STATUS_FAILED;
+ }
+
+#if defined (USE_THEORADEC)
+ _theoraDecoder = new TheoraDecoder();
+#else
+ return STATUS_FAILED;
+#endif
+ _theoraDecoder->loadStream(_file);
+
+ if (!_theoraDecoder->isVideoLoaded()) {
+ return STATUS_FAILED;
+ }
+
+ _state = THEORA_STATE_PAUSED;
+
+ // Additional setup.
+ _surface.create(_theoraDecoder->getWidth(), _theoraDecoder->getHeight(), _theoraDecoder->getPixelFormat());
+ _texture = new BaseSurfaceOSystem(_gameRef);
+ _texture->create(_theoraDecoder->getWidth(), _theoraDecoder->getHeight());
+ _state = THEORA_STATE_PLAYING;
+ _playZoom = 100;
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool VideoTheoraPlayer::resetStream() {
+ warning("VidTheoraPlayer::resetStream - stubbed");
+#if 0 // Stubbed for now, as theora isn't seekable
+ 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 STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool VideoTheoraPlayer::play(TVideoPlayback type, int x, int y, bool freezeGame, bool freezeMusic, bool looping, uint32 startTime, float forceZoom, int volume) {
+ if (forceZoom < 0.0f) {
+ forceZoom = 100.0f;
+ }
+ if (volume < 0) {
+ _volume = _gameRef->_soundMgr->getVolumePercent(Audio::Mixer::kSFXSoundType);
+ } else {
+ _volume = volume;
+ }
+
+ _freezeGame = freezeGame;
+
+ if (!_playbackStarted && _freezeGame) {
+ _gameRef->freeze(freezeMusic);
+ }
+
+ _playbackStarted = false;
+ float width, height;
+ if (_theoraDecoder) {
+ _surface.free();
+ _surface.copyFrom(*_theoraDecoder->decodeNextFrame());
+ _state = THEORA_STATE_PLAYING;
+ _looping = looping;
+ _playbackType = type;
+
+ _startTime = startTime;
+ _volume = volume;
+ _posX = x;
+ _posY = y;
+ _playZoom = forceZoom;
+
+ width = (float)_theoraDecoder->getWidth();
+ height = (float)_theoraDecoder->getHeight();
+ } else {
+ width = (float)_gameRef->_renderer->_width;
+ height = (float)_gameRef->_renderer->_height;
+ }
+
+ switch (type) {
+ case VID_PLAY_POS:
+ _playZoom = forceZoom;
+ _posX = x;
+ _posY = y;
+ break;
+
+ case VID_PLAY_STRETCH: {
+ float zoomX = (float)((float)_gameRef->_renderer->_width / width * 100);
+ float zoomY = (float)((float)_gameRef->_renderer->_height / height * 100);
+ _playZoom = MIN(zoomX, zoomY);
+ _posX = (int)((_gameRef->_renderer->_width - width * (_playZoom / 100)) / 2);
+ _posY = (int)((_gameRef->_renderer->_height - height * (_playZoom / 100)) / 2);
+ }
+ break;
+
+ case VID_PLAY_CENTER:
+ _playZoom = 100.0f;
+ _posX = (int)((_gameRef->_renderer->_width - width) / 2);
+ _posY = (int)((_gameRef->_renderer->_height - height) / 2);
+ break;
+ }
+ return STATUS_OK;
+#if 0 // Stubbed for now as theora isn't seekable
+ if (StartTime) SeekToTime(StartTime);
+
+ Update();
+#endif
+ return STATUS_FAILED;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool VideoTheoraPlayer::stop() {
+ _theoraDecoder->close();
+ _state = THEORA_STATE_FINISHED;
+ if (_freezeGame) {
+ _gameRef->unfreeze();
+ }
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool VideoTheoraPlayer::update() {
+ _currentTime = _freezeGame ? _gameRef->_liveTimer : _gameRef->_timer;
+
+ if (!isPlaying()) {
+ return STATUS_OK;
+ }
+
+ if (_playbackStarted /*&& m_Sound && !m_Sound->IsPlaying()*/) {
+ return STATUS_OK;
+ }
+
+ if (_playbackStarted && !_freezeGame && _gameRef->_state == GAME_FROZEN) {
+ return STATUS_OK;
+ }
+
+ if (_theoraDecoder) {
+ if (_theoraDecoder->endOfVideo() && _looping) {
+ warning("Should loop movie %s", _filename.c_str());
+ _theoraDecoder->rewind();
+ } else if (_theoraDecoder->endOfVideo() && !_looping) {
+ debugC(kWintermuteDebugLog, "Finished movie %s", _filename.c_str());
+ _state = THEORA_STATE_FINISHED;
+ _playbackStarted = false;
+ if (_freezeGame) {
+ _gameRef->unfreeze();
+ }
+ }
+ if (_state == THEORA_STATE_PLAYING) {
+ if (_theoraDecoder->getTimeToNextFrame() == 0) {
+ _surface.free();
+ _surface.copyFrom(*_theoraDecoder->decodeNextFrame());
+ if (_texture) {
+ writeVideo();
+ }
+ }
+ return STATUS_OK;
+ }
+ }
+ // Skip the busy-loop?
+ if ((!_texture || !_videoFrameReady) && !_theoraDecoder->endOfVideo()) {
+ // end playback
+ if (!_looping) {
+ _state = THEORA_STATE_FINISHED;
+ if (_freezeGame) {
+ _gameRef->unfreeze();
+ }
+ return STATUS_OK;
+ } else {
+ resetStream();
+ return STATUS_OK;
+ }
+ }
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+uint32 VideoTheoraPlayer::getMovieTime() {
+ if (!_playbackStarted) {
+ return 0;
+ } else {
+ return _theoraDecoder->getTime();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool VideoTheoraPlayer::writeVideo() {
+ if (!_texture) {
+ return STATUS_FAILED;
+ }
+
+ _texture->startPixelOp();
+
+ writeAlpha();
+ if (_alphaImage) {
+ _texture->putSurface(_surface, true);
+ } else {
+ _texture->putSurface(_surface, false);
+ }
+
+ //RenderFrame(_texture, &yuv);
+
+ _texture->endPixelOp();
+ _videoFrameReady = true;
+ return STATUS_OK;
+}
+
+void VideoTheoraPlayer::writeAlpha() { // TODO: Endian-fix.
+ if (_alphaImage && _surface.w == _alphaImage->getSurface()->w && _surface.h == _alphaImage->getSurface()->h) {
+ assert(_alphaImage->getSurface()->format.bytesPerPixel == 4);
+ assert(_surface.format.bytesPerPixel == 4);
+ const byte *alphaData = (byte *)_alphaImage->getSurface()->getBasePtr(0, 0);
+#ifdef SCUMM_LITTLE_ENDIAN
+ int alphaPlace = (_alphaImage->getSurface()->format.aShift / 8);
+#else
+ int alphaPlace = 3 - (_alphaImage->getSurface()->format.aShift / 8);
+#endif
+ alphaData += alphaPlace;
+ byte *imgData = (byte *)_surface.getBasePtr(0, 0);
+#ifdef SCUMM_LITTLE_ENDIAN
+ imgData += (_surface.format.aShift / 8);
+#else
+ imgData += 3 - (_surface.format.aShift / 8);
+#endif
+ for (int i = 0; i < _surface.w * _surface.h; i++) {
+ *imgData = *alphaData;
+ alphaData += 4;
+ imgData += 4;
+ }
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool VideoTheoraPlayer::display(uint32 alpha) {
+ Rect32 rc;
+ bool res;
+
+ if (_texture && _videoFrameReady) {
+ BasePlatform::setRect(&rc, 0, 0, _texture->getWidth(), _texture->getHeight());
+ if (_playZoom == 100.0f) {
+ res = _texture->displayTrans(_posX, _posY, rc, alpha);
+ } else {
+ res = _texture->displayTransZoom(_posX, _posY, rc, _playZoom, _playZoom, alpha);
+ }
+ } else {
+ res = STATUS_FAILED;
+ }
+ // TODO: Add subtitles-support
+/* if (m_Subtitler && _gameRef->m_VideoSubtitles) {
+ m_Subtitler->display();
+ }*/
+
+ return res;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool VideoTheoraPlayer::setAlphaImage(const Common::String &filename) {
+ delete _alphaImage;
+ _alphaImage = new BaseImage();
+ if (!_alphaImage || DID_FAIL(_alphaImage->loadFile(filename))) {
+ delete _alphaImage;
+ _alphaImage = NULL;
+ _alphaFilename = "";
+ return STATUS_FAILED;
+ }
+
+ if (_alphaFilename != filename) {
+ _alphaFilename = filename;
+ }
+ //TODO: Conversion.
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+byte VideoTheoraPlayer::getAlphaAt(int x, int y) {
+ if (_alphaImage) {
+ return _alphaImage->getAlphaAt(x, y);
+ } else {
+ return 0xFF;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+inline int intlog(int num) {
+ int r = 0;
+ while (num > 0) {
+ num = num / 2;
+ r = r + 1;
+ }
+
+ return r;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool VideoTheoraPlayer::seekToTime(uint32 time) {
+ warning("VideoTheoraPlayer::SeekToTime(%d) - not supported", time);
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool VideoTheoraPlayer::pause() {
+ if (_state == THEORA_STATE_PLAYING) {
+ _state = THEORA_STATE_PAUSED;
+ _theoraDecoder->pauseVideo(true);
+ return STATUS_OK;
+ } else {
+ return STATUS_FAILED;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool VideoTheoraPlayer::resume() {
+ if (_state == THEORA_STATE_PAUSED) {
+ _state = THEORA_STATE_PLAYING;
+ _theoraDecoder->pauseVideo(false);
+ return STATUS_OK;
+ } else {
+ return STATUS_FAILED;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool VideoTheoraPlayer::persist(BasePersistenceManager *persistMgr) {
+ //BaseClass::persist(persistMgr);
+
+ if (persistMgr->getIsSaving()) {
+ _savedPos = getMovieTime() * 1000;
+ _savedState = _state;
+ } else {
+ SetDefaults();
+ }
+
+ persistMgr->transfer(TMEMBER(_gameRef));
+ persistMgr->transfer(TMEMBER(_savedPos));
+ persistMgr->transfer(TMEMBER(_savedState));
+ persistMgr->transfer(TMEMBER(_filename));
+ persistMgr->transfer(TMEMBER(_alphaFilename));
+ persistMgr->transfer(TMEMBER(_posX));
+ persistMgr->transfer(TMEMBER(_posY));
+ persistMgr->transfer(TMEMBER(_playZoom));
+ persistMgr->transfer(TMEMBER_INT(_playbackType));
+ persistMgr->transfer(TMEMBER(_looping));
+ persistMgr->transfer(TMEMBER(_volume));
+
+ if (!persistMgr->getIsSaving() && (_savedState != THEORA_STATE_NONE)) {
+ initializeSimple();
+ }
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool VideoTheoraPlayer::initializeSimple() {
+ if (DID_SUCCEED(initialize(_filename))) {
+ if (_alphaFilename != "") {
+ setAlphaImage(_alphaFilename);
+ }
+ play(_playbackType, _posX, _posY, false, false, _looping, _savedPos, _playZoom);
+ } else {
+ _state = THEORA_STATE_FINISHED;
+ }
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+BaseSurface *VideoTheoraPlayer::getTexture() {
+ return _texture;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/video/video_theora_player.h b/engines/wintermute/video/video_theora_player.h
new file mode 100644
index 0000000000..41b45d2289
--- /dev/null
+++ b/engines/wintermute/video/video_theora_player.h
@@ -0,0 +1,148 @@
+/* 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_VIDTHEORAPLAYER_H
+#define WINTERMUTE_VIDTHEORAPLAYER_H
+
+#include "engines/wintermute/base/base.h"
+#include "engines/wintermute/persistent.h"
+#include "video/video_decoder.h"
+#include "common/stream.h"
+#include "graphics/surface.h"
+
+namespace Wintermute {
+class BaseSurface;
+class BaseImage;
+class VideoTheoraPlayer : public BaseClass {
+private:
+ enum {
+ THEORA_STATE_NONE = 0,
+ THEORA_STATE_PLAYING = 1,
+ THEORA_STATE_PAUSED = 2,
+ THEORA_STATE_FINISHED = 3
+ };
+ Video::RewindableVideoDecoder *_theoraDecoder;
+ Graphics::Surface _surface;
+public:
+ DECLARE_PERSISTENT(VideoTheoraPlayer, BaseClass)
+
+ VideoTheoraPlayer(BaseGame *inGame);
+ virtual ~VideoTheoraPlayer(void);
+
+ // external objects
+ Common::SeekableReadStream *_file;
+ Common::String _filename;
+
+ BaseSurface *_texture;
+ //CVidSubtitler *_subtitler;
+
+ // control methods
+ bool initialize(const Common::String &filename, const Common::String &subtitleFile = NULL);
+ bool initializeSimple();
+ bool update();
+ bool 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);
+ bool stop();
+ bool display(uint32 alpha = 0xFFFFFFFF);
+
+ bool pause();
+ bool resume();
+
+ bool isPlaying() {
+ return _state == THEORA_STATE_PLAYING;
+ };
+ bool isFinished() {
+ return _state == THEORA_STATE_FINISHED;
+ };
+ bool isPaused() {
+ return _state == THEORA_STATE_PAUSED;
+ };
+
+ uint32 getMovieTime();
+
+ BaseSurface *getTexture();
+
+ int _state;
+ uint32 _startTime;
+
+ int _savedState;
+ uint32 _savedPos;
+
+
+ // alpha related
+ BaseImage *_alphaImage;
+ Common::String _alphaFilename;
+ bool setAlphaImage(const Common::String &filename);
+ __inline byte getAlphaAt(int x, int y);
+ void writeAlpha();
+
+ bool seekToTime(uint32 Time);
+
+
+ void cleanup();
+ bool resetStream();
+
+ // video properties
+ TVideoPlayback _playbackType;
+ int _posX;
+ int _posY;
+ float _playZoom;
+ int _volume;
+
+ bool _looping;
+ bool _dontDropFrames;
+ bool _freezeGame;
+ uint32 _currentTime;
+
+
+private:
+ // seeking support
+ bool _seekingKeyframe;
+ float _timeOffset;
+
+ bool _frameRendered;
+
+ bool getIsFrameReady() {
+ return _videoFrameReady;
+ }
+private:
+ bool _audioFrameReady;
+ bool _videoFrameReady;
+ float _videobufTime;
+
+ bool writeVideo();
+
+ bool _playbackStarted;
+
+ // helpers
+ void SetDefaults();
+
+};
+
+} // end of namespace Wintermute
+
+#endif