if(policy == wait && boost::this_thread::get_id() != thread_.get_id())\r
thread_.join();\r
}\r
+\r
+ void clear()\r
+ {\r
+ std::function<void()> func;\r
+ while(execution_queue_.try_pop(func)){}\r
+ }\r
\r
template<typename Func>\r
auto begin_invoke(Func&& func) -> boost::unique_future<decltype(func())> // noexcept\r
<ClInclude Include="producer\flash\cg_producer.h" />\r
<ClInclude Include="producer\flash\FlashAxContainer.h" />\r
<ClInclude Include="producer\flash\flash_producer.h" />\r
+ <ClInclude Include="producer\flash\TimerHelper.h" />\r
<ClInclude Include="producer\frame_producer.h" />\r
<ClInclude Include="producer\image\image_loader.h" />\r
<ClInclude Include="producer\image\image_producer.h" />\r
<ClInclude Include="producer\flash\axflash.h">\r
<Filter>Source\channel\producer\flash\interop</Filter>\r
</ClInclude>\r
+ <ClInclude Include="producer\flash\TimerHelper.h">\r
+ <Filter>Source\channel\producer\flash\interop</Filter>\r
+ </ClInclude>\r
</ItemGroup>\r
<ItemGroup>\r
<ClCompile Include="protocol\amcp\AMCPCommandQueue.cpp">\r
#include "..\..\stdafx.h"\r
\r
#include "FlashAxContainer.h"\r
-#include "..\..\format\video_format.h"\r
-#include "flash_producer.h"\r
+#include "TimerHelper.h"\r
\r
using namespace ATL;\r
\r
#if defined(_MSC_VER)\r
-#pragma warning (push, 1) // TODO: Legacy code, just disable warnings\r
+#pragma warning (push, 1) // TODO: Legacy code, just disable warnings, will replace with boost::asio in future\r
#endif\r
\r
-namespace caspar { namespace core {\r
+namespace caspar {\r
namespace flash {\r
\r
CComBSTR FlashAxContainer::flashGUID_(_T("{D27CDB6E-AE6D-11CF-96B8-444553540000}"));\r
_ATL_FUNC_INFO fnInfoFlashCallEvent = { CC_STDCALL, VT_EMPTY, 1, { VT_BSTR } };\r
_ATL_FUNC_INFO fnInfoReadyStateChangeEvent = { CC_STDCALL, VT_EMPTY, 1, { VT_I4 } };\r
\r
-FlashAxContainer::FlashAxContainer() : bInPlaceActive_(FALSE), m_lpDD4(0)\r
+FlashAxContainer::FlashAxContainer() : bInPlaceActive_(FALSE), pTimerHelper(0), bInvalidRect_(false), bReadyToRender_(false), bHasNewTiming_(false), m_lpDD4(0), timerCount_(0)\r
{\r
- bInvalidRect_ = false;\r
- bCallSuccessful_ = false;\r
- bReadyToRender_ = false;\r
}\r
FlashAxContainer::~FlashAxContainer()\r
-{\r
+{ \r
if(m_lpDD4)\r
{\r
m_lpDD4->Release();\r
m_lpDD4 = nullptr;\r
}\r
- CASPAR_LOG(info) << "[FlashAxContainer] Destroyed";\r
+\r
+ if(pTimerHelper != 0)\r
+ delete pTimerHelper;\r
}\r
\r
\r
}\r
\r
\r
+///////////////////\r
+// IAdviseSink\r
+///////////////////\r
+void STDMETHODCALLTYPE FlashAxContainer::OnDataChange(FORMATETC* pFormatetc, STGMEDIUM* pStgmed)\r
+{\r
+ ATLTRACE(_T("IAdviseSink::OnDataChange\n"));\r
+}\r
+\r
+void STDMETHODCALLTYPE FlashAxContainer::OnViewChange(DWORD dwAspect, LONG lindex)\r
+{\r
+ ATLTRACE(_T("IAdviseSink::OnViewChange\n"));\r
+}\r
+\r
+void STDMETHODCALLTYPE FlashAxContainer::OnRename(IMoniker* pmk)\r
+{\r
+ ATLTRACE(_T("IAdviseSink::OnRename\n"));\r
+}\r
+\r
+void STDMETHODCALLTYPE FlashAxContainer::OnSave()\r
+{\r
+ ATLTRACE(_T("IAdviseSink::OnSave\n"));\r
+}\r
+\r
+void STDMETHODCALLTYPE FlashAxContainer::OnClose()\r
+{\r
+ ATLTRACE(_T("IAdviseSink::OnClose\n"));\r
+}\r
+\r
+\r
//DirectDraw GUIDS\r
\r
DEFINE_GUID2(CLSID_DirectDraw,0xD7B70EE0,0x4340,0x11CF,0xB0,0x63,0x00,0x20,0xAF,0xC2,0xCD,0x35);\r
*ppvObj = NULL;\r
\r
HRESULT hr;\r
-\r
// Author: Makarov Igor\r
// Transparent Flash Control in Plain C++ \r
// http://www.codeproject.com/KB/COM/flashcontrol.aspx \r
{\r
delete m_lpDD4;\r
m_lpDD4 = NULL;\r
- CASPAR_LOG(warning) << "[FlashAxContainer] Failed to query DirectDraw Service";\r
return E_NOINTERFACE;\r
}\r
}\r
return hr;\r
}\r
\r
+\r
+///////////////////\r
+// ITimerService\r
+///////////////////\r
+HRESULT STDMETHODCALLTYPE FlashAxContainer::CreateTimer(ITimer *pReferenceTimer, ITimer **ppNewTimer)\r
+{\r
+ ATLTRACE(_T("ITimerService::CreateTimer\n"));\r
+ if(pTimerHelper != 0)\r
+ {\r
+ delete pTimerHelper;\r
+ pTimerHelper = 0;\r
+ }\r
+ pTimerHelper = new TimerHelper();\r
+ return QueryInterface(__uuidof(ITimer), (void**) ppNewTimer);\r
+}\r
+\r
+HRESULT STDMETHODCALLTYPE FlashAxContainer::GetNamedTimer(REFGUID rguidName, ITimer **ppTimer)\r
+{\r
+ ATLTRACE(_T("ITimerService::GetNamedTimer"));\r
+ if(ppTimer == NULL)\r
+ return E_POINTER;\r
+ else\r
+ *ppTimer = NULL;\r
+\r
+ return E_FAIL;\r
+}\r
+\r
+HRESULT STDMETHODCALLTYPE FlashAxContainer::SetNamedTimerReference(REFGUID rguidName, ITimer *pReferenceTimer)\r
+{\r
+ ATLTRACE(_T("ITimerService::SetNamedTimerReference"));\r
+ return S_OK;\r
+}\r
+\r
+///////////\r
+// ITimer\r
+///////////\r
+HRESULT STDMETHODCALLTYPE FlashAxContainer::Advise(VARIANT vtimeMin, VARIANT vtimeMax, VARIANT vtimeInterval, DWORD dwFlags, ITimerSink *pTimerSink, DWORD *pdwCookie)\r
+{\r
+ ATLTRACE(_T("Timer::Advise\n"));\r
+\r
+ if(pdwCookie == 0)\r
+ return E_POINTER;\r
+\r
+ if(pTimerHelper != 0)\r
+ {\r
+ pTimerHelper->Setup(vtimeMin.ulVal, vtimeInterval.ulVal, pTimerSink);\r
+ *pdwCookie = pTimerHelper->ID;\r
+ bHasNewTiming_ = true;\r
+\r
+ return S_OK;\r
+ }\r
+ else\r
+ return E_OUTOFMEMORY;\r
+}\r
+\r
+HRESULT STDMETHODCALLTYPE FlashAxContainer::Unadvise(/* [in] */ DWORD dwCookie)\r
+{\r
+ ATLTRACE(_T("Timer::Unadvice\n"));\r
+ if(pTimerHelper != 0)\r
+ {\r
+ pTimerHelper->pTimerSink = 0;\r
+ return S_OK;\r
+ }\r
+ else\r
+ return E_FAIL;\r
+}\r
+\r
+HRESULT STDMETHODCALLTYPE FlashAxContainer::Freeze(/* [in] */ BOOL fFreeze)\r
+{\r
+ ATLTRACE(_T("Timer::Freeze\n"));\r
+ return S_OK;\r
+}\r
+\r
+HRESULT STDMETHODCALLTYPE FlashAxContainer::GetTime(/* [out] */ VARIANT *pvtime)\r
+{\r
+ ATLTRACE(_T("Timer::GetTime\n"));\r
+ if(pvtime == 0)\r
+ return E_POINTER;\r
+\r
+// return E_NOTIMPL;\r
+ pvtime->lVal = 0;\r
+ return S_OK;\r
+}\r
+\r
+int FlashAxContainer::GetFPS() {\r
+ if(pTimerHelper != 0 && pTimerHelper->interval > 0)\r
+ return (1000 / pTimerHelper->interval);\r
+ \r
+ return 0;\r
+}\r
+\r
bool FlashAxContainer::IsReadyToRender() const {\r
return bReadyToRender_;\r
}\r
}\r
else if(str.find(TEXT("OnCommand")) != std::wstring::npos) {\r
//this is how templatehost 1.8 reports that a command has been received\r
- CASPAR_LOG(info) << "TEMPLATEHOST: " << str;\r
+ CASPAR_LOG(info) << TEXT("TEMPLATEHOST: ") << str;\r
bCallSuccessful_ = true;\r
}\r
else if(str.find(TEXT("Activity")) != std::wstring::npos)\r
{\r
- CASPAR_LOG(info) << "TEMPLATEHOST: " << str;\r
+ CASPAR_LOG(info) << TEXT("TEMPLATEHOST: ") << str;\r
\r
//this is how templatehost 1.7 reports that a command has been received\r
if(str.find(TEXT("Command recieved")) != std::wstring::npos)\r
bCallSuccessful_ = true;\r
\r
- /*if(pflash_producer_ != 0 && pflash_producer_->pMonitor_) {\r
+ /*if(pFlashProducer_ != 0 && pFlashProducer_->pMonitor_) {\r
std::wstring::size_type pos = str.find(TEXT('@'));\r
if(pos != std::wstring::npos)\r
- pflash_producer_->GetMonitor()->Inform(str.substr(pos, str.find(TEXT('<'), pos)-pos));\r
+ pFlashProducer_->pMonitor_->Inform(str.substr(pos, str.find(TEXT('<'), pos)-pos));\r
}*/\r
}\r
else if(str.find(TEXT("OnNotify")) != std::wstring::npos)\r
{\r
- CASPAR_LOG(info) << "TEMPLATEHOST: " << str;\r
+ CASPAR_LOG(info) << TEXT("TEMPLATEHOST: ") << str;\r
\r
- //if(pflash_producer_ != 0 && pflash_producer_->pMonitor_) {\r
+ //if(pFlashProducer_ != 0 && pFlashProducer_->pMonitor_) {\r
// std::wstring::size_type pos = str.find(TEXT('@'));\r
// if(pos != std::wstring::npos)\r
- // pflash_producer_->pMonitor_->Inform(str.substr(pos, str.find(TEXT('<'), pos)-pos));\r
+ // pFlashProducer_->pMonitor_->Inform(str.substr(pos, str.find(TEXT('<'), pos)-pos));\r
//}\r
}\r
else if(str.find(TEXT("IsEmpty")) != std::wstring::npos)\r
{\r
ATLTRACE(_T("ShockwaveFlash::IsEmpty\n"));\r
+ bIsEmpty_ = true;\r
}\r
else if(str.find(TEXT("OnError")) != std::wstring::npos)\r
{\r
return S_OK;\r
}\r
\r
-HRESULT FlashAxContainer::SetFormat(const video_format_desc& format_desc) \r
-{\r
- if(m_spInPlaceObjectWindowless == nullptr)\r
- return E_FAIL;\r
-\r
- if(m_rcPos.right != format_desc.width || m_rcPos.bottom != format_desc.height)\r
+void FlashAxContainer::SetFormat(const core::video_format_desc& fmtDesc) {\r
+ if(m_spInPlaceObjectWindowless != 0)\r
{\r
- RECT oldRcPos = m_rcPos;\r
m_rcPos.top = 0;\r
m_rcPos.left = 0;\r
- m_rcPos.right = format_desc.width;\r
- m_rcPos.bottom = format_desc.height;\r
+ m_rcPos.right = fmtDesc.width;\r
+ m_rcPos.bottom = fmtDesc.height;\r
\r
m_pxSize.cx = m_rcPos.right - m_rcPos.left;\r
m_pxSize.cy = m_rcPos.bottom - m_rcPos.top;\r
m_rcPos.right = m_rcPos.left + m_pxSize.cx;\r
m_rcPos.bottom = m_rcPos.top + m_pxSize.cy;\r
\r
- HRESULT result = m_spInPlaceObjectWindowless->SetObjectRects(&m_rcPos, &m_rcPos);\r
+ m_spInPlaceObjectWindowless->SetObjectRects(&m_rcPos, &m_rcPos);\r
bInvalidRect_ = true;\r
- if(FAILED(result))\r
- m_spInPlaceObjectWindowless->SetObjectRects(&oldRcPos, &oldRcPos);\r
-\r
- return result;\r
}\r
-\r
- return S_OK;\r
}\r
\r
HRESULT FlashAxContainer::QueryControl(REFIID iid, void** ppUnk)\r
// ATLTRACE(_T("FlashAxContainer::DrawControl\n"));\r
DVASPECTINFO aspectInfo = {sizeof(DVASPECTINFO), DVASPECTINFOFLAG_CANOPTIMIZE};\r
HRESULT hr = S_OK;\r
- \r
+\r
hr = m_spViewObject->Draw(DVASPECT_CONTENT, -1, &aspectInfo, NULL, NULL, targetDC, NULL, NULL, NULL, NULL); \r
- assert(SUCCEEDED(hr));\r
bInvalidRect_ = false;\r
-\r
-/* const video_format_desc& format_desc = video_format_desc::format_descs[format_];\r
+/* const video_format_desc& fmtDesc = video_format_desc::FormatDescriptions[format_];\r
\r
//Trying to redraw just the dirty rectangles. Doesn't seem to work when the movie uses "filters", such as glow, dropshadow etc.\r
std::vector<flash::DirtyRect>::iterator it = bDirtyRects_.begin();\r
std::vector<flash::DirtyRect>::iterator end = bDirtyRects_.end();\r
for(; it != end; ++it) {\r
flash::DirtyRect& dirtyRect = (*it);\r
- if(dirtyRect.bWhole || dirtyRect.rect.right >= format_desc.width || dirtyRect.rect.bottom >= format_desc.height) {\r
+ if(dirtyRect.bWhole || dirtyRect.rect.right >= fmtDesc.width || dirtyRect.rect.bottom >= fmtDesc.height) {\r
m_spInPlaceObjectWindowless->SetObjectRects(&m_rcPos, &m_rcPos);\r
hr = m_spViewObject->Draw(DVASPECT_OPAQUE, -1, NULL, NULL, NULL, targetDC, NULL, NULL, NULL, NULL); \r
break;\r
return (hr == S_OK);\r
}\r
\r
-bool FlashAxContainer::InvalidRectangle() const\r
+void FlashAxContainer::Tick()\r
{\r
- return bInvalidRect_;\r
+ if(pTimerHelper)\r
+ {\r
+ DWORD time = pTimerHelper->Invoke(); // Tick flash\r
+ if(time - timerCount_ >= 400)\r
+ {\r
+ timerCount_ = time;\r
+ HRESULT hr;\r
+ m_spInPlaceObjectWindowless->OnWindowMessage(WM_TIMER, 3, 0, &hr);\r
+ }\r
+ }\r
}\r
\r
-bool FlashAxContainer::CallFunction(const std::wstring& param)\r
+bool FlashAxContainer::FlashCall(const std::wstring& str)\r
{\r
- bCallSuccessful_ = false;\r
-\r
+ CComBSTR result;\r
CComPtr<IShockwaveFlash> spFlash;\r
- if(SUCCEEDED(QueryControl(&spFlash)))\r
- {\r
- CComBSTR request(param.c_str());\r
- CComBSTR result;\r
- return SUCCEEDED(spFlash->CallFunction(request, &result)) || bCallSuccessful_; \r
- }\r
+ QueryControl(&spFlash);\r
+ CComBSTR request(str.c_str());\r
\r
- return false;\r
+ bCallSuccessful_ = false;\r
+ for(size_t retries = 0; !bCallSuccessful_ && retries < 4; ++retries)\r
+ spFlash->CallFunction(request, &result);\r
+ return bCallSuccessful_;\r
}\r
\r
-// Receives the following messages:\r
-// 0 0x0000 ?\r
-// 275 0x0113 WM_TIMER\r
-// 536 0x0218 WM_POWERBROADCAST\r
-// 49286 0xC086 ?\r
-\r
-\r
} //namespace flash\r
-}} //namespace caspar
\ No newline at end of file
+} //namespace caspar
\ No newline at end of file
*\r
*/\r
\r
-#if defined(_MSC_VER)\r
-#pragma once\r
-#endif\r
-\r
#ifndef _FLASHAXCONTAINER_H__\r
#define _FLASHAXCONTAINER_H__\r
\r
-#pragma warning(push)\r
-#pragma warning(disable : 4996)\r
-\r
- #include <atlbase.h>\r
-\r
- #include <atlcom.h>\r
- #include <atlhost.h>\r
+#pragma once\r
\r
-#pragma warning(push)\r
+#include <atlbase.h>\r
+#include <atlcom.h>\r
+#include <atlhost.h>\r
\r
-#include "..\..\processor\write_frame.h"\r
-#include "..\..\format\video_format.h"\r
\r
#include <ocmm.h>\r
#include <vector>\r
-#include <tbb\atomic.h>\r
\r
+#include "../../format/video_format.h"\r
#include "axflash.h"\r
+//#import "progid:ShockwaveFlash.ShockwaveFlash.9" no_namespace, named_guids\r
\r
#include <ddraw.h>\r
#include <comdef.h>\r
\r
+#include "TimerHelper.h"\r
+\r
#ifndef DEFINE_GUID2\r
#define DEFINE_GUID2(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \\r
const GUID name \\r
\r
_COM_SMARTPTR_TYPEDEF(IDirectDraw4, IID_IDirectDraw4);\r
\r
-//#import "progid:ShockwaveFlash.ShockwaveFlash.9" no_namespace, named_guids\r
+namespace caspar {\r
+\r
+namespace flash {\r
+\r
+class TimerHelper;\r
+struct DirtyRect {\r
+ DirtyRect(LONG l, LONG t, LONG r, LONG b, bool e) : bErase(e), bWhole(false) { \r
+ rect.left = l;\r
+ rect.top = t;\r
+ rect.right = r;\r
+ rect.bottom = b; \r
+ }\r
+ DirtyRect(const RECT& rc, bool e) : bErase(e), bWhole(false) {\r
+ rect.left = rc.left;\r
+ rect.top = rc.top;\r
+ rect.right = rc.right;\r
+ rect.bottom = rc.bottom; \r
+ }\r
+ explicit DirtyRect(bool b) : bWhole(b) {}\r
+\r
+ RECT rect;\r
+ bool bErase;\r
+ bool bWhole;\r
+};\r
\r
-namespace caspar { namespace core { namespace flash {\r
- \r
extern _ATL_FUNC_INFO fnInfoFlashCallEvent;\r
extern _ATL_FUNC_INFO fnInfoReadyStateChangeEvent;\r
\r
public IOleInPlaceSiteWindowless,\r
public IObjectWithSiteImpl<FlashAxContainer>,\r
public IServiceProvider,\r
+ public IAdviseSink,\r
+ public ITimerService,\r
+ public ITimer,\r
public IDispatchImpl<IDispatch>,\r
public IDispEventSimpleImpl<0, FlashAxContainer, &DIID__IShockwaveFlashEvents>\r
{\r
\r
COM_INTERFACE_ENTRY(IServiceProvider)\r
\r
+ COM_INTERFACE_ENTRY(IAdviseSink)\r
+\r
+ COM_INTERFACE_ENTRY(ITimerService)\r
+\r
+ COM_INTERFACE_ENTRY(ITimer)\r
END_COM_MAP()\r
\r
BEGIN_SINK_MAP(FlashAxContainer)\r
STDMETHOD(OnFocus)(BOOL fGotFocus);\r
STDMETHOD(ShowPropertyFrame)();\r
\r
+// IAdviseSink\r
+ STDMETHOD_(void, OnDataChange)(FORMATETC* pFormatetc, STGMEDIUM* pStgmed);\r
+ STDMETHOD_(void, OnViewChange)(DWORD dwAspect, LONG lindex);\r
+ STDMETHOD_(void, OnRename)(IMoniker* pmk);\r
+ STDMETHOD_(void, OnSave)();\r
+ STDMETHOD_(void, OnClose)();\r
+\r
// IServiceProvider\r
STDMETHOD(QueryService)( REFGUID rsid, REFIID riid, void** ppvObj);\r
\r
ATLTRACENOTIMPL(_T("IOleContainer::LockContainer"));\r
}\r
\r
+//ITimerService\r
+ STDMETHOD(CreateTimer)(ITimer *pReferenceTimer, ITimer **ppNewTimer);\r
+ STDMETHOD(GetNamedTimer)(REFGUID rguidName, ITimer **ppTimer);\r
+ STDMETHOD(SetNamedTimerReference)(REFGUID rguidName, ITimer *pReferenceTimer);\r
+\r
+//ITimer\r
+ STDMETHOD(Advise)(VARIANT vtimeMin, VARIANT vtimeMax, VARIANT vtimeInterval, DWORD dwFlags, ITimerSink *pTimerSink, DWORD *pdwCookie);\r
+ STDMETHOD(Unadvise)(DWORD dwCookie);\r
+ STDMETHOD(Freeze)(BOOL fFreeze);\r
+ STDMETHOD(GetTime)(VARIANT *pvtime);\r
+ int GetFPS();\r
+\r
HRESULT CreateAxControl();\r
void DestroyAxControl();\r
HRESULT QueryControl(REFIID iid, void** ppUnk);\r
return QueryControl(__uuidof(Q), (void**)ppUnk);\r
}\r
\r
- bool InvalidRectangle() const;\r
- bool CallFunction(const std::wstring& param);\r
-\r
// static ATL::CComObject<FlashAxContainer>* CreateInstance();\r
\r
+ void Tick();\r
+ bool FlashCall(const std::wstring& str);\r
bool DrawControl(HDC targetDC);\r
+ bool InvalidRect() const { return bInvalidRect_; } \r
+ bool IsEmpty() const { return bIsEmpty_; }\r
\r
- HRESULT SetFormat(const video_format_desc&);\r
+ void SetFormat(const core::video_format_desc&);\r
bool IsReadyToRender() const;\r
void EnterFullscreen();\r
\r
static bool CheckForFlashSupport();\r
+\r
+ ATL::CComPtr<IOleInPlaceObjectWindowless> m_spInPlaceObjectWindowless;\r
+\r
private:\r
+ TimerHelper* pTimerHelper;\r
+ volatile bool bInvalidRect_;\r
+ volatile bool bCallSuccessful_;\r
+ volatile bool bReadyToRender_;\r
+ volatile bool bIsEmpty_;\r
+ volatile bool bHasNewTiming_;\r
+ std::vector<DirtyRect> bDirtyRects_;\r
+\r
+\r
IDirectDraw4Ptr *m_lpDD4;\r
static CComBSTR flashGUID_;\r
\r
- tbb::atomic<bool> bInvalidRect_;\r
- tbb::atomic<bool> bCallSuccessful_;\r
- tbb::atomic<bool> bReadyToRender_;\r
+ DWORD timerCount_;\r
\r
// state\r
bool bUIActive_;\r
ATL::CComPtr<IOleObject> m_spServices;\r
ATL::CComPtr<IOleObject> m_spOleObject;\r
ATL::CComPtr<IViewObjectEx> m_spViewObject;\r
- ATL::CComPtr<IOleInPlaceObjectWindowless> m_spInPlaceObjectWindowless;\r
\r
// ATL::CComPtr<ATL::CComObject<MyMoniker> > m_spMyMoniker;\r
};\r
\r
} //namespace flash\r
-}} //namespace caspar\r
+} //namespace caspar\r
\r
#endif //_FLASHAXCONTAINER_H__
\ No newline at end of file
--- /dev/null
+/*\r
+* copyright (c) 2010 Sveriges Television AB <info@casparcg.com>\r
+*\r
+* This file is part of CasparCG.\r
+*\r
+* CasparCG is free software: you can redistribute it and/or modify\r
+* it under the terms of the GNU General Public License as published by\r
+* the Free Software Foundation, either version 3 of the License, or\r
+* (at your option) any later version.\r
+*\r
+* CasparCG is distributed in the hope that it will be useful,\r
+* but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+* GNU General Public License for more details.\r
+\r
+* You should have received a copy of the GNU General Public License\r
+* along with CasparCG. If not, see <http://www.gnu.org/licenses/>.\r
+*\r
+*/\r
+ \r
+#ifndef _TIMER_HELPER_H__\r
+#define _TIMER_HELPER_H__\r
+\r
+#include "FlashAxContainer.h"\r
+\r
+namespace caspar {\r
+namespace flash {\r
+\r
+ class TimerHelper\r
+ {\r
+ TimerHelper(const TimerHelper&);\r
+ const TimerHelper& operator=(const TimerHelper&);\r
+\r
+ public:\r
+ TimerHelper()\r
+ {}\r
+ TimerHelper(DWORD first, DWORD interv, ITimerSink* pTS) : firstTime(first), interval(interv), currentTime(first), pTimerSink(pTS)\r
+ {\r
+ ID = first;\r
+ }\r
+ ~TimerHelper()\r
+ {\r
+ }\r
+ void Setup(DWORD first, DWORD interv, ITimerSink* pTS)\r
+ {\r
+ firstTime = first;\r
+ interval = interv;\r
+ currentTime = first;\r
+ pTimerSink = pTS;\r
+ ID = first;\r
+ }\r
+\r
+ DWORD Invoke()\r
+ {\r
+ if(pTimerSink != 0)\r
+ {\r
+ VARIANT value;\r
+ value.vt = VT_UI4;\r
+ value.ulVal = currentTime;\r
+\r
+ pTimerSink->OnTimer(value);\r
+ currentTime += interval;\r
+ }\r
+ return currentTime;\r
+ }\r
+\r
+ DWORD firstTime;\r
+ DWORD interval;\r
+ DWORD currentTime;\r
+ ATL::CComPtr<ITimerSink> pTimerSink;\r
+ DWORD ID;\r
+ };\r
+\r
+} //namespace flash\r
+} //namespace caspar\r
+\r
+#endif //_TIMER_HELPER_H__
\ No newline at end of file
class flash_renderer\r
{\r
public:\r
- flash_renderer() : tail_(draw_frame::empty()), head_(draw_frame::empty()), bmp_data_(nullptr), ax_(nullptr) {}\r
-\r
- ~flash_renderer()\r
- { \r
- if(ax_)\r
- {\r
- ax_->DestroyAxControl();\r
- ax_->Release();\r
- }\r
- CASPAR_LOG(info) << print() << L" Ended";\r
- }\r
-\r
- void load(const std::shared_ptr<frame_processor_device>& frame_processor, const std::wstring& filename)\r
+ flash_renderer(const std::shared_ptr<frame_processor_device>& frame_processor, const std::wstring& filename) \r
+ : head_(draw_frame::empty()), bmp_data_(nullptr), ax_(nullptr), filename_(filename), hdc_(CreateCompatibleDC(0), DeleteDC), frame_processor_(frame_processor), \r
+ format_desc_(frame_processor->get_video_format_desc())\r
{\r
- filename_ = filename;\r
- hdc_.reset(CreateCompatibleDC(0), DeleteDC);\r
- frame_processor_ = frame_processor;\r
CASPAR_LOG(info) << print() << L" Started";\r
\r
- if(FAILED(CComObject<FlashAxContainer>::CreateInstance(&ax_)))\r
+ if(FAILED(CComObject<caspar::flash::FlashAxContainer>::CreateInstance(&ax_)))\r
BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(bprint() + "Failed to create FlashAxContainer"));\r
\r
if(FAILED(ax_->CreateAxControl()))\r
if(FAILED(spFlash->put_ScaleMode(2))) //Exact fit. Scale without respect to the aspect ratio.\r
BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(bprint() + "Failed to Set Scale Mode"));\r
\r
- if(FAILED(ax_->SetFormat(frame_processor_->get_video_format_desc()))) // stop if failed\r
- BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(bprint() + "Failed to Set Format"));\r
+ ax_->SetFormat(format_desc_);\r
+ //if(FAILED(ax_->SetFormat(frame_processor_->get_video_format_desc()))) // stop if failed\r
+ // BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(bprint() + "Failed to Set Format"));\r
\r
- format_desc_ = frame_processor->get_video_format_desc();\r
-\r
BITMAPINFO info;\r
memset(&info, 0, sizeof(BITMAPINFO));\r
info.bmiHeader.biBitCount = 32;\r
\r
bmp_.reset(CreateDIBSection(static_cast<HDC>(hdc_.get()), &info, DIB_RGB_COLORS, reinterpret_cast<void**>(&bmp_data_), 0, 0), DeleteObject);\r
SelectObject(static_cast<HDC>(hdc_.get()), bmp_.get()); \r
-\r
- frame_buffer_.set_capacity(20);\r
- for(int n = 0; n < frame_buffer_.capacity()/2; ++n)\r
- frame_buffer_.try_push(draw_frame::empty());\r
- is_running_ = true;\r
}\r
\r
- void param(const std::wstring& param) \r
+ ~flash_renderer()\r
{ \r
- params_.push(param);\r
+ if(ax_)\r
+ {\r
+ ax_->DestroyAxControl();\r
+ ax_->Release();\r
+ }\r
+ CASPAR_LOG(info) << print() << L" Ended";\r
}\r
- \r
- void tick()\r
+ \r
+ void param(const std::wstring& param)\r
{ \r
- std::wstring param;\r
- if(params_.try_pop(param))\r
- {\r
- for(size_t retries = 0; !ax_->CallFunction(param); ++retries)\r
- {\r
- CASPAR_LOG(debug) << print() << L" Retrying. Count: " << retries;\r
- if(retries > 3)\r
- CASPAR_LOG(warning) << "Flash Function Call Failed. Param: " << param;\r
- }\r
- } \r
-\r
- if(!is_running_)\r
- PostQuitMessage(0);\r
+ if(!ax_->FlashCall(param))\r
+ CASPAR_LOG(warning) << "Flash Function Call Failed. Param: " << param;\r
+ }\r
+ \r
+ safe_ptr<draw_frame> render_frame()\r
+ {\r
+ auto frame = render_simple_frame();\r
+ \r
+ auto running_fps = ax_->GetFPS();\r
+ auto target_fps = static_cast<int>(format_desc_.mode == video_mode::progressive ? format_desc_.fps : format_desc_.fps/2.0);\r
+ if(target_fps < running_fps)\r
+ frame = draw_frame::interlace(frame, render_simple_frame(), format_desc_.mode);\r
+ return frame;\r
}\r
+ \r
+ std::wstring print() const{ return L"flash[" + boost::filesystem::wpath(filename_).filename() + L"] Render thread"; }\r
+ std::string bprint() const{ return narrow(print()); }\r
\r
- void render()\r
+private:\r
+\r
+ safe_ptr<draw_frame> render_simple_frame()\r
{\r
- if(ax_->IsReadyToRender() && ax_->InvalidRectangle())\r
+ if(!ax_->IsEmpty())\r
+ ax_->Tick();\r
+\r
+ if(ax_->InvalidRect())\r
{ \r
std::fill_n(bmp_data_, format_desc_.size, 0);\r
ax_->DrawControl(static_cast<HDC>(hdc_.get()));\r
auto frame = frame_processor_->create_frame();\r
std::copy_n(bmp_data_, format_desc_.size, frame->image_data().begin());\r
head_ = frame;\r
- } \r
- if(!frame_buffer_.try_push(head_))\r
- CASPAR_LOG(trace) << print() << " overflow";\r
- }\r
- \r
- safe_ptr<draw_frame> get_frame()\r
- { \r
- if(!frame_buffer_.try_pop(tail_))\r
- CASPAR_LOG(trace) << print() << " underflow";\r
+ } \r
\r
- auto frame = tail_;\r
- if(frame_buffer_.size() > frame_buffer_.capacity()/2) // Regulate between interlaced and progressive\r
- {\r
- frame_buffer_.try_pop(tail_);\r
- frame = draw_frame::interlace(frame, tail_, format_desc_.mode);\r
- }\r
- return frame;\r
- }\r
- \r
- void stop()\r
- {\r
- is_running_ = false;\r
+ return head_;\r
}\r
\r
- std::wstring print() const{ return L"flash[" + boost::filesystem::wpath(filename_).filename() + L"] Render thread"; }\r
- std::string bprint() const{ return narrow(print()); }\r
-\r
-private:\r
- tbb::atomic<bool> is_running_;\r
std::wstring filename_;\r
std::shared_ptr<frame_processor_device> frame_processor_;\r
video_format_desc format_desc_;\r
std::shared_ptr<void> hdc_;\r
std::shared_ptr<void> bmp_;\r
\r
- CComObject<FlashAxContainer>* ax_;\r
- tbb::concurrent_bounded_queue<safe_ptr<draw_frame>> frame_buffer_; \r
- safe_ptr<draw_frame> tail_;\r
+ CComObject<caspar::flash::FlashAxContainer>* ax_;\r
safe_ptr<draw_frame> head_;\r
-\r
- tbb::concurrent_queue<std::wstring> params_;\r
};\r
\r
struct flash_producer::implementation\r
{ \r
- implementation(const std::wstring& filename) : filename_(filename), renderer_(std::make_shared<flash_renderer>())\r
+ implementation(const std::wstring& filename) : filename_(filename), tail_(draw_frame::empty())\r
{ \r
if(!boost::filesystem::exists(filename))\r
BOOST_THROW_EXCEPTION(file_not_found() << boost::errinfo_file_name(narrow(filename)));\r
+ \r
+ executor_.begin_invoke([=]\r
+ {\r
+ ::OleInitialize(nullptr);\r
+ });\r
+\r
+ frame_buffer_.set_capacity(25);\r
+ while(frame_buffer_.try_push(draw_frame::empty())){}\r
}\r
\r
~implementation()\r
{\r
- renderer_->stop();\r
- }\r
- \r
- void param(const std::wstring& param) \r
- { \r
- renderer_->param(param);\r
+ executor_.clear();\r
+ executor_.invoke([=]\r
+ {\r
+ renderer_ = nullptr;\r
+ ::OleUninitialize();\r
+ });\r
}\r
- \r
- void run()\r
- {\r
- CASPAR_LOG(info) << print() << " started.";\r
-\r
- ::OleInitialize(nullptr);\r
- \r
- volatile auto keep_alive = renderer_;\r
- renderer_->load(frame_processor_, filename_);\r
-\r
- MSG msg;\r
- while(GetMessage(&msg, 0, 0, 0))\r
- { \r
- if(msg.message == WM_USER + 1)\r
- renderer_->render();\r
-\r
- TranslateMessage(&msg);\r
- DispatchMessage(&msg);\r
-\r
- renderer_->tick(); \r
- }\r
\r
- renderer_ = nullptr;\r
-\r
- ::OleUninitialize();\r
+ virtual safe_ptr<draw_frame> receive()\r
+ { \r
+ if(!frame_buffer_.try_pop(tail_))\r
+ CASPAR_LOG(trace) << print() << " underflow";\r
\r
- CASPAR_LOG(info) << print() << " ended.";\r
+ executor_.begin_invoke([=]\r
+ {\r
+ auto frame = draw_frame::empty();\r
+ try\r
+ {\r
+ if(renderer_)\r
+ frame = renderer_->render_frame();\r
+ }\r
+ catch(...)\r
+ {\r
+ CASPAR_LOG_CURRENT_EXCEPTION();\r
+ renderer_ = nullptr;\r
+ }\r
+ frame_buffer_.try_push(frame);\r
+ }); \r
+ \r
+ return tail_;\r
}\r
-\r
+ \r
virtual void initialize(const safe_ptr<frame_processor_device>& frame_processor)\r
{\r
frame_processor_ = frame_processor;\r
- thread_ = boost::thread([this]{run();});\r
+ executor_.start();\r
}\r
+ \r
+ void param(const std::wstring& param) \r
+ { \r
+ executor_.begin_invoke([=]\r
+ {\r
+ if(!renderer_)\r
+ renderer_.reset(new flash_renderer(frame_processor_, filename_));\r
\r
- virtual safe_ptr<draw_frame> receive()\r
- {\r
- return renderer_->get_frame();\r
+ try\r
+ {\r
+ renderer_->param(param);\r
+ }\r
+ catch(...)\r
+ {\r
+ CASPAR_LOG_CURRENT_EXCEPTION();\r
+ renderer_ = nullptr;\r
+ }\r
+ });\r
}\r
\r
- boost::thread thread_;\r
+ safe_ptr<draw_frame> tail_;\r
+ tbb::concurrent_bounded_queue<safe_ptr<draw_frame>> frame_buffer_;\r
+ executor executor_;\r
\r
std::shared_ptr<flash_renderer> renderer_;\r
std::shared_ptr<frame_processor_device> frame_processor_;\r
<stretch>uniform</stretch>\r
<windowed>true</windowed>\r
</ogl>\r
- <audio/>\r
+ <!--audio/-->\r
<!--decklink> \r
</decklink>\r
<bluefish>\r