| // Copyright (c) 2010 The WebM project authors. All Rights Reserved. | |
| // | |
| // Use of this source code is governed by a BSD-style license | |
| // that can be found in the LICENSE file in the root of the source | |
| // tree. An additional intellectual property rights grant can be found | |
| // in the file PATENTS. All contributing project authors may | |
| // be found in the AUTHORS file in the root of the source tree. | |
| #include <strmif.h> | |
| //#include <comdef.h> | |
| #include "webmvorbisencoderfilter.hpp" | |
| #include "webmvorbisencoderoutpin.hpp" | |
| #include "cmediasample.hpp" | |
| #include "mediatypeutil.hpp" | |
| #include "webmtypes.hpp" | |
| #include "vorbistypes.hpp" | |
| #include <vfwmsgs.h> | |
| #include <mmreg.h> | |
| #include <uuids.h> | |
| #include <cassert> | |
| #include <process.h> | |
| #ifdef _DEBUG | |
| #include "odbgstream.hpp" | |
| #include <iomanip> | |
| #include "iidstr.hpp" | |
| using std::endl; | |
| using std::dec; | |
| using std::hex; | |
| using std::fixed; | |
| using std::setprecision; | |
| #endif | |
| using std::wstring; | |
| namespace WebmVorbisEncoderLib | |
| { | |
| Outpin::Outpin(Filter* pFilter) : | |
| Pin(PINDIR_OUTPUT, L"output"), | |
| m_pFilter(pFilter), | |
| m_hThread(0) | |
| { | |
| SetDefaultMediaTypes(); | |
| //m_hQuit = CreateEvent(0, 0, 0, 0); | |
| //assert(m_hQuit); | |
| } | |
| Outpin::~Outpin() | |
| { | |
| assert(m_hThread == 0); | |
| assert(!bool(m_pAllocator)); | |
| assert(!bool(m_pInputPin)); | |
| //const BOOL b = CloseHandle(m_hQuit); | |
| //assert(b); | |
| } | |
| HRESULT Outpin::Start() //transition from stopped | |
| { | |
| if (m_pPinConnection == 0) | |
| return S_FALSE; //nothing we need to do | |
| assert(bool(m_pAllocator)); | |
| assert(bool(m_pInputPin)); | |
| const HRESULT hr = m_pAllocator->Commit(); | |
| hr; | |
| assert(SUCCEEDED(hr)); //TODO | |
| StartThread(); | |
| return S_OK; | |
| } | |
| void Outpin::Stop() //transition to stopped | |
| { | |
| if (m_pPinConnection == 0) | |
| return; //nothing was done | |
| assert(bool(m_pAllocator)); | |
| assert(bool(m_pInputPin)); | |
| const HRESULT hr = m_pAllocator->Decommit(); | |
| hr; | |
| assert(SUCCEEDED(hr)); | |
| StopThread(); | |
| //m_bDone = true; | |
| } | |
| HRESULT Outpin::QueryInterface(const IID& iid, void** ppv) | |
| { | |
| if (ppv == 0) | |
| return E_POINTER; | |
| IUnknown*& pUnk = reinterpret_cast<IUnknown*&>(*ppv); | |
| if (iid == __uuidof(IUnknown)) | |
| pUnk = static_cast<IPin*>(this); | |
| else if (iid == __uuidof(IPin)) | |
| pUnk = static_cast<IPin*>(this); | |
| else if (iid == __uuidof(IMediaSeeking)) | |
| pUnk = static_cast<IMediaSeeking*>(this); | |
| else | |
| { | |
| #if 0 | |
| wodbgstream os; | |
| os << "vp8dec::outpin::QI: iid=" << IIDStr(iid) << std::endl; | |
| #endif | |
| pUnk = 0; | |
| return E_NOINTERFACE; | |
| } | |
| pUnk->AddRef(); | |
| return S_OK; | |
| } | |
| ULONG Outpin::AddRef() | |
| { | |
| return m_pFilter->AddRef(); | |
| } | |
| ULONG Outpin::Release() | |
| { | |
| return m_pFilter->Release(); | |
| } | |
| HRESULT Outpin::Connect( | |
| IPin* pin, | |
| const AM_MEDIA_TYPE* pmt) | |
| { | |
| if (pin == 0) | |
| return E_POINTER; | |
| GraphUtil::IMemInputPinPtr pInputPin; | |
| HRESULT hr = pin->QueryInterface(&pInputPin); | |
| if (hr != S_OK) | |
| return hr; | |
| Filter::Lock lock; | |
| hr = lock.Seize(m_pFilter); | |
| if (FAILED(hr)) | |
| return hr; | |
| if (m_pFilter->m_state != State_Stopped) | |
| return VFW_E_NOT_STOPPED; | |
| if (bool(m_pPinConnection)) | |
| return VFW_E_ALREADY_CONNECTED; | |
| if (!bool(m_pFilter->m_inpin.m_pPinConnection)) | |
| return VFW_E_NO_TYPES; //VFW_E_NOT_CONNECTED? | |
| m_connection_mtv.Clear(); | |
| if (pmt) | |
| { | |
| hr = QueryAccept(pmt); | |
| //TODO: accept partial media type? | |
| if (hr != S_OK) | |
| return VFW_E_TYPE_NOT_ACCEPTED; | |
| hr = pin->ReceiveConnection(this, pmt); | |
| if (FAILED(hr)) | |
| return hr; | |
| const AM_MEDIA_TYPE& mt = *pmt; | |
| m_connection_mtv.Add(mt); | |
| } | |
| else | |
| { | |
| ULONG i = 0; | |
| const ULONG j = m_preferred_mtv.Size(); | |
| while (i < j) | |
| { | |
| const AM_MEDIA_TYPE& mt = m_preferred_mtv[i]; | |
| hr = pin->ReceiveConnection(this, &mt); | |
| if (SUCCEEDED(hr)) | |
| break; | |
| ++i; | |
| } | |
| if (i >= j) | |
| return VFW_E_NO_ACCEPTABLE_TYPES; | |
| const AM_MEDIA_TYPE& mt = m_preferred_mtv[i]; | |
| m_connection_mtv.Add(mt); | |
| } | |
| GraphUtil::IMemAllocatorPtr pAllocator; | |
| hr = pInputPin->GetAllocator(&pAllocator); | |
| if (FAILED(hr)) | |
| { | |
| hr = CMediaSample::CreateAllocator(&pAllocator); | |
| if (FAILED(hr)) | |
| return VFW_E_NO_ALLOCATOR; | |
| } | |
| assert(bool(pAllocator)); | |
| ALLOCATOR_PROPERTIES props, actual; | |
| props.cBuffers = -1; //number of buffers | |
| props.cbBuffer = -1; //size of each buffer, excluding prefix | |
| props.cbAlign = -1; //applies to prefix, too | |
| props.cbPrefix = -1; //imediasample::getbuffer does NOT include prefix | |
| hr = pInputPin->GetAllocatorRequirements(&props); | |
| const long cBuffers = 3; | |
| if (props.cBuffers < cBuffers) | |
| props.cBuffers = cBuffers; | |
| typedef VorbisTypes::VORBISFORMAT2 FMT; | |
| const AM_MEDIA_TYPE& mt = m_connection_mtv[0]; | |
| assert(mt.formattype == VorbisTypes::FORMAT_Vorbis2); | |
| assert(mt.cbFormat >= sizeof(FMT)); | |
| assert(mt.pbFormat); | |
| const FMT& fmt = (FMT&)(*mt.pbFormat); | |
| const long bytes_per_sample = 2; //assume 16 bits | |
| const long block_align = fmt.channels * bytes_per_sample; | |
| const long avg_bytes_per_sec = block_align * fmt.samplesPerSec; | |
| const long cbBuffer = avg_bytes_per_sec / 2; //TODO: estimate better | |
| if (props.cbBuffer < cbBuffer) | |
| props.cbBuffer = cbBuffer; | |
| if (props.cbAlign <= 0) | |
| props.cbAlign = 1; | |
| if (props.cbPrefix < 0) | |
| props.cbPrefix = 0; | |
| hr = pAllocator->SetProperties(&props, &actual); | |
| if (FAILED(hr)) | |
| return hr; | |
| hr = pInputPin->NotifyAllocator(pAllocator, 0); //allow writes | |
| if (FAILED(hr) && (hr != E_NOTIMPL)) | |
| return hr; | |
| m_pPinConnection = pin; | |
| m_pAllocator = pAllocator; | |
| m_pInputPin = pInputPin; | |
| return S_OK; | |
| } | |
| HRESULT Outpin::OnDisconnect() | |
| { | |
| m_pInputPin = 0; | |
| m_pAllocator = 0; | |
| return S_OK; | |
| } | |
| HRESULT Outpin::ReceiveConnection( | |
| IPin*, | |
| const AM_MEDIA_TYPE*) | |
| { | |
| return E_UNEXPECTED; //for input pins only | |
| } | |
| HRESULT Outpin::QueryAccept(const AM_MEDIA_TYPE* pmt) | |
| { | |
| if (pmt == 0) | |
| return E_INVALIDARG; | |
| Filter::Lock lock; | |
| const HRESULT hr = lock.Seize(m_pFilter); | |
| if (FAILED(hr)) | |
| return hr; | |
| const Inpin& inpin = m_pFilter->m_inpin; | |
| if (!bool(inpin.m_pPinConnection)) | |
| return VFW_E_NO_TYPES; | |
| const AM_MEDIA_TYPE& mtOut = *pmt; | |
| if (mtOut.majortype != MEDIATYPE_Audio) | |
| return S_FALSE; | |
| if (mtOut.subtype != VorbisTypes::MEDIASUBTYPE_Vorbis2) | |
| return S_FALSE; | |
| if (mtOut.formattype != VorbisTypes::FORMAT_Vorbis2) | |
| return S_FALSE; | |
| if (mtOut.pbFormat == 0) | |
| return S_FALSE; | |
| typedef VorbisTypes::VORBISFORMAT2 FMT; | |
| if (mtOut.cbFormat < sizeof(FMT)) | |
| return S_FALSE; | |
| const FMT& fmtOut = (FMT&)(*mtOut.pbFormat); | |
| const AM_MEDIA_TYPE& mtIn = inpin.m_connection_mtv[0]; | |
| const WAVEFORMATEX& wfxIn = (WAVEFORMATEX&)(*mtIn.pbFormat); | |
| if (fmtOut.channels != wfxIn.nChannels) | |
| return S_FALSE; | |
| if (fmtOut.samplesPerSec != wfxIn.nSamplesPerSec) | |
| return S_FALSE; | |
| //TODO: check bits/sample | |
| //TODO: what about vorbis headers (ident, setup, comment)? | |
| return S_OK; | |
| } | |
| HRESULT Outpin::QueryInternalConnections(IPin** pa, ULONG* pn) | |
| { | |
| if (pn == 0) | |
| return E_POINTER; | |
| Filter::Lock lock; | |
| HRESULT hr = lock.Seize(m_pFilter); | |
| if (FAILED(hr)) | |
| return hr; | |
| ULONG& n = *pn; | |
| if (n == 0) | |
| { | |
| if (pa == 0) //query for required number | |
| { | |
| n = 1; | |
| return S_OK; | |
| } | |
| return S_FALSE; //means "insufficient number of array elements" | |
| } | |
| if (n < 1) | |
| { | |
| n = 0; | |
| return S_FALSE; //means "insufficient number of array elements" | |
| } | |
| if (pa == 0) | |
| { | |
| n = 0; | |
| return E_POINTER; | |
| } | |
| IPin*& pPin = pa[0]; | |
| pPin = &m_pFilter->m_inpin; | |
| pPin->AddRef(); | |
| n = 1; | |
| return S_OK; | |
| } | |
| HRESULT Outpin::EndOfStream() | |
| { | |
| return E_UNEXPECTED; //for inpins only | |
| } | |
| HRESULT Outpin::NewSegment( | |
| REFERENCE_TIME, | |
| REFERENCE_TIME, | |
| double) | |
| { | |
| return E_UNEXPECTED; | |
| } | |
| HRESULT Outpin::BeginFlush() | |
| { | |
| return E_UNEXPECTED; | |
| } | |
| HRESULT Outpin::EndFlush() | |
| { | |
| return E_UNEXPECTED; | |
| } | |
| HRESULT Outpin::GetCapabilities(DWORD* pdw) | |
| { | |
| const Inpin& inpin = m_pFilter->m_inpin; | |
| const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection); | |
| if (bool(pSeek)) | |
| return pSeek->GetCapabilities(pdw); | |
| if (pdw == 0) | |
| return E_POINTER; | |
| DWORD& dw = *pdw; | |
| dw = 0; | |
| return S_OK; //? | |
| } | |
| HRESULT Outpin::CheckCapabilities(DWORD* pdw) | |
| { | |
| const Inpin& inpin = m_pFilter->m_inpin; | |
| const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection); | |
| if (bool(pSeek)) | |
| return pSeek->CheckCapabilities(pdw); | |
| if (pdw == 0) | |
| return E_POINTER; | |
| DWORD& dw = *pdw; | |
| const DWORD dwRequested = dw; | |
| if (dwRequested == 0) | |
| return E_INVALIDARG; | |
| return E_FAIL; | |
| } | |
| HRESULT Outpin::IsFormatSupported(const GUID* p) | |
| { | |
| const Inpin& inpin = m_pFilter->m_inpin; | |
| const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection); | |
| if (bool(pSeek)) | |
| return pSeek->IsFormatSupported(p); | |
| if (p == 0) | |
| return E_POINTER; | |
| const GUID& g = *p; | |
| if (g == TIME_FORMAT_MEDIA_TIME) | |
| return S_OK; | |
| return S_FALSE; | |
| } | |
| HRESULT Outpin::QueryPreferredFormat(GUID* p) | |
| { | |
| const Inpin& inpin = m_pFilter->m_inpin; | |
| const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection); | |
| if (bool(pSeek)) | |
| return pSeek->QueryPreferredFormat(p); | |
| if (p == 0) | |
| return E_POINTER; | |
| *p = TIME_FORMAT_MEDIA_TIME; | |
| return S_OK; | |
| } | |
| HRESULT Outpin::GetTimeFormat(GUID* p) | |
| { | |
| const Inpin& inpin = m_pFilter->m_inpin; | |
| const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection); | |
| if (bool(pSeek)) | |
| return pSeek->GetTimeFormat(p); | |
| if (p == 0) | |
| return E_POINTER; | |
| *p = TIME_FORMAT_MEDIA_TIME; | |
| return S_OK; | |
| } | |
| HRESULT Outpin::IsUsingTimeFormat(const GUID* p) | |
| { | |
| const Inpin& inpin = m_pFilter->m_inpin; | |
| const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection); | |
| if (bool(pSeek)) | |
| return pSeek->IsUsingTimeFormat(p); | |
| if (p == 0) | |
| return E_INVALIDARG; | |
| return (*p == TIME_FORMAT_MEDIA_TIME) ? S_OK : S_FALSE; | |
| } | |
| HRESULT Outpin::SetTimeFormat(const GUID* p) | |
| { | |
| const Inpin& inpin = m_pFilter->m_inpin; | |
| const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection); | |
| if (bool(pSeek)) | |
| return pSeek->SetTimeFormat(p); | |
| if (p == 0) | |
| return E_INVALIDARG; | |
| if (*p == TIME_FORMAT_MEDIA_TIME) | |
| return S_OK; | |
| return E_INVALIDARG; | |
| } | |
| HRESULT Outpin::GetDuration(LONGLONG* p) | |
| { | |
| const Inpin& inpin = m_pFilter->m_inpin; | |
| const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection); | |
| if (bool(pSeek)) | |
| return pSeek->GetDuration(p); | |
| if (p == 0) | |
| return E_POINTER; | |
| return E_FAIL; | |
| } | |
| HRESULT Outpin::GetStopPosition(LONGLONG* p) | |
| { | |
| const Inpin& inpin = m_pFilter->m_inpin; | |
| const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection); | |
| if (bool(pSeek)) | |
| return pSeek->GetStopPosition(p); | |
| if (p == 0) | |
| return E_POINTER; | |
| return E_FAIL; | |
| } | |
| HRESULT Outpin::GetCurrentPosition(LONGLONG* p) | |
| { | |
| const Inpin& inpin = m_pFilter->m_inpin; | |
| const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection); | |
| if (bool(pSeek)) | |
| return pSeek->GetCurrentPosition(p); | |
| if (p == 0) | |
| return E_POINTER; | |
| return E_FAIL; | |
| } | |
| HRESULT Outpin::ConvertTimeFormat( | |
| LONGLONG* ptgt, | |
| const GUID* ptgtfmt, | |
| LONGLONG src, | |
| const GUID* psrcfmt) | |
| { | |
| const Inpin& inpin = m_pFilter->m_inpin; | |
| const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection); | |
| if (bool(pSeek)) | |
| return pSeek->ConvertTimeFormat(ptgt, ptgtfmt, src, psrcfmt); | |
| if (ptgt == 0) | |
| return E_POINTER; | |
| LONGLONG& tgt = *ptgt; | |
| const GUID& tgtfmt = ptgtfmt ? *ptgtfmt : TIME_FORMAT_MEDIA_TIME; | |
| const GUID& srcfmt = psrcfmt ? *psrcfmt : TIME_FORMAT_MEDIA_TIME; | |
| if (tgtfmt != TIME_FORMAT_MEDIA_TIME) | |
| return E_INVALIDARG; | |
| if (srcfmt != TIME_FORMAT_MEDIA_TIME) | |
| return E_INVALIDARG; | |
| tgt = src; | |
| return S_OK; | |
| } | |
| HRESULT Outpin::SetPositions( | |
| LONGLONG* pCurr, | |
| DWORD dwCurr, | |
| LONGLONG* pStop, | |
| DWORD dwStop) | |
| { | |
| const Inpin& inpin = m_pFilter->m_inpin; | |
| const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection); | |
| if (bool(pSeek)) | |
| return pSeek->SetPositions(pCurr, dwCurr, pStop, dwStop); | |
| return E_FAIL; | |
| } | |
| HRESULT Outpin::GetPositions( | |
| LONGLONG* pCurrPos, | |
| LONGLONG* pStopPos) | |
| { | |
| const Inpin& inpin = m_pFilter->m_inpin; | |
| const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection); | |
| if (bool(pSeek)) | |
| return pSeek->GetPositions(pCurrPos, pStopPos); | |
| return E_FAIL; | |
| } | |
| HRESULT Outpin::GetAvailable( | |
| LONGLONG* pEarliest, | |
| LONGLONG* pLatest) | |
| { | |
| const Inpin& inpin = m_pFilter->m_inpin; | |
| const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection); | |
| if (bool(pSeek)) | |
| return pSeek->GetAvailable(pEarliest, pLatest); | |
| return E_FAIL; | |
| } | |
| HRESULT Outpin::SetRate(double r) | |
| { | |
| const Inpin& inpin = m_pFilter->m_inpin; | |
| const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection); | |
| if (bool(pSeek)) | |
| return pSeek->SetRate(r); | |
| return E_FAIL; | |
| } | |
| HRESULT Outpin::GetRate(double* p) | |
| { | |
| const Inpin& inpin = m_pFilter->m_inpin; | |
| const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection); | |
| if (bool(pSeek)) | |
| return pSeek->GetRate(p); | |
| return E_FAIL; | |
| } | |
| HRESULT Outpin::GetPreroll(LONGLONG* p) | |
| { | |
| const Inpin& inpin = m_pFilter->m_inpin; | |
| const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection); | |
| if (bool(pSeek)) | |
| return pSeek->GetPreroll(p); | |
| return E_FAIL; | |
| } | |
| HRESULT Outpin::GetName(PIN_INFO& info) const | |
| { | |
| const wchar_t* const name_ = L"Vorbis"; | |
| #if _MSC_VER >= 1400 | |
| enum { namelen = sizeof(info.achName) / sizeof(WCHAR) }; | |
| const errno_t e = wcscpy_s(info.achName, namelen, name_); | |
| e; | |
| assert(e == 0); | |
| #else | |
| wcscpy(info.achName, name_); | |
| #endif | |
| return S_OK; | |
| } | |
| HRESULT Outpin::Seize(CLockable::Lock& lock) | |
| { | |
| return lock.Seize(m_pFilter); | |
| } | |
| FILTER_STATE Outpin::GetState() | |
| { | |
| return m_pFilter->m_state; | |
| } | |
| IBaseFilter* Outpin::GetFilter() //no addref here | |
| { | |
| return m_pFilter; //no addref here! | |
| } | |
| void Outpin::OnInpinConnect(const AM_MEDIA_TYPE& mtIn) | |
| { | |
| m_preferred_mtv.Clear(); | |
| m_preferred_mtv.Add(mtIn); | |
| } | |
| void Outpin::OnInpinDisconnect() | |
| { | |
| if (bool(m_pPinConnection)) | |
| { | |
| IFilterGraph* const pGraph = m_pFilter->m_info.pGraph; | |
| assert(pGraph); | |
| HRESULT hr = pGraph->Disconnect(m_pPinConnection); | |
| assert(SUCCEEDED(hr)); | |
| hr = pGraph->Disconnect(this); | |
| assert(SUCCEEDED(hr)); | |
| assert(!bool(m_pPinConnection)); | |
| } | |
| SetDefaultMediaTypes(); | |
| } | |
| void Outpin::SetDefaultMediaTypes() | |
| { | |
| m_preferred_mtv.Clear(); | |
| //Create a partial media type; this is the best we can do | |
| //without having an inpin connection (hence no media type). | |
| AM_MEDIA_TYPE mt; | |
| mt.majortype = MEDIATYPE_Audio; | |
| mt.subtype = VorbisTypes::MEDIASUBTYPE_Vorbis2; | |
| mt.bFixedSizeSamples = FALSE; | |
| mt.bTemporalCompression = FALSE; | |
| mt.lSampleSize = 0; | |
| mt.formattype = GUID_NULL; | |
| mt.pUnk = 0; | |
| mt.cbFormat = 0; | |
| mt.pbFormat = 0; | |
| m_preferred_mtv.Add(mt); | |
| } | |
| #if 0 //TODO | |
| const WAVEFORMATEX* Outpin::GetFormat() const | |
| { | |
| if (!bool(m_pPinConnection)) | |
| return 0; | |
| if (m_connection_mtv.Empty()) | |
| return 0; | |
| const AM_MEDIA_TYPE& mt = m_connection_mtv[0]; | |
| assert(mt.formattype == FORMAT_WaveFormatEx); | |
| assert(mt.cbFormat >= sizeof(WAVEFORMATEX)); | |
| assert(mt.pbFormat); | |
| const WAVEFORMATEX& wfx = (WAVEFORMATEX&)(*mt.pbFormat); | |
| return &wfx; | |
| } | |
| #endif | |
| void Outpin::StartThread() | |
| { | |
| assert(m_hThread == 0); | |
| const uintptr_t h = _beginthreadex( | |
| 0, //security | |
| 0, //stack size | |
| &Outpin::ThreadProc, | |
| this, | |
| 0, //run immediately | |
| 0); //thread id | |
| m_hThread = reinterpret_cast<HANDLE>(h); | |
| assert(m_hThread); | |
| #ifdef _DEBUG | |
| wodbgstream os; | |
| os << "webmvorbisenc::Outpin[" << m_id << "]::StartThread: hThread=0x" | |
| << hex << h << dec | |
| << endl; | |
| #endif | |
| } | |
| void Outpin::StopThread() | |
| { | |
| if (m_hThread == 0) | |
| return; | |
| #ifdef _DEBUG | |
| wodbgstream os; | |
| os << "webmvorbisenc::Outpin[" << m_id << "]::StopThread: hThread=0x" | |
| << hex << uintptr_t(m_hThread) << dec | |
| << endl; | |
| #endif | |
| //BOOL b = SetEvent(m_hQuit); | |
| //assert(b); | |
| const DWORD dw = WaitForSingleObject(m_hThread, 5000); | |
| assert(dw == WAIT_OBJECT_0); | |
| const BOOL b = CloseHandle(m_hThread); | |
| assert(b); | |
| m_hThread = 0; | |
| } | |
| //bool Outpin::Done() const | |
| //{ | |
| // if (m_hThread == 0) | |
| // return true; | |
| // | |
| // const DWORD dw = WaitForSingleObject(m_hThread, 0); | |
| // | |
| // if (dw == WAIT_TIMEOUT) | |
| // return false; | |
| // | |
| // assert(dw == WAIT_OBJECT_0); | |
| // | |
| // return true; | |
| //} | |
| unsigned Outpin::ThreadProc(void* pv) | |
| { | |
| Outpin* const pPin = static_cast<Outpin*>(pv); | |
| assert(pPin); | |
| #ifdef _DEBUG | |
| wodbgstream os; | |
| os << "webmvorbisenc::Outpin[" | |
| << pPin->m_id | |
| << "]::ThreadProc(begin): hThread=0x" | |
| << hex << uintptr_t(pPin->m_hThread) << dec | |
| << endl; | |
| #endif | |
| pPin->Main(); | |
| #ifdef _DEBUG | |
| os << "webmvorbisenc::Outpin[" | |
| << pPin->m_id << "]::ThreadProc(end): hThread=0x" | |
| << hex << uintptr_t(pPin->m_hThread) << dec | |
| << endl; | |
| #endif | |
| return 0; | |
| } | |
| unsigned Outpin::Main() | |
| { | |
| assert(bool(m_pPinConnection)); | |
| assert(bool(m_pInputPin)); | |
| Inpin& inpin = m_pFilter->m_inpin; | |
| const HANDLE hSamples = inpin.m_hSamples; | |
| for (;;) | |
| { | |
| GraphUtil::IMediaSamplePtr pSample; | |
| Filter::Lock lock; | |
| HRESULT hrLock = lock.Seize(m_pFilter); | |
| assert(SUCCEEDED(hrLock)); | |
| if (FAILED(hrLock)) | |
| return 0; //TODO: signal error | |
| const int status = inpin.GetSample(&pSample); | |
| hrLock = lock.Release(); | |
| assert(SUCCEEDED(hrLock)); | |
| if (FAILED(hrLock)) | |
| return 0; | |
| if (status < -1) //terminate thread | |
| return 0; | |
| if (status == 0) //EOS (no payload) | |
| { | |
| #ifdef _DEBUG | |
| odbgstream os; | |
| os << "webmvorbisenc::outpin::EOS: calling pin->EOS" << endl; | |
| #endif | |
| const HRESULT hrEOS = m_pPinConnection->EndOfStream(); | |
| hrEOS; | |
| #ifdef _DEBUG | |
| os << "webmvorbisenc::outpin::EOS: called pin->EOS; hr=0x" | |
| << hex << hrEOS << dec | |
| << endl; | |
| #endif | |
| } | |
| else if (status > 0) //have payload to send downstream | |
| { | |
| #if 0 | |
| LONGLONG st, sp; | |
| const HRESULT hr = pSample->GetTime(&st, &sp); | |
| assert(SUCCEEDED(hr)); | |
| odbgstream os; | |
| os << "A: " | |
| << fixed | |
| << setprecision(3) | |
| << (double(st)/10000000.0) | |
| << endl; | |
| #endif | |
| const HRESULT hrReceive = m_pInputPin->Receive(pSample); | |
| if (hrReceive == S_OK) | |
| continue; | |
| inpin.OnCompletion(); | |
| } | |
| const DWORD dw = WaitForSingleObject(hSamples, INFINITE); | |
| if (dw == WAIT_FAILED) | |
| return 0; //signal error to FGM | |
| assert(dw == WAIT_OBJECT_0); | |
| } | |
| } | |
| } //end namespace WebmVorbisEncoderLib |