HRESULT Filter::Run(REFERENCE_TIME start) { Lock lock; HRESULT hr = lock.Seize(this); if (FAILED(hr)) return hr; //odbgstream os; //os << "mkvsplit::Filter::Run" << endl; switch (m_state) { case State_Stopped: OnStart(); break; case State_Paused: case State_Running: default: break; } m_start = start; m_state = State_Running; return S_OK; }
HRESULT Filter::Stop() { // Stop is a synchronous operation: when it completes, // the filter is stopped. Lock lock; HRESULT hr = lock.Seize(this); if (FAILED(hr)) return hr; // odbgstream os; // os << "mkvsplit::Filter::Stop" << endl; switch (m_state) { case kStatePaused: case kStatePausedWaitingForKeyframe: case kStateRunning: case kStateRunningWaitingForKeyframe: m_state = kStateStopped; OnStop(); // decommit outpin's allocator break; case kStateStopped: break; default: assert(false); break; } return S_OK; }
HRESULT Filter::Stop() { //Stop is a synchronous operation: when it completes, //the filter is stopped. //odbgstream os; Lock lock; HRESULT hr = lock.Seize(this); if (FAILED(hr)) return hr; //odbgstream os; //os << "mkvsplit::Filter::Stop" << endl; switch (m_state) { case State_Paused: case State_Running: m_state = State_Stopped; OnStop(); //decommit outpin's allocator break; case State_Stopped: default: break; } return S_OK; }
HRESULT Filter::Pause() { //Unlike Stop(), Pause() can be asynchronous (that's why you have //GetState()). Lock lock; HRESULT hr = lock.Seize(this); if (FAILED(hr)) return hr; //odbgstream os; //os << "mkvsplit::Filter::Pause" << endl; switch (m_state) { case State_Stopped: OnStart(); //commit outpin's allocator break; case State_Running: case State_Paused: default: break; } m_state = State_Paused; return S_OK; }
HRESULT Filter::EnumPins(IEnumPins** pp) { Lock lock; HRESULT hr = lock.Seize(this); if (FAILED(hr)) return hr; const ULONG outpins_count = static_cast<ULONG>(m_outpins.size()); const ULONG n = 1 + outpins_count; //odbgstream os; //os << "WebmSplit::filter::enumpins: n=" << n << endl; const size_t cb = n * sizeof(IPin*); IPin** const pins = (IPin**)_alloca(cb); IPin** pin = pins; *pin++ = &m_inpin; typedef outpins_t::iterator iter_t; iter_t i = m_outpins.begin(); const iter_t j = m_outpins.end(); while (i != j) *pin++ = *i++; return CEnumPins::CreateInstance(pins, n, pp); }
HRESULT Filter::Run(REFERENCE_TIME start) { Lock lock; HRESULT hr = lock.Seize(this); if (FAILED(hr)) return hr; #ifdef _DEBUG odbgstream os; os << "webmvorbisencoder::Filter::Run" << endl; #endif switch (m_state) { case State_Stopped: OnStart(); break; case State_Paused: case State_Running: default: break; } m_start = start; m_state = State_Running; return S_OK; }
HRESULT Filter::Pause() { //Unlike Stop(), Pause() can be asynchronous (that's why you have //GetState()). We could use that here to build the samples index. Lock lock; HRESULT hr = lock.Seize(this); if (FAILED(hr)) return hr; //odbgstream os; //os << "WebmSplit::Filter::Pause" << endl; switch (m_state) { case State_Stopped: OnStart(); break; case State_Running: case State_Paused: default: break; } m_state = State_Paused; return S_OK; }
HRESULT Filter::JoinFilterGraph( IFilterGraph *pGraph, LPCWSTR name) { Lock lock; HRESULT hr = lock.Seize(this); if (FAILED(hr)) return hr; //NOTE: //No, do not adjust reference counts here! //Read the docs for the reasons why. //ENDNOTE. m_info.pGraph = pGraph; if (name == 0) m_info.achName[0] = L'\0'; else { enum { size = sizeof(m_info.achName)/sizeof(WCHAR) }; const errno_t e = wcscpy_s(m_info.achName, size, name); e; assert(e == 0); //TODO } return S_OK; }
HRESULT Filter::ApplyPostProcessing() { Lock lock; HRESULT hr = lock.Seize(this); if (FAILED(hr)) return hr; if (m_state != State_Paused) return VFW_E_NOT_PAUSED; return m_inpin.OnApplyPostProcessing(); }
HRESULT Filter::GetState(DWORD, FILTER_STATE* p) { if (p == 0) return E_POINTER; Lock lock; const HRESULT hr = lock.Seize(this); if (FAILED(hr)) return hr; *p = GetStateLocked(); return S_OK; }
HRESULT Filter::SetFlags(int Flags) { Lock lock; HRESULT hr = lock.Seize(this); if (FAILED(hr)) return hr; if (Flags & ~0x07) return E_INVALIDARG; m_cfg.flags = Flags; return S_OK; }
HRESULT Filter::EnumPins(IEnumPins** pp) { Lock lock; HRESULT hr = lock.Seize(this); if (FAILED(hr)) return hr; IPin* pins[2]; pins[0] = &m_inpin; pins[1] = &m_outpin; return CEnumPins::CreateInstance(pins, 2, pp); }
HRESULT Filter::GetFlags(int* pFlags) { if (pFlags == 0) return E_POINTER; Lock lock; HRESULT hr = lock.Seize(this); if (FAILED(hr)) return hr; *pFlags = m_cfg.flags; return S_OK; }
HRESULT Filter::GetNoiseLevel(int* pLevel) { if (pLevel == 0) return E_POINTER; Lock lock; HRESULT hr = lock.Seize(this); if (FAILED(hr)) return hr; *pLevel = m_cfg.noise; return S_OK; }
HRESULT Filter::Stop() { //Stop is a synchronous operation: when it completes, //the filter is stopped. //odbgstream os; Lock lock; HRESULT hr = lock.Seize(this); if (FAILED(hr)) return hr; #ifdef _DEBUG odbgstream os; os << "webmvorbisencoder::Filter::Stop(begin)" << endl; #endif switch (m_state) { case State_Paused: case State_Running: m_state = State_Stopped; //Stop inpin first, to signal thread to terminate. m_inpin.Stop(); hr = lock.Release(); assert(SUCCEEDED(hr)); //Now stop outpin, to terminate its thread too. m_outpin.Stop(); break; case State_Stopped: default: break; } #ifdef _DEBUG os << "webmvorbisencoder::Filter::Stop(end)" << endl; #endif return S_OK; }
HRESULT Filter::SetNoiseLevel(int level) { Lock lock; HRESULT hr = lock.Seize(this); if (FAILED(hr)) return hr; if (level < 0) return E_INVALIDARG; if (level > 16) return E_INVALIDARG; m_cfg.noise = level; return S_OK; }
HRESULT Filter::SetSyncSource(IReferenceClock* clock) { Lock lock; HRESULT hr = lock.Seize(this); if (FAILED(hr)) return hr; if (m_clock) m_clock->Release(); m_clock = clock; if (m_clock) m_clock->AddRef(); return S_OK; }
HRESULT Filter::GetSyncSource(IReferenceClock** pclock) { if (pclock == 0) return E_POINTER; Lock lock; HRESULT hr = lock.Seize(this); if (FAILED(hr)) return hr; IReferenceClock*& clock = *pclock; clock = m_clock; if (clock) clock->AddRef(); return S_OK; }
HRESULT Filter::Pause() { //Unlike Stop(), Pause() can be asynchronous (that's why you have //GetState()). Lock lock; HRESULT hr = lock.Seize(this); if (FAILED(hr)) return hr; //odbgstream os; //os << "mkvsplit::Filter::Pause" << endl; switch (m_state) { case kStateStopped: OnStart(); //commit outpin's allocator m_state = kStatePausedWaitingForKeyframe; break; case kStateRunning: m_state = kStatePaused; break; case kStateRunningWaitingForKeyframe: m_state = kStatePausedWaitingForKeyframe; break; case kStatePausedWaitingForKeyframe: case kStatePaused: break; default: assert(false); break; } return S_OK; }
HRESULT Filter::Run(REFERENCE_TIME start) { Lock lock; HRESULT hr = lock.Seize(this); if (FAILED(hr)) return hr; //odbgstream os; //os << "mkvsplit::Filter::Run" << endl; switch (m_state) { case kStateStopped: OnStart(); m_state = kStateRunningWaitingForKeyframe; break; case kStatePausedWaitingForKeyframe: m_state = kStateRunningWaitingForKeyframe; break; case kStatePaused: m_state = kStateRunning; break; case kStateRunningWaitingForKeyframe: case kStateRunning: break; default: assert(false); break; } m_start = start; return S_OK; }
HRESULT Filter::QueryFilterInfo(FILTER_INFO* p) { if (p == 0) return E_POINTER; Lock lock; HRESULT hr = lock.Seize(this); if (FAILED(hr)) return hr; enum { size = sizeof(p->achName)/sizeof(WCHAR) }; const errno_t e = wcscpy_s(p->achName, size, m_info.achName); e; assert(e == 0); p->pGraph = m_info.pGraph; if (p->pGraph) p->pGraph->AddRef(); return S_OK; }
unsigned Filter::Main() { assert(m_pSegment); for (;;) { Sleep(0); #if 0 LONGLONG cluster_pos, new_pos; const long status = m_pSegment->ParseCluster(cluster_pos, new_pos); if (status < 0) //TODO: how to handle outpin streaming threads? return 1; Lock lock; const HRESULT hr = lock.Seize(this); assert(SUCCEEDED(hr)); //TODO if (FAILED(hr)) return 1; const bool bDone = m_pSegment->AddCluster(cluster_pos, new_pos); //odbgstream os; //os << "webmsplit::filter::main: ParseCluster; cluster_pos=" // << cluster_pos // << " new_pos=" // << new_pos // << " count=" // << m_pSegment->GetCount() // << " unparsed=" // << m_pSegment->Unparsed() // << endl; #else Lock lock; HRESULT hr = lock.Seize(this); if (FAILED(hr)) return 1; for (;;) { LONGLONG pos; LONG size; const long status = m_pSegment->LoadCluster(pos, size); if (status >= 0) break; if (status != mkvparser::E_BUFFER_NOT_FULL) return 1; hr = m_inpin.m_reader.Wait(*this, pos, size, INFINITE); if (FAILED(hr)) //wait was cancelled return 1; } const bool bDone = m_pSegment->DoneParsing(); #endif OnNewCluster(); if (bDone) return 0; if (m_state == State_Stopped) return 0; } }
HRESULT Filter::GetState( DWORD timeout, FILTER_STATE* p) { if (p == 0) return E_POINTER; //What the GetState.timeout parameter refers to is not to locking //the filter, but rather to waiting to determine the current state. //A request to Stop is always synchronous (hence no timeout parameter), //but a request to Pause can be asynchronous, so the caller can say //how long he's willing to wait for the transition (to paused) to //complete. //TODO: implement a waiting scheme here. We'll probably have to //use SignalObjectAndWait atomically release the mutex and then //wait for the condition variable to change. //if (hr == VFW_E_TIMEOUT) // return VFW_S_STATE_INTERMEDIATE; Lock lock; HRESULT hrLock = lock.Seize(this); //The lock is only used for synchronization. If Seize fails, //it means there's a serious problem with the filter. if (FAILED(hrLock)) return E_FAIL; FILTER_STATE& state = *p; if (m_cStarvation < 0) //not starving { state = m_state; return S_OK; } assert(m_pSegment); long count = m_pSegment->GetCount(); if (count > m_cStarvation) { m_cStarvation = -1; state = m_state; //TODO: should be State_Paused? return S_OK; } for (;;) { lock.Release(); DWORD index; //TODO: this timeout isn't quite correct. The parameter refers //to the total wait time. As used here in the call to WaitForHandles, //it refers to the wait time for this pass through the loop. const HRESULT hrWait = CoWaitForMultipleHandles( 0, //wait flags timeout, 1, &m_hNewCluster, &index); if (SUCCEEDED(hrWait)) assert(index == 0); else if (hrWait != RPC_S_CALLPENDING) //error, despite "S" in name return hrWait; hrLock = lock.Seize(this); if (FAILED(hrLock)) return E_FAIL; count = m_pSegment->GetCount(); if (count > m_cStarvation) { m_cStarvation = -1; state = m_state; //TODO: should be State_Paused? return S_OK; } if (FAILED(hrWait)) //there was a timeout before receiving signal return VFW_S_STATE_INTERMEDIATE; } }
HRESULT Filter::Stop() { //Stop is a synchronous operation: when it completes, //the filter is stopped. Lock lock; HRESULT hr = lock.Seize(this); if (FAILED(hr)) return hr; switch (m_state) { case State_Paused: case State_Running: //Stop is synchronous. When stop completes, all threads //should be stopped. What does "stopped" mean" In our //case it probably means "terminated". //It's a bit tricky here because we hold the filter //lock. If threads need to acquire filter lock //then we'll have to release it. Only the FGM can call //Stop, etc, so there's no problem to release lock //while Stop is executing, to allow threads to acquire //filter lock temporarily. //The streaming thread will receiving an indication //automatically (assuming it's connected), either via //GetBuffer or Receive, so there's nothing this filter //needs to do to tell the streaming thread to stop. //One implementation strategy is to have build a //vector of thread handles, and then wait for a signal //on one of them. When the handle is signalled //(meaning that the thread has terminated), then //we remove that handle from the vector, close the //handle, and the wait again. Repeat until the //all threads have been terminated. //We also need to clean up any unused samples, //and decommit the allocator. (In fact, we could //decommit the allocator immediately, and then wait //for the threads to terminated.) m_state = State_Stopped; hr = m_inpin.m_reader.BeginFlush(); assert(SUCCEEDED(hr)); lock.Release(); OnStop(); hr = lock.Seize(this); assert(SUCCEEDED(hr)); //TODO hr = m_inpin.m_reader.EndFlush(); assert(SUCCEEDED(hr)); break; case State_Stopped: default: break; } return S_OK; }