Skip to content

Commit

Permalink
BLADERUNNER: Don't base VQADecoder on Video::VideoDecoder anyway
Browse files Browse the repository at this point in the history
VideoDecoder doesn't handle audio underflow very well,
and Blade Runner's VQA files don't have any audio prebuffer.

VQAPlayer doesn't handle it perfectly either, but underflow
happens a lot less. To be improved.
  • Loading branch information
madmoose authored and sev- committed Sep 29, 2016
1 parent a67e9e1 commit 8217ed6
Show file tree
Hide file tree
Showing 6 changed files with 161 additions and 139 deletions.
3 changes: 2 additions & 1 deletion engines/bladerunner/module.mk
Expand Up @@ -12,7 +12,8 @@ MODULE_OBJS = \
image.o \
outtake.o \
settings.o \
vqa_decoder.o
vqa_decoder.o \
vqa_player.o

# This module can be built as a plugin
ifeq ($(ENABLE_BLADERUNNER), DYNAMIC_PLUGIN)
Expand Down
34 changes: 16 additions & 18 deletions engines/bladerunner/outtake.cpp
Expand Up @@ -23,7 +23,9 @@
#include "bladerunner/outtake.h"

#include "bladerunner/bladerunner.h"
#include "bladerunner/vqa_decoder.h"
#include "bladerunner/vqa_player.h"

#include "audio/audiostream.h"

#include "common/debug.h"
#include "common/events.h"
Expand All @@ -43,31 +45,27 @@ void OuttakePlayer::play(const Common::String &name, bool noLocalization, int co
else
resName = name + "_E.VQA";

Common::SeekableReadStream *s = _vm->getResourceStream(resName);

_vqaDecoder = new VQADecoder();
_vqaDecoder->loadStream(s);
VQAPlayer vqa_player(_vm);

_vqaDecoder->start();
vqa_player.open(resName);

uint32 last = _vm->_system->getMillis();
_vm->_mixer->stopAll();
while (!_vm->shouldQuit()) {
Common::Event event;
while (_vm->_system->getEventManager()->pollEvent(event))
if ((event.type == Common::EVENT_KEYDOWN && event.kbd.keycode == Common::KEYCODE_ESCAPE) || event.type == Common::EVENT_LBUTTONUP)
return;

while (!_vqaDecoder->endOfVideo() && !_vm->shouldQuit()) {
if (_vqaDecoder->needsUpdate()) {
uint32 now = _vm->_system->getMillis();
debug("delta: %d", now - last);
last = now;
int frame = vqa_player.update();
if (frame == -3)
break;

const Graphics::Surface *surface = _vqaDecoder->decodeNextFrame();
if (frame >= 0) {
const Graphics::Surface *surface = vqa_player.getSurface();
_vm->_system->copyRectToScreen((const byte *)surface->getBasePtr(0, 0), surface->pitch, 0, 0, 640, 480);
_vm->_system->updateScreen();
}

Common::Event event;
while (_vm->_system->getEventManager()->pollEvent(event))
if ((event.type == Common::EVENT_KEYDOWN && event.kbd.keycode == Common::KEYCODE_ESCAPE) || event.type == Common::EVENT_LBUTTONUP)
return;

_vm->_system->delayMillis(10);
}
}
Expand Down
64 changes: 38 additions & 26 deletions engines/bladerunner/vqa_decoder.cpp
Expand Up @@ -128,7 +128,7 @@ VQADecoder::~VQADecoder() {
}

bool VQADecoder::loadStream(Common::SeekableReadStream *s) {
close();
// close();
_s = s;

IFFChunkHeader chd;
Expand Down Expand Up @@ -160,22 +160,22 @@ bool VQADecoder::loadStream(Common::SeekableReadStream *s) {
case kMSCI: rc = readMSCI(s, chd.size); break;
case kVQHD: rc = readVQHD(s, chd.size); break;
default:
debug("Unhandled chunk '%s'", strTag(chd.id));
warning("Unhandled chunk '%s'", strTag(chd.id));
s->skip(roundup(chd.size));
rc = true;
}

if (!rc) {
debug("failed to handle chunk %s", strTag(chd.id));
warning("failed to handle chunk %s", strTag(chd.id));
return false;
}
} while (chd.id != kFINF);

_videoTrack = new VQAVideoTrack(this);
addTrack(_videoTrack);
// addTrack(_videoTrack);

_audioTrack = new VQAAudioTrack(this);
addTrack(_audioTrack);
// addTrack(_audioTrack);

/*
for (int i = 0; i != _loopInfo.loopCount; ++i) {
Expand All @@ -189,6 +189,14 @@ bool VQADecoder::loadStream(Common::SeekableReadStream *s) {
return true;
}

const Graphics::Surface *VQADecoder::decodeVideoFrame() {
return _videoTrack->decodeVideoFrame();
}

Audio::SeekableAudioStream *VQADecoder::decodeAudioFrame() {
return _audioTrack->decodeAudioFrame();
}

void VQADecoder::readNextPacket() {
IFFChunkHeader chd;

Expand Down Expand Up @@ -286,6 +294,12 @@ bool VQADecoder::readVQHD(Common::SeekableReadStream *s, uint32 size)
debug("_header.unk5 %d", _header.unk5);
}

assert(_header.version == 2);
assert(_header.freq == 22050);
assert(_header.channels == 1);
assert(_header.bits == 16);
assert(_header.colors == 0);

return true;
}

Expand Down Expand Up @@ -345,18 +359,18 @@ bool VQADecoder::readMSCI(Common::SeekableReadStream *s, uint32 size)
{
case kVIEW:
_maxVIEWChunkSize = size;
debug("max VIEW size: %08x", _maxVIEWChunkSize);
// debug("max VIEW size: %08x", _maxVIEWChunkSize);
break;
case kZBUF:
_maxZBUFChunkSize = size;
debug("max ZBUF size: %08x", _maxZBUFChunkSize);
// debug("max ZBUF size: %08x", _maxZBUFChunkSize);
break;
case kAESC:
_maxAESCChunkSize = size;
debug("max AESC size: %08x", _maxAESCChunkSize);
// debug("max AESC size: %08x", _maxAESCChunkSize);
break;
default:
debug("Unknown tag in MSCT: %s", strTag(tag));
warning("Unknown tag in MSCT: %s", strTag(tag));
}

uint32 zero;
Expand Down Expand Up @@ -483,7 +497,7 @@ bool VQADecoder::readLNIN(Common::SeekableReadStream *s, uint32 size)

for (int i = 0; i != loopNamesCount; ++i) {
char *begin = names + loopNameOffsets[i];
size_t len = ((i == loopNamesCount) ? chd.size : loopNameOffsets[i+1]) - loopNameOffsets[i];
uint32 len = ((i == loopNamesCount) ? chd.size : loopNameOffsets[i+1]) - loopNameOffsets[i];

_loopInfo.loops[i].name = Common::String(begin, len);
}
Expand Down Expand Up @@ -571,8 +585,7 @@ Common::Rational VQADecoder::VQAVideoTrack::getFrameRate() const {
return _frameRate;
}

const Graphics::Surface *VQADecoder::VQAVideoTrack::decodeNextFrame() {
debug("_curFrame: %d", _curFrame);
const Graphics::Surface *VQADecoder::VQAVideoTrack::decodeVideoFrame() {
if (_hasNewFrame) {
decodeFrame((uint16*)_surface->getPixels());
_curFrame++;
Expand Down Expand Up @@ -607,7 +620,7 @@ bool VQADecoder::VQAVideoTrack::readVQFL(Common::SeekableReadStream *s, uint32 s

bool VQADecoder::VQAVideoTrack::readCBFZ(Common::SeekableReadStream *s, uint32 size) {
if (size > _maxCBFZSize) {
debug("%d > %d", size, _maxCBFZSize);
warning("readCBFZ: chunk too large: %d > %d", size, _maxCBFZSize);
return false;
}

Expand Down Expand Up @@ -871,41 +884,40 @@ bool VQADecoder::VQAVideoTrack::decodeFrame(uint16 *frame)
dstBlock += count;
break;
default:
debug("Undefined case %d", command >> 13);
warning("VQAVideoTrack::decodeFrame: Undefined case %d", command >> 13);
}
}

return true;
}

VQADecoder::VQAAudioTrack::VQAAudioTrack(VQADecoder *vqaDecoder) {
_audioStream = Audio::makeQueuingAudioStream(vqaDecoder->_header.freq, false);
_frequency = vqaDecoder->_header.freq;
}

VQADecoder::VQAAudioTrack::~VQAAudioTrack() {
delete _audioStream;
}

Audio::AudioStream *VQADecoder::VQAAudioTrack::getAudioStream() const {
return _audioStream;
Audio::SeekableAudioStream *VQADecoder::VQAAudioTrack::decodeAudioFrame() {
int16 *audioFrame = (int16*)malloc(4 * 735);
memset(audioFrame, 0, 4 * 735);

_adpcmDecoder.decode(_compressedAudioFrame, 735, audioFrame);

uint flags = Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN;

return Audio::makeRawStream((byte*)audioFrame, 4 * 735, _frequency, flags, DisposeAfterUse::YES);
}

bool VQADecoder::VQAAudioTrack::readSND2(Common::SeekableReadStream *s, uint32 size)
{
if (size != 735) {
error("audio frame size: %d", size);
warning("audio frame size: %d", size);
return false;
}

s->read(_compressedAudioFrame, roundup(size));

int16 *audioFrame = (int16*)malloc(4 * size);
memset(audioFrame, 0, 4 * size);

_adpcmDecoder.decode(_compressedAudioFrame, size, audioFrame);

_audioStream->queueBuffer((byte*)audioFrame, 4 * size, DisposeAfterUse::YES, Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN);

return true;
}

Expand Down
55 changes: 23 additions & 32 deletions engines/bladerunner/vqa_decoder.h
Expand Up @@ -38,17 +38,29 @@

namespace BladeRunner {

class VQADecoder : public Video::VideoDecoder
class VQADecoder
{
public:
VQADecoder();
~VQADecoder();

bool loadStream(Common::SeekableReadStream *s);

protected:
void readNextPacket();

const Graphics::Surface *decodeVideoFrame();
Audio::SeekableAudioStream *decodeAudioFrame();

uint16 numFrames() const { return _header.numFrames; }
uint8 frameRate() const { return _header.frameRate; }

uint16 offsetX() const { return _header.offsetX; }
uint16 offsetY() const { return _header.offsetY; }

uint16 frequency() const { return _header.freq; }

protected:

private:
struct Header
{
Expand Down Expand Up @@ -121,9 +133,6 @@ class VQADecoder : public Video::VideoDecoder
VQAVideoTrack *_videoTrack;
VQAAudioTrack *_audioTrack;

// bool _hasView;
// view_t view;

bool readVQHD(Common::SeekableReadStream *s, uint32 size);
bool readMSCI(Common::SeekableReadStream *s, uint32 size);
bool readMFCI(Common::SeekableReadStream *s, uint32 size);
Expand All @@ -133,7 +142,7 @@ class VQADecoder : public Video::VideoDecoder
bool readLNIN(Common::SeekableReadStream *s, uint32 size);
bool readCLIP(Common::SeekableReadStream *s, uint32 size);

class VQAVideoTrack : public FixedRateVideoTrack {
class VQAVideoTrack {
public:
VQAVideoTrack(VQADecoder *vqaDecoder);
~VQAVideoTrack();
Expand All @@ -143,7 +152,7 @@ class VQADecoder : public Video::VideoDecoder
Graphics::PixelFormat getPixelFormat() const;
int getCurFrame() const;
int getFrameCount() const;
const Graphics::Surface *decodeNextFrame();
const Graphics::Surface *decodeVideoFrame();

bool readVQFR(Common::SeekableReadStream *s, uint32 size);
bool readVPTR(Common::SeekableReadStream *s, uint32 size);
Expand All @@ -157,6 +166,8 @@ class VQADecoder : public Video::VideoDecoder
protected:
Common::Rational getFrameRate() const;

bool useAudioSync() const { return false; }

private:
Graphics::Surface *_surface;
bool _hasNewFrame;
Expand All @@ -172,56 +183,36 @@ class VQADecoder : public Video::VideoDecoder
uint32 _maxCBFZSize;
uint32 _maxZBUFChunkSize;

size_t _codebookSize;
uint32 _codebookSize;
uint8 *_codebook;
uint8 *_cbfz;
uint8 *_zbufChunk;

size_t _vptrSize;
uint32 _vptrSize;
uint8 *_vptr;

int _curFrame;


void VPTRWriteBlock(uint16 *frame, unsigned int dstBlock, unsigned int srcBlock, int count, bool alpha = false);
bool decodeFrame(uint16 *frame);
};

class VQAAudioTrack : public AudioTrack {
class VQAAudioTrack {
public:
VQAAudioTrack(VQADecoder *vqaDecoder);
~VQAAudioTrack();

bool readSND2(Common::SeekableReadStream *s, uint32 size);
bool readSN2J(Common::SeekableReadStream *s, uint32 size);

Audio::SeekableAudioStream *decodeAudioFrame();
protected:
Audio::AudioStream *getAudioStream() const;

private:
Audio::QueuingAudioStream *_audioStream;

uint16 _frequency;
ADPCMWestwoodDecoder _adpcmDecoder;
uint8 _compressedAudioFrame[735];
};

/*
bool readFrame();
int getFrameTime() { return 1000 / _header.frameRate; }
void VPTRWriteBlock(uint16 *frame, unsigned int dstBlock, unsigned int srcBlock, int count, bool alpha = false) const;
bool seekToFrame(int frame);
bool decodeFrame(uint16 *frame);
int16 *getAudioFrame();
// bool get_view(view_t *view);
bool getZBUF(uint16 *zbuf);
friend class VQAPlayer;
*/
};

}; // End of namespace BladeRunner
Expand Down

0 comments on commit 8217ed6

Please sign in to comment.