- Main Page
- Namespaces
- Classes
- Files
- Directories

- File List
- File Members

# StreamSwitcher.cpp

    00001 // Copyright 2003 Gabest.
    00002 // http://www.gabest.org
    00003 //
    00004 // This program is free software; you can redistribute it and/or modify
    00005 // it under the terms of the GNU General Public License as published by
    00006 // the Free Software Foundation; either version 2 of the License, or
    00007 // (at your option) any later version.
    00008 //
    00009 // This program is distributed in the hope that it will be useful,
    00010 // but WITHOUT ANY WARRANTY; without even the implied warranty of
    00011 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    00012 // GNU General Public License for more details.
    00013 //
    00014 // You should have received a copy of the GNU General Public License
    00015 // along with this program; if not, write to the Free Software
    00016 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA, or visit
    00017 // http://www.gnu.org/copyleft/gpl.html
    00018
    00019 #include "StdAfx.h"
    00020 #include "StreamSwitcher.h"
    00021
    00022 #include "Shlwapi.h"
    00023 #include <atlpath.h>
    00024 #include <mmreg.h>
    00025 #include <ks.h>
    00026 #include <ksmedia.h>
    00027 #include "AudioSwitcher.h"
    00028 #include "Audio.h"
    00029 #include "..\..\..\DSUtil\DSUtil.h"
    00030
    00031 #include <initguid.h>
    00032 #include "..\..\..\..\include\Ogg\OggDS.h"
    00033
    00034 #define BLOCKSTREAM
    00035
    00036 //
    00037 // CStreamSwitcherPassThru
    00038 //
    00039
    00040 CStreamSwitcherPassThru::CStreamSwitcherPassThru(LPUNKNOWN pUnk, HRESULT* phr, CStreamSwitcherFilter* pFilter)
    00041         : CMediaPosition(NAME("CStreamSwitcherPassThru"), pUnk)
    00042         , m_pFilter(pFilter)
    00043 {
    00044 }
    00045
    00046 STDMETHODIMP CStreamSwitcherPassThru::NonDelegatingQueryInterface(REFIID riid, void** ppv)
    00047 {
    00048     CheckPointer(ppv, E_POINTER);
    00049     *ppv = NULL;
    00050
    00051     return
    00052                 QI(IMediaSeeking)
    00053                 CMediaPosition::NonDelegatingQueryInterface(riid, ppv);
    00054 }
    00055
    00056 template<class T>
    00057 HRESULT GetPeer(CStreamSwitcherFilter* pFilter, T** ppT)
    00058 {
    00059     *ppT = NULL;
    00060
    00061         CBasePin* pPin = pFilter->GetInputPin();
    00062         if(!pPin) return E_NOTIMPL;
    00063
    00064     CComPtr<IPin> pConnected;
    00065     if(FAILED(pPin->ConnectedTo(&pConnected)))
    00066                 return E_NOTIMPL;
    00067
    00068         if(CComQIPtr<T> pT = pConnected)
    00069         {
    00070                 *ppT = pT.Detach();
    00071                 return S_OK;
    00072         }
    00073
    00074         return E_NOTIMPL;
    00075 }
    00076
    00077 #define CallPeerSeeking(call) \
    00078         CComPtr<IMediaSeeking> pMS; \
    00079         if(FAILED(GetPeer(m_pFilter, &pMS))) return E_NOTIMPL; \
    00080         return pMS->##call; \
    00081
    00082 #define CallPeer(call) \
    00083         CComPtr<IMediaPosition> pMP; \
    00084         if(FAILED(GetPeer(m_pFilter, &pMP))) return E_NOTIMPL; \
    00085         return pMP->##call; \
    00086
    00087 #define CallPeerSeekingAll(call) \
    00088         HRESULT hr = E_NOTIMPL; \
    00089         POSITION pos = m_pFilter->m_pInputs.GetHeadPosition(); \
    00090         while(pos) \
    00091         { \
    00092                 CBasePin* pPin = m_pFilter->m_pInputs.GetNext(pos); \
    00093                 CComPtr<IPin> pConnected; \
    00094             if(FAILED(pPin->ConnectedTo(&pConnected))) \
    00095                         continue; \
    00096                 if(CComQIPtr<IMediaSeeking> pMS = pConnected) \
    00097                 { \
    00098                         HRESULT hr2 = pMS->call; \
    00099                         if(pPin == m_pFilter->GetInputPin()) \
    00100                                 hr = hr2; \
    00101                 } \
    00102         } \
    00103         return hr; \
    00104
    00105 #define CallPeerAll(call) \
    00106         HRESULT hr = E_NOTIMPL; \
    00107         POSITION pos = m_pFilter->m_pInputs.GetHeadPosition(); \
    00108         while(pos) \
    00109         { \
    00110                 CBasePin* pPin = m_pFilter->m_pInputs.GetNext(pos); \
    00111                 CComPtr<IPin> pConnected; \
    00112             if(FAILED(pPin->ConnectedTo(&pConnected))) \
    00113                         continue; \
    00114                 if(CComQIPtr<IMediaPosition> pMP = pConnected) \
    00115                 { \
    00116                         HRESULT hr2 = pMP->call; \
    00117                         if(pPin == m_pFilter->GetInputPin()) \
    00118                                 hr = hr2; \
    00119                 } \
    00120         } \
    00121         return hr; \
    00122
    00123
    00124 // IMediaSeeking
    00125
    00126 STDMETHODIMP CStreamSwitcherPassThru::GetCapabilities(DWORD* pCaps)
    00127         {CallPeerSeeking(GetCapabilities(pCaps));}
    00128 STDMETHODIMP CStreamSwitcherPassThru::CheckCapabilities(DWORD* pCaps)
    00129         {CallPeerSeeking(CheckCapabilities(pCaps));}
    00130 STDMETHODIMP CStreamSwitcherPassThru::IsFormatSupported(const GUID* pFormat)
    00131         {CallPeerSeeking(IsFormatSupported(pFormat));}
    00132 STDMETHODIMP CStreamSwitcherPassThru::QueryPreferredFormat(GUID* pFormat)
    00133         {CallPeerSeeking(QueryPreferredFormat(pFormat));}
    00134 STDMETHODIMP CStreamSwitcherPassThru::SetTimeFormat(const GUID* pFormat)
    00135         {CallPeerSeeking(SetTimeFormat(pFormat));}
    00136 STDMETHODIMP CStreamSwitcherPassThru::GetTimeFormat(GUID* pFormat)
    00137         {CallPeerSeeking(GetTimeFormat(pFormat));}
    00138 STDMETHODIMP CStreamSwitcherPassThru::IsUsingTimeFormat(const GUID* pFormat)
    00139         {CallPeerSeeking(IsUsingTimeFormat(pFormat));}
    00140 STDMETHODIMP CStreamSwitcherPassThru::ConvertTimeFormat(LONGLONG* pTarget, const GUID* pTargetFormat, LONGLONG Source, const GUID* pSourceFormat)
    00141         {CallPeerSeeking(ConvertTimeFormat(pTarget, pTargetFormat, Source, pSourceFormat));}
    00142 STDMETHODIMP CStreamSwitcherPassThru::SetPositions(LONGLONG* pCurrent, DWORD CurrentFlags, LONGLONG* pStop, DWORD StopFlags)
    00143         {CallPeerSeekingAll(SetPositions(pCurrent, CurrentFlags, pStop, StopFlags));}
    00144 STDMETHODIMP CStreamSwitcherPassThru::GetPositions(LONGLONG* pCurrent, LONGLONG* pStop)
    00145         {CallPeerSeeking(GetPositions(pCurrent, pStop));}
    00146 STDMETHODIMP CStreamSwitcherPassThru::GetCurrentPosition(LONGLONG* pCurrent)
    00147         {CallPeerSeeking(GetCurrentPosition(pCurrent));}
    00148 STDMETHODIMP CStreamSwitcherPassThru::GetStopPosition(LONGLONG* pStop)
    00149         {CallPeerSeeking(GetStopPosition(pStop));}
    00150 STDMETHODIMP CStreamSwitcherPassThru::GetDuration(LONGLONG* pDuration)
    00151         {CallPeerSeeking(GetDuration(pDuration));}
    00152 STDMETHODIMP CStreamSwitcherPassThru::GetPreroll(LONGLONG* pllPreroll)
    00153         {CallPeerSeeking(GetPreroll(pllPreroll));}
    00154 STDMETHODIMP CStreamSwitcherPassThru::GetAvailable(LONGLONG* pEarliest, LONGLONG* pLatest)
    00155         {CallPeerSeeking(GetAvailable(pEarliest, pLatest));}
    00156 STDMETHODIMP CStreamSwitcherPassThru::GetRate(double* pdRate)
    00157         {CallPeerSeeking(GetRate(pdRate));}
    00158 STDMETHODIMP CStreamSwitcherPassThru::SetRate(double dRate)
    00159         {if(0.0 == dRate) return E_INVALIDARG;
    00160         CallPeerSeekingAll(SetRate(dRate));}
    00161
    00162 // IMediaPosition
    00163
    00164 STDMETHODIMP CStreamSwitcherPassThru::get_Duration(REFTIME* plength)
    00165         {CallPeer(get_Duration(plength));}
    00166 STDMETHODIMP CStreamSwitcherPassThru::get_CurrentPosition(REFTIME* pllTime)
    00167         {CallPeer(get_CurrentPosition(pllTime));}
    00168 STDMETHODIMP CStreamSwitcherPassThru::put_CurrentPosition(REFTIME llTime)
    00169         {CallPeerAll(put_CurrentPosition(llTime));}
    00170 STDMETHODIMP CStreamSwitcherPassThru::get_StopTime(REFTIME* pllTime)
    00171         {CallPeer(get_StopTime(pllTime));}
    00172 STDMETHODIMP CStreamSwitcherPassThru::put_StopTime(REFTIME llTime)
    00173         {CallPeerAll(put_StopTime(llTime));}
    00174 STDMETHODIMP CStreamSwitcherPassThru::get_PrerollTime(REFTIME * pllTime)
    00175         {CallPeer(get_PrerollTime(pllTime));}
    00176 STDMETHODIMP CStreamSwitcherPassThru::put_PrerollTime(REFTIME llTime)
    00177         {CallPeerAll(put_PrerollTime(llTime));}
    00178 STDMETHODIMP CStreamSwitcherPassThru::get_Rate(double* pdRate)
    00179         {CallPeer(get_Rate(pdRate));}
    00180 STDMETHODIMP CStreamSwitcherPassThru::put_Rate(double dRate)
    00181         {if(0.0 == dRate) return E_INVALIDARG;
    00182         CallPeerAll(put_Rate(dRate));}
    00183 STDMETHODIMP CStreamSwitcherPassThru::CanSeekForward(LONG* pCanSeekForward)
    00184         {CallPeer(CanSeekForward(pCanSeekForward));}
    00185 STDMETHODIMP CStreamSwitcherPassThru::CanSeekBackward(LONG* pCanSeekBackward)
    00186         {CallPeer(CanSeekBackward(pCanSeekBackward));}
    00187
    00188 //
    00189 // CStreamSwitcherAllocator
    00190 //
    00191
    00192 CStreamSwitcherAllocator::CStreamSwitcherAllocator(CStreamSwitcherInputPin* pPin, HRESULT* phr)
    00193         : CMemAllocator(NAME("CStreamSwitcherAllocator"), NULL, phr)
    00194         , m_pPin(pPin)
    00195         , m_fMediaTypeChanged(false)
    00196 {
    00197         ASSERT(phr);
    00198         ASSERT(pPin);
    00199 }
    00200
    00201 #ifdef DEBUG
    00202 CStreamSwitcherAllocator::~CStreamSwitcherAllocator()
    00203 {
    00204     ASSERT(m_bCommitted == FALSE);
    00205 }
    00206 #endif
    00207
    00208 STDMETHODIMP_(ULONG) CStreamSwitcherAllocator::NonDelegatingAddRef()
    00209 {
    00210         return m_pPin->m_pFilter->AddRef();
    00211 }
    00212
    00213 STDMETHODIMP_(ULONG) CStreamSwitcherAllocator::NonDelegatingRelease()
    00214 {
    00215         return m_pPin->m_pFilter->Release();
    00216 }
    00217
    00218 STDMETHODIMP CStreamSwitcherAllocator::GetBuffer(
    00219         IMediaSample** ppBuffer,
    00220         REFERENCE_TIME* pStartTime, REFERENCE_TIME* pEndTime,
    00221         DWORD dwFlags)
    00222 {
    00223         HRESULT hr = VFW_E_NOT_COMMITTED;
    00224
    00225         if(!m_bCommitted)
    00226         return hr;
    00227 /*
    00228 TRACE(_T("CStreamSwitcherAllocator::GetBuffer m_pPin->m_evBlock.Wait() + %x\n"), this);
    00229         m_pPin->m_evBlock.Wait();
    00230 TRACE(_T("CStreamSwitcherAllocator::GetBuffer m_pPin->m_evBlock.Wait() - %x\n"), this);
    00231 */
    00232         if(m_fMediaTypeChanged)
    00233         {
    00234                 if(!m_pPin || !m_pPin->m_pFilter)
    00235                         return hr;
    00236
    00237                 CStreamSwitcherOutputPin* pOut = ((CStreamSwitcherFilter*)m_pPin->m_pFilter)->GetOutputPin();
    00238                 if(!pOut || !pOut->CurrentAllocator())
    00239                         return hr;
    00240
    00241                 ALLOCATOR_PROPERTIES Properties, Actual;
    00242                 if(FAILED(pOut->CurrentAllocator()->GetProperties(&Actual)))
    00243                         return hr;
    00244                 if(FAILED(GetProperties(&Properties)))
    00245                         return hr;
    00246
    00247                 if(!m_bCommitted || Properties.cbBuffer < Actual.cbBuffer)
    00248                 {
    00249                         Properties.cbBuffer = Actual.cbBuffer;
    00250                         if(FAILED(Decommit())) return hr;
    00251                         if(FAILED(SetProperties(&Properties, &Actual))) return hr;
    00252                         if(FAILED(Commit())) return hr;
    00253                         ASSERT(Actual.cbBuffer >= Properties.cbBuffer);
    00254                         if(Actual.cbBuffer < Properties.cbBuffer) return hr;
    00255                 }
    00256         }
    00257
    00258         hr = CMemAllocator::GetBuffer(ppBuffer, pStartTime, pEndTime, dwFlags);
    00259
    00260         if(m_fMediaTypeChanged && SUCCEEDED(hr))
    00261         {
    00262                 (*ppBuffer)->SetMediaType(&m_mt);
    00263                 m_fMediaTypeChanged = false;
    00264         }
    00265
    00266         return hr;
    00267 }
    00268
    00269 void CStreamSwitcherAllocator::NotifyMediaType(const CMediaType& mt)
    00270 {
    00271         CopyMediaType(&m_mt, &mt);
    00272         m_fMediaTypeChanged = true;
    00273 }
    00274
    00275
    00276 //
    00277 // CStreamSwitcherInputPin
    00278 //
    00279
    00280 CStreamSwitcherInputPin::CStreamSwitcherInputPin(CStreamSwitcherFilter* pFilter, HRESULT* phr, LPCWSTR pName)
    00281     : CBaseInputPin(NAME("CStreamSwitcherInputPin"), pFilter, &pFilter->m_csState, phr, pName)
    00282         , m_Allocator(this, phr)
    00283         , m_bSampleSkipped(FALSE)
    00284         , m_bQualityChanged(FALSE)
    00285         , m_bUsingOwnAllocator(FALSE)
    00286         , m_evBlock(TRUE)
    00287         , m_fCanBlock(false)
    00288         , m_hNotifyEvent(NULL)
    00289 {
    00290         m_bCanReconnectWhenActive = TRUE;
    00291 }
    00292
    00293 [uuid("138130AF-A79B-45D5-B4AA-87697457BA87")]
    00294 class NeroAudioDecoder {};
    00295
    00296 STDMETHODIMP CStreamSwitcherInputPin::NonDelegatingQueryInterface(REFIID riid, void** ppv)
    00297 {
    00298         return
    00299                 QI(IStreamSwitcherInputPin)
    00300                 IsConnected() && GetCLSID(GetFilterFromPin(GetConnected())) == __uuidof(NeroAudioDecoder) && QI(IPinConnection)
    00301                 __super::NonDelegatingQueryInterface(riid, ppv);
    00302 }
    00303
    00304 // IPinConnection
    00305
    00306 STDMETHODIMP CStreamSwitcherInputPin::DynamicQueryAccept(const AM_MEDIA_TYPE* pmt)
    00307 {
    00308         return QueryAccept(pmt);
    00309 }
    00310
    00311 STDMETHODIMP CStreamSwitcherInputPin::NotifyEndOfStream(HANDLE hNotifyEvent)
    00312 {
    00313         if(m_hNotifyEvent) SetEvent(m_hNotifyEvent);
    00314         m_hNotifyEvent = hNotifyEvent;
    00315         return S_OK;
    00316 }
    00317
    00318 STDMETHODIMP CStreamSwitcherInputPin::IsEndPin()
    00319 {
    00320         return S_OK;
    00321 }
    00322
    00323 STDMETHODIMP CStreamSwitcherInputPin::DynamicDisconnect()
    00324 {
    00325         CAutoLock cAutoLock(&m_csReceive);
    00326         Disconnect();
    00327         return S_OK;
    00328 }
    00329
    00330 // IStreamSwitcherInputPin
    00331
    00332 STDMETHODIMP_(bool) CStreamSwitcherInputPin::IsActive()
    00333 {
    00334         // TODO: lock onto something here
    00335         return(this == ((CStreamSwitcherFilter*)m_pFilter)->GetInputPin());
    00336 }
    00337
    00338 //
    00339
    00340 HRESULT CStreamSwitcherInputPin::QueryAcceptDownstream(const AM_MEDIA_TYPE* pmt)
    00341 {
    00342         HRESULT hr = S_OK;
    00343
    00344         CStreamSwitcherOutputPin* pOut = ((CStreamSwitcherFilter*)m_pFilter)->GetOutputPin();
    00345
    00346         if(pOut && pOut->IsConnected())
    00347         {
    00348                 if(CComPtr<IPinConnection> pPC = pOut->CurrentPinConnection())
    00349                 {
    00350                         hr = pPC->DynamicQueryAccept(pmt);
    00351                         if(hr == S_OK) return S_OK;
    00352                 }
    00353
    00354                 hr = pOut->GetConnected()->QueryAccept(pmt);
    00355         }
    00356
    00357         return hr;
    00358 }
    00359
    00360 void CStreamSwitcherInputPin::Block(bool fBlock)
    00361 {
    00362         if(fBlock) m_evBlock.Reset();
    00363         else m_evBlock.Set();
    00364 }
    00365
    00366 HRESULT CStreamSwitcherInputPin::InitializeOutputSample(IMediaSample* pInSample, IMediaSample** ppOutSample)
    00367 {
    00368         if(!pInSample || !ppOutSample)
    00369                 return E_POINTER;
    00370
    00371         CStreamSwitcherOutputPin* pOut = ((CStreamSwitcherFilter*)m_pFilter)->GetOutputPin();
    00372         ASSERT(pOut->GetConnected());
    00373
    00374     CComPtr<IMediaSample> pOutSample;
    00375
    00376         DWORD dwFlags = m_bSampleSkipped ? AM_GBF_PREVFRAMESKIPPED : 0;
    00377
    00378     if(!(m_SampleProps.dwSampleFlags & AM_SAMPLE_SPLICEPOINT))
    00379                 dwFlags |= AM_GBF_NOTASYNCPOINT;
    00380
    00381         HRESULT hr = pOut->GetDeliveryBuffer(&pOutSample
    00382         , m_SampleProps.dwSampleFlags & AM_SAMPLE_TIMEVALID ? &m_SampleProps.tStart : NULL
    00383         , m_SampleProps.dwSampleFlags & AM_SAMPLE_STOPVALID ? &m_SampleProps.tStop : NULL
    00384         , dwFlags);
    00385
    00386     if(FAILED(hr))
    00387                 return hr;
    00388
    00389         if(!pOutSample)
    00390                 return E_FAIL;
    00391
    00392     if(CComQIPtr<IMediaSample2> pOutSample2 = pOutSample)
    00393         {
    00394         AM_SAMPLE2_PROPERTIES OutProps;
    00395                 EXECUTE_ASSERT(SUCCEEDED(pOutSample2->GetProperties(FIELD_OFFSET(AM_SAMPLE2_PROPERTIES, tStart), (PBYTE)&OutProps)));
    00396         OutProps.dwTypeSpecificFlags = m_SampleProps.dwTypeSpecificFlags;
    00397         OutProps.dwSampleFlags =
    00398             (OutProps.dwSampleFlags & AM_SAMPLE_TYPECHANGED) |
    00399             (m_SampleProps.dwSampleFlags & ~AM_SAMPLE_TYPECHANGED);
    00400
    00401         OutProps.tStart = m_SampleProps.tStart;
    00402         OutProps.tStop  = m_SampleProps.tStop;
    00403         OutProps.cbData = FIELD_OFFSET(AM_SAMPLE2_PROPERTIES, dwStreamId);
    00404
    00405         hr = pOutSample2->SetProperties(FIELD_OFFSET(AM_SAMPLE2_PROPERTIES, dwStreamId), (PBYTE)&OutProps);
    00406         if(m_SampleProps.dwSampleFlags & AM_SAMPLE_DATADISCONTINUITY)
    00407                         m_bSampleSkipped = FALSE;
    00408     }
    00409     else
    00410         {
    00411         if(m_SampleProps.dwSampleFlags & AM_SAMPLE_TIMEVALID)
    00412                         pOutSample->SetTime(&m_SampleProps.tStart, &m_SampleProps.tStop);
    00413
    00414                 if(m_SampleProps.dwSampleFlags & AM_SAMPLE_SPLICEPOINT)
    00415                         pOutSample->SetSyncPoint(TRUE);
    00416
    00417                 if(m_SampleProps.dwSampleFlags & AM_SAMPLE_DATADISCONTINUITY)
    00418                 {
    00419                         pOutSample->SetDiscontinuity(TRUE);
    00420             m_bSampleSkipped = FALSE;
    00421         }
    00422
    00423                 LONGLONG MediaStart, MediaEnd;
    00424         if(pInSample->GetMediaTime(&MediaStart, &MediaEnd) == NOERROR)
    00425                         pOutSample->SetMediaTime(&MediaStart, &MediaEnd);
    00426     }
    00427
    00428         *ppOutSample = pOutSample.Detach();
    00429
    00430         return S_OK;
    00431 }
    00432
    00433 // pure virtual
    00434
    00435 HRESULT CStreamSwitcherInputPin::CheckMediaType(const CMediaType* pmt)
    00436 {
    00437         return ((CStreamSwitcherFilter*)m_pFilter)->CheckMediaType(pmt);
    00438 }
    00439
    00440 // virtual
    00441
    00442 HRESULT CStreamSwitcherInputPin::CheckConnect(IPin* pPin)
    00443 {
    00444         return (IPin*)((CStreamSwitcherFilter*)m_pFilter)->GetOutputPin() == pPin
    00445                 ? E_FAIL
    00446                 : __super::CheckConnect(pPin);
    00447 }
    00448
    00449 HRESULT CStreamSwitcherInputPin::CompleteConnect(IPin* pReceivePin)
    00450 {
    00451         HRESULT hr = __super::CompleteConnect(pReceivePin);
    00452         if(FAILED(hr)) return hr;
    00453
    00454     ((CStreamSwitcherFilter*)m_pFilter)->CompleteConnect(PINDIR_INPUT, this, pReceivePin);
    00455
    00456         m_fCanBlock = false;
    00457         bool fForkedSomewhere = false;
    00458
    00459         CStringW fileName;
    00460         CStringW pinName;
    00461
    00462     IPin* pPin = (IPin*)this;
    00463         IBaseFilter* pBF = (IBaseFilter*)m_pFilter;
    00464
    00465         while((pPin = GetUpStreamPin(pBF, pPin)) && (pBF = GetFilterFromPin(pPin)))
    00466         {
    00467                 if(IsSplitter(pBF))
    00468                 {
    00469                         pinName = GetPinName(pPin);
    00470                 }
    00471
    00472                 CLSID clsid = GetCLSID(pBF);
    00473                 if(clsid == CLSID_AviSplitter || clsid == CLSID_OggSplitter)
    00474                         m_fCanBlock = true;
    00475
    00476                 int nIn, nOut, nInC, nOutC;
    00477                 CountPins(pBF, nIn, nOut, nInC, nOutC);
    00478                 fForkedSomewhere = fForkedSomewhere || nIn > 1 || nOut > 1;
    00479
    00480                 if(CComQIPtr<IFileSourceFilter> pFSF = pBF)
    00481                 {
    00482                         WCHAR* pszName = NULL;
    00483                         AM_MEDIA_TYPE mt;
    00484                         if(SUCCEEDED(pFSF->GetCurFile(&pszName, &mt)) && pszName)
    00485                         {
    00486                                 fileName = pszName;
    00487                                 CoTaskMemFree(pszName);
    00488
    00489                                 fileName.Replace('\\', '/');
    00490                                 CStringW fn = fileName.Mid(fileName.ReverseFind('/')+1);
    00491                                 if(!fn.IsEmpty()) fileName = fn;
    00492
    00493                                 if(!pinName.IsEmpty()) fileName += L" / " + pinName;
    00494
    00495                                 WCHAR* pName = new WCHAR[fileName.GetLength()+1];
    00496                                 if(pName)
    00497                                 {
    00498                                         wcscpy(pName, fileName);
    00499                                         if(m_pName) delete [] m_pName;
    00500                                         m_pName = pName;
    00501                                 }
    00502                         }
    00503
    00504                         break;
    00505                 }
    00506
    00507                 pPin = GetFirstPin(pBF);
    00508         }
    00509
    00510         if(!fForkedSomewhere)
    00511                 m_fCanBlock = true;
    00512
    00513         m_hNotifyEvent = NULL;
    00514
    00515         return S_OK;
    00516 }
    00517
    00518 HRESULT CStreamSwitcherInputPin::Active()
    00519 {
    00520         Block(!IsActive());
    00521
    00522         return __super::Active();
    00523 }
    00524
    00525 HRESULT CStreamSwitcherInputPin::Inactive()
    00526 {
    00527         Block(false);
    00528
    00529         return __super::Inactive();
    00530 }
    00531
    00532 // IPin
    00533
    00534 STDMETHODIMP CStreamSwitcherInputPin::QueryAccept(const AM_MEDIA_TYPE* pmt)
    00535 {
    00536         HRESULT hr = __super::QueryAccept(pmt);
    00537         if(S_OK != hr) return hr;
    00538
    00539         return QueryAcceptDownstream(pmt);
    00540 }
    00541
    00542 STDMETHODIMP CStreamSwitcherInputPin::ReceiveConnection(IPin* pConnector, const AM_MEDIA_TYPE* pmt)
    00543 {
    00544         // FIXME: this locked up once
    00545 //    CAutoLock cAutoLock(&((CStreamSwitcherFilter*)m_pFilter)->m_csReceive);
    00546
    00547         HRESULT hr;
    00548         if(S_OK != (hr = QueryAcceptDownstream(pmt)))
    00549                 return VFW_E_TYPE_NOT_ACCEPTED;
    00550
    00551         if(m_Connected)
    00552                 m_Connected->Release(), m_Connected = NULL;
    00553
    00554         return SUCCEEDED(__super::ReceiveConnection(pConnector, pmt)) ? S_OK : E_FAIL;
    00555 }
    00556
    00557 STDMETHODIMP CStreamSwitcherInputPin::GetAllocator(IMemAllocator** ppAllocator)
    00558 {
    00559     CheckPointer(ppAllocator, E_POINTER);
    00560
    00561     if(m_pAllocator == NULL)
    00562         {
    00563         (m_pAllocator = &m_Allocator)->AddRef();
    00564     }
    00565
    00566     (*ppAllocator = m_pAllocator)->AddRef();
    00567
    00568     return NOERROR;
    00569 }
    00570
    00571 STDMETHODIMP CStreamSwitcherInputPin::NotifyAllocator(IMemAllocator* pAllocator, BOOL bReadOnly)
    00572 {
    00573         HRESULT hr = __super::NotifyAllocator(pAllocator, bReadOnly);
    00574         if(FAILED(hr)) return hr;
    00575
    00576         m_bUsingOwnAllocator = (pAllocator == (IMemAllocator*)&m_Allocator);
    00577
    00578         return S_OK;
    00579 }
    00580
    00581 STDMETHODIMP CStreamSwitcherInputPin::BeginFlush()
    00582 {
    00583     CAutoLock cAutoLock(&((CStreamSwitcherFilter*)m_pFilter)->m_csState);
    00584
    00585         CStreamSwitcherOutputPin* pOut = ((CStreamSwitcherFilter*)m_pFilter)->GetOutputPin();
    00586     if(!IsConnected() || !pOut || !pOut->IsConnected())
    00587                 return VFW_E_NOT_CONNECTED;
    00588
    00589         HRESULT hr = __super::BeginFlush();
    00590     if(FAILED(hr))
    00591                 return hr;
    00592
    00593         return IsActive() ? pOut->DeliverBeginFlush() : Block(false), S_OK;
    00594 }
    00595
    00596 STDMETHODIMP CStreamSwitcherInputPin::EndFlush()
    00597 {
    00598         CAutoLock cAutoLock(&((CStreamSwitcherFilter*)m_pFilter)->m_csState);
    00599
    00600         CStreamSwitcherOutputPin* pOut = ((CStreamSwitcherFilter*)m_pFilter)->GetOutputPin();
    00601     if(!IsConnected() || !pOut || !pOut->IsConnected())
    00602                 return VFW_E_NOT_CONNECTED;
    00603
    00604         HRESULT hr = __super::EndFlush();
    00605     if(FAILED(hr))
    00606                 return hr;
    00607
    00608         return IsActive() ? pOut->DeliverEndFlush() : Block(true), S_OK;
    00609 }
    00610
    00611 STDMETHODIMP CStreamSwitcherInputPin::EndOfStream()
    00612 {
    00613     CAutoLock cAutoLock(&m_csReceive);
    00614
    00615         CStreamSwitcherOutputPin* pOut = ((CStreamSwitcherFilter*)m_pFilter)->GetOutputPin();
    00616         if(!IsConnected() || !pOut || !pOut->IsConnected())
    00617                 return VFW_E_NOT_CONNECTED;
    00618
    00619         if(m_hNotifyEvent)
    00620         {
    00621                 SetEvent(m_hNotifyEvent), m_hNotifyEvent = NULL;
    00622                 return S_OK;
    00623         }
    00624
    00625         return IsActive() ? pOut->DeliverEndOfStream() : S_OK;
    00626 }
    00627
    00628 // IMemInputPin
    00629
    00630 STDMETHODIMP CStreamSwitcherInputPin::Receive(IMediaSample* pSample)
    00631 {
    00632         AM_MEDIA_TYPE* pmt = NULL;
    00633         if(SUCCEEDED(pSample->GetMediaType(&pmt)) && pmt)
    00634         {
    00635                 const CMediaType mt(*pmt);
    00636                 DeleteMediaType(pmt), pmt = NULL;
    00637                 SetMediaType(&mt);
    00638         }
    00639
    00640         // DAMN!!!!!! this doesn't work if the stream we are blocking
    00641         // shares the same thread with another stream, mpeg splitters
    00642         // are usually like that. Our nicely built up multithreaded
    00643         // strategy is useless because of this, ARRRRRRGHHHHHH.
    00644
    00645 #ifdef BLOCKSTREAM
    00646         if(m_fCanBlock)
    00647                 m_evBlock.Wait();
    00648 #endif
    00649
    00650         if(!IsActive())
    00651         {
    00652 #ifdef BLOCKSTREAM
    00653                 if(m_fCanBlock)
    00654                         return S_FALSE;
    00655 #endif
    00656
    00657                 TRACE(_T("&^%$#@\n"));
    00658 //Sleep(32);
    00659                 return E_FAIL; // a stupid fix for this stupid problem
    00660         }
    00661
    00662     CAutoLock cAutoLock(&m_csReceive);
    00663
    00664         CStreamSwitcherOutputPin* pOut = ((CStreamSwitcherFilter*)m_pFilter)->GetOutputPin();
    00665         ASSERT(pOut->GetConnected());
    00666
    00667         HRESULT hr = __super::Receive(pSample);
    00668         if(S_OK != hr) return hr;
    00669
    00670         if(m_SampleProps.dwStreamId != AM_STREAM_MEDIA)
    00671         {
    00672                 return pOut->Deliver(pSample);
    00673         }
    00674
    00675         //
    00676
    00677         ALLOCATOR_PROPERTIES props, actual;
    00678         hr = m_pAllocator->GetProperties(&props);
    00679         hr = pOut->CurrentAllocator()->GetProperties(&actual);
    00680
    00681         REFERENCE_TIME rtStart = 0, rtStop = 0;
    00682         if(S_OK == pSample->GetTime(&rtStart, &rtStop))
    00683         {
    00684                 //
    00685         }
    00686
    00687         long cbBuffer = pSample->GetActualDataLength();
    00688
    00689         CMediaType mtOut = m_mt;
    00690         mtOut = ((CStreamSwitcherFilter*)m_pFilter)->CreateNewOutputMediaType(mtOut, cbBuffer);
    00691
    00692         bool fTypeChanged = false;
    00693
    00694         if(mtOut != pOut->CurrentMediaType() || cbBuffer > actual.cbBuffer)
    00695         {
    00696                 fTypeChanged = true;
    00697
    00698                 m_SampleProps.dwSampleFlags |= AM_SAMPLE_TYPECHANGED/*|AM_SAMPLE_DATADISCONTINUITY|AM_SAMPLE_TIMEDISCONTINUITY*/;
    00699
    00700 /*
    00701                 if(CComQIPtr<IPinConnection> pPC = pOut->CurrentPinConnection())
    00702                 {
    00703                         HANDLE hEOS = CreateEvent(NULL, FALSE, FALSE, NULL);
    00704                         hr = pPC->NotifyEndOfStream(hEOS);
    00705                         hr = pOut->DeliverEndOfStream();
    00706                         WaitForSingleObject(hEOS, 3000);
    00707                         CloseHandle(hEOS);
    00708                         hr = pOut->DeliverBeginFlush();
    00709                         hr = pOut->DeliverEndFlush();
    00710                 }
    00711 */
    00712
    00713                 if(props.cBuffers < 8 && mtOut.majortype == MEDIATYPE_Audio)
    00714                         props.cBuffers = 8;
    00715
    00716                 props.cbBuffer = cbBuffer;
    00717
    00718                 if(actual.cbAlign != props.cbAlign
    00719                 || actual.cbPrefix != props.cbPrefix
    00720                 || actual.cBuffers < props.cBuffers
    00721                 || actual.cbBuffer < props.cbBuffer)
    00722                 {
    00723                         hr = pOut->DeliverBeginFlush();
    00724                         hr = pOut->DeliverEndFlush();
    00725                         hr = pOut->CurrentAllocator()->Decommit();
    00726                         hr = pOut->CurrentAllocator()->SetProperties(&props, &actual);
    00727                         hr = pOut->CurrentAllocator()->Commit();
    00728                 }
    00729         }
    00730
    00731         CComPtr<IMediaSample> pOutSample;
    00732         if(FAILED(InitializeOutputSample(pSample, &pOutSample)))
    00733                 return E_FAIL;
    00734
    00735         pmt = NULL;
    00736         if(SUCCEEDED(pOutSample->GetMediaType(&pmt)) && pmt)
    00737         {
    00738                 const CMediaType mt(*pmt);
    00739                 DeleteMediaType(pmt), pmt = NULL;
    00740                 // TODO
    00741                 ASSERT(0);
    00742         }
    00743
    00744         if(fTypeChanged)
    00745         {
    00746                 pOut->SetMediaType(&mtOut);
    00747                 ((CStreamSwitcherFilter*)m_pFilter)->OnNewOutputMediaType(m_mt, mtOut);
    00748                 pOutSample->SetMediaType(&mtOut);
    00749         }
    00750
    00751         // Transform
    00752
    00753         hr = ((CStreamSwitcherFilter*)m_pFilter)->Transform(pSample, pOutSample);
    00754
    00755         //
    00756
    00757     if(S_OK == hr)
    00758         {
    00759                 hr = pOut->Deliver(pOutSample);
    00760         m_bSampleSkipped = FALSE;
    00761 /*
    00762                 if(FAILED(hr))
    00763                 {
    00764                         ASSERT(0);
    00765                 }
    00766 */
    00767         }
    00768     else if(S_FALSE == hr)
    00769         {
    00770                 hr = S_OK;
    00771                 pOutSample = NULL;
    00772                 m_bSampleSkipped = TRUE;
    00773
    00774                 if(!m_bQualityChanged)
    00775                 {
    00776                         m_pFilter->NotifyEvent(EC_QUALITY_CHANGE, 0, 0);
    00777                         m_bQualityChanged = TRUE;
    00778                 }
    00779         }
    00780
    00781         return hr;
    00782 }
    00783
    00784 STDMETHODIMP CStreamSwitcherInputPin::NewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate)
    00785 {
    00786         if(!IsConnected())
    00787                 return S_OK;
    00788
    00789         CAutoLock cAutoLock(&m_csReceive);
    00790
    00791         CStreamSwitcherOutputPin* pOut = ((CStreamSwitcherFilter*)m_pFilter)->GetOutputPin();
    00792     if(!pOut || !pOut->IsConnected())
    00793                 return VFW_E_NOT_CONNECTED;
    00794
    00795         HRESULT hr = pOut->DeliverNewSegment(tStart, tStop, dRate);
    00796
    00797         return hr;
    00798 }
    00799
    00800
    00801 //
    00802 // CStreamSwitcherOutputPin
    00803 //
    00804
    00805 CStreamSwitcherOutputPin::CStreamSwitcherOutputPin(CStreamSwitcherFilter* pFilter, HRESULT* phr)
    00806         : CBaseOutputPin(NAME("CStreamSwitcherOutputPin"), pFilter, &pFilter->m_csState, phr, L"Out")
    00807 {
    00808 //      m_bCanReconnectWhenActive = TRUE;
    00809 }
    00810
    00811 STDMETHODIMP CStreamSwitcherOutputPin::NonDelegatingQueryInterface(REFIID riid, void **ppv)
    00812 {
    00813     CheckPointer(ppv,E_POINTER);
    00814     ValidateReadWritePtr(ppv, sizeof(PVOID));
    00815     *ppv = NULL;
    00816
    00817     if(riid == IID_IMediaPosition || riid == IID_IMediaSeeking)
    00818         {
    00819         if(m_pStreamSwitcherPassThru == NULL)
    00820                 {
    00821                         HRESULT hr = S_OK;
    00822                         m_pStreamSwitcherPassThru = (IUnknown*)(INonDelegatingUnknown*)
    00823                                 new CStreamSwitcherPassThru(GetOwner(), &hr, (CStreamSwitcherFilter*)m_pFilter);
    00824
    00825                         if(!m_pStreamSwitcherPassThru) return E_OUTOFMEMORY;
    00826             if(FAILED(hr)) return hr;
    00827         }
    00828
    00829         return m_pStreamSwitcherPassThru->QueryInterface(riid, ppv);
    00830     }
    00831 /*
    00832         else if(riid == IID_IStreamBuilder)
    00833         {
    00834                 return GetInterface((IStreamBuilder*)this, ppv);
    00835         }
    00836 */
    00837         return CBaseOutputPin::NonDelegatingQueryInterface(riid, ppv);
    00838 }
    00839
    00840 HRESULT CStreamSwitcherOutputPin::QueryAcceptUpstream(const AM_MEDIA_TYPE* pmt)
    00841 {
    00842         HRESULT hr = S_FALSE;
    00843
    00844         CStreamSwitcherInputPin* pIn = ((CStreamSwitcherFilter*)m_pFilter)->GetInputPin();
    00845
    00846         if(pIn && pIn->IsConnected() && (pIn->IsUsingOwnAllocator() || pIn->CurrentMediaType() == *pmt))
    00847         {
    00848                 if(CComQIPtr<IPin> pPinTo = pIn->GetConnected())
    00849                 {
    00850                         if(S_OK != (hr = pPinTo->QueryAccept(pmt)))
    00851                                 return VFW_E_TYPE_NOT_ACCEPTED;
    00852                 }
    00853                 else
    00854                 {
    00855                         return E_FAIL;
    00856                 }
    00857         }
    00858
    00859         return hr;
    00860 }
    00861
    00862 // pure virtual
    00863
    00864 HRESULT CStreamSwitcherOutputPin::DecideBufferSize(IMemAllocator* pAllocator, ALLOCATOR_PROPERTIES* pProperties)
    00865 {
    00866         CStreamSwitcherInputPin* pIn = ((CStreamSwitcherFilter*)m_pFilter)->GetInputPin();
    00867         if(!pIn || !pIn->IsConnected()) return E_UNEXPECTED;
    00868
    00869         CComPtr<IMemAllocator> pAllocatorIn;
    00870         pIn->GetAllocator(&pAllocatorIn);
    00871         if(!pAllocatorIn) return E_UNEXPECTED;
    00872
    00873         HRESULT hr;
    00874     if(FAILED(hr = pAllocatorIn->GetProperties(pProperties)))
    00875                 return hr;
    00876
    00877         if(pProperties->cBuffers < 8 && pIn->CurrentMediaType().majortype == MEDIATYPE_Audio)
    00878                 pProperties->cBuffers = 8;
    00879
    00880         ALLOCATOR_PROPERTIES Actual;
    00881     if(FAILED(hr = pAllocator->SetProperties(pProperties, &Actual)))
    00882                 return hr;
    00883
    00884         return(pProperties->cBuffers > Actual.cBuffers || pProperties->cbBuffer > Actual.cbBuffer
    00885                 ? E_FAIL
    00886                 : NOERROR);
    00887 }
    00888
    00889 // virtual
    00890
    00891 [uuid("AEFA5024-215A-4FC7-97A4-1043C86FD0B8")]
    00892 class MatrixMixer {};
    00893
    00894 HRESULT CStreamSwitcherOutputPin::CheckConnect(IPin* pPin)
    00895 {
    00896         CComPtr<IBaseFilter> pBF = GetFilterFromPin(pPin);
    00897
    00898         return
    00899                 IsAudioWaveRenderer(pBF) || GetCLSID(pBF) == __uuidof(MatrixMixer)
    00900                 ? __super::CheckConnect(pPin)
    00901                 : E_FAIL;
    00902
    00903 //      return CComQIPtr<IPinConnection>(pPin) ? CBaseOutputPin::CheckConnect(pPin) : E_NOINTERFACE;
    00904 //      return CBaseOutputPin::CheckConnect(pPin);
    00905 }
    00906
    00907 HRESULT CStreamSwitcherOutputPin::BreakConnect()
    00908 {
    00909         m_pPinConnection = NULL;
    00910         return __super::BreakConnect();
    00911 }
    00912
    00913 HRESULT CStreamSwitcherOutputPin::CompleteConnect(IPin* pReceivePin)
    00914 {
    00915         m_pPinConnection = CComQIPtr<IPinConnection>(pReceivePin);
    00916         return __super::CompleteConnect(pReceivePin);
    00917 }
    00918
    00919 HRESULT CStreamSwitcherOutputPin::CheckMediaType(const CMediaType* pmt)
    00920 {
    00921         return ((CStreamSwitcherFilter*)m_pFilter)->CheckMediaType(pmt);
    00922 }
    00923
    00924 HRESULT CStreamSwitcherOutputPin::GetMediaType(int iPosition, CMediaType* pmt)
    00925 {
    00926         CStreamSwitcherInputPin* pIn = ((CStreamSwitcherFilter*)m_pFilter)->GetInputPin();
    00927         if(!pIn || !pIn->IsConnected()) return E_UNEXPECTED;
    00928
    00929         CComPtr<IEnumMediaTypes> pEM;
    00930         if(FAILED(pIn->GetConnected()->EnumMediaTypes(&pEM)))
    00931                 return VFW_S_NO_MORE_ITEMS;
    00932
    00933         if(iPosition > 0 && FAILED(pEM->Skip(iPosition)))
    00934                 return VFW_S_NO_MORE_ITEMS;
    00935
    00936         AM_MEDIA_TYPE* tmp = NULL;
    00937         if(S_OK != pEM->Next(1, &tmp, NULL) || !tmp)
    00938                 return VFW_S_NO_MORE_ITEMS;
    00939
    00940         CopyMediaType(pmt, tmp);
    00941         DeleteMediaType(tmp);
    00942 /*
    00943         if(iPosition < 0) return E_INVALIDARG;
    00944     if(iPosition > 0) return VFW_S_NO_MORE_ITEMS;
    00945
    00946         CopyMediaType(pmt, &pIn->CurrentMediaType());
    00947 */
    00948         return S_OK;
    00949 }
    00950
    00951 // IPin
    00952
    00953 STDMETHODIMP CStreamSwitcherOutputPin::QueryAccept(const AM_MEDIA_TYPE* pmt)
    00954 {
    00955         HRESULT hr = __super::QueryAccept(pmt);
    00956         if(S_OK != hr) return hr;
    00957
    00958         return QueryAcceptUpstream(pmt);
    00959 }
    00960
    00961 // IQualityControl
    00962
    00963 STDMETHODIMP CStreamSwitcherOutputPin::Notify(IBaseFilter* pSender, Quality q)
    00964 {
    00965         CStreamSwitcherInputPin* pIn = ((CStreamSwitcherFilter*)m_pFilter)->GetInputPin();
    00966         if(!pIn || !pIn->IsConnected()) return VFW_E_NOT_CONNECTED;
    00967     return pIn->PassNotify(q);
    00968 }
    00969
    00970 // IStreamBuilder
    00971
    00972 STDMETHODIMP CStreamSwitcherOutputPin::Render(IPin* ppinOut, IGraphBuilder* pGraph)
    00973 {
    00974         CComPtr<IBaseFilter> pBF;
    00975         pBF.CoCreateInstance(CLSID_DSoundRender);
    00976         if(!pBF || FAILED(pGraph->AddFilter(pBF, L"Default DirectSound Device")))
    00977         {
    00978                 return E_FAIL;
    00979         }
    00980
    00981         if(FAILED(pGraph->ConnectDirect(ppinOut, GetFirstDisconnectedPin(pBF, PINDIR_INPUT), NULL)))
    00982         {
    00983                 pGraph->RemoveFilter(pBF);
    00984                 return E_FAIL;
    00985         }
    00986
    00987         return S_OK;
    00988 }
    00989
    00990 STDMETHODIMP CStreamSwitcherOutputPin::Backout(IPin* ppinOut, IGraphBuilder* pGraph)
    00991 {
    00992         return S_OK;
    00993 }
    00994
    00995 //
    00996 // CStreamSwitcherFilter
    00997 //
    00998
    00999 CStreamSwitcherFilter::CStreamSwitcherFilter(LPUNKNOWN lpunk, HRESULT* phr, const CLSID& clsid)
    01000         : CBaseFilter(NAME("CStreamSwitcherFilter"), lpunk, &m_csState, clsid)
    01001 {
    01002         if(phr) *phr = S_OK;
    01003
    01004         HRESULT hr = S_OK;
    01005
    01006         do
    01007         {
    01008                 CAutoPtr<CStreamSwitcherInputPin> pInput;
    01009                 CAutoPtr<CStreamSwitcherOutputPin> pOutput;
    01010
    01011                 hr = S_OK;
    01012         pInput.Attach(new CStreamSwitcherInputPin(this, &hr, L"Channel 1"));
    01013                 if(!pInput || FAILED(hr)) break;
    01014
    01015                 hr = S_OK;
    01016                 pOutput.Attach(new CStreamSwitcherOutputPin(this, &hr));
    01017         if(!pOutput || FAILED(hr)) break;
    01018
    01019                 CAutoLock cAutoLock(&m_csPins);
    01020
    01021                 m_pInputs.AddHead(m_pInput = pInput.Detach());
    01022                 m_pOutput = pOutput.Detach();
    01023
    01024                 return;
    01025         }
    01026         while(false);
    01027
    01028         if(phr) *phr = E_FAIL;
    01029 }
    01030
    01031 CStreamSwitcherFilter::~CStreamSwitcherFilter()
    01032 {
    01033         CAutoLock cAutoLock(&m_csPins);
    01034
    01035         POSITION pos = m_pInputs.GetHeadPosition();
    01036         while(pos) delete m_pInputs.GetNext(pos);
    01037         m_pInputs.RemoveAll();
    01038         m_pInput = NULL;
    01039
    01040         delete m_pOutput;
    01041         m_pOutput = NULL;
    01042 }
    01043
    01044 STDMETHODIMP CStreamSwitcherFilter::NonDelegatingQueryInterface(REFIID riid, void** ppv)
    01045 {
    01046         return
    01047                 QI(IAMStreamSelect)
    01048                 __super::NonDelegatingQueryInterface(riid, ppv);
    01049 }
    01050
    01051 //
    01052
    01053 int CStreamSwitcherFilter::GetPinCount()
    01054 {
    01055         CAutoLock cAutoLock(&m_csPins);
    01056
    01057         return(1 + (int)m_pInputs.GetCount());
    01058 }
    01059
    01060 CBasePin* CStreamSwitcherFilter::GetPin(int n)
    01061 {
    01062         CAutoLock cAutoLock(&m_csPins);
    01063
    01064         if(n < 0 || n >= GetPinCount()) return NULL;
    01065         else if(n == 0) return m_pOutput;
    01066         else return m_pInputs.GetAt(m_pInputs.FindIndex(n-1));
    01067 }
    01068
    01069 int CStreamSwitcherFilter::GetConnectedInputPinCount()
    01070 {
    01071         CAutoLock cAutoLock(&m_csPins);
    01072
    01073         int nConnected = 0;
    01074
    01075         POSITION pos = m_pInputs.GetHeadPosition();
    01076         while(pos)
    01077         {
    01078                 if(m_pInputs.GetNext(pos)->IsConnected())
    01079                         nConnected++;
    01080         }
    01081
    01082         return(nConnected);
    01083 }
    01084
    01085 CStreamSwitcherInputPin* CStreamSwitcherFilter::GetConnectedInputPin(int n)
    01086 {
    01087         if(n >= 0)
    01088         {
    01089                 POSITION pos = m_pInputs.GetHeadPosition();
    01090                 while(pos)
    01091                 {
    01092                         CStreamSwitcherInputPin* pPin = m_pInputs.GetNext(pos);
    01093                         if(pPin->IsConnected())
    01094                         {
    01095                                 if(n == 0) return(pPin);
    01096                                 n--;
    01097                         }
    01098                 }
    01099         }
    01100
    01101         return NULL;
    01102 }
    01103
    01104 CStreamSwitcherInputPin* CStreamSwitcherFilter::GetInputPin()
    01105 {
    01106         return m_pInput;
    01107 }
    01108
    01109 CStreamSwitcherOutputPin* CStreamSwitcherFilter::GetOutputPin()
    01110 {
    01111         return m_pOutput;
    01112 }
    01113
    01114 //
    01115
    01116 HRESULT CStreamSwitcherFilter::CompleteConnect(PIN_DIRECTION dir, CBasePin* pPin, IPin* pReceivePin)
    01117 {
    01118         if(dir == PINDIR_INPUT)
    01119         {
    01120                 CAutoLock cAutoLock(&m_csPins);
    01121
    01122                 int nConnected = GetConnectedInputPinCount();
    01123
    01124                 if(nConnected == 1)
    01125                 {
    01126                         m_pInput = (CStreamSwitcherInputPin*)pPin;
    01127                 }
    01128
    01129                 if(nConnected == m_pInputs.GetCount())
    01130                 {
    01131                         CStringW name;
    01132                         name.Format(L"Channel %d", ++m_PinVersion);
    01133
    01134                         HRESULT hr = S_OK;
    01135                         CStreamSwitcherInputPin* pPin = new CStreamSwitcherInputPin(this, &hr, name);
    01136                         if(!pPin || FAILED(hr)) return E_FAIL;
    01137                         m_pInputs.AddTail(pPin);
    01138                 }
    01139         }
    01140
    01141         return S_OK;
    01142 }
    01143
    01144 // this should be very thread safe, I hope it is, it must be... :)
    01145
    01146 void CStreamSwitcherFilter::SelectInput(CStreamSwitcherInputPin* pInput)
    01147 {
    01148         // make sure no input thinks it is active
    01149         m_pInput = NULL;
    01150
    01151         // release blocked GetBuffer in our own allocator & block all Receive
    01152         POSITION pos = m_pInputs.GetHeadPosition();
    01153         while(pos)
    01154         {
    01155                 CStreamSwitcherInputPin* pPin = m_pInputs.GetNext(pos);
    01156                 pPin->Block(false);
    01157                 // a few Receive calls can arrive here, but since m_pInput == NULL neighter of them gets delivered
    01158                 pPin->Block(true);
    01159         }
    01160
    01161         // this will let waiting GetBuffer() calls go on inside our Receive()
    01162         if(m_pOutput)
    01163         {
    01164                 m_pOutput->DeliverBeginFlush();
    01165                 m_pOutput->DeliverEndFlush();
    01166         }
    01167
    01168         if(!pInput) return;
    01169
    01170         // set new input
    01171         m_pInput = pInput;
    01172
    01173         // let it go
    01174         m_pInput->Block(false);
    01175 }
    01176
    01177 //
    01178
    01179 HRESULT CStreamSwitcherFilter::Transform(IMediaSample* pIn, IMediaSample* pOut)
    01180 {
    01181         BYTE* pDataIn = NULL;
    01182         BYTE* pDataOut = NULL;
    01183
    01184         HRESULT hr;
    01185         if(FAILED(hr = pIn->GetPointer(&pDataIn))) return hr;
    01186         if(FAILED(hr = pOut->GetPointer(&pDataOut))) return hr;
    01187
    01188         long len = pIn->GetActualDataLength();
    01189         long size = pOut->GetSize();
    01190
    01191         if(!pDataIn || !pDataOut /*|| len > size || len <= 0*/) return S_FALSE; // FIXME
    01192
    01193         memcpy(pDataOut, pDataIn, min(len, size));
    01194         pOut->SetActualDataLength(min(len, size));
    01195
    01196         return S_OK;
    01197 }
    01198
    01199 CMediaType CStreamSwitcherFilter::CreateNewOutputMediaType(CMediaType mt, long& cbBuffer)
    01200 {
    01201         return(mt);
    01202 }
    01203
    01204 // IAMStreamSelect
    01205
    01206 STDMETHODIMP CStreamSwitcherFilter::Count(DWORD* pcStreams)
    01207 {
    01208         if(!pcStreams) return E_POINTER;
    01209
    01210         CAutoLock cAutoLock(&m_csPins);
    01211
    01212         *pcStreams = GetConnectedInputPinCount();
    01213
    01214         return S_OK;
    01215 }
    01216
    01217 STDMETHODIMP CStreamSwitcherFilter::Info(long lIndex, AM_MEDIA_TYPE** ppmt, DWORD* pdwFlags, LCID* plcid, DWORD* pdwGroup, WCHAR** ppszName, IUnknown** ppObject, IUnknown** ppUnk)
    01218 {
    01219         CAutoLock cAutoLock(&m_csPins);
    01220
    01221         CBasePin* pPin = GetConnectedInputPin(lIndex);
    01222         if(!pPin) return E_INVALIDARG;
    01223
    01224         if(ppmt)
    01225                 *ppmt = CreateMediaType(&m_pOutput->CurrentMediaType());
    01226
    01227         if(pdwFlags)
    01228                 *pdwFlags = (m_pInput == pPin) ? AMSTREAMSELECTINFO_EXCLUSIVE : 0;
    01229
    01230         if(plcid)
    01231                 *plcid = 0;
    01232
    01233         if(pdwGroup)
    01234                 *pdwGroup = 0;
    01235
    01236         if(ppszName && (*ppszName = (WCHAR*)CoTaskMemAlloc((wcslen(pPin->Name())+1)*sizeof(WCHAR))))
    01237                 wcscpy(*ppszName, pPin->Name());
    01238
    01239         if(ppObject)
    01240                 *ppObject = NULL;
    01241
    01242         if(ppUnk)
    01243                 *ppUnk = NULL;
    01244
    01245         return S_OK;
    01246 }
    01247
    01248 STDMETHODIMP CStreamSwitcherFilter::Enable(long lIndex, DWORD dwFlags)
    01249 {
    01250         if(dwFlags != AMSTREAMSELECTENABLE_ENABLE)
    01251                 return E_NOTIMPL;
    01252
    01253         PauseGraph;
    01254
    01255         CStreamSwitcherInputPin* pNewInput = GetConnectedInputPin(lIndex);
    01256         if(!pNewInput) return E_INVALIDARG;
    01257
    01258         SelectInput(pNewInput);
    01259
    01260         ResumeGraph;
    01261
    01262         return S_OK;
    01263 }
    01264