|
|
/*++
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; }
|