[Scummvm-cvs-logs] SF.net SVN: scummvm:[55203] scummvm/trunk/graphics/video

mthreepwood at users.sourceforge.net mthreepwood at users.sourceforge.net
Tue Jan 11 18:27:37 CET 2011


Revision: 55203
          http://scummvm.svn.sourceforge.net/scummvm/?rev=55203&view=rev
Author:   mthreepwood
Date:     2011-01-11 17:27:37 +0000 (Tue, 11 Jan 2011)

Log Message:
-----------
VIDEO: Add seeking capability to QuickTimeDecoder

Modified Paths:
--------------
    scummvm/trunk/graphics/video/qt_decoder.cpp
    scummvm/trunk/graphics/video/qt_decoder.h

Modified: scummvm/trunk/graphics/video/qt_decoder.cpp
===================================================================
--- scummvm/trunk/graphics/video/qt_decoder.cpp	2011-01-11 17:27:31 UTC (rev 55202)
+++ scummvm/trunk/graphics/video/qt_decoder.cpp	2011-01-11 17:27:37 UTC (rev 55203)
@@ -144,19 +144,127 @@
 	return codec->getPixelFormat();
 }
 
-void QuickTimeDecoder::rewind() {
-	VideoDecoder::reset();
+uint32 QuickTimeDecoder::findKeyFrame(uint32 frame) const {
+	for (int i = _streams[_videoStreamIndex]->keyframe_count - 1; i >= 0; i--)
+		if (_streams[_videoStreamIndex]->keyframes[i] <= frame)
+				return _streams[_videoStreamIndex]->keyframes[i];
+	
+	// If none found, we'll assume the requested frame is a key frame
+	return frame;
+}
+
+void QuickTimeDecoder::seekToFrame(uint32 frame) {
+	assert(_videoStreamIndex >= 0);
+	assert(frame < _streams[_videoStreamIndex]->nb_frames);
+
+	// Stop all audio (for now)
+	stopAudio();
+
+	// Track down the keyframe
+	_curFrame = findKeyFrame(frame) - 1;
+	while (_curFrame < (int32)frame - 1)
+		decodeNextFrame();
+
+	// Map out the starting point
 	_nextFrameStartTime = 0;
+	uint32 curFrame = 0;
 
-	// Restart the audio too
-	stopAudio();
+	for (int32 i = 0; i < _streams[_videoStreamIndex]->stts_count && curFrame < frame; i++) {
+		for (int32 j = 0; j < _streams[_videoStreamIndex]->stts_data[i].count && curFrame < frame; j++) {
+			curFrame++;
+			_nextFrameStartTime += _streams[_videoStreamIndex]->stts_data[i].duration;
+		}
+	}
+
+	// Adjust the video starting point
+	_startTime = g_system->getMillis() - _nextFrameStartTime;
+
+	// Adjust the audio starting point
 	if (_audioStreamIndex >= 0) {
-		_curAudioChunk = 0;
+		_audioStartOffset = VideoTimestamp(_nextFrameStartTime, _streams[_videoStreamIndex]->time_scale);
+
+		// Re-create the audio stream
 		STSDEntry *entry = &_streams[_audioStreamIndex]->stsdEntries[0];
 		_audStream = Audio::makeQueuingAudioStream(entry->sampleRate, entry->channels == 2);
+
+		// First, we need to track down what audio sample we need
+		uint32 curTime = 0;
+		uint sample = 0;
+		bool done = false;
+		for (int32 i = 0; i < _streams[_audioStreamIndex]->stts_count && !done; i++) {
+			for (int32 j = 0; j < _streams[_audioStreamIndex]->stts_data[i].count; j++) {
+				curTime += _streams[_audioStreamIndex]->stts_data[i].duration;
+
+				if (curTime > Graphics::VideoTimestamp(_nextFrameStartTime, _streams[_videoStreamIndex]->time_scale).getUnitsInScale(_streams[_audioStreamIndex]->time_scale)) {
+					done = true;
+					break;
+				}
+
+				sample++;
+			}
+		}
+
+		// Now to track down what chunk it's in
+		_curAudioChunk = 0;
+		uint32 totalSamples = 0;
+		for (uint32 i = 0; i < _streams[_audioStreamIndex]->sample_to_chunk_sz; i++, _curAudioChunk++) {			
+			int sampleToChunkIndex = -1;
+
+			for (uint32 j = 0; j < _streams[_audioStreamIndex]->sample_to_chunk_sz; j++)
+				if (i >= _streams[_audioStreamIndex]->sample_to_chunk[j].first)
+					sampleToChunkIndex = j;
+
+			assert(sampleToChunkIndex >= 0);
+
+			totalSamples += _streams[_audioStreamIndex]->sample_to_chunk[sampleToChunkIndex].count;
+
+			if (sample < totalSamples) {
+				totalSamples -= _streams[_audioStreamIndex]->sample_to_chunk[sampleToChunkIndex].count;
+				break;
+			}
+		}
+		
+		// Reposition the audio stream
+		readNextAudioChunk();
+		if (sample != totalSamples) {
+			// HACK: Skip a certain amount of samples from the stream
+			// (There's got to be a better way to do this!)
+			int16 *tempBuffer = new int16[sample - totalSamples];
+			_audStream->readBuffer(tempBuffer, sample - totalSamples);
+			delete[] tempBuffer;
+			debug(3, "Skipping %d audio samples", sample - totalSamples);
+		}
+		
+		// Restart the audio
+		startAudio();
 	}
+}
 
-	startAudio();
+void QuickTimeDecoder::seekToTime(VideoTimestamp time) {
+	// TODO: Audio-only seeking (or really, have QuickTime sounds)
+	if (_videoStreamIndex < 0)
+		error("Audio-only seeking not supported");
+
+	// Convert to the local time scale
+	uint32 localTime = time.getUnitsInScale(_streams[_videoStreamIndex]->time_scale);
+
+	// Try to find the last frame that should have been decoded
+	uint32 frame = 0;
+	uint32 totalDuration = 0;
+	bool done = false;
+
+	for (int32 i = 0; i < _streams[_videoStreamIndex]->stts_count && !done; i++) {
+		for (int32 j = 0; j < _streams[_videoStreamIndex]->stts_data[i].count; j++) {
+			totalDuration += _streams[_videoStreamIndex]->stts_data[i].duration;
+			if (localTime < totalDuration) {
+				done = true;
+				break;
+			}
+			frame++;
+		}
+	}
+
+	seekToFrame(frame);
 }
 
 Codec *QuickTimeDecoder::createCodec(uint32 codecTag, byte bitsPerPixel) {
@@ -174,10 +282,10 @@
 		return new SMCDecoder(getWidth(), getHeight());
 	} else if (codecTag == MKID_BE('SVQ1')) {
 		// Sorenson Video 1: Used by some Myst ME videos.
-		warning ("Sorenson Video 1 not yet supported");
+		warning("Sorenson Video 1 not yet supported");
 	} else if (codecTag == MKID_BE('SVQ3')) {
 		// Sorenson Video 3: Used by some Myst ME videos.
-		warning ("Sorenson Video 3 not yet supported");
+		warning("Sorenson Video 3 not yet supported");
 	} else if (codecTag == MKID_BE('jpeg')) {
 		// Motion JPEG: Used by some Myst ME 10th Anniversary videos.
 		return new JPEGDecoder();
@@ -185,7 +293,7 @@
 		// CDToons: Used by most of the Broderbund games.
 		return new CDToonsDecoder(getWidth(), getHeight());
 	} else {
-		warning ("Unsupported codec \'%s\'", tag2str(codecTag));
+		warning("Unsupported codec \'%s\'", tag2str(codecTag));
 	}
 
 	return NULL;
@@ -285,9 +393,9 @@
 
 uint32 QuickTimeDecoder::getElapsedTime() const {
 	if (_audStream)
-		return g_system->getMixer()->getSoundElapsedTime(_audHandle);
+		return g_system->getMixer()->getSoundElapsedTime(_audHandle) + _audioStartOffset.getUnitsInScale(1000);
 
-	return g_system->getMillis() - _startTime;
+	return VideoDecoder::getElapsedTime();
 }
 
 uint32 QuickTimeDecoder::getTimeToNextFrame() const {
@@ -310,7 +418,6 @@
 
 	_foundMOOV = false;
 	_numStreams = 0;
-	_partial = 0;
 	_videoStreamIndex = _audioStreamIndex = -1;
 	_startTime = 0;
 
@@ -348,7 +455,6 @@
 	_fd = stream;
 	_foundMOOV = false;
 	_numStreams = 0;
-	_partial = 0;
 	_videoStreamIndex = _audioStreamIndex = -1;
 	_startTime = 0;
 
@@ -407,6 +513,8 @@
 
 			startAudio();
 		}
+
+		_audioStartOffset = VideoTimestamp(0);
 	}
 
 	// Initialize video, if present
@@ -651,7 +759,6 @@
 	if (!sc)
 		return -1;
 
-	sc->sample_to_chunk_index = -1;
 	sc->codec_type = CODEC_TYPE_MOV_OTHER;
 	sc->start_time = 0; // XXX: check
 	_streams[_numStreams++] = sc;
@@ -985,10 +1092,10 @@
 		return -1;
 
 	for (uint32 i = 0; i < st->sample_to_chunk_sz; i++) {
-		st->sample_to_chunk[i].first = _fd->readUint32BE();
+		st->sample_to_chunk[i].first = _fd->readUint32BE() - 1;
 		st->sample_to_chunk[i].count = _fd->readUint32BE();
 		st->sample_to_chunk[i].id = _fd->readUint32BE();
-		//printf ("Sample to Chunk[%d]: First = %d, Count = %d\n", i, st->sample_to_chunk[i].first, st->sample_to_chunk[i].count);
+		//warning("Sample to Chunk[%d]: First = %d, Count = %d", i, st->sample_to_chunk[i].first, st->sample_to_chunk[i].count);
 	}
 
 	return 0;
@@ -1010,7 +1117,7 @@
 		return -1;
 
 	for (uint32 i = 0; i < st->keyframe_count; i++) {
-		st->keyframes[i] = _fd->readUint32BE();
+		st->keyframes[i] = _fd->readUint32BE() - 1; // Adjust here, the frames are based on 1
 		debug(6, "keyframes[%d] = %d", i, st->keyframes[i]);
 
 	}
@@ -1107,18 +1214,6 @@
 		st->chunk_offsets[i] = _fd->readUint32BE() - _beginOffset;
 	}
 
-	for (uint32 i = 0; i < _numStreams; i++) {
-		MOVStreamContext *sc2 = _streams[i];
-
-		if (sc2 && sc2->chunk_offsets) {
-			uint32 first = sc2->chunk_offsets[0];
-			uint32 last = sc2->chunk_offsets[sc2->chunk_count - 1];
-
-			if(first >= st->chunk_offsets[st->chunk_count - 1] || last <= st->chunk_offsets[0])
-				_ni = 1;
-		}
-	}
-
 	return 0;
 }
 
@@ -1175,7 +1270,7 @@
 		int32 sampleToChunkIndex = -1;
 
 		for (uint32 j = 0; j < _streams[_videoStreamIndex]->sample_to_chunk_sz; j++)
-			if (i >= _streams[_videoStreamIndex]->sample_to_chunk[j].first - 1)
+			if (i >= _streams[_videoStreamIndex]->sample_to_chunk[j].first)
 				sampleToChunkIndex = j;
 
 		if (sampleToChunkIndex < 0)
@@ -1273,12 +1368,52 @@
 	uint32 sampleCount = 0;
 
 	for (uint32 j = 0; j < _streams[_audioStreamIndex]->sample_to_chunk_sz; j++)
-		if (chunk >= (_streams[_audioStreamIndex]->sample_to_chunk[j].first - 1))
+		if (chunk >= _streams[_audioStreamIndex]->sample_to_chunk[j].first)
 			sampleCount = _streams[_audioStreamIndex]->sample_to_chunk[j].count;
 
 	return sampleCount;
 }
 
+void QuickTimeDecoder::readNextAudioChunk() {
+	STSDEntry *entry = &_streams[_audioStreamIndex]->stsdEntries[0];
+	Common::MemoryWriteStreamDynamic *wStream = new Common::MemoryWriteStreamDynamic();
+
+	_fd->seek(_streams[_audioStreamIndex]->chunk_offsets[_curAudioChunk]);
+
+	// First, we have to get the sample count
+	uint32 sampleCount = getAudioChunkSampleCount(_curAudioChunk);
+	assert(sampleCount);
+
+	// Then calculate the right sizes
+	while (sampleCount > 0) {
+		uint32 samples = 0, size = 0;
+
+		if (entry->samplesPerFrame >= 160) {
+			samples = entry->samplesPerFrame;
+			size = entry->bytesPerFrame;
+		} else if (entry->samplesPerFrame > 1) {
+			samples = MIN<uint32>((1024 / entry->samplesPerFrame) * entry->samplesPerFrame, sampleCount);
+			size = (samples / entry->samplesPerFrame) * entry->bytesPerFrame;
+		} else {
+			samples = MIN<uint32>(1024, sampleCount);
+			size = samples * _streams[_audioStreamIndex]->sample_size;
+		}
+
+		// Now, we read in the data for this data and output it
+		byte *data = (byte *)malloc(size);
+		_fd->read(data, size);
+		wStream->write(data, size);
+		free(data);
+		sampleCount -= samples;
+	}
+
+	// Now queue the buffer
+	_audStream->queueAudioStream(createAudioStream(new Common::MemoryReadStream(wStream->getData(), wStream->size(), DisposeAfterUse::YES)));
+	delete wStream;
+
+	_curAudioChunk++;
+}
+
 void QuickTimeDecoder::updateAudioBuffer() {
 	if (!_audStream)
 		return;
@@ -1302,42 +1437,8 @@
 	numberOfChunksNeeded += 3;
 
 	// Keep three streams in buffer so that if/when the first two end, it goes right into the next
-	for (; _audStream->numQueuedStreams() < numberOfChunksNeeded && _curAudioChunk < _streams[_audioStreamIndex]->chunk_count; _curAudioChunk++) {
-		Common::MemoryWriteStreamDynamic *wStream = new Common::MemoryWriteStreamDynamic();
-
-		_fd->seek(_streams[_audioStreamIndex]->chunk_offsets[_curAudioChunk]);
-
-		// First, we have to get the sample count
-		uint32 sampleCount = getAudioChunkSampleCount(_curAudioChunk);
-		assert(sampleCount);
-
-		// Then calculate the right sizes
-		while (sampleCount > 0) {
-			uint32 samples = 0, size = 0;
-
-			if (entry->samplesPerFrame >= 160) {
-				samples = entry->samplesPerFrame;
-				size = entry->bytesPerFrame;
-			} else if (entry->samplesPerFrame > 1) {
-				samples = MIN<uint32>((1024 / entry->samplesPerFrame) * entry->samplesPerFrame, sampleCount);
-				size = (samples / entry->samplesPerFrame) * entry->bytesPerFrame;
-			} else {
-				samples = MIN<uint32>(1024, sampleCount);
-				size = samples * _streams[_audioStreamIndex]->sample_size;
-			}
-
-			// Now, we read in the data for this data and output it
-			byte *data = (byte *)malloc(size);
-			_fd->read(data, size);
-			wStream->write(data, size);
-			free(data);
-			sampleCount -= samples;
-		}
-
-		// Now queue the buffer
-		_audStream->queueAudioStream(createAudioStream(new Common::MemoryReadStream(wStream->getData(), wStream->size(), DisposeAfterUse::YES)));
-		delete wStream;
-	}
+	while (_audStream->numQueuedStreams() < numberOfChunksNeeded && _curAudioChunk < _streams[_audioStreamIndex]->chunk_count)
+		readNextAudioChunk();
 }
 
 QuickTimeDecoder::STSDEntry::STSDEntry() {
@@ -1370,7 +1471,6 @@
 QuickTimeDecoder::MOVStreamContext::~MOVStreamContext() {
 	delete[] chunk_offsets;
 	delete[] stts_data;
-	delete[] ctts_data;
 	delete[] sample_to_chunk;
 	delete[] sample_sizes;
 	delete[] keyframes;

Modified: scummvm/trunk/graphics/video/qt_decoder.h
===================================================================
--- scummvm/trunk/graphics/video/qt_decoder.h	2011-01-11 17:27:31 UTC (rev 55202)
+++ scummvm/trunk/graphics/video/qt_decoder.h	2011-01-11 17:27:37 UTC (rev 55203)
@@ -52,7 +52,7 @@
 namespace Graphics {
 
 
-class QuickTimeDecoder : public RewindableVideoDecoder {
+class QuickTimeDecoder : public SeekableVideoDecoder {
 public:
 	QuickTimeDecoder();
 	virtual ~QuickTimeDecoder();
@@ -113,8 +113,9 @@
 	uint32 getTimeToNextFrame() const;
 	PixelFormat getPixelFormat() const;
 
-	// RewindableVideoDecoder API
-	void rewind();
+	// SeekableVideoDecoder API
+	void seekToFrame(uint32 frame);
+	void seekToTime(VideoTimestamp time);
 
 private:
 	// This is the file handle from which data is read from. It can be the actual file handle or a decompressed stream.
@@ -176,17 +177,9 @@
 		uint32 *chunk_offsets;
 		int stts_count;
 		MOVstts *stts_data;
-		int ctts_count;
-		MOVstts *ctts_data;
 		int edit_count; /* number of 'edit' (elst atom) */
 		uint32 sample_to_chunk_sz;
 		MOVstsc *sample_to_chunk;
-		int32 sample_to_chunk_index;
-		int sample_to_time_index;
-		uint32 sample_to_time_sample;
-		uint32 sample_to_time_time;
-		int sample_to_ctime_index;
-		int sample_to_ctime_sample;
 		uint32 sample_size;
 		uint32 sample_count;
 		uint32 *sample_sizes;
@@ -197,7 +190,7 @@
 
 		uint16 width;
 		uint16 height;
-		int codec_type;
+		CodecType codec_type;
 
 		uint32 stsdEntryCount;
 		STSDEntry *stsdEntries;
@@ -215,9 +208,7 @@
 	bool _foundMOOV;
 	uint32 _timeScale;
 	uint32 _duration;
-	MOVStreamContext *_partial;
 	uint32 _numStreams;
-	int _ni;
 	Common::Rational _scaleFactorX;
 	Common::Rational _scaleFactorY;
 	MOVStreamContext *_streams[20];
@@ -237,15 +228,18 @@
 	void startAudio();
 	void stopAudio();
 	void updateAudioBuffer();
+	void readNextAudioChunk();
 	uint32 getAudioChunkSampleCount(uint chunk);
 	int8 _audioStreamIndex;
 	uint _curAudioChunk;
 	Audio::SoundHandle _audHandle;
+	VideoTimestamp _audioStartOffset;
 
 	Codec *createCodec(uint32 codecTag, byte bitsPerPixel);
 	Codec *findDefaultVideoCodec() const;
 	uint32 _nextFrameStartTime;
 	int8 _videoStreamIndex;
+	uint32 findKeyFrame(uint32 frame) const;
 
 	Surface *_scaledSurface;
 	const Surface *scaleSurface(const Surface *frame);


This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.




More information about the Scummvm-git-logs mailing list