VIDEO: Fix QuickTime regression with mediaTime and dithering

Fixes an error when playing a QuickTime video that has a mediaTime set
on its first edit. This was caused by mediaTime changes in:
ef184a6cef98a1756c0669a03fa9b6fa2386b6e7

Those changes buffered frames during initialization so that the keyframe
and other frames don't play instead of the intended start frame that
mediaTime specifies. My mistake was that decoding isn't allowed during
VideoDecoder::loadStream(); VideoDecoder::setDitheringPalette() requires
that no frames have been decoded yet, and at least Director and Mohawk
call that.

Now the initial mediaTime buffering is delayed until the first decode.

Fixes bug #13479 where certain Myst videos error.
Fixes the opening movie in the Director game Chop Suey.
This commit is contained in:
sluicebox 2022-08-03 17:32:03 -04:00
parent f1099a5cf2
commit 69662ee9a4
2 changed files with 28 additions and 5 deletions

View File

@ -299,7 +299,8 @@ QuickTimeDecoder::VideoTrackHandler::VideoTrackHandler(QuickTimeDecoder *decoder
_curEdit = 0;
_curFrame = -1;
enterNewEditListEntry(true); // might set _curFrame
_delayedFrameToBufferTo = -1;
enterNewEditListEntry(true, true); // might set _curFrame
_durationOverride = -1;
_scaledSurface = 0;
@ -369,6 +370,8 @@ bool QuickTimeDecoder::VideoTrackHandler::endOfTrack() const {
}
bool QuickTimeDecoder::VideoTrackHandler::seek(const Audio::Timestamp &requestedTime) {
_delayedFrameToBufferTo = -1; // abort any delayed buffering
uint32 convertedFrames = requestedTime.convertToFramerate(_decoder->_timeScale).totalNumberOfFrames();
for (_curEdit = 0; !atLastEdit(); _curEdit++)
if (convertedFrames >= _parent->editList[_curEdit].timeOffset && convertedFrames < _parent->editList[_curEdit].timeOffset + _parent->editList[_curEdit].trackDuration)
@ -580,6 +583,8 @@ const byte *QuickTimeDecoder::VideoTrackHandler::getPalette() const {
}
bool QuickTimeDecoder::VideoTrackHandler::setReverse(bool reverse) {
_delayedFrameToBufferTo = -1; // abort any delayed buffering
_reversed = reverse;
if (_reversed) {
@ -721,7 +726,7 @@ bool QuickTimeDecoder::VideoTrackHandler::isEmptyEdit() const {
return (_parent->editList[_curEdit].mediaTime == -1);
}
void QuickTimeDecoder::VideoTrackHandler::enterNewEditListEntry(bool bufferFrames) {
void QuickTimeDecoder::VideoTrackHandler::enterNewEditListEntry(bool bufferFrames, bool initializingTrack) {
if (atLastEdit())
return;
@ -765,8 +770,15 @@ void QuickTimeDecoder::VideoTrackHandler::enterNewEditListEntry(bool bufferFrame
// Track down the keyframe
// Then decode until the frame before target
_curFrame = findKeyFrame(frameNum) - 1;
while (_curFrame < (int32)frameNum - 1)
bufferNextFrame();
if (initializingTrack) {
// We can't decode frames during track initialization,
// so delay buffering until the first decode.
_delayedFrameToBufferTo = (int32)frameNum - 1;
} else {
while (_curFrame < (int32)frameNum - 1) {
bufferNextFrame();
}
}
} else {
// Since frameNum is the frame that needs to be displayed
// we'll set _curFrame to be the "last frame displayed"
@ -777,6 +789,16 @@ void QuickTimeDecoder::VideoTrackHandler::enterNewEditListEntry(bool bufferFrame
}
const Graphics::Surface *QuickTimeDecoder::VideoTrackHandler::bufferNextFrame() {
// Buffer any frames that were identified during track initialization
// and delayed until decoding.
if (_delayedFrameToBufferTo != -1) {
int32 frameNum = _delayedFrameToBufferTo;
_delayedFrameToBufferTo = -1;
while (_curFrame < frameNum) {
bufferNextFrame();
}
}
_curFrame++;
// Get the next packet

View File

@ -155,6 +155,7 @@ private:
Common::QuickTimeParser::Track *_parent;
uint32 _curEdit;
int32 _curFrame;
int32 _delayedFrameToBufferTo;
uint32 _nextFrameStartTime; // media time
Graphics::Surface *_scaledSurface;
int32 _durationOverride; // media time
@ -172,7 +173,7 @@ private:
uint32 getCurFrameDuration(); // media time
uint32 findKeyFrame(uint32 frame) const;
bool isEmptyEdit() const;
void enterNewEditListEntry(bool bufferFrames);
void enterNewEditListEntry(bool bufferFrames, bool intializingTrack = false);
const Graphics::Surface *bufferNextFrame();
uint32 getRateAdjustedFrameTime() const; // media time
uint32 getCurEditTimeOffset() const; // media time