BlinkenArea - GitList
Repositories
Blog
Wiki
Blinker
Code
Commits
Branches
Tags
Search
Tree:
aad9d14
Branches
Tags
master
Blinker
src
common
Player.cpp
code cleanup of synchronization in player
Stefan Schuermans
commited
aad9d14
at 2014-01-03 22:42:57
Player.cpp
Blame
History
Raw
/* Blinker Copyright 2011-2014 Stefan Schuermans <stefan@blinkenarea.org> Copyleft GNU public license - http://www.gnu.org/copyleft/gpl.html a blinkenarea.org project */ #include <list> #include <string> #include <BlinkenLib/BlinkenFrame.h> #include <BlinkenLib/BlinkenMovie.h> #include "Directory.h" #include "File.h" #include "InStreamFile.h" #include "ListTracker.h" #include "ListTracker_impl.h" #include "Mgrs.h" #include "Module.h" #include "OutStreamFile.h" #include "Player.h" #include "PlayerMovie.h" #include "StreamRecv.h" #include "Time.h" #include "TimeCallee.h" namespace Blinker { /** * @brief constructor * @param[in] name module name * @param[in] mgrs managers * @param[in] dirBase base directory */ Player::Player(const std::string &name, Mgrs &mgrs, const Directory &dirBase): Module(name, mgrs, dirBase), m_fileOutStream(dirBase.getFile("outstream"), mgrs.m_streamMgr), m_fileHaltStream(dirBase.getFile("haltstream"), mgrs.m_streamMgr), m_fileInSync(dirBase.getFile("insync"), mgrs.m_syncMgr), m_playlistTracker(*this, dirBase.getSubdir("playlist")), m_curValid(false), m_curEntry(m_playlistTracker.m_list.begin()), m_curFrame(0), m_curFrameStart(0), m_curFrameEnd(0), m_curChange(false), m_halted(false) { m_maxDeviation.fromMs(30); // init and load playlist m_fileHaltStream.setStreamRecv(this); m_fileInSync.setSyncRecv(this); m_playlistTracker.init(); checkCurChanged(); } /// virtual destructor Player::~Player() { // cancel time callback request m_mgrs.m_callMgr.cancelTimeCall(this); // free all movies, detach from stream sources m_playlistTracker.clear(); m_fileHaltStream.setStreamRecv(NULL); m_fileInSync.setSyncRecv(NULL); } /// check for update of configuration void Player::updateConfig() { // output stream name file was modified -> re-get output stream if (m_fileOutStream.checkModified()) { m_fileOutStream.update(); sendFrame(); } // halt stream name file was modified -> re-get halt stream if (m_fileHaltStream.checkModified()) m_fileHaltStream.update(); // input sync stream name file was modified -> re-get input sync stream if (m_fileInSync.checkModified()) m_fileInSync.update(); // playlist update m_playlistTracker.updateConfig(); checkCurChanged(); } /** * @brief set current frame * @param[in] stream stream name * @param[in] pFrame current frame (NULL for none) */ void Player::setFrame(const std::string &stream, stBlinkenFrame *pFrame) { // this is coming from the halt stream, which will halt the player // whenever a frame is available on this halt stream // halt stream active -> halt player if (pFrame) halt(); // halt stream inactive -> continue playing else cont(); (void)stream; // unused } /** * @brief send synchronization information * @param[in] sync sync stream name * @param[in] pInfo synchronization information */ void Player::sendInfo(const std::string &sync, Info &info) { bool change = false, seekPos = true; // halt player if pause flag is set if (info.pause) halt(); // seek to other movie if not correct one if (m_curEntry == m_playlistTracker.m_list.end() || !checkName(m_curEntry->m_name, info.name)) { // movie found -> change has been done if (seekToMovie(info.name)) change = true; // movie not found -> do not seek to position (wrong movie anyway) else seekPos = false; } // seek to requested position in movie if (m_curValid && seekPos) { if (seekToPos(info.pos)) change = true; } // send frame and update timed callback if something changed if (change) { sendFrame(); if (m_curValid && !m_halted) { m_nextTime = Time::now() + m_remainTime; // remainTime changed -> update m_mgrs.m_callMgr.requestTimeCall(this, m_nextTime); } } // continue playing if pause flag is not set if (!info.pause) cont(); (void)sync; // unused } /// callback when requested time reached void Player::timeCall() { // leave if halted if (m_halted) return; // leave if time is not yet ready for next frame if (Time::now() < m_nextTime) { // request call at time for next frame m_mgrs.m_callMgr.requestTimeCall(this, m_nextTime); return; } // go to next frame ++m_curFrame; m_curFrameStart = m_curFrameEnd; // process new current frame procFrame(); } /// halt player void Player::halt() { // player already halted -> leave if (m_halted) return; m_halted = true; if (m_curValid) { // store remaining frame time m_remainTime = m_nextTime - Time::now(); if (m_remainTime < Time::zero) m_remainTime = Time::zero; } // cancel time call m_mgrs.m_callMgr.cancelTimeCall(this); } /// continue playing void Player::cont() { // player not halted -> leave if (!m_halted) return; m_halted = false; if (m_curValid) { // determine time for next frame m_nextTime = Time::now() + m_remainTime; // schedule time call m_mgrs.m_callMgr.requestTimeCall(this, m_nextTime); } } /// check if current movie changed and react void Player::checkCurChanged() { // current movie changed if (m_curChange) { m_curChange = false; // go to begin of new current movie and start playing now m_curFrame = 0; m_curFrameStart = 0; m_curFrameEnd = 0; m_remainTime = Time::zero; m_nextTime = Time::now(); procFrame(); } // if (m_curChange) } /// process current frame void Player::procFrame() { // movie finished -> next movie // use while loops to handle empty movies / empty playlist m_curValid = true; bool wrapped = false; while (true) { // playlist finished -> re-start from beginning while (m_curEntry == m_playlistTracker.m_list.end()) { m_curEntry = m_playlistTracker.m_list.begin(); m_curFrame = 0; m_curFrameStart = 0; m_curFrameEnd = 0; // detect empty playlist or playlist with only empty movies if (wrapped) { m_curValid = false; break; } wrapped = true; } if (!m_curValid) break; // movie not yet finished -> done if (m_curEntry->m_pObj->m_pMovie && m_curFrame < BlinkenMovieGetFrameCnt(m_curEntry->m_pObj->m_pMovie)) break; // movie finished -> next movie ++m_curEntry; m_curFrame = 0; m_curFrame = 0; m_curFrameStart = 0; } // while (true) // send new frame to stream sendFrame(); // if a frame is there if (m_curValid) { // get frame time and calculate absolute time for next frame stBlinkenFrame *pFrame = BlinkenMovieGetFrame(m_curEntry->m_pObj->m_pMovie, m_curFrame); m_remainTime.fromMs(BlinkenFrameGetDuration(pFrame)); m_nextTime += m_remainTime; m_curFrameEnd = m_curFrameStart + m_remainTime; // request call at time for next frame if (!m_halted) m_mgrs.m_callMgr.requestTimeCall(this, m_nextTime); } } /// send current frame to output stream void Player::sendFrame() { // frame available -> send it if (m_curValid) { stBlinkenFrame *pFrame = BlinkenMovieGetFrame(m_curEntry->m_pObj->m_pMovie, m_curFrame); m_fileOutStream.setFrame(pFrame); } // no frame available -> send this information else m_fileOutStream.setFrame(NULL); } /** * @brief seek to movie by name * @param[in] name name of movie to seek to (name cmp according to SyncRecv) * @return true if movie was found and seeked to, false otherwise */ bool Player::seekToMovie(const std::string &name) { // find movie by name PlaylistIt it = m_playlistTracker.m_list.begin(); while (it != m_playlistTracker.m_list.end()) { if (checkName(it->m_name, name)) break; ++it; } // movie not found -> give up, return error if (it == m_playlistTracker.m_list.end()) return false; // seek to movie m_curValid = false; m_curEntry = it; m_curFrame = 0; m_remainTime = Time::zero; if (m_curEntry->m_pObj->m_pMovie) { stBlinkenFrame *pFrame = BlinkenMovieGetFrame(m_curEntry->m_pObj->m_pMovie, m_curFrame); if (pFrame) { m_remainTime.fromMs(BlinkenFrameGetDuration(pFrame)); m_curValid = true; } } m_curFrameStart = Time::zero; m_curFrameEnd = m_remainTime; m_nextTime = Time::now() + m_remainTime; return true; } /** * @brief seek to position in current movie * @param[in] tgtPos target position in current movie to seek to * @return true if movie position has been changed, false otherwise */ bool Player::seekToPos(const Time &tgtPos) { bool change = false; // get remaining frame time and current position in movie if (!m_halted) { // remaining frame time always up-to-date in halted mode m_remainTime = m_nextTime - Time::now(); if (m_remainTime < Time::zero) m_remainTime = Time::zero; } Time pos = m_curFrameEnd - m_remainTime; // seek to earlier position in movie if needed if (pos > tgtPos + m_maxDeviation) { Time goback = pos - tgtPos; if (seekBackwards(goback)) change = true; } // if (pos > tgtPos + m_maxDeviation) // seek to later position in movie if needed if (pos + m_maxDeviation < tgtPos) { Time go = tgtPos - pos; if (seekForwards(go)) change = true; } // if (pos + m_maxDeviation < tgtPos) return change; } /** * @brief seek backwards in current movie * @param[in] goback time to seek backwards in current movie * @return true if movie position has been changed, false otherwise */ bool Player::seekBackwards(Time goback) { bool change = false; // time of current frame Time frame = m_curFrameEnd - m_curFrameStart; // time the current frame has already been shown Time shown = frame - m_remainTime; while (goback > Time::zero) { // correct by just showing current frame some time longer if (goback <= shown) { m_remainTime += goback; break; } // go back to begin of current frame goback -= shown; m_remainTime = frame; // go back to end of previous frame if (m_curFrame <= 0) break; m_curFrame--; m_curFrameEnd = m_curFrameStart; frame = Time::zero; stBlinkenFrame *pFrame = BlinkenMovieGetFrame(m_curEntry->m_pObj->m_pMovie, m_curFrame); if (pFrame) frame.fromMs(BlinkenFrameGetDuration(pFrame)); m_curFrameStart = m_curFrameEnd - frame; m_remainTime = Time::zero; shown = frame; } // while (goback > Time::zero) return change; } /** * @brief seek forwards in current movie * @param[in] go time to seek forwards in current movie * @return true if movie position has been changed, false otherwise */ bool Player::seekForwards(Time go) { bool change = false; // frame count of current movie int frameCnt = BlinkenMovieGetFrameCnt(m_curEntry->m_pObj->m_pMovie); // time of current frame Time frame = m_curFrameEnd - m_curFrameStart; while (go > Time::zero) { change = true; // correct by just showing current frame some time shorter if (go <= m_remainTime) { m_remainTime -= go; break; } // go to end of current frame go -= m_remainTime; m_remainTime = Time::zero; // go to begin of next frame if (m_curFrame >= frameCnt) break; m_curFrame++; m_curFrameStart = m_curFrameEnd; frame = Time::zero; stBlinkenFrame *pFrame = BlinkenMovieGetFrame(m_curEntry->m_pObj->m_pMovie, m_curFrame); if (pFrame) frame.fromMs(BlinkenFrameGetDuration(pFrame)); m_curFrameEnd = m_curFrameStart + frame; m_remainTime = frame; } // while (go > Time::zero) return change; } } // namespace Blinker