diff options
| -rw-r--r-- | engines/sci/console.cpp | 6 | ||||
| -rw-r--r-- | engines/sci/engine/kvideo.cpp | 7 | ||||
| -rw-r--r-- | video/avi_decoder.cpp | 554 | ||||
| -rw-r--r-- | video/avi_decoder.h | 317 | 
4 files changed, 441 insertions, 443 deletions
diff --git a/engines/sci/console.cpp b/engines/sci/console.cpp index 2a4ad1743d..a6a6d4496f 100644 --- a/engines/sci/console.cpp +++ b/engines/sci/console.cpp @@ -259,10 +259,12 @@ void Console::postEnter() {  			videoDecoder = new RobotDecoder(g_system->getMixer(), _engine->getPlatform() == Common::kPlatformMacintosh);  		} else if (_videoFile.hasSuffix(".duk")) {  			duckMode = true; -			videoDecoder = new Video::AviDecoder(g_system->getMixer()); +			videoDecoder = new Video::AVIDecoder(); +			((Video::AdvancedVideoDecoder *)videoDecoder)->start();  #endif  		} else if (_videoFile.hasSuffix(".avi")) { -			videoDecoder = new Video::AviDecoder(g_system->getMixer()); +			videoDecoder = new Video::AVIDecoder(); +			((Video::AdvancedVideoDecoder *)videoDecoder)->start();  		} else {  			warning("Unrecognized video type");  		} diff --git a/engines/sci/engine/kvideo.cpp b/engines/sci/engine/kvideo.cpp index bfe32a8d82..2c1532cc46 100644 --- a/engines/sci/engine/kvideo.cpp +++ b/engines/sci/engine/kvideo.cpp @@ -191,7 +191,7 @@ reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) {  		switch (argv[0].toUint16()) {  		case 0: {  			Common::String filename = s->_segMan->getString(argv[1]); -			videoDecoder = new Video::AviDecoder(g_system->getMixer()); +			videoDecoder = new Video::AVIDecoder();  			if (filename.equalsIgnoreCase("gk2a.avi")) {  				// HACK: Switch to 16bpp graphics for Indeo3. @@ -212,6 +212,7 @@ reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) {  				videoDecoder = 0;  			} else {  				s->_videoState.fileName = filename; +				((Video::AdvancedVideoDecoder *)videoDecoder)->start();  			}  			break;  		} @@ -407,13 +408,15 @@ reg_t kPlayDuck(EngineState *s, int argc, reg_t *argv) {  		s->_videoState.reset();  		s->_videoState.fileName = Common::String::format("%d.duk", argv[1].toUint16()); -		videoDecoder = new Video::AviDecoder(g_system->getMixer()); +		videoDecoder = new Video::AVIDecoder();  		if (!videoDecoder->loadFile(s->_videoState.fileName)) {  			warning("Could not open Duck %s", s->_videoState.fileName.c_str());  			break;  		} +		((Video::AdvancedVideoDecoder *)videoDecoder)->start(); +  		if (reshowCursor)  			g_sci->_gfxCursor->kernelHide(); diff --git a/video/avi_decoder.cpp b/video/avi_decoder.cpp index 2ea7e8d90e..375cc6f0f3 100644 --- a/video/avi_decoder.cpp +++ b/video/avi_decoder.cpp @@ -42,106 +42,128 @@  namespace Video { -/* +#define UNKNOWN_HEADER(a) error("Unknown header found -- \'%s\'", tag2str(a)) + +// IDs used throughout the AVI files +// that will be handled by this player +#define ID_RIFF MKTAG('R','I','F','F') +#define ID_AVI  MKTAG('A','V','I',' ') +#define ID_LIST MKTAG('L','I','S','T') +#define ID_HDRL MKTAG('h','d','r','l') +#define ID_AVIH MKTAG('a','v','i','h') +#define ID_STRL MKTAG('s','t','r','l') +#define ID_STRH MKTAG('s','t','r','h') +#define ID_VIDS MKTAG('v','i','d','s') +#define ID_AUDS MKTAG('a','u','d','s') +#define ID_MIDS MKTAG('m','i','d','s') +#define ID_TXTS MKTAG('t','x','t','s') +#define ID_JUNK MKTAG('J','U','N','K') +#define ID_STRF MKTAG('s','t','r','f') +#define ID_MOVI MKTAG('m','o','v','i') +#define ID_REC  MKTAG('r','e','c',' ') +#define ID_VEDT MKTAG('v','e','d','t') +#define ID_IDX1 MKTAG('i','d','x','1') +#define ID_STRD MKTAG('s','t','r','d') +#define ID_00AM MKTAG('0','0','A','M') +//#define ID_INFO MKTAG('I','N','F','O') + +// Codec tags +#define ID_RLE  MKTAG('R','L','E',' ') +#define ID_CRAM MKTAG('C','R','A','M') +#define ID_MSVC MKTAG('m','s','v','c') +#define ID_WHAM MKTAG('W','H','A','M') +#define ID_CVID MKTAG('c','v','i','d') +#define ID_IV32 MKTAG('i','v','3','2') +#define ID_DUCK MKTAG('D','U','C','K') +  static byte char2num(char c) { -	return (c >= 48 && c <= 57) ? c - 48 : 0; +	c = tolower((byte)c); +	return (c >= 'a' && c <= 'f') ? c - 'a' + 10 : c - '0';  } -static byte getStreamNum(uint32 tag) { -	return char2num((char)(tag >> 24)) * 16 + char2num((char)(tag >> 16)); +static byte getStreamIndex(uint32 tag) { +	return char2num((tag >> 24) & 0xFF) << 4 | char2num((tag >> 16) & 0xFF);  } -*/  static uint16 getStreamType(uint32 tag) {  	return tag & 0xffff;  } -AviDecoder::AviDecoder(Audio::Mixer *mixer, Audio::Mixer::SoundType soundType) : _mixer(mixer) { -	_soundType = soundType; - -	_videoCodec = NULL; +AVIDecoder::AVIDecoder(Audio::Mixer::SoundType soundType) : _soundType(soundType) {  	_decodedHeader = false; -	_audStream = NULL; -	_fileStream = NULL; -	_audHandle = new Audio::SoundHandle(); -	_dirtyPalette = false; -	memset(_palette, 0, sizeof(_palette)); -	memset(&_wvInfo, 0, sizeof(PCMWAVEFORMAT)); -	memset(&_bmInfo, 0, sizeof(BITMAPINFOHEADER)); -	memset(&_vidsHeader, 0, sizeof(AVIStreamHeader)); -	memset(&_audsHeader, 0, sizeof(AVIStreamHeader)); -	memset(&_ixInfo, 0, sizeof(AVIOLDINDEX)); +	_fileStream = 0; +	memset(&_ixInfo, 0, sizeof(_ixInfo)); +	memset(&_header, 0, sizeof(_header));  } -AviDecoder::~AviDecoder() { +AVIDecoder::~AVIDecoder() {  	close(); -	delete _audHandle;  } -void AviDecoder::runHandle(uint32 tag) { -	assert (_fileStream); +void AVIDecoder::runHandle(uint32 tag) { +	assert(_fileStream);  	if (_fileStream->eos())  		return; -	debug (3, "Decoding tag %s", tag2str(tag)); +	debug(3, "Decoding tag %s", tag2str(tag));  	switch (tag) { -		case ID_RIFF: -			/*_filesize = */_fileStream->readUint32LE(); -			if (_fileStream->readUint32BE() != ID_AVI) -				error("RIFF file is not an AVI video"); -			break; -		case ID_LIST: -			handleList(); -			break; -		case ID_AVIH: -			_header.size = _fileStream->readUint32LE(); -			_header.microSecondsPerFrame = _fileStream->readUint32LE(); -			_header.maxBytesPerSecond = _fileStream->readUint32LE(); -			_header.padding = _fileStream->readUint32LE(); -			_header.flags = _fileStream->readUint32LE(); -			_header.totalFrames = _fileStream->readUint32LE(); -			_header.initialFrames = _fileStream->readUint32LE(); -			_header.streams = _fileStream->readUint32LE(); -			_header.bufferSize = _fileStream->readUint32LE(); -			_header.width = _fileStream->readUint32LE(); -			_header.height = _fileStream->readUint32LE(); -			//Ignore 16 bytes of reserved data -			_fileStream->skip(16); -			break; -		case ID_STRH: -			handleStreamHeader(); -			break; -		case ID_STRD: // Extra stream info, safe to ignore -		case ID_VEDT: // Unknown, safe to ignore -		case ID_JUNK: // Alignment bytes, should be ignored -			{ -			uint32 junkSize = _fileStream->readUint32LE(); -			_fileStream->skip(junkSize + (junkSize & 1)); // Alignment -			} break; -		case ID_IDX1: -			_ixInfo.size = _fileStream->readUint32LE(); -			_ixInfo.indices = new AVIOLDINDEX::Index[_ixInfo.size / 16]; -			debug (0, "%d Indices", (_ixInfo.size / 16)); -			for (uint32 i = 0; i < (_ixInfo.size / 16); i++) { -				_ixInfo.indices[i].id = _fileStream->readUint32BE(); -				_ixInfo.indices[i].flags = _fileStream->readUint32LE(); -				_ixInfo.indices[i].offset = _fileStream->readUint32LE(); -				_ixInfo.indices[i].size = _fileStream->readUint32LE(); -				debug (0, "Index %d == Tag \'%s\', Offset = %d, Size = %d", i, tag2str(_ixInfo.indices[i].id), _ixInfo.indices[i].offset, _ixInfo.indices[i].size); -			} -			break; -		default: -			error ("Unknown tag \'%s\' found", tag2str(tag)); +	case ID_RIFF: +		/*_filesize = */_fileStream->readUint32LE(); +		if (_fileStream->readUint32BE() != ID_AVI) +			error("RIFF file is not an AVI video"); +		break; +	case ID_LIST: +		handleList(); +		break; +	case ID_AVIH: +		_header.size = _fileStream->readUint32LE(); +		_header.microSecondsPerFrame = _fileStream->readUint32LE(); +		_header.maxBytesPerSecond = _fileStream->readUint32LE(); +		_header.padding = _fileStream->readUint32LE(); +		_header.flags = _fileStream->readUint32LE(); +		_header.totalFrames = _fileStream->readUint32LE(); +		_header.initialFrames = _fileStream->readUint32LE(); +		_header.streams = _fileStream->readUint32LE(); +		_header.bufferSize = _fileStream->readUint32LE(); +		_header.width = _fileStream->readUint32LE(); +		_header.height = _fileStream->readUint32LE(); +		// Ignore 16 bytes of reserved data +		_fileStream->skip(16); +		break; +	case ID_STRH: +		handleStreamHeader(); +		break; +	case ID_STRD: // Extra stream info, safe to ignore +	case ID_VEDT: // Unknown, safe to ignore +	case ID_JUNK: // Alignment bytes, should be ignored +		{ +		uint32 junkSize = _fileStream->readUint32LE(); +		_fileStream->skip(junkSize + (junkSize & 1)); // Alignment +		} break; +	case ID_IDX1: +		_ixInfo.size = _fileStream->readUint32LE(); +		_ixInfo.indices = new OldIndex::Index[_ixInfo.size / 16]; +		debug(0, "%d Indices", (_ixInfo.size / 16)); +		for (uint32 i = 0; i < (_ixInfo.size / 16); i++) { +			_ixInfo.indices[i].id = _fileStream->readUint32BE(); +			_ixInfo.indices[i].flags = _fileStream->readUint32LE(); +			_ixInfo.indices[i].offset = _fileStream->readUint32LE(); +			_ixInfo.indices[i].size = _fileStream->readUint32LE(); +			debug(0, "Index %d == Tag \'%s\', Offset = %d, Size = %d", i, tag2str(_ixInfo.indices[i].id), _ixInfo.indices[i].offset, _ixInfo.indices[i].size); +		} +		break; +	default: +		error("Unknown tag \'%s\' found", tag2str(tag));  	}  } -void AviDecoder::handleList() { +void AVIDecoder::handleList() {  	uint32 listSize = _fileStream->readUint32LE() - 4; // Subtract away listType's 4 bytes  	uint32 listType = _fileStream->readUint32BE();  	uint32 curPos = _fileStream->pos(); -	debug (0, "Found LIST of type %s", tag2str(listType)); +	debug(0, "Found LIST of type %s", tag2str(listType));  	while ((_fileStream->pos() - curPos) < listSize)  		runHandle(_fileStream->readUint32BE()); @@ -151,12 +173,14 @@ void AviDecoder::handleList() {  		_decodedHeader = true;  } -void AviDecoder::handleStreamHeader() { +void AVIDecoder::handleStreamHeader() {  	AVIStreamHeader sHeader;  	sHeader.size = _fileStream->readUint32LE();  	sHeader.streamType = _fileStream->readUint32BE(); +  	if (sHeader.streamType == ID_MIDS || sHeader.streamType == ID_TXTS) -		error ("Unhandled MIDI/Text stream"); +		error("Unhandled MIDI/Text stream"); +  	sHeader.streamHandler = _fileStream->readUint32BE();  	sHeader.flags = _fileStream->readUint32LE();  	sHeader.priority = _fileStream->readUint16LE(); @@ -174,63 +198,67 @@ void AviDecoder::handleStreamHeader() {  	if (_fileStream->readUint32BE() != ID_STRF)  		error("Could not find STRF tag"); +  	uint32 strfSize = _fileStream->readUint32LE();  	uint32 startPos = _fileStream->pos();  	if (sHeader.streamType == ID_VIDS) { -		_vidsHeader = sHeader; - -		_bmInfo.size = _fileStream->readUint32LE(); -		_bmInfo.width = _fileStream->readUint32LE(); -		assert (_header.width == _bmInfo.width); -		_bmInfo.height = _fileStream->readUint32LE(); -		assert (_header.height == _bmInfo.height); -		_bmInfo.planes = _fileStream->readUint16LE(); -		_bmInfo.bitCount = _fileStream->readUint16LE(); -		_bmInfo.compression = _fileStream->readUint32BE(); -		_bmInfo.sizeImage = _fileStream->readUint32LE(); -		_bmInfo.xPelsPerMeter = _fileStream->readUint32LE(); -		_bmInfo.yPelsPerMeter = _fileStream->readUint32LE(); -		_bmInfo.clrUsed = _fileStream->readUint32LE(); -		_bmInfo.clrImportant = _fileStream->readUint32LE(); - -		if (_bmInfo.bitCount == 8) { -			if (_bmInfo.clrUsed == 0) -				_bmInfo.clrUsed = 256; - -			for (uint32 i = 0; i < _bmInfo.clrUsed; i++) { -				_palette[i * 3 + 2] = _fileStream->readByte(); -				_palette[i * 3 + 1] = _fileStream->readByte(); -				_palette[i * 3] = _fileStream->readByte(); +		BitmapInfoHeader bmInfo; +		bmInfo.size = _fileStream->readUint32LE(); +		bmInfo.width = _fileStream->readUint32LE(); +		bmInfo.height = _fileStream->readUint32LE(); +		bmInfo.planes = _fileStream->readUint16LE(); +		bmInfo.bitCount = _fileStream->readUint16LE(); +		bmInfo.compression = _fileStream->readUint32BE(); +		bmInfo.sizeImage = _fileStream->readUint32LE(); +		bmInfo.xPelsPerMeter = _fileStream->readUint32LE(); +		bmInfo.yPelsPerMeter = _fileStream->readUint32LE(); +		bmInfo.clrUsed = _fileStream->readUint32LE(); +		bmInfo.clrImportant = _fileStream->readUint32LE(); + +		if (bmInfo.clrUsed == 0) +			bmInfo.clrUsed = 256; + +		if (sHeader.streamHandler == 0) +			sHeader.streamHandler = bmInfo.compression; + +		AVIVideoTrack *track = new AVIVideoTrack(_header.totalFrames, sHeader, bmInfo); + +		if (bmInfo.bitCount == 8) { +			byte *palette = const_cast<byte *>(track->getPalette()); +			for (uint32 i = 0; i < bmInfo.clrUsed; i++) { +				palette[i * 3 + 2] = _fileStream->readByte(); +				palette[i * 3 + 1] = _fileStream->readByte(); +				palette[i * 3] = _fileStream->readByte();  				_fileStream->readByte();  			} -			_dirtyPalette = true; +			track->markPaletteDirty();  		} -		if (!_vidsHeader.streamHandler) -			_vidsHeader.streamHandler = _bmInfo.compression; +		addTrack(track);  	} else if (sHeader.streamType == ID_AUDS) { -		_audsHeader = sHeader; - -		_wvInfo.tag = _fileStream->readUint16LE(); -		_wvInfo.channels = _fileStream->readUint16LE(); -		_wvInfo.samplesPerSec = _fileStream->readUint32LE(); -		_wvInfo.avgBytesPerSec = _fileStream->readUint32LE(); -		_wvInfo.blockAlign = _fileStream->readUint16LE(); -		_wvInfo.size = _fileStream->readUint16LE(); +		PCMWaveFormat wvInfo; +		wvInfo.tag = _fileStream->readUint16LE(); +		wvInfo.channels = _fileStream->readUint16LE(); +		wvInfo.samplesPerSec = _fileStream->readUint32LE(); +		wvInfo.avgBytesPerSec = _fileStream->readUint32LE(); +		wvInfo.blockAlign = _fileStream->readUint16LE(); +		wvInfo.size = _fileStream->readUint16LE();  		// AVI seems to treat the sampleSize as including the second  		// channel as well, so divide for our sake. -		if (_wvInfo.channels == 2) -			_audsHeader.sampleSize /= 2; +		if (wvInfo.channels == 2) +			sHeader.sampleSize /= 2; + +		addTrack(new AVIAudioTrack(sHeader, wvInfo, _soundType));  	}  	// Ensure that we're at the end of the chunk  	_fileStream->seek(startPos + strfSize);  } -bool AviDecoder::loadStream(Common::SeekableReadStream *stream) { +bool AVIDecoder::loadStream(Common::SeekableReadStream *stream) {  	close();  	_fileStream = stream; @@ -252,74 +280,31 @@ bool AviDecoder::loadStream(Common::SeekableReadStream *stream) {  	if (nextTag == ID_LIST) {  		_fileStream->readUint32BE(); // Skip size  		if (_fileStream->readUint32BE() != ID_MOVI) -			error ("Expected 'movi' LIST"); -	} else -		error ("Expected 'movi' LIST"); - -	// Now, create the codec -	_videoCodec = createCodec(); - -	// Initialize the video stuff too -	_audStream = createAudioStream(); -	if (_audStream) -		_mixer->playStream(_soundType, _audHandle, _audStream, -1, getVolume(), getBalance()); - -	debug (0, "Frames = %d, Dimensions = %d x %d", _header.totalFrames, _header.width, _header.height); -	debug (0, "Frame Rate = %d", _vidsHeader.rate / _vidsHeader.scale); -	if (_wvInfo.samplesPerSec != 0) -		debug (0, "Sound Rate = %d", _wvInfo.samplesPerSec); -	debug (0, "Video Codec = \'%s\'", tag2str(_vidsHeader.streamHandler)); - -	if (!_videoCodec) -		return false; +			error("Expected 'movi' LIST"); +	} else { +		error("Expected 'movi' LIST"); +	}  	return true;  } -void AviDecoder::close() { -	if (!_fileStream) -		return; +void AVIDecoder::close() { +	AdvancedVideoDecoder::close();  	delete _fileStream;  	_fileStream = 0; - -	// Deinitialize sound -	_mixer->stopHandle(*_audHandle); -	_audStream = 0; -  	_decodedHeader = false; -	delete _videoCodec; -	_videoCodec = 0; -  	delete[] _ixInfo.indices; -	_ixInfo.indices = 0; - -	memset(_palette, 0, sizeof(_palette)); -	memset(&_wvInfo, 0, sizeof(PCMWAVEFORMAT)); -	memset(&_bmInfo, 0, sizeof(BITMAPINFOHEADER)); -	memset(&_vidsHeader, 0, sizeof(AVIStreamHeader)); -	memset(&_audsHeader, 0, sizeof(AVIStreamHeader)); -	memset(&_ixInfo, 0, sizeof(AVIOLDINDEX)); - -	reset(); -} - -uint32 AviDecoder::getTime() const { -	if (_audStream) -		return _mixer->getSoundElapsedTime(*_audHandle); - -	return FixedRateVideoDecoder::getTime(); +	memset(&_ixInfo, 0, sizeof(_ixInfo)); +	memset(&_header, 0, sizeof(_header));  } -const Graphics::Surface *AviDecoder::decodeNextFrame() { +void AVIDecoder::readNextPacket() {  	uint32 nextTag = _fileStream->readUint32BE();  	if (_fileStream->eos()) -		return NULL; - -	if (_curFrame == -1) -		_startTime = g_system->getMillis(); +		return;  	if (nextTag == ID_LIST) {  		// A list of audio/video chunks @@ -327,138 +312,159 @@ const Graphics::Surface *AviDecoder::decodeNextFrame() {  		int32 startPos = _fileStream->pos();  		if (_fileStream->readUint32BE() != ID_REC) -			error ("Expected 'rec ' LIST"); - -		// Decode chunks in the list and see if we get a frame -		const Graphics::Surface *frame = NULL; -		while (_fileStream->pos() < startPos + (int32)listSize) { -			const Graphics::Surface *temp = decodeNextFrame(); -			if (temp) -				frame = temp; -		} +			error("Expected 'rec ' LIST"); -		return frame; -	} else if (getStreamType(nextTag) == 'wb') { -		// Audio Chunk -		uint32 chunkSize = _fileStream->readUint32LE(); -		queueAudioBuffer(chunkSize); -		_fileStream->skip(chunkSize & 1); // Alignment -	} else if (getStreamType(nextTag) == 'dc' || getStreamType(nextTag) == 'id' || -	           getStreamType(nextTag) == 'AM' || getStreamType(nextTag) == '32' || -			   getStreamType(nextTag) == 'iv') { -		// Compressed Frame -		_curFrame++; -		uint32 chunkSize = _fileStream->readUint32LE(); - -		if (chunkSize == 0) // Keep last frame on screen -			return NULL; - -		Common::SeekableReadStream *frameData = _fileStream->readStream(chunkSize); -		const Graphics::Surface *surface = _videoCodec->decodeImage(frameData); -		delete frameData; -		_fileStream->skip(chunkSize & 1); // Alignment -		return surface; -	} else if (getStreamType(nextTag) == 'pc') { -		// Palette Change -		_fileStream->readUint32LE(); // Chunk size, not needed here -		byte firstEntry = _fileStream->readByte(); -		uint16 numEntries = _fileStream->readByte(); -		_fileStream->readUint16LE(); // Reserved - -		// 0 entries means all colors are going to be changed -		if (numEntries == 0) -			numEntries = 256; - -		for (uint16 i = firstEntry; i < numEntries + firstEntry; i++) { -			_palette[i * 3] = _fileStream->readByte(); -			_palette[i * 3 + 1] = _fileStream->readByte(); -			_palette[i * 3 + 2] = _fileStream->readByte(); -			_fileStream->readByte(); // Flags that don't serve us any purpose -		} +		// Decode chunks in the list +		while (_fileStream->pos() < startPos + (int32)listSize) +			readNextPacket(); -		_dirtyPalette = true; +		return; +	} else if (nextTag == ID_JUNK || nextTag == ID_IDX1) { +		runHandle(nextTag); +		return; +	} -		// No alignment necessary. It's always even. -	} else if (nextTag == ID_JUNK) { -		runHandle(ID_JUNK); -	} else if (nextTag == ID_IDX1) { -		runHandle(ID_IDX1); -	} else -		error("Tag = \'%s\', %d", tag2str(nextTag), _fileStream->pos()); +	Track *track = getTrack(getStreamIndex(nextTag)); -	return NULL; -} +	if (!track) +		error("Cannot get track from tag '%s'", tag2str(nextTag)); -Codec *AviDecoder::createCodec() { -	switch (_vidsHeader.streamHandler) { -		case ID_CRAM: -		case ID_MSVC: -		case ID_WHAM: -			return new MSVideo1Decoder(_bmInfo.width, _bmInfo.height, _bmInfo.bitCount); -		case ID_RLE: -			return new MSRLEDecoder(_bmInfo.width, _bmInfo.height, _bmInfo.bitCount); -		case ID_CVID: -			return new CinepakDecoder(_bmInfo.bitCount); -		case ID_IV32: -			return new Indeo3Decoder(_bmInfo.width, _bmInfo.height); -#ifdef VIDEO_CODECS_TRUEMOTION1_H -		case ID_DUCK: -			return new TrueMotion1Decoder(_bmInfo.width, _bmInfo.height); -#endif -		default: -			warning ("Unknown/Unhandled compression format \'%s\'", tag2str(_vidsHeader.streamHandler)); +	uint32 chunkSize = _fileStream->readUint32LE(); +	Common::SeekableReadStream *chunk = _fileStream->readStream(chunkSize); +	_fileStream->skip(chunkSize & 1); + +	if (track->getTrackType() == Track::kTrackTypeAudio) { +		if (getStreamType(nextTag) != 'wb') +			error("Invalid audio track tag '%s'", tag2str(nextTag)); + +		((AVIAudioTrack *)track)->queueSound(chunk); +	} else { +		AVIVideoTrack *videoTrack = (AVIVideoTrack *)track; + +		if (getStreamType(nextTag) == 'pc') { +			// Palette Change +			byte firstEntry = chunk->readByte(); +			uint16 numEntries = chunk->readByte(); +			chunk->readUint16LE(); // Reserved + +			// 0 entries means all colors are going to be changed +			if (numEntries == 0) +				numEntries = 256; + +			byte *palette = const_cast<byte *>(videoTrack->getPalette()); + +			for (uint16 i = firstEntry; i < numEntries + firstEntry; i++) { +				palette[i * 3] = chunk->readByte(); +				palette[i * 3 + 1] = chunk->readByte(); +				palette[i * 3 + 2] = chunk->readByte(); +				chunk->readByte(); // Flags that don't serve us any purpose +			} + +			delete chunk; +			videoTrack->markPaletteDirty(); +		} else if (getStreamType(nextTag) == 'db') { +			// TODO: Check if this really is uncompressed. Many videos +			// falsely put compressed data in here. +			error("Uncompressed AVI frame found"); +		} else { +			// Otherwise, assume it's a compressed frame +			videoTrack->decodeFrame(chunk); +		}  	} +} -	return NULL; +AVIDecoder::AVIVideoTrack::AVIVideoTrack(int frameCount, const AVIStreamHeader &streamHeader, const BitmapInfoHeader &bitmapInfoHeader)  +		: _frameCount(frameCount), _vidsHeader(streamHeader), _bmInfo(bitmapInfoHeader) { +	memset(_palette, 0, sizeof(_palette)); +	_videoCodec = createCodec(); +	_dirtyPalette = false; +	_lastFrame = 0; +	_curFrame = -1;  } -Graphics::PixelFormat AviDecoder::getPixelFormat() const { -	assert(_videoCodec); -	return _videoCodec->getPixelFormat(); +AVIDecoder::AVIVideoTrack::~AVIVideoTrack() { +	delete _videoCodec;  } -Audio::QueuingAudioStream *AviDecoder::createAudioStream() { -	if (_wvInfo.tag == kWaveFormatPCM || _wvInfo.tag == kWaveFormatDK3) -		return Audio::makeQueuingAudioStream(_wvInfo.samplesPerSec, _wvInfo.channels == 2); -	else if (_wvInfo.tag != kWaveFormatNone) // No sound -		warning("Unsupported AVI audio format %d", _wvInfo.tag); +void AVIDecoder::AVIVideoTrack::decodeFrame(Common::SeekableReadStream *stream) { +	if (_videoCodec) +		_lastFrame = _videoCodec->decodeImage(stream); -	return NULL; +	delete stream; +	_curFrame++;  } -void AviDecoder::queueAudioBuffer(uint32 chunkSize) { -	// Return if we haven't created the queue (unsupported audio format) -	if (!_audStream) { -		_fileStream->skip(chunkSize); -		return; +Graphics::PixelFormat AVIDecoder::AVIVideoTrack::getPixelFormat() const { +	if (_videoCodec) +		return _videoCodec->getPixelFormat(); + +	return Graphics::PixelFormat(); +} + +Codec *AVIDecoder::AVIVideoTrack::createCodec() { +	switch (_vidsHeader.streamHandler) { +	case ID_CRAM: +	case ID_MSVC: +	case ID_WHAM: +		return new MSVideo1Decoder(_bmInfo.width, _bmInfo.height, _bmInfo.bitCount); +	case ID_RLE: +		return new MSRLEDecoder(_bmInfo.width, _bmInfo.height, _bmInfo.bitCount); +	case ID_CVID: +		return new CinepakDecoder(_bmInfo.bitCount); +	case ID_IV32: +		return new Indeo3Decoder(_bmInfo.width, _bmInfo.height); +#ifdef VIDEO_CODECS_TRUEMOTION1_H +	case ID_DUCK: +		return new TrueMotion1Decoder(_bmInfo.width, _bmInfo.height); +#endif +	default: +		warning("Unknown/Unhandled compression format \'%s\'", tag2str(_vidsHeader.streamHandler));  	} -	Common::SeekableReadStream *stream = _fileStream->readStream(chunkSize); +	return 0; +} -	if (_wvInfo.tag == kWaveFormatPCM) { -		byte flags = 0; -		if (_audsHeader.sampleSize == 2) -			flags |= Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN; -		else -			flags |= Audio::FLAG_UNSIGNED; +AVIDecoder::AVIAudioTrack::AVIAudioTrack(const AVIStreamHeader &streamHeader, const PCMWaveFormat &waveFormat, Audio::Mixer::SoundType soundType) +		: _audsHeader(streamHeader), _wvInfo(waveFormat), _soundType(soundType) { +	_audStream = createAudioStream(); +} -		if (_wvInfo.channels == 2) -			flags |= Audio::FLAG_STEREO; +AVIDecoder::AVIAudioTrack::~AVIAudioTrack() { +	delete _audStream; +} -		_audStream->queueAudioStream(Audio::makeRawStream(stream, _wvInfo.samplesPerSec, flags, DisposeAfterUse::YES), DisposeAfterUse::YES); -	} else if (_wvInfo.tag == kWaveFormatDK3) { -		_audStream->queueAudioStream(Audio::makeADPCMStream(stream, DisposeAfterUse::YES, chunkSize, Audio::kADPCMDK3, _wvInfo.samplesPerSec, _wvInfo.channels, _wvInfo.blockAlign), DisposeAfterUse::YES); +void AVIDecoder::AVIAudioTrack::queueSound(Common::SeekableReadStream *stream) { +	if (_audStream) { +		if (_wvInfo.tag == kWaveFormatPCM) { +			byte flags = 0; +			if (_audsHeader.sampleSize == 2) +				flags |= Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN; +			else +				flags |= Audio::FLAG_UNSIGNED; + +			if (_wvInfo.channels == 2) +				flags |= Audio::FLAG_STEREO; + +			_audStream->queueAudioStream(Audio::makeRawStream(stream, _wvInfo.samplesPerSec, flags, DisposeAfterUse::YES), DisposeAfterUse::YES); +		} else if (_wvInfo.tag == kWaveFormatDK3) { +			_audStream->queueAudioStream(Audio::makeADPCMStream(stream, DisposeAfterUse::YES, stream->size(), Audio::kADPCMDK3, _wvInfo.samplesPerSec, _wvInfo.channels, _wvInfo.blockAlign), DisposeAfterUse::YES); +		} +	} else { +		delete stream;  	}  } -void AviDecoder::updateVolume() { -	if (g_system->getMixer()->isSoundHandleActive(*_audHandle)) -		g_system->getMixer()->setChannelVolume(*_audHandle, getVolume()); +Audio::AudioStream *AVIDecoder::AVIAudioTrack::getAudioStream() const { +	return _audStream;  } -void AviDecoder::updateBalance() { -	if (g_system->getMixer()->isSoundHandleActive(*_audHandle)) -		g_system->getMixer()->setChannelBalance(*_audHandle, getBalance()); +Audio::QueuingAudioStream *AVIDecoder::AVIAudioTrack::createAudioStream() { +	if (_wvInfo.tag == kWaveFormatPCM || _wvInfo.tag == kWaveFormatDK3) +		return Audio::makeQueuingAudioStream(_wvInfo.samplesPerSec, _wvInfo.channels == 2); +	else if (_wvInfo.tag != kWaveFormatNone) // No sound +		warning("Unsupported AVI audio format %d", _wvInfo.tag); + +	return 0;  }  } // End of namespace Video diff --git a/video/avi_decoder.h b/video/avi_decoder.h index fb4dae6711..010702cce3 100644 --- a/video/avi_decoder.h +++ b/video/avi_decoder.h @@ -47,196 +47,183 @@ namespace Video {  class Codec; -#define UNKNOWN_HEADER(a) error("Unknown header found -- \'%s\'", tag2str(a)) - -// IDs used throughout the AVI files -// that will be handled by this player -#define ID_RIFF MKTAG('R','I','F','F') -#define ID_AVI  MKTAG('A','V','I',' ') -#define ID_LIST MKTAG('L','I','S','T') -#define ID_HDRL MKTAG('h','d','r','l') -#define ID_AVIH MKTAG('a','v','i','h') -#define ID_STRL MKTAG('s','t','r','l') -#define ID_STRH MKTAG('s','t','r','h') -#define ID_VIDS MKTAG('v','i','d','s') -#define ID_AUDS MKTAG('a','u','d','s') -#define ID_MIDS MKTAG('m','i','d','s') -#define ID_TXTS MKTAG('t','x','t','s') -#define ID_JUNK MKTAG('J','U','N','K') -#define ID_STRF MKTAG('s','t','r','f') -#define ID_MOVI MKTAG('m','o','v','i') -#define ID_REC  MKTAG('r','e','c',' ') -#define ID_VEDT MKTAG('v','e','d','t') -#define ID_IDX1 MKTAG('i','d','x','1') -#define ID_STRD MKTAG('s','t','r','d') -#define ID_00AM MKTAG('0','0','A','M') -//#define ID_INFO MKTAG('I','N','F','O') - -// Codec tags -#define ID_RLE  MKTAG('R','L','E',' ') -#define ID_CRAM MKTAG('C','R','A','M') -#define ID_MSVC MKTAG('m','s','v','c') -#define ID_WHAM MKTAG('W','H','A','M') -#define ID_CVID MKTAG('c','v','i','d') -#define ID_IV32 MKTAG('i','v','3','2') -#define ID_DUCK MKTAG('D','U','C','K') - -struct BITMAPINFOHEADER { -	uint32 size; -	uint32 width; -	uint32 height; -	uint16 planes; -	uint16 bitCount; -	uint32 compression; -	uint32 sizeImage; -	uint32 xPelsPerMeter; -	uint32 yPelsPerMeter; -	uint32 clrUsed; -	uint32 clrImportant; -}; - -struct WAVEFORMAT { -	uint16 tag; -	uint16 channels; -	uint32 samplesPerSec; -	uint32 avgBytesPerSec; -	uint16 blockAlign; -}; - -struct PCMWAVEFORMAT : public WAVEFORMAT { -	uint16 size; -}; - -struct WAVEFORMATEX : public WAVEFORMAT { -	uint16 bitsPerSample; -	uint16 size; -}; - -struct AVIOLDINDEX { -	uint32 size; -	struct Index { -		uint32 id; -		uint32 flags; -		uint32 offset; -		uint32 size; -	} *indices; -}; - -// Index Flags -enum IndexFlags { -	AVIIF_INDEX = 0x10 -}; - -// Audio Codecs -enum { -	kWaveFormatNone = 0, -	kWaveFormatPCM = 1, -	kWaveFormatDK3 = 98 -}; - -struct AVIHeader { -	uint32 size; -	uint32 microSecondsPerFrame; -	uint32 maxBytesPerSecond; -	uint32 padding; -	uint32 flags; -	uint32 totalFrames; -	uint32 initialFrames; -	uint32 streams; -	uint32 bufferSize; -	uint32 width; -	uint32 height; -}; - -// Flags from the AVIHeader -enum AviFlags { -	AVIF_HASINDEX = 0x00000010, -	AVIF_MUSTUSEINDEX = 0x00000020, -	AVIF_ISINTERLEAVED = 0x00000100, -	AVIF_TRUSTCKTYPE = 0x00000800, -	AVIF_WASCAPTUREFILE = 0x00010000, -	AVIF_WASCOPYRIGHTED = 0x00020000 -}; - -struct AVIStreamHeader { -	uint32 size; -	uint32 streamType; -	uint32 streamHandler; -	uint32 flags; -	uint16 priority; -	uint16 language; -	uint32 initialFrames; -	uint32 scale; -	uint32 rate; -	uint32 start; -	uint32 length; -	uint32 bufferSize; -	uint32 quality; -	uint32 sampleSize; -	Common::Rect frame; -}; -  /**   * Decoder for AVI videos.   *   * Video decoder used in engines:   *  - sci   */ -class AviDecoder : public FixedRateVideoDecoder { +class AVIDecoder : public AdvancedVideoDecoder {  public: -	AviDecoder(Audio::Mixer *mixer, -			Audio::Mixer::SoundType soundType = Audio::Mixer::kPlainSoundType); -	virtual ~AviDecoder(); +	AVIDecoder(Audio::Mixer::SoundType soundType = Audio::Mixer::kPlainSoundType); +	virtual ~AVIDecoder();  	bool loadStream(Common::SeekableReadStream *stream);  	void close(); - -	bool isVideoLoaded() const { return _fileStream != 0; }  	uint16 getWidth() const { return _header.width; }  	uint16 getHeight() const { return _header.height; } -	uint32 getFrameCount() const { return _header.totalFrames; } -	uint32 getTime() const; -	const Graphics::Surface *decodeNextFrame(); -	Graphics::PixelFormat getPixelFormat() const; -	const byte *getPalette() { _dirtyPalette = false; return _palette; } -	bool hasDirtyPalette() const { return _dirtyPalette; }  protected: -	// VideoDecoder API -	void updateVolume(); -	void updateBalance(); - -	// FixedRateVideoDecoder API -	Common::Rational getFrameRate() const { return Common::Rational(_vidsHeader.rate, _vidsHeader.scale); } +	 void readNextPacket();  private: -	Audio::Mixer *_mixer; -	BITMAPINFOHEADER _bmInfo; -	PCMWAVEFORMAT _wvInfo; -	AVIOLDINDEX _ixInfo; -	AVIHeader _header; -	AVIStreamHeader _vidsHeader; -	AVIStreamHeader _audsHeader; -	byte _palette[3 * 256]; -	bool _dirtyPalette; +	struct BitmapInfoHeader { +		uint32 size; +		uint32 width; +		uint32 height; +		uint16 planes; +		uint16 bitCount; +		uint32 compression; +		uint32 sizeImage; +		uint32 xPelsPerMeter; +		uint32 yPelsPerMeter; +		uint32 clrUsed; +		uint32 clrImportant; +	}; + +	struct WaveFormat { +		uint16 tag; +		uint16 channels; +		uint32 samplesPerSec; +		uint32 avgBytesPerSec; +		uint16 blockAlign; +	}; + +	struct PCMWaveFormat : public WaveFormat { +		uint16 size; +	}; + +	struct WaveFormatEX : public WaveFormat { +		uint16 bitsPerSample; +		uint16 size; +	}; + +	struct OldIndex { +		uint32 size; +		struct Index { +			uint32 id; +			uint32 flags; +			uint32 offset; +			uint32 size; +		} *indices; +	}; + +	// Index Flags +	enum IndexFlags { +		AVIIF_INDEX = 0x10 +	}; + +	struct AVIHeader { +		uint32 size; +		uint32 microSecondsPerFrame; +		uint32 maxBytesPerSecond; +		uint32 padding; +		uint32 flags; +		uint32 totalFrames; +		uint32 initialFrames; +		uint32 streams; +		uint32 bufferSize; +		uint32 width; +		uint32 height; +	}; + +	// Flags from the AVIHeader +	enum AVIFlags { +		AVIF_HASINDEX = 0x00000010, +		AVIF_MUSTUSEINDEX = 0x00000020, +		AVIF_ISINTERLEAVED = 0x00000100, +		AVIF_TRUSTCKTYPE = 0x00000800, +		AVIF_WASCAPTUREFILE = 0x00010000, +		AVIF_WASCOPYRIGHTED = 0x00020000 +	}; + +	struct AVIStreamHeader { +		uint32 size; +		uint32 streamType; +		uint32 streamHandler; +		uint32 flags; +		uint16 priority; +		uint16 language; +		uint32 initialFrames; +		uint32 scale; +		uint32 rate; +		uint32 start; +		uint32 length; +		uint32 bufferSize; +		uint32 quality; +		uint32 sampleSize; +		Common::Rect frame; +	}; + +	class AVIVideoTrack : public FixedRateVideoTrack { +	public: +		AVIVideoTrack(int frameCount, const AVIStreamHeader &streamHeader, const BitmapInfoHeader &bitmapInfoHeader); +		~AVIVideoTrack(); + +		void decodeFrame(Common::SeekableReadStream *stream); + +		uint16 getWidth() const { return _bmInfo.width; } +		uint16 getHeight() const { return _bmInfo.height; } +		Graphics::PixelFormat getPixelFormat() const; +		int getCurFrame() const { return _curFrame; } +		int getFrameCount() const { return _frameCount; } +		const Graphics::Surface *decodeNextFrame() { return _lastFrame; } +		const byte *getPalette() const { _dirtyPalette = false; return _palette; } +		bool hasDirtyPalette() const { return _dirtyPalette; } +		void markPaletteDirty() { _dirtyPalette = true; } + +	protected: +		Common::Rational getFrameRate() const { return Common::Rational(_vidsHeader.rate, _vidsHeader.scale); } + +	private: +		AVIStreamHeader _vidsHeader; +		BitmapInfoHeader _bmInfo; +		byte _palette[3 * 256]; +		mutable bool _dirtyPalette; +		int _frameCount, _curFrame; + +		Codec *_videoCodec; +		const Graphics::Surface *_lastFrame; +		Codec *createCodec(); +	}; + +	class AVIAudioTrack : public AudioTrack { +	public: +		AVIAudioTrack(const AVIStreamHeader &streamHeader, const PCMWaveFormat &waveFormat, Audio::Mixer::SoundType soundType); +		~AVIAudioTrack(); + +		void queueSound(Common::SeekableReadStream *stream); +		Audio::Mixer::SoundType getSoundType() const { return _soundType; } + +	protected: +		Audio::AudioStream *getAudioStream() const; + +	private: +		// Audio Codecs +		enum { +			kWaveFormatNone = 0, +			kWaveFormatPCM = 1, +			kWaveFormatDK3 = 98 +		}; + +		AVIStreamHeader _audsHeader; +		PCMWaveFormat _wvInfo; +		Audio::Mixer::SoundType _soundType; +		Audio::QueuingAudioStream *_audStream; +		Audio::QueuingAudioStream *createAudioStream(); +	}; + +	OldIndex _ixInfo; +	AVIHeader _header;	  	Common::SeekableReadStream *_fileStream;  	bool _decodedHeader; -	Codec *_videoCodec; -	Codec *createCodec(); -  	Audio::Mixer::SoundType _soundType;  	void runHandle(uint32 tag);  	void handleList();  	void handleStreamHeader(); -	void handlePalChange(); - -	Audio::SoundHandle *_audHandle; -	Audio::QueuingAudioStream *_audStream; -	Audio::QueuingAudioStream *createAudioStream(); -	void queueAudioBuffer(uint32 chunkSize);  };  } // End of namespace Video  | 
