From e5ceafbe9d383b70ca015bc650556ae8d5630da9 Mon Sep 17 00:00:00 2001 From: rachytski Date: Thu, 24 Nov 2011 16:28:32 +0400 Subject: [PATCH] added PThreadVideoTimer and tests for it. still it doesn't work as expected. --- platform/platform.pro | 4 +- platform/platform_tests/platform_tests.pro | 4 +- platform/platform_tests/video_timer_test.cpp | 46 +++++++ platform/pthread_video_timer.cpp | 133 +++++++++++++++++++ platform/video_timer.hpp | 1 + 5 files changed, 185 insertions(+), 3 deletions(-) create mode 100644 platform/platform_tests/video_timer_test.cpp create mode 100644 platform/pthread_video_timer.cpp diff --git a/platform/platform.pro b/platform/platform.pro index 3169295253..8c10b98b6d 100644 --- a/platform/platform.pro +++ b/platform/platform.pro @@ -41,7 +41,8 @@ include($$ROOT_DIR/common.pri) ios_concurrent_runner.mm \ platform_ios.mm } else:android* { - SOURCES += platform_android.cpp + SOURCES += platform_android.cpp \ + pthread_video_timer.cpp } macx*|iphone* { @@ -68,3 +69,4 @@ SOURCES += \ video_timer.cpp \ http_request.cpp \ chunks_download_strategy.cpp \ + pthread_video_timer.cpp \ diff --git a/platform/platform_tests/platform_tests.pro b/platform/platform_tests/platform_tests.pro index 26ebf49ea6..40d89c3373 100644 --- a/platform/platform_tests/platform_tests.pro +++ b/platform/platform_tests/platform_tests.pro @@ -20,7 +20,7 @@ win32* { } macx* { QT *= gui # needed for QApplication with event loop, to test async events (downloader, etc.) - LIBS *= "-framework Foundation" "-framework IOKit" + LIBS *= "-framework Foundation" "-framework IOKit" "-framework QuartzCore" } win32*|linux* { QT *= network @@ -33,4 +33,4 @@ SOURCES += \ concurrent_runner_test.cpp \ language_test.cpp \ downloader_test.cpp \ - + video_timer_test.cpp \ diff --git a/platform/platform_tests/video_timer_test.cpp b/platform/platform_tests/video_timer_test.cpp new file mode 100644 index 0000000000..b00f20464d --- /dev/null +++ b/platform/platform_tests/video_timer_test.cpp @@ -0,0 +1,46 @@ +#include "../../testing/testing.hpp" + +#include "../video_timer.hpp" + +#include "../../base/thread.hpp" +#include "../../base/logging.hpp" + +#include "../../std/bind.hpp" + +void incrementValue(int & i) +{ + ++i; +} + +UNIT_TEST(TimerTest) +{ + int i = 0; +#ifdef OMIM_OS_MAC + VideoTimer * videoTimer = CreatePThreadVideoTimer(bind(&incrementValue, ref(i))); +#endif + + LOG(LINFO, ("checking for approximately 60 cycles in second")); + + videoTimer->start(); + + threads::Sleep(1000); + + videoTimer->pause(); + + TEST((i >= 57) && (i <= 61), ("timer doesn't work, i=", i)); + + videoTimer->resume(); + + threads::Sleep(200); + + videoTimer->stop(); + + videoTimer->start(); + + threads::Sleep(200); + + videoTimer->stop(); +} + + + diff --git a/platform/pthread_video_timer.cpp b/platform/pthread_video_timer.cpp new file mode 100644 index 0000000000..5f9dbdbecf --- /dev/null +++ b/platform/pthread_video_timer.cpp @@ -0,0 +1,133 @@ +#include + +#include "video_timer.hpp" +#include "../base/logging.hpp" +#include +#include + +class PThreadVideoTimer : public VideoTimer +{ +private: + + pthread_t m_handle; + pthread_mutex_t m_mutex; + pthread_cond_t m_cond; + int m_frameRate; + +public: + PThreadVideoTimer(VideoTimer::TFrameFn frameFn) + : VideoTimer(frameFn), m_frameRate(60) + { + ::pthread_mutex_init(&m_mutex, 0); + ::pthread_cond_init(&m_cond, 0); + } + + ~PThreadVideoTimer() + { + stop(); + + ::pthread_mutex_destroy(&m_mutex); + ::pthread_cond_destroy(&m_cond); + } + + static void * TimerProc(void * p) + { + PThreadVideoTimer * t = reinterpret_cast(p); + + ::timeval prevtv; + ::gettimeofday(&prevtv, 0); + + ::timeval curtv; + + int64_t interval = 1000000000 / t->m_frameRate; + int64_t halfInterval = interval / 2; + + while (true) + { + ::pthread_mutex_lock(&t->m_mutex); + + t->perform(); + + ::gettimeofday(&curtv, 0); + + int64_t sec = (int64_t)curtv.tv_sec - (int64_t)prevtv.tv_sec; + int64_t nsec = ((int64_t)curtv.tv_usec - (int64_t)prevtv.tv_usec) * 1000; + + int64_t nsecDiff = sec * (int64_t)1000000000 + nsec; + int64_t ceiledDiff = ((nsecDiff + interval - 1) / interval) * interval; + + /// how much we should wait + + if ((ceiledDiff - nsecDiff) < halfInterval) + /// less than a half-frame left, should wait till next frame + ceiledDiff += interval; + + ::timespec ts; + + ts.tv_sec = prevtv.tv_sec + (prevtv.tv_usec * 1000 + ceiledDiff) / 1000000000; + ts.tv_nsec = (prevtv.tv_usec * 1000 + ceiledDiff) % 1000000000; + + ::pthread_cond_timedwait(&t->m_cond, &t->m_mutex, &ts); + + ::gettimeofday(&prevtv, 0); + + if (t->m_state == EStopped) + { + ::pthread_mutex_unlock(&t->m_mutex); + break; + } + + ::pthread_mutex_unlock(&t->m_mutex); + } + + ::pthread_exit(0); + + return 0; + } + + void start() + { + if (m_state == EStopped) + { + ::pthread_create(&m_handle, 0, &TimerProc, reinterpret_cast(this)); + m_state = ERunning; + } + } + + void resume() + { + if (m_state == EPaused) + { + m_state = EStopped; + start(); + } + } + + void pause() + { + stop(); + m_state = EPaused; + } + + void stop() + { + if (m_state == ERunning) + { + ::pthread_mutex_lock(&m_mutex); + m_state = EStopped; + ::pthread_cond_signal(&m_cond); + ::pthread_mutex_unlock(&m_mutex); + ::pthread_join(m_handle, 0); + } + } + + void perform() + { + m_frameFn(); + } +}; + +VideoTimer * CreatePThreadVideoTimer(VideoTimer::TFrameFn frameFn) +{ + return new PThreadVideoTimer(frameFn); +} diff --git a/platform/video_timer.hpp b/platform/video_timer.hpp index dc286df6f2..58e4201f7c 100644 --- a/platform/video_timer.hpp +++ b/platform/video_timer.hpp @@ -40,3 +40,4 @@ public: extern "C" VideoTimer * CreateIOSVideoTimer(VideoTimer::TFrameFn frameFn); extern "C" VideoTimer * CreateAppleVideoTimer(VideoTimer::TFrameFn frameFn); extern "C" VideoTimer * CreateWin32VideoTimer(VideoTimer::TFrameFn frameFn); +extern "C" VideoTimer * CreatePThreadVideoTimer(VideoTimer::TFrameFn frameFn);