<commit_before>//
//  libavg - Media Playback Engine. 
//  Copyright (C) 2003-2008 <NAME>
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
//  Current versions can be found at www.libavg.de
//

#include "Player.h"

#include "../avgconfigwrapper.h"
#include "AVGNode.h"
#include "DivNode.h"
#include "WordsNode.h"
#include "VideoNode.h"
#include "CameraNode.h"
#include "ImageNode.h"
#include "PanoImageNode.h"
#include "SoundNode.h"
#include "LineNode.h"
#include "RectNode.h"
#include "CurveNode.h"
#include "PolyLineNode.h"
#include "PolygonNode.h"
#include "CircleNode.h"
#include "MeshNode.h"
#include "NodeDefinition.h"
#include "PluginManager.h"
#include "TextEngine.h"

#include "TrackerEventSource.h"
#include "Event.h"
#include "MouseEvent.h"
#include "KeyEvent.h"

#include "SDLDisplayEngine.h"

#include "../base/FileHelper.h"
#include "../base/StringHelper.h"
#include "../base/OSHelper.h"
#include "../base/Exception.h"
#include "../base/Logger.h"
#include "../base/ConfigMgr.h"
#include "../base/XMLHelper.h"
#include "../base/Profiler.h"
#include "../base/ScopeTimer.h"
#include "../base/TimeSource.h"
#include "../base/MathHelper.h"

#include "../imaging/Camera.h"

#include "../audio/SDLAudioEngine.h"

#undef HAVE_TEMPNAM
#include <Magick++.h>

#include <libxml/xmlmemory.h>

#ifdef _WIN32
#include <direct.h>
#define getcwd _getcwd
#endif

#include <algorithm>
#include <iostream>
#include <sstream>
#include <assert.h>
#include <math.h>

using namespace std;

namespace avg {
    
Player * Player::s_pPlayer=0;

Player::Player()
    : m_pRootNode(),
      m_pDisplayEngine(0),
      m_pAudioEngine(0),
      m_bAudioEnabled(true),
      m_pTracker(0),
      m_bInHandleTimers(false),
      m_bCurrentTimeoutDeleted(false),
      m_pEventCaptureNode(),
      m_bUseFakeCamera(false),
      m_bStopOnEscape(true),
      m_bIsPlaying(false),
      m_bFakeFPS(false),
      m_FakeFPS(0),
      m_FrameTime(0),
      m_Volume(1),
      m_FrameEndSignal(&IFrameEndListener::onFrameEnd),
      m_PlaybackEndSignal(&IPlaybackEndListener::onPlaybackEnd),
      m_PreRenderSignal(&IPreRenderListener::onPreRender),
      m_dtd(0),
      m_bPythonAvailable(true)
{
    if (s_pPlayer) {
        throw Exception(AVG_ERR_UNKNOWN, "Player has already been instantiated.");
    }
    ThreadProfilerPtr pProfiler = ThreadProfiler::get();
    pProfiler->setName("main");
    Profiler::get().registerThreadProfiler(pProfiler);
    initConfig();

    // Register all node types
    registerNodeType(AVGNode::createDefinition());
    registerNodeType(DivNode::createDefinition());
    registerNodeType(ImageNode::createDefinition());
    registerNodeType(WordsNode::createDefinition());
    registerNodeType(VideoNode::createDefinition());
    registerNodeType(CameraNode::createDefinition());
    registerNodeType(PanoImageNode::createDefinition());
    registerNodeType(SoundNode::createDefinition());
    registerNodeType(LineNode::createDefinition());
    registerNodeType(RectNode::createDefinition());
    registerNodeType(CurveNode::createDefinition());
    registerNodeType(PolyLineNode::createDefinition());
    registerNodeType(PolygonNode::createDefinition());
    registerNodeType(CircleNode::createDefinition());
    registerNodeType(MeshNode::createDefinition());
    
    m_pTestHelper = new TestHelper(this);

    // Early initialization of TextEngine singletons (dualton? ;-))
    // to avoid locale clashes with Magick (bug 54)
    TextEngine::get(true);
    TextEngine::get(false);

#ifdef _WIN32
    Magick::InitializeMagick((getAvgLibPath()+"magick\\").c_str());
#endif

    s_pPlayer = this;
}

void deletePlayer()
{
    delete Player::s_pPlayer;
    Player::s_pPlayer = 0;
}

Player::~Player()
{
    if (m_pDisplayEngine) {
        delete m_pDisplayEngine;
    }
    if (m_pAudioEngine) {
        delete m_pAudioEngine;
    }
    if (m_dtd) {
        xmlFreeDtd(m_dtd);
    }
    delete m_pTestHelper;
}

Player* Player::get()
{
    if (!s_pPlayer) {
        s_pPlayer = new Player();
        atexit(deletePlayer);
    }
    return s_pPlayer;
}

bool Player::exists()
{
    return s_pPlayer != 0;
}

void Player::setResolution(bool bFullscreen, int width, int height, int bpp)
{
    m_DP.m_bFullscreen = bFullscreen;
    if (bpp) {
        m_DP.m_BPP = bpp;
    }
    if (width) {
        m_DP.m_WindowWidth = width;
    }
    if (height) {
        m_DP.m_WindowHeight = height;
    }
}

void Player::setWindowPos(int x, int y)
{
    m_DP.m_x = x;
    m_DP.m_y = y;
}

void Player::setOGLOptions(bool bUsePOW2Textures, bool bUseShaders, 
                bool bUsePixelBuffers, int MultiSampleSamples)
{
    m_bUsePOW2Textures = bUsePOW2Textures;
    m_bUseShaders = bUseShaders;
    m_bUsePixelBuffers = bUsePixelBuffers;
    m_MultiSampleSamples = MultiSampleSamples;
}

void Player::setMultiSampleSamples(int MultiSampleSamples)
{
    m_MultiSampleSamples = MultiSampleSamples;
}

void Player::enableAudio(bool bEnable)
{
    if (m_bIsPlaying) {
        throw Exception(AVG_ERR_UNSUPPORTED, 
                "Player.enableAudio must be called before Player.play().");
    }
    m_bAudioEnabled = bEnable;
}

void Player::setAudioOptions(int samplerate, int channels)
{
    m_AP.m_SampleRate = samplerate;
    m_AP.m_Channels = channels;
}

void Player::loadFile(const std::string& sFilename)
{
    string RealFilename;
    try {
        AVG_TRACE(Logger::MEMORY, 
                std::string("Player::loadFile(") + sFilename + ")");

        // When loading an avg file, assets are loaded from a directory relative
        // to the file.
        char szBuf[1024];
        char * pBuf = getcwd(szBuf, 1024);
        if (sFilename[0] == '/') {
            RealFilename = sFilename; 
        } else {
            m_CurDirName = string(pBuf)+"/";
            RealFilename = m_CurDirName+sFilename;
        }
        m_CurDirName = RealFilename.substr(0, RealFilename.rfind('/')+1);

        string sAVG;
        readWholeFile(RealFilename, sAVG);
        internalLoad(sAVG);

        // Reset the directory to load assets from to the current dir.
        m_CurDirName = string(pBuf)+"/";
    } catch (Exception& ex) {
        switch (ex.GetCode()) {
            case AVG_ERR_XML_PARSE:
                throw (Exception(AVG_ERR_XML_PARSE, 
                        string("Error parsing xml document ")+RealFilename));
                break;
            case AVG_ERR_XML_VALID:
                throw (Exception(AVG_ERR_XML_VALID, 
                        RealFilename + " does not validate."));
                break;
            default:
                throw;
        }
    }
}

void Player::loadString(const std::string& sAVG)
{
    try {
        AVG_TRACE(Logger::MEMORY, "Player::loadString()");

        string sEffectiveDoc = removeStartEndSpaces(sAVG);
        internalLoad(sEffectiveDoc);
    } catch (Exception& ex) {
        switch (ex.GetCode()) {
            case AVG_ERR_XML_PARSE:
                throw Exception(AVG_ERR_XML_PARSE, "Error parsing xml string.");
                break;
            case AVG_ERR_XML_VALID:
                throw Exception(AVG_ERR_XML_VALID, "Error validating xml string.");
                break;
            default:
                throw;
        }
    }
}

void Player::play()
{
    try {
        initPlayback();
        try {
            m_pDisplayEngine->render(m_pRootNode);
            if (m_pDisplayEngine->wasFrameLate()) {
                ThreadProfiler::get()->dumpFrame();
            }
            ThreadProfiler::get()->start();

            while (!m_bStopping) {
                doFrame();
            }
        } catch (...) {
            cleanup();
            throw;
        }
        cleanup();
        AVG_TRACE(Logger::PLAYER, "Playback ended.");
    } catch (Exception& ex) {
        m_bIsPlaying = false;
        AVG_TRACE(Logger::ERROR, ex.GetStr());
        throw;
    }
}

void Player::stop()
{
    if (m_bIsPlaying) {
        m_bStopping = true;
    } else {
        cleanup();
    }
}
        
void Player::initPlayback()
{
    m_bIsPlaying = true;
    if (!m_pRootNode) {
        throw Exception(AVG_ERR_NO_NODE, "Play called, but no xml file loaded.");
    }
    AVG_TRACE(Logger::PLAYER, "Playback started.");
    initGraphics();
    if (m_bAudioEnabled) {
        initAudio();
    }
    try {
        m_pRootNode->setRenderingEngines(m_pDisplayEngine, m_pAudioEngine);
    } catch (Exception&) {
        m_pDisplayEngine = 0;
        m_pAudioEngine = 0;
        throw;
    }

    m_pEventDispatcher->addSource(m_pEventSource);
    m_pEventDispatcher->addSource(m_pTestHelper);
    m_pEventDispatcher->addSink(this);

    m_pDisplayEngine->initRender();
    m_bStopping = false;
    if (m_pTracker) {
        m_pTracker->start();
    }

    m_FrameTime = 0;
    m_NumFrames = 0;
}

bool Player::isPlaying()
{
    return m_bIsPlaying;
}

void Player::setFramerate(double rate)
{
    if (m_bIsPlaying) {
        m_pDisplayEngine->setFramerate(rate);
    } else {
        m_DP.m_Framerate = rate;
        m_DP.m_VBRate = 0;
    }
}

void Player::setVBlankFramerate(int rate)
{
    if (m_bIsPlaying) {
        m_pDisplayEngine->setVBlankRate(rate);
    } else {
        m_DP.m_Framerate = 0;
        m_DP.m_VBRate = rate;
    }
}
        
double Player::getEffectiveFramerate()
{
    if (m_bIsPlaying) {
        return m_pDisplayEngine->getEffectiveFramerate();
    } else {
        return 0;
    }
}

TestHelper * Player::getTestHelper()
{
    return m_pTestHelper;
}

void Player::setFakeFPS(double fps)
{
    if (fabs(fps + 1.0) < 0.0001) {
        // fps = -1
        m_bFakeFPS = false;
    } else {
        m_bFakeFPS = true;
        m_FakeFPS = fps;
    }

    if (m_pAudioEngine) {
        m_pAudioEngine->setAudioEnabled(!m_bFakeFPS);
    }
}

long long Player::getFrameTime()
{
    return m_FrameTime;
}

void Player::addEventSource(IEventSource * pSource)
{
    if (!m_pEventDispatcher) {
        throw Exception(AVG_ERR_UNSUPPORTED, 
                "You must use loadFile() before addEventSource().");
    }
    m_pEventDispatcher->addSource(pSource);
}

TrackerEventSource * Player::addTracker()
{
    TrackerConfig Config;
    Config.load();
    CameraPtr pCamera;

    string sDriver = Config.getParam("/camera/driver/@value");
    string sDevice = Config.getParam("/camera/device/@value");
    bool bFW800 = Config.getBoolParam("/camera/fw800/@value");
    IntPoint CaptureSize(Config.getPointParam("/camera/size/"));
    string sCaptureFormat = Config.getParam("/camera/format/@value");
    double FrameRate = Config.getDoubleParam("/camera/framerate/@value");

    PixelFormat camPF = Bitmap::stringToPixelFormat(sCaptureFormat);
    if (camPF == NO_PIXELFORMAT) {
        throw Exception(AVG_ERR_INVALID_ARGS,
                "Unknown camera pixel format "+sCaptureFormat+".");
    }
    
    AVG_TRACE(Logger::CONFIG, "Trying to create a Tracker for " << sDriver
            << " Camera: " << sDevice << " Size: " << CaptureSize << "format: "
            << sCaptureFormat);
    pCamera = createCamera(sDriver, sDevice, -1, bFW800, CaptureSize, camPF, I8, 
            FrameRate);
    AVG_TRACE(Logger::CONFIG, "Got Camera " << pCamera->getDevice() << " from driver: " 
            << pCamera->getDriverName());
    m_pTracker = new TrackerEventSource(pCamera, Config, 
            IntPoint(m_DP.m_Width, m_DP.m_Height), true);
    addEventSource(m_pTracker);
    if (m_bIsPlaying) {
        m_pTracker->start();
    }

    return m_pTracker;
}

TrackerEventSource * Player::getTracker()
{
    return m_pTracker;
}

int Player::setInterval(int time, PyObject * pyfunc)
{
    Timeout *t = new Timeout(time, pyfunc, true, getFrameTime());
    if (m_bInHandleTimers) {
        m_NewTimeouts.push_back(t);
    } else {
        addTimeout(t);
    }
    return t->GetID();
}

int Player::setTimeout(int time, PyObject * pyfunc)
{
    Timeout *t = new Timeout(time, pyfunc, false, getFrameTime());
    if (m_bInHandleTimers) {
        m_NewTimeouts.push_back(t);
    } else {
        addTimeout(t);
    }
    return t->GetID();
}

int Player::setOnFrameHandler(PyObject * pyfunc) 
{
    return setInterval(0, pyfunc);
}

bool Player::clearInterval(int id)
{
    vector<Timeout*>::iterator it;
    for (it=m_PendingTimeouts.begin(); it!=m_PendingTimeouts.end(); it++) {
        if (id == (*it)->GetID()) {
            if (it == m_PendingTimeouts.begin()) {
                m_bCurrentTimeoutDeleted = true;
            }
            delete *it;
            m_PendingTimeouts.erase(it);<commit_msg>Fixed bug that caused timeouts to fire twice in certain circumstances.<commit_after>//
//  libavg - Media Playback Engine. 
//  Copyright (C) 2003-2008 <NAME>
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
//  Current versions can be found at www.libavg.de
//

#include "Player.h"

#include "../avgconfigwrapper.h"
#include "AVGNode.h"
#include "DivNode.h"
#include "WordsNode.h"
#include "VideoNode.h"
#include "CameraNode.h"
#include "ImageNode.h"
#include "PanoImageNode.h"
#include "SoundNode.h"
#include "LineNode.h"
#include "RectNode.h"
#include "CurveNode.h"
#include "PolyLineNode.h"
#include "PolygonNode.h"
#include "CircleNode.h"
#include "MeshNode.h"
#include "NodeDefinition.h"
#include "PluginManager.h"
#include "TextEngine.h"

#include "TrackerEventSource.h"
#include "Event.h"
#include "MouseEvent.h"
#include "KeyEvent.h"

#include "SDLDisplayEngine.h"

#include "../base/FileHelper.h"
#include "../base/StringHelper.h"
#include "../base/OSHelper.h"
#include "../base/Exception.h"
#include "../base/Logger.h"
#include "../base/ConfigMgr.h"
#include "../base/XMLHelper.h"
#include "../base/Profiler.h"
#include "../base/ScopeTimer.h"
#include "../base/TimeSource.h"
#include "../base/MathHelper.h"

#include "../imaging/Camera.h"

#include "../audio/SDLAudioEngine.h"

#undef HAVE_TEMPNAM
#include <Magick++.h>

#include <libxml/xmlmemory.h>

#ifdef _WIN32
#include <direct.h>
#define getcwd _getcwd
#endif

#include <algorithm>
#include <iostream>
#include <sstream>
#include <assert.h>
#include <math.h>

using namespace std;

namespace avg {
    
Player * Player::s_pPlayer=0;

Player::Player()
    : m_pRootNode(),
      m_pDisplayEngine(0),
      m_pAudioEngine(0),
      m_bAudioEnabled(true),
      m_pTracker(0),
      m_bInHandleTimers(false),
      m_bCurrentTimeoutDeleted(false),
      m_pEventCaptureNode(),
      m_bUseFakeCamera(false),
      m_bStopOnEscape(true),
      m_bIsPlaying(false),
      m_bFakeFPS(false),
      m_FakeFPS(0),
      m_FrameTime(0),
      m_Volume(1),
      m_FrameEndSignal(&IFrameEndListener::onFrameEnd),
      m_PlaybackEndSignal(&IPlaybackEndListener::onPlaybackEnd),
      m_PreRenderSignal(&IPreRenderListener::onPreRender),
      m_dtd(0),
      m_bPythonAvailable(true)
{
    if (s_pPlayer) {
        throw Exception(AVG_ERR_UNKNOWN, "Player has already been instantiated.");
    }
    ThreadProfilerPtr pProfiler = ThreadProfiler::get();
    pProfiler->setName("main");
    Profiler::get().registerThreadProfiler(pProfiler);
    initConfig();

    // Register all node types
    registerNodeType(AVGNode::createDefinition());
    registerNodeType(DivNode::createDefinition());
    registerNodeType(ImageNode::createDefinition());
    registerNodeType(WordsNode::createDefinition());
    registerNodeType(VideoNode::createDefinition());
    registerNodeType(CameraNode::createDefinition());
    registerNodeType(PanoImageNode::createDefinition());
    registerNodeType(SoundNode::createDefinition());
    registerNodeType(LineNode::createDefinition());
    registerNodeType(RectNode::createDefinition());
    registerNodeType(CurveNode::createDefinition());
    registerNodeType(PolyLineNode::createDefinition());
    registerNodeType(PolygonNode::createDefinition());
    registerNodeType(CircleNode::createDefinition());
    registerNodeType(MeshNode::createDefinition());
    
    m_pTestHelper = new TestHelper(this);

    // Early initialization of TextEngine singletons (dualton? ;-))
    // to avoid locale clashes with Magick (bug 54)
    TextEngine::get(true);
    TextEngine::get(false);

#ifdef _WIN32
    Magick::InitializeMagick((getAvgLibPath()+"magick\\").c_str());
#endif

    s_pPlayer = this;
}

void deletePlayer()
{
    delete Player::s_pPlayer;
    Player::s_pPlayer = 0;
}

Player::~Player()
{
    if (m_pDisplayEngine) {
        delete m_pDisplayEngine;
    }
    if (m_pAudioEngine) {
        delete m_pAudioEngine;
    }
    if (m_dtd) {
        xmlFreeDtd(m_dtd);
    }
    delete m_pTestHelper;
}

Player* Player::get()
{
    if (!s_pPlayer) {
        s_pPlayer = new Player();
        atexit(deletePlayer);
    }
    return s_pPlayer;
}

bool Player::exists()
{
    return s_pPlayer != 0;
}

void Player::setResolution(bool bFullscreen, int width, int height, int bpp)
{
    m_DP.m_bFullscreen = bFullscreen;
    if (bpp) {
        m_DP.m_BPP = bpp;
    }
    if (width) {
        m_DP.m_WindowWidth = width;
    }
    if (height) {
        m_DP.m_WindowHeight = height;
    }
}

void Player::setWindowPos(int x, int y)
{
    m_DP.m_x = x;
    m_DP.m_y = y;
}

void Player::setOGLOptions(bool bUsePOW2Textures, bool bUseShaders, 
                bool bUsePixelBuffers, int MultiSampleSamples)
{
    m_bUsePOW2Textures = bUsePOW2Textures;
    m_bUseShaders = bUseShaders;
    m_bUsePixelBuffers = bUsePixelBuffers;
    m_MultiSampleSamples = MultiSampleSamples;
}

void Player::setMultiSampleSamples(int MultiSampleSamples)
{
    m_MultiSampleSamples = MultiSampleSamples;
}

void Player::enableAudio(bool bEnable)
{
    if (m_bIsPlaying) {
        throw Exception(AVG_ERR_UNSUPPORTED, 
                "Player.enableAudio must be called before Player.play().");
    }
    m_bAudioEnabled = bEnable;
}

void Player::setAudioOptions(int samplerate, int channels)
{
    m_AP.m_SampleRate = samplerate;
    m_AP.m_Channels = channels;
}

void Player::loadFile(const std::string& sFilename)
{
    string RealFilename;
    try {
        AVG_TRACE(Logger::MEMORY, 
                std::string("Player::loadFile(") + sFilename + ")");

        // When loading an avg file, assets are loaded from a directory relative
        // to the file.
        char szBuf[1024];
        char * pBuf = getcwd(szBuf, 1024);
        if (sFilename[0] == '/') {
            RealFilename = sFilename; 
        } else {
            m_CurDirName = string(pBuf)+"/";
            RealFilename = m_CurDirName+sFilename;
        }
        m_CurDirName = RealFilename.substr(0, RealFilename.rfind('/')+1);

        string sAVG;
        readWholeFile(RealFilename, sAVG);
        internalLoad(sAVG);

        // Reset the directory to load assets from to the current dir.
        m_CurDirName = string(pBuf)+"/";
    } catch (Exception& ex) {
        switch (ex.GetCode()) {
            case AVG_ERR_XML_PARSE:
                throw (Exception(AVG_ERR_XML_PARSE, 
                        string("Error parsing xml document ")+RealFilename));
                break;
            case AVG_ERR_XML_VALID:
                throw (Exception(AVG_ERR_XML_VALID, 
                        RealFilename + " does not validate."));
                break;
            default:
                throw;
        }
    }
}

void Player::loadString(const std::string& sAVG)
{
    try {
        AVG_TRACE(Logger::MEMORY, "Player::loadString()");

        string sEffectiveDoc = removeStartEndSpaces(sAVG);
        internalLoad(sEffectiveDoc);
    } catch (Exception& ex) {
        switch (ex.GetCode()) {
            case AVG_ERR_XML_PARSE:
                throw Exception(AVG_ERR_XML_PARSE, "Error parsing xml string.");
                break;
            case AVG_ERR_XML_VALID:
                throw Exception(AVG_ERR_XML_VALID, "Error validating xml string.");
                break;
            default:
                throw;
        }
    }
}

void Player::play()
{
    try {
        initPlayback();
        try {
            m_pDisplayEngine->render(m_pRootNode);
            if (m_pDisplayEngine->wasFrameLate()) {
                ThreadProfiler::get()->dumpFrame();
            }
            ThreadProfiler::get()->start();

            while (!m_bStopping) {
                doFrame();
            }
        } catch (...) {
            cleanup();
            throw;
        }
        cleanup();
        AVG_TRACE(Logger::PLAYER, "Playback ended.");
    } catch (Exception& ex) {
        m_bIsPlaying = false;
        AVG_TRACE(Logger::ERROR, ex.GetStr());
        throw;
    }
}

void Player::stop()
{
    if (m_bIsPlaying) {
        m_bStopping = true;
    } else {
        cleanup();
    }
}
        
void Player::initPlayback()
{
    m_bIsPlaying = true;
    if (!m_pRootNode) {
        throw Exception(AVG_ERR_NO_NODE, "Play called, but no xml file loaded.");
    }
    AVG_TRACE(Logger::PLAYER, "Playback started.");
    initGraphics();
    if (m_bAudioEnabled) {
        initAudio();
    }
    try {
        m_pRootNode->setRenderingEngines(m_pDisplayEngine, m_pAudioEngine);
    } catch (Exception&) {
        m_pDisplayEngine = 0;
        m_pAudioEngine = 0;
        throw;
    }

    m_pEventDispatcher->addSource(m_pEventSource);
    m_pEventDispatcher->addSource(m_pTestHelper);
    m_pEventDispatcher->addSink(this);

    m_pDisplayEngine->initRender();
    m_bStopping = false;
    if (m_pTracker) {
        m_pTracker->start();
    }

    m_FrameTime = 0;
    m_NumFrames = 0;
}

bool Player::isPlaying()
{
    return m_bIsPlaying;
}

void Player::setFramerate(double rate)
{
    if (m_bIsPlaying) {
        m_pDisplayEngine->setFramerate(rate);
    } else {
        m_DP.m_Framerate = rate;
        m_DP.m_VBRate = 0;
    }
}

void Player::setVBlankFramerate(int rate)
{
    if (m_bIsPlaying) {
        m_pDisplayEngine->setVBlankRate(rate);
    } else {
        m_DP.m_Framerate = 0;
        m_DP.m_VBRate = rate;
    }
}
        
double Player::getEffectiveFramerate()
{
    if (m_bIsPlaying) {
        return m_pDisplayEngine->getEffectiveFramerate();
    } else {
        return 0;
    }
}

TestHelper * Player::getTestHelper()
{
    return m_pTestHelper;
}

void Player::setFakeFPS(double fps)
{
    if (fabs(fps + 1.0) < 0.0001) {
        // fps = -1
        m_bFakeFPS = false;
    } else {
        m_bFakeFPS = true;
        m_FakeFPS = fps;
    }

    if (m_pAudioEngine) {
        m_pAudioEngine->setAudioEnabled(!m_bFakeFPS);
    }
}

long long Player::getFrameTime()
{
    return m_FrameTime;
}

void Player::addEventSource(IEventSource * pSource)
{
    if (!m_pEventDispatcher) {
        throw Exception(AVG_ERR_UNSUPPORTED, 
                "You must use loadFile() before addEventSource().");
    }
    m_pEventDispatcher->addSource(pSource);
}

TrackerEventSource * Player::addTracker()
{
    TrackerConfig Config;
    Config.load();
    CameraPtr pCamera;

    string sDriver = Config.getParam("/camera/driver/@value");
    string sDevice = Config.getParam("/camera/device/@value");
    bool bFW800 = Config.getBoolParam("/camera/fw800/@value");
    IntPoint CaptureSize(Config.getPointParam("/camera/size/"));
    string sCaptureFormat = Config.getParam("/camera/format/@value");
    double FrameRate = Config.getDoubleParam("/camera/framerate/@value");

    PixelFormat camPF = Bitmap::stringToPixelFormat(sCaptureFormat);
    if (camPF == NO_PIXELFORMAT) {
        throw Exception(AVG_ERR_INVALID_ARGS,
                "Unknown camera pixel format "+sCaptureFormat+".");
    }
    
    AVG_TRACE(Logger::CONFIG, "Trying to create a Tracker for " << sDriver
            << " Camera: " << sDevice << " Size: " << CaptureSize << "format: "
            << sCaptureFormat);
    pCamera = createCamera(sDriver, sDevice, -1, bFW800, CaptureSize, camPF, I8, 
            FrameRate);
    AVG_TRACE(Logger::CONFIG, "Got Camera " << pCamera->getDevice() << " from driver: " 
            << pCamera->getDriverName());
    m_pTracker = new TrackerEventSource(pCamera, Config, 
            IntPoint(m_DP.m_Width, m_DP.m_Height), true);
    addEventSource(m_pTracker);
    if (m_bIsPlaying) {
        m_pTracker->start();
    }

    return m_pTracker;
}

TrackerEventSource * Player::getTracker()
{
    return m_pTracker;
}

int Player::setInterval(int time, PyObject * pyfunc)
{
    Timeout *t = new Timeout(time, pyfunc, true, getFrameTime());
    if (m_bInHandleTimers) {
        m_NewTimeouts.push_back(t);
    } else {
        addTimeout(t);
    }
    return t->GetID();
}

int Player::setTimeout(int time, PyObject * pyfunc)
{
    Timeout *t = new Timeout(time, pyfunc, false, getFrameTime());
    if (m_bInHandleTimers) {
        m_NewTimeouts.push_back(t);
    } else {
        addTimeout(t);
    }
    return t->GetID();
}

int Player::setOnFrameHandler(PyObject * pyfunc) 
{
    return setInterval(0, pyfunc);
}

bool Player::clearInterval(int id)
{
    vector<Timeout*>::iterator it;
    for (it=m_PendingTimeouts.begin(); it!=m_PendingTimeouts.end(); it++) {
        if (id == (*it)->GetID()) {
            if (it == m_PendingTimeouts.begin() && m_bInHandleTimers) {
                m_bCurrentTimeoutDeleted = true;
            }
            delete *it;
            m_PendingTimeouts.erase(it);
{'fraction_non_alphanumeric': 0.125462508495054, 'fraction_numerical': 0.006040927282337839, 'mean_word_length': 2.8431514799767847, 'pattern_counts': {'":': 0, '<': 65, '<?xml version=': 0, '>': 67, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 14}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5793140', 'n_tokens_mistral': 8843, 'n_tokens_neox': 8610, 'n_words': 1732}