scummvm/video/mpegps_decoder.cpp
2020-04-28 10:38:37 +02:00

738 lines
18 KiB
C++

/* 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/ac3.h"
#include "audio/decoders/mp3.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 {
// --------------------------------------------------------------------------
// Decoder - This is the part that takes a packet and figures out what to do
// with it.
// --------------------------------------------------------------------------
enum {
kStartCodePack = 0x1BA,
kStartCodeSystemHeader = 0x1BB,
kStartCodeProgramStreamMap = 0x1BC,
kStartCodePrivateStream1 = 0x1BD,
kStartCodePaddingStream = 0x1BE,
kStartCodePrivateStream2 = 0x1BF
};
MPEGPSDecoder::MPEGPSDecoder(double decibel) {
_decibel = decibel;
_demuxer = new MPEGPSDemuxer();
}
MPEGPSDecoder::~MPEGPSDecoder() {
close();
delete _demuxer;
}
bool MPEGPSDecoder::loadStream(Common::SeekableReadStream *stream) {
close();
if (!_demuxer->loadStream(stream)) {
close();
return false;
}
if (!addFirstVideoTrack()) {
close();
return false;
}
return true;
}
void MPEGPSDecoder::close() {
VideoDecoder::close();
_demuxer->close();
_streamMap.clear();
}
MPEGPSDecoder::MPEGStream *MPEGPSDecoder::getStream(uint32 startCode, Common::SeekableReadStream *packet) {
MPEGStream *stream = 0;
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)
bool handled = false;
const char *typeName;
switch (streamType) {
case kPrivateStreamAC3: {
typeName = "AC-3";
#ifdef USE_A52
handled = true;
AC3AudioTrack *ac3Track = new AC3AudioTrack(*packet, _decibel, getSoundType());
stream = ac3Track;
_streamMap[startCode] = ac3Track;
addTrack(ac3Track);
#endif
break;
}
case kPrivateStreamDTS:
typeName = "DTS";
break;
case kPrivateStreamDVDPCM:
typeName = "DVD PCM";
break;
case kPrivateStreamPS2Audio:
typeName = "PS2 Audio";
break;
default:
typeName = "Unknown";
break;
}
if (!handled) {
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, getSoundType());
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;
}
}
return stream;
}
void MPEGPSDecoder::readNextPacket() {
for (;;) {
int32 startCode;
uint32 pts, dts;
Common::SeekableReadStream *packet = _demuxer->getNextPacket(getTime(), startCode, pts, dts);
if (!packet) {
// End of stream
for (TrackListIterator it = getTrackListBegin(); it != getTrackListEnd(); it++)
if ((*it)->getTrackType() == Track::kTrackTypeVideo)
((MPEGVideoTrack *)*it)->setEndOfTrack();
return;
}
MPEGStream *stream = getStream(startCode, packet);
if (stream) {
packet->seek(0);
bool done = stream->sendPacket(packet, pts, dts);
if (done && stream->getStreamType() == MPEGStream::kStreamTypeVideo)
return;
} else {
delete packet;
}
}
}
bool MPEGPSDecoder::addFirstVideoTrack() {
int32 startCode;
uint32 pts, dts;
Common::SeekableReadStream *packet = _demuxer->getFirstVideoPacket(startCode, pts, dts);
if (!packet)
return false;
// 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.
MPEGVideoTrack *track = new MPEGVideoTrack(packet, getDefaultHighColorFormat());
addTrack(track);
_streamMap[startCode] = track;
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;
default:
break;
}
return kPrivateStreamUnknown;
}
// --------------------------------------------------------------------------
// Demuxer - This is the part that reads packets from the stream and delivers
// them to the decoder.
//
// It will buffer a number of packets in advance, because otherwise it may
// not encounter any audio packets until it's far too late to decode them.
// Before I added this, there would be 9 or 10 frames of video before the
// first audio packet, even though the timestamp indicated that the audio
// should start slightly before the video.
// --------------------------------------------------------------------------
#define PREBUFFERED_PACKETS 150
#define AUDIO_THRESHOLD 100
MPEGPSDecoder::MPEGPSDemuxer::MPEGPSDemuxer() {
_stream = 0;
}
MPEGPSDecoder::MPEGPSDemuxer::~MPEGPSDemuxer() {
close();
}
bool MPEGPSDecoder::MPEGPSDemuxer::loadStream(Common::SeekableReadStream *stream) {
close();
_stream = stream;
int queuedPackets = 0;
while (queueNextPacket() && queuedPackets < PREBUFFERED_PACKETS) {
queuedPackets++;
}
return true;
}
void MPEGPSDecoder::MPEGPSDemuxer::close() {
delete _stream;
_stream = 0;
while (!_audioQueue.empty()) {
Packet packet = _audioQueue.pop();
delete packet._stream;
}
while (!_videoQueue.empty()) {
Packet packet = _videoQueue.pop();
delete packet._stream;
}
}
Common::SeekableReadStream *MPEGPSDecoder::MPEGPSDemuxer::getFirstVideoPacket(int32 &startCode, uint32 &pts, uint32 &dts) {
if (_videoQueue.empty())
return nullptr;
Packet packet = _videoQueue.front();
startCode = packet._startCode;
pts = packet._pts;
dts = packet._dts;
return packet._stream;
}
Common::SeekableReadStream *MPEGPSDecoder::MPEGPSDemuxer::getNextPacket(uint32 currentTime, int32 &startCode, uint32 &pts, uint32 &dts) {
queueNextPacket();
// The idea here is to prioritize the delivery of audio packets,
// because when the decoder wants a frame it will keep asking until it
// gets a frame. There is nothing like that in the decoder to ensure
// speedy delivery of audio.
if (!_audioQueue.empty()) {
Packet packet = _audioQueue.front();
bool usePacket = false;
if (packet._pts == 0xFFFFFFFF) {
// No timestamp? Use it just in case. This could be a
// bad idea, but in my tests all audio packets have a
// time stamp.
usePacket = true;
} else {
uint32 packetTime = packet._pts / 90;
if (packetTime <= currentTime || packetTime - currentTime < AUDIO_THRESHOLD || _videoQueue.empty()) {
// The packet is overdue, or will be soon.
//
// TODO: We should pad or trim the first audio
// packet based on the timestamp to get the
// audio to start at the exact desired time.
// But for some reason it seems to work well
// enough anyway. For now.
usePacket = true;
}
}
if (usePacket) {
_audioQueue.pop();
startCode = packet._startCode;
pts = packet._pts;
dts = packet._dts;
return packet._stream;
}
}
if (!_videoQueue.empty()) {
Packet packet = _videoQueue.pop();
startCode = packet._startCode;
pts = packet._pts;
dts = packet._dts;
return packet._stream;
}
return nullptr;
}
bool MPEGPSDecoder::MPEGPSDemuxer::queueNextPacket() {
if (_stream->eos())
return false;
for (;;) {
int32 startCode;
uint32 pts, dts;
int size = readNextPacketHeader(startCode, pts, dts);
if (size < 0) {
// End of stream
return false;
}
Common::SeekableReadStream *stream = _stream->readStream(size);
if (startCode == kStartCodePrivateStream1 || (startCode >= 0x1C0 && startCode <= 0x1DF)) {
// Audio packet
_audioQueue.push(Packet(stream, startCode, pts, dts));
return true;
}
if (startCode >= 0x1E0 && startCode <= 0x1EF) {
// Video packet
_videoQueue.push(Packet(stream, startCode, pts, dts));
return true;
}
delete stream;
}
}
int MPEGPSDecoder::MPEGPSDemuxer::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;
}
}
#define MAX_SYNC_SIZE 100000
int MPEGPSDecoder::MPEGPSDemuxer::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;
}
uint32 MPEGPSDecoder::MPEGPSDemuxer::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::MPEGPSDemuxer::parseProgramStreamMap(int length) {
_stream->readByte();
_stream->readByte();
// skip program stream info
_stream->skip(_stream->readUint16BE());
int esMapLength = _stream->readUint16BE();
while (esMapLength >= 4) {
_stream->readByte(); // type
_stream->readByte(); // esID
uint16 esInfoLength = _stream->readUint16BE();
// Skip program stream info
_stream->skip(esInfoLength);
esMapLength -= 4 + esInfoLength;
}
_stream->readUint32BE(); // CRC32
}
// --------------------------------------------------------------------------
// Video track
// --------------------------------------------------------------------------
MPEGPSDecoder::MPEGVideoTrack::MPEGVideoTrack(Common::SeekableReadStream *firstPacket, const Graphics::PixelFormat &format) {
_surface = 0;
_endOfTrack = false;
_curFrame = -1;
_framePts = 0xFFFFFFFF;
_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
if (pts != 0xFFFFFFFF) {
_framePts = pts;
}
uint32 framePeriod;
bool foundFrame = _mpegDecoder->decodePacket(*packet, framePeriod, _surface);
if (foundFrame) {
_curFrame++;
// If there has been a timestamp since the previous frame, use that for
// syncing. Usually it will be the timestamp from the current packet,
// but it might not be.
if (_framePts != 0xFFFFFFFF) {
_nextFrameStartTime = Audio::Timestamp(_framePts / 90, 27000000);
} else {
_nextFrameStartTime = _nextFrameStartTime.addFrames(framePeriod);
}
_framePts = 0xFFFFFFFF;
}
#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);
}
// --------------------------------------------------------------------------
// Audio track
// --------------------------------------------------------------------------
#ifdef USE_MAD
// The audio code here is almost entirely based on what we do in mp3.cpp
MPEGPSDecoder::MPEGAudioTrack::MPEGAudioTrack(Common::SeekableReadStream &firstPacket, Audio::Mixer::SoundType soundType) :
AudioTrack(soundType) {
_audStream = Audio::makePacketizedMP3Stream(firstPacket);
}
MPEGPSDecoder::MPEGAudioTrack::~MPEGAudioTrack() {
delete _audStream;
}
bool MPEGPSDecoder::MPEGAudioTrack::sendPacket(Common::SeekableReadStream *packet, uint32 pts, uint32 dts) {
_audStream->queuePacket(packet);
return true;
}
Audio::AudioStream *MPEGPSDecoder::MPEGAudioTrack::getAudioStream() const {
return _audStream;
}
#endif
#ifdef USE_A52
MPEGPSDecoder::AC3AudioTrack::AC3AudioTrack(Common::SeekableReadStream &firstPacket, double decibel, Audio::Mixer::SoundType soundType) :
AudioTrack(soundType) {
_audStream = Audio::makeAC3Stream(firstPacket, decibel);
if (!_audStream)
error("Could not create AC-3 stream");
}
MPEGPSDecoder::AC3AudioTrack::~AC3AudioTrack() {
delete _audStream;
}
bool MPEGPSDecoder::AC3AudioTrack::sendPacket(Common::SeekableReadStream *packet, uint32 pts, uint32 dts) {
// Skip DVD code
packet->readUint32LE();
if (packet->eos())
return true;
_audStream->queuePacket(packet);
return true;
}
Audio::AudioStream *MPEGPSDecoder::AC3AudioTrack::getAudioStream() const {
return _audStream;
}
#endif
} // End of namespace Video