/*
* (C) 2003-2006 Gabest
* (C) 2006-2015 see Authors.txt
*
* This file is part of MPC-HC.
*
* MPC-HC 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 3 of the License, or
* (at your option) any later version.
*
* MPC-HC 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 this program. If not, see .
*
*/
#include "stdafx.h"
#include
#include "StreamSwitcher.h"
#include "../../../DSUtil/DSUtil.h"
#ifdef STANDALONE_FILTER
#include
#endif
#include "moreuuids.h"
#define BLOCKSTREAM
//
// CStreamSwitcherPassThru
//
CStreamSwitcherPassThru::CStreamSwitcherPassThru(LPUNKNOWN pUnk, HRESULT* phr, CStreamSwitcherFilter* pFilter)
: CMediaPosition(NAME("CStreamSwitcherPassThru"), pUnk)
, m_pFilter(pFilter)
{
}
STDMETHODIMP CStreamSwitcherPassThru::NonDelegatingQueryInterface(REFIID riid, void** ppv)
{
CheckPointer(ppv, E_POINTER);
*ppv = nullptr;
return
QI(IMediaSeeking)
CMediaPosition::NonDelegatingQueryInterface(riid, ppv);
}
template
HRESULT GetPeer(CStreamSwitcherFilter* pFilter, T** ppT)
{
*ppT = nullptr;
CBasePin* pPin = pFilter->GetInputPin();
if (!pPin) {
return E_NOTIMPL;
}
CComPtr pConnected;
if (FAILED(pPin->ConnectedTo(&pConnected))) {
return E_NOTIMPL;
}
if (CComQIPtr pT = pConnected) {
*ppT = pT.Detach();
return S_OK;
}
return E_NOTIMPL;
}
#define CallPeerSeeking(call) \
CComPtr pMS; \
if (FAILED(GetPeer(m_pFilter, &pMS))) \
return E_NOTIMPL; \
return pMS->##call;
#define CallPeer(call) \
CComPtr pMP; \
if (FAILED(GetPeer(m_pFilter, &pMP))) \
return E_NOTIMPL; \
return pMP->##call;
#define CallPeerSeekingAll(call) \
HRESULT hr = E_NOTIMPL; \
POSITION pos = m_pFilter->m_pInputs.GetHeadPosition(); \
while (pos) { \
CBasePin* pPin = m_pFilter->m_pInputs.GetNext(pos); \
CComPtr pConnected; \
if (FAILED(pPin->ConnectedTo(&pConnected))) \
continue; \
if (CComQIPtr pMS = pConnected) { \
HRESULT hr2 = pMS->call; \
if (pPin == m_pFilter->GetInputPin()) \
hr = hr2; \
} \
} \
return hr;
#define CallPeerAll(call) \
HRESULT hr = E_NOTIMPL; \
POSITION pos = m_pFilter->m_pInputs.GetHeadPosition(); \
while (pos) { \
CBasePin* pPin = m_pFilter->m_pInputs.GetNext(pos); \
CComPtr pConnected; \
if (FAILED(pPin->ConnectedTo(&pConnected))) \
continue; \
if (CComQIPtr pMP = pConnected) { \
HRESULT hr2 = pMP->call; \
if (pPin == m_pFilter->GetInputPin()) \
hr = hr2; \
} \
} \
return hr;
// IMediaSeeking
STDMETHODIMP CStreamSwitcherPassThru::GetCapabilities(DWORD* pCaps)
{
CallPeerSeeking(GetCapabilities(pCaps));
}
STDMETHODIMP CStreamSwitcherPassThru::CheckCapabilities(DWORD* pCaps)
{
CallPeerSeeking(CheckCapabilities(pCaps));
}
STDMETHODIMP CStreamSwitcherPassThru::IsFormatSupported(const GUID* pFormat)
{
CallPeerSeeking(IsFormatSupported(pFormat));
}
STDMETHODIMP CStreamSwitcherPassThru::QueryPreferredFormat(GUID* pFormat)
{
CallPeerSeeking(QueryPreferredFormat(pFormat));
}
STDMETHODIMP CStreamSwitcherPassThru::SetTimeFormat(const GUID* pFormat)
{
CallPeerSeeking(SetTimeFormat(pFormat));
}
STDMETHODIMP CStreamSwitcherPassThru::GetTimeFormat(GUID* pFormat)
{
CallPeerSeeking(GetTimeFormat(pFormat));
}
STDMETHODIMP CStreamSwitcherPassThru::IsUsingTimeFormat(const GUID* pFormat)
{
CallPeerSeeking(IsUsingTimeFormat(pFormat));
}
STDMETHODIMP CStreamSwitcherPassThru::ConvertTimeFormat(LONGLONG* pTarget, const GUID* pTargetFormat, LONGLONG Source, const GUID* pSourceFormat)
{
CallPeerSeeking(ConvertTimeFormat(pTarget, pTargetFormat, Source, pSourceFormat));
}
STDMETHODIMP CStreamSwitcherPassThru::SetPositions(LONGLONG* pCurrent, DWORD CurrentFlags, LONGLONG* pStop, DWORD StopFlags)
{
CallPeerSeekingAll(SetPositions(pCurrent, CurrentFlags, pStop, StopFlags));
}
STDMETHODIMP CStreamSwitcherPassThru::GetPositions(LONGLONG* pCurrent, LONGLONG* pStop)
{
CallPeerSeeking(GetPositions(pCurrent, pStop));
}
STDMETHODIMP CStreamSwitcherPassThru::GetCurrentPosition(LONGLONG* pCurrent)
{
CallPeerSeeking(GetCurrentPosition(pCurrent));
}
STDMETHODIMP CStreamSwitcherPassThru::GetStopPosition(LONGLONG* pStop)
{
CallPeerSeeking(GetStopPosition(pStop));
}
STDMETHODIMP CStreamSwitcherPassThru::GetDuration(LONGLONG* pDuration)
{
CallPeerSeeking(GetDuration(pDuration));
}
STDMETHODIMP CStreamSwitcherPassThru::GetPreroll(LONGLONG* pllPreroll)
{
CallPeerSeeking(GetPreroll(pllPreroll));
}
STDMETHODIMP CStreamSwitcherPassThru::GetAvailable(LONGLONG* pEarliest, LONGLONG* pLatest)
{
CallPeerSeeking(GetAvailable(pEarliest, pLatest));
}
STDMETHODIMP CStreamSwitcherPassThru::GetRate(double* pdRate)
{
CallPeerSeeking(GetRate(pdRate));
}
STDMETHODIMP CStreamSwitcherPassThru::SetRate(double dRate)
{
if (0.0 == dRate) {
return E_INVALIDARG;
}
CallPeerSeekingAll(SetRate(dRate));
}
// IMediaPosition
STDMETHODIMP CStreamSwitcherPassThru::get_Duration(REFTIME* plength)
{
CallPeer(get_Duration(plength));
}
STDMETHODIMP CStreamSwitcherPassThru::get_CurrentPosition(REFTIME* pllTime)
{
CallPeer(get_CurrentPosition(pllTime));
}
STDMETHODIMP CStreamSwitcherPassThru::put_CurrentPosition(REFTIME llTime)
{
CallPeerAll(put_CurrentPosition(llTime));
}
STDMETHODIMP CStreamSwitcherPassThru::get_StopTime(REFTIME* pllTime)
{
CallPeer(get_StopTime(pllTime));
}
STDMETHODIMP CStreamSwitcherPassThru::put_StopTime(REFTIME llTime)
{
CallPeerAll(put_StopTime(llTime));
}
STDMETHODIMP CStreamSwitcherPassThru::get_PrerollTime(REFTIME* pllTime)
{
CallPeer(get_PrerollTime(pllTime));
}
STDMETHODIMP CStreamSwitcherPassThru::put_PrerollTime(REFTIME llTime)
{
CallPeerAll(put_PrerollTime(llTime));
}
STDMETHODIMP CStreamSwitcherPassThru::get_Rate(double* pdRate)
{
CallPeer(get_Rate(pdRate));
}
STDMETHODIMP CStreamSwitcherPassThru::put_Rate(double dRate)
{
if (0.0 == dRate) {
return E_INVALIDARG;
}
CallPeerAll(put_Rate(dRate));
}
STDMETHODIMP CStreamSwitcherPassThru::CanSeekForward(LONG* pCanSeekForward)
{
CallPeer(CanSeekForward(pCanSeekForward));
}
STDMETHODIMP CStreamSwitcherPassThru::CanSeekBackward(LONG* pCanSeekBackward)
{
CallPeer(CanSeekBackward(pCanSeekBackward));
}
//
// CStreamSwitcherAllocator
//
CStreamSwitcherAllocator::CStreamSwitcherAllocator(CStreamSwitcherInputPin* pPin, HRESULT* phr)
: CMemAllocator(NAME("CStreamSwitcherAllocator"), nullptr, phr)
, m_pPin(pPin)
, m_fMediaTypeChanged(false)
{
ASSERT(phr);
ASSERT(pPin);
}
#ifdef _DEBUG
CStreamSwitcherAllocator::~CStreamSwitcherAllocator()
{
ASSERT(m_bCommitted == FALSE);
}
#endif
STDMETHODIMP_(ULONG) CStreamSwitcherAllocator::NonDelegatingAddRef()
{
return m_pPin->m_pFilter->AddRef();
}
STDMETHODIMP_(ULONG) CStreamSwitcherAllocator::NonDelegatingRelease()
{
return m_pPin->m_pFilter->Release();
}
STDMETHODIMP CStreamSwitcherAllocator::GetBuffer(
IMediaSample** ppBuffer,
REFERENCE_TIME* pStartTime, REFERENCE_TIME* pEndTime,
DWORD dwFlags)
{
HRESULT hr = VFW_E_NOT_COMMITTED;
if (!m_bCommitted) {
return hr;
}
/*
TRACE(_T("CStreamSwitcherAllocator::GetBuffer m_pPin->m_evBlock.Wait() + %x\n"), this);
m_pPin->m_evBlock.Wait();
TRACE(_T("CStreamSwitcherAllocator::GetBuffer m_pPin->m_evBlock.Wait() - %x\n"), this);
*/
if (m_fMediaTypeChanged) {
if (!m_pPin || !m_pPin->m_pFilter) {
return hr;
}
CStreamSwitcherOutputPin* pOut = (static_cast(m_pPin->m_pFilter))->GetOutputPin();
if (!pOut || !pOut->CurrentAllocator()) {
return hr;
}
ALLOCATOR_PROPERTIES Properties, Actual;
if (FAILED(pOut->CurrentAllocator()->GetProperties(&Actual))) {
return hr;
}
if (FAILED(GetProperties(&Properties))) {
return hr;
}
if (!m_bCommitted || Properties.cbBuffer < Actual.cbBuffer) {
Properties.cbBuffer = Actual.cbBuffer;
if (FAILED(Decommit())) {
return hr;
}
if (FAILED(SetProperties(&Properties, &Actual))) {
return hr;
}
if (FAILED(Commit())) {
return hr;
}
ASSERT(Actual.cbBuffer >= Properties.cbBuffer);
if (Actual.cbBuffer < Properties.cbBuffer) {
return hr;
}
}
}
hr = CMemAllocator::GetBuffer(ppBuffer, pStartTime, pEndTime, dwFlags);
if (m_fMediaTypeChanged && SUCCEEDED(hr)) {
(*ppBuffer)->SetMediaType(&m_mt);
m_fMediaTypeChanged = false;
}
return hr;
}
void CStreamSwitcherAllocator::NotifyMediaType(const CMediaType& mt)
{
CopyMediaType(&m_mt, &mt);
m_fMediaTypeChanged = true;
}
//
// CStreamSwitcherInputPin
//
CStreamSwitcherInputPin::CStreamSwitcherInputPin(CStreamSwitcherFilter* pFilter, HRESULT* phr, LPCWSTR pName)
: CBaseInputPin(NAME("CStreamSwitcherInputPin"), pFilter, &pFilter->m_csState, phr, pName)
, m_Allocator(this, phr)
, m_bSampleSkipped(FALSE)
, m_bQualityChanged(FALSE)
, m_bUsingOwnAllocator(FALSE)
, m_evBlock(TRUE)
, m_fCanBlock(false)
, m_hNotifyEvent(nullptr)
{
m_bCanReconnectWhenActive = true;
}
class __declspec(uuid("138130AF-A79B-45D5-B4AA-87697457BA87"))
NeroAudioDecoder {};
STDMETHODIMP CStreamSwitcherInputPin::NonDelegatingQueryInterface(REFIID riid, void** ppv)
{
return
QI(IStreamSwitcherInputPin)
IsConnected() && GetCLSID(GetFilterFromPin(GetConnected())) == __uuidof(NeroAudioDecoder) && QI(IPinConnection)
__super::NonDelegatingQueryInterface(riid, ppv);
}
// IPinConnection
STDMETHODIMP CStreamSwitcherInputPin::DynamicQueryAccept(const AM_MEDIA_TYPE* pmt)
{
return QueryAccept(pmt);
}
STDMETHODIMP CStreamSwitcherInputPin::NotifyEndOfStream(HANDLE hNotifyEvent)
{
if (m_hNotifyEvent) {
SetEvent(m_hNotifyEvent);
}
m_hNotifyEvent = hNotifyEvent;
return S_OK;
}
STDMETHODIMP CStreamSwitcherInputPin::IsEndPin()
{
return S_OK;
}
STDMETHODIMP CStreamSwitcherInputPin::DynamicDisconnect()
{
CAutoLock cAutoLock(&m_csReceive);
Disconnect();
return S_OK;
}
// IStreamSwitcherInputPin
STDMETHODIMP_(bool) CStreamSwitcherInputPin::IsActive()
{
// TODO: lock onto something here
return (this == (static_cast(m_pFilter))->GetInputPin());
}
//
HRESULT CStreamSwitcherInputPin::QueryAcceptDownstream(const AM_MEDIA_TYPE* pmt)
{
HRESULT hr = S_OK;
CStreamSwitcherOutputPin* pOut = (static_cast(m_pFilter))->GetOutputPin();
if (pOut && pOut->IsConnected()) {
if (CComPtr pPC = pOut->CurrentPinConnection()) {
hr = pPC->DynamicQueryAccept(pmt);
if (hr == S_OK) {
return S_OK;
}
}
hr = pOut->GetConnected()->QueryAccept(pmt);
}
return hr;
}
void CStreamSwitcherInputPin::Block(bool fBlock)
{
if (fBlock) {
m_evBlock.Reset();
} else {
m_evBlock.Set();
}
}
HRESULT CStreamSwitcherInputPin::InitializeOutputSample(IMediaSample* pInSample, IMediaSample** ppOutSample)
{
if (!pInSample || !ppOutSample) {
return E_POINTER;
}
CStreamSwitcherOutputPin* pOut = (static_cast(m_pFilter))->GetOutputPin();
ASSERT(pOut->GetConnected());
CComPtr pOutSample;
DWORD dwFlags = m_bSampleSkipped ? AM_GBF_PREVFRAMESKIPPED : 0;
if (!(m_SampleProps.dwSampleFlags & AM_SAMPLE_SPLICEPOINT)) {
dwFlags |= AM_GBF_NOTASYNCPOINT;
}
HRESULT hr = pOut->GetDeliveryBuffer(&pOutSample
, m_SampleProps.dwSampleFlags & AM_SAMPLE_TIMEVALID ? &m_SampleProps.tStart : nullptr
, m_SampleProps.dwSampleFlags & AM_SAMPLE_STOPVALID ? &m_SampleProps.tStop : nullptr
, dwFlags);
if (FAILED(hr)) {
return hr;
}
if (!pOutSample) {
return E_FAIL;
}
if (CComQIPtr pOutSample2 = pOutSample) {
AM_SAMPLE2_PROPERTIES OutProps;
EXECUTE_ASSERT(SUCCEEDED(pOutSample2->GetProperties(FIELD_OFFSET(AM_SAMPLE2_PROPERTIES, tStart), (PBYTE)&OutProps)));
OutProps.dwTypeSpecificFlags = m_SampleProps.dwTypeSpecificFlags;
OutProps.dwSampleFlags =
(OutProps.dwSampleFlags & AM_SAMPLE_TYPECHANGED) |
(m_SampleProps.dwSampleFlags & ~AM_SAMPLE_TYPECHANGED);
OutProps.tStart = m_SampleProps.tStart;
OutProps.tStop = m_SampleProps.tStop;
OutProps.cbData = FIELD_OFFSET(AM_SAMPLE2_PROPERTIES, dwStreamId);
pOutSample2->SetProperties(FIELD_OFFSET(AM_SAMPLE2_PROPERTIES, dwStreamId), (PBYTE)&OutProps);
if (m_SampleProps.dwSampleFlags & AM_SAMPLE_DATADISCONTINUITY) {
m_bSampleSkipped = FALSE;
}
} else {
if (m_SampleProps.dwSampleFlags & AM_SAMPLE_TIMEVALID) {
pOutSample->SetTime(&m_SampleProps.tStart, &m_SampleProps.tStop);
}
if (m_SampleProps.dwSampleFlags & AM_SAMPLE_SPLICEPOINT) {
pOutSample->SetSyncPoint(TRUE);
}
if (m_SampleProps.dwSampleFlags & AM_SAMPLE_DATADISCONTINUITY) {
pOutSample->SetDiscontinuity(TRUE);
m_bSampleSkipped = FALSE;
}
LONGLONG MediaStart, MediaEnd;
if (pInSample->GetMediaTime(&MediaStart, &MediaEnd) == NOERROR) {
pOutSample->SetMediaTime(&MediaStart, &MediaEnd);
}
}
*ppOutSample = pOutSample.Detach();
return S_OK;
}
// pure virtual
HRESULT CStreamSwitcherInputPin::CheckMediaType(const CMediaType* pmt)
{
return (static_cast(m_pFilter))->CheckMediaType(pmt);
}
// virtual
HRESULT CStreamSwitcherInputPin::CheckConnect(IPin* pPin)
{
return (IPin*)(static_cast(m_pFilter))->GetOutputPin() == pPin
? E_FAIL
: __super::CheckConnect(pPin);
}
HRESULT CStreamSwitcherInputPin::CompleteConnect(IPin* pReceivePin)
{
HRESULT hr = __super::CompleteConnect(pReceivePin);
if (FAILED(hr)) {
return hr;
}
(static_cast(m_pFilter))->CompleteConnect(PINDIR_INPUT, this, pReceivePin);
m_fCanBlock = false;
bool fForkedSomewhere = false;
CStringW fileName;
CStringW pinName;
IPin* pPin = (IPin*)this;
IBaseFilter* pBF = (IBaseFilter*)m_pFilter;
pPin = GetUpStreamPin(pBF, pPin);
if (pPin) {
pBF = GetFilterFromPin(pPin);
}
while (pPin && pBF) {
if (IsSplitter(pBF)) {
pinName = GetPinName(pPin);
}
CLSID clsid = GetCLSID(pBF);
if (clsid == CLSID_AviSplitter || clsid == CLSID_OggSplitter) {
m_fCanBlock = true;
}
int nIn, nOut, nInC, nOutC;
CountPins(pBF, nIn, nOut, nInC, nOutC);
fForkedSomewhere = fForkedSomewhere || nIn > 1 || nOut > 1;
DWORD cStreams = 0;
if (CComQIPtr pSSF = pBF) {
hr = pSSF->Count(&cStreams);
if (SUCCEEDED(hr)) {
for (int i = 0; i < (int)cStreams; i++) {
AM_MEDIA_TYPE* pmt = nullptr;
hr = pSSF->Info(i, &pmt, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);
if (SUCCEEDED(hr) && pmt && pmt->majortype == MEDIATYPE_Audio) {
m_pSSF = pSSF;
DeleteMediaType(pmt);
break;
}
DeleteMediaType(pmt);
}
}
}
if (CComQIPtr pFSF = pBF) {
WCHAR* pszName = nullptr;
AM_MEDIA_TYPE mt;
if (SUCCEEDED(pFSF->GetCurFile(&pszName, &mt)) && pszName) {
fileName = pszName;
CoTaskMemFree(pszName);
fileName.Replace('\\', '/');
CStringW fn = fileName.Mid(fileName.ReverseFind('/') + 1);
if (!fn.IsEmpty()) {
fileName = fn;
}
// Haali & LAVFSplitter return only one "Audio" pin name, cause CMainFrame::OnInitMenuPopup lookup find the wrong popmenu,
// add space at the end to prevent this, internal filter never return "Audio" only.
if (!pinName.IsEmpty()) {
fileName = pinName + L" ";
}
WCHAR* pName = DEBUG_NEW WCHAR[fileName.GetLength() + 1];
if (pName) {
wcscpy_s(pName, fileName.GetLength() + 1, fileName);
if (m_pName) {
delete [] m_pName;
}
m_pName = pName;
if (cStreams == 1) { // Simple external track, no need to use the info from IAMStreamSelect
m_pSSF.Release();
}
}
}
break;
}
pPin = GetFirstPin(pBF);
pPin = GetUpStreamPin(pBF, pPin);
if (pPin) {
pBF = GetFilterFromPin(pPin);
}
}
if (!fForkedSomewhere) {
m_fCanBlock = true;
}
m_hNotifyEvent = nullptr;
return S_OK;
}
HRESULT CStreamSwitcherInputPin::Active()
{
Block(!IsActive());
return __super::Active();
}
HRESULT CStreamSwitcherInputPin::Inactive()
{
Block(false);
return __super::Inactive();
}
// IPin
STDMETHODIMP CStreamSwitcherInputPin::QueryAccept(const AM_MEDIA_TYPE* pmt)
{
HRESULT hr = __super::QueryAccept(pmt);
if (S_OK != hr) {
return hr;
}
return QueryAcceptDownstream(pmt);
}
STDMETHODIMP CStreamSwitcherInputPin::ReceiveConnection(IPin* pConnector, const AM_MEDIA_TYPE* pmt)
{
// FIXME: this locked up once
// CAutoLock cAutoLock(&((CStreamSwitcherFilter*)m_pFilter)->m_csReceive);
HRESULT hr;
if (S_OK != (hr = QueryAcceptDownstream(pmt))) {
return VFW_E_TYPE_NOT_ACCEPTED;
}
if (m_Connected && m_Connected != pConnector) {
return VFW_E_ALREADY_CONNECTED;
}
SAFE_RELEASE(m_Connected);
return SUCCEEDED(__super::ReceiveConnection(pConnector, pmt)) ? S_OK : E_FAIL;
}
STDMETHODIMP CStreamSwitcherInputPin::GetAllocator(IMemAllocator** ppAllocator)
{
CheckPointer(ppAllocator, E_POINTER);
if (m_pAllocator == nullptr) {
(m_pAllocator = &m_Allocator)->AddRef();
}
(*ppAllocator = m_pAllocator)->AddRef();
return NOERROR;
}
STDMETHODIMP CStreamSwitcherInputPin::NotifyAllocator(IMemAllocator* pAllocator, BOOL bReadOnly)
{
HRESULT hr = __super::NotifyAllocator(pAllocator, bReadOnly);
if (FAILED(hr)) {
return hr;
}
m_bUsingOwnAllocator = (pAllocator == (IMemAllocator*)&m_Allocator);
return S_OK;
}
STDMETHODIMP CStreamSwitcherInputPin::BeginFlush()
{
CAutoLock cAutoLock(&(static_cast(m_pFilter))->m_csState);
HRESULT hr;
CStreamSwitcherFilter* pSSF = static_cast(m_pFilter);
CStreamSwitcherOutputPin* pOut = pSSF->GetOutputPin();
if (!IsConnected() || !pOut || !pOut->IsConnected()) {
return VFW_E_NOT_CONNECTED;
}
if (FAILED(hr = __super::BeginFlush())) {
return hr;
}
return IsActive() ? pSSF->DeliverBeginFlush() : Block(false), S_OK;
}
STDMETHODIMP CStreamSwitcherInputPin::EndFlush()
{
CAutoLock cAutoLock(&(static_cast(m_pFilter))->m_csState);
HRESULT hr;
CStreamSwitcherFilter* pSSF = static_cast(m_pFilter);
CStreamSwitcherOutputPin* pOut = pSSF->GetOutputPin();
if (!IsConnected() || !pOut || !pOut->IsConnected()) {
return VFW_E_NOT_CONNECTED;
}
if (FAILED(hr = __super::EndFlush())) {
return hr;
}
return IsActive() ? pSSF->DeliverEndFlush() : Block(true), S_OK;
}
STDMETHODIMP CStreamSwitcherInputPin::EndOfStream()
{
CAutoLock cAutoLock(&m_csReceive);
CStreamSwitcherFilter* pSSF = static_cast(m_pFilter);
CStreamSwitcherOutputPin* pOut = pSSF->GetOutputPin();
if (!IsConnected() || !pOut || !pOut->IsConnected()) {
return VFW_E_NOT_CONNECTED;
}
if (m_hNotifyEvent) {
SetEvent(m_hNotifyEvent), m_hNotifyEvent = nullptr;
return S_OK;
}
return IsActive() ? pSSF->DeliverEndOfStream() : S_OK;
}
// IMemInputPin
STDMETHODIMP CStreamSwitcherInputPin::Receive(IMediaSample* pSample)
{
AM_MEDIA_TYPE* pmt = nullptr;
if (SUCCEEDED(pSample->GetMediaType(&pmt)) && pmt) {
const CMediaType mt(*pmt);
DeleteMediaType(pmt), pmt = nullptr;
SetMediaType(&mt);
}
// DAMN!!!!!! this doesn't work if the stream we are blocking
// shares the same thread with another stream, mpeg splitters
// are usually like that. Our nicely built up multithreaded
// strategy is useless because of this, ARRRRRRGHHHHHH.
#ifdef BLOCKSTREAM
if (m_fCanBlock) {
m_evBlock.Wait();
}
#endif
if (!IsActive()) {
#ifdef BLOCKSTREAM
if (m_fCanBlock) {
return S_FALSE;
}
#endif
TRACE(_T("&^$#@ : a stupid fix for this stupid problem\n"));
//Sleep(32);
return E_FAIL; // a stupid fix for this stupid problem
}
CAutoLock cAutoLock(&m_csReceive);
CStreamSwitcherOutputPin* pOut = (static_cast(m_pFilter))->GetOutputPin();
ASSERT(pOut->GetConnected());
HRESULT hr = __super::Receive(pSample);
if (S_OK != hr) {
return hr;
}
if (m_SampleProps.dwStreamId != AM_STREAM_MEDIA) {
return pOut->Deliver(pSample);
}
//
ALLOCATOR_PROPERTIES props, actual;
m_pAllocator->GetProperties(&props);
pOut->CurrentAllocator()->GetProperties(&actual);
REFERENCE_TIME rtStart = 0, rtStop = 0;
if (S_OK == pSample->GetTime(&rtStart, &rtStop)) {
//
}
long cbBuffer = pSample->GetActualDataLength();
CMediaType mtOut = m_mt;
mtOut = (static_cast(m_pFilter))->CreateNewOutputMediaType(mtOut, cbBuffer);
bool fTypeChanged = false;
if (mtOut != pOut->CurrentMediaType() || cbBuffer > actual.cbBuffer) {
fTypeChanged = true;
m_SampleProps.dwSampleFlags |= AM_SAMPLE_TYPECHANGED/*|AM_SAMPLE_DATADISCONTINUITY|AM_SAMPLE_TIMEDISCONTINUITY*/;
if (props.cBuffers < 8 && mtOut.majortype == MEDIATYPE_Audio) {
props.cBuffers = 8;
}
props.cbBuffer = cbBuffer;
if (actual.cbAlign != props.cbAlign
|| actual.cbPrefix != props.cbPrefix
|| actual.cBuffers < props.cBuffers
|| actual.cbBuffer < props.cbBuffer) {
pOut->DeliverBeginFlush();
pOut->DeliverEndFlush();
pOut->CurrentAllocator()->Decommit();
pOut->CurrentAllocator()->SetProperties(&props, &actual);
pOut->CurrentAllocator()->Commit();
}
}
CComPtr pOutSample;
if (FAILED(InitializeOutputSample(pSample, &pOutSample))) {
return E_FAIL;
}
pmt = nullptr;
if (SUCCEEDED(pOutSample->GetMediaType(&pmt)) && pmt) {
const CMediaType mt(*pmt);
DeleteMediaType(pmt), pmt = nullptr;
// TODO
ASSERT(0);
}
if (fTypeChanged) {
pOut->SetMediaType(&mtOut);
(static_cast(m_pFilter))->OnNewOutputMediaType(m_mt, mtOut);
pOutSample->SetMediaType(&mtOut);
}
// Transform
hr = (static_cast(m_pFilter))->Transform(pSample, pOutSample);
if (S_OK == hr) {
hr = pOut->Deliver(pOutSample);
m_bSampleSkipped = FALSE;
} else if (S_FALSE == hr) {
hr = S_OK;
pOutSample = nullptr;
m_bSampleSkipped = TRUE;
if (!m_bQualityChanged) {
m_pFilter->NotifyEvent(EC_QUALITY_CHANGE, 0, 0);
m_bQualityChanged = TRUE;
}
}
return hr;
}
STDMETHODIMP CStreamSwitcherInputPin::NewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate)
{
if (!IsConnected()) {
return S_OK;
}
CAutoLock cAutoLock(&m_csReceive);
CStreamSwitcherFilter* pSSF = static_cast(m_pFilter);
CStreamSwitcherOutputPin* pOut = pSSF->GetOutputPin();
if (!pOut || !pOut->IsConnected()) {
return VFW_E_NOT_CONNECTED;
}
return pSSF->DeliverNewSegment(tStart, tStop, dRate);
}
//
// CStreamSwitcherOutputPin
//
CStreamSwitcherOutputPin::CStreamSwitcherOutputPin(CStreamSwitcherFilter* pFilter, HRESULT* phr)
: CBaseOutputPin(NAME("CStreamSwitcherOutputPin"), pFilter, &pFilter->m_csState, phr, L"Out")
{
// m_bCanReconnectWhenActive = true;
}
STDMETHODIMP CStreamSwitcherOutputPin::NonDelegatingQueryInterface(REFIID riid, void** ppv)
{
CheckPointer(ppv, E_POINTER);
ValidateReadWritePtr(ppv, sizeof(PVOID));
*ppv = nullptr;
if (riid == IID_IMediaPosition || riid == IID_IMediaSeeking) {
if (m_pStreamSwitcherPassThru == nullptr) {
HRESULT hr = S_OK;
m_pStreamSwitcherPassThru = (IUnknown*)(INonDelegatingUnknown*)
DEBUG_NEW CStreamSwitcherPassThru(GetOwner(), &hr, static_cast(m_pFilter));
if (!m_pStreamSwitcherPassThru) {
return E_OUTOFMEMORY;
}
if (FAILED(hr)) {
return hr;
}
}
return m_pStreamSwitcherPassThru->QueryInterface(riid, ppv);
}
/*
else if (riid == IID_IStreamBuilder)
{
return GetInterface((IStreamBuilder*)this, ppv);
}
*/
return CBaseOutputPin::NonDelegatingQueryInterface(riid, ppv);
}
HRESULT CStreamSwitcherOutputPin::QueryAcceptUpstream(const AM_MEDIA_TYPE* pmt)
{
HRESULT hr = S_FALSE;
CStreamSwitcherInputPin* pIn = (static_cast(m_pFilter))->GetInputPin();
if (pIn && pIn->IsConnected() && (pIn->IsUsingOwnAllocator() || pIn->CurrentMediaType() == *pmt)) {
if (CComQIPtr pPinTo = pIn->GetConnected()) {
if (S_OK != (hr = pPinTo->QueryAccept(pmt))) {
return VFW_E_TYPE_NOT_ACCEPTED;
}
} else {
return E_FAIL;
}
}
return hr;
}
// pure virtual
HRESULT CStreamSwitcherOutputPin::DecideBufferSize(IMemAllocator* pAllocator, ALLOCATOR_PROPERTIES* pProperties)
{
CStreamSwitcherInputPin* pIn = (static_cast(m_pFilter))->GetInputPin();
if (!pIn || !pIn->IsConnected()) {
return E_UNEXPECTED;
}
CComPtr pAllocatorIn;
pIn->GetAllocator(&pAllocatorIn);
if (!pAllocatorIn) {
return E_UNEXPECTED;
}
HRESULT hr;
if (FAILED(hr = pAllocatorIn->GetProperties(pProperties))) {
return hr;
}
if (pProperties->cBuffers < 8 && pIn->CurrentMediaType().majortype == MEDIATYPE_Audio) {
pProperties->cBuffers = 8;
}
ALLOCATOR_PROPERTIES Actual;
if (FAILED(hr = pAllocator->SetProperties(pProperties, &Actual))) {
return hr;
}
return (pProperties->cBuffers > Actual.cBuffers || pProperties->cbBuffer > Actual.cbBuffer
? E_FAIL
: NOERROR);
}
// virtual
class __declspec(uuid("AEFA5024-215A-4FC7-97A4-1043C86FD0B8"))
MatrixMixer {};
HRESULT CStreamSwitcherOutputPin::CheckConnect(IPin* pPin)
{
CComPtr pBF = GetFilterFromPin(pPin);
return
IsAudioWaveRenderer(pBF) || GetCLSID(pBF) == __uuidof(MatrixMixer)
? __super::CheckConnect(pPin)
: E_FAIL;
// return CComQIPtr(pPin) ? CBaseOutputPin::CheckConnect(pPin) : E_NOINTERFACE;
// return CBaseOutputPin::CheckConnect(pPin);
}
HRESULT CStreamSwitcherOutputPin::BreakConnect()
{
m_pPinConnection = nullptr;
return __super::BreakConnect();
}
HRESULT CStreamSwitcherOutputPin::CompleteConnect(IPin* pReceivePin)
{
m_pPinConnection = CComQIPtr(pReceivePin);
HRESULT hr = __super::CompleteConnect(pReceivePin);
CStreamSwitcherInputPin* pIn = (static_cast(m_pFilter))->GetInputPin();
CMediaType mt;
if (SUCCEEDED(hr) && pIn && pIn->IsConnected()
&& SUCCEEDED(pIn->GetConnected()->ConnectionMediaType(&mt)) && m_mt != mt) {
if (pIn->GetConnected()->QueryAccept(&m_mt) == S_OK) {
hr = m_pFilter->ReconnectPin(pIn->GetConnected(), &m_mt);
} else {
hr = VFW_E_TYPE_NOT_ACCEPTED;
}
}
return hr;
}
HRESULT CStreamSwitcherOutputPin::CheckMediaType(const CMediaType* pmt)
{
return (static_cast(m_pFilter))->CheckMediaType(pmt);
}
HRESULT CStreamSwitcherOutputPin::GetMediaType(int iPosition, CMediaType* pmt)
{
CStreamSwitcherInputPin* pIn = (static_cast(m_pFilter))->GetInputPin();
if (!pIn || !pIn->IsConnected()) {
return E_UNEXPECTED;
}
CComPtr pEM;
if (FAILED(pIn->GetConnected()->EnumMediaTypes(&pEM))) {
return VFW_S_NO_MORE_ITEMS;
}
if (iPosition > 0 && FAILED(pEM->Skip(iPosition))) {
return VFW_S_NO_MORE_ITEMS;
}
AM_MEDIA_TYPE* tmp = nullptr;
if (S_OK != pEM->Next(1, &tmp, nullptr) || !tmp) {
return VFW_S_NO_MORE_ITEMS;
}
CopyMediaType(pmt, tmp);
DeleteMediaType(tmp);
/*
if (iPosition < 0) return E_INVALIDARG;
if (iPosition > 0) return VFW_S_NO_MORE_ITEMS;
CopyMediaType(pmt, &pIn->CurrentMediaType());
*/
return S_OK;
}
// IPin
STDMETHODIMP CStreamSwitcherOutputPin::QueryAccept(const AM_MEDIA_TYPE* pmt)
{
HRESULT hr = __super::QueryAccept(pmt);
if (S_OK != hr) {
return hr;
}
return QueryAcceptUpstream(pmt);
}
// IQualityControl
STDMETHODIMP CStreamSwitcherOutputPin::Notify(IBaseFilter* pSender, Quality q)
{
CStreamSwitcherInputPin* pIn = (static_cast(m_pFilter))->GetInputPin();
if (!pIn || !pIn->IsConnected()) {
return VFW_E_NOT_CONNECTED;
}
return pIn->PassNotify(q);
}
// IStreamBuilder
STDMETHODIMP CStreamSwitcherOutputPin::Render(IPin* ppinOut, IGraphBuilder* pGraph)
{
CComPtr pBF;
pBF.CoCreateInstance(CLSID_DSoundRender);
if (!pBF || FAILED(pGraph->AddFilter(pBF, L"Default DirectSound Device"))) {
return E_FAIL;
}
if (FAILED(pGraph->ConnectDirect(ppinOut, GetFirstDisconnectedPin(pBF, PINDIR_INPUT), nullptr))) {
pGraph->RemoveFilter(pBF);
return E_FAIL;
}
return S_OK;
}
STDMETHODIMP CStreamSwitcherOutputPin::Backout(IPin* ppinOut, IGraphBuilder* pGraph)
{
return S_OK;
}
//
// CStreamSwitcherFilter
//
CStreamSwitcherFilter::CStreamSwitcherFilter(LPUNKNOWN lpunk, HRESULT* phr, const CLSID& clsid)
: CBaseFilter(NAME("CStreamSwitcherFilter"), lpunk, &m_csState, clsid)
, m_pInput(nullptr)
, m_pOutput(nullptr)
{
if (phr) {
*phr = S_OK;
}
HRESULT hr = S_OK;
do {
CAutoPtr pInput;
CAutoPtr pOutput;
hr = S_OK;
pInput.Attach(DEBUG_NEW CStreamSwitcherInputPin(this, &hr, L"Channel 1"));
if (!pInput || FAILED(hr)) {
break;
}
hr = S_OK;
pOutput.Attach(DEBUG_NEW CStreamSwitcherOutputPin(this, &hr));
if (!pOutput || FAILED(hr)) {
break;
}
CAutoLock cAutoLock(&m_csPins);
m_pInputs.AddHead(m_pInput = pInput.Detach());
m_pOutput = pOutput.Detach();
return;
} while (false);
if (phr) {
*phr = E_FAIL;
}
}
CStreamSwitcherFilter::~CStreamSwitcherFilter()
{
CAutoLock cAutoLock(&m_csPins);
POSITION pos = m_pInputs.GetHeadPosition();
while (pos) {
delete m_pInputs.GetNext(pos);
}
m_pInputs.RemoveAll();
m_pInput = nullptr;
delete m_pOutput;
m_pOutput = nullptr;
}
STDMETHODIMP CStreamSwitcherFilter::NonDelegatingQueryInterface(REFIID riid, void** ppv)
{
return
QI(IAMStreamSelect)
__super::NonDelegatingQueryInterface(riid, ppv);
}
//
int CStreamSwitcherFilter::GetPinCount()
{
CAutoLock cAutoLock(&m_csPins);
return (1 + (int)m_pInputs.GetCount());
}
CBasePin* CStreamSwitcherFilter::GetPin(int n)
{
CAutoLock cAutoLock(&m_csPins);
if (n < 0 || n >= GetPinCount()) {
return nullptr;
} else if (n == 0) {
return m_pOutput;
} else {
return m_pInputs.GetAt(m_pInputs.FindIndex(n - 1));
}
}
int CStreamSwitcherFilter::GetConnectedInputPinCount()
{
CAutoLock cAutoLock(&m_csPins);
int nConnected = 0;
POSITION pos = m_pInputs.GetHeadPosition();
while (pos) {
if (m_pInputs.GetNext(pos)->IsConnected()) {
nConnected++;
}
}
return nConnected;
}
CStreamSwitcherInputPin* CStreamSwitcherFilter::GetConnectedInputPin(int n)
{
if (n >= 0) {
POSITION pos = m_pInputs.GetHeadPosition();
while (pos) {
CStreamSwitcherInputPin* pPin = m_pInputs.GetNext(pos);
if (pPin->IsConnected()) {
if (n == 0) {
return pPin;
}
n--;
}
}
}
return nullptr;
}
CStreamSwitcherInputPin* CStreamSwitcherFilter::GetInputPin()
{
return m_pInput;
}
CStreamSwitcherOutputPin* CStreamSwitcherFilter::GetOutputPin()
{
return m_pOutput;
}
//
HRESULT CStreamSwitcherFilter::CompleteConnect(PIN_DIRECTION dir, CBasePin* pPin, IPin* pReceivePin)
{
if (dir == PINDIR_INPUT) {
CAutoLock cAutoLock(&m_csPins);
int nConnected = GetConnectedInputPinCount();
if (nConnected == 1) {
m_pInput = static_cast(pPin);
}
if ((size_t)nConnected == m_pInputs.GetCount()) {
CStringW name;
name.Format(L"Channel %ld", ++m_PinVersion);
HRESULT hr = S_OK;
CStreamSwitcherInputPin* pInputPin = DEBUG_NEW CStreamSwitcherInputPin(this, &hr, name);
if (!pInputPin || FAILED(hr)) {
delete pInputPin;
return E_FAIL;
}
m_pInputs.AddTail(pInputPin);
}
}
return S_OK;
}
// this should be very thread safe, I hope it is, it must be... :)
void CStreamSwitcherFilter::SelectInput(CStreamSwitcherInputPin* pInput)
{
// make sure no input thinks it is active
m_pInput = nullptr;
// release blocked GetBuffer in our own allocator & block all Receive
POSITION pos = m_pInputs.GetHeadPosition();
while (pos) {
CStreamSwitcherInputPin* pPin = m_pInputs.GetNext(pos);
pPin->Block(false);
// a few Receive calls can arrive here, but since m_pInput == nullptr neighter of them gets delivered
pPin->Block(true);
}
// this will let waiting GetBuffer() calls go on inside our Receive()
if (m_pOutput) {
m_pOutput->DeliverBeginFlush();
m_pOutput->DeliverEndFlush();
}
if (!pInput) {
return;
}
// set new input
m_pInput = pInput;
// let it go
m_pInput->Block(false);
}
//
HRESULT CStreamSwitcherFilter::Transform(IMediaSample* pIn, IMediaSample* pOut)
{
BYTE* pDataIn = nullptr;
BYTE* pDataOut = nullptr;
HRESULT hr;
if (FAILED(hr = pIn->GetPointer(&pDataIn))) {
return hr;
}
if (FAILED(hr = pOut->GetPointer(&pDataOut))) {
return hr;
}
long len = pIn->GetActualDataLength();
long size = pOut->GetSize();
if (!pDataIn || !pDataOut /*|| len > size || len <= 0*/) {
return S_FALSE; // FIXME
}
memcpy(pDataOut, pDataIn, std::min(len, size));
pOut->SetActualDataLength(std::min(len, size));
return S_OK;
}
CMediaType CStreamSwitcherFilter::CreateNewOutputMediaType(CMediaType mt, long& cbBuffer)
{
return mt;
}
HRESULT CStreamSwitcherFilter::DeliverEndOfStream()
{
return m_pOutput ? m_pOutput->DeliverEndOfStream() : E_FAIL;
}
HRESULT CStreamSwitcherFilter::DeliverBeginFlush()
{
return m_pOutput ? m_pOutput->DeliverBeginFlush() : E_FAIL;
}
HRESULT CStreamSwitcherFilter::DeliverEndFlush()
{
return m_pOutput ? m_pOutput->DeliverEndFlush() : E_FAIL;
}
HRESULT CStreamSwitcherFilter::DeliverNewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate)
{
return m_pOutput ? m_pOutput->DeliverNewSegment(tStart, tStop, dRate) : E_FAIL;
}
// IAMStreamSelect
STDMETHODIMP CStreamSwitcherFilter::Count(DWORD* pcStreams)
{
CheckPointer(pcStreams, E_POINTER);
CAutoLock cAutoLock(&m_csPins);
*pcStreams = 0;
POSITION pos = m_pInputs.GetHeadPosition();
while (pos) {
CStreamSwitcherInputPin* pInputPin = m_pInputs.GetNext(pos);
if (pInputPin->IsConnected()) {
if (CComPtr pSSF = pInputPin->GetStreamSelectionFilter()) {
DWORD cStreams = 0;
HRESULT hr = pSSF->Count(&cStreams);
if (SUCCEEDED(hr)) {
for (int i = 0; i < (int)cStreams; i++) {
AM_MEDIA_TYPE* pmt = nullptr;
hr = pSSF->Info(i, &pmt, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);
if (SUCCEEDED(hr) && pmt && pmt->majortype == MEDIATYPE_Audio) {
(*pcStreams)++;
}
DeleteMediaType(pmt);
}
}
} else {
(*pcStreams)++;
}
}
}
return S_OK;
}
// pdwGroup value is set to:
// - 0 if the track isn't controlled by any underlying IAMStreamSelect interface
// - 1 if the track is controlled by an underlying IAMStreamSelect interface and is not selected at that level
// - 2 if the track is controlled by an underlying IAMStreamSelect interface and is selected at that level
STDMETHODIMP CStreamSwitcherFilter::Info(long lIndex, AM_MEDIA_TYPE** ppmt, DWORD* pdwFlags, LCID* plcid, DWORD* pdwGroup, WCHAR** ppszName, IUnknown** ppObject, IUnknown** ppUnk)
{
CAutoLock cAutoLock(&m_csPins);
IUnknown* pObject = nullptr;
bool bFound = false;
POSITION pos = m_pInputs.GetHeadPosition();
while (pos && !bFound) {
CStreamSwitcherInputPin* pInputPin = m_pInputs.GetNext(pos);
if (pInputPin->IsConnected()) {
if (CComPtr pSSF = pInputPin->GetStreamSelectionFilter()) {
DWORD cStreams = 0;
HRESULT hr = pSSF->Count(&cStreams);
if (SUCCEEDED(hr)) {
for (int i = 0; i < (int)cStreams; i++) {
AM_MEDIA_TYPE* pmt = nullptr;
DWORD dwFlags;
LPWSTR pszName = nullptr;
hr = pSSF->Info(i, &pmt, &dwFlags, plcid, nullptr, &pszName, nullptr, nullptr);
if (SUCCEEDED(hr) && pmt && pmt->majortype == MEDIATYPE_Audio) {
if (lIndex == 0) {
bFound = true;
pObject = pSSF;
if (ppmt) {
*ppmt = pmt;
} else {
DeleteMediaType(pmt);
}
if (pdwFlags) {
*pdwFlags = (m_pInput == pInputPin) ? dwFlags : 0;
}
if (pdwGroup) {
*pdwGroup = (dwFlags & (AMSTREAMSELECTINFO_ENABLED | AMSTREAMSELECTINFO_EXCLUSIVE)) ? 2 : 1;
}
if (ppszName) {
*ppszName = pszName;
} else {
CoTaskMemFree(pszName);
}
break;
} else {
lIndex--;
}
}
DeleteMediaType(pmt);
CoTaskMemFree(pszName);
}
}
} else if (lIndex == 0) {
bFound = true;
if (ppmt) {
*ppmt = CreateMediaType(&m_pOutput->CurrentMediaType());
}
if (pdwFlags) {
*pdwFlags = (m_pInput == pInputPin) ? AMSTREAMSELECTINFO_EXCLUSIVE : 0;
}
if (plcid) {
*plcid = 0;
}
if (pdwGroup) {
*pdwGroup = 0;
}
if (ppszName) {
*ppszName = (WCHAR*)CoTaskMemAlloc((wcslen(pInputPin->Name()) + 1) * sizeof(WCHAR));
if (*ppszName) {
wcscpy_s(*ppszName, wcslen(pInputPin->Name()) + 1, pInputPin->Name());
}
}
} else {
lIndex--;
}
}
}
if (!bFound) {
return E_INVALIDARG;
}
if (ppObject) {
*ppObject = pObject;
if (pObject) {
pObject->AddRef();
}
}
if (ppUnk) {
*ppUnk = nullptr;
}
return S_OK;
}
STDMETHODIMP CStreamSwitcherFilter::Enable(long lIndex, DWORD dwFlags)
{
if (dwFlags != AMSTREAMSELECTENABLE_ENABLE) {
return E_NOTIMPL;
}
PauseGraph;
bool bFound = false;
int i = 0;
CStreamSwitcherInputPin* pNewInputPin = nullptr;
POSITION pos = m_pInputs.GetHeadPosition();
while (pos && !bFound) {
pNewInputPin = m_pInputs.GetNext(pos);
if (pNewInputPin->IsConnected()) {
if (CComPtr pSSF = pNewInputPin->GetStreamSelectionFilter()) {
DWORD cStreams = 0;
HRESULT hr = pSSF->Count(&cStreams);
if (SUCCEEDED(hr)) {
for (i = 0; i < (int)cStreams; i++) {
AM_MEDIA_TYPE* pmt = nullptr;
hr = pSSF->Info(i, &pmt, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);
if (SUCCEEDED(hr) && pmt && pmt->majortype == MEDIATYPE_Audio) {
if (lIndex == 0) {
bFound = true;
DeleteMediaType(pmt);
break;
} else {
lIndex--;
}
}
DeleteMediaType(pmt);
}
}
} else if (lIndex == 0) {
bFound = true;
} else {
lIndex--;
}
}
}
if (!bFound) {
return E_INVALIDARG;
}
SelectInput(pNewInputPin);
if (CComPtr pSSF = pNewInputPin->GetStreamSelectionFilter()) {
pSSF->Enable(i, dwFlags);
}
ResumeGraph;
return S_OK;
}