diff options
Diffstat (limited to 'drivers/theoraplayer/src/TheoraVideoManager.cpp')
-rw-r--r-- | drivers/theoraplayer/src/TheoraVideoManager.cpp | 485 |
1 files changed, 0 insertions, 485 deletions
diff --git a/drivers/theoraplayer/src/TheoraVideoManager.cpp b/drivers/theoraplayer/src/TheoraVideoManager.cpp deleted file mode 100644 index 53b211374a..0000000000 --- a/drivers/theoraplayer/src/TheoraVideoManager.cpp +++ /dev/null @@ -1,485 +0,0 @@ -/************************************************************************************ -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 "TheoraVideoManager.h" -#include "TheoraWorkerThread.h" -#include "TheoraVideoClip.h" -#include "TheoraFrameQueue.h" -#include "TheoraAudioInterface.h" -#include "TheoraUtil.h" -#include "TheoraDataSource.h" -#include "TheoraException.h" -#ifdef __THEORA - #include <theora/codec.h> - #include <vorbis/codec.h> - #include "TheoraVideoClip_Theora.h" -#endif -#ifdef __AVFOUNDATION - #include "TheoraVideoClip_AVFoundation.h" -#endif -#ifdef __FFMPEG - #include "TheoraVideoClip_FFmpeg.h" -#endif -#ifdef _ANDROID //libtheoraplayer addition for cpu feature detection - #include "cpu-features.h" -#endif -// declaring function prototype here so I don't have to put it in a header file -// it only needs to be used by this plugin and called once -extern "C" -{ - void initYUVConversionModule(); -} - -#include "core/os/memory.h" - -//#define _DECODING_BENCHMARK //uncomment to test average decoding time on a given device - - -// -------------------------- -//#define _SCHEDULING_DEBUG -#ifdef _SCHEDULING_DEBUG -float gThreadDiagnosticTimer = 0; -#endif -// -------------------------- - -#ifdef _DECODING_BENCHMARK -void benchmark(TheoraVideoClip* clip) -{ - int nPrecached = 256; - int n = nPrecached; - char msg[1024]; - clock_t t = clock(); - while (n > 0) - { - clip->waitForCache(1.0f, 1000000); - n -= 32; - clip->getFrameQueue()->clear(); - } - float diff = ((float) (clock() - t) * 1000.0f) / CLOCKS_PER_SEC; - sprintf(msg, "BENCHMARK: %s: Decoding %d frames took %.1fms (%.2fms average per frame)\n",clip->getName().c_str(), nPrecached, diff, diff / nPrecached); - TheoraVideoManager::getSingleton().logMessage(msg); - clip->seek(0); -} -#endif - -struct TheoraWorkCandidate -{ - TheoraVideoClip* clip; - float priority, queuedTime, workTime, entitledTime; -}; - -TheoraVideoManager* g_ManagerSingleton = NULL; - -void theora_writelog(std::string output) -{ - printf("%s\n", output.c_str()); -} - -void (*g_LogFuction)(std::string) = theora_writelog; - -void TheoraVideoManager::setLogFunction(void (*fn)(std::string)) -{ - g_LogFuction = fn; -} - -TheoraVideoManager* TheoraVideoManager::getSingletonPtr() -{ - return g_ManagerSingleton; -} - -TheoraVideoManager& TheoraVideoManager::getSingleton() -{ - return *g_ManagerSingleton; -} - -TheoraVideoManager::TheoraVideoManager(int num_worker_threads) : - mDefaultNumPrecachedFrames(8) -{ - if (num_worker_threads < 1) throw TheoraGenericException("Unable to create TheoraVideoManager, at least one worker thread is reqired"); - - g_ManagerSingleton = this; - - std::string msg = "Initializing Theora Playback Library (" + getVersionString() + ")\n"; -#ifdef __THEORA - msg += " - libtheora version: " + std::string(th_version_string()) + "\n" + - " - libvorbis version: " + std::string(vorbis_version_string()) + "\n"; -#endif -#ifdef _ANDROID - uint64_t features = android_getCpuFeaturesExt(); - char s[128]; - sprintf(s, " - Android: CPU Features: %u\n", (unsigned int) features); - msg += s; - if ((features & ANDROID_CPU_ARM_FEATURE_NEON) == 0) - msg += " - Android: NEON features NOT SUPPORTED by CPU\n"; - else - msg += " - Android: Detected NEON CPU features\n"; -#endif - -#ifdef __AVFOUNDATION - msg += " - using Apple AVFoundation classes.\n"; -#endif -#ifdef __FFMPEG - msg += " - using FFmpeg library.\n"; -#endif - - logMessage(msg + "------------------------------------"); - mAudioFactory = NULL; - mWorkMutex = new TheoraMutex(); - - // for CPU based yuv2rgb decoding - initYUVConversionModule(); - - createWorkerThreads(num_worker_threads); -} - -TheoraVideoManager::~TheoraVideoManager() -{ - destroyWorkerThreads(); - - mWorkMutex->lock(); - ClipList::iterator ci; - for (ci = mClips.begin(); ci != mClips.end(); ++ci) - delete (*ci); - mClips.clear(); - mWorkMutex->unlock(); - delete mWorkMutex; -} - -void TheoraVideoManager::logMessage(std::string msg) -{ - g_LogFuction(msg); -} - -TheoraVideoClip* TheoraVideoManager::getVideoClipByName(std::string name) -{ - TheoraVideoClip* clip = NULL; - mWorkMutex->lock(); - - foreach(TheoraVideoClip*, mClips) - { - if ((*it)->getName() == name) - { - clip = *it; - break; - } - } - mWorkMutex->unlock(); - - return clip; -} - -void TheoraVideoManager::setAudioInterfaceFactory(TheoraAudioInterfaceFactory* factory) -{ - mAudioFactory = factory; -} - -TheoraAudioInterfaceFactory* TheoraVideoManager::getAudioInterfaceFactory() -{ - return mAudioFactory; -} - -TheoraVideoClip* TheoraVideoManager::createVideoClip(std::string filename, - TheoraOutputMode output_mode, - int numPrecachedOverride, - bool usePower2Stride, - int p_track) -{ - TheoraDataSource* src=memnew(TheoraFileDataSource(filename)); - return createVideoClip(src,output_mode,numPrecachedOverride,usePower2Stride, p_track); -} - -TheoraVideoClip* TheoraVideoManager::createVideoClip(TheoraDataSource* data_source, - TheoraOutputMode output_mode, - int numPrecachedOverride, - bool usePower2Stride, - int p_audio_track) -{ - mWorkMutex->lock(); - - TheoraVideoClip* clip = NULL; - int nPrecached = numPrecachedOverride ? numPrecachedOverride : mDefaultNumPrecachedFrames; - logMessage("Creating video from data source: " + data_source->repr() + " [" + str(nPrecached) + " precached frames]."); - -#ifdef __AVFOUNDATION - TheoraFileDataSource* fileDataSource = dynamic_cast<TheoraFileDataSource*>(data_source); - std::string filename; - if (fileDataSource == NULL) - { - TheoraMemoryFileDataSource* memoryDataSource = dynamic_cast<TheoraMemoryFileDataSource*>(data_source); - if (memoryDataSource != NULL) filename = memoryDataSource->getFilename(); - // if the user has his own data source, it's going to be a problem for AVAssetReader since it only supports reading from files... - } - else filename = fileDataSource->getFilename(); - - if (filename.size() > 4 && filename.substr(filename.size() - 4, filename.size()) == ".mp4") - { - clip = new TheoraVideoClip_AVFoundation(data_source, output_mode, nPrecached, usePower2Stride); - } -#endif -#if defined(__AVFOUNDATION) && defined(__THEORA) - else -#endif -#ifdef __THEORA - clip = new TheoraVideoClip_Theora(data_source, output_mode, nPrecached, usePower2Stride); -#endif -#ifdef __FFMPEG - clip = new TheoraVideoClip_FFmpeg(data_source, output_mode, nPrecached, usePower2Stride); -#endif - - clip->set_audio_track(p_audio_track); - clip->load(data_source); - clip->decodeNextFrame(); // ensure the first frame is always preloaded and have the main thread do it to prevent potential thread starvatio - - mClips.push_back(clip); - mWorkMutex->unlock(); - -#ifdef _DECODING_BENCHMARK - benchmark(clip); -#endif - return clip; -} - -void TheoraVideoManager::destroyVideoClip(TheoraVideoClip* clip) -{ - if (clip) - { - th_writelog("Destroying video clip: " + clip->getName()); - mWorkMutex->lock(); - bool reported = 0; - while (clip->mAssignedWorkerThread) - { - if (!reported) - { - th_writelog(" - Waiting for WorkerThread to finish decoding in order to destroy"); - reported = 1; - } - _psleep(1); - } - if (reported) th_writelog(" - WorkerThread done, destroying..."); - - // erase the clip from the clip list - foreach (TheoraVideoClip*, mClips) - { - if ((*it) == clip) - { - mClips.erase(it); - break; - } - } - // remove all it's references from the work log - mWorkLog.remove(clip); - - // delete the actual clip - delete clip; -#ifdef _DEBUG - th_writelog("Destroyed video."); -#endif - mWorkMutex->unlock(); - } -} - -TheoraVideoClip* TheoraVideoManager::requestWork(TheoraWorkerThread* caller) -{ - if (!mWorkMutex) return NULL; - mWorkMutex->lock(); - - TheoraVideoClip* selectedClip = NULL; - float maxQueuedTime = 0, totalAccessCount = 0, prioritySum = 0, diff, maxDiff = -1; - int nReadyFrames; - std::vector<TheoraWorkCandidate> candidates; - TheoraVideoClip* clip; - TheoraWorkCandidate candidate; - - // first pass is for playing videos, but if no such videos are available for decoding - // paused videos are selected in the second pass. - // Note that paused videos that are waiting for cache are considered equal to playing - // videos in the scheduling context - - for (int i = 0; i < 2 && candidates.size() == 0; ++i) - { - foreach (TheoraVideoClip*, mClips) - { - clip = *it; - if (clip->isBusy() || (i == 0 && clip->isPaused() && !clip->mWaitingForCache)) continue; - nReadyFrames = clip->getNumReadyFrames(); - if (nReadyFrames == clip->getFrameQueue()->getSize()) continue; - - candidate.clip = clip; - candidate.priority = clip->getPriority(); - candidate.queuedTime = (float) nReadyFrames / (clip->getFPS() * clip->getPlaybackSpeed()); - candidate.workTime = (float) clip->mThreadAccessCount; - - totalAccessCount += candidate.workTime; - if (maxQueuedTime < candidate.queuedTime) maxQueuedTime = candidate.queuedTime; - - candidates.push_back(candidate); - } - } - - // prevent division by zero - if (totalAccessCount == 0) totalAccessCount = 1; - if (maxQueuedTime == 0) maxQueuedTime = 1; - - // normalize candidate values - foreach (TheoraWorkCandidate, candidates) - { - it->workTime /= totalAccessCount; - // adjust user priorities to favor clips that have fewer frames queued - it->priority *= 1.0f - (it->queuedTime / maxQueuedTime) * 0.5f; - prioritySum += it->priority; - } - foreach (TheoraWorkCandidate, candidates) - { - it->entitledTime = it->priority / prioritySum; - } - - // now, based on how much access time has been given to each clip in the work log - // and how much time should be given to each clip based on calculated priorities, - // we choose a best suited clip for this worker thread to decode next - foreach (TheoraWorkCandidate, candidates) - { - diff = it->entitledTime - it->workTime; - - if (maxDiff < diff) - { - maxDiff = diff; - selectedClip = it->clip; - } - } - - if (selectedClip) - { - selectedClip->mAssignedWorkerThread = caller; - - int nClips = (int) mClips.size(); - unsigned int maxWorkLogSize = (nClips - 1) * 50; - - if (nClips > 1) - { - mWorkLog.push_front(selectedClip); - ++selectedClip->mThreadAccessCount; - } - - TheoraVideoClip* c; - while (mWorkLog.size() > maxWorkLogSize) - { - c = mWorkLog.back(); - mWorkLog.pop_back(); - c->mThreadAccessCount--; - } -#ifdef _SCHEDULING_DEBUG - if (mClips.size() > 1) - { - int accessCount = mWorkLog.size(); - if (gThreadDiagnosticTimer > 2.0f) - { - gThreadDiagnosticTimer = 0; - std::string logstr = "-----\nTheora Playback Library debug CPU time analysis (" + str(accessCount) + "):\n"; - int percent; - foreach (TheoraVideoClip*, mClips) - { - percent = ((float) (*it)->mThreadAccessCount / mWorkLog.size()) * 100.0f; - logstr += (*it)->getName() + " (" + str((*it)->getPriority()) + "): " + str((*it)->mThreadAccessCount) + ", " + str(percent) + "%\n"; - } - logstr += "-----"; - th_writelog(logstr); - } - } -#endif - } - - mWorkMutex->unlock(); - return selectedClip; -} - -void TheoraVideoManager::update(float timeDelta) -{ - mWorkMutex->lock(); - foreach (TheoraVideoClip*, mClips) - { - (*it)->update(timeDelta); - (*it)->decodedAudioCheck(); - } - mWorkMutex->unlock(); -#ifdef _SCHEDULING_DEBUG - gThreadDiagnosticTimer += timeDelta; -#endif -} - -int TheoraVideoManager::getNumWorkerThreads() -{ - return (int) mWorkerThreads.size(); -} - -void TheoraVideoManager::createWorkerThreads(int n) -{ - TheoraWorkerThread* t; - for (int i=0;i<n;++i) - { - t=new TheoraWorkerThread(); - t->start(); - mWorkerThreads.push_back(t); - } -} - -void TheoraVideoManager::destroyWorkerThreads() -{ - foreach(TheoraWorkerThread*,mWorkerThreads) - { - (*it)->join(); - delete (*it); - } - mWorkerThreads.clear(); -} - -void TheoraVideoManager::setNumWorkerThreads(int n) -{ - if (n == getNumWorkerThreads()) return; - if (n < 1) throw TheoraGenericException("Unable to change the number of worker threads in TheoraVideoManager, at least one worker thread is reqired"); - - th_writelog("changing number of worker threats to: "+str(n)); - - destroyWorkerThreads(); - createWorkerThreads(n); -} - -std::string TheoraVideoManager::getVersionString() -{ - int a, b, c; - getVersion(&a, &b, &c); - std::string out = str(a) + "." + str(b); - if (c != 0) - { - if (c < 0) out += " RC" + str(-c); - else out += "." + str(c); - } - return out; -} - -void TheoraVideoManager::getVersion(int* a, int* b, int* c) // TODO, return a struct instead of the current solution. -{ - *a = 1; - *b = 1; - *c = 0; -} - -std::vector<std::string> TheoraVideoManager::getSupportedDecoders() -{ - std::vector<std::string> lst; -#ifdef __THEORA - lst.push_back("Theora"); -#endif -#ifdef __AVFOUNDATION - lst.push_back("AVFoundation"); -#endif -#ifdef __FFMPEG - lst.push_back("FFmpeg"); -#endif - - return lst; -} |