diff options
Diffstat (limited to 'Managers/MultimediaManager.cpp')
-rw-r--r-- | Managers/MultimediaManager.cpp | 629 |
1 files changed, 629 insertions, 0 deletions
diff --git a/Managers/MultimediaManager.cpp b/Managers/MultimediaManager.cpp new file mode 100644 index 00000000..faab66cf --- /dev/null +++ b/Managers/MultimediaManager.cpp @@ -0,0 +1,629 @@ +/* + * MultimediaManager.cpp + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#include "MultimediaManager.h" + +#include <fstream> + +using namespace libdash::framework::adaptation; +using namespace libdash::framework::buffer; +using namespace viper::managers; +using namespace dash::mpd; + +#include <queue> + +MultimediaManager::MultimediaManager(ViperGui *viperGui, int segBufSize, std::string downloadPath, bool nodecoding) : + viperGui (viperGui), + segmentBufferSize (segBufSize), + downloadPath (downloadPath), + offset (offset), + mpd (NULL), + period (NULL), + videoAdaptationSet (NULL), + videoRepresentation (NULL), + videoLogic (NULL), + videoStream (NULL), + audioAdaptationSet (NULL), + audioRepresentation (NULL), + audioLogic (NULL), + videoRendererHandle (NULL), + audioRendererHandle (NULL), + audioStream (NULL), + started (false), + stopping (false), + framesDisplayed (0), + segmentsDownloaded (0), + isVideoRendering (false), + isAudioRendering (false), + eos (false), + playing (false), + noDecoding (nodecoding) +{ + InitializeCriticalSection (&this->monitorMutex); + InitializeCriticalSection (&this->monitorBufferMutex); + InitializeCriticalSection (&this->monitor_playing_video_mutex); + InitializeConditionVariable (&this->playingVideoStatusChanged); + InitializeCriticalSection (&this->monitor_playing_audio_mutex); + InitializeConditionVariable (&this->playingAudioStatusChanged); + + this->manager = CreateDashManager(); +} + +MultimediaManager::~MultimediaManager () +{ + this->stop(); + this->manager->Delete(); + DeleteCriticalSection (&this->monitorMutex); + DeleteCriticalSection (&this->monitorBufferMutex); + DeleteCriticalSection (&this->monitor_playing_video_mutex); + DeleteConditionVariable (&this->playingVideoStatusChanged); + DeleteCriticalSection (&this->monitor_playing_audio_mutex); + DeleteConditionVariable (&this->playingAudioStatusChanged); +} + +IMPD* MultimediaManager::getMPD() +{ + return this->mpd; +} + +bool MultimediaManager::init(const std::string& url) +{ + EnterCriticalSection(&this->monitorMutex); + this->mpd = this->manager->Open((char *)url.c_str()); + Debug("url : %s\n", url.c_str()); + if(this->mpd == NULL) + { + LeaveCriticalSection(&this->monitorMutex); + return false; + } + Debug("Done DL the mpd\n"); + LeaveCriticalSection(&this->monitorMutex); + return true; +} + +bool MultimediaManager::initICN(const std::string& url) +{ + EnterCriticalSection(&this->monitorMutex); + libdash::framework::input::IICNConnection* icnConn = new libdash::framework::input::ICNConnectionConsumerApi(20.0, this->beta, this->drop); + icnConn->InitForMPD(url); + int ret = 0; + char * data = (char *)malloc(4096); + int pos = url.find_last_of("/"); + if(pos == std::string::npos) + { + pos = strlen(url.c_str()); + } + else + { + pos = pos + 1; + } + + std::string downloadFile(this->downloadPath + url.substr(pos).c_str()); + FILE *fp; + fp = fopen(downloadFile.c_str(), "w"); + if(fp == NULL) + { + free(data); + delete icnConn; + LeaveCriticalSection(&this->monitorMutex); + return false; + } + ret = icnConn->Read((uint8_t*)data, 4096); + while(ret) + { + fwrite(data, sizeof(char), ret, fp); + ret = icnConn->Read((uint8_t*)data,4096); + } + fclose(fp); + this->mpd = this->manager->Open(const_cast<char*>(downloadFile.c_str()), url); + if(this->mpd == NULL) + { + remove(downloadFile.c_str()); + free(data); + delete icnConn; + LeaveCriticalSection(&this->monitorMutex); + return false; + } + remove(downloadFile.c_str()); + free(data); + delete icnConn; + LeaveCriticalSection(&this->monitorMutex); + return true; +} + +bool MultimediaManager::isStarted() +{ + return this->started; +} + +bool MultimediaManager::isStopping() +{ + return this->stopping; +} + +bool MultimediaManager::isICN() +{ + return this->icn; +} + +void MultimediaManager::start(bool icnEnabled, double icnAlpha, uint32_t nextOffset) +{ + this->icn = icnEnabled; + this->icnAlpha = icnAlpha; + if (this->started) + this->stop(); + if(icnAlpha <= 1 && icnAlpha >=0) + { + qDebug("ICN-enhanced rate estimation: alpha = %f\n",icnAlpha); + + } else { + qDebug("normal rate estimation\n"); + } + EnterCriticalSection(&this->monitorMutex); + if (this->videoAdaptationSet && this->videoRepresentation) + { + this->initVideoRendering(nextOffset); + this->videoStream->setAdaptationLogic(this->videoLogic); + this->videoLogic->setMultimediaManager(this); + this->videoStream->start(); + this->startVideoRenderingThread(); + } + this->started = true; + this->playing = true; + LeaveCriticalSection(&this->monitorMutex); +} + +void MultimediaManager::stop() +{ + if (!this->started) + return; + this->stopping = true; + EnterCriticalSection(&this->monitorMutex); + this->stopVideo(); + this->stopping = false; + this->started = false; + LeaveCriticalSection(&this->monitorMutex); + Debug("VIDEO STOPPED\n"); + this->period = this->mpd->GetPeriods().at(0); + this->videoAdaptationSet = this->period->GetAdaptationSets().at(0); + this->videoRepresentation = this->videoAdaptationSet->GetRepresentation().at(0); + +} + +void MultimediaManager::stopVideo() +{ + if(this->started && this->videoStream) + { + this->videoStream->stop(); + this->stopVideoRenderingThread(); + delete this->videoStream; + delete this->videoLogic; + this->videoStream = NULL; + this->videoLogic = NULL; + } +} + +void MultimediaManager::stopAudio() +{ + if (this->started && this->audioStream) + { + //TODO add audio support + } +} + +bool MultimediaManager::setVideoQuality(IPeriod* period, IAdaptationSet *adaptationSet, IRepresentation *representation) +{ + EnterCriticalSection(&this->monitorMutex); + + this->period = period; + this->videoAdaptationSet = adaptationSet; + this->videoRepresentation = representation; + if (this->videoStream) + this->videoStream->setRepresentation(this->period, this->videoAdaptationSet, this->videoRepresentation); + + LeaveCriticalSection(&this->monitorMutex); + return true; +} + +bool MultimediaManager::setAudioQuality(IPeriod* period, IAdaptationSet *adaptationSet, IRepresentation *representation) +{ + EnterCriticalSection(&this->monitorMutex); + + this->period = period; + this->audioAdaptationSet = adaptationSet; + this->audioRepresentation = representation; + if (this->audioStream) + this->audioStream->setRepresentation(this->period, this->audioAdaptationSet, this->audioRepresentation); + LeaveCriticalSection(&this->monitorMutex); + return true; +} + +bool MultimediaManager::isUserDependent() +{ + if(this->videoLogic) + return this->videoLogic->isUserDependent(); + else + return true; +} + +bool MultimediaManager::setVideoAdaptationLogic(libdash::framework::adaptation::LogicType type, struct libdash::framework::adaptation::AdaptationParameters *params) +{ + if(this->videoAdaptationSet) + { + if(this->videoLogic) + delete(this->videoLogic); + this->videoLogic = AdaptationLogicFactory::create(type, this->mpd, this->period, this->videoAdaptationSet, 1, params); + this->logicName = LogicType_string[type]; + } + else + { + this->videoLogic = NULL; + return true; + } + if(this->videoLogic) + return true; + else + return false; +} + +void MultimediaManager::shouldAbort(bool isVideo) +{ + if(isVideo) + { + this->videoStream->shouldAbort(); + return; + } + else + { + this->audioStream->shouldAbort(); + } +} + +void MultimediaManager::setTargetDownloadingTime(bool isVideo, double target) +{ + if(isVideo) + this->videoStream->setTargetDownloadingTime(target); + else + this->audioStream->setTargetDownloadingTime(target); +} + +bool MultimediaManager::setAudioAdaptationLogic(libdash::framework::adaptation::LogicType type, struct libdash::framework::adaptation::AdaptationParameters *params) +{ + if(this->audioAdaptationSet) + { + this->audioLogic = AdaptationLogicFactory::create(type, this->mpd, this->period, this->audioAdaptationSet, 0, params); + this->logicName = LogicType_string[type]; + } + else + { + this->audioLogic = NULL; + return true; + } + if(this->audioLogic) + return true; + else + return false; +} + +void MultimediaManager::attachManagerObserver(IMultimediaManagerObserver *observer) +{ + this->managerObservers.push_back(observer); +} + +void MultimediaManager::notifyVideoBufferObservers(uint32_t fillstateInPercent) +{ + for (size_t i = 0; i < this->managerObservers.size(); i++) + this->managerObservers.at(i)->onVideoBufferStateChanged(fillstateInPercent); +} + +void MultimediaManager::notifyVideoSegmentBufferObservers(uint32_t fillstateInPercent) +{ + for (size_t i = 0; i < this->managerObservers.size(); i++) + this->managerObservers.at(i)->onVideoSegmentBufferStateChanged(fillstateInPercent); +} + +void MultimediaManager::notifyAudioSegmentBufferObservers(uint32_t fillstateInPercent) +{ + for (size_t i = 0; i < this->managerObservers.size(); i++) + this->managerObservers.at(i)->onAudioSegmentBufferStateChanged(fillstateInPercent); +} + +void MultimediaManager::notifyAudioBufferObservers(uint32_t fillstateInPercent) +{ + for (size_t i = 0; i < this->managerObservers.size(); i++) + this->managerObservers.at(i)->onAudioBufferStateChanged(fillstateInPercent); +} + +void MultimediaManager::initVideoRendering(uint32_t offset) +{ + this->videoStream = new MultimediaStream(viper::managers::VIDEO, this->mpd, this->segmentBufferSize, this->isICN(), this->icnAlpha, this->noDecoding, this->beta, this->drop); + this->videoStream->attachStreamObserver(this); + this->videoStream->setRepresentation(this->period, this->videoAdaptationSet, this->videoRepresentation); + this->videoStream->setPosition(offset); +} + +void MultimediaManager::initAudioPlayback(uint32_t offset) +{ + this->audioStream = new MultimediaStream(viper::managers::AUDIO, this->mpd, this->segmentBufferSize, this->isICN(), this->icnAlpha, this->noDecoding, this->beta, this->drop); + this->audioStream->attachStreamObserver(this); + this->audioStream->setRepresentation(this->period, this->audioAdaptationSet, this->audioRepresentation); + this->audioStream->setPosition(offset); +} + +void MultimediaManager::setLooping(bool looping) +{ + if(this->videoStream) + { + this->videoStream->setLooping(looping); + } + if(this->audioStream) + { + this->audioStream->setLooping(looping); + } +} + +void MultimediaManager::onSegmentDownloaded () +{ + this->segmentsDownloaded++; +} + +void MultimediaManager::onSegmentBufferStateChanged(StreamType type, uint32_t fillstateInPercent, int maxC) +{ + switch (type) + { + case AUDIO: + this->notifyAudioSegmentBufferObservers(fillstateInPercent); + break; + case VIDEO: + this->notifyVideoSegmentBufferObservers(fillstateInPercent); + break; + default: + break; + } +} + +void MultimediaManager::onVideoBufferStateChanged(uint32_t fillstateInPercent) +{ + this->notifyVideoBufferObservers(fillstateInPercent); +} + +void MultimediaManager::onAudioBufferStateChanged(uint32_t fillstateInPercent) +{ + this->notifyAudioBufferObservers(fillstateInPercent); +} + +void MultimediaManager::setFrameRate(double framerate) +{ + this->frameRate = framerate; +} + +void MultimediaManager::setEOS(bool value) +{ + this->eos = value; + if(value) //ie: End of Stream so the rendering thread(s) will finish + { + this->stopping = true; + if(this->videoRendererHandle != NULL) + { + this->stopVideoRenderingThread(); + this->videoRendererHandle = NULL; + } + if(this->audioRendererHandle != NULL) + { + this->stopAudioRenderingThread(); + this->audioRendererHandle = NULL; + } + this->stopping = false; + for(size_t i = 0; i < this->managerObservers.size(); i++) + this->managerObservers.at(i)->onEOS(); + } +} + +bool MultimediaManager::startVideoRenderingThread() +{ + this->isVideoRendering = true; + if(!noDecoding) + this->videoRendererHandle = createThreadPortable (pushVideo, this); + else + this->videoRendererHandle = createThreadPortable (pushVideoNoOut, this); + + if(this->videoRendererHandle == NULL) + return false; + + return true; +} + +void MultimediaManager::stopVideoRenderingThread() +{ + this->isVideoRendering = false; + if (this->videoRendererHandle != NULL) + { + JoinThread(this->videoRendererHandle); + destroyThreadPortable(this->videoRendererHandle); + } +} + +bool MultimediaManager::startAudioRenderingThread() +{ + this->isAudioRendering = true; + if(this->audioRendererHandle == NULL) + return false; + return true; +} + +void MultimediaManager::stopAudioRenderingThread() +{ + this->isAudioRendering = false; + if (this->audioRendererHandle != NULL) + { + JoinThread(this->audioRendererHandle); + destroyThreadPortable(this->audioRendererHandle); + } +} + +bool MultimediaManager::isPlaying() +{ + return this->playing; +} + +void MultimediaManager::onPausePressed() +{ + EnterCriticalSection(&this->monitor_playing_video_mutex); + EnterCriticalSection(&this->monitor_playing_audio_mutex); + this->playing = !this->playing; + WakeAllConditionVariable(&this->playingVideoStatusChanged); + WakeAllConditionVariable(&this->playingAudioStatusChanged); + LeaveCriticalSection(&this->monitor_playing_video_mutex); + LeaveCriticalSection(&this->monitor_playing_audio_mutex); +} + +void* MultimediaManager::pushVideoNoOut(void *data) +{ + MultimediaManager *manager = (MultimediaManager*) data; + manager->lastPointInTime = std::chrono::system_clock::now(); + manager->bufferingLimit = manager->lastPointInTime; + libdash::framework::input::MediaObject *segment = manager->videoStream->getSegment(); + using duration_in_milliSeconds = std::chrono::duration<long int, std::ratio<1,1000>>; + std::chrono::time_point<std::chrono::system_clock> timeOfInsertion; + std::chrono::time_point<std::chrono::system_clock> startTime; + long int actualPosition; + + while(manager->isVideoRendering) + { + if (segment) + { + timeOfInsertion = std::chrono::system_clock::now(); + actualPosition = std::chrono::duration_cast<duration_in_milliSeconds>(manager->bufferingLimit - timeOfInsertion).count(); + if(actualPosition < 0) + { + Debug("MANAGER:\tRebuffered %d ms\n", actualPosition *(-1)); + manager->lastPointInTime = timeOfInsertion; + //TODO Replace the 2 by a variable with segmentDuration + manager->bufferingLimit = manager->lastPointInTime + std::chrono::seconds(2); + } + else + { + //TODO Replace the 2 by a variable with segmentDuration + Debug("MANAGER: INSERT TO BUFFER old_fillness: %f, new_fillness: %f\n", (double)((double)actualPosition/1000.0) / (double) this->segmentBufferSize, (double)((double)(actualPosition + 2000)/1000.0) / (double) manager->segmentBufferSize); + manager->bufferingLimit = manager->bufferingLimit + std::chrono::seconds(2); + manager->lastPointInTime = timeOfInsertion; + } + delete segment; + } + else + { + //noDecoding here means noGUI + if(manager->noDecoding) + manager->setEOS(true); + } + segment = manager->videoStream->getSegment(); + } + return NULL; + +} + +void MultimediaManager::notifyStatistics(int segNum, uint32_t bitrate, int fps, uint32_t quality) +{ + for(size_t i = 0; i < this->managerObservers.size(); i++) + { + this->managerObservers.at(i)->notifyStatistics(segNum, bitrate, fps, quality); + } +} + +void MultimediaManager::notifyQualityDownloading(uint32_t quality) +{ + for(size_t i = 0; i < this->managerObservers.size(); i++) + { + this->managerObservers.at(i)->notifyQualityDownloading(quality); + } +} + +void MultimediaManager::notifyBufferChange() +{ + if(this->videoStream) + { + this->videoStream->notifyBufferChange(this->getUBufferLevel(), this->segmentBufferSize); + } + if(this->audioStream) + { + this->audioStream->notifyBufferChange(this->getUBufferLevel(), this->segmentBufferSize); + } +} + +int MultimediaManager::getBufferLevel() +{ + return (int)this->getUBufferLevel(); +} + +uint32_t MultimediaManager::getUBufferLevel() +{ + int mBufferLevel = 0; + int segmentDurationInMs = 2000; + + if(noDecoding) + { + std::chrono::time_point<std::chrono::system_clock> timeNow = std::chrono::system_clock::now(); + using duration_in_milliSeconds = std::chrono::duration<long int, std::ratio<1,1000>>; + long int actualPos = std::chrono::duration_cast<duration_in_milliSeconds>(this->bufferingLimit - timeNow).count(); + int res = ((double)actualPos) / (double (this->segmentBufferSize * segmentDurationInMs)) * 100; + return res >= 0 ? res > 100 ? 100 : (uint32_t) res : 0; + } + else + { + mBufferLevel = this->viperGui->getBufferDuration(); + int res = ((int)mBufferLevel)/((double) (this->segmentBufferSize * this->viperGui->getSegmentDuration())) * 100; + return res >= 0 ? res > 100 ? 100 : (uint32_t) res : 0; + + } +} + +bool MultimediaManager::canPush() +{ + int segmentDurationInMs = 2000; + while(this->getUBufferLevel() >= 100 && !this->stopping) + { + sleep(segmentDurationInMs / 1000); + } + return true; +} + +void* MultimediaManager::pushVideo(void *data) +{ + MultimediaManager *manager = (MultimediaManager*) data; + libdash::framework::input::MediaObject *segment = manager->videoStream->getSegment(); + long int segmentDurationInMs = 2000; + while(manager->isVideoRendering) + { + if (segment) + { + manager->notifyBufferChange(); + manager->viperGui->writeData(segment); + delete segment; + } + segment = manager->videoStream->getSegment(); + } + return NULL; +} + +void MultimediaManager::setOffset(int offset) +{ + this->offset = offset; +} + +void MultimediaManager::setBeta(float beta) +{ + this->beta = beta; +} + +void MultimediaManager::setDrop(float drop) +{ + this->drop = drop; +} |