diff options
Diffstat (limited to 'drivers/theoraplayer/src/TheoraVideoClip.cpp')
-rw-r--r-- | drivers/theoraplayer/src/TheoraVideoClip.cpp | 493 |
1 files changed, 493 insertions, 0 deletions
diff --git a/drivers/theoraplayer/src/TheoraVideoClip.cpp b/drivers/theoraplayer/src/TheoraVideoClip.cpp new file mode 100644 index 0000000000..3ee4b83370 --- /dev/null +++ b/drivers/theoraplayer/src/TheoraVideoClip.cpp @@ -0,0 +1,493 @@ +/************************************************************************************ +This source file is part of the Theora Video Playback Library +For latest info, see http://libtheoraplayer.googlecode.com +************************************************************************************* +Copyright (c) 2008-2014 Kresimir Spes (kspes@cateia.com) +This program is free software; you can redistribute it and/or modify it under +the terms of the BSD license: http://opensource.org/licenses/BSD-3-Clause +*************************************************************************************/ +#include "TheoraVideoClip.h" +#include "TheoraVideoManager.h" +#include "TheoraVideoFrame.h" +#include "TheoraFrameQueue.h" +#include "TheoraAudioInterface.h" +#include "TheoraTimer.h" +#include "TheoraDataSource.h" +#include "TheoraUtil.h" +#include "TheoraException.h" + +#include "core/os/memory.h" + +TheoraVideoClip::TheoraVideoClip(TheoraDataSource* data_source, + TheoraOutputMode output_mode, + int nPrecachedFrames, + bool usePower2Stride): + mAudioInterface(NULL), + mNumDroppedFrames(0), + mNumDisplayedFrames(0), + mSeekFrame(-1), + mDuration(-1), + mNumFrames(-1), + mFPS(1), + mUseAlpha(0), + mFrameDuration(0), + mName(data_source->repr()), + mStride(usePower2Stride), + mSubFrameWidth(0), + mSubFrameHeight(0), + mSubFrameOffsetX(0), + mSubFrameOffsetY(0), + mAudioGain(1), + mRequestedOutputMode(output_mode), + mAutoRestart(0), + mEndOfFile(0), + mRestarted(0), + mIteration(0), + mPlaybackIteration(0), + mStream(0), + mThreadAccessCount(0), + mPriority(1), + mFirstFrameDisplayed(0), + mWaitingForCache(false), + mOutputMode(TH_UNDEFINED) +{ + mAudioMutex = NULL; + mThreadAccessMutex = new TheoraMutex(); + mTimer = mDefaultTimer = new TheoraTimer(); + + mFrameQueue = NULL; + mAssignedWorkerThread = NULL; + mNumPrecachedFrames = nPrecachedFrames; + setOutputMode(output_mode); +} + +TheoraVideoClip::~TheoraVideoClip() +{ + // wait untill a worker thread is done decoding the frame + mThreadAccessMutex->lock(); + + delete mDefaultTimer; + + if (mStream) memdelete(mStream); + + if (mFrameQueue) delete mFrameQueue; + + if (mAudioInterface) + { + mAudioMutex->lock(); // ensure a thread isn't using this mutex + memdelete(mAudioInterface); // notify audio interface it's time to call it a day + mAudioMutex ->unlock(); + delete mAudioMutex; + } + + mThreadAccessMutex->unlock(); + + delete mThreadAccessMutex; +} + +TheoraTimer* TheoraVideoClip::getTimer() +{ + return mTimer; +} + +void TheoraVideoClip::setTimer(TheoraTimer* timer) +{ + if (!timer) mTimer = mDefaultTimer; + else mTimer = timer; +} + +void TheoraVideoClip::resetFrameQueue() +{ + mFrameQueue->clear(); + mPlaybackIteration = mIteration = 0; +} + +void TheoraVideoClip::restart() +{ + mEndOfFile = true; //temp, to prevent threads to decode while restarting + mThreadAccessMutex->lock(); + _restart(); + mTimer->seek(0); + mFirstFrameDisplayed = false; + resetFrameQueue(); + mEndOfFile = false; + mRestarted = false; + mSeekFrame = -1; + mThreadAccessMutex->unlock(); +} + +void TheoraVideoClip::update(float timeDelta) +{ + if (mTimer->isPaused()) + { + mTimer->update(0); // update timer in case there is some code that needs to execute each frame + return; + } + float time = mTimer->getTime(), speed = mTimer->getSpeed(); + if (time + timeDelta * speed >= mDuration) + { + if (mAutoRestart && mRestarted) + { + float seekTime = time + timeDelta * speed; + for (;seekTime >= mDuration;) + { + seekTime -= mDuration; + ++mPlaybackIteration; + } + + mTimer->seek(seekTime); + } + else + { + if (time != mDuration) + { + mTimer->update((mDuration - time) / speed); + } + } + } + else + { + mTimer->update(timeDelta); + } +} + +float TheoraVideoClip::updateToNextFrame() +{ + TheoraVideoFrame* f = mFrameQueue->getFirstAvailableFrame(); + if (!f) return 0; + + float time = f->mTimeToDisplay - mTimer->getTime(); + update(time); + return time; +} + +TheoraFrameQueue* TheoraVideoClip::getFrameQueue() +{ + return mFrameQueue; +} + +void TheoraVideoClip::popFrame() +{ + ++mNumDisplayedFrames; + + // after transfering frame data to the texture, free the frame + // so it can be used again + if (!mFirstFrameDisplayed) + { + mFrameQueue->lock(); + mFrameQueue->_pop(1); + mFirstFrameDisplayed = true; + mFrameQueue->unlock(); + } + else + { + mFrameQueue->pop(); + } +} + +int TheoraVideoClip::getWidth() +{ + return mUseAlpha ? mWidth / 2 : mWidth; +} + +int TheoraVideoClip::getHeight() +{ + return mHeight; +} + +int TheoraVideoClip::getSubFrameWidth() +{ + return mUseAlpha ? mWidth / 2 : mSubFrameWidth; +} + +int TheoraVideoClip::getSubFrameHeight() +{ + return mUseAlpha ? mHeight : mSubFrameHeight; +} + +int TheoraVideoClip::getSubFrameOffsetX() +{ + return mUseAlpha ? 0 : mSubFrameOffsetX; +} + +int TheoraVideoClip::getSubFrameOffsetY() +{ + return mUseAlpha ? 0 : mSubFrameOffsetY; +} + +float TheoraVideoClip::getAbsPlaybackTime() +{ + return mTimer->getTime() + mPlaybackIteration * mDuration; +} + +int TheoraVideoClip::discardOutdatedFrames(float absTime) +{ + int nReady = mFrameQueue->_getReadyCount(); + // only drop frames if you have more frames to show. otherwise even the late frame will do.. + if (nReady == 1) return 0; + float time = absTime; + + int nPop = 0; + TheoraVideoFrame* frame; + float timeToDisplay; + + std::list<TheoraVideoFrame*>& queue = mFrameQueue->_getFrameQueue(); + foreach_l (TheoraVideoFrame*, queue) + { + frame = *it; + if (!frame->mReady) break; + timeToDisplay = frame->mTimeToDisplay + frame->mIteration * mDuration; + if (time > timeToDisplay + mFrameDuration) + { + ++nPop; + if (nReady - nPop == 1) break; // always leave at least one in the queue + } + else break; + } + + if (nPop > 0) + { +#ifdef _DEBUG + std::string log = getName() + ": dropped frame "; + + int i = nPop; + foreach_l (TheoraVideoFrame*, queue) + { + log += str((int) (*it)->getFrameNumber()); + if (i-- > 1) + { + log += ", "; + } + else break; + } + th_writelog(log); +#endif + mNumDroppedFrames += nPop; + mFrameQueue->_pop(nPop); + } + + return nPop; +} + +TheoraVideoFrame* TheoraVideoClip::getNextFrame() +{ + TheoraVideoFrame* frame; + // if we are about to seek, then the current frame queue is invalidated + // (will be cleared when a worker thread does the actual seek) + if (mSeekFrame != -1) return NULL; + + mFrameQueue->lock(); + float time = getAbsPlaybackTime(); + discardOutdatedFrames(time); + + frame = mFrameQueue->_getFirstAvailableFrame(); + if (frame != NULL) + { + if (frame->mTimeToDisplay + frame->mIteration * mDuration > time && mFirstFrameDisplayed) + { + frame = NULL; // frame is ready but it's not yet time to display it, except when we haven't displayed any frames yet + } + } + + mFrameQueue->unlock(); + return frame; +} + +std::string TheoraVideoClip::getName() +{ + return mName; +} + +bool TheoraVideoClip::isBusy() +{ + return mAssignedWorkerThread || mOutputMode != mRequestedOutputMode; +} + +TheoraOutputMode TheoraVideoClip::getOutputMode() +{ + return mOutputMode; +} + +void TheoraVideoClip::setOutputMode(TheoraOutputMode mode) +{ + if (mode == TH_UNDEFINED) throw TheoraGenericException("Invalid output mode: TH_UNDEFINED for video: " + mName); + if (mOutputMode == mode) return; + mRequestedOutputMode = mode; + mUseAlpha = (mode == TH_RGBA || + mode == TH_ARGB || + mode == TH_BGRA || + mode == TH_ABGR || + mode == TH_GREY3A || + mode == TH_AGREY3 || + mode == TH_YUVA || + mode == TH_AYUV); + if (mAssignedWorkerThread) + { + mThreadAccessMutex->lock(); + // discard current frames and recreate them + mFrameQueue->setSize(mFrameQueue->getSize()); + mThreadAccessMutex->unlock(); + + } + mOutputMode = mRequestedOutputMode; +} + +float TheoraVideoClip::getTimePosition() +{ + return mTimer->getTime(); +} + +int TheoraVideoClip::getNumPrecachedFrames() +{ + return mFrameQueue->getSize(); +} + +void TheoraVideoClip::setNumPrecachedFrames(int n) +{ + if (mFrameQueue->getSize() != n) + mFrameQueue->setSize(n); +} + +int TheoraVideoClip::_getNumReadyFrames() +{ + if (mSeekFrame != -1) return 0; + return mFrameQueue->_getReadyCount(); +} + +int TheoraVideoClip::getNumReadyFrames() +{ + if (mSeekFrame != -1) return 0; // we are about to seek, consider frame queue empty even though it will be emptied upon seek + return mFrameQueue->getReadyCount(); +} + +float TheoraVideoClip::getDuration() +{ + return mDuration; +} + +float TheoraVideoClip::getFPS() +{ + return mFPS; +} + +void TheoraVideoClip::play() +{ + mTimer->play(); +} + +void TheoraVideoClip::pause() +{ + mTimer->pause(); +} + +bool TheoraVideoClip::isPaused() +{ + return mTimer->isPaused(); +} + +bool TheoraVideoClip::isDone() +{ + return mEndOfFile && !mFrameQueue->getFirstAvailableFrame(); +} + +void TheoraVideoClip::stop() +{ + pause(); + resetFrameQueue(); + mFirstFrameDisplayed = false; + seek(0); +} + +void TheoraVideoClip::setPlaybackSpeed(float speed) +{ + mTimer->setSpeed(speed); +} + +float TheoraVideoClip::getPlaybackSpeed() +{ + return mTimer->getSpeed(); +} + +void TheoraVideoClip::seek(float time) +{ + seekToFrame((int) (time * getFPS())); +} + +void TheoraVideoClip::seekToFrame(int frame) +{ + if (frame < 0) mSeekFrame = 0; + else if (frame > mNumFrames) mSeekFrame = mNumFrames; + else mSeekFrame = frame; + + mFirstFrameDisplayed = false; + mEndOfFile = false; +} + +void TheoraVideoClip::waitForCache(float desired_cache_factor, float max_wait_time) +{ + mWaitingForCache = true; + bool paused = mTimer->isPaused(); + if (!paused) mTimer->pause(); + int elapsed = 0; + int desired_num_precached_frames = (int) (desired_cache_factor * getNumPrecachedFrames()); + while (getNumReadyFrames() < desired_num_precached_frames) + { + _psleep(10); + elapsed += 10; + if (elapsed >= max_wait_time * 1000) break; + } + if (!paused) mTimer->play(); + mWaitingForCache = false; +} + +float TheoraVideoClip::getPriority() +{ + return mPriority; +} + +void TheoraVideoClip::setPriority(float priority) +{ + mPriority = priority; +} + +float TheoraVideoClip::getPriorityIndex() +{ + float priority = (float) getNumReadyFrames(); + if (mTimer->isPaused()) priority += getNumPrecachedFrames() / 2; + + return priority; +} + +void TheoraVideoClip::setAudioInterface(TheoraAudioInterface* iface) +{ + mAudioInterface = iface; + if (iface && !mAudioMutex) mAudioMutex = new TheoraMutex; + if (!iface && mAudioMutex) + { + delete mAudioMutex; + mAudioMutex = NULL; + } +} + +TheoraAudioInterface* TheoraVideoClip::getAudioInterface() +{ + return mAudioInterface; +} + +void TheoraVideoClip::setAudioGain(float gain) +{ + if (gain > 1) mAudioGain=1; + if (gain < 0) mAudioGain=0; + else mAudioGain=gain; +} + +float TheoraVideoClip::getAudioGain() +{ + return mAudioGain; +} + +void TheoraVideoClip::setAutoRestart(bool value) +{ + mAutoRestart = value; + if (value) mEndOfFile = false; +} |