[Scummvm-cvs-logs] scummvm master -> d1f907485ecb8feccdd7522e9949d1a705a69874

clone2727 clone2727 at gmail.com
Wed May 18 16:08:16 CEST 2011


This automated email contains information about 6 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
f559741bfc SWORD25: Fix a/v sync with using the TheoraDecoder standalone
0addffbfd3 GRAPHICS: Add a YUV to RGB table lookup for use with Theora
f8323cc672 SWORD25: Make Theora handle the case when the packet eos is not set
affb6a38a1 GRAPHICS: Add some docs and sanity checks to the YUV to RGB code
14e1cc728f SWORD25: Properly use endOfVideo()
d1f907485e SWORD25: Implement TheoraDecoder::pauseVideoIntern()


Commit: f559741bfc0024b772724201b7e764441fee13c2
    https://github.com/scummvm/scummvm/commit/f559741bfc0024b772724201b7e764441fee13c2
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2011-05-18T07:05:13-07:00

Commit Message:
SWORD25: Fix a/v sync with using the TheoraDecoder standalone

Hopefully should fix a/v sync from in-game. If not, the engine needs to be changed slightly

Changed paths:
    engines/sword25/fmv/theora_decoder.cpp
    engines/sword25/fmv/theora_decoder.h



diff --git a/engines/sword25/fmv/theora_decoder.cpp b/engines/sword25/fmv/theora_decoder.cpp
index f7e5e7a..4f5a006 100644
--- a/engines/sword25/fmv/theora_decoder.cpp
+++ b/engines/sword25/fmv/theora_decoder.cpp
@@ -59,6 +59,7 @@ TheoraDecoder::TheoraDecoder(Audio::Mixer::SoundType soundType) {
 	_vorbisPacket = 0;
 	_theoraDecode = 0;
 	_theoraSetup = 0;
+	_nextFrameStartTime = 0.0;
 
 	_soundType = soundType;
 	_audStream = 0;
@@ -99,6 +100,8 @@ int TheoraDecoder::bufferData() {
 bool TheoraDecoder::loadStream(Common::SeekableReadStream *stream) {
 	close();
 
+	_endOfAudio = false;
+	_endOfVideo = false;
 	_fileStream = stream;
 
 	// start up Ogg stream synchronization layer
@@ -272,17 +275,26 @@ bool TheoraDecoder::loadStream(Common::SeekableReadStream *stream) {
 		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);
 	} else {
 		// tear down the partial vorbis setup
 		vorbis_info_clear(&_vorbisInfo);
 		vorbis_comment_clear(&_vorbisComment);
-	}
-
-	// open audio
-	if (_vorbisPacket) {
-		_audStream = Audio::makeQueuingAudioStream(_vorbisInfo.rate, _vorbisInfo.channels);
-		if (_audStream)
-			g_system->getMixer()->playStream(_soundType, _audHandle, _audStream);
+		_endOfAudio = true;
 	}
 
 	_surface = new Graphics::Surface();
@@ -332,49 +344,8 @@ void TheoraDecoder::close() {
 }
 
 const Graphics::Surface *TheoraDecoder::decodeNextFrame() {
-	int i, j;
-
-	// 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) / _vorbisInfo.channels) >> 1;
-			for (i = 0; i < ret && i < maxsamples; i++)
-				for (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)
-				_audiobufReady = true;
-
-#if ENABLE_THEORA_SEEKING
-			if (_vorbisDSP.granulepos >= 0)
-				_audiobufGranulePos = _vorbisDSP.granulepos - ret + i;
-			else
-				_audiobufGranulePos += i;
-#endif
-		} 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 && !_theoraOut.e_o_s) {
+	// First, let's get our frame
+	while (_theoraPacket) {
 		// theora is one in, one out...
 		if (ogg_stream_packetout(&_theoraOut, &_oggPacket) > 0) {
 
@@ -384,22 +355,7 @@ const Graphics::Surface *TheoraDecoder::decodeNextFrame() {
 				_ppInc = 0;
 			}
 
-#if ENABLE_THEORA_SEEKING
-			// 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);
-#else
 			if (th_decode_packetin(_theoraDecode, &_oggPacket, NULL) == 0) {
-#endif
 				_curFrame++;
 
 				// Convert YUV data to RGB data
@@ -410,33 +366,103 @@ const Graphics::Surface *TheoraDecoder::decodeNextFrame() {
 				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) {
+				_endOfVideo = true;
+				break;
+			}
+
 			// Queue more data
 			bufferData();
 			while (ogg_sync_pageout(&_oggSync, &_oggPage) > 0)
 				queuePage(&_oggPage);
 		}
+
+		// Update audio if we can
+		queueAudio();
 	}
 
-	// If playback has begun, top audio buffer off immediately.
-	if (_audiobufReady) {
-		_audStream->queueBuffer((byte *)_audiobuf, AUDIOFD_FRAGSIZE, DisposeAfterUse::NO, Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN | Audio::FLAG_STEREO);
+	// 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);
 
-		// 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));
-		_audiobufFill = 0;
-		_audiobufReady = false;
+		bool queuedAudio = queueAudio();
+		if (_vorbisOut.e_o_s && !queuedAudio) {
+			_endOfAudio = true;
+			break;
+		}
 	}
 
 	return _surface;
 }
 
+bool TheoraDecoder::queueAudio() {
+	if (!_audStream)
+		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) {
+				_audStream->queueBuffer((byte *)_audiobuf, AUDIOFD_FRAGSIZE, DisposeAfterUse::NO, Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN | Audio::FLAG_STEREO);
+
+				// 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));
+				_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() {
-	FixedRateVideoDecoder::reset();
+	VideoDecoder::reset();
 
 	// FIXME: This does a rewind() instead of a reset()!
 
@@ -459,14 +485,27 @@ void TheoraDecoder::reset() {
 }
 
 bool TheoraDecoder::endOfVideo() const {
-	return !isVideoLoaded() || _theoraOut.e_o_s;
+	return !isVideoLoaded() || (_endOfVideo && (!_audStream || (_audStream->endOfData() && _endOfAudio)));
+}
+
+uint32 TheoraDecoder::getTimeToNextFrame() const {
+	if (endOfVideo() || _curFrame < 0)
+		return 0;
+
+	uint32 elapsedTime = getElapsedTime();
+	uint32 nextFrameStartTime = (uint32)(_nextFrameStartTime * 1000);
+
+	if (nextFrameStartTime <= elapsedTime)
+		return 0;
+
+	return nextFrameStartTime - elapsedTime;
 }
 
 uint32 TheoraDecoder::getElapsedTime() const {
 	if (_audStream)
 		return g_system->getMixer()->getSoundElapsedTime(*_audHandle);
 
-	return FixedRateVideoDecoder::getElapsedTime();
+	return VideoDecoder::getElapsedTime();
 }
 
 static void convertYUVtoBGRA(int y, int u, int v, byte *dst, Graphics::PixelFormat format) {
diff --git a/engines/sword25/fmv/theora_decoder.h b/engines/sword25/fmv/theora_decoder.h
index a1ce3ae..4a646f1 100644
--- a/engines/sword25/fmv/theora_decoder.h
+++ b/engines/sword25/fmv/theora_decoder.h
@@ -41,8 +41,6 @@ namespace Common {
 class SeekableReadStream;
 }
 
-//#define ENABLE_THEORA_SEEKING		// enables the extra calculations used for video seeking
-
 namespace Sword25 {
 
 /**
@@ -51,7 +49,7 @@ namespace Sword25 {
  * Video decoder used in engines:
  *  - sword25
  */
-class TheoraDecoder : public Video::FixedRateVideoDecoder {
+class TheoraDecoder : public Video::VideoDecoder {
 public:
 	TheoraDecoder(Audio::Mixer::SoundType soundType = Audio::Mixer::kMusicSoundType);
 	virtual ~TheoraDecoder();
@@ -83,16 +81,14 @@ public:
 	}
 
 	Graphics::PixelFormat getPixelFormat() const { return _surface->format; }
-
 	uint32 getElapsedTime() const;
+	uint32 getTimeToNextFrame() const;
 
 	bool endOfVideo() const;
 
-protected:
-	Common::Rational getFrameRate() const {	return _frameRate; }
-
 private:
 	void queuePage(ogg_page *page);
+	bool queueAudio();
 	int bufferData();
 	void translateYUVtoRGBA(th_ycbcr_buffer &YUVBuffer);
 
@@ -100,7 +96,9 @@ private:
 	Common::SeekableReadStream *_fileStream;
 	Graphics::Surface *_surface;
 	Common::Rational _frameRate;
-	uint32 _frameCount;
+	double _nextFrameStartTime;
+	bool _endOfVideo;
+	bool _endOfAudio;
 
 	Audio::Mixer::SoundType _soundType;
 	Audio::SoundHandle *_audHandle;
@@ -131,12 +129,6 @@ private:
 	int _audiobufFill;
 	bool _audiobufReady;
 	ogg_int16_t *_audiobuf;
-
-#if ENABLE_THEORA_SEEKING
-	double _videobufTime;
-	ogg_int64_t  _videobufGranulePos;
-	ogg_int64_t  _audiobufGranulePos; // time position of last sample
-#endif
 };
 
 } // End of namespace Sword25


Commit: 0addffbfd3952f766e7f839c210465c9c34b94bd
    https://github.com/scummvm/scummvm/commit/0addffbfd3952f766e7f839c210465c9c34b94bd
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2011-05-18T07:05:13-07:00

Commit Message:
GRAPHICS: Add a YUV to RGB table lookup for use with Theora

Based on the video/mpeg_player.* one, which is based on lots of other things (too many to name, go see the file)

Changed paths:
  A graphics/yuv_to_rgb.cpp
  A graphics/yuv_to_rgb.h
    engines/sword25/fmv/theora_decoder.cpp
    graphics/module.mk



diff --git a/engines/sword25/fmv/theora_decoder.cpp b/engines/sword25/fmv/theora_decoder.cpp
index 4f5a006..9aa7ead 100644
--- a/engines/sword25/fmv/theora_decoder.cpp
+++ b/engines/sword25/fmv/theora_decoder.cpp
@@ -39,7 +39,8 @@
 #ifdef USE_THEORADEC
 #include "common/system.h"
 #include "common/textconsole.h"
-#include "graphics/conversion.h"
+#include "common/util.h"
+#include "graphics/yuv_to_rgb.h"
 #include "audio/decoders/raw.h"
 #include "sword25/kernel/common.h"
 
@@ -508,16 +509,6 @@ uint32 TheoraDecoder::getElapsedTime() const {
 	return VideoDecoder::getElapsedTime();
 }
 
-static void convertYUVtoBGRA(int y, int u, int v, byte *dst, Graphics::PixelFormat format) {
-	byte r, g, b;
-	Graphics::YUV2RGB(y, u, v, r, g, b);
-
-	if (format.bytesPerPixel == 2)
-		*((uint16 *)dst) = format.RGBToColor(r, g, b);
-	else
-		*((uint32 *)dst) = format.RGBToColor(r, g, b);
-}
-
 enum TheoraYUVBuffers {
 	kBufferY = 0,
 	kBufferU = 1,
@@ -537,40 +528,7 @@ void TheoraDecoder::translateYUVtoRGBA(th_ycbcr_buffer &YUVBuffer) {
 	assert(YUVBuffer[kBufferU].height == YUVBuffer[kBufferY].height >> 1);
 	assert(YUVBuffer[kBufferV].height == YUVBuffer[kBufferY].height >> 1);
 
-	const byte *ySrc = YUVBuffer[kBufferY].data;
-	const byte *uSrc = YUVBuffer[kBufferU].data;
-	const byte *vSrc = YUVBuffer[kBufferV].data;
-	byte *dst  = (byte *)_surface->pixels;
-	int u = 0, v = 0;
-
-	const int blockSize = YUVBuffer[kBufferY].width * getPixelFormat().bytesPerPixel;
-	const int halfHeight = YUVBuffer[kBufferY].height >> 1;
-	const int halfWidth = YUVBuffer[kBufferY].width >> 1;
-	const int yStep = (YUVBuffer[kBufferY].stride << 1) - YUVBuffer[kBufferY].width;
-	// The UV step is usually 0, since in most cases stride == width.
-	// The asserts at the top ensure that the U and V steps are equal
-	// (and they must always be equal)
-	const int uvStep = YUVBuffer[kBufferU].stride - YUVBuffer[kBufferU].width;
-	const int stride = YUVBuffer[kBufferY].stride;
-
-	for (int h = 0; h < halfHeight; ++h) {
-		for (int w = 0; w < halfWidth; ++w) {
-			u = *uSrc++;
-			v = *vSrc++;
-
-			for (int i = 0; i <= 1; i++) {
-				convertYUVtoBGRA(*ySrc, u, v, dst, getPixelFormat());
-				convertYUVtoBGRA(*(ySrc + stride), u, v, dst + blockSize, getPixelFormat());
-				ySrc++;
-				dst += 4;	// BGRA
-			}
-		}
-
-		dst   += blockSize;
-		ySrc  += yStep;
-		uSrc  += uvStep;
-		vSrc  += uvStep;
-	}
+	Graphics::convertYUV420ToRGB(_surface, YUVBuffer[kBufferY].data, YUVBuffer[kBufferU].data, YUVBuffer[kBufferV].data, YUVBuffer[kBufferY].width, YUVBuffer[kBufferY].height, YUVBuffer[kBufferY].stride, YUVBuffer[kBufferU].stride);
 }
 
 } // End of namespace Sword25
diff --git a/graphics/module.mk b/graphics/module.mk
index 59621dc..a9051c8 100644
--- a/graphics/module.mk
+++ b/graphics/module.mk
@@ -25,7 +25,8 @@ MODULE_OBJS := \
 	thumbnail.o \
 	VectorRenderer.o \
 	VectorRendererSpec.o \
-	wincursor.o
+	wincursor.o \
+	yuv_to_rgb.o
 
 ifdef USE_SCALERS
 MODULE_OBJS += \
diff --git a/graphics/yuv_to_rgb.cpp b/graphics/yuv_to_rgb.cpp
new file mode 100644
index 0000000..b1107a7
--- /dev/null
+++ b/graphics/yuv_to_rgb.cpp
@@ -0,0 +1,240 @@
+/* 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$
+ *
+ */
+
+// The YUV to RGB conversion code is derived from SDL's YUV overlay code, which
+// in turn appears to be derived from mpeg_play. The following copyright
+// notices have been included in accordance with the original license. Please
+// note that the term "software" in this context only applies to the
+// buildLookup() and plotYUV*() functions below.
+
+// Copyright (c) 1995 The Regents of the University of California.
+// All rights reserved.
+//
+// Permission to use, copy, modify, and distribute this software and its
+// documentation for any purpose, without fee, and without written agreement is
+// hereby granted, provided that the above copyright notice and the following
+// two paragraphs appear in all copies of this software.
+//
+// IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+// DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+// OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+// CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+// ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+// PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+
+// Copyright (c) 1995 Erik Corry
+// All rights reserved.
+//
+// Permission to use, copy, modify, and distribute this software and its
+// documentation for any purpose, without fee, and without written agreement is
+// hereby granted, provided that the above copyright notice and the following
+// two paragraphs appear in all copies of this software.
+//
+// IN NO EVENT SHALL ERIK CORRY BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
+// SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF
+// THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF ERIK CORRY HAS BEEN ADVISED
+// OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ERIK CORRY SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+// PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS"
+// BASIS, AND ERIK CORRY HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT,
+// UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+
+// Portions of this software Copyright (c) 1995 Brown University.
+// All rights reserved.
+//
+// Permission to use, copy, modify, and distribute this software and its
+// documentation for any purpose, without fee, and without written agreement
+// is hereby granted, provided that the above copyright notice and the
+// following two paragraphs appear in all copies of this software.
+//
+// IN NO EVENT SHALL BROWN UNIVERSITY BE LIABLE TO ANY PARTY FOR
+// DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+// OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF BROWN
+// UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// BROWN UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+// PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS"
+// BASIS, AND BROWN UNIVERSITY HAS NO OBLIGATION TO PROVIDE MAINTENANCE,
+// SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+
+#include "common/scummsys.h"
+#include "common/singleton.h"
+
+#include "graphics/surface.h"
+
+namespace Graphics {
+
+class YUVToRGBLookup {
+public:
+	YUVToRGBLookup(Graphics::PixelFormat format);
+	~YUVToRGBLookup();
+
+	int16 *_colorTab;
+	uint32 *_rgbToPix;
+};
+
+YUVToRGBLookup::YUVToRGBLookup(Graphics::PixelFormat format) {
+	_colorTab = new int16[4 * 256]; // 2048 bytes
+
+	int16 *Cr_r_tab = &_colorTab[0 * 256];
+	int16 *Cr_g_tab = &_colorTab[1 * 256];
+	int16 *Cb_g_tab = &_colorTab[2 * 256];
+	int16 *Cb_b_tab = &_colorTab[3 * 256];
+
+	_rgbToPix = new uint32[3 * 768]; // 9216 bytes
+
+	uint32 *r_2_pix_alloc = &_rgbToPix[0 * 768];
+	uint32 *g_2_pix_alloc = &_rgbToPix[1 * 768];
+	uint32 *b_2_pix_alloc = &_rgbToPix[2 * 768];
+
+	int16 CR, CB;
+	int i;
+
+	// Generate the tables for the display surface
+
+	for (i = 0; i < 256; i++) {
+		// Gamma correction (luminescence table) and chroma correction
+		// would be done here. See the Berkeley mpeg_play sources.
+
+		CR = CB = (i - 128);
+		Cr_r_tab[i] = (int16) ( (0.419 / 0.299) * CR) + 0 * 768 + 256;
+		Cr_g_tab[i] = (int16) (-(0.299 / 0.419) * CR) + 1 * 768 + 256;
+		Cb_g_tab[i] = (int16) (-(0.114 / 0.331) * CB);
+		Cb_b_tab[i] = (int16) ( (0.587 / 0.331) * CB) + 2 * 768 + 256;
+	}
+
+	// Set up entries 0-255 in rgb-to-pixel value tables.
+	for (i = 0; i < 256; i++) {
+		r_2_pix_alloc[i + 256] = format.RGBToColor(i, 0, 0);
+		g_2_pix_alloc[i + 256] = format.RGBToColor(0, i, 0);
+		b_2_pix_alloc[i + 256] = format.RGBToColor(0, 0, i);
+	}
+
+	// Spread out the values we have to the rest of the array so that we do
+	// not need to check for overflow.
+	for (i = 0; i < 256; i++) {
+		r_2_pix_alloc[i] = r_2_pix_alloc[256];
+		r_2_pix_alloc[i + 512] = r_2_pix_alloc[511];
+		g_2_pix_alloc[i] = g_2_pix_alloc[256];
+		g_2_pix_alloc[i + 512] = g_2_pix_alloc[511];
+		b_2_pix_alloc[i] = b_2_pix_alloc[256];
+		b_2_pix_alloc[i + 512] = b_2_pix_alloc[511];
+	}
+}
+
+YUVToRGBLookup::~YUVToRGBLookup() {
+	delete[] _rgbToPix;
+	delete[] _colorTab;
+}
+
+class YUVToRGBManager : public Common::Singleton<YUVToRGBManager> {
+public:
+	const YUVToRGBLookup *getLookup(Graphics::PixelFormat format);
+
+private:
+	friend class Common::Singleton<SingletonBaseType>;
+	YUVToRGBManager();
+	~YUVToRGBManager();
+
+	Graphics::PixelFormat _lastFormat;
+	YUVToRGBLookup *_lookup;
+};
+
+YUVToRGBManager::YUVToRGBManager() {
+	_lookup = 0;
+}
+
+YUVToRGBManager::~YUVToRGBManager() {
+	delete _lookup;
+}
+
+const YUVToRGBLookup *YUVToRGBManager::getLookup(Graphics::PixelFormat format) {
+	if (_lastFormat == format)
+		return _lookup;
+
+	delete _lookup;
+	_lookup = new YUVToRGBLookup(format);
+	_lastFormat = format;
+	return _lookup;
+}
+
+} // End of namespace Graphics
+
+DECLARE_SINGLETON(Graphics::YUVToRGBManager);
+
+#define YUVToRGBMan (Graphics::YUVToRGBManager::instance())
+
+namespace Graphics {
+
+#define PUT_PIXEL(s, d) \
+	L = &lookup->_rgbToPix[(s)]; \
+	if (dst->format.bytesPerPixel == 2) \
+		*((uint16 *)(d)) = (L[cr_r] | L[crb_g] | L[cb_b]); \
+	else \
+		*((uint32 *)(d)) = (L[cr_r] | L[crb_g] | L[cb_b])
+
+void convertYUV420ToRGB(Graphics::Surface *dst, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) {
+	const YUVToRGBLookup *lookup = YUVToRGBMan.getLookup(dst->format);
+
+	byte *dstPtr = (byte *)dst->pixels;
+
+	int halfHeight = yHeight >> 1;
+	int halfWidth = yWidth >> 1;
+
+	for (int h = 0; h < halfHeight; h++) {
+		for (int w = 0; w < halfWidth; w++) {
+			register uint32 *L;
+
+			int16 cr_r  = lookup->_colorTab[*vSrc + 0 * 256];
+			int16 crb_g = lookup->_colorTab[*vSrc + 1 * 256] + lookup->_colorTab[*uSrc + 2 * 256];
+			int16 cb_b  = lookup->_colorTab[*uSrc + 3 * 256];
+			++uSrc;
+			++vSrc;
+
+			PUT_PIXEL(*ySrc, dstPtr);
+			PUT_PIXEL(*(ySrc + yPitch), dstPtr + dst->pitch);
+			ySrc++;
+			dstPtr += dst->format.bytesPerPixel;
+			PUT_PIXEL(*ySrc, dstPtr);
+			PUT_PIXEL(*(ySrc + yPitch), dstPtr + dst->pitch);
+			ySrc++;
+			dstPtr += dst->format.bytesPerPixel;
+		}
+
+		dstPtr += dst->pitch;
+		ySrc += (yPitch << 1) - yWidth;
+		uSrc += uvPitch - halfWidth;
+		vSrc += uvPitch - halfWidth;
+	}
+}
+
+} // End of namespace Graphics
diff --git a/graphics/yuv_to_rgb.h b/graphics/yuv_to_rgb.h
new file mode 100644
index 0000000..30f64ee
--- /dev/null
+++ b/graphics/yuv_to_rgb.h
@@ -0,0 +1,40 @@
+/* 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 GRAPHICS_YUV_TO_RGB_H
+#define GRAPHICS_YUV_TO_RGB_H
+
+#include "common/scummsys.h"
+#include "graphics/surface.h"
+
+namespace Graphics {
+
+struct Surface;
+
+void convertYUV420ToRGB(Graphics::Surface *dst, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch);
+
+} // End of namespace Graphics
+
+#endif


Commit: f8323cc67230b64de5af93624f57aba4020fecdf
    https://github.com/scummvm/scummvm/commit/f8323cc67230b64de5af93624f57aba4020fecdf
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2011-05-18T07:05:13-07:00

Commit Message:
SWORD25: Make Theora handle the case when the packet eos is not set

Changed paths:
    engines/sword25/fmv/theora_decoder.cpp



diff --git a/engines/sword25/fmv/theora_decoder.cpp b/engines/sword25/fmv/theora_decoder.cpp
index 9aa7ead..7957cb0 100644
--- a/engines/sword25/fmv/theora_decoder.cpp
+++ b/engines/sword25/fmv/theora_decoder.cpp
@@ -383,7 +383,7 @@ const Graphics::Surface *TheoraDecoder::decodeNextFrame() {
 			}
 		} else {
 			// If we can't get any more frames, we're done.
-			if (_theoraOut.e_o_s) {
+			if (_theoraOut.e_o_s || _fileStream->eos()) {
 				_endOfVideo = true;
 				break;
 			}
@@ -406,7 +406,7 @@ const Graphics::Surface *TheoraDecoder::decodeNextFrame() {
 			queuePage(&_oggPage);
 
 		bool queuedAudio = queueAudio();
-		if (_vorbisOut.e_o_s && !queuedAudio) {
+		if ((_vorbisOut.e_o_s  || _fileStream->eos()) && !queuedAudio) {
 			_endOfAudio = true;
 			break;
 		}


Commit: affb6a38a198d410922492026e5277794a310279
    https://github.com/scummvm/scummvm/commit/affb6a38a198d410922492026e5277794a310279
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2011-05-18T07:05:13-07:00

Commit Message:
GRAPHICS: Add some docs and sanity checks to the YUV to RGB code

Changed paths:
    graphics/yuv_to_rgb.cpp
    graphics/yuv_to_rgb.h



diff --git a/graphics/yuv_to_rgb.cpp b/graphics/yuv_to_rgb.cpp
index b1107a7..831736c 100644
--- a/graphics/yuv_to_rgb.cpp
+++ b/graphics/yuv_to_rgb.cpp
@@ -203,6 +203,13 @@ namespace Graphics {
 		*((uint32 *)(d)) = (L[cr_r] | L[crb_g] | L[cb_b])
 
 void convertYUV420ToRGB(Graphics::Surface *dst, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) {
+	// Sanity checks
+	assert(dst && dst->pixels);
+	assert(dst->format.bytesPerPixel == 2 || dst->format.bytesPerPixel == 4);
+	assert(ySrc && uSrc && vSrc);
+	assert((yWidth & 1) == 0);
+	assert((yHeight & 1) == 0);
+
 	const YUVToRGBLookup *lookup = YUVToRGBMan.getLookup(dst->format);
 
 	byte *dstPtr = (byte *)dst->pixels;
diff --git a/graphics/yuv_to_rgb.h b/graphics/yuv_to_rgb.h
index 30f64ee..9b561f2 100644
--- a/graphics/yuv_to_rgb.h
+++ b/graphics/yuv_to_rgb.h
@@ -23,6 +23,12 @@
  *
  */
 
+/**
+ * @file
+ * YUV to RGB conversion used in engines:
+ * - sword25
+ */
+
 #ifndef GRAPHICS_YUV_TO_RGB_H
 #define GRAPHICS_YUV_TO_RGB_H
 
@@ -33,6 +39,18 @@ namespace Graphics {
 
 struct Surface;
 
+/**
+ * Convert a YUV420 image to an RGB surface
+ *
+ * @param dst     the destination surface
+ * @param ySrc    the source of the y component
+ * @param uSrc    the source of the u component
+ * @param vSrc    the source of the v component
+ * @param yWidth  the width of the y surface (must be divisible by 2)
+ * @param yHeight the height of the y surface (must be divisible by 2)
+ * @param yPitch  the pitch of the y surface
+ * @param uvPitch the pitch of the u and v surfaces
+ */
 void convertYUV420ToRGB(Graphics::Surface *dst, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch);
 
 } // End of namespace Graphics


Commit: 14e1cc728f0e7233dfecce6c58ebfa319512152b
    https://github.com/scummvm/scummvm/commit/14e1cc728f0e7233dfecce6c58ebfa319512152b
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2011-05-18T07:05:14-07:00

Commit Message:
SWORD25: Properly use endOfVideo()

Changed paths:
    engines/sword25/fmv/movieplayer.cpp



diff --git a/engines/sword25/fmv/movieplayer.cpp b/engines/sword25/fmv/movieplayer.cpp
index c60f5d4..d267506 100644
--- a/engines/sword25/fmv/movieplayer.cpp
+++ b/engines/sword25/fmv/movieplayer.cpp
@@ -119,21 +119,23 @@ bool MoviePlayer::pause() {
 
 void MoviePlayer::update() {
 	if (_decoder.isVideoLoaded()) {
-		const Graphics::Surface *s = _decoder.decodeNextFrame();
-		if (s) {
-			// Transfer the next frame
-			assert(s->format.bytesPerPixel == 4);
+		if (_decoder.endOfVideo()) {
+			// Movie complete, so unload the movie
+			unloadMovie();
+		} else {
+			const Graphics::Surface *s = _decoder.decodeNextFrame();
+			if (s) {
+				// Transfer the next frame
+				assert(s->format.bytesPerPixel == 4);
 
 #ifdef THEORA_INDIRECT_RENDERING
-			byte *frameData = (byte *)s->getBasePtr(0, 0);
-			_outputBitmap->setContent(frameData, s->pitch * s->h, 0, s->pitch);
+				byte *frameData = (byte *)s->getBasePtr(0, 0);
+				_outputBitmap->setContent(frameData, s->pitch * s->h, 0, s->pitch);
 #else
-			g_system->copyRectToScreen((byte *)s->getBasePtr(0, 0), s->pitch, _outX, _outY, MIN(s->w, _backSurface->w), MIN(s->h, _backSurface->h));
-			g_system->updateScreen();
+				g_system->copyRectToScreen((byte *)s->getBasePtr(0, 0), s->pitch, _outX, _outY, MIN(s->w, _backSurface->w), MIN(s->h, _backSurface->h));
+				g_system->updateScreen();
 #endif
-		} else {
-			// Movie complete, so unload the movie
-			unloadMovie();
+			}
 		}
 	}
 }


Commit: d1f907485ecb8feccdd7522e9949d1a705a69874
    https://github.com/scummvm/scummvm/commit/d1f907485ecb8feccdd7522e9949d1a705a69874
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2011-05-18T07:05:14-07:00

Commit Message:
SWORD25: Implement TheoraDecoder::pauseVideoIntern()

Changed paths:
    engines/sword25/fmv/theora_decoder.cpp
    engines/sword25/fmv/theora_decoder.h



diff --git a/engines/sword25/fmv/theora_decoder.cpp b/engines/sword25/fmv/theora_decoder.cpp
index 7957cb0..07cb563 100644
--- a/engines/sword25/fmv/theora_decoder.cpp
+++ b/engines/sword25/fmv/theora_decoder.cpp
@@ -509,6 +509,11 @@ uint32 TheoraDecoder::getElapsedTime() const {
 	return VideoDecoder::getElapsedTime();
 }
 
+void TheoraDecoder::pauseVideoIntern(bool pause) {
+	if (_audStream)
+		g_system->getMixer()->pauseHandle(*_audHandle, pause);
+}
+
 enum TheoraYUVBuffers {
 	kBufferY = 0,
 	kBufferU = 1,
diff --git a/engines/sword25/fmv/theora_decoder.h b/engines/sword25/fmv/theora_decoder.h
index 4a646f1..10a0564 100644
--- a/engines/sword25/fmv/theora_decoder.h
+++ b/engines/sword25/fmv/theora_decoder.h
@@ -86,13 +86,15 @@ public:
 
 	bool endOfVideo() const;
 
+protected:
+	void pauseVideoIntern(bool pause);
+
 private:
 	void queuePage(ogg_page *page);
 	bool queueAudio();
 	int bufferData();
 	void translateYUVtoRGBA(th_ycbcr_buffer &YUVBuffer);
 
-private:
 	Common::SeekableReadStream *_fileStream;
 	Graphics::Surface *_surface;
 	Common::Rational _frameRate;






More information about the Scummvm-git-logs mailing list