aboutsummaryrefslogtreecommitdiff
path: root/video/qt_decoder.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'video/qt_decoder.cpp')
-rw-r--r--video/qt_decoder.cpp214
1 files changed, 174 insertions, 40 deletions
diff --git a/video/qt_decoder.cpp b/video/qt_decoder.cpp
index b4dab9ddfb..7539d4a1e2 100644
--- a/video/qt_decoder.cpp
+++ b/video/qt_decoder.cpp
@@ -110,7 +110,7 @@ const Graphics::Surface *QuickTimeDecoder::decodeNextFrame() {
return frame;
}
-Common::QuickTimeParser::SampleDesc *QuickTimeDecoder::readSampleDesc(Common::QuickTimeParser::Track *track, uint32 format) {
+Common::QuickTimeParser::SampleDesc *QuickTimeDecoder::readSampleDesc(Common::QuickTimeParser::Track *track, uint32 format, uint32 descSize) {
if (track->codecType == CODEC_TYPE_VIDEO) {
debug(0, "Video Codec FourCC: \'%s\'", tag2str(format));
@@ -205,7 +205,7 @@ Common::QuickTimeParser::SampleDesc *QuickTimeDecoder::readSampleDesc(Common::Qu
}
// Pass it on up
- return Audio::QuickTimeAudioDecoder::readSampleDesc(track, format);
+ return Audio::QuickTimeAudioDecoder::readSampleDesc(track, format, descSize);
}
void QuickTimeDecoder::init() {
@@ -333,6 +333,7 @@ QuickTimeDecoder::VideoTrackHandler::VideoTrackHandler(QuickTimeDecoder *decoder
_scaledSurface = 0;
_curPalette = 0;
_dirtyPalette = false;
+ _reversed = false;
}
QuickTimeDecoder::VideoTrackHandler::~VideoTrackHandler() {
@@ -344,29 +345,39 @@ QuickTimeDecoder::VideoTrackHandler::~VideoTrackHandler() {
bool QuickTimeDecoder::VideoTrackHandler::endOfTrack() const {
// A track is over when we've finished going through all edits
- return _curEdit == _parent->editCount;
+ return _reversed ? (_curEdit == 0 && _curFrame < 0) : atLastEdit();
}
bool QuickTimeDecoder::VideoTrackHandler::seek(const Audio::Timestamp &requestedTime) {
- // First, figure out what edit we're in
- Audio::Timestamp time = requestedTime.convertToFramerate(_parent->timeScale);
-
- // Continue until we get to where we need to be
- for (_curEdit = 0; !endOfTrack(); _curEdit++)
- if ((uint32)time.totalNumberOfFrames() >= getCurEditTimeOffset() && (uint32)time.totalNumberOfFrames() < getCurEditTimeOffset() + getCurEditTrackDuration())
+ 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)
break;
- // This track is done
- if (endOfTrack())
+ // If we did reach the end of the track, break out
+ if (atLastEdit())
+ return true;
+
+ // If this track is in an empty edit, position us at the next non-empty
+ // edit. There's nothing else to do after this.
+ if (_parent->editList[_curEdit].mediaTime == -1) {
+ while (!atLastEdit() && _parent->editList[_curEdit].mediaTime == -1)
+ _curEdit++;
+
+ if (!atLastEdit())
+ enterNewEditList(true);
+
return true;
+ }
enterNewEditList(false);
// One extra check for the end of a track
- if (endOfTrack())
+ if (atLastEdit())
return true;
// Now we're in the edit and need to figure out what frame we need
+ Audio::Timestamp time = requestedTime.convertToFramerate(_parent->timeScale);
while (getRateAdjustedFrameTime() < (uint32)time.totalNumberOfFrames()) {
_curFrame++;
if (_durationOverride >= 0) {
@@ -381,17 +392,24 @@ bool QuickTimeDecoder::VideoTrackHandler::seek(const Audio::Timestamp &requested
// Compare the starting point for the frame to where we need to be
_holdNextFrameStartTime = getRateAdjustedFrameTime() != (uint32)time.totalNumberOfFrames();
- // If we went past the time, go back a frame
+ // If we went past the time, go back a frame. _curFrame before this point is at the frame
+ // that should be displayed. This adjustment ensures it is on the frame before the one that
+ // should be displayed.
if (_holdNextFrameStartTime)
_curFrame--;
- // Handle the keyframe here
- int32 destinationFrame = _curFrame + 1;
+ if (_reversed) {
+ // Call setReverse again to update
+ setReverse(true);
+ } else {
+ // Handle the keyframe here
+ int32 destinationFrame = _curFrame + 1;
- assert(destinationFrame < (int32)_parent->frameCount);
- _curFrame = findKeyFrame(destinationFrame) - 1;
- while (_curFrame < destinationFrame - 1)
- bufferNextFrame();
+ assert(destinationFrame < (int32)_parent->frameCount);
+ _curFrame = findKeyFrame(destinationFrame) - 1;
+ while (_curFrame < destinationFrame - 1)
+ bufferNextFrame();
+ }
return true;
}
@@ -428,27 +446,54 @@ const Graphics::Surface *QuickTimeDecoder::VideoTrackHandler::decodeNextFrame()
if (endOfTrack())
return 0;
+ if (_reversed) {
+ // Subtract one to place us on the frame before the current displayed frame.
+ _curFrame--;
+
+ // We have one "dummy" frame at the end to so the last frame is displayed
+ // for the right amount of time.
+ if (_curFrame < 0)
+ return 0;
+
+ // Decode from the last key frame to the frame before the one we need.
+ // TODO: Probably would be wise to do some caching
+ int targetFrame = _curFrame;
+ _curFrame = findKeyFrame(targetFrame) - 1;
+ while (_curFrame != targetFrame - 1)
+ bufferNextFrame();
+ }
+
const Graphics::Surface *frame = bufferNextFrame();
- if (_holdNextFrameStartTime) {
- // Don't set the next frame start time here; we just did a seek
- _holdNextFrameStartTime = false;
- } else if (_durationOverride >= 0) {
- // Use our own duration from the edit list calculation
- _nextFrameStartTime += _durationOverride;
- _durationOverride = -1;
+ if (_reversed) {
+ if (_holdNextFrameStartTime) {
+ // Don't set the next frame start time here; we just did a seek
+ _holdNextFrameStartTime = false;
+ } else {
+ // Just need to subtract the time
+ _nextFrameStartTime -= getFrameDuration();
+ }
} else {
- _nextFrameStartTime += getFrameDuration();
- }
+ if (_holdNextFrameStartTime) {
+ // Don't set the next frame start time here; we just did a seek
+ _holdNextFrameStartTime = false;
+ } else if (_durationOverride >= 0) {
+ // Use our own duration from the edit list calculation
+ _nextFrameStartTime += _durationOverride;
+ _durationOverride = -1;
+ } else {
+ _nextFrameStartTime += getFrameDuration();
+ }
- // Update the edit list, if applicable
- // HACK: We're also accepting the time minus one because edit lists
- // aren't as accurate as one would hope.
- if (!endOfTrack() && getRateAdjustedFrameTime() >= getCurEditTimeOffset() + getCurEditTrackDuration() - 1) {
- _curEdit++;
+ // Update the edit list, if applicable
+ // HACK: We're also accepting the time minus one because edit lists
+ // aren't as accurate as one would hope.
+ if (!atLastEdit() && getRateAdjustedFrameTime() >= getCurEditTimeOffset() + getCurEditTrackDuration() - 1) {
+ _curEdit++;
- if (!endOfTrack())
- enterNewEditList(true);
+ if (!atLastEdit())
+ enterNewEditList(true);
+ }
}
if (frame && (_parent->scaleFactorX != 1 || _parent->scaleFactorY != 1)) {
@@ -464,6 +509,68 @@ const Graphics::Surface *QuickTimeDecoder::VideoTrackHandler::decodeNextFrame()
return frame;
}
+bool QuickTimeDecoder::VideoTrackHandler::setReverse(bool reverse) {
+ _reversed = reverse;
+
+ if (_reversed) {
+ if (_parent->editCount != 1) {
+ // TODO: Myst's holo.mov needs this :(
+ warning("Can only set reverse without edits");
+ return false;
+ }
+
+ if (atLastEdit()) {
+ // If we're at the end of the video, go to the penultimate edit.
+ // The current frame is set to one beyond the last frame here;
+ // one "past" the currently displayed frame.
+ _curEdit = _parent->editCount - 1;
+ _curFrame = _parent->frameCount;
+ _nextFrameStartTime = _parent->editList[_curEdit].trackDuration + _parent->editList[_curEdit].timeOffset;
+ } else if (_holdNextFrameStartTime) {
+ // We just seeked, so "pivot" around the frame that should be displayed
+ _curFrame++;
+ _nextFrameStartTime -= getFrameDuration();
+ _curFrame++;
+ } else {
+ // We need to put _curFrame to be the one after the one that should be displayed.
+ // Since we're on the frame that should be displaying right now, add one.
+ _curFrame++;
+ }
+ } else {
+ // Update the edit list, if applicable
+ // HACK: We're also accepting the time minus one because edit lists
+ // aren't as accurate as one would hope.
+ if (!atLastEdit() && getRateAdjustedFrameTime() >= getCurEditTimeOffset() + getCurEditTrackDuration() - 1) {
+ _curEdit++;
+
+ if (atLastEdit())
+ return true;
+ }
+
+ if (_holdNextFrameStartTime) {
+ // We just seeked, so "pivot" around the frame that should be displayed
+ _curFrame--;
+ _nextFrameStartTime += getFrameDuration();
+ }
+
+ // We need to put _curFrame to be the one before the one that should be displayed.
+ // Since we're on the frame that should be displaying right now, subtract one.
+ // (As long as the current frame isn't -1, of course)
+ if (_curFrame > 0) {
+ // We then need to handle the keyframe situation
+ int targetFrame = _curFrame - 1;
+ _curFrame = findKeyFrame(targetFrame) - 1;
+ while (_curFrame < targetFrame)
+ bufferNextFrame();
+ } else if (_curFrame == 0) {
+ // Make us start at the first frame (no keyframe needed)
+ _curFrame--;
+ }
+ }
+
+ return true;
+}
+
Common::Rational QuickTimeDecoder::VideoTrackHandler::getScaledWidth() const {
return Common::Rational(_parent->width) / _parent->scaleFactorX;
}
@@ -545,10 +652,10 @@ uint32 QuickTimeDecoder::VideoTrackHandler::findKeyFrame(uint32 frame) const {
void QuickTimeDecoder::VideoTrackHandler::enterNewEditList(bool bufferFrames) {
// Bypass all empty edit lists first
- while (!endOfTrack() && _parent->editList[_curEdit].mediaTime == -1)
+ while (!atLastEdit() && _parent->editList[_curEdit].mediaTime == -1)
_curEdit++;
- if (endOfTrack())
+ if (atLastEdit())
return;
uint32 frameNum = 0;
@@ -557,6 +664,8 @@ void QuickTimeDecoder::VideoTrackHandler::enterNewEditList(bool bufferFrames) {
uint32 prevDuration = 0;
// Track down where the mediaTime is in the media
+ // This is basically time -> frame mapping
+ // Note that this code uses first frame = 0
for (int32 i = 0; i < _parent->timeToSampleCount && !done; i++) {
for (int32 j = 0; j < _parent->timeToSample[i].count; j++) {
if (totalDuration == (uint32)_parent->editList[_curEdit].mediaTime) {
@@ -577,10 +686,13 @@ void QuickTimeDecoder::VideoTrackHandler::enterNewEditList(bool bufferFrames) {
if (bufferFrames) {
// Track down the keyframe
+ // Then decode until the frame before target
_curFrame = findKeyFrame(frameNum) - 1;
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"
_curFrame = frameNum - 1;
}
@@ -589,6 +701,8 @@ void QuickTimeDecoder::VideoTrackHandler::enterNewEditList(bool bufferFrames) {
// Set an override for the duration since we came up in-between two frames
if (prevDuration != totalDuration)
_durationOverride = totalDuration - prevDuration;
+ else
+ _durationOverride = -1;
}
const Graphics::Surface *QuickTimeDecoder::VideoTrackHandler::bufferNextFrame() {
@@ -598,14 +712,18 @@ const Graphics::Surface *QuickTimeDecoder::VideoTrackHandler::bufferNextFrame()
uint32 descId;
Common::SeekableReadStream *frameData = getNextFramePacket(descId);
- if (!frameData || !descId || descId > _parent->sampleDescs.size())
+ if (!frameData || !descId || descId > _parent->sampleDescs.size()) {
+ delete frameData;
return 0;
+ }
// Find which video description entry we want
VideoSampleDesc *entry = (VideoSampleDesc *)_parent->sampleDescs[descId - 1];
- if (!entry->_videoCodec)
+ if (!entry->_videoCodec) {
+ delete frameData;
return 0;
+ }
const Graphics::Surface *frame = entry->_videoCodec->decodeImage(frameData);
delete frameData;
@@ -638,7 +756,19 @@ uint32 QuickTimeDecoder::VideoTrackHandler::getRateAdjustedFrameTime() const {
uint32 QuickTimeDecoder::VideoTrackHandler::getCurEditTimeOffset() const {
// Need to convert to the track scale
- return _parent->editList[_curEdit].timeOffset * _parent->timeScale / _decoder->_timeScale;
+
+ // We have to round the time off to the nearest in the scale, otherwise
+ // bad things happen. QuickTime docs are pretty silent on all this stuff,
+ // so this was found from samples. It doesn't help that this is really
+ // the only open source implementation of QuickTime edits.
+
+ uint32 mult = _parent->editList[_curEdit].timeOffset * _parent->timeScale;
+ uint32 result = mult / _decoder->_timeScale;
+
+ if ((mult % _decoder->_timeScale) > (_decoder->_timeScale / 2))
+ result++;
+
+ return result;
}
uint32 QuickTimeDecoder::VideoTrackHandler::getCurEditTrackDuration() const {
@@ -646,4 +776,8 @@ uint32 QuickTimeDecoder::VideoTrackHandler::getCurEditTrackDuration() const {
return _parent->editList[_curEdit].trackDuration * _parent->timeScale / _decoder->_timeScale;
}
+bool QuickTimeDecoder::VideoTrackHandler::atLastEdit() const {
+ return _curEdit == _parent->editCount;
+}
+
} // End of namespace Video