WINTERMUTE: Update to use new TGA-decoder and new Video-system

This commit is contained in:
Einar Johan Trøan Sømåen 2012-08-31 13:49:38 +02:00
parent 3fe7f2cbe2
commit 16b27090b1
9 changed files with 17 additions and 954 deletions

View File

@ -32,8 +32,8 @@
#include "graphics/decoders/png.h"
#include "graphics/decoders/jpeg.h"
#include "graphics/decoders/bmp.h"
#include "graphics/decoders/tga.h"
#include "graphics/surface.h"
#include "engines/wintermute/graphics/tga.h"
#include "common/textconsole.h"
#include "common/stream.h"
#include "common/system.h"
@ -69,7 +69,7 @@ bool BaseImage::loadFile(const Common::String &filename) {
} else if (_filename.hasSuffix(".bmp")) {
_decoder = new Graphics::BitmapDecoder();
} else if (_filename.hasSuffix(".tga")) {
_decoder = new Wintermute::TGA();
_decoder = new Graphics::TGADecoder();
} else if (_filename.hasSuffix(".jpg")) {
_decoder = new Graphics::JPEGDecoder();
} else {

View File

@ -36,8 +36,8 @@
#include "graphics/decoders/png.h"
#include "graphics/decoders/bmp.h"
#include "graphics/decoders/jpeg.h"
#include "graphics/decoders/tga.h"
#include "engines/wintermute/graphics/transparent_surface.h"
#include "engines/wintermute/graphics/tga.h"
#include "graphics/pixelformat.h"
#include "graphics/surface.h"
#include "common/stream.h"

View File

@ -1,164 +0,0 @@
/* 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.
*/
/* Based on code from eos https://github.com/DrMcCoy/xoreos/
* relicensed under GPLv2+ with permission from DrMcCoy and clone2727
*/
#include "common/util.h"
#include "common/stream.h"
#include "common/textconsole.h"
#include "common/error.h"
#include "engines/wintermute/graphics/tga.h"
namespace Wintermute {
TGA::TGA() {
}
TGA::~TGA() {
destroy();
}
void TGA::destroy() {
_surface.free();
}
bool TGA::loadStream(Common::SeekableReadStream &tga) {
byte imageType, pixelDepth;
bool success;
success = readHeader(tga, imageType, pixelDepth);
success = readData(tga, imageType, pixelDepth);
if (tga.err() || !success) {
warning("Failed reading TGA-file");
return false;
}
return success;
}
bool TGA::readHeader(Common::SeekableReadStream &tga, byte &imageType, byte &pixelDepth) {
if (!tga.seek(0)) {
warning("Failed reading TGA-file");
return false;
}
// TGAs have an optional "id" string in the header
uint32 idLength = tga.readByte();
// Number of colors in the color map / palette
if (tga.readByte() != 0) {
warning("Unsupported feature: Color map");
return false;
}
// Image type. 2 == unmapped RGB, 3 == Grayscale
imageType = tga.readByte();
if ((imageType != 2) && (imageType != 3)) {
warning("Unsupported image type: %d", imageType);
return false;
}
// Color map specifications + X + Y
tga.skip(5 + 2 + 2);
// Image dimensions
_surface.w = tga.readUint16LE();
_surface.h = tga.readUint16LE();
// Bits per pixel
pixelDepth = tga.readByte();
_surface.format.bytesPerPixel = pixelDepth / 8;
if (imageType == 2) {
if (pixelDepth == 24) {
_hasAlpha = false;
_format = Graphics::PixelFormat(pixelDepth / 8, 8, 8, 8, 0, 16, 8, 0, 0);
} else if (pixelDepth == 16 || pixelDepth == 32) {
_hasAlpha = true;
_format = Graphics::PixelFormat(pixelDepth / 8, 8, 8, 8, 8, 24, 16, 8, 0);
} else {
warning("Unsupported pixel depth: %d, %d", imageType, pixelDepth);
return false;
}
} else if (imageType == 3) {
if (pixelDepth != 8) {
warning("Unsupported pixel depth: %d, %d", imageType, pixelDepth);
return false;
}
_hasAlpha = false;
_format = Graphics::PixelFormat(1, 0, 0, 0, 0, 0, 0, 0, 0);
}
// Image descriptor
tga.skip(1);
// Skip the id string
tga.skip(idLength);
return true;
}
bool TGA::readData(Common::SeekableReadStream &tga, byte imageType, byte pixelDepth) {
if (imageType == 2) {
_surface.create(_surface.w, _surface.h, _format);
if (pixelDepth == 16) {
// Convert from 16bpp to 32bpp
// 16bpp TGA is ARGB1555
uint16 count = _surface.w * _surface.h;
byte *dst = (byte *)_surface.pixels;
while (count--) {
uint16 pixel = tga.readUint16LE();
*dst++ = (pixel & 0x1F) << 3;
*dst++ = (pixel & 0x3E0) >> 2;
*dst++ = (pixel & 0x7C00) >> 7;
*dst++ = (pixel & 0x8000) ? 0xFF : 0x00;
}
} else {
// Read it in raw
tga.read(_surface.pixels, _surface.pitch * _surface.w);
}
} else if (imageType == 3) {
_surface.create(_surface.w, _surface.h, _surface.format);
byte *data = (byte *)_surface.pixels;
uint32 count = _surface.w * _surface.h;
while (count-- > 0) {
byte g = tga.readByte();
memset(data, g, 3);
data[3] = 0xFF;
data += 4;
}
}
return true;
}
} // End of namespace Graphics

View File

@ -1,59 +0,0 @@
/* 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.
*/
/* Based on code from eos https://github.com/DrMcCoy/xoreos/
* relicensed under GPLv2+ with permission from DrMcCoy and clone2727
*/
#ifndef WINTERMUTE_GRAPHICS_IMAGES_TGA_H
#define WINTERMUTE_GRAPHICS_IMAGES_TGA_H
#include "graphics/surface.h"
#include "graphics/decoders/image_decoder.h"
namespace Common {
class SeekableReadStream;
}
namespace Wintermute {
/** TarGa image. */
class TGA : public Graphics::ImageDecoder {
public:
TGA();
virtual ~TGA();
virtual void destroy();
virtual const Graphics::Surface *getSurface() const {
return &_surface;
};
virtual bool loadStream(Common::SeekableReadStream &stream);
private:
Graphics::PixelFormat _format;
bool _hasAlpha;
Graphics::Surface _surface;
// Loading helpers
bool readHeader(Common::SeekableReadStream &tga, byte &imageType, byte &pixelDepth);
bool readData(Common::SeekableReadStream &tga, byte imageType, byte pixelDepth);
};
} // End of namespace Graphics
#endif // GRAPHICS_IMAGES_TGA_H

View File

@ -87,7 +87,6 @@ MODULE_OBJS := \
base/saveload.o \
detection.o \
graphics/transparent_surface.o \
graphics/tga.o \
math/math_util.o \
math/matrix4.o \
math/vector2.o \
@ -109,7 +108,6 @@ MODULE_OBJS := \
utils/utils.o \
video/video_player.o \
video/video_theora_player.o \
video/decoders/theora_decoder.o \
wintermute.o \
persistent.o

View File

@ -1,570 +0,0 @@
/* 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

View File

@ -1,147 +0,0 @@
/* 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

View File

@ -35,7 +35,7 @@
#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 "video/theora_decoder.h"
#include "engines/wintermute/wintermute.h"
#include "common/system.h"
@ -126,7 +126,7 @@ bool VideoTheoraPlayer::initialize(const Common::String &filename, const Common:
}
#if defined (USE_THEORADEC)
_theoraDecoder = new TheoraDecoder();
_theoraDecoder = new Video::TheoraDecoder();
#else
return STATUS_FAILED;
#endif
@ -225,6 +225,8 @@ bool VideoTheoraPlayer::play(TVideoPlayback type, int x, int y, bool freezeGame,
_posY = (int)((_gameRef->_renderer->_height - height) / 2);
break;
}
_theoraDecoder->start();
return STATUS_OK;
#if 0 // Stubbed for now as theora isn't seekable
if (StartTime) SeekToTime(StartTime);
@ -274,11 +276,14 @@ bool VideoTheoraPlayer::update() {
}
}
if (_state == THEORA_STATE_PLAYING) {
if (_theoraDecoder->getTimeToNextFrame() == 0) {
_surface.free();
_surface.copyFrom(*_theoraDecoder->decodeNextFrame());
if (_texture) {
writeVideo();
if (!_theoraDecoder->endOfVideo() && _theoraDecoder->getTimeToNextFrame() == 0) {
const Graphics::Surface *decodedFrame = _theoraDecoder->decodeNextFrame();
if (decodedFrame) {
_surface.free();
_surface.copyFrom(*decodedFrame);
if (_texture) {
writeVideo();
}
}
}
return STATUS_OK;
@ -333,7 +338,7 @@ bool VideoTheoraPlayer::writeVideo() {
return STATUS_OK;
}
void VideoTheoraPlayer::writeAlpha() { // TODO: Endian-fix.
void VideoTheoraPlayer::writeAlpha() {
if (_alphaImage && _surface.w == _alphaImage->getSurface()->w && _surface.h == _alphaImage->getSurface()->h) {
assert(_alphaImage->getSurface()->format.bytesPerPixel == 4);
assert(_surface.format.bytesPerPixel == 4);

View File

@ -46,7 +46,7 @@ private:
THEORA_STATE_PAUSED = 2,
THEORA_STATE_FINISHED = 3
};
Video::RewindableVideoDecoder *_theoraDecoder;
Video::VideoDecoder *_theoraDecoder;
Graphics::Surface _surface;
public:
DECLARE_PERSISTENT(VideoTheoraPlayer, BaseClass)