[Scummvm-cvs-logs] scummvm master -> 7054bcd9c53f704f0fdd08a67fec00289f075c32

clone2727 clone2727 at gmail.com
Thu Jan 8 06:56:34 CET 2015


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

Summary:
7054bcd9c5 VIDEO: Add MPEG-PS demuxer


Commit: 7054bcd9c53f704f0fdd08a67fec00289f075c32
    https://github.com/scummvm/scummvm/commit/7054bcd9c53f704f0fdd08a67fec00289f075c32
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2015-01-08T00:45:13-05:00

Commit Message:
VIDEO: Add MPEG-PS demuxer

For use with ZVision and later MADE

Changed paths:
  A video/mpegps_decoder.cpp
  A video/mpegps_decoder.h
    video/module.mk



diff --git a/video/module.mk b/video/module.mk
index 5754350..be01459 100644
--- a/video/module.mk
+++ b/video/module.mk
@@ -5,6 +5,7 @@ MODULE_OBJS := \
 	coktel_decoder.o \
 	dxa_decoder.o \
 	flic_decoder.o \
+	mpegps_decoder.o \
 	psx_decoder.o \
 	qt_decoder.o \
 	smk_decoder.o \
diff --git a/video/mpegps_decoder.cpp b/video/mpegps_decoder.cpp
new file mode 100644
index 0000000..d8f7f5a
--- /dev/null
+++ b/video/mpegps_decoder.cpp
@@ -0,0 +1,732 @@
+/* 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.
+ *
+ */
+
+#include "audio/audiostream.h"
+#include "audio/decoders/raw.h"
+#include "common/debug.h"
+#include "common/endian.h"
+#include "common/stream.h"
+#include "common/system.h"
+#include "common/textconsole.h"
+
+#include "video/mpegps_decoder.h"
+#include "image/codecs/mpeg.h"
+
+// The demuxing code is based on libav's demuxing code
+
+namespace Video {
+
+enum {
+	kStartCodePack = 0x1BA,
+	kStartCodeSystemHeader = 0x1BB,
+	kStartCodeProgramStreamMap = 0x1BC,
+	kStartCodePrivateStream1 = 0x1BD,
+	kStartCodePaddingStream = 0x1BE,
+	kStartCodePrivateStream2 = 0x1BF
+};
+
+MPEGPSDecoder::MPEGPSDecoder() {
+	_stream = 0;
+	memset(_psmESType, 0, 256);
+}
+
+MPEGPSDecoder::~MPEGPSDecoder() {
+	close();
+}
+
+bool MPEGPSDecoder::loadStream(Common::SeekableReadStream *stream) {
+	close();
+
+	_stream = stream;
+
+	if (!addFirstVideoTrack()) {
+		close();
+		return false;
+	}
+
+	_stream->seek(0);
+	return true;
+}
+
+void MPEGPSDecoder::close() {
+	VideoDecoder::close();
+
+	delete _stream;
+	_stream = 0;
+
+	_streamMap.clear();
+
+	memset(_psmESType, 0, 256);
+}
+
+void MPEGPSDecoder::readNextPacket() {
+	if (_stream->eos())
+		return;
+
+	for (;;) {
+		int32 startCode;
+		uint32 pts, dts;
+		int size = readNextPacketHeader(startCode, pts, dts);
+
+		if (size < 0) {
+			// End of stream
+			for (TrackListIterator it = getTrackListBegin(); it != getTrackListEnd(); it++)
+				if ((*it)->getTrackType() == Track::kTrackTypeVideo)
+					((MPEGVideoTrack *)*it)->setEndOfTrack();
+			return;
+		}
+
+		MPEGStream *stream = 0;
+		Common::SeekableReadStream *packet = _stream->readStream(size);
+
+		if (_streamMap.contains(startCode)) {
+			// We already found the stream
+			stream = _streamMap[startCode];
+		} else {
+			// We haven't seen this before
+
+			if (startCode == kStartCodePrivateStream1) {
+				PrivateStreamType streamType = detectPrivateStreamType(packet);
+				packet->seek(0);
+
+				// TODO: Handling of these types (as needed)
+
+				const char *typeName;
+
+				switch (streamType) {
+				case kPrivateStreamAC3:
+					typeName = "AC-3";
+					break;
+				case kPrivateStreamDTS:
+					typeName = "DTS";
+					break;
+				case kPrivateStreamDVDPCM:
+					typeName = "DVD PCM";
+					break;
+				case kPrivateStreamPS2Audio:
+					typeName = "PS2 Audio";
+					break;
+				default:
+					typeName = "Unknown";
+					break;
+				}
+
+				warning("Unhandled DVD private stream: %s", typeName);
+
+				// Make it 0 so we don't get the warning twice
+				_streamMap[startCode] = 0;
+			} else if (startCode >= 0x1E0 && startCode <= 0x1EF) {
+				// Video stream
+				// TODO: Multiple video streams
+				warning("Found extra video stream 0x%04X", startCode);
+				_streamMap[startCode] = 0;
+			} else if (startCode >= 0x1C0 && startCode <= 0x1DF) {
+#ifdef USE_MAD
+				// MPEG Audio stream
+				MPEGAudioTrack *audioTrack = new MPEGAudioTrack(packet);
+				stream = audioTrack;
+				_streamMap[startCode] = audioTrack;
+				addTrack(audioTrack);
+#else
+				warning("Found audio stream 0x%04X, but no MAD support compiled in", startCode);
+				_streamMap[startCode] = 0;
+#endif
+			} else {
+				// Probably not relevant
+				debug(0, "Found unhandled MPEG-PS stream type 0x%04x", startCode);
+				_streamMap[startCode] = 0;
+			}
+		}
+
+		if (stream) {
+			bool done = stream->sendPacket(packet, pts, dts);
+
+			if (done && stream->getStreamType() == MPEGStream::kStreamTypeVideo)
+				return;
+		} else {
+			delete packet;
+		}
+	}
+}
+
+#define MAX_SYNC_SIZE 100000
+
+int MPEGPSDecoder::findNextStartCode(uint32 &size) {
+	size = MAX_SYNC_SIZE;
+	int32 state = 0xFF;
+
+	while (size > 0) {
+		byte v = _stream->readByte();
+
+		if (_stream->eos())
+			return -1;
+
+		size--;
+
+		if (state == 0x1)
+			return ((state << 8) | v) & 0xFFFFFF;
+
+		state = ((state << 8) | v) & 0xFFFFFF;
+	}
+
+	return -1;
+}
+
+int MPEGPSDecoder::readNextPacketHeader(int32 &startCode, uint32 &pts, uint32 &dts) {
+	for (;;) {
+		uint32 size;
+		startCode = findNextStartCode(size);
+
+		if (_stream->eos())
+			return -1;
+
+		if (startCode < 0)
+			continue;
+
+		uint32 lastSync = _stream->pos();
+
+		if (startCode == kStartCodePack || startCode == kStartCodeSystemHeader)
+			continue;
+
+		int length = _stream->readUint16BE();
+
+		if (startCode == kStartCodePaddingStream || startCode == kStartCodePrivateStream2) {
+			_stream->skip(length);
+			continue;
+		}
+
+		if (startCode == kStartCodeProgramStreamMap) {
+			parseProgramStreamMap(length);
+			continue;
+		}
+
+		// Find matching stream
+		if (!((startCode >= 0x1C0 && startCode <= 0x1DF) ||
+				(startCode >= 0x1E0 && startCode <= 0x1EF) ||
+				startCode == kStartCodePrivateStream1 || startCode == 0x1FD))
+			continue;
+
+		// Stuffing
+		byte c;
+		for (;;) {
+			if (length < 1) {
+				_stream->seek(lastSync);
+				continue;
+			}
+
+			c = _stream->readByte();
+			length--;
+
+			// XXX: for mpeg1, should test only bit 7
+			if (c != 0xFF)
+				break;
+		}
+
+		if ((c & 0xC0) == 0x40) {
+			// Buffer scale and size
+			_stream->readByte();
+			c = _stream->readByte();
+			length -= 2;
+		}
+
+		pts = 0xFFFFFFFF;
+		dts = 0xFFFFFFFF;
+
+		if ((c & 0xE0) == 0x20) {
+			dts = pts = readPTS(c);
+			length -= 4;
+
+			if (c & 0x10) {
+				dts = readPTS(-1);
+				length -= 5;
+			}
+		} else if ((c & 0xC0) == 0x80) {
+			// MPEG-2 PES
+			byte flags = _stream->readByte();
+			int headerLength = _stream->readByte();
+			length -= 2;
+
+			if (headerLength > length) {
+				_stream->seek(lastSync);
+				continue;
+			}
+
+			length -= headerLength;
+
+			if (flags & 0x80) {
+				dts = pts = readPTS(-1);
+				headerLength -= 5;
+
+				if (flags & 0x40) {
+					dts = readPTS(-1);
+					headerLength -= 5;
+				}
+			}
+
+			if (flags & 0x3F && headerLength == 0) {
+				flags &= 0xC0;
+				warning("Further flags set but no bytes left");
+			}
+
+			if (flags & 0x01) { // PES extension
+				byte pesExt =_stream->readByte();
+				headerLength--;
+
+				// Skip PES private data, program packet sequence
+				int skip = (pesExt >> 4) & 0xB;
+				skip += skip & 0x9;
+
+				if (pesExt & 0x40 || skip > headerLength) {
+					warning("pesExt %x is invalid", pesExt);
+					pesExt = skip = 0;
+				} else {
+					_stream->skip(skip);
+					headerLength -= skip;
+				}
+
+				if (pesExt & 0x01) { // PES extension 2
+					byte ext2Length = _stream->readByte();
+					headerLength--;
+
+					if ((ext2Length & 0x7F) != 0) {
+						byte idExt = _stream->readByte();
+
+						if ((idExt & 0x80) == 0)
+							startCode = (startCode & 0xFF) << 8;
+
+						headerLength--;
+					}
+				}
+			}
+
+			if (headerLength < 0) {
+				_stream->seek(lastSync);
+				continue;
+			}
+
+			_stream->skip(headerLength);
+		} else if (c != 0xF) {
+			continue;
+		}
+
+		if (length < 0) {
+			_stream->seek(lastSync);
+			continue;
+		}
+
+		return length;
+	}
+}
+
+uint32 MPEGPSDecoder::readPTS(int c) {
+	byte buf[5];
+
+	buf[0] = (c < 0) ? _stream->readByte() : c;
+	_stream->read(buf + 1, 4);
+
+	return ((buf[0] & 0x0E) << 29) | ((READ_BE_UINT16(buf + 1) >> 1) << 15) | (READ_BE_UINT16(buf + 3) >> 1);
+}
+
+void MPEGPSDecoder::parseProgramStreamMap(int length) {
+	_stream->readByte();
+	_stream->readByte();
+
+	// skip program stream info
+	_stream->skip(_stream->readUint16BE());
+
+	int esMapLength = _stream->readUint16BE();
+
+	while (esMapLength >= 4) {
+		byte type = _stream->readByte();
+		byte esID = _stream->readByte();
+		uint16 esInfoLength = _stream->readUint16BE();
+
+		// Remember mapping from stream id to stream type
+		_psmESType[esID] = type;
+
+		// Skip program stream info
+		_stream->skip(esInfoLength);
+
+		esMapLength -= 4 + esInfoLength;
+	}
+
+	_stream->readUint32BE(); // CRC32
+}
+
+bool MPEGPSDecoder::addFirstVideoTrack() {
+	for (;;) {
+		int32 startCode;
+		uint32 pts, dts;
+		int size = readNextPacketHeader(startCode, pts, dts);
+
+		// End of stream? We failed
+		if (size < 0)
+			return false;
+
+		if (startCode >= 0x1E0 && startCode <= 0x1EF) {
+			// Video stream
+			// Can be MPEG-1/2 or MPEG-4/h.264. We'll assume the former and
+			// I hope we never need the latter.
+			Common::SeekableReadStream *firstPacket = _stream->readStream(size);
+			MPEGVideoTrack *track = new MPEGVideoTrack(firstPacket, getDefaultHighColorFormat());
+			addTrack(track);
+			_streamMap[startCode] = track;
+			delete firstPacket;
+			break;
+		}
+
+		_stream->skip(size);
+	}
+
+	return true;
+}
+
+MPEGPSDecoder::PrivateStreamType MPEGPSDecoder::detectPrivateStreamType(Common::SeekableReadStream *packet) {
+	uint32 dvdCode = packet->readUint32LE();
+	if (packet->eos())
+		return kPrivateStreamUnknown;
+
+	uint32 ps2Header = packet->readUint32BE();
+	if (!packet->eos() && ps2Header == MKTAG('S', 'S', 'h', 'd'))
+		return kPrivateStreamPS2Audio;
+
+	switch (dvdCode & 0xE0) {
+	case 0x80:
+		if ((dvdCode & 0xF8) == 0x88)
+			return kPrivateStreamDTS;
+
+		return kPrivateStreamAC3;
+	case 0xA0:
+		return kPrivateStreamDVDPCM;
+	}
+
+	return kPrivateStreamUnknown;
+}
+
+MPEGPSDecoder::MPEGVideoTrack::MPEGVideoTrack(Common::SeekableReadStream *firstPacket, const Graphics::PixelFormat &format) {
+	_surface = 0;
+	_endOfTrack = false;
+	_curFrame = -1;
+	_nextFrameStartTime = Audio::Timestamp(0, 27000000); // 27 MHz timer
+
+	findDimensions(firstPacket, format);
+
+#ifdef USE_MPEG2
+	_mpegDecoder = new Image::MPEGDecoder();
+#endif
+}
+
+MPEGPSDecoder::MPEGVideoTrack::~MPEGVideoTrack() {
+#ifdef USE_MPEG2
+	delete _mpegDecoder;
+#endif
+
+	if (_surface) {
+		_surface->free();
+		delete _surface;
+	}
+}
+
+uint16 MPEGPSDecoder::MPEGVideoTrack::getWidth() const {
+	return _surface ? _surface->w : 0;
+}
+
+uint16 MPEGPSDecoder::MPEGVideoTrack::getHeight() const {
+	return _surface ? _surface->h : 0;
+}
+
+Graphics::PixelFormat MPEGPSDecoder::MPEGVideoTrack::getPixelFormat() const {
+	if (!_surface)
+		return Graphics::PixelFormat();
+
+	return _surface->format;
+}
+
+const Graphics::Surface *MPEGPSDecoder::MPEGVideoTrack::decodeNextFrame() {
+	return _surface;
+}
+
+bool MPEGPSDecoder::MPEGVideoTrack::sendPacket(Common::SeekableReadStream *packet, uint32 pts, uint32 dts) {
+#ifdef USE_MPEG2
+	uint32 framePeriod;
+	bool foundFrame = _mpegDecoder->decodePacket(*packet, framePeriod, _surface);
+
+	if (foundFrame) {
+		_curFrame++;
+		_nextFrameStartTime = _nextFrameStartTime.addFrames(framePeriod);
+	}
+#endif
+
+	delete packet;
+
+#ifdef USE_MPEG2
+	return foundFrame;
+#else
+	return true;
+#endif
+}
+
+void MPEGPSDecoder::MPEGVideoTrack::findDimensions(Common::SeekableReadStream *firstPacket, const Graphics::PixelFormat &format) {
+	// First, check for the picture start code
+	if (firstPacket->readUint32BE() != 0x1B3)
+		error("Failed to detect MPEG sequence start");
+
+	// This is part of the bitstream, but there's really no purpose
+	// to use Common::BitStream just for this: 12 bits width, 12 bits
+	// height
+	uint16 width = firstPacket->readByte() << 4;
+	uint16 height = firstPacket->readByte();
+	width |= (height & 0xF0) >> 4;
+	height = ((height & 0x0F) << 8) | firstPacket->readByte();
+
+	debug(0, "MPEG dimensions: %dx%d", width, height);
+
+	_surface = new Graphics::Surface();
+	_surface->create(width, height, format);
+
+	firstPacket->seek(0);
+}
+
+#ifdef USE_MAD
+
+// The audio code here is almost entirely based on what we do in mp3.cpp
+
+MPEGPSDecoder::MPEGAudioTrack::MPEGAudioTrack(Common::SeekableReadStream *firstPacket) {
+	// The MAD_BUFFER_GUARD must always contain zeros (the reason
+	// for this is that the Layer III Huffman decoder of libMAD
+	// may read a few bytes beyond the end of the input buffer).
+	memset(_buf + BUFFER_SIZE, 0, MAD_BUFFER_GUARD);
+
+	_state = MP3_STATE_INIT;
+	_audStream = 0;
+
+	// Find out our audio parameters
+	initStream(firstPacket);
+
+	while (_state != MP3_STATE_EOS)
+		readHeader(firstPacket);
+
+	_audStream = Audio::makeQueuingAudioStream(_frame.header.samplerate, MAD_NCHANNELS(&_frame.header) == 2);
+
+	deinitStream();
+
+	firstPacket->seek(0);
+	_state = MP3_STATE_INIT;
+}
+
+MPEGPSDecoder::MPEGAudioTrack::~MPEGAudioTrack() {
+	deinitStream();
+	delete _audStream;
+}
+
+static inline int scaleSample(mad_fixed_t sample) {
+	// round
+	sample += (1L << (MAD_F_FRACBITS - 16));
+
+	// clip
+	if (sample > MAD_F_ONE - 1)
+		sample = MAD_F_ONE - 1;
+	else if (sample < -MAD_F_ONE)
+		sample = -MAD_F_ONE;
+
+	// quantize and scale to not saturate when mixing a lot of channels
+	return sample >> (MAD_F_FRACBITS + 1 - 16);
+}
+
+bool MPEGPSDecoder::MPEGAudioTrack::sendPacket(Common::SeekableReadStream *packet, uint32 pts, uint32 dts) {
+	while (_state != MP3_STATE_EOS)
+		decodeMP3Data(packet);
+
+	_state = MP3_STATE_READY;
+	delete packet;
+	return true;
+}
+
+Audio::AudioStream *MPEGPSDecoder::MPEGAudioTrack::getAudioStream() const {
+	return _audStream;
+}
+
+void MPEGPSDecoder::MPEGAudioTrack::initStream(Common::SeekableReadStream *packet) {
+	if (_state != MP3_STATE_INIT)
+		deinitStream();
+
+	// Init MAD
+	mad_stream_init(&_stream);
+	mad_frame_init(&_frame);
+	mad_synth_init(&_synth);
+
+	// Reset the stream data
+	packet->seek(0, SEEK_SET);
+
+	// Update state
+	_state = MP3_STATE_READY;
+
+	// Read the first few sample bytes
+	readMP3Data(packet);
+}
+
+void MPEGPSDecoder::MPEGAudioTrack::deinitStream() {
+	if (_state == MP3_STATE_INIT)
+		return;
+
+	// Deinit MAD
+	mad_synth_finish(&_synth);
+	mad_frame_finish(&_frame);
+	mad_stream_finish(&_stream);
+
+	_state = MP3_STATE_EOS;
+}
+
+void MPEGPSDecoder::MPEGAudioTrack::readMP3Data(Common::SeekableReadStream *packet) {
+	uint32 remaining = 0;
+
+	// Give up immediately if we already used up all data in the stream
+	if (packet->eos()) {
+		_state = MP3_STATE_EOS;
+		return;
+	}
+
+	if (_stream.next_frame) {
+		// If there is still data in the MAD stream, we need to preserve it.
+		// Note that we use memmove, as we are reusing the same buffer,
+		// and hence the data regions we copy from and to may overlap.
+		remaining = _stream.bufend - _stream.next_frame;
+		assert(remaining < BUFFER_SIZE);	// Paranoia check
+		memmove(_buf, _stream.next_frame, remaining);
+	}
+
+	memset(_buf + remaining, 0, BUFFER_SIZE - remaining);
+
+	// Try to read the next block
+	uint32 size = packet->read(_buf + remaining, BUFFER_SIZE - remaining);
+	if (size == 0) {
+		_state = MP3_STATE_EOS;
+		return;
+	}
+
+	// Feed the data we just read into the stream decoder
+	_stream.error = MAD_ERROR_NONE;
+	mad_stream_buffer(&_stream, _buf, size + remaining);
+}
+
+void MPEGPSDecoder::MPEGAudioTrack::readHeader(Common::SeekableReadStream *packet) {
+	if (_state != MP3_STATE_READY)
+		return;
+
+	// If necessary, load more data into the stream decoder
+	if (_stream.error == MAD_ERROR_BUFLEN)
+		readMP3Data(packet);
+
+	while (_state != MP3_STATE_EOS) {
+		_stream.error = MAD_ERROR_NONE;
+
+		// Decode the next header. Note: mad_frame_decode would do this for us, too.
+		// However, for seeking we don't want to decode the full frame (else it would
+		// be far too slow). Hence we perform this explicitly in a separate step.
+		if (mad_header_decode(&_frame.header, &_stream) == -1) {
+			if (_stream.error == MAD_ERROR_BUFLEN) {
+				readMP3Data(packet);  // Read more data
+				continue;
+			} else if (MAD_RECOVERABLE(_stream.error)) {
+				debug(6, "MPEGAudioTrack::readHeader(): Recoverable error in mad_header_decode (%s)", mad_stream_errorstr(&_stream));
+				continue;
+			} else {
+				warning("MPEGAudioTrack::readHeader(): Unrecoverable error in mad_header_decode (%s)", mad_stream_errorstr(&_stream));
+				break;
+			}
+		}
+
+		break;
+	}
+
+	if (_stream.error != MAD_ERROR_NONE)
+		_state = MP3_STATE_EOS;
+}
+
+void MPEGPSDecoder::MPEGAudioTrack::decodeMP3Data(Common::SeekableReadStream *packet) {
+	if (_state == MP3_STATE_INIT)
+		initStream(packet);
+
+	if (_state == MP3_STATE_EOS)
+		return;
+
+	do {
+		// If necessary, load more data into the stream decoder
+		if (_stream.error == MAD_ERROR_BUFLEN)
+			readMP3Data(packet);
+
+		while (_state == MP3_STATE_READY) {
+			_stream.error = MAD_ERROR_NONE;
+
+			// Decode the next frame
+			if (mad_frame_decode(&_frame, &_stream) == -1) {
+				if (_stream.error == MAD_ERROR_BUFLEN) {
+					break; // Read more data
+				} else if (MAD_RECOVERABLE(_stream.error)) {
+					// Note: we will occasionally see MAD_ERROR_BADDATAPTR errors here.
+					// These are normal and expected (caused by our frame skipping (i.e. "seeking")
+					// code above).
+					debug(6, "MPEGAudioTrack::decodeMP3Data(): Recoverable error in mad_frame_decode (%s)", mad_stream_errorstr(&_stream));
+					continue;
+				} else {
+					warning("MPEGAudioTrack::decodeMP3Data(): Unrecoverable error in mad_frame_decode (%s)", mad_stream_errorstr(&_stream));
+					break;
+				}
+			}
+
+			// Synthesize PCM data
+			mad_synth_frame(&_synth, &_frame);
+
+			// Output it to our queue
+			if (_synth.pcm.length != 0) {
+				byte *buffer = (byte *)malloc(_synth.pcm.length * 2 * MAD_NCHANNELS(&_frame.header));
+				int16 *ptr = (int16 *)buffer;
+
+				for (int i = 0; i < _synth.pcm.length; i++) {
+					*ptr++ = (int16)scaleSample(_synth.pcm.samples[0][i]);
+
+					if (MAD_NCHANNELS(&_frame.header) == 2)
+						*ptr++ = (int16)scaleSample(_synth.pcm.samples[1][i]);
+				}
+
+				int flags = Audio::FLAG_16BITS;
+
+				if (_audStream->isStereo())
+					flags |= Audio::FLAG_STEREO;
+
+#ifdef SCUMM_LITTLE_ENDIAN
+				flags |= Audio::FLAG_LITTLE_ENDIAN;
+#endif
+
+				_audStream->queueBuffer(buffer, _synth.pcm.length * 2 * MAD_NCHANNELS(&_frame.header), DisposeAfterUse::YES, flags);
+			}
+			break;
+		}
+	} while (_state != MP3_STATE_EOS && _stream.error == MAD_ERROR_BUFLEN);
+
+	if (_stream.error != MAD_ERROR_NONE)
+		_state = MP3_STATE_EOS;
+}
+
+#endif
+
+} // End of namespace Video
diff --git a/video/mpegps_decoder.h b/video/mpegps_decoder.h
new file mode 100644
index 0000000..0184d6f
--- /dev/null
+++ b/video/mpegps_decoder.h
@@ -0,0 +1,189 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef VIDEO_MPEGPS_DECODER_H
+#define VIDEO_MPEGPS_DECODER_H
+
+#include "common/hashmap.h"
+#include "graphics/surface.h"
+#include "video/video_decoder.h"
+
+#ifdef USE_MAD
+#include <mad.h>
+#endif
+
+namespace Audio {
+class QueuingAudioStream;
+}
+
+namespace Common {
+class SeekableReadStream;
+}
+
+namespace Graphics {
+struct PixelFormat;
+}
+
+namespace Image {
+class MPEGDecoder;
+}
+
+namespace Video {
+
+/**
+ * Decoder for MPEG Program Stream videos.
+ * Video decoder used in engines:
+ *  - zvision
+ */
+class MPEGPSDecoder : public VideoDecoder {
+public:
+	MPEGPSDecoder();
+	virtual ~MPEGPSDecoder();
+
+	bool loadStream(Common::SeekableReadStream *stream);
+	void close();
+
+protected:
+	void readNextPacket();
+	bool useAudioSync() const { return false; }
+
+private:
+	// Base class for handling MPEG streams
+	class MPEGStream {
+	public:
+		virtual ~MPEGStream() {}
+
+		enum StreamType {
+			kStreamTypeVideo,
+			kStreamTypeAudio
+		};
+
+		virtual bool sendPacket(Common::SeekableReadStream *firstPacket, uint32 pts, uint32 dts) = 0;
+		virtual StreamType getStreamType() const = 0;
+	};
+
+	// An MPEG 1/2 video track
+	class MPEGVideoTrack : public VideoTrack, public MPEGStream {
+	public:
+		MPEGVideoTrack(Common::SeekableReadStream *firstPacket, const Graphics::PixelFormat &format);
+		~MPEGVideoTrack();
+
+		bool endOfTrack() const { return _endOfTrack; }
+		uint16 getWidth() const;
+		uint16 getHeight() const;
+		Graphics::PixelFormat getPixelFormat() const;
+		int getCurFrame() const { return _curFrame; }
+		uint32 getNextFrameStartTime() const { return _nextFrameStartTime.msecs(); }
+		const Graphics::Surface *decodeNextFrame();
+
+		bool sendPacket(Common::SeekableReadStream *packet, uint32 pts, uint32 dts);
+		StreamType getStreamType() const { return kStreamTypeVideo; }
+
+		void setEndOfTrack() { _endOfTrack = true; }
+
+	private:
+		bool _endOfTrack;
+		int _curFrame;
+		Audio::Timestamp _nextFrameStartTime;
+		Graphics::Surface *_surface;
+
+		void findDimensions(Common::SeekableReadStream *firstPacket, const Graphics::PixelFormat &format);
+
+#ifdef USE_MPEG2
+		Image::MPEGDecoder *_mpegDecoder;
+#endif
+	};
+
+#ifdef USE_MAD
+	// An MPEG audio track
+	// TODO: Merge this with the normal MP3Stream somehow
+	class MPEGAudioTrack : public AudioTrack, public MPEGStream {
+	public:
+		MPEGAudioTrack(Common::SeekableReadStream *firstPacket);
+		~MPEGAudioTrack();
+
+		bool sendPacket(Common::SeekableReadStream *packet, uint32 pts, uint32 dts);
+		StreamType getStreamType() const { return kStreamTypeAudio; }
+
+	protected:
+		Audio::AudioStream *getAudioStream() const;
+
+	private:
+		Audio::QueuingAudioStream *_audStream;
+
+		enum State {
+			MP3_STATE_INIT,  // Need to init the decoder
+			MP3_STATE_READY, // ready for processing data
+			MP3_STATE_EOS    // end of data reached (may need to loop)
+		};
+
+		State _state;
+
+		mad_stream _stream;
+		mad_frame _frame;
+		mad_synth _synth;
+
+		enum {
+			BUFFER_SIZE = 5 * 8192
+		};
+
+		// This buffer contains a slab of input data
+		byte _buf[BUFFER_SIZE + MAD_BUFFER_GUARD];
+
+		void initStream(Common::SeekableReadStream *packet);
+		void deinitStream();
+		void readMP3Data(Common::SeekableReadStream *packet);
+		void readHeader(Common::SeekableReadStream *packet);
+		void decodeMP3Data(Common::SeekableReadStream *packet);
+	};
+#endif
+
+	// The different types of private streams we can detect at the moment
+	enum PrivateStreamType {
+		kPrivateStreamUnknown,
+		kPrivateStreamAC3,
+		kPrivateStreamDTS,
+		kPrivateStreamDVDPCM,
+		kPrivateStreamPS2Audio
+	};
+
+	PrivateStreamType detectPrivateStreamType(Common::SeekableReadStream *packet);
+
+	bool addFirstVideoTrack();
+
+	int readNextPacketHeader(int32 &startCode, uint32 &pts, uint32 &dts);
+	int findNextStartCode(uint32 &size);
+	uint32 readPTS(int c);
+
+	void parseProgramStreamMap(int length);
+	byte _psmESType[256];
+
+	// A map from stream types to stream handlers
+	typedef Common::HashMap<int, MPEGStream *> StreamMap;
+	StreamMap _streamMap;
+
+	Common::SeekableReadStream *_stream;
+};
+
+} // End of namespace Video
+
+#endif






More information about the Scummvm-git-logs mailing list