topical media & game development

talk show tell print

hush-src-multi-BaseClasses-source.cpp / cpp



  //------------------------------------------------------------------------------
  // File: Source.cpp
  //
  // Desc: DirectShow  base classes - implements CSource, which is a Quartz
  //       source filter 'template.'
  //
  // Copyright (c) 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(TCHAR *pName, LPUNKNOWN lpunk, CLSID clsid)
      : CBaseFilter(pName, lpunk, &m_cStateLock, clsid),
        m_iPins(0),
        m_paStreams(NULL)
  {
  }
  
  CSource::CSource(TCHAR *pName, LPUNKNOWN lpunk, CLSID clsid, HRESULT *phr)
      : CBaseFilter(pName, lpunk, &m_cStateLock, clsid),
        m_iPins(0),
        m_paStreams(NULL)
  {
      UNREFERENCED_PARAMETER(phr);
  }
  
  ifdef UNICODE
  CSource::CSource(CHAR *pName, LPUNKNOWN lpunk, CLSID clsid)
      : CBaseFilter(pName, lpunk, &m_cStateLock, clsid),
        m_iPins(0),
        m_paStreams(NULL)
  {
  }
  
  CSource::CSource(CHAR *pName, LPUNKNOWN lpunk, CLSID clsid, 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(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(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, 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(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(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(8);
      if (*Id==NULL) {
         return E_OUTOFMEMORY;
      }
      IntToWstr(i, *Id);
      return NOERROR;
  }
  
  //
  // CSourceStream::Constructor
  //
  // increments the number of pins present on the filter
  CSourceStream::CSourceStream(
      TCHAR *pObjectName,
      HRESULT *phr,
      CSource *ps,
      LPCWSTR pPinName)
      : CBaseOutputPin(pObjectName, ps, ps->pStateLock(), phr, pPinName),
        m_pFilter(ps) {
  
       *phr = m_pFilter->AddPin(this);
  }
  
  ifdef UNICODE
  CSourceStream::CSourceStream(
      char *pObjectName,
      HRESULT *phr,
      CSource *ps,
      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, 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;
  }
  
  


(C) Æliens 20/2/2008

You may not copy or print any of this material without explicit permission of the author or the publisher. In case of other copyright issues, contact the author.