/* * Copyright (C) 2003-2006 Gabest * http://www.gabest.org * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Make; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * http://www.gnu.org/copyleft/gpl.html * */ #include "stdafx.h" #include "ISubPic.h" #include "..\DSUtil\DSUtil.h" // // ISubPicImpl // ISubPicImpl::ISubPicImpl() : CUnknown(NAME("ISubPicImpl"), NULL) , m_rtStart(0), m_rtStop(0) , m_rcDirty(0, 0, 0, 0), m_maxsize(0, 0), m_size(0, 0), m_vidrect(0, 0, 0, 0) { } STDMETHODIMP ISubPicImpl::NonDelegatingQueryInterface(REFIID riid, void** ppv) { return QI(ISubPic) __super::NonDelegatingQueryInterface(riid, ppv); } // ISubPic STDMETHODIMP_(REFERENCE_TIME) ISubPicImpl::GetStart() { return(m_rtStart); } STDMETHODIMP_(REFERENCE_TIME) ISubPicImpl::GetStop() { return(m_rtStop); } STDMETHODIMP_(void) ISubPicImpl::SetStart(REFERENCE_TIME rtStart) { m_rtStart = rtStart; } STDMETHODIMP_(void) ISubPicImpl::SetStop(REFERENCE_TIME rtStop) { m_rtStop = rtStop; } STDMETHODIMP ISubPicImpl::CopyTo(ISubPic* pSubPic) { if(!pSubPic) return E_POINTER; pSubPic->SetStart(m_rtStart); pSubPic->SetStop(m_rtStop); pSubPic->SetDirtyRect(m_rcDirty); pSubPic->SetSize(m_size, m_vidrect); return S_OK; } STDMETHODIMP ISubPicImpl::GetDirtyRect(RECT* pDirtyRect) { return pDirtyRect ? *pDirtyRect = m_rcDirty, S_OK : E_POINTER; } STDMETHODIMP ISubPicImpl::SetDirtyRect(RECT* pDirtyRect) { return pDirtyRect ? m_rcDirty = *pDirtyRect, S_OK : E_POINTER; } STDMETHODIMP ISubPicImpl::GetMaxSize(SIZE* pMaxSize) { return pMaxSize ? *pMaxSize = m_maxsize, S_OK : E_POINTER; } STDMETHODIMP ISubPicImpl::SetSize(SIZE size, RECT vidrect) { m_size = size; m_vidrect = vidrect; if(m_size.cx > m_maxsize.cx) { m_size.cy = MulDiv(m_size.cy, m_maxsize.cx, m_size.cx); m_size.cx = m_maxsize.cx; } if(m_size.cy > m_maxsize.cy) { m_size.cx = MulDiv(m_size.cx, m_maxsize.cy, m_size.cy); m_size.cy = m_maxsize.cy; } if(m_size.cx != size.cx || m_size.cy != size.cy) { m_vidrect.top = MulDiv(m_vidrect.top, m_size.cx, size.cx); m_vidrect.bottom = MulDiv(m_vidrect.bottom, m_size.cx, size.cx); m_vidrect.left = MulDiv(m_vidrect.left, m_size.cy, size.cy); m_vidrect.right = MulDiv(m_vidrect.right, m_size.cy, size.cy); } return S_OK; } // // ISubPicAllocatorImpl // ISubPicAllocatorImpl::ISubPicAllocatorImpl(SIZE cursize, bool fDynamicWriteOnly, bool fPow2Textures) : CUnknown(NAME("ISubPicAllocatorImpl"), NULL) , m_cursize(cursize) , m_fDynamicWriteOnly(fDynamicWriteOnly) , m_fPow2Textures(fPow2Textures) { m_curvidrect = CRect(CPoint(0,0), m_cursize); } STDMETHODIMP ISubPicAllocatorImpl::NonDelegatingQueryInterface(REFIID riid, void** ppv) { return QI(ISubPicAllocator) __super::NonDelegatingQueryInterface(riid, ppv); } // ISubPicAllocator STDMETHODIMP ISubPicAllocatorImpl::SetCurSize(SIZE cursize) { m_cursize = cursize; return S_OK; } STDMETHODIMP ISubPicAllocatorImpl::SetCurVidRect(RECT curvidrect) { m_curvidrect = curvidrect; return S_OK; } STDMETHODIMP ISubPicAllocatorImpl::GetStatic(ISubPic** ppSubPic) { if(!ppSubPic) return E_POINTER; if(!m_pStatic) { if(!Alloc(true, &m_pStatic) || !m_pStatic) return E_OUTOFMEMORY; } m_pStatic->SetSize(m_cursize, m_curvidrect); (*ppSubPic = m_pStatic)->AddRef(); return S_OK; } STDMETHODIMP ISubPicAllocatorImpl::AllocDynamic(ISubPic** ppSubPic) { if(!ppSubPic) return E_POINTER; if(!Alloc(false, ppSubPic) || !*ppSubPic) return E_OUTOFMEMORY; (*ppSubPic)->SetSize(m_cursize, m_curvidrect); return S_OK; } STDMETHODIMP_(bool) ISubPicAllocatorImpl::IsDynamicWriteOnly() { return(m_fDynamicWriteOnly); } STDMETHODIMP ISubPicAllocatorImpl::ChangeDevice(IUnknown* pDev) { m_pStatic = NULL; return S_OK; } // // ISubPicProviderImpl // ISubPicProviderImpl::ISubPicProviderImpl(CCritSec* pLock) : CUnknown(NAME("ISubPicProviderImpl"), NULL) , m_pLock(pLock) { } ISubPicProviderImpl::~ISubPicProviderImpl() { } STDMETHODIMP ISubPicProviderImpl::NonDelegatingQueryInterface(REFIID riid, void** ppv) { return QI(ISubPicProvider) __super::NonDelegatingQueryInterface(riid, ppv); } // ISubPicProvider STDMETHODIMP ISubPicProviderImpl::Lock() { return m_pLock ? m_pLock->Lock(), S_OK : E_FAIL; } STDMETHODIMP ISubPicProviderImpl::Unlock() { return m_pLock ? m_pLock->Unlock(), S_OK : E_FAIL; } // // ISubPicQueueImpl // ISubPicQueueImpl::ISubPicQueueImpl(ISubPicAllocator* pAllocator, HRESULT* phr) : CUnknown(NAME("ISubPicQueueImpl"), NULL) , m_pAllocator(pAllocator) , m_rtNow(0) , m_fps(25.0) { if(phr) *phr = S_OK; if(!m_pAllocator) { if(phr) *phr = E_FAIL; return; } } ISubPicQueueImpl::~ISubPicQueueImpl() { } STDMETHODIMP ISubPicQueueImpl::NonDelegatingQueryInterface(REFIID riid, void** ppv) { return QI(ISubPicQueue) __super::NonDelegatingQueryInterface(riid, ppv); } // ISubPicQueue STDMETHODIMP ISubPicQueueImpl::SetSubPicProvider(ISubPicProvider* pSubPicProvider) { CAutoLock cAutoLock(&m_csSubPicProvider); // if(m_pSubPicProvider != pSubPicProvider) { m_pSubPicProvider = pSubPicProvider; Invalidate(); } return S_OK; } STDMETHODIMP ISubPicQueueImpl::GetSubPicProvider(ISubPicProvider** pSubPicProvider) { if(!pSubPicProvider) return E_POINTER; CAutoLock cAutoLock(&m_csSubPicProvider); if(m_pSubPicProvider) (*pSubPicProvider = m_pSubPicProvider)->AddRef(); return !!*pSubPicProvider ? S_OK : E_FAIL; } STDMETHODIMP ISubPicQueueImpl::SetFPS(double fps) { m_fps = fps; return S_OK; } STDMETHODIMP ISubPicQueueImpl::SetTime(REFERENCE_TIME rtNow) { m_rtNow = rtNow; return S_OK; } // private HRESULT ISubPicQueueImpl::RenderTo(ISubPic* pSubPic, REFERENCE_TIME rtStart, REFERENCE_TIME rtStop, double fps) { HRESULT hr = E_FAIL; if(!pSubPic) return hr; CComPtr pSubPicProvider; if(FAILED(GetSubPicProvider(&pSubPicProvider)) || !pSubPicProvider) return hr; if(FAILED(pSubPicProvider->Lock())) return hr; SubPicDesc spd; if(SUCCEEDED(pSubPic->ClearDirtyRect(0xFF000000)) && SUCCEEDED(pSubPic->Lock(spd))) { CRect r(0,0,0,0); hr = pSubPicProvider->Render(spd, (rtStart+rtStop)/2, fps, r); pSubPic->SetStart(rtStart); pSubPic->SetStop(rtStop); pSubPic->Unlock(r); } pSubPicProvider->Unlock(); return hr; } // // CSubPicQueue // CSubPicQueue::CSubPicQueue(int nMaxSubPic, ISubPicAllocator* pAllocator, HRESULT* phr) : ISubPicQueueImpl(pAllocator, phr) , m_nMaxSubPic(nMaxSubPic) , m_rtQueueStart(0) { if(phr && FAILED(*phr)) return; if(m_nMaxSubPic < 1) {if(phr) *phr = E_INVALIDARG; return;} m_fBreakBuffering = false; for(int i = 0; i < EVENT_COUNT; i++) m_ThreadEvents[i] = CreateEvent(NULL, FALSE, FALSE, NULL); CAMThread::Create(); } CSubPicQueue::~CSubPicQueue() { m_fBreakBuffering = true; SetEvent(m_ThreadEvents[EVENT_EXIT]); CAMThread::Close(); for(int i = 0; i < EVENT_COUNT; i++) CloseHandle(m_ThreadEvents[i]); } // ISubPicQueue STDMETHODIMP CSubPicQueue::SetFPS(double fps) { HRESULT hr = __super::SetFPS(fps); if(FAILED(hr)) return hr; SetEvent(m_ThreadEvents[EVENT_TIME]); return S_OK; } STDMETHODIMP CSubPicQueue::SetTime(REFERENCE_TIME rtNow) { HRESULT hr = __super::SetTime(rtNow); if(FAILED(hr)) return hr; SetEvent(m_ThreadEvents[EVENT_TIME]); return S_OK; } STDMETHODIMP CSubPicQueue::Invalidate(REFERENCE_TIME rtInvalidate) { { // CAutoLock cQueueLock(&m_csQueueLock); // RemoveAll(); m_rtInvalidate = rtInvalidate; m_fBreakBuffering = true; SetEvent(m_ThreadEvents[EVENT_TIME]); } return S_OK; } STDMETHODIMP_(bool) CSubPicQueue::LookupSubPic(REFERENCE_TIME rtNow, ISubPic** ppSubPic) { if(!ppSubPic) return(false); *ppSubPic = NULL; CAutoLock cQueueLock(&m_csQueueLock); POSITION pos = GetHeadPosition(); while(pos) { CComPtr pSubPic = GetNext(pos); if(pSubPic->GetStart() <= rtNow && rtNow < pSubPic->GetStop()) { *ppSubPic = pSubPic.Detach(); break; } } return(!!*ppSubPic); } STDMETHODIMP CSubPicQueue::GetStats(int& nSubPics, REFERENCE_TIME& rtNow, REFERENCE_TIME& rtStart, REFERENCE_TIME& rtStop) { CAutoLock cQueueLock(&m_csQueueLock); nSubPics = GetCount(); rtNow = m_rtNow; rtStart = m_rtQueueStart; rtStop = GetCount() > 0 ? GetTail()->GetStop() : rtStart; return S_OK; } STDMETHODIMP CSubPicQueue::GetStats(int nSubPic, REFERENCE_TIME& rtStart, REFERENCE_TIME& rtStop) { CAutoLock cQueueLock(&m_csQueueLock); rtStart = rtStop = -1; if(nSubPic >= 0 && nSubPic < (int)GetCount()) { if(POSITION pos = FindIndex(nSubPic)) { rtStart = GetAt(pos)->GetStart(); rtStop = GetAt(pos)->GetStop(); } } else { return E_INVALIDARG; } return S_OK; } // private REFERENCE_TIME CSubPicQueue::UpdateQueue() { CAutoLock cQueueLock(&m_csQueueLock); REFERENCE_TIME rtNow = m_rtNow; if(rtNow < m_rtQueueStart) { RemoveAll(); } else { while(GetCount() > 0 && rtNow >= GetHead()->GetStop()) RemoveHead(); } m_rtQueueStart = rtNow; if(GetCount() > 0) rtNow = GetTail()->GetStop(); return(rtNow); } void CSubPicQueue::AppendQueue(ISubPic* pSubPic) { CAutoLock cQueueLock(&m_csQueueLock); AddTail(pSubPic); } // overrides DWORD CSubPicQueue::ThreadProc() { SetThreadPriority(m_hThread, THREAD_PRIORITY_LOWEST/*THREAD_PRIORITY_BELOW_NORMAL*/); while((WaitForMultipleObjects(EVENT_COUNT, m_ThreadEvents, FALSE, INFINITE) - WAIT_OBJECT_0) == EVENT_TIME) { double fps = m_fps; REFERENCE_TIME rtNow = UpdateQueue(); int nMaxSubPic = m_nMaxSubPic; CComPtr pSubPicProvider; if(SUCCEEDED(GetSubPicProvider(&pSubPicProvider)) && pSubPicProvider && SUCCEEDED(pSubPicProvider->Lock())) { for(POSITION pos = pSubPicProvider->GetStartPosition(rtNow, fps); pos && !m_fBreakBuffering && GetCount() < (size_t)nMaxSubPic; pos = pSubPicProvider->GetNext(pos)) { REFERENCE_TIME rtStart = pSubPicProvider->GetStart(pos, fps); REFERENCE_TIME rtStop = pSubPicProvider->GetStop(pos, fps); if(m_rtNow >= rtStart) { // m_fBufferUnderrun = true; if(m_rtNow >= rtStop) continue; } if(rtStart >= m_rtNow + 60*10000000i64) // we are already one minute ahead, this should be enough break; if(rtNow < rtStop) { CComPtr pStatic; if(FAILED(m_pAllocator->GetStatic(&pStatic))) break; HRESULT hr = RenderTo(pStatic, rtStart, rtStop, fps); if(FAILED(hr)) break; if(S_OK != hr) // subpic was probably empty continue; CComPtr pDynamic; if(FAILED(m_pAllocator->AllocDynamic(&pDynamic)) || FAILED(pStatic->CopyTo(pDynamic))) break; AppendQueue(pDynamic); } } pSubPicProvider->Unlock(); } if(m_fBreakBuffering) { CAutoLock cQueueLock(&m_csQueueLock); REFERENCE_TIME rtInvalidate = m_rtInvalidate; while(GetCount() && GetTail()->GetStop() > rtInvalidate) { if(GetTail()->GetStart() < rtInvalidate) GetTail()->SetStop(rtInvalidate); else RemoveTail(); } m_fBreakBuffering = false; } } return(0); } // // CSubPicQueueNoThread // CSubPicQueueNoThread::CSubPicQueueNoThread(ISubPicAllocator* pAllocator, HRESULT* phr) : ISubPicQueueImpl(pAllocator, phr) { } CSubPicQueueNoThread::~CSubPicQueueNoThread() { } // ISubPicQueue STDMETHODIMP CSubPicQueueNoThread::Invalidate(REFERENCE_TIME rtInvalidate) { CAutoLock cQueueLock(&m_csLock); m_pSubPic = NULL; return S_OK; } STDMETHODIMP_(bool) CSubPicQueueNoThread::LookupSubPic(REFERENCE_TIME rtNow, ISubPic** ppSubPic) { if(!ppSubPic) return(false); *ppSubPic = NULL; CComPtr pSubPic; { CAutoLock cAutoLock(&m_csLock); if(!m_pSubPic) { if(FAILED(m_pAllocator->AllocDynamic(&m_pSubPic))) return(false); } pSubPic = m_pSubPic; } if(pSubPic->GetStart() <= rtNow && rtNow < pSubPic->GetStop()) { (*ppSubPic = pSubPic)->AddRef(); } else { CComPtr pSubPicProvider; if(SUCCEEDED(GetSubPicProvider(&pSubPicProvider)) && pSubPicProvider && SUCCEEDED(pSubPicProvider->Lock())) { double fps = m_fps; if(POSITION pos = pSubPicProvider->GetStartPosition(rtNow, fps)) { REFERENCE_TIME rtStart = pSubPicProvider->GetStart(pos, fps); REFERENCE_TIME rtStop = pSubPicProvider->GetStop(pos, fps); if(pSubPicProvider->IsAnimated(pos)) { rtStart = rtNow; rtStop = rtNow+1; } if(rtStart <= rtNow && rtNow < rtStop) { if(m_pAllocator->IsDynamicWriteOnly()) { CComPtr pStatic; if(SUCCEEDED(m_pAllocator->GetStatic(&pStatic)) && SUCCEEDED(RenderTo(pStatic, rtStart, rtStop, fps)) && SUCCEEDED(pStatic->CopyTo(pSubPic))) (*ppSubPic = pSubPic)->AddRef(); } else { if(SUCCEEDED(RenderTo(m_pSubPic, rtStart, rtStop, fps))) (*ppSubPic = pSubPic)->AddRef(); } } } pSubPicProvider->Unlock(); if(*ppSubPic) { CAutoLock cAutoLock(&m_csLock); m_pSubPic = *ppSubPic; } } } return(!!*ppSubPic); } STDMETHODIMP CSubPicQueueNoThread::GetStats(int& nSubPics, REFERENCE_TIME& rtNow, REFERENCE_TIME& rtStart, REFERENCE_TIME& rtStop) { CAutoLock cAutoLock(&m_csLock); nSubPics = 0; rtNow = m_rtNow; rtStart = rtStop = 0; if(m_pSubPic) { nSubPics = 1; rtStart = m_pSubPic->GetStart(); rtStop = m_pSubPic->GetStop(); } return S_OK; } STDMETHODIMP CSubPicQueueNoThread::GetStats(int nSubPic, REFERENCE_TIME& rtStart, REFERENCE_TIME& rtStop) { CAutoLock cAutoLock(&m_csLock); if(!m_pSubPic || nSubPic != 0) return E_INVALIDARG; rtStart = m_pSubPic->GetStart(); rtStop = m_pSubPic->GetStop(); return S_OK; } // // ISubPicAllocatorPresenterImpl // ISubPicAllocatorPresenterImpl::ISubPicAllocatorPresenterImpl(HWND hWnd, HRESULT& hr) : CUnknown(NAME("ISubPicAllocatorPresenterImpl"), NULL) , m_hWnd(hWnd) , m_NativeVideoSize(0, 0), m_AspectRatio(0, 0) , m_VideoRect(0, 0, 0, 0), m_WindowRect(0, 0, 0, 0) , m_fps(25.0) { if(!IsWindow(m_hWnd)) {hr = E_INVALIDARG; return;} GetWindowRect(m_hWnd, &m_WindowRect); SetVideoAngle(Vector(), false); hr = S_OK; } ISubPicAllocatorPresenterImpl::~ISubPicAllocatorPresenterImpl() { } STDMETHODIMP ISubPicAllocatorPresenterImpl::NonDelegatingQueryInterface(REFIID riid, void** ppv) { return QI(ISubPicAllocatorPresenter) __super::NonDelegatingQueryInterface(riid, ppv); } void ISubPicAllocatorPresenterImpl::AlphaBltSubPic(CSize size, SubPicDesc* pTarget) { CComPtr pSubPic; if(m_pSubPicQueue->LookupSubPic(m_rtNow, &pSubPic)) { SubPicDesc spd; pSubPic->GetDesc(spd); if(spd.w > 0 && spd.h > 0) { CRect r; pSubPic->GetDirtyRect(r); // FIXME r.DeflateRect(1, 1); CRect rDstText( r.left * size.cx / spd.w, r.top * size.cy / spd.h, r.right * size.cx / spd.w, r.bottom * size.cy / spd.h); pSubPic->AlphaBlt(r, rDstText, pTarget); } } } // ISubPicAllocatorPresenter STDMETHODIMP_(SIZE) ISubPicAllocatorPresenterImpl::GetVideoSize(bool fCorrectAR) { CSize VideoSize(m_NativeVideoSize); if(fCorrectAR && m_AspectRatio.cx > 0 && m_AspectRatio.cy > 0) VideoSize.cx = VideoSize.cy*m_AspectRatio.cx/m_AspectRatio.cy; return(VideoSize); } STDMETHODIMP_(void) ISubPicAllocatorPresenterImpl::SetPosition(RECT w, RECT v) { bool fWindowPosChanged = !!(m_WindowRect != w); bool fWindowSizeChanged = !!(m_WindowRect.Size() != CRect(w).Size()); m_WindowRect = w; bool fVideoRectChanged = !!(m_VideoRect != v); m_VideoRect = v; if(fWindowSizeChanged || fVideoRectChanged) { if(m_pAllocator) { m_pAllocator->SetCurSize(m_WindowRect.Size()); m_pAllocator->SetCurVidRect(m_VideoRect); } if(m_pSubPicQueue) { m_pSubPicQueue->Invalidate(); } } if(fWindowPosChanged || fVideoRectChanged) Paint(fWindowSizeChanged || fVideoRectChanged); } STDMETHODIMP_(void) ISubPicAllocatorPresenterImpl::SetTime(REFERENCE_TIME rtNow) { /* if(m_rtNow <= rtNow && rtNow <= m_rtNow + 1000000) return; */ m_rtNow = rtNow; if(m_pSubPicQueue) { m_pSubPicQueue->SetTime(rtNow); } } STDMETHODIMP_(double) ISubPicAllocatorPresenterImpl::GetFPS() { return(m_fps); } STDMETHODIMP_(void) ISubPicAllocatorPresenterImpl::SetSubPicProvider(ISubPicProvider* pSubPicProvider) { m_SubPicProvider = pSubPicProvider; if(m_pSubPicQueue) m_pSubPicQueue->SetSubPicProvider(pSubPicProvider); } STDMETHODIMP_(void) ISubPicAllocatorPresenterImpl::Invalidate(REFERENCE_TIME rtInvalidate) { if(m_pSubPicQueue) m_pSubPicQueue->Invalidate(rtInvalidate); } #include void ISubPicAllocatorPresenterImpl::Transform(CRect r, Vector v[4]) { v[0] = Vector(r.left, r.top, 0); v[1] = Vector(r.right, r.top, 0); v[2] = Vector(r.left, r.bottom, 0); v[3] = Vector(r.right, r.bottom, 0); Vector center(r.CenterPoint().x, r.CenterPoint().y, 0); int l = (int)(Vector(r.Size().cx, r.Size().cy, 0).Length()*1.5f)+1; for(int i = 0; i < 4; i++) { v[i] = m_xform << (v[i] - center); v[i].z = v[i].z / l + 0.5f; v[i].x /= v[i].z*2; v[i].y /= v[i].z*2; v[i] += center; } } STDMETHODIMP ISubPicAllocatorPresenterImpl::SetVideoAngle(Vector v, bool fRepaint) { m_xform = XForm(Ray(Vector(0, 0, 0), v), Vector(1, 1, 1), false); if(fRepaint) Paint(true); return S_OK; }