text: /*++
  
  Copyright (c) 1998 Microsoft Corporation
  
  Module Name:
  
      bgaudio.cpp
  
  Abstract:
  
      Implementation of the audio bridge filters.
  
  Author:
  
      Mu Han (muhan) 11/16/1998
  
  --*/
  
  #include "stdafx.h"
  
  CTAPIAudioBridgeSinkFilter::CTAPIAudioBridgeSinkFilter(
      IN LPUNKNOWN        pUnk, 
      IN IDataBridge *    pIDataBridge, 
      OUT HRESULT *       phr
      ) 
      : CTAPIBridgeSinkFilter(pUnk, pIDataBridge, phr)
  {
  }
  
  HRESULT CTAPIAudioBridgeSinkFilter::CreateInstance(
      IN IDataBridge *    pIDataBridge, 
      OUT IBaseFilter ** ppIBaseFilter
      )
  /*++
  
  Routine Description:
  
      This method create a instance of the bridge's sink filter.
  
  Arguments:
  
      ppIBaseFilter - the returned filter interface pointer.
  
  Return Value:
  
      E_OUTOFMEMORY - no memory for the new object.
  
  --*/
  {
      ENTER_FUNCTION("CTAPIAudioBridgeSinkFilter::CreateInstance");
  
      BGLOG((BG_TRACE, "%s entered.", __fxName));
  
      HRESULT hr = S_OK;
      CUnknown* pUnknown = new CTAPIAudioBridgeSinkFilter(NULL, pIDataBridge, &hr);
                  
      if (pUnknown == NULL) 
      {
          hr = E_OUTOFMEMORY;
          BGLOG((BG_ERROR, 
              "%s, out of memory creating the filter", 
              __fxName));
      }
      else if (FAILED(hr))
      {
          BGLOG((BG_ERROR, 
              "%s, the filter's constructor failed, hr:%d", 
              __fxName, hr));
  
          delete pUnknown;
      }
      else
      {
          pUnknown->NonDelegatingAddRef();
  
          hr = pUnknown->NonDelegatingQueryInterface(
              __uuidof(IBaseFilter), (void **)ppIBaseFilter
              );
  
          pUnknown->NonDelegatingRelease();
      }
  
      BGLOG((BG_TRACE, 
          "%s, returning:%p, hr:%x", __fxName, *ppIBaseFilter, hr));
  
      return hr;
  } 
  
  HRESULT CTAPIAudioBridgeSinkFilter::GetMediaType(
      IN      int     iPosition, 
      OUT     CMediaType *pMediaType
      )
  /*++
  
  Routine Description:
  
      Get the media type that this filter wants to support. Currently we
      only support PCM L16 8KHz samples.
  
  Arguments:
  
      IN  int iPosition, 
          the index of the media type, zero based..
          
      In  CMediaType *pMediaType
          Pointer to a CMediaType object to save the returned media type.
  
  Return Value:
  
      S_OK - success
      E_OUTOFMEMORY - no memory
  
  --*/
  {
      ENTER_FUNCTION("CTAPIAudioBridgeSinkFilter::GetMediaType");
  
      BGLOG((BG_TRACE, 
          "%s, iPosition:%d, pMediaType:%p", 
          __fxName, iPosition, pMediaType));
  
      HRESULT hr = VFW_S_NO_MORE_ITEMS;
  
      BGLOG((BG_TRACE, "%s returns %d", __fxName, hr));
  
      return hr;
  }
  
  
  HRESULT CTAPIAudioBridgeSinkFilter::CheckMediaType(
      const CMediaType *pMediaType
      )
  /*++
  
  Routine Description:
  
      Check the media type that this filter wants to support. Currently we
      only support PCM L16 8KHz samples.
  
  Arguments:
  
      In  CMediaType *pMediaType
          Pointer to a CMediaType object to save the returned media type.
  
  Return Value:
  
      S_OK - success
      E_OUTOFMEMORY - no memory
      E_UNEXPECTED - internal media type not set
      VFW_E_TYPE_NOT_ACCEPTED - media type rejected
      VFW_E_INVALIDMEDIATYPE  - bad media type
  
  --*/
  {
      ENTER_FUNCTION("CTAPIAudioBridgeSinkFilter::CheckMediaType");
  
      BGLOG((BG_TRACE, 
          "%s, pMediaType:%p", __fxName, pMediaType));
  
      ASSERT(!IsBadReadPtr(pMediaType, sizeof(AM_MEDIA_TYPE)));
  
      // media type is only stored in source filter
      // return S_OK here
      // if error, the source filter will detect it anyway
      HRESULT hr = S_OK;
  
      BGLOG((BG_TRACE, "%s returns %d", __fxName, hr));
  
      return hr;
  }
  
  
  CTAPIAudioBridgeSourceFilter::CTAPIAudioBridgeSourceFilter(
      IN LPUNKNOWN        pUnk, 
      OUT HRESULT *       phr
      ) 
      : CTAPIBridgeSourceFilter(pUnk, phr)
  {
      m_fPropSet = FALSE; // allocator properties not set yet
      m_fMtSet = FALSE; // media type not set yet
  
      // m_last_wall_time, m_last_stream_time not initiated
      m_fClockStarted = FALSE;
      m_fJustBurst = FALSE;
  
      m_nInputSize = 0;
      m_nOutputSize = 0;
      m_nOutputFree = 0;
      m_pOutputSample = NULL;
  }
  
  CTAPIAudioBridgeSourceFilter::~CTAPIAudioBridgeSourceFilter ()
  {
      if (m_fMtSet)
      {
          FreeMediaType (m_mt);
      }
  
      if (NULL != m_pOutputSample)
      {
          m_pOutputSample->Release ();
      }
  }
  
  HRESULT CTAPIAudioBridgeSourceFilter::CreateInstance(
      OUT IBaseFilter ** ppIBaseFilter
      )
  /*++
  
  Routine Description:
  
      This method create a instance of the bridge's sink filter.
  
  Arguments:
  
      ppIBaseFilter - the returned filter interface pointer.
  
  Return Value:
  
      E_OUTOFMEMORY - no memory for the new object.
  
  --*/
  {
      ENTER_FUNCTION("CTAPIAudioBridgeSourceFilter::CreateInstance");
  
      BGLOG((BG_TRACE, "%s entered.", __fxName));
  
      HRESULT hr = S_OK;
      CUnknown* pUnknown = new CTAPIAudioBridgeSourceFilter(NULL, &hr);
                  
      if (pUnknown == NULL) 
      {
          hr = E_OUTOFMEMORY;
          BGLOG((BG_ERROR,  
              "%s, out of memory creating the filter", 
              __fxName));
      }
      else if (FAILED(hr))
      {
          BGLOG((BG_ERROR, 
              "%s, the filter's constructor failed, hr:%d", 
              __fxName, hr));
  
          delete pUnknown;
      }
      else
      {
          pUnknown->NonDelegatingAddRef();
  
          hr = pUnknown->NonDelegatingQueryInterface(
              __uuidof(IBaseFilter), (void **)ppIBaseFilter
              );
  
          pUnknown->NonDelegatingRelease();
      }
  
      BGLOG((BG_TRACE, 
          "%s, returning:%p, hr:%x", __fxName, *ppIBaseFilter, hr));
  
      return hr;
  } 
  
  HRESULT CTAPIAudioBridgeSourceFilter::GetMediaType(
      IN      int     iPosition, 
      OUT     CMediaType *pMediaType
      )
  /*++
  
  Routine Description:
  
      Get the media type that this filter wants to support. Currently we
      only support PCM L16 8KHz samples.
  
  Arguments:
  
      IN  int iPosition, 
          the index of the media type, zero based..
          
      In  CMediaType *pMediaType
          Pointer to a CMediaType object to save the returned media type.
  
  Return Value:
  
      S_OK - success
      E_OUTOFMEMORY - no memory
  
  --*/
  {
      ENTER_FUNCTION("CTAPIAudioBridgeSourceFilter::GetMediaType");
  
      BGLOG((BG_TRACE, 
          "%s, iPosition:%d, pMediaType:%p", 
          __fxName, iPosition, pMediaType));
  
      ASSERT(!IsBadWritePtr(pMediaType, sizeof(AM_MEDIA_TYPE)));
  
     HRESULT hr;
     if (iPosition == 0)
     {
          AM_MEDIA_TYPE *pmt = NULL;
          hr = m_pOutputPin->GetFormat (&pmt);
          if (FAILED(hr))
              return hr;
          *pMediaType = *pmt;
          FreeMediaType (*pmt);
          free (pmt);
      }
      else
      {
          hr = VFW_S_NO_MORE_ITEMS;
      }
      // END
  
      BGLOG((BG_TRACE, "%s returns %d", __fxName, hr));
  
      return hr;
  }
  
  
  HRESULT CTAPIAudioBridgeSourceFilter::CheckMediaType(
      const CMediaType *pMediaType
      )
  /*++
  
  Routine Description:
  
      Check the media type that this filter wants to support. Currently we
      only support PCM L16 8KHz samples.
  
  Arguments:
  
      In  CMediaType *pMediaType
          Pointer to a CMediaType object to save the returned media type.
  
  Return Value:
  
      S_OK - success
      E_OUTOFMEMORY - no memory
      VFW_E_TYPE_NOT_ACCEPTED - media type rejected
      VFW_E_INVALIDMEDIATYPE  - bad media type
  
  --*/
  {
      ENTER_FUNCTION("CTAPIAudioBridgeSourceFilter::CheckMediaType");
  
      BGLOG((BG_TRACE, 
          "%s, pMediaType:%p", __fxName, pMediaType));
  
      ASSERT(!IsBadReadPtr(pMediaType, sizeof(AM_MEDIA_TYPE)));
  
      if (!m_fMtSet)
      {
          BGLOG ((BG_ERROR, "%s tries to check media type before setting", __fxName));
          return E_UNEXPECTED;
      }
  
      // create media type based on stored AM_MEDIA_TYPE
      CMediaType *pmediatype = new CMediaType (m_mt);
      if (NULL == pmediatype)
      {
          BGLOG ((BG_ERROR, "%s failed to new media type class", __fxName));
          return E_OUTOFMEMORY;
      }
  
      HRESULT hr;
      if (*pMediaType == *pmediatype)
          hr = S_OK;
      else
      {
          hr = VFW_E_TYPE_NOT_ACCEPTED;
          BGLOG ((BG_TRACE, "%s rejects media type class %p", __fxName, pMediaType));
      }
  
      delete pmediatype;
  
      BGLOG((BG_TRACE, "%s returns %d", __fxName, hr));
  
      return hr;
  }
  
  
  HRESULT CTAPIAudioBridgeSourceFilter::SendSample(
      IN IMediaSample *pSample
      )
  /*++
  
  Routine Description:
  
      Process a sample from the bridge sink filter. Overides the base implementation
  
  Arguments:
  
      pSample - The media sample object.
  
  Return Value:
  
      HRESULT.
  
  --*/
  {
      HRESULT hr;
  
      ENTER_FUNCTION ("CTAPIAudioBridgeSourceFilter::SendSample");
  
      CAutoLock Lock(m_pLock);
  
      _ASSERT(m_pOutputPin != NULL);
  
      // we don't deliver anything if the filter is not in running state.
      if (m_State != State_Running) 
      {
          return S_OK;
      }
  
      // if the sample is the 1st of the burst
      if (S_OK == pSample->IsDiscontinuity ())
      {
          LONGLONG start, end;
  
          m_fJustBurst = TRUE;
          if (S_OK != (hr = pSample->GetTime (&start, &end)))
          {
              BGLOG ((BG_TRACE, "%s, 1st sample in a burst, GetTime returns %x", __fxName, hr));
  
              // timestampes stored remain unchange
              return S_OK;
          }
          // else. in NT 5.1, 1st sample has valid timestamp.
      }
  
      // check if allocator properties is set
      if (!m_fPropSet)
      {
          BGLOG ((BG_ERROR, "%s tries to send sample before setting allocator property", __fxName));
          return E_UNEXPECTED;
      }
  
      // check if media type is set
      if (!m_fMtSet)
      {
          BGLOG ((BG_ERROR, "%s tries to send sample before setting media type", __fxName));
          return E_UNEXPECTED;
      }
  
      /*
      * get size info
      */
      // get input sample size and output allocator size
      HRESULT nInputSize, nOutputSize;
  
      nInputSize = pSample->GetActualDataLength ();
      nOutputSize = m_prop.cbBuffer;
  
      // 1st run, record size
      if (m_nInputSize == 0 || m_nOutputSize == 0)
      {
          m_nInputSize = nInputSize;
          m_nOutputSize = nOutputSize;
      }
  
      if (
          m_nInputSize != nInputSize ||
          m_nOutputSize != nOutputSize ||
          m_nInputSize == 0 ||
          m_nOutputSize == 0
          )
      {
          BGLOG ((BG_ERROR, "%s, sample size (%d => %d) or output size (%d => %d) is changed",
                __fxName, m_nInputSize, nInputSize, m_nOutputSize, nOutputSize));
          return E_UNEXPECTED;
      }
  
      /*
      * get time info
      */
      REFERENCE_TIME wall;
  
      // wall time
      if (FAILED (hr = m_pClock->GetTime (&wall)))
      {
          BGLOG ((BG_ERROR, "%s failed to get wall time", __fxName));
          return hr;
      }
  
      // if timestamp not initiated
      if (!m_fClockStarted)
      {
          m_last_stream_time = 0;
          m_last_wall_time = wall;
          m_fClockStarted = TRUE;
  
          // delta is the time of playing sample:
          m_output_sample_time = nOutputSize * 80000; // s->10000ns, bits->bytes
          m_output_sample_time /= ((WAVEFORMATEX*)m_mt.pbFormat)->wBitsPerSample *
                  ((WAVEFORMATEX*)m_mt.pbFormat)->nSamplesPerSec;
          m_output_sample_time *= 1000; // bytes/100ns
      }
  
      /*
      * calculate new stream time
      */
      if (m_fJustBurst)
      {
          // 1st useful sample after burst
          m_last_stream_time += (wall - m_last_wall_time);
          m_last_wall_time = wall;
  
          m_fJustBurst = FALSE;
  
          // clear buffer
          if (NULL != m_pOutputSample)
          {
              m_pOutputSample->Release ();
              m_pOutputSample = NULL;
              m_nOutputFree = 0;
          }
      }
  
      REFERENCE_TIME end = m_last_stream_time + m_output_sample_time;
  
      /*
      * case 1: input size == output size
      */
      if (m_nInputSize == m_nOutputSize)
      {
          if (FAILED (pSample->SetTime (&m_last_stream_time, &end)))
          {
              BGLOG ((BG_ERROR, "%s failed to set time", __fxName));
          }
  
          // adjust time
          m_last_stream_time = end;
          m_last_wall_time += m_output_sample_time;
  
          // deliver directly
          return m_pOutputPin->Deliver(pSample);
      }
  
      /*
      * case 2: size differs
      */
      BYTE *pInputBuffer, *pOutputBuffer;
  
      if (FAILED (hr = pSample->GetPointer (&pInputBuffer)))
      {
          BGLOG ((BG_ERROR, "%s failed to get buffer pointer from input sample %p",
              __fxName, pSample));
          return hr;
      }
      
      LONG nNextPos = 0;
  
      // old fashion goto
  DELIVERY_BUFFER:
  
      // get delivery buffer if it's null
      if (NULL == m_pOutputSample)
      {
          hr = m_pOutputPin->GetDeliveryBuffer (
              &m_pOutputSample, // media sample **
              NULL, // start time
              NULL, // end time
              AM_GBF_NOTASYNCPOINT // dynamic format changes are not allowed,
              );
          if (FAILED (hr))
          {
              BGLOG ((BG_ERROR, "%s, output pin failed to get delivery buffer. return %d",
                  __fxName, hr));
              return hr;
          }
  
          if (m_pOutputSample->GetSize() < m_nOutputSize)
          {
              // oops, what happend, the size should be the same
              BGLOG ((BG_ERROR, "%s, delivery buffer size %d and output size %d are inconsistent",
                  __fxName, m_pOutputSample->GetSize(), m_nOutputSize));
              return E_UNEXPECTED;
          }
  
          // set size
          if (FAILED (hr = m_pOutputSample->SetActualDataLength (m_nOutputSize)))
          {
              BGLOG ((BG_ERROR, "%s failed to set output sample size", __fxName));
              return hr;
          }
  /*
          // set format
          if (FAILED (hr = m_pOutputSample->SetMediaType (&m_mt)))
          {
              BGLOG ((BG_ERROR, "%s failed to set media type for delivery buffer", __fxName));
              return hr;
          }
  */
          // set time
          if (FAILED (hr = m_pOutputSample->SetTime (&m_last_stream_time, &end)))
          {
              BGLOG ((BG_ERROR, "%s failed to set stream time for delivery buffer", __fxName));
              return hr;
          }
  
          // the whole buffer is free
          m_nOutputFree = m_nOutputSize;
      }
  
      // get buffer in output sample
      if (FAILED (hr = m_pOutputSample->GetPointer (&pOutputBuffer)))
      {
          BGLOG ((BG_ERROR, "%s failed to get buffer pointer from output sample %p",
              __fxName, m_pOutputSample));
  
          // release output sample
          m_pOutputSample->Release ();
          m_pOutputSample = NULL;
          m_nOutputFree = 0;
  
          return hr;
      }
  
      // if input buffer is smaller than free output buffer
      // copy input to output and return
      if (m_nInputSize-nNextPos < m_nOutputFree)
      {
          CopyMemory (
              (PVOID)(pOutputBuffer + (m_nOutputSize - m_nOutputFree)),
              (PVOID)(pInputBuffer + nNextPos),
              (DWORD)(m_nInputSize - nNextPos)
              );
  
          // reduce free buffer size
          m_nOutputFree -= m_nInputSize - nNextPos;
  
          return S_OK;
      }
  
      // else: input buffer is greater or equal to free output buffer
      CopyMemory (
          (PVOID)(pOutputBuffer + (m_nOutputSize - m_nOutputFree)),
          (PVOID)(pInputBuffer + nNextPos),
          (DWORD)(m_nOutputFree)
          );
  
      // now output sample is full, deliver it
      if (FAILED (hr = m_pOutputPin->Deliver (m_pOutputSample)))
      {
          BGLOG ((BG_ERROR, "%s failed to deliver copied sample. return %x", __fxName, hr));
  
          // clear sample
          m_pOutputSample->Release ();
          m_pOutputSample = NULL;
          m_nOutputFree = 0;
  
          return hr;
      }
  
      // adjust next position in input buffer
      nNextPos += m_nOutputFree;
  
      // clear output sample since it was deliverd
      m_pOutputSample->Release ();
      m_pOutputSample = NULL;
      m_nOutputFree = 0;
  
      // adjust time
      m_last_stream_time = end;
      m_last_wall_time += m_output_sample_time;
  
      // check if nothing left
      if (nNextPos == m_nInputSize)
          return S_OK;
  
      // there is more in input buffer
      goto DELIVERY_BUFFER;
  }
  
  HRESULT CTAPIAudioBridgeSourceFilter::GetAllocatorProperties (OUT ALLOCATOR_PROPERTIES *pprop)
  /*++
  
  Routine Description:
  
      Returns the allocator properties
  
  Arguments:
  
      pprop -
          The pointer to an ALLOCATOR_PROPERTIES
  
  Return Value:
  
      E_POINTER -
          if pprop is NULL
  
      S_OK
  
  --*/
  {
      ENTER_FUNCTION ("CTAPIAudioBridgeSourceFilter::GetAllocatorProperties");
      _ASSERT(pprop);
  
      if (!pprop)
          return E_POINTER;
  
      if (!m_fPropSet)
      {
          BGLOG ((BG_INFO, "%s retrieves allocator properties before setting", __fxName));
          // return default value anyway
          // buffer size = (16bits / 8bits) * 8khz * 30 ms = 480 bytes
          pprop->cBuffers = 1;
          pprop->cbBuffer = 480; // default
          pprop->cbAlign = 0;
          pprop->cbPrefix = 0;
          return S_OK;
      }
  
      // properties were set
      pprop->cBuffers = m_prop.cBuffers;
      pprop->cbBuffer = m_prop.cbBuffer;
      pprop->cbAlign = m_prop.cbAlign;
      pprop->cbPrefix = m_prop.cbPrefix;
      return S_OK;
  }
  
  HRESULT CTAPIAudioBridgeSourceFilter::SuggestAllocatorProperties (IN const ALLOCATOR_PROPERTIES *pprop)
  /*++
  
  Routine Description:
  
      Asks the pin to use the allocator buffer properties
  
  Arguments:
  
      pprop -
          The pointer to an ALLOCATOR_PROPERTIES
  
  Return Value:
  
      E_POINTER
  
      S_OK
  
  --*/
  {
      _ASSERT (pprop);
  
      if (!pprop)
          return E_POINTER;
  
      m_prop.cBuffers = pprop->cBuffers;
      m_prop.cbBuffer = pprop->cbBuffer;
      m_prop.cbAlign = pprop->cbAlign;
      m_prop.cbPrefix = pprop->cbPrefix;
      m_fPropSet = TRUE;
  
      return S_OK;
  }
  
  HRESULT CTAPIAudioBridgeSourceFilter::GetFormat (OUT AM_MEDIA_TYPE **ppmt)
  /*++
  
  Routine Description:
  
      Retrieves the stream format
  
  Arguments:
  
      ppmt -
          Address of a pointer to an AM_MEDIA_TYPE structure
  
  Return Value:
  
      E_PONTER
  
      E_OUTOFMEMORY
  
      HRESULT of SetFormat
  
      S_OK
  
  --*/
  {
      ENTER_FUNCTION ("CTAPIAudioBridgeSourceFilter::GetFormat");
  
      _ASSERT (ppmt);
      if (NULL == ppmt)
          return E_POINTER;
  
      if (NULL != *ppmt)
          BGLOG ((BG_INFO, "is media type structure freed when passed in %s?", __fxName));
  
      *ppmt = new AM_MEDIA_TYPE;
      if (NULL == *ppmt)
      {
          BGLOG ((BG_ERROR, "%s out of memory", __fxName));
          return E_OUTOFMEMORY;
      }
  
      if (!m_fMtSet)
      {
          BGLOG ((BG_INFO, "%s retrieves media type before setting. Default is to set.", __fxName));
  
          // st format
          HRESULT hr;
          AM_MEDIA_TYPE mt;
          WAVEFORMATEX wfx;
  
          wfx.wFormatTag          = WAVE_FORMAT_PCM;
          wfx.wBitsPerSample      = 16;
          wfx.nChannels           = 1;
          wfx.nSamplesPerSec      = 8000;
          wfx.nBlockAlign         = wfx.wBitsPerSample * wfx.nChannels / 8;
          wfx.nAvgBytesPerSec     = ((DWORD) wfx.nBlockAlign * wfx.nSamplesPerSec);
          wfx.cbSize              = 0;
  
          mt.majortype            = MEDIATYPE_Audio;
          mt.subtype              = MEDIASUBTYPE_PCM;
          mt.bFixedSizeSamples    = TRUE;
          mt.bTemporalCompression = FALSE;
          mt.lSampleSize          = 0;
          mt.formattype           = FORMAT_WaveFormatEx;
          mt.pUnk                 = NULL;
          mt.cbFormat             = sizeof(WAVEFORMATEX);
          mt.pbFormat             = (BYTE*)&wfx;
  
          hr = SetFormat (&mt);
          if (FAILED (hr))
          {
              BGLOG ((BG_ERROR, "%s, failed to set default format", __fxName));
              return hr;
          }
      }
  
      CopyMediaType (*ppmt, &m_mt);
  
      return S_OK;
  }
  
  HRESULT CTAPIAudioBridgeSourceFilter::SetFormat (IN AM_MEDIA_TYPE *pmt)
  /*++
  
  Routine Description:
  
      Sets the stream format
  
  Arguments:
  
      pmt -
          Pointer to an AM_MEDIA_TYPE structure
  
  Return Value:
  
      E_POINTER
  
      S_OK
  
  --*/
  {
      _ASSERT (pmt);
  
      if (NULL == pmt)
          return E_POINTER;
  
      CopyMediaType (&m_mt, pmt);
      m_fMtSet = TRUE;
      return S_OK;
  }
  