Browse Source

Split theora::VideoStream into OggDemuxer and TheoraVideoStream

This makes the internal API more modular, but the split also forces a cleaner
implementation. It also means the OggDemuxer is now responsible for the
ogg-specific stuff like seeking and (ogg-specific) type detection.

Backported from the love.video-vpx branch of love-experiments

--HG--
branch : minor
Bart van Strien 7 years ago
parent
commit
0e9f71b29a

+ 4 - 2
CMakeLists.txt

@@ -1048,8 +1048,10 @@ set(LOVE_SRC_MODULE_VIDEO_ROOT
 set(LOVE_SRC_MODULE_VIDEO_THEORA
 set(LOVE_SRC_MODULE_VIDEO_THEORA
 	src/modules/video/theora/Video.cpp
 	src/modules/video/theora/Video.cpp
 	src/modules/video/theora/Video.h
 	src/modules/video/theora/Video.h
-	src/modules/video/theora/VideoStream.cpp
-	src/modules/video/theora/VideoStream.h
+	src/modules/video/theora/OggDemuxer.cpp
+	src/modules/video/theora/OggDemuxer.h
+	src/modules/video/theora/TheoraVideoStream.cpp
+	src/modules/video/theora/TheoraVideoStream.h
 )
 )
 
 
 set(LOVE_SRC_MODULE_VIDEO
 set(LOVE_SRC_MODULE_VIDEO

+ 242 - 0
src/modules/video/theora/OggDemuxer.cpp

@@ -0,0 +1,242 @@
+/**
+ * Copyright (c) 2006-2016 LOVE Development Team
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ *    claim that you wrote the original software. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ **/
+
+#include "OggDemuxer.h"
+
+namespace love
+{
+namespace video
+{
+namespace theora
+{
+
+OggDemuxer::OggDemuxer(love::filesystem::File *file)
+	: file(file)
+	, streamInited(false)
+	, videoSerial(0)
+{
+	ogg_sync_init(&sync);
+}
+
+OggDemuxer::~OggDemuxer()
+{
+	if (streamInited)
+		ogg_stream_clear(&stream);
+	ogg_sync_clear(&sync);
+}
+
+void OggDemuxer::readPage()
+{
+	char *syncBuffer = nullptr;
+	while (ogg_sync_pageout(&sync, &page) != 1)
+	{
+		if (syncBuffer && !streamInited && ogg_stream_check(&stream))
+			throw love::Exception("Invalid stream");
+
+		syncBuffer = ogg_sync_buffer(&sync, 8192);
+		size_t read = file->read(syncBuffer, 8192);
+		ogg_sync_wrote(&sync, read);
+	}
+}
+
+bool OggDemuxer::readPacket(ogg_packet &packet, bool mustSucceed)
+{
+	if (!streamInited)
+		throw love::Exception("Reading from OggDemuxer before initialization (engine bug)");
+
+	while (ogg_stream_packetout(&stream, &packet) != 1)
+	{
+		do
+		{
+			// We need to read another page, but there is none, we're at the end
+			if (ogg_page_eos(&page) && !mustSucceed)
+				return true;
+
+			readPage();
+		} while (ogg_page_serialno(&page) != videoSerial);
+
+		ogg_stream_pagein(&stream, &page);
+	}
+
+	return false;
+}
+
+void OggDemuxer::resync()
+{
+	ogg_sync_reset(&sync);
+	ogg_sync_pageseek(&sync, &page);
+	ogg_stream_reset(&stream);
+}
+
+bool OggDemuxer::isEos() const
+{
+	return ogg_page_eos(&page);
+}
+
+const std::string &OggDemuxer::getFilename() const
+{
+	return file->getFilename();
+}
+
+OggDemuxer::StreamType OggDemuxer::determineType()
+{
+	ogg_packet packet;
+	if (ogg_stream_packetpeek(&stream, &packet) != 1)
+		return TYPE_UNKNOWN;
+
+	// Theora
+	// See https://www.theora.org/doc/Theora.pdf section 6.1
+	if (packet.bytes >= 7) {
+		uint8_t headerType = packet.packet[0];
+		if (headerType & 0x80 && std::strncmp((const char*) packet.packet+1, "theora", 6) == 0)
+			return TYPE_THEORA;
+	}
+
+	return TYPE_UNKNOWN;
+}
+
+OggDemuxer::StreamType OggDemuxer::findStream()
+{
+	if (streamInited)
+	{
+		file->seek(0);
+		ogg_stream_clear(&stream);
+		ogg_sync_reset(&sync);
+	}
+
+	streamInited = true;
+	while (true)
+	{
+		// If this page isn't at the start of a stream, we've seen all streams
+		readPage();
+		if (!ogg_page_bos(&page))
+			break;
+
+		videoSerial = ogg_page_serialno(&page);
+		ogg_stream_init(&stream, videoSerial);
+		ogg_stream_pagein(&stream, &page);
+
+		StreamType type = determineType();
+		switch(type)
+		{
+		case TYPE_THEORA:
+			return type;
+		default:
+			break;
+		}
+
+		ogg_stream_clear(&stream);
+		ogg_sync_reset(&sync);
+	}
+
+	streamInited = false;
+	ogg_stream_clear(&stream);
+	ogg_sync_reset(&sync);
+
+	return TYPE_UNKNOWN;
+}
+
+bool OggDemuxer::seek(ogg_packet &packet, double target, std::function<double(int64)> getTime)
+{
+	static const double rewindThreshold = 0.01;
+
+	if (target < rewindThreshold)
+	{
+		file->seek(0);
+		resync();
+		readPacket(packet, true);
+		return true;
+	}
+
+	double low = 0;
+	double high = file->getSize();
+
+	while (high-low > rewindThreshold)
+	{
+		// Determine our next binary search position
+		double pos = (high+low)/2;
+		file->seek(pos);
+
+		// Break sync
+		resync();
+
+		// Read a page
+		readPage();
+		readPacket(packet, false);
+		if (isEos())
+		{
+			// EOS, so we're definitely past our target (or the target is past
+			// the end)
+			high = pos;
+
+			// And a workaround for single-page files:
+			if (high < rewindThreshold)
+			{
+				file->seek(0);
+				resync();
+				readPacket(packet, true);
+			}
+			else
+				continue;
+		}
+
+		// Now search all packets in this page
+		int result = -1;
+		for (int i = 0; i < ogg_page_packets(&page); ++i)
+		{
+			if (i > 0)
+				readPacket(packet, true);
+
+			// Determine if this is the right place
+			double curTime = getTime(packet.granulepos);
+			double nextTime = getTime(packet.granulepos+1);
+
+			if (curTime == -1)
+				continue; // Invalid granule position (magic?)
+			else if (curTime <= target && nextTime > target)
+			{
+				// the current frame should be displaying right now
+				result = 0;
+				break;
+			}
+			else if (curTime > target)
+			{
+				// No need to check the other packets, they're all past
+				// this one
+				result = 1;
+				break;
+			}
+		}
+
+		// The sign of result determines the direction
+		if (result == 0)
+			break;
+		else if (result < 0)
+			low = pos;
+		else
+			high = pos;
+	}
+
+	return true;
+}
+
+} // theora
+} // video
+} // love

+ 77 - 0
src/modules/video/theora/OggDemuxer.h

@@ -0,0 +1,77 @@
+/**
+ * Copyright (c) 2006-2016 LOVE Development Team
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ *    claim that you wrote the original software. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ **/
+
+#ifndef LOVE_VIDEO_THEORA_OGGDEMUXER_H
+#define LOVE_VIDEO_THEORA_OGGDEMUXER_H
+
+// STL
+#include <functional>
+
+// LOVE
+#include "filesystem/File.h"
+
+// OGG
+#include <ogg/ogg.h>
+
+namespace love
+{
+namespace video
+{
+namespace theora
+{
+
+class OggDemuxer
+{
+public:
+	enum StreamType
+	{
+		TYPE_THEORA,
+		TYPE_UNKNOWN,
+	};
+
+	OggDemuxer(love::filesystem::File *file);
+	~OggDemuxer();
+
+	StreamType findStream();
+	bool readPacket(ogg_packet &packet, bool mustSucceed = false);
+	void resync();
+	bool isEos() const;
+	const std::string &getFilename() const;
+	bool seek(ogg_packet &packet, double target, std::function<double(int64)> getTime);
+
+private:
+	StrongRef<love::filesystem::File> file;
+
+	ogg_sync_state sync;
+	ogg_stream_state stream;
+	ogg_page page;
+
+	bool streamInited;
+	int videoSerial;
+
+	void readPage();
+	StreamType determineType();
+}; // OggDemuxer
+
+} // theora
+} // video
+} // love
+
+#endif // LOVE_VIDEO_THEORA_OGGDEMUXER_H

+ 32 - 178
src/modules/video/theora/VideoStream.cpp → src/modules/video/theora/TheoraVideoStream.cpp

@@ -22,7 +22,7 @@
 #include <iostream>
 #include <iostream>
 
 
 // LOVE
 // LOVE
-#include "VideoStream.h"
+#include "TheoraVideoStream.h"
 
 
 using love::filesystem::File;
 using love::filesystem::File;
 
 
@@ -33,19 +33,18 @@ namespace video
 namespace theora
 namespace theora
 {
 {
 
 
-VideoStream::VideoStream(love::filesystem::File *file)
-	: file(file)
+TheoraVideoStream::TheoraVideoStream(love::filesystem::File *file)
+	: demuxer(file)
 	, headerParsed(false)
 	, headerParsed(false)
-	, streamInited(false)
-	, videoSerial(0)
 	, decoder(nullptr)
 	, decoder(nullptr)
 	, frameReady(false)
 	, frameReady(false)
 	, lastFrame(0)
 	, lastFrame(0)
 	, nextFrame(0)
 	, nextFrame(0)
-	, eos(false)
 	, lagCounter(0)
 	, lagCounter(0)
 {
 {
-	ogg_sync_init(&sync);
+	if (demuxer.findStream() != OggDemuxer::TYPE_THEORA)
+		throw love::Exception("Invalid video file, video is not theora");
+
 	th_info_init(&videoInfo);
 	th_info_init(&videoInfo);
 
 
 	frontBuffer = new Frame();
 	frontBuffer = new Frame();
@@ -60,29 +59,24 @@ VideoStream::VideoStream(love::filesystem::File *file)
 		delete backBuffer;
 		delete backBuffer;
 		delete frontBuffer;
 		delete frontBuffer;
 		th_info_clear(&videoInfo);
 		th_info_clear(&videoInfo);
-		ogg_sync_clear(&sync);
 		throw ex;
 		throw ex;
 	}
 	}
 
 
 	frameSync.set(new DeltaSync(), Acquire::NORETAIN);
 	frameSync.set(new DeltaSync(), Acquire::NORETAIN);
 }
 }
 
 
-VideoStream::~VideoStream()
+TheoraVideoStream::~TheoraVideoStream()
 {
 {
 	if (decoder)
 	if (decoder)
 		th_decode_free(decoder);
 		th_decode_free(decoder);
 
 
 	th_info_clear(&videoInfo);
 	th_info_clear(&videoInfo);
-	if (headerParsed)
-		ogg_stream_clear(&stream);
-
-	ogg_sync_clear(&sync);
 
 
 	delete frontBuffer;
 	delete frontBuffer;
 	delete backBuffer;
 	delete backBuffer;
 }
 }
 
 
-int VideoStream::getWidth() const
+int TheoraVideoStream::getWidth() const
 {
 {
 	if (headerParsed)
 	if (headerParsed)
 		return videoInfo.pic_width;
 		return videoInfo.pic_width;
@@ -90,7 +84,7 @@ int VideoStream::getWidth() const
 		return 0;
 		return 0;
 }
 }
 
 
-int VideoStream::getHeight() const
+int TheoraVideoStream::getHeight() const
 {
 {
 	if (headerParsed)
 	if (headerParsed)
 		return videoInfo.pic_height;
 		return videoInfo.pic_height;
@@ -98,72 +92,30 @@ int VideoStream::getHeight() const
 		return 0;
 		return 0;
 }
 }
 
 
-const std::string &VideoStream::getFilename() const
+const std::string &TheoraVideoStream::getFilename() const
 {
 {
-	return file->getFilename();
+	return demuxer.getFilename();
 }
 }
 
 
-void VideoStream::setSync(FrameSync *frameSync)
+void TheoraVideoStream::setSync(FrameSync *frameSync)
 {
 {
 	love::thread::Lock l(bufferMutex);
 	love::thread::Lock l(bufferMutex);
 	this->frameSync = frameSync;
 	this->frameSync = frameSync;
 }
 }
 
 
-const void *VideoStream::getFrontBuffer() const
+const void *TheoraVideoStream::getFrontBuffer() const
 {
 {
 	return frontBuffer;
 	return frontBuffer;
 }
 }
 
 
-size_t VideoStream::getSize() const
+size_t TheoraVideoStream::getSize() const
 {
 {
 	return sizeof(Frame);
 	return sizeof(Frame);
 }
 }
 
 
-bool VideoStream::isPlaying() const
-{
-	return frameSync->isPlaying() && !eos;
-}
-
-void VideoStream::readPage()
+bool TheoraVideoStream::isPlaying() const
 {
 {
-	char *syncBuffer = nullptr;
-	while (ogg_sync_pageout(&sync, &page) != 1)
-	{
-		if (syncBuffer && !headerParsed && ogg_stream_check(&stream))
-			throw love::Exception("Invalid stream");
-
-		syncBuffer = ogg_sync_buffer(&sync, 8192);
-		size_t read = file->read(syncBuffer, 8192);
-		ogg_sync_wrote(&sync, read);
-	}
-}
-
-bool VideoStream::readPacket(bool mustSucceed)
-{
-	if (!streamInited)
-	{
-		readPage();
-		videoSerial = ogg_page_serialno(&page);
-		ogg_stream_init(&stream, videoSerial);
-		streamInited = true;
-		ogg_stream_pagein(&stream, &page);
-	}
-
-	while (ogg_stream_packetout(&stream, &packet) != 1)
-	{
-		do
-		{
-			// We need to read another page, but there is none, we're at the end
-			if (ogg_page_eos(&page) && !mustSucceed)
-				return eos = true;
-
-			readPage();
-		} while (ogg_page_serialno(&page) != videoSerial);
-
-		ogg_stream_pagein(&stream, &page);
-	}
-
-	return false;
+	return frameSync->isPlaying() && !demuxer.isEos();
 }
 }
 
 
 template<typename T>
 template<typename T>
@@ -181,7 +133,7 @@ inline void scaleFormat(th_pixel_fmt fmt, T &x, T &y)
 	}
 	}
 }
 }
 
 
-void VideoStream::parseHeader()
+void TheoraVideoStream::parseHeader()
 {
 {
 	if (headerParsed)
 	if (headerParsed)
 		return;
 		return;
@@ -191,16 +143,8 @@ void VideoStream::parseHeader()
 	th_comment_init(&comment);
 	th_comment_init(&comment);
 	int ret;
 	int ret;
 
 
-	do
-	{
-		readPacket();
-		ret = th_decode_headerin(&videoInfo, &comment, &setupInfo, &packet);
-		if (ret == TH_ENOTFORMAT)
-		{
-			ogg_stream_clear(&stream);
-			streamInited = false;
-		}
-	} while(ret < 0 && !ogg_page_eos(&page));
+	demuxer.readPacket(packet);
+	ret = th_decode_headerin(&videoInfo, &comment, &setupInfo, &packet);
 
 
 	if (ret < 0)
 	if (ret < 0)
 	{
 	{
@@ -210,7 +154,7 @@ void VideoStream::parseHeader()
 
 
 	while (ret > 0)
 	while (ret > 0)
 	{
 	{
-		readPacket();
+		demuxer.readPacket(packet);
 		ret = th_decode_headerin(&videoInfo, &comment, &setupInfo, &packet);
 		ret = th_decode_headerin(&videoInfo, &comment, &setupInfo, &packet);
 	}
 	}
 
 
@@ -246,111 +190,21 @@ void VideoStream::parseHeader()
 	th_decode_packetin(decoder, &packet, nullptr);
 	th_decode_packetin(decoder, &packet, nullptr);
 }
 }
 
 
-// Arbitrary seeking isn't supported yet, but rewinding is
-void VideoStream::rewind()
-{
-	// Seek our data stream back to the start
-	file->seek(0);
-
-	// Break our sync, and discard the rest of the page
-	ogg_sync_reset(&sync);
-	ogg_sync_pageseek(&sync, &page);
-
-	// Read our first page/packet from the stream again
-	readPacket(true);
-
-	// Now tell theora we're at frame 1 (not 0!)
-	int64 granPos = 1;
-	th_decode_ctl(decoder, TH_DECCTL_SET_GRANPOS, &granPos, sizeof(granPos));
-
-	// Force a redraw, since this will always be less than the sync's position
-	lastFrame = nextFrame = -1;
-	eos = false;
-}
-
-void VideoStream::seekDecoder(double target)
+void TheoraVideoStream::seekDecoder(double target)
 {
 {
-	const double rewindThreshold = 0.01;
+	bool success = demuxer.seek(packet, target, [this](int64 granulepos) {
+		return th_granule_time(decoder, granulepos);
+	});
 
 
-	if (target < rewindThreshold)
-	{
-		rewind();
+	if (!success)
 		return;
 		return;
-	}
-
-	double low = 0;
-	double high = file->getSize();
-
-	while (high-low > rewindThreshold)
-	{
-		// Determine our next binary search position
-		double pos = (high+low)/2;
-		file->seek(pos);
-
-		// Break sync
-		ogg_sync_reset(&sync);
-		ogg_sync_pageseek(&sync, &page);
-
-		// Read a page
-		readPacket(false);
-		if (eos)
-		{
-			// EOS, so we're definitely past our target (or the target is past
-			// the end)
-			high = pos;
-			eos = false;
-
-			// And a workaround for single-page files:
-			if (high < rewindThreshold)
-				rewind();
-			else
-				continue;
-		}
-
-		// Now search all packets in this page
-		int result = -1;
-		for (int i = 0; i < ogg_page_packets(&page); ++i)
-		{
-			if (i > 0)
-				readPacket(true);
-
-			// Determine if this is the right place
-			double curTime = th_granule_time(decoder, packet.granulepos);
-			double nextTime = th_granule_time(decoder, packet.granulepos+1);
-
-			if (curTime == -1)
-				continue; // Invalid granule position (magic?)
-			else if (curTime <= target && nextTime > target)
-			{
-				// the current frame should be displaying right now
-				result = 0;
-				break;
-			}
-			else if (curTime > target)
-			{
-				// No need to check the other packets, they're all past
-				// this one
-				result = 1;
-				break;
-			}
-		}
-
-		// The sign of result determines the direction
-		if (result == 0)
-			break;
-		else if (result < 0)
-			low = pos;
-		else
-			high = pos;
-	}
 
 
 	// Now update theora and our decoder on this new position of ours
 	// Now update theora and our decoder on this new position of ours
 	lastFrame = nextFrame = -1;
 	lastFrame = nextFrame = -1;
-	eos = false;
 	th_decode_ctl(decoder, TH_DECCTL_SET_GRANPOS, &packet.granulepos, sizeof(packet.granulepos));
 	th_decode_ctl(decoder, TH_DECCTL_SET_GRANPOS, &packet.granulepos, sizeof(packet.granulepos));
 }
 }
 
 
-void VideoStream::threadedFillBackBuffer(double dt)
+void TheoraVideoStream::threadedFillBackBuffer(double dt)
 {
 {
 	// Synchronize
 	// Synchronize
 	frameSync->update(dt);
 	frameSync->update(dt);
@@ -362,7 +216,7 @@ void VideoStream::threadedFillBackBuffer(double dt)
 
 
 	// If we're at the end of the stream, or if we're displaying the right frame
 	// If we're at the end of the stream, or if we're displaying the right frame
 	// stop here
 	// stop here
-	if (eos || position < nextFrame)
+	if (demuxer.isEos() || position < nextFrame)
 		return;
 		return;
 
 
 	th_ycbcr_buffer bufferinfo;
 	th_ycbcr_buffer bufferinfo;
@@ -371,7 +225,7 @@ void VideoStream::threadedFillBackBuffer(double dt)
 	ogg_int64_t granulePosition;
 	ogg_int64_t granulePosition;
 	do
 	do
 	{
 	{
-		if (readPacket())
+		if (demuxer.readPacket(packet))
 			return;
 			return;
 	} while (th_decode_packetin(decoder, &packet, &granulePosition) != 0);
 	} while (th_decode_packetin(decoder, &packet, &granulePosition) != 0);
 	lastFrame = nextFrame;
 	lastFrame = nextFrame;
@@ -413,7 +267,7 @@ void VideoStream::threadedFillBackBuffer(double dt)
 	if (position > nextFrame)
 	if (position > nextFrame)
 	{
 	{
 		if (++lagCounter > 5)
 		if (++lagCounter > 5)
-			seek(position);
+			seekDecoder(position);
 	}
 	}
 	else
 	else
 		lagCounter = 0;
 		lagCounter = 0;
@@ -422,14 +276,14 @@ void VideoStream::threadedFillBackBuffer(double dt)
 	frameReady = true;
 	frameReady = true;
 }
 }
 
 
-void VideoStream::fillBackBuffer()
+void TheoraVideoStream::fillBackBuffer()
 {
 {
 	// Done in worker thread
 	// Done in worker thread
 }
 }
 
 
-bool VideoStream::swapBuffers()
+bool TheoraVideoStream::swapBuffers()
 {
 {
-	if (eos)
+	if (demuxer.isEos())
 		return false;
 		return false;
 
 
 	love::thread::Lock l(bufferMutex);
 	love::thread::Lock l(bufferMutex);

+ 7 - 14
src/modules/video/theora/VideoStream.h → src/modules/video/theora/TheoraVideoStream.h

@@ -27,6 +27,7 @@
 #include "common/int.h"
 #include "common/int.h"
 #include "filesystem/File.h"
 #include "filesystem/File.h"
 #include "thread/threads.h"
 #include "thread/threads.h"
+#include "OggDemuxer.h"
 
 
 // OGG/Theora
 // OGG/Theora
 #include <ogg/ogg.h>
 #include <ogg/ogg.h>
@@ -40,11 +41,11 @@ namespace video
 namespace theora
 namespace theora
 {
 {
 
 
-class VideoStream : public love::video::VideoStream
+class TheoraVideoStream : public love::video::VideoStream
 {
 {
 public:
 public:
-	VideoStream(love::filesystem::File *file);
-	~VideoStream();
+	TheoraVideoStream(love::filesystem::File *file);
+	~TheoraVideoStream();
 
 
 	const void *getFrontBuffer() const;
 	const void *getFrontBuffer() const;
 	size_t getSize() const;
 	size_t getSize() const;
@@ -61,14 +62,10 @@ public:
 	void threadedFillBackBuffer(double dt);
 	void threadedFillBackBuffer(double dt);
 
 
 private:
 private:
-	StrongRef<love::filesystem::File> file;
+	OggDemuxer demuxer;
 
 
 	bool headerParsed;
 	bool headerParsed;
-	bool streamInited;
-	int videoSerial;
-	ogg_sync_state sync;
-	ogg_stream_state stream;
-	ogg_page page;
+
 	ogg_packet packet;
 	ogg_packet packet;
 
 
 	th_info videoInfo;
 	th_info videoInfo;
@@ -86,15 +83,11 @@ private:
 
 
 	double lastFrame;
 	double lastFrame;
 	double nextFrame;
 	double nextFrame;
-	bool eos;
 	unsigned int lagCounter;
 	unsigned int lagCounter;
 
 
-	void readPage();
-	bool readPacket(bool mustSucceed = false); // true if eos
 	void parseHeader();
 	void parseHeader();
-	void rewind();
 	void seekDecoder(double target);
 	void seekDecoder(double target);
-}; // VideoStream
+}; // TheoraVideoStream
 
 
 } // theora
 } // theora
 } // video
 } // video

+ 3 - 3
src/modules/video/theora/Video.cpp

@@ -46,7 +46,7 @@ Video::~Video()
 
 
 VideoStream *Video::newVideoStream(love::filesystem::File *file)
 VideoStream *Video::newVideoStream(love::filesystem::File *file)
 {
 {
-	VideoStream *stream = new VideoStream(file);
+	TheoraVideoStream *stream = new TheoraVideoStream(file);
 	workerThread->addStream(stream);
 	workerThread->addStream(stream);
 	return stream;
 	return stream;
 }
 }
@@ -67,7 +67,7 @@ Worker::~Worker()
 	stop();
 	stop();
 }
 }
 
 
-void Worker::addStream(VideoStream *stream)
+void Worker::addStream(TheoraVideoStream *stream)
 {
 {
 	love::thread::Lock l(mutex);
 	love::thread::Lock l(mutex);
 	streams.push_back(stream);
 	streams.push_back(stream);
@@ -110,7 +110,7 @@ void Worker::threadFunction()
 
 
 		for (auto it = streams.begin(); it != streams.end(); ++it)
 		for (auto it = streams.begin(); it != streams.end(); ++it)
 		{
 		{
-			VideoStream *stream = *it;
+			TheoraVideoStream *stream = *it;
 			if (stream->getReferenceCount() == 1)
 			if (stream->getReferenceCount() == 1)
 			{
 			{
 				// We're the only ones left
 				// We're the only ones left

+ 4 - 3
src/modules/video/theora/Video.h

@@ -28,7 +28,8 @@
 #include "filesystem/File.h"
 #include "filesystem/File.h"
 #include "video/Video.h"
 #include "video/Video.h"
 #include "thread/threads.h"
 #include "thread/threads.h"
-#include "VideoStream.h"
+#include "video/VideoStream.h"
+#include "TheoraVideoStream.h"
 
 
 namespace love
 namespace love
 {
 {
@@ -63,13 +64,13 @@ public:
 	// Implements Threadable
 	// Implements Threadable
 	void threadFunction();
 	void threadFunction();
 
 
-	void addStream(VideoStream *stream);
+	void addStream(TheoraVideoStream *stream);
 	// Frees itself!
 	// Frees itself!
 	void stop();
 	void stop();
 
 
 private:
 private:
 
 
-	std::vector<StrongRef<VideoStream>> streams;
+	std::vector<StrongRef<TheoraVideoStream>> streams;
 
 
 	love::thread::MutexRef mutex;
 	love::thread::MutexRef mutex;
 	love::thread::ConditionalRef cond;
 	love::thread::ConditionalRef cond;