2001-10-01 22:53:58 +02:00
|
|
|
/*
|
|
|
|
* Audio Renderer (CLSID_AudioRender)
|
|
|
|
*
|
|
|
|
* FIXME
|
|
|
|
* - implements IRefereneceClock.
|
|
|
|
* - implements seeking.
|
|
|
|
*
|
|
|
|
* hidenori@a2.ctktv.ne.jp
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
#include "windef.h"
|
|
|
|
#include "winbase.h"
|
|
|
|
#include "wingdi.h"
|
|
|
|
#include "winuser.h"
|
|
|
|
#include "mmsystem.h"
|
|
|
|
#include "winerror.h"
|
|
|
|
#include "strmif.h"
|
|
|
|
#include "control.h"
|
|
|
|
#include "vfwmsgs.h"
|
|
|
|
#include "uuids.h"
|
2001-10-08 22:56:08 +02:00
|
|
|
#include "evcode.h"
|
2001-10-01 22:53:58 +02:00
|
|
|
|
|
|
|
#include "debugtools.h"
|
|
|
|
DEFAULT_DEBUG_CHANNEL(quartz);
|
|
|
|
|
|
|
|
#include "quartz_private.h"
|
|
|
|
#include "audren.h"
|
2001-10-22 21:03:26 +02:00
|
|
|
#include "seekpass.h"
|
2001-10-01 22:53:58 +02:00
|
|
|
|
|
|
|
|
|
|
|
static const WCHAR QUARTZ_AudioRender_Name[] =
|
|
|
|
{ 'A','u','d','i','o',' ','R','e','n','d','e','r',0 };
|
|
|
|
static const WCHAR QUARTZ_AudioRenderPin_Name[] =
|
|
|
|
{ 'I','n',0 };
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
*
|
|
|
|
* CAudioRendererImpl waveOut methods (internal)
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
static
|
|
|
|
HRESULT QUARTZ_HRESULT_From_MMRESULT( MMRESULT mr )
|
|
|
|
{
|
|
|
|
HRESULT hr = E_FAIL;
|
|
|
|
|
|
|
|
switch ( mr )
|
|
|
|
{
|
|
|
|
case MMSYSERR_NOERROR:
|
|
|
|
hr = S_OK;
|
|
|
|
break;
|
|
|
|
case MMSYSERR_NOMEM:
|
|
|
|
hr = E_OUTOFMEMORY;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CAudioRendererImpl_waveOutEventCallback(
|
|
|
|
HWAVEOUT hwo, UINT uMsg,
|
|
|
|
DWORD dwInstance, DWORD dwParam1, DWORD dwParam2 )
|
|
|
|
{
|
|
|
|
CAudioRendererImpl* This = (CAudioRendererImpl*)dwInstance;
|
|
|
|
|
|
|
|
if ( uMsg == WOM_DONE )
|
|
|
|
SetEvent( This->m_hEventRender );
|
|
|
|
}
|
|
|
|
|
|
|
|
static
|
|
|
|
void CAudioRendererImpl_waveOutReset(
|
|
|
|
CAudioRendererImpl* This )
|
|
|
|
{
|
|
|
|
if ( !This->m_fWaveOutInit )
|
|
|
|
return;
|
|
|
|
|
|
|
|
waveOutReset( This->m_hWaveOut );
|
|
|
|
SetEvent( This->m_hEventRender );
|
|
|
|
}
|
|
|
|
|
|
|
|
static
|
|
|
|
void CAudioRendererImpl_waveOutUninit(
|
|
|
|
CAudioRendererImpl* This )
|
|
|
|
{
|
|
|
|
DWORD i;
|
|
|
|
|
|
|
|
TRACE("(%p)\n",This);
|
|
|
|
|
|
|
|
if ( !This->m_fWaveOutInit )
|
|
|
|
return;
|
|
|
|
|
|
|
|
waveOutReset( This->m_hWaveOut );
|
|
|
|
SetEvent( This->m_hEventRender );
|
|
|
|
|
|
|
|
for ( i = 0; i < WINE_QUARTZ_WAVEOUT_COUNT; i++ )
|
|
|
|
{
|
|
|
|
if ( This->m_hdr[i].dwFlags & WHDR_PREPARED )
|
|
|
|
{
|
|
|
|
waveOutUnprepareHeader(
|
|
|
|
This->m_hWaveOut,
|
|
|
|
&This->m_hdr[i], sizeof(WAVEHDR) );
|
|
|
|
This->m_hdr[i].dwFlags = 0;
|
|
|
|
}
|
|
|
|
if ( This->m_hdr[i].lpData != NULL )
|
|
|
|
{
|
|
|
|
QUARTZ_FreeMem( This->m_hdr[i].lpData );
|
|
|
|
This->m_hdr[i].lpData = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
waveOutClose( This->m_hWaveOut );
|
|
|
|
This->m_hWaveOut = (HWAVEOUT)NULL;
|
|
|
|
if ( This->m_hEventRender != (HANDLE)NULL )
|
|
|
|
{
|
|
|
|
CloseHandle( This->m_hEventRender );
|
|
|
|
This->m_hEventRender = (HANDLE)NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
This->m_fWaveOutInit = FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static
|
|
|
|
HRESULT CAudioRendererImpl_waveOutInit(
|
|
|
|
CAudioRendererImpl* This, WAVEFORMATEX* pwfx )
|
|
|
|
{
|
|
|
|
MMRESULT mr;
|
|
|
|
HRESULT hr;
|
|
|
|
DWORD i;
|
|
|
|
DWORD dwBlockSize;
|
|
|
|
|
|
|
|
if ( This->m_fWaveOutInit )
|
2001-10-22 21:03:26 +02:00
|
|
|
return NOERROR;
|
2001-10-01 22:53:58 +02:00
|
|
|
|
|
|
|
if ( pwfx == NULL )
|
|
|
|
return E_POINTER;
|
|
|
|
if ( pwfx->nBlockAlign == 0 )
|
|
|
|
return E_INVALIDARG;
|
|
|
|
|
|
|
|
This->m_hEventRender = (HANDLE)NULL;
|
|
|
|
This->m_hWaveOut = (HWAVEOUT)NULL;
|
|
|
|
This->m_dwBlockSize = 0;
|
|
|
|
This->m_phdrCur = NULL;
|
|
|
|
ZeroMemory( &This->m_hdr, sizeof(This->m_hdr) );
|
|
|
|
|
|
|
|
|
|
|
|
mr = waveOutOpen(
|
|
|
|
&This->m_hWaveOut, WAVE_MAPPER, pwfx,
|
|
|
|
(DWORD)CAudioRendererImpl_waveOutEventCallback, (DWORD)This,
|
|
|
|
CALLBACK_FUNCTION );
|
|
|
|
hr = QUARTZ_HRESULT_From_MMRESULT( mr );
|
|
|
|
if ( FAILED(hr) )
|
|
|
|
return hr;
|
|
|
|
This->m_fWaveOutInit = TRUE;
|
|
|
|
|
|
|
|
This->m_hEventRender = CreateEventA(
|
|
|
|
NULL, TRUE, TRUE, NULL );
|
|
|
|
if ( This->m_hEventRender == (HANDLE)NULL )
|
|
|
|
{
|
|
|
|
hr = E_OUTOFMEMORY;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
dwBlockSize = pwfx->nAvgBytesPerSec / pwfx->nBlockAlign;
|
|
|
|
if ( dwBlockSize == 0 )
|
|
|
|
dwBlockSize = 1;
|
|
|
|
dwBlockSize *= pwfx->nBlockAlign;
|
|
|
|
This->m_dwBlockSize = dwBlockSize;
|
|
|
|
|
|
|
|
for ( i = 0; i < WINE_QUARTZ_WAVEOUT_COUNT; i++ )
|
|
|
|
{
|
|
|
|
This->m_hdr[i].lpData = (CHAR*)QUARTZ_AllocMem( dwBlockSize );
|
|
|
|
if ( This->m_hdr[i].lpData == NULL )
|
|
|
|
{
|
|
|
|
hr = E_OUTOFMEMORY;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
mr = waveOutPrepareHeader(
|
|
|
|
This->m_hWaveOut,
|
|
|
|
&This->m_hdr[i], sizeof(WAVEHDR) );
|
|
|
|
hr = QUARTZ_HRESULT_From_MMRESULT( mr );
|
|
|
|
if ( FAILED(hr) )
|
|
|
|
goto err;
|
|
|
|
This->m_hdr[i].dwFlags |= WHDR_DONE;
|
|
|
|
This->m_hdr[i].dwBufferLength = dwBlockSize;
|
|
|
|
This->m_hdr[i].dwUser = i;
|
|
|
|
}
|
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
err:
|
|
|
|
CAudioRendererImpl_waveOutUninit(This);
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
2002-01-22 00:37:53 +01:00
|
|
|
#if 0
|
|
|
|
/* FIXME: Not used for now */
|
|
|
|
|
2001-10-22 21:03:26 +02:00
|
|
|
static HRESULT CAudioRendererImpl_waveOutPause( CAudioRendererImpl* This )
|
|
|
|
{
|
|
|
|
if ( !This->m_fWaveOutInit )
|
|
|
|
return E_UNEXPECTED;
|
|
|
|
|
|
|
|
return QUARTZ_HRESULT_From_MMRESULT( waveOutPause(
|
|
|
|
This->m_hWaveOut ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT CAudioRendererImpl_waveOutRun( CAudioRendererImpl* This )
|
|
|
|
{
|
|
|
|
if ( !This->m_fWaveOutInit )
|
|
|
|
return E_UNEXPECTED;
|
|
|
|
|
|
|
|
return QUARTZ_HRESULT_From_MMRESULT( waveOutRestart(
|
|
|
|
This->m_hWaveOut ) );
|
|
|
|
}
|
2002-01-22 00:37:53 +01:00
|
|
|
#endif
|
2001-10-22 21:03:26 +02:00
|
|
|
|
2001-10-01 22:53:58 +02:00
|
|
|
static
|
|
|
|
WAVEHDR* CAudioRendererImpl_waveOutGetBuffer(
|
|
|
|
CAudioRendererImpl* This )
|
|
|
|
{
|
|
|
|
DWORD i;
|
|
|
|
|
|
|
|
if ( !This->m_fWaveOutInit )
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if ( This->m_phdrCur != NULL )
|
|
|
|
return This->m_phdrCur;
|
|
|
|
|
|
|
|
for ( i = 0; i < WINE_QUARTZ_WAVEOUT_COUNT; i++ )
|
|
|
|
{
|
|
|
|
if ( This->m_hdr[i].dwFlags & WHDR_DONE )
|
|
|
|
{
|
|
|
|
This->m_phdrCur = &(This->m_hdr[i]);
|
|
|
|
This->m_phdrCur->dwFlags &= ~WHDR_DONE;
|
|
|
|
This->m_phdrCur->dwBufferLength = 0;
|
|
|
|
return This->m_phdrCur;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static
|
|
|
|
HRESULT CAudioRendererImpl_waveOutWriteData(
|
|
|
|
CAudioRendererImpl* This,
|
|
|
|
const BYTE* pData, DWORD cbData, DWORD* pcbWritten )
|
|
|
|
{
|
|
|
|
DWORD cbAvail;
|
|
|
|
|
|
|
|
*pcbWritten = 0;
|
|
|
|
|
|
|
|
if ( !This->m_fWaveOutInit )
|
|
|
|
return E_UNEXPECTED;
|
|
|
|
|
|
|
|
if ( cbData == 0 )
|
|
|
|
return S_OK;
|
|
|
|
|
|
|
|
if ( CAudioRendererImpl_waveOutGetBuffer(This) == NULL )
|
|
|
|
return S_FALSE;
|
|
|
|
|
|
|
|
cbAvail = This->m_dwBlockSize - This->m_phdrCur->dwBufferLength;
|
|
|
|
if ( cbAvail > cbData )
|
|
|
|
cbAvail = cbData;
|
|
|
|
memcpy( This->m_phdrCur->lpData, pData, cbAvail );
|
|
|
|
pData += cbAvail;
|
|
|
|
cbData -= cbAvail;
|
|
|
|
This->m_phdrCur->dwBufferLength += cbAvail;
|
|
|
|
|
|
|
|
*pcbWritten = cbAvail;
|
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static
|
|
|
|
HRESULT CAudioRendererImpl_waveOutFlush(
|
|
|
|
CAudioRendererImpl* This )
|
|
|
|
{
|
|
|
|
MMRESULT mr;
|
|
|
|
HRESULT hr;
|
|
|
|
|
|
|
|
if ( !This->m_fWaveOutInit )
|
|
|
|
return E_UNEXPECTED;
|
|
|
|
if ( This->m_phdrCur == NULL )
|
|
|
|
return E_UNEXPECTED;
|
|
|
|
|
|
|
|
if ( This->m_phdrCur->dwBufferLength == 0 )
|
|
|
|
return S_OK;
|
|
|
|
|
|
|
|
mr = waveOutWrite(
|
|
|
|
This->m_hWaveOut,
|
|
|
|
This->m_phdrCur, sizeof(WAVEHDR) );
|
|
|
|
hr = QUARTZ_HRESULT_From_MMRESULT( mr );
|
|
|
|
if ( FAILED(hr) )
|
|
|
|
return hr;
|
|
|
|
|
|
|
|
This->m_phdrCur = NULL;
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
2002-01-22 00:37:53 +01:00
|
|
|
#if 0
|
|
|
|
/* FIXME: Not used for now */
|
|
|
|
|
2001-10-01 22:53:58 +02:00
|
|
|
static
|
|
|
|
HRESULT CAudioRendererImpl_waveOutGetVolume(
|
|
|
|
CAudioRendererImpl* This,
|
|
|
|
DWORD* pdwLeft, DWORD* pdwRight )
|
|
|
|
{
|
|
|
|
MMRESULT mr;
|
|
|
|
HRESULT hr;
|
|
|
|
DWORD dwVol;
|
|
|
|
|
|
|
|
if ( !This->m_fWaveOutInit )
|
|
|
|
return E_UNEXPECTED;
|
|
|
|
|
|
|
|
mr = waveOutGetVolume(
|
|
|
|
This->m_hWaveOut, &dwVol );
|
|
|
|
hr = QUARTZ_HRESULT_From_MMRESULT( mr );
|
|
|
|
if ( FAILED(hr) )
|
|
|
|
return hr;
|
|
|
|
|
|
|
|
*pdwLeft = LOWORD(dwVol);
|
|
|
|
*pdwRight = HIWORD(dwVol);
|
|
|
|
|
|
|
|
return NOERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
static
|
|
|
|
HRESULT CAudioRendererImpl_waveOutSetVolume(
|
|
|
|
CAudioRendererImpl* This,
|
|
|
|
DWORD dwLeft, DWORD dwRight )
|
|
|
|
{
|
|
|
|
MMRESULT mr;
|
|
|
|
DWORD dwVol;
|
|
|
|
|
|
|
|
if ( !This->m_fWaveOutInit )
|
|
|
|
return E_UNEXPECTED;
|
|
|
|
|
|
|
|
dwVol = dwLeft | (dwRight<<16);
|
|
|
|
|
|
|
|
mr = waveOutSetVolume(
|
|
|
|
This->m_hWaveOut, dwVol );
|
|
|
|
return QUARTZ_HRESULT_From_MMRESULT( mr );
|
|
|
|
}
|
2002-01-22 00:37:53 +01:00
|
|
|
#endif
|
2001-10-01 22:53:58 +02:00
|
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
*
|
|
|
|
* CAudioRendererImpl methods
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
static HRESULT CAudioRendererImpl_OnActive( CBaseFilterImpl* pImpl )
|
2001-10-22 21:03:26 +02:00
|
|
|
{
|
|
|
|
CAudioRendererImpl_THIS(pImpl,basefilter);
|
2002-01-22 00:37:53 +01:00
|
|
|
/* HRESULT hr; */
|
2001-10-22 21:03:26 +02:00
|
|
|
|
|
|
|
FIXME( "(%p)\n", This );
|
|
|
|
|
|
|
|
if ( This->pPin->pin.pmtConn == NULL )
|
|
|
|
return NOERROR;
|
|
|
|
|
|
|
|
This->m_fInFlush = FALSE;
|
|
|
|
|
|
|
|
/* FIXME - don't work correctly.
|
|
|
|
hr = CAudioRendererImpl_waveOutRun(This);
|
|
|
|
if ( FAILED(hr) )
|
|
|
|
return hr;
|
|
|
|
*/
|
|
|
|
|
|
|
|
return NOERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT CAudioRendererImpl_OnInactive( CBaseFilterImpl* pImpl )
|
2001-10-01 22:53:58 +02:00
|
|
|
{
|
|
|
|
CAudioRendererImpl_THIS(pImpl,basefilter);
|
|
|
|
WAVEFORMATEX* pwfx;
|
2001-10-22 21:03:26 +02:00
|
|
|
HRESULT hr;
|
2001-10-01 22:53:58 +02:00
|
|
|
|
|
|
|
FIXME( "(%p)\n", This );
|
|
|
|
|
|
|
|
if ( This->pPin->pin.pmtConn == NULL )
|
|
|
|
return NOERROR;
|
|
|
|
|
|
|
|
pwfx = (WAVEFORMATEX*)This->pPin->pin.pmtConn->pbFormat;
|
|
|
|
if ( pwfx == NULL )
|
|
|
|
return E_FAIL;
|
|
|
|
|
|
|
|
This->m_fInFlush = FALSE;
|
|
|
|
|
2001-10-22 21:03:26 +02:00
|
|
|
hr = CAudioRendererImpl_waveOutInit(This,pwfx);
|
|
|
|
if ( FAILED(hr) )
|
|
|
|
return hr;
|
|
|
|
|
|
|
|
/* FIXME - may cause deadlock.
|
|
|
|
hr = CAudioRendererImpl_waveOutPause(This);
|
|
|
|
if ( FAILED(hr) )
|
|
|
|
return hr;
|
|
|
|
*/
|
|
|
|
|
|
|
|
return NOERROR;
|
2001-10-01 22:53:58 +02:00
|
|
|
}
|
|
|
|
|
2001-10-22 21:03:26 +02:00
|
|
|
static HRESULT CAudioRendererImpl_OnStop( CBaseFilterImpl* pImpl )
|
2001-10-01 22:53:58 +02:00
|
|
|
{
|
|
|
|
CAudioRendererImpl_THIS(pImpl,basefilter);
|
|
|
|
|
|
|
|
FIXME( "(%p)\n", This );
|
|
|
|
|
|
|
|
CAudioRendererImpl_waveOutUninit(This);
|
|
|
|
|
|
|
|
This->m_fInFlush = FALSE;
|
|
|
|
|
|
|
|
TRACE("returned\n" );
|
|
|
|
|
|
|
|
return NOERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const CBaseFilterHandlers filterhandlers =
|
|
|
|
{
|
|
|
|
CAudioRendererImpl_OnActive, /* pOnActive */
|
|
|
|
CAudioRendererImpl_OnInactive, /* pOnInactive */
|
2001-10-22 21:03:26 +02:00
|
|
|
CAudioRendererImpl_OnStop, /* pOnStop */
|
2001-10-01 22:53:58 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
*
|
|
|
|
* CAudioRendererPinImpl methods
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2001-10-22 21:03:26 +02:00
|
|
|
static HRESULT CAudioRendererPinImpl_OnDisconnect( CPinBaseImpl* pImpl )
|
|
|
|
{
|
|
|
|
CAudioRendererPinImpl_THIS(pImpl,pin);
|
|
|
|
|
|
|
|
TRACE("(%p)\n",This);
|
|
|
|
|
|
|
|
if ( This->meminput.pAllocator != NULL )
|
|
|
|
{
|
|
|
|
IMemAllocator_Decommit(This->meminput.pAllocator);
|
|
|
|
IMemAllocator_Release(This->meminput.pAllocator);
|
|
|
|
This->meminput.pAllocator = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NOERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2001-10-01 22:53:58 +02:00
|
|
|
static HRESULT CAudioRendererPinImpl_CheckMediaType( CPinBaseImpl* pImpl, const AM_MEDIA_TYPE* pmt )
|
|
|
|
{
|
|
|
|
CAudioRendererPinImpl_THIS(pImpl,pin);
|
|
|
|
const WAVEFORMATEX* pwfx;
|
|
|
|
|
2001-10-08 22:56:08 +02:00
|
|
|
TRACE("(%p,%p)\n",This,pmt);
|
2001-10-01 22:53:58 +02:00
|
|
|
|
|
|
|
if ( !IsEqualGUID( &pmt->majortype, &MEDIATYPE_Audio ) )
|
2001-10-22 21:03:26 +02:00
|
|
|
{
|
|
|
|
TRACE("not audio\n");
|
2001-10-01 22:53:58 +02:00
|
|
|
return E_FAIL;
|
2001-10-22 21:03:26 +02:00
|
|
|
}
|
|
|
|
if ( !IsEqualGUID( &pmt->subtype, &MEDIASUBTYPE_NULL ) &&
|
|
|
|
!IsEqualGUID( &pmt->subtype, &MEDIASUBTYPE_PCM ) )
|
|
|
|
{
|
|
|
|
TRACE("not PCM\n");
|
2001-10-01 22:53:58 +02:00
|
|
|
return E_FAIL;
|
2001-10-22 21:03:26 +02:00
|
|
|
}
|
2001-10-01 22:53:58 +02:00
|
|
|
if ( !IsEqualGUID( &pmt->formattype, &FORMAT_WaveFormatEx ) )
|
2001-10-22 21:03:26 +02:00
|
|
|
{
|
|
|
|
TRACE("not WAVE\n");
|
2001-10-01 22:53:58 +02:00
|
|
|
return E_FAIL;
|
2001-10-22 21:03:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
TRACE("testing WAVE header\n");
|
|
|
|
|
2001-10-01 22:53:58 +02:00
|
|
|
if ( pmt->cbFormat < (sizeof(WAVEFORMATEX)-sizeof(WORD)) )
|
|
|
|
return E_FAIL;
|
|
|
|
|
|
|
|
pwfx = (const WAVEFORMATEX*)pmt->pbFormat;
|
|
|
|
if ( pwfx == NULL )
|
|
|
|
return E_FAIL;
|
|
|
|
if ( pwfx->wFormatTag != 1 )
|
|
|
|
return E_FAIL;
|
|
|
|
|
2001-10-22 21:03:26 +02:00
|
|
|
TRACE("returned successfully.\n");
|
|
|
|
|
2001-10-01 22:53:58 +02:00
|
|
|
return NOERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT CAudioRendererPinImpl_Receive( CPinBaseImpl* pImpl, IMediaSample* pSample )
|
|
|
|
{
|
|
|
|
CAudioRendererPinImpl_THIS(pImpl,pin);
|
|
|
|
HRESULT hr;
|
|
|
|
BYTE* pData = NULL;
|
|
|
|
DWORD dwDataLength;
|
|
|
|
DWORD dwWritten;
|
|
|
|
|
|
|
|
FIXME( "(%p,%p)\n",This,pSample );
|
|
|
|
|
|
|
|
if ( !This->pRender->m_fWaveOutInit )
|
|
|
|
return E_UNEXPECTED;
|
|
|
|
if ( This->pRender->m_fInFlush )
|
|
|
|
return S_FALSE;
|
|
|
|
if ( pSample == NULL )
|
|
|
|
return E_POINTER;
|
|
|
|
|
|
|
|
hr = IMediaSample_GetPointer( pSample, &pData );
|
|
|
|
if ( FAILED(hr) )
|
|
|
|
return hr;
|
|
|
|
dwDataLength = (DWORD)IMediaSample_GetActualDataLength( pSample );
|
|
|
|
|
|
|
|
while ( 1 )
|
|
|
|
{
|
|
|
|
TRACE("trying to write %lu bytes\n",dwDataLength);
|
|
|
|
|
|
|
|
ResetEvent( This->pRender->m_hEventRender );
|
|
|
|
hr = CAudioRendererImpl_waveOutWriteData(
|
|
|
|
This->pRender,pData,dwDataLength,&dwWritten);
|
|
|
|
if ( FAILED(hr) )
|
|
|
|
break;
|
|
|
|
if ( hr == S_FALSE )
|
|
|
|
{
|
|
|
|
WaitForSingleObject( This->pRender->m_hEventRender, INFINITE );
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
pData += dwWritten;
|
|
|
|
dwDataLength -= dwWritten;
|
|
|
|
hr = CAudioRendererImpl_waveOutFlush(This->pRender);
|
|
|
|
if ( FAILED(hr) )
|
|
|
|
break;
|
|
|
|
if ( dwDataLength == 0 )
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT CAudioRendererPinImpl_ReceiveCanBlock( CPinBaseImpl* pImpl )
|
|
|
|
{
|
|
|
|
CAudioRendererPinImpl_THIS(pImpl,pin);
|
|
|
|
|
|
|
|
TRACE( "(%p)\n", This );
|
|
|
|
|
|
|
|
/* might block. */
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT CAudioRendererPinImpl_EndOfStream( CPinBaseImpl* pImpl )
|
|
|
|
{
|
|
|
|
CAudioRendererPinImpl_THIS(pImpl,pin);
|
|
|
|
|
|
|
|
FIXME( "(%p)\n", This );
|
|
|
|
|
|
|
|
This->pRender->m_fInFlush = FALSE;
|
|
|
|
|
2001-10-08 22:56:08 +02:00
|
|
|
/* FIXME - don't notify twice until stopped or seeked. */
|
|
|
|
return CBaseFilterImpl_MediaEventNotify(
|
|
|
|
&This->pRender->basefilter, EC_COMPLETE,
|
|
|
|
(LONG_PTR)S_OK, (LONG_PTR)(IBaseFilter*)(This->pRender) );
|
2001-10-01 22:53:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT CAudioRendererPinImpl_BeginFlush( CPinBaseImpl* pImpl )
|
|
|
|
{
|
|
|
|
CAudioRendererPinImpl_THIS(pImpl,pin);
|
|
|
|
|
|
|
|
FIXME( "(%p)\n", This );
|
|
|
|
|
|
|
|
This->pRender->m_fInFlush = TRUE;
|
|
|
|
CAudioRendererImpl_waveOutReset(This->pRender);
|
|
|
|
|
|
|
|
return NOERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT CAudioRendererPinImpl_EndFlush( CPinBaseImpl* pImpl )
|
|
|
|
{
|
|
|
|
CAudioRendererPinImpl_THIS(pImpl,pin);
|
|
|
|
|
|
|
|
FIXME( "(%p)\n", This );
|
|
|
|
|
|
|
|
This->pRender->m_fInFlush = FALSE;
|
|
|
|
|
|
|
|
return NOERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT CAudioRendererPinImpl_NewSegment( CPinBaseImpl* pImpl, REFERENCE_TIME rtStart, REFERENCE_TIME rtStop, double rate )
|
|
|
|
{
|
|
|
|
CAudioRendererPinImpl_THIS(pImpl,pin);
|
|
|
|
|
|
|
|
FIXME( "(%p)\n", This );
|
|
|
|
|
2001-10-08 22:56:08 +02:00
|
|
|
This->pRender->m_fInFlush = FALSE;
|
|
|
|
|
2001-10-01 22:53:58 +02:00
|
|
|
return NOERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const CBasePinHandlers pinhandlers =
|
|
|
|
{
|
2001-10-08 22:56:08 +02:00
|
|
|
NULL, /* pOnPreConnect */
|
|
|
|
NULL, /* pOnPostConnect */
|
2001-10-22 21:03:26 +02:00
|
|
|
CAudioRendererPinImpl_OnDisconnect, /* pOnDisconnect */
|
2001-10-01 22:53:58 +02:00
|
|
|
CAudioRendererPinImpl_CheckMediaType, /* pCheckMediaType */
|
|
|
|
NULL, /* pQualityNotify */
|
|
|
|
CAudioRendererPinImpl_Receive, /* pReceive */
|
|
|
|
CAudioRendererPinImpl_ReceiveCanBlock, /* pReceiveCanBlock */
|
|
|
|
CAudioRendererPinImpl_EndOfStream, /* pEndOfStream */
|
|
|
|
CAudioRendererPinImpl_BeginFlush, /* pBeginFlush */
|
|
|
|
CAudioRendererPinImpl_EndFlush, /* pEndFlush */
|
|
|
|
CAudioRendererPinImpl_NewSegment, /* pNewSegment */
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
*
|
|
|
|
* new/delete CAudioRendererImpl
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* can I use offsetof safely? - FIXME? */
|
|
|
|
static QUARTZ_IFEntry FilterIFEntries[] =
|
|
|
|
{
|
|
|
|
{ &IID_IPersist, offsetof(CAudioRendererImpl,basefilter)-offsetof(CAudioRendererImpl,unk) },
|
|
|
|
{ &IID_IMediaFilter, offsetof(CAudioRendererImpl,basefilter)-offsetof(CAudioRendererImpl,unk) },
|
|
|
|
{ &IID_IBaseFilter, offsetof(CAudioRendererImpl,basefilter)-offsetof(CAudioRendererImpl,unk) },
|
|
|
|
{ &IID_IBasicAudio, offsetof(CAudioRendererImpl,basaud)-offsetof(CAudioRendererImpl,unk) },
|
|
|
|
};
|
|
|
|
|
2001-10-22 21:03:26 +02:00
|
|
|
static HRESULT CAudioRendererImpl_OnQueryInterface(
|
|
|
|
IUnknown* punk, const IID* piid, void** ppobj )
|
|
|
|
{
|
|
|
|
CAudioRendererImpl_THIS(punk,unk);
|
|
|
|
|
|
|
|
if ( This->pSeekPass == NULL )
|
|
|
|
return E_NOINTERFACE;
|
|
|
|
|
|
|
|
if ( IsEqualGUID( &IID_IMediaPosition, piid ) ||
|
|
|
|
IsEqualGUID( &IID_IMediaSeeking, piid ) )
|
|
|
|
{
|
|
|
|
TRACE( "IMediaSeeking(or IMediaPosition) is queried\n" );
|
|
|
|
return IUnknown_QueryInterface( (IUnknown*)(&This->pSeekPass->unk), piid, ppobj );
|
|
|
|
}
|
|
|
|
|
|
|
|
return E_NOINTERFACE;
|
|
|
|
}
|
|
|
|
|
2001-10-01 22:53:58 +02:00
|
|
|
static void QUARTZ_DestroyAudioRenderer(IUnknown* punk)
|
|
|
|
{
|
|
|
|
CAudioRendererImpl_THIS(punk,unk);
|
|
|
|
|
|
|
|
TRACE( "(%p)\n", This );
|
|
|
|
|
|
|
|
if ( This->pPin != NULL )
|
|
|
|
{
|
|
|
|
IUnknown_Release(This->pPin->unk.punkControl);
|
|
|
|
This->pPin = NULL;
|
|
|
|
}
|
2001-10-22 21:03:26 +02:00
|
|
|
if ( This->pSeekPass != NULL )
|
|
|
|
{
|
|
|
|
IUnknown_Release((IUnknown*)&This->pSeekPass->unk);
|
|
|
|
This->pSeekPass = NULL;
|
|
|
|
}
|
2001-10-01 22:53:58 +02:00
|
|
|
|
|
|
|
CAudioRendererImpl_UninitIBasicAudio(This);
|
|
|
|
CBaseFilterImpl_UninitIBaseFilter(&This->basefilter);
|
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT QUARTZ_CreateAudioRenderer(IUnknown* punkOuter,void** ppobj)
|
|
|
|
{
|
|
|
|
CAudioRendererImpl* This = NULL;
|
|
|
|
HRESULT hr;
|
|
|
|
|
|
|
|
TRACE("(%p,%p)\n",punkOuter,ppobj);
|
|
|
|
|
|
|
|
This = (CAudioRendererImpl*)
|
|
|
|
QUARTZ_AllocObj( sizeof(CAudioRendererImpl) );
|
|
|
|
if ( This == NULL )
|
|
|
|
return E_OUTOFMEMORY;
|
2001-10-22 21:03:26 +02:00
|
|
|
This->pSeekPass = NULL;
|
2001-10-01 22:53:58 +02:00
|
|
|
This->pPin = NULL;
|
|
|
|
This->m_fInFlush = FALSE;
|
|
|
|
This->m_fWaveOutInit = FALSE;
|
|
|
|
This->m_hEventRender = (HANDLE)NULL;
|
|
|
|
|
|
|
|
QUARTZ_IUnkInit( &This->unk, punkOuter );
|
2001-10-22 21:03:26 +02:00
|
|
|
This->qiext.pNext = NULL;
|
|
|
|
This->qiext.pOnQueryInterface = &CAudioRendererImpl_OnQueryInterface;
|
|
|
|
QUARTZ_IUnkAddDelegation( &This->unk, &This->qiext );
|
2001-10-01 22:53:58 +02:00
|
|
|
|
|
|
|
hr = CBaseFilterImpl_InitIBaseFilter(
|
|
|
|
&This->basefilter,
|
|
|
|
This->unk.punkControl,
|
|
|
|
&CLSID_AudioRender,
|
|
|
|
QUARTZ_AudioRender_Name,
|
|
|
|
&filterhandlers );
|
|
|
|
if ( SUCCEEDED(hr) )
|
|
|
|
{
|
|
|
|
hr = CAudioRendererImpl_InitIBasicAudio(This);
|
|
|
|
if ( FAILED(hr) )
|
|
|
|
{
|
|
|
|
CBaseFilterImpl_UninitIBaseFilter(&This->basefilter);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( FAILED(hr) )
|
|
|
|
{
|
|
|
|
QUARTZ_FreeObj(This);
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
This->unk.pEntries = FilterIFEntries;
|
|
|
|
This->unk.dwEntries = sizeof(FilterIFEntries)/sizeof(FilterIFEntries[0]);
|
|
|
|
This->unk.pOnFinalRelease = QUARTZ_DestroyAudioRenderer;
|
|
|
|
|
|
|
|
hr = QUARTZ_CreateAudioRendererPin(
|
|
|
|
This,
|
|
|
|
&This->basefilter.csFilter,
|
|
|
|
&This->pPin );
|
|
|
|
if ( SUCCEEDED(hr) )
|
|
|
|
hr = QUARTZ_CompList_AddComp(
|
|
|
|
This->basefilter.pInPins,
|
|
|
|
(IUnknown*)&This->pPin->pin,
|
|
|
|
NULL, 0 );
|
2001-10-22 21:03:26 +02:00
|
|
|
if ( SUCCEEDED(hr) )
|
|
|
|
hr = QUARTZ_CreateSeekingPassThruInternal(
|
|
|
|
(IUnknown*)&(This->unk), &This->pSeekPass,
|
|
|
|
TRUE, (IPin*)&(This->pPin->pin) );
|
|
|
|
|
2001-10-01 22:53:58 +02:00
|
|
|
if ( FAILED(hr) )
|
|
|
|
{
|
|
|
|
IUnknown_Release( This->unk.punkControl );
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
*ppobj = (void*)&(This->unk);
|
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
*
|
|
|
|
* new/delete CAudioRendererPinImpl
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* can I use offsetof safely? - FIXME? */
|
|
|
|
static QUARTZ_IFEntry PinIFEntries[] =
|
|
|
|
{
|
|
|
|
{ &IID_IPin, offsetof(CAudioRendererPinImpl,pin)-offsetof(CAudioRendererPinImpl,unk) },
|
|
|
|
{ &IID_IMemInputPin, offsetof(CAudioRendererPinImpl,meminput)-offsetof(CAudioRendererPinImpl,unk) },
|
|
|
|
};
|
|
|
|
|
|
|
|
static void QUARTZ_DestroyAudioRendererPin(IUnknown* punk)
|
|
|
|
{
|
|
|
|
CAudioRendererPinImpl_THIS(punk,unk);
|
|
|
|
|
|
|
|
TRACE( "(%p)\n", This );
|
|
|
|
|
|
|
|
CPinBaseImpl_UninitIPin( &This->pin );
|
|
|
|
CMemInputPinBaseImpl_UninitIMemInputPin( &This->meminput );
|
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT QUARTZ_CreateAudioRendererPin(
|
|
|
|
CAudioRendererImpl* pFilter,
|
|
|
|
CRITICAL_SECTION* pcsPin,
|
|
|
|
CAudioRendererPinImpl** ppPin)
|
|
|
|
{
|
|
|
|
CAudioRendererPinImpl* This = NULL;
|
|
|
|
HRESULT hr;
|
|
|
|
|
|
|
|
TRACE("(%p,%p,%p)\n",pFilter,pcsPin,ppPin);
|
|
|
|
|
|
|
|
This = (CAudioRendererPinImpl*)
|
|
|
|
QUARTZ_AllocObj( sizeof(CAudioRendererPinImpl) );
|
|
|
|
if ( This == NULL )
|
|
|
|
return E_OUTOFMEMORY;
|
|
|
|
|
|
|
|
QUARTZ_IUnkInit( &This->unk, NULL );
|
|
|
|
This->pRender = pFilter;
|
|
|
|
|
|
|
|
hr = CPinBaseImpl_InitIPin(
|
|
|
|
&This->pin,
|
|
|
|
This->unk.punkControl,
|
|
|
|
pcsPin,
|
|
|
|
&pFilter->basefilter,
|
|
|
|
QUARTZ_AudioRenderPin_Name,
|
|
|
|
FALSE,
|
|
|
|
&pinhandlers );
|
|
|
|
|
|
|
|
if ( SUCCEEDED(hr) )
|
|
|
|
{
|
|
|
|
hr = CMemInputPinBaseImpl_InitIMemInputPin(
|
|
|
|
&This->meminput,
|
|
|
|
This->unk.punkControl,
|
|
|
|
&This->pin );
|
|
|
|
if ( FAILED(hr) )
|
|
|
|
{
|
|
|
|
CPinBaseImpl_UninitIPin( &This->pin );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( FAILED(hr) )
|
|
|
|
{
|
|
|
|
QUARTZ_FreeObj(This);
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
This->unk.pEntries = PinIFEntries;
|
|
|
|
This->unk.dwEntries = sizeof(PinIFEntries)/sizeof(PinIFEntries[0]);
|
|
|
|
This->unk.pOnFinalRelease = QUARTZ_DestroyAudioRendererPin;
|
|
|
|
|
|
|
|
*ppPin = This;
|
|
|
|
|
|
|
|
TRACE("returned successfully.\n");
|
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
*
|
|
|
|
* CAudioRendererImpl::IBasicAudio
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
static HRESULT WINAPI
|
|
|
|
IBasicAudio_fnQueryInterface(IBasicAudio* iface,REFIID riid,void** ppobj)
|
|
|
|
{
|
|
|
|
CAudioRendererImpl_THIS(iface,basaud);
|
|
|
|
|
|
|
|
TRACE("(%p)->()\n",This);
|
|
|
|
|
|
|
|
return IUnknown_QueryInterface(This->unk.punkControl,riid,ppobj);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ULONG WINAPI
|
|
|
|
IBasicAudio_fnAddRef(IBasicAudio* iface)
|
|
|
|
{
|
|
|
|
CAudioRendererImpl_THIS(iface,basaud);
|
|
|
|
|
|
|
|
TRACE("(%p)->()\n",This);
|
|
|
|
|
|
|
|
return IUnknown_AddRef(This->unk.punkControl);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ULONG WINAPI
|
|
|
|
IBasicAudio_fnRelease(IBasicAudio* iface)
|
|
|
|
{
|
|
|
|
CAudioRendererImpl_THIS(iface,basaud);
|
|
|
|
|
|
|
|
TRACE("(%p)->()\n",This);
|
|
|
|
|
|
|
|
return IUnknown_Release(This->unk.punkControl);
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI
|
|
|
|
IBasicAudio_fnGetTypeInfoCount(IBasicAudio* iface,UINT* pcTypeInfo)
|
|
|
|
{
|
|
|
|
CAudioRendererImpl_THIS(iface,basaud);
|
|
|
|
|
|
|
|
FIXME("(%p)->()\n",This);
|
|
|
|
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI
|
|
|
|
IBasicAudio_fnGetTypeInfo(IBasicAudio* iface,UINT iTypeInfo, LCID lcid, ITypeInfo** ppobj)
|
|
|
|
{
|
|
|
|
CAudioRendererImpl_THIS(iface,basaud);
|
|
|
|
|
|
|
|
FIXME("(%p)->()\n",This);
|
|
|
|
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI
|
|
|
|
IBasicAudio_fnGetIDsOfNames(IBasicAudio* iface,REFIID riid, LPOLESTR* ppwszName, UINT cNames, LCID lcid, DISPID* pDispId)
|
|
|
|
{
|
|
|
|
CAudioRendererImpl_THIS(iface,basaud);
|
|
|
|
|
|
|
|
FIXME("(%p)->()\n",This);
|
|
|
|
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI
|
|
|
|
IBasicAudio_fnInvoke(IBasicAudio* iface,DISPID DispId, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarRes, EXCEPINFO* pExcepInfo, UINT* puArgErr)
|
|
|
|
{
|
|
|
|
CAudioRendererImpl_THIS(iface,basaud);
|
|
|
|
|
|
|
|
FIXME("(%p)->()\n",This);
|
|
|
|
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static HRESULT WINAPI
|
|
|
|
IBasicAudio_fnput_Volume(IBasicAudio* iface,long lVol)
|
|
|
|
{
|
|
|
|
CAudioRendererImpl_THIS(iface,basaud);
|
|
|
|
|
|
|
|
FIXME("(%p)->()\n",This);
|
|
|
|
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI
|
|
|
|
IBasicAudio_fnget_Volume(IBasicAudio* iface,long* plVol)
|
|
|
|
{
|
|
|
|
CAudioRendererImpl_THIS(iface,basaud);
|
|
|
|
|
|
|
|
FIXME("(%p)->()\n",This);
|
|
|
|
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI
|
|
|
|
IBasicAudio_fnput_Balance(IBasicAudio* iface,long lBalance)
|
|
|
|
{
|
|
|
|
CAudioRendererImpl_THIS(iface,basaud);
|
|
|
|
|
|
|
|
FIXME("(%p)->()\n",This);
|
|
|
|
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI
|
|
|
|
IBasicAudio_fnget_Balance(IBasicAudio* iface,long* plBalance)
|
|
|
|
{
|
|
|
|
CAudioRendererImpl_THIS(iface,basaud);
|
|
|
|
|
|
|
|
FIXME("(%p)->()\n",This);
|
|
|
|
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static ICOM_VTABLE(IBasicAudio) ibasicaudio =
|
|
|
|
{
|
|
|
|
ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
|
|
|
|
/* IUnknown fields */
|
|
|
|
IBasicAudio_fnQueryInterface,
|
|
|
|
IBasicAudio_fnAddRef,
|
|
|
|
IBasicAudio_fnRelease,
|
|
|
|
/* IDispatch fields */
|
|
|
|
IBasicAudio_fnGetTypeInfoCount,
|
|
|
|
IBasicAudio_fnGetTypeInfo,
|
|
|
|
IBasicAudio_fnGetIDsOfNames,
|
|
|
|
IBasicAudio_fnInvoke,
|
|
|
|
/* IBasicAudio fields */
|
|
|
|
IBasicAudio_fnput_Volume,
|
|
|
|
IBasicAudio_fnget_Volume,
|
|
|
|
IBasicAudio_fnput_Balance,
|
|
|
|
IBasicAudio_fnget_Balance,
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
HRESULT CAudioRendererImpl_InitIBasicAudio( CAudioRendererImpl* This )
|
|
|
|
{
|
|
|
|
TRACE("(%p)\n",This);
|
|
|
|
ICOM_VTBL(&This->basaud) = &ibasicaudio;
|
|
|
|
|
|
|
|
return NOERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CAudioRendererImpl_UninitIBasicAudio( CAudioRendererImpl* This )
|
|
|
|
{
|
|
|
|
TRACE("(%p)\n",This);
|
|
|
|
}
|
|
|
|
|