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 

Generated on Tue Dec 13 14:47:26 2005 for guliverkli by  doxygen 1.4.5