forked from LeenkxTeam/LNXSDK
		
	
		
			
				
	
	
		
			523 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			523 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| //------------------------------------------------------------------------------
 | |
| // File: Source.cpp
 | |
| //
 | |
| // Desc: DirectShow  base classes - implements CSource, which is a Quartz
 | |
| //       source filter 'template.'
 | |
| //
 | |
| // Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.
 | |
| //------------------------------------------------------------------------------
 | |
| 
 | |
| 
 | |
| // Locking Strategy.
 | |
| //
 | |
| // Hold the filter critical section (m_pFilter->pStateLock()) to serialise
 | |
| // access to functions. Note that, in general, this lock may be held
 | |
| // by a function when the worker thread may want to hold it. Therefore
 | |
| // if you wish to access shared state from the worker thread you will
 | |
| // need to add another critical section object. The execption is during
 | |
| // the threads processing loop, when it is safe to get the filter critical
 | |
| // section from within FillBuffer().
 | |
| 
 | |
| #include <streams.h>
 | |
| 
 | |
| 
 | |
| //
 | |
| // CSource::Constructor
 | |
| //
 | |
| // Initialise the pin count for the filter. The user will create the pins in
 | |
| // the derived class.
 | |
| CSource::CSource(__in_opt LPCTSTR pName, __inout_opt LPUNKNOWN lpunk, CLSID clsid)
 | |
|     : CBaseFilter(pName, lpunk, &m_cStateLock, clsid),
 | |
|       m_iPins(0),
 | |
|       m_paStreams(NULL)
 | |
| {
 | |
| }
 | |
| 
 | |
| CSource::CSource(__in_opt LPCTSTR pName, __inout_opt LPUNKNOWN lpunk, CLSID clsid, __inout HRESULT *phr)
 | |
|     : CBaseFilter(pName, lpunk, &m_cStateLock, clsid),
 | |
|       m_iPins(0),
 | |
|       m_paStreams(NULL)
 | |
| {
 | |
|     UNREFERENCED_PARAMETER(phr);
 | |
| }
 | |
| 
 | |
| #ifdef UNICODE
 | |
| CSource::CSource(__in_opt LPCSTR pName, __inout_opt LPUNKNOWN lpunk, CLSID clsid)
 | |
|     : CBaseFilter(pName, lpunk, &m_cStateLock, clsid),
 | |
|       m_iPins(0),
 | |
|       m_paStreams(NULL)
 | |
| {
 | |
| }
 | |
| 
 | |
| CSource::CSource(__in_opt LPCSTR pName, __inout_opt LPUNKNOWN lpunk, CLSID clsid, __inout HRESULT *phr)
 | |
|     : CBaseFilter(pName, lpunk, &m_cStateLock, clsid),
 | |
|       m_iPins(0),
 | |
|       m_paStreams(NULL)
 | |
| {
 | |
|     UNREFERENCED_PARAMETER(phr);
 | |
| }
 | |
| #endif
 | |
| 
 | |
| //
 | |
| // CSource::Destructor
 | |
| //
 | |
| CSource::~CSource()
 | |
| {
 | |
|     /*  Free our pins and pin array */
 | |
|     while (m_iPins != 0) {
 | |
| 	// deleting the pins causes them to be removed from the array...
 | |
| 	delete m_paStreams[m_iPins - 1];
 | |
|     }
 | |
| 
 | |
|     ASSERT(m_paStreams == NULL);
 | |
| }
 | |
| 
 | |
| 
 | |
| //
 | |
| //  Add a new pin
 | |
| //
 | |
| HRESULT CSource::AddPin(__in CSourceStream *pStream)
 | |
| {
 | |
|     CAutoLock lock(&m_cStateLock);
 | |
| 
 | |
|     /*  Allocate space for this pin and the old ones */
 | |
|     CSourceStream **paStreams = new CSourceStream *[m_iPins + 1];
 | |
|     if (paStreams == NULL) {
 | |
|         return E_OUTOFMEMORY;
 | |
|     }
 | |
|     if (m_paStreams != NULL) {
 | |
|         CopyMemory((PVOID)paStreams, (PVOID)m_paStreams,
 | |
|                    m_iPins * sizeof(m_paStreams[0]));
 | |
|         paStreams[m_iPins] = pStream;
 | |
|         delete [] m_paStreams;
 | |
|     }
 | |
|     m_paStreams = paStreams;
 | |
|     m_paStreams[m_iPins] = pStream;
 | |
|     m_iPins++;
 | |
|     return S_OK;
 | |
| }
 | |
| 
 | |
| //
 | |
| //  Remove a pin - pStream is NOT deleted
 | |
| //
 | |
| HRESULT CSource::RemovePin(__in CSourceStream *pStream)
 | |
| {
 | |
|     int i;
 | |
|     for (i = 0; i < m_iPins; i++) {
 | |
|         if (m_paStreams[i] == pStream) {
 | |
|             if (m_iPins == 1) {
 | |
|                 delete [] m_paStreams;
 | |
|                 m_paStreams = NULL;
 | |
|             } else {
 | |
|                 /*  no need to reallocate */
 | |
| 		while (++i < m_iPins)
 | |
| 		    m_paStreams[i - 1] = m_paStreams[i];
 | |
|             }
 | |
|             m_iPins--;
 | |
|             return S_OK;
 | |
|         }
 | |
|     }
 | |
|     return S_FALSE;
 | |
| }
 | |
| 
 | |
| //
 | |
| // FindPin
 | |
| //
 | |
| // Set *ppPin to the IPin* that has the id Id.
 | |
| // or to NULL if the Id cannot be matched.
 | |
| STDMETHODIMP CSource::FindPin(LPCWSTR Id, __deref_out IPin **ppPin)
 | |
| {
 | |
|     CheckPointer(ppPin,E_POINTER);
 | |
|     ValidateReadWritePtr(ppPin,sizeof(IPin *));
 | |
|     // The -1 undoes the +1 in QueryId and ensures that totally invalid
 | |
|     // strings (for which WstrToInt delivers 0) give a deliver a NULL pin.
 | |
|     int i = WstrToInt(Id) -1;
 | |
|     *ppPin = GetPin(i);
 | |
|     if (*ppPin!=NULL){
 | |
|         (*ppPin)->AddRef();
 | |
|         return NOERROR;
 | |
|     } else {
 | |
|         return VFW_E_NOT_FOUND;
 | |
|     }
 | |
| }
 | |
| 
 | |
| //
 | |
| // FindPinNumber
 | |
| //
 | |
| // return the number of the pin with this IPin* or -1 if none
 | |
| int CSource::FindPinNumber(__in IPin *iPin) {
 | |
|     int i;
 | |
|     for (i=0; i<m_iPins; ++i) {
 | |
|         if ((IPin *)(m_paStreams[i])==iPin) {
 | |
|             return i;
 | |
|         }
 | |
|     }
 | |
|     return -1;
 | |
| }
 | |
| 
 | |
| //
 | |
| // GetPinCount
 | |
| //
 | |
| // Returns the number of pins this filter has
 | |
| int CSource::GetPinCount(void) {
 | |
| 
 | |
|     CAutoLock lock(&m_cStateLock);
 | |
|     return m_iPins;
 | |
| }
 | |
| 
 | |
| 
 | |
| //
 | |
| // GetPin
 | |
| //
 | |
| // Return a non-addref'd pointer to pin n
 | |
| // needed by CBaseFilter
 | |
| CBasePin *CSource::GetPin(int n) {
 | |
| 
 | |
|     CAutoLock lock(&m_cStateLock);
 | |
| 
 | |
|     // n must be in the range 0..m_iPins-1
 | |
|     // if m_iPins>n  && n>=0 it follows that m_iPins>0
 | |
|     // which is what used to be checked (i.e. checking that we have a pin)
 | |
|     if ((n >= 0) && (n < m_iPins)) {
 | |
| 
 | |
|         ASSERT(m_paStreams[n]);
 | |
| 	return m_paStreams[n];
 | |
|     }
 | |
|     return NULL;
 | |
| }
 | |
| 
 | |
| 
 | |
| //
 | |
| 
 | |
| 
 | |
| // *
 | |
| // * --- CSourceStream ----
 | |
| // *
 | |
| 
 | |
| //
 | |
| // Set Id to point to a CoTaskMemAlloc'd
 | |
| STDMETHODIMP CSourceStream::QueryId(__deref_out LPWSTR *Id) {
 | |
|     CheckPointer(Id,E_POINTER);
 | |
|     ValidateReadWritePtr(Id,sizeof(LPWSTR));
 | |
| 
 | |
|     // We give the pins id's which are 1,2,...
 | |
|     // FindPinNumber returns -1 for an invalid pin
 | |
|     int i = 1+ m_pFilter->FindPinNumber(this);
 | |
|     if (i<1) return VFW_E_NOT_FOUND;
 | |
|     *Id = (LPWSTR)CoTaskMemAlloc(sizeof(WCHAR) * 12);
 | |
|     if (*Id==NULL) {
 | |
|        return E_OUTOFMEMORY;
 | |
|     }
 | |
|     IntToWstr(i, *Id);
 | |
|     return NOERROR;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| //
 | |
| // CSourceStream::Constructor
 | |
| //
 | |
| // increments the number of pins present on the filter
 | |
| CSourceStream::CSourceStream(
 | |
|     __in_opt LPCTSTR pObjectName,
 | |
|     __inout HRESULT *phr,
 | |
|     __inout CSource *ps,
 | |
|     __in_opt LPCWSTR pPinName)
 | |
|     : CBaseOutputPin(pObjectName, ps, ps->pStateLock(), phr, pPinName),
 | |
|       m_pFilter(ps) {
 | |
| 
 | |
|      *phr = m_pFilter->AddPin(this);
 | |
| }
 | |
| 
 | |
| #ifdef UNICODE
 | |
| CSourceStream::CSourceStream(
 | |
|     __in_opt LPCSTR pObjectName,
 | |
|     __inout HRESULT *phr,
 | |
|     __inout CSource *ps,
 | |
|     __in_opt LPCWSTR pPinName)
 | |
|     : CBaseOutputPin(pObjectName, ps, ps->pStateLock(), phr, pPinName),
 | |
|       m_pFilter(ps) {
 | |
| 
 | |
|      *phr = m_pFilter->AddPin(this);
 | |
| }
 | |
| #endif
 | |
| //
 | |
| // CSourceStream::Destructor
 | |
| //
 | |
| // Decrements the number of pins on this filter
 | |
| CSourceStream::~CSourceStream(void) {
 | |
| 
 | |
|      m_pFilter->RemovePin(this);
 | |
| }
 | |
| 
 | |
| 
 | |
| //
 | |
| // CheckMediaType
 | |
| //
 | |
| // Do we support this type? Provides the default support for 1 type.
 | |
| HRESULT CSourceStream::CheckMediaType(const CMediaType *pMediaType) {
 | |
| 
 | |
|     CAutoLock lock(m_pFilter->pStateLock());
 | |
| 
 | |
|     CMediaType mt;
 | |
|     GetMediaType(&mt);
 | |
| 
 | |
|     if (mt == *pMediaType) {
 | |
|         return NOERROR;
 | |
|     }
 | |
| 
 | |
|     return E_FAIL;
 | |
| }
 | |
| 
 | |
| 
 | |
| //
 | |
| // GetMediaType/3
 | |
| //
 | |
| // By default we support only one type
 | |
| // iPosition indexes are 0-n
 | |
| HRESULT CSourceStream::GetMediaType(int iPosition, __inout CMediaType *pMediaType) {
 | |
| 
 | |
|     CAutoLock lock(m_pFilter->pStateLock());
 | |
| 
 | |
|     if (iPosition<0) {
 | |
|         return E_INVALIDARG;
 | |
|     }
 | |
|     if (iPosition>0) {
 | |
|         return VFW_S_NO_MORE_ITEMS;
 | |
|     }
 | |
|     return GetMediaType(pMediaType);
 | |
| }
 | |
| 
 | |
| 
 | |
| //
 | |
| // Active
 | |
| //
 | |
| // The pin is active - start up the worker thread
 | |
| HRESULT CSourceStream::Active(void) {
 | |
| 
 | |
|     CAutoLock lock(m_pFilter->pStateLock());
 | |
| 
 | |
|     HRESULT hr;
 | |
| 
 | |
|     if (m_pFilter->IsActive()) {
 | |
| 	return S_FALSE;	// succeeded, but did not allocate resources (they already exist...)
 | |
|     }
 | |
| 
 | |
|     // do nothing if not connected - its ok not to connect to
 | |
|     // all pins of a source filter
 | |
|     if (!IsConnected()) {
 | |
|         return NOERROR;
 | |
|     }
 | |
| 
 | |
|     hr = CBaseOutputPin::Active();
 | |
|     if (FAILED(hr)) {
 | |
|         return hr;
 | |
|     }
 | |
| 
 | |
|     ASSERT(!ThreadExists());
 | |
| 
 | |
|     // start the thread
 | |
|     if (!Create()) {
 | |
|         return E_FAIL;
 | |
|     }
 | |
| 
 | |
|     // Tell thread to initialize. If OnThreadCreate Fails, so does this.
 | |
|     hr = Init();
 | |
|     if (FAILED(hr))
 | |
| 	return hr;
 | |
| 
 | |
|     return Pause();
 | |
| }
 | |
| 
 | |
| 
 | |
| //
 | |
| // Inactive
 | |
| //
 | |
| // Pin is inactive - shut down the worker thread
 | |
| // Waits for the worker to exit before returning.
 | |
| HRESULT CSourceStream::Inactive(void) {
 | |
| 
 | |
|     CAutoLock lock(m_pFilter->pStateLock());
 | |
| 
 | |
|     HRESULT hr;
 | |
| 
 | |
|     // do nothing if not connected - its ok not to connect to
 | |
|     // all pins of a source filter
 | |
|     if (!IsConnected()) {
 | |
|         return NOERROR;
 | |
|     }
 | |
| 
 | |
|     // !!! need to do this before trying to stop the thread, because
 | |
|     // we may be stuck waiting for our own allocator!!!
 | |
| 
 | |
|     hr = CBaseOutputPin::Inactive();  // call this first to Decommit the allocator
 | |
|     if (FAILED(hr)) {
 | |
| 	return hr;
 | |
|     }
 | |
| 
 | |
|     if (ThreadExists()) {
 | |
| 	hr = Stop();
 | |
| 
 | |
| 	if (FAILED(hr)) {
 | |
| 	    return hr;
 | |
| 	}
 | |
| 
 | |
| 	hr = Exit();
 | |
| 	if (FAILED(hr)) {
 | |
| 	    return hr;
 | |
| 	}
 | |
| 
 | |
| 	Close();	// Wait for the thread to exit, then tidy up.
 | |
|     }
 | |
| 
 | |
|     // hr = CBaseOutputPin::Inactive();  // call this first to Decommit the allocator
 | |
|     //if (FAILED(hr)) {
 | |
|     //	return hr;
 | |
|     //}
 | |
| 
 | |
|     return NOERROR;
 | |
| }
 | |
| 
 | |
| 
 | |
| //
 | |
| // ThreadProc
 | |
| //
 | |
| // When this returns the thread exits
 | |
| // Return codes > 0 indicate an error occured
 | |
| DWORD CSourceStream::ThreadProc(void) {
 | |
| 
 | |
|     HRESULT hr;  // the return code from calls
 | |
|     Command com;
 | |
| 
 | |
|     do {
 | |
| 	com = GetRequest();
 | |
| 	if (com != CMD_INIT) {
 | |
| 	    DbgLog((LOG_ERROR, 1, TEXT("Thread expected init command")));
 | |
| 	    Reply((DWORD) E_UNEXPECTED);
 | |
| 	}
 | |
|     } while (com != CMD_INIT);
 | |
| 
 | |
|     DbgLog((LOG_TRACE, 1, TEXT("CSourceStream worker thread initializing")));
 | |
| 
 | |
|     hr = OnThreadCreate(); // perform set up tasks
 | |
|     if (FAILED(hr)) {
 | |
|         DbgLog((LOG_ERROR, 1, TEXT("CSourceStream::OnThreadCreate failed. Aborting thread.")));
 | |
|         OnThreadDestroy();
 | |
| 	Reply(hr);	// send failed return code from OnThreadCreate
 | |
|         return 1;
 | |
|     }
 | |
| 
 | |
|     // Initialisation suceeded
 | |
|     Reply(NOERROR);
 | |
| 
 | |
|     Command cmd;
 | |
|     do {
 | |
| 	cmd = GetRequest();
 | |
| 
 | |
| 	switch (cmd) {
 | |
| 
 | |
| 	case CMD_EXIT:
 | |
| 	    Reply(NOERROR);
 | |
| 	    break;
 | |
| 
 | |
| 	case CMD_RUN:
 | |
| 	    DbgLog((LOG_ERROR, 1, TEXT("CMD_RUN received before a CMD_PAUSE???")));
 | |
| 	    // !!! fall through???
 | |
| 	
 | |
| 	case CMD_PAUSE:
 | |
| 	    Reply(NOERROR);
 | |
| 	    DoBufferProcessingLoop();
 | |
| 	    break;
 | |
| 
 | |
| 	case CMD_STOP:
 | |
| 	    Reply(NOERROR);
 | |
| 	    break;
 | |
| 
 | |
| 	default:
 | |
| 	    DbgLog((LOG_ERROR, 1, TEXT("Unknown command %d received!"), cmd));
 | |
| 	    Reply((DWORD) E_NOTIMPL);
 | |
| 	    break;
 | |
| 	}
 | |
|     } while (cmd != CMD_EXIT);
 | |
| 
 | |
|     hr = OnThreadDestroy();	// tidy up.
 | |
|     if (FAILED(hr)) {
 | |
|         DbgLog((LOG_ERROR, 1, TEXT("CSourceStream::OnThreadDestroy failed. Exiting thread.")));
 | |
|         return 1;
 | |
|     }
 | |
| 
 | |
|     DbgLog((LOG_TRACE, 1, TEXT("CSourceStream worker thread exiting")));
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| //
 | |
| // DoBufferProcessingLoop
 | |
| //
 | |
| // Grabs a buffer and calls the users processing function.
 | |
| // Overridable, so that different delivery styles can be catered for.
 | |
| HRESULT CSourceStream::DoBufferProcessingLoop(void) {
 | |
| 
 | |
|     Command com;
 | |
| 
 | |
|     OnThreadStartPlay();
 | |
| 
 | |
|     do {
 | |
| 	while (!CheckRequest(&com)) {
 | |
| 
 | |
| 	    IMediaSample *pSample;
 | |
| 
 | |
| 	    HRESULT hr = GetDeliveryBuffer(&pSample,NULL,NULL,0);
 | |
| 	    if (FAILED(hr)) {
 | |
|                 Sleep(1);
 | |
| 		continue;	// go round again. Perhaps the error will go away
 | |
| 			    // or the allocator is decommited & we will be asked to
 | |
| 			    // exit soon.
 | |
| 	    }
 | |
| 
 | |
| 	    // Virtual function user will override.
 | |
| 	    hr = FillBuffer(pSample);
 | |
| 
 | |
| 	    if (hr == S_OK) {
 | |
| 		hr = Deliver(pSample);
 | |
|                 pSample->Release();
 | |
| 
 | |
|                 // downstream filter returns S_FALSE if it wants us to
 | |
|                 // stop or an error if it's reporting an error.
 | |
|                 if(hr != S_OK)
 | |
|                 {
 | |
|                   DbgLog((LOG_TRACE, 2, TEXT("Deliver() returned %08x; stopping"), hr));
 | |
|                   return S_OK;
 | |
|                 }
 | |
| 
 | |
| 	    } else if (hr == S_FALSE) {
 | |
|                 // derived class wants us to stop pushing data
 | |
| 		pSample->Release();
 | |
| 		DeliverEndOfStream();
 | |
| 		return S_OK;
 | |
| 	    } else {
 | |
|                 // derived class encountered an error
 | |
|                 pSample->Release();
 | |
| 		DbgLog((LOG_ERROR, 1, TEXT("Error %08lX from FillBuffer!!!"), hr));
 | |
|                 DeliverEndOfStream();
 | |
|                 m_pFilter->NotifyEvent(EC_ERRORABORT, hr, 0);
 | |
|                 return hr;
 | |
| 	    }
 | |
| 
 | |
|             // all paths release the sample
 | |
| 	}
 | |
| 
 | |
|         // For all commands sent to us there must be a Reply call!
 | |
| 
 | |
| 	if (com == CMD_RUN || com == CMD_PAUSE) {
 | |
| 	    Reply(NOERROR);
 | |
| 	} else if (com != CMD_STOP) {
 | |
| 	    Reply((DWORD) E_UNEXPECTED);
 | |
| 	    DbgLog((LOG_ERROR, 1, TEXT("Unexpected command!!!")));
 | |
| 	}
 | |
|     } while (com != CMD_STOP);
 | |
| 
 | |
|     return S_FALSE;
 | |
| }
 | |
| 
 |