From bddceced34f829b0640d1aecacac1e1079eb0839 Mon Sep 17 00:00:00 2001 From: Matthew Hoops Date: Tue, 18 May 2010 14:59:20 +0000 Subject: Have QTPlayer inherit from VideoDecoder. The video downscaling (QuickTime is insane) has also been moved to the QTPlayer class. svn-id: r49081 --- engines/mohawk/video/qt_player.cpp | 146 +++++++++++++++++++++++++------------ engines/mohawk/video/qt_player.h | 79 ++++++++++---------- engines/mohawk/video/video.cpp | 36 +++------ 3 files changed, 145 insertions(+), 116 deletions(-) diff --git a/engines/mohawk/video/qt_player.cpp b/engines/mohawk/video/qt_player.cpp index 803da4f5f1..b19343005e 100644 --- a/engines/mohawk/video/qt_player.cpp +++ b/engines/mohawk/video/qt_player.cpp @@ -56,36 +56,38 @@ namespace Mohawk { // QTPlayer //////////////////////////////////////////// -QTPlayer::QTPlayer() { +QTPlayer::QTPlayer() : Graphics::VideoDecoder() { _audStream = NULL; _beginOffset = 0; _videoCodec = NULL; - _noCodecFound = false; _curFrame = -1; - _lastFrameStart = _nextFrameStart = 0; + _startTime = _nextFrameStartTime = 0; _audHandle = Audio::SoundHandle(); _numStreams = 0; + _fd = 0; + _scaledSurface = 0; + _dirtyPalette = false; } QTPlayer::~QTPlayer() { - stop(); + close(); } -uint16 QTPlayer::getWidth() { +uint16 QTPlayer::getWidth() const { if (_videoStreamIndex < 0) return 0; - return _streams[_videoStreamIndex]->width; + return _streams[_videoStreamIndex]->width / getScaleMode(); } -uint16 QTPlayer::getHeight() { +uint16 QTPlayer::getHeight() const { if (_videoStreamIndex < 0) return 0; - return _streams[_videoStreamIndex]->height; + return _streams[_videoStreamIndex]->height / getScaleMode(); } -uint32 QTPlayer::getFrameCount() { +uint32 QTPlayer::getFrameCount() const { if (_videoStreamIndex < 0) return 0; @@ -106,7 +108,7 @@ uint32 QTPlayer::getCodecTag() { return _streams[_videoStreamIndex]->codec_tag; } -ScaleMode QTPlayer::getScaleMode() { +ScaleMode QTPlayer::getScaleMode() const { if (_videoStreamIndex < 0) return kScaleNormal; @@ -121,8 +123,8 @@ uint32 QTPlayer::getFrameDuration() { for (int32 i = 0; i < _streams[_videoStreamIndex]->stts_count; i++) { curFrameIndex += _streams[_videoStreamIndex]->stts_data[i].count; if ((uint32)_curFrame < curFrameIndex) { - // Ok, now we have what duration this frame has. Now, we have to convert the duration to 1/100 ms. - return _streams[_videoStreamIndex]->stts_data[i].duration * 1000 * 100 / _streams[_videoStreamIndex]->time_scale; + // Ok, now we have what duration this frame has. + return _streams[_videoStreamIndex]->stts_data[i].duration; } } @@ -131,20 +133,17 @@ uint32 QTPlayer::getFrameDuration() { return 0; } -void QTPlayer::stop() { - stopAudio(); - - if (!_noCodecFound) - delete _videoCodec; +Graphics::PixelFormat QTPlayer::getPixelFormat() const { + if (!_videoCodec) + return Graphics::PixelFormat::createFormatCLUT8(); - closeFile(); + return _videoCodec->getPixelFormat(); } -void QTPlayer::reset() { +void QTPlayer::rewind() { delete _videoCodec; _videoCodec = NULL; - _noCodecFound = false; _curFrame = -1; - _lastFrameStart = _nextFrameStart = 0; + _startTime = _nextFrameStartTime = 0; // Restart the audio too stopAudio(); @@ -207,57 +206,82 @@ void QTPlayer::stopAudio() { _audStream = NULL; // the mixer automatically frees the stream } -Graphics::Surface *QTPlayer::getNextFrame() { - if (_noCodecFound || _curFrame >= (int32)getFrameCount() - 1) +Graphics::Surface *QTPlayer::decodeNextFrame() { + if (!_videoCodec || _curFrame >= (int32)getFrameCount() - 1) return NULL; - if (_nextFrameStart == 0) - _nextFrameStart = g_system->getMillis() * 100; + if (_startTime == 0) + _startTime = g_system->getMillis(); - _lastFrameStart = _nextFrameStart; _curFrame++; - _nextFrameStart = getFrameDuration() + _lastFrameStart; + _nextFrameStartTime += getFrameDuration(); Common::SeekableReadStream *frameData = getNextFramePacket(); - if (!_videoCodec) { - _videoCodec = createCodec(getCodecTag(), getBitsPerPixel()); - // If we don't get it still, the codec is unsupported ;) - if (!_videoCodec) { - _noCodecFound = true; - return NULL; - } - } - if (frameData) { Graphics::Surface *frame = _videoCodec->decodeImage(frameData); delete frameData; - return frame; + return scaleSurface(frame); } return NULL; } -bool QTPlayer::endOfVideo() { - return (!_audStream || _audStream->endOfData()) && (_noCodecFound || _curFrame >= (int32)getFrameCount() - 1); +Graphics::Surface *QTPlayer::scaleSurface(Graphics::Surface *frame) { + if (getScaleMode() == kScaleNormal) + return frame; + + assert(_scaledSurface); + + for (uint32 j = 0; j < _scaledSurface->h; j++) + for (uint32 k = 0; k < _scaledSurface->w; k++) + memcpy(_scaledSurface->getBasePtr(k, j), frame->getBasePtr(k * getScaleMode(), j * getScaleMode()), frame->bytesPerPixel); + + return _scaledSurface; } -bool QTPlayer::needsUpdate() { - if (endOfVideo()) - return false; +bool QTPlayer::endOfVideo() const { + return (!_audStream || _audStream->endOfData()) && (!_videoCodec || _curFrame >= (int32)getFrameCount() - 1); +} - if (_curFrame == -1) - return true; +void QTPlayer::addPauseTime(uint32 p) { + Graphics::VideoDecoder::addPauseTime(p); + if (_videoStreamIndex >= 0) + _nextFrameStartTime += p * _streams[_videoStreamIndex]->time_scale / 1000; +} + +bool QTPlayer::needsUpdate() const { + return !endOfVideo() && getTimeToNextFrame() == 0; +} + +uint32 QTPlayer::getElapsedTime() const { + if (_audStream) + return g_system->getMixer()->getSoundElapsedTime(_audHandle); + + return g_system->getMillis() - _startTime; +} + +uint32 QTPlayer::getTimeToNextFrame() const { + if (endOfVideo() || _curFrame < 0) + return 0; + + // Convert from the Sega FILM base to 1000 + uint32 nextFrameStartTime = _nextFrameStartTime * 1000 / _streams[_videoStreamIndex]->time_scale; + uint32 elapsedTime = getElapsedTime(); + + if (nextFrameStartTime <= elapsedTime) + return 0; - return (g_system->getMillis() * 100 - _lastFrameStart) >= getFrameDuration(); + return nextFrameStartTime - elapsedTime; } -bool QTPlayer::loadFile(Common::SeekableReadStream *stream) { - _fd = stream; +bool QTPlayer::load(Common::SeekableReadStream &stream) { + _fd = &stream; _foundMOOV = _foundMDAT = false; _numStreams = 0; _partial = 0; _videoStreamIndex = _audioStreamIndex = -1; + _startTime = 0; initParseTable(); @@ -313,6 +337,18 @@ bool QTPlayer::loadFile(Common::SeekableReadStream *stream) { // Make sure the bits per sample transfers to the sample size if (_streams[_audioStreamIndex]->codec_tag == MKID_BE('raw ') || _streams[_audioStreamIndex]->codec_tag == MKID_BE('twos')) _streams[_audioStreamIndex]->sample_size = (_streams[_audioStreamIndex]->bits_per_sample / 8) * _streams[_audioStreamIndex]->channels; + + startAudio(); + } + + if (_videoStreamIndex >= 0) { + _videoCodec = createCodec(getCodecTag(), getBitsPerPixel()); + + if (getScaleMode() != kScaleNormal) { + // We have to initialize the scaled surface + _scaledSurface = new Graphics::Surface(); + _scaledSurface->create(getWidth(), getHeight(), getPixelFormat().bytesPerPixel); + } } return true; @@ -785,6 +821,8 @@ int QTPlayer::readSTSD(MOVatom atom) { // if the depth is 2, 4, or 8 bpp, file is palettized if (colorDepth == 2 || colorDepth == 4 || colorDepth == 8) { + _dirtyPalette = true; + if (colorGreyscale) { debug(0, "Greyscale palette"); @@ -1078,14 +1116,26 @@ int QTPlayer::readWAVE(MOVatom atom) { return 0; } -void QTPlayer::closeFile() { +void QTPlayer::close() { + stopAudio(); + + delete _videoCodec; _videoCodec = 0; + for (uint32 i = 0; i < _numStreams; i++) delete _streams[i]; delete _fd; + if (_scaledSurface) { + _scaledSurface->free(); + delete _scaledSurface; + _scaledSurface = 0; + } + // The audio stream is deleted automatically _audStream = NULL; + + Graphics::VideoDecoder::reset(); } Common::SeekableReadStream *QTPlayer::getNextFramePacket() { diff --git a/engines/mohawk/video/qt_player.h b/engines/mohawk/video/qt_player.h index ce780e22e2..5234f7cc59 100644 --- a/engines/mohawk/video/qt_player.h +++ b/engines/mohawk/video/qt_player.h @@ -36,7 +36,10 @@ #include "common/scummsys.h" #include "common/queue.h" + +#include "graphics/video/video_decoder.h" #include "graphics/video/codecs/codec.h" + #include "sound/audiostream.h" #include "sound/mixer.h" @@ -52,7 +55,7 @@ enum ScaleMode { kScaleQuarter = 4 }; -class QTPlayer { +class QTPlayer : public Graphics::VideoDecoder { public: QTPlayer(); virtual ~QTPlayer(); @@ -61,54 +64,37 @@ public: * Returns the width of the video * @return the width of the video */ - uint16 getWidth(); + uint16 getWidth() const; /** * Returns the height of the video * @return the height of the video */ - uint16 getHeight(); + uint16 getHeight() const; /** * Returns the amount of frames in the video * @return the amount of frames in the video */ - uint32 getFrameCount(); - - /** - * Returns the bits per pixel of the video - * @return the bits per pixel of the video - */ - byte getBitsPerPixel(); - - /** - * Returns the codec tag of the video - * @return the codec tag of the video - */ - uint32 getCodecTag(); + uint32 getFrameCount() const; /** * Load a QuickTime video file from a SeekableReadStream * @param stream the stream to load */ - bool loadFile(Common::SeekableReadStream* stream); + bool load(Common::SeekableReadStream &stream); /** * Close a QuickTime encoded video file */ - void closeFile(); - - /** - * Returns the downscale mode of the video - * @return the downscale mode of the video - */ - ScaleMode getScaleMode(); + void close(); /** * Returns the palette of the video * @return the palette of the video */ - byte *getPalette() { return _palette; } + byte *getPalette() { _dirtyPalette = false; return _palette; } + bool hasDirtyPalette() const { return _dirtyPalette; } /** * Set the beginning offset of the video so we can modify the offsets in the stco @@ -117,18 +103,22 @@ public: */ void setChunkBeginOffset(uint32 offset) { _beginOffset = offset; } - int32 getCurFrame() { return _curFrame; } - void addPauseTime(uint32 p) { _lastFrameStart += p; _nextFrameStart += p; } - Graphics::Surface *getNextFrame(); - void updateAudioBuffer(); - void startAudio(); - void stopAudio(); + bool isVideoLoaded() const { return _fd != 0; } + void addPauseTime(uint32 p); + Graphics::Surface *decodeNextFrame(); + bool needsUpdate() const; + bool endOfVideo() const; + uint32 getElapsedTime() const; + uint32 getTimeToNextFrame() const; + Graphics::PixelFormat getPixelFormat() const; + + void rewind(); // For a future RewindableVideoDecoder class + + // TODO: These audio functions need to be removed from the public and/or added to + // the VideoDecoder API directly. + void updateAudioBuffer(); // This is going to be problematic. void pauseAudio(); void resumeAudio(); - bool needsUpdate(); - bool endOfVideo(); - void stop(); - void reset(); protected: // This is the file handle from which data is read from. It can be the actual file handle or a decompressed stream. @@ -238,25 +228,32 @@ protected: ScaleMode _scaleMode; MOVStreamContext *_streams[20]; byte _palette[256 * 4]; + bool _dirtyPalette; + uint32 _beginOffset; void initParseTable(); Audio::AudioStream *createAudioStream(Common::SeekableReadStream *stream); bool checkAudioCodecSupport(uint32 tag); Common::SeekableReadStream *getNextFramePacket(); uint32 getFrameDuration(); + uint32 getCodecTag(); + byte getBitsPerPixel(); Audio::QueuingAudioStream *_audStream; - int8 _videoStreamIndex; + void startAudio(); + void stopAudio(); int8 _audioStreamIndex; uint _curAudioChunk; - uint32 _beginOffset; + Audio::SoundHandle _audHandle; Graphics::Codec *createCodec(uint32 codecTag, byte bitsPerPixel); Graphics::Codec *_videoCodec; - bool _noCodecFound; - int32 _curFrame; - uint32 _lastFrameStart, _nextFrameStart; // In 1/100 ms - Audio::SoundHandle _audHandle; + uint32 _nextFrameStartTime; + int8 _videoStreamIndex; + + Graphics::Surface *_scaledSurface; + Graphics::Surface *scaleSurface(Graphics::Surface *frame); + ScaleMode getScaleMode() const; int readDefault(MOVatom atom); int readLeaf(MOVatom atom); diff --git a/engines/mohawk/video/video.cpp b/engines/mohawk/video/video.cpp index c0206a5f06..c4991ec06c 100644 --- a/engines/mohawk/video/video.cpp +++ b/engines/mohawk/video/video.cpp @@ -126,7 +126,7 @@ void VideoManager::waitUntilMovieEnds(VideoHandle videoHandle) { _vm->_system->delayMillis(10); } - _videoStreams[videoHandle]->stop(); + _videoStreams[videoHandle]->close(); _videoStreams.clear(); } @@ -155,7 +155,7 @@ bool VideoManager::updateBackgroundMovies() { // Remove any videos that are over if (_videoStreams[i]->endOfVideo()) { if (_videoStreams[i].loop) { - _videoStreams[i]->reset(); + _videoStreams[i]->rewind(); } else { delete _videoStreams[i].video; memset(&_videoStreams[i], 0, sizeof(VideoEntry)); @@ -166,7 +166,7 @@ bool VideoManager::updateBackgroundMovies() { // Check if we need to draw a frame if (_videoStreams[i]->needsUpdate()) { - Graphics::Surface *frame = _videoStreams[i]->getNextFrame(); + Graphics::Surface *frame = _videoStreams[i]->decodeNextFrame(); bool deleteFrame = false; if (frame && _videoStreams[i].enabled) { @@ -196,26 +196,10 @@ bool VideoManager::updateBackgroundMovies() { deleteFrame = true; } - // Check if we're drawing at a 2x or 4x resolution (because of - // evil QuickTime scaling it first). - if (_videoStreams[i]->getScaleMode() == kScaleHalf || _videoStreams[i]->getScaleMode() == kScaleQuarter) { - byte scaleFactor = (_videoStreams[i]->getScaleMode() == kScaleHalf) ? 2 : 4; - - Graphics::Surface scaledSurf; - scaledSurf.create(_videoStreams[i]->getWidth() / scaleFactor, _videoStreams[i]->getHeight() / scaleFactor, frame->bytesPerPixel); - - for (uint32 j = 0; j < scaledSurf.h; j++) - for (uint32 k = 0; k < scaledSurf.w; k++) - memcpy(scaledSurf.getBasePtr(k, j), frame->getBasePtr(k * scaleFactor, j * scaleFactor), frame->bytesPerPixel); - - _vm->_system->copyRectToScreen((byte*)scaledSurf.pixels, scaledSurf.pitch, _videoStreams[i].x, _videoStreams[i].y, scaledSurf.w, scaledSurf.h); - scaledSurf.free(); - } else { - // Clip the width/height to make sure we stay on the screen (Myst does this a few times) - uint16 width = MIN(_videoStreams[i]->getWidth(), _vm->_system->getWidth() - _videoStreams[i].x); - uint16 height = MIN(_videoStreams[i]->getHeight(), _vm->_system->getHeight() - _videoStreams[i].y); - _vm->_system->copyRectToScreen((byte*)frame->pixels, frame->pitch, _videoStreams[i].x, _videoStreams[i].y, width, height); - } + // Clip the width/height to make sure we stay on the screen (Myst does this a few times) + uint16 width = MIN(_videoStreams[i]->getWidth(), _vm->_system->getWidth() - _videoStreams[i].x); + uint16 height = MIN(_videoStreams[i]->getHeight(), _vm->_system->getHeight() - _videoStreams[i].y); + _vm->_system->copyRectToScreen((byte*)frame->pixels, frame->pitch, _videoStreams[i].x, _videoStreams[i].y, width, height); // We've drawn something to the screen, make sure we update it updateScreen = true; @@ -346,8 +330,7 @@ VideoHandle VideoManager::createVideoHandle(uint16 id, uint16 x, uint16 y, bool entry.loop = loop; entry.enabled = true; entry->setChunkBeginOffset(_vm->getResourceOffset(ID_TMOV, id)); - entry->loadFile(_vm->getRawData(ID_TMOV, id)); - entry->startAudio(); + entry->load(*_vm->getRawData(ID_TMOV, id)); // Search for any deleted videos so we can take a formerly used slot for (uint32 i = 0; i < _videoStreams.size(); i++) @@ -383,8 +366,7 @@ VideoHandle VideoManager::createVideoHandle(Common::String filename, uint16 x, u return NULL_VID_HANDLE; } - entry->loadFile(file); - entry->startAudio(); + entry->load(*file); // Search for any deleted videos so we can take a formerly used slot for (uint32 i = 0; i < _videoStreams.size(); i++) -- cgit v1.2.3