| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708 |
- /*
- ** Command & Conquer Generals Zero Hour(tm)
- ** Copyright 2025 Electronic Arts Inc.
- **
- ** This program is free software: you can redistribute it and/or modify
- ** it under the terms of the GNU General Public License as published by
- ** the Free Software Foundation, either version 3 of the License, or
- ** (at your option) any later version.
- **
- ** This program is distributed in the hope that it will be useful,
- ** but WITHOUT ANY WARRANTY; without even the implied warranty of
- ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ** GNU General Public License for more details.
- **
- ** You should have received a copy of the GNU General Public License
- ** along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- #include "Common/SimplePlayer.h"
- #include "Common/URLLaunch.h"
- ///////////////////////////////////////////////////////////////////////////////
- CSimplePlayer::CSimplePlayer( HRESULT* phr )
- {
- m_cRef = 1;
- m_cBuffersOutstanding = 0;
- m_pReader = NULL;
- m_pHeader = NULL;
- m_hwo = NULL;
- m_fEof = FALSE;
- m_pszUrl = NULL;
- *phr = S_OK;
- m_hOpenEvent = CreateEvent( NULL, FALSE, FALSE, SIMPLE_PLAYER_OPEN_EVENT );
- if ( NULL == m_hOpenEvent )
- {
- *phr = E_OUTOFMEMORY;
- }
- m_hCloseEvent = CreateEvent( NULL, FALSE, FALSE, SIMPLE_PLAYER_CLOSE_EVENT );
- if ( NULL == m_hCloseEvent )
- {
- *phr = E_OUTOFMEMORY;
- }
- m_hrOpen = S_OK;
- m_hCompletionEvent = NULL;
- InitializeCriticalSection( &m_CriSec );
- m_whdrHead = NULL;
- }
- ///////////////////////////////////////////////////////////////////////////////
- CSimplePlayer::~CSimplePlayer()
- {
- DEBUG_ASSERTCRASH( 0 == m_cBuffersOutstanding,("CSimplePlayer destructor m_cBuffersOutstanding != 0") );
- Close();
- //
- // final remove of everything in the wave header list
- //
- RemoveWaveHeaders();
- DeleteCriticalSection( &m_CriSec );
- if( m_pHeader != NULL )
- {
- m_pHeader->Release();
- m_pHeader = NULL;
- }
- if( m_pReader != NULL )
- {
- m_pReader->Release();
- m_pReader = NULL;
- }
- if( m_hwo != NULL )
- {
- waveOutClose( m_hwo );
- }
- delete [] m_pszUrl;
- if ( m_hOpenEvent )
- {
- CloseHandle( m_hOpenEvent );
- }
- if ( m_hCloseEvent )
- {
- CloseHandle( m_hCloseEvent );
- }
- }
- ///////////////////////////////////////////////////////////////////////////////
- HRESULT STDMETHODCALLTYPE CSimplePlayer::QueryInterface(
- REFIID riid,
- void **ppvObject )
- {
- return( E_NOINTERFACE );
- }
- ///////////////////////////////////////////////////////////////////////////////
- ULONG STDMETHODCALLTYPE CSimplePlayer::AddRef()
- {
- return( InterlockedIncrement( &m_cRef ) );
- }
- ///////////////////////////////////////////////////////////////////////////////
- ULONG STDMETHODCALLTYPE CSimplePlayer::Release()
- {
- ULONG uRet = InterlockedDecrement( &m_cRef );
- if( 0 == uRet )
- {
- delete this;
- }
- return( uRet );
- }
- ///////////////////////////////////////////////////////////////////////////////
- HRESULT STDMETHODCALLTYPE CSimplePlayer::OnSample(
- /* [in] */ DWORD dwOutputNum,
- /* [in] */ QWORD cnsSampleTime,
- /* [in] */ QWORD cnsSampleDuration,
- /* [in] */ DWORD dwFlags,
- /* [in] */ INSSBuffer __RPC_FAR *pSample,
- /* [in] */ VOID *pvContext )
- {
- if( 0 != dwOutputNum )
- {
- return( S_OK );
- }
- HRESULT hr = S_OK;
- BYTE *pData;
- DWORD cbData;
- //
- // first UnprepareHeader and remove everthing in the ready list
- //
- RemoveWaveHeaders( );
- hr = pSample->GetBufferAndLength( &pData, &cbData );
- if ( FAILED( hr ) )
- {
- return( E_UNEXPECTED );
- }
- DEBUG_LOG(( " New Sample of length %d and PS time %d ms\n",
- cbData, ( DWORD ) ( cnsSampleTime / 10000 ) ));
- LPWAVEHDR pwh = (LPWAVEHDR) new BYTE[ sizeof( WAVEHDR ) + cbData ];
- if( NULL == pwh )
- {
- DEBUG_LOG(( "OnSample OUT OF MEMORY! \n"));
- *m_phrCompletion = E_OUTOFMEMORY;
- SetEvent( m_hCompletionEvent );
- return( E_UNEXPECTED );
- }
- pwh->lpData = (LPSTR)&pwh[1];
- pwh->dwBufferLength = cbData;
- pwh->dwBytesRecorded = cbData;
- pwh->dwUser = 0;
- pwh->dwLoops = 0;
- pwh->dwFlags = 0;
- CopyMemory( pwh->lpData, pData, cbData );
- MMRESULT mmr;
- mmr = waveOutPrepareHeader( m_hwo, pwh, sizeof(WAVEHDR) );
- mmr = MMSYSERR_NOERROR;
- if( mmr != MMSYSERR_NOERROR )
- {
- DEBUG_LOG(( "failed to prepare wave buffer, error=%lu\n" , mmr ));
- *m_phrCompletion = E_UNEXPECTED;
- SetEvent( m_hCompletionEvent );
- return( E_UNEXPECTED );
- }
- mmr = waveOutWrite( m_hwo, pwh, sizeof(WAVEHDR) );
- mmr = MMSYSERR_NOERROR;
- if( mmr != MMSYSERR_NOERROR )
- {
- delete pwh;
- DEBUG_LOG(( "failed to write wave sample, error=%lu\n" , mmr ));
- *m_phrCompletion = E_UNEXPECTED;
- SetEvent( m_hCompletionEvent );
- return( E_UNEXPECTED );
- }
- InterlockedIncrement( &m_cBuffersOutstanding );
- return( S_OK );
- }
- ///////////////////////////////////////////////////////////////////////////////
- HRESULT CSimplePlayer::Play( LPCWSTR pszUrl, DWORD dwSecDuration, HANDLE hCompletionEvent, HRESULT *phrCompletion )
- {
- HRESULT hr;
- //
- // If the URL is not a UNC path, a full path, or an Internet-style URL then assume it is
- // a relative local file name that needs to be expanded to a full path.
- //
- WCHAR wszFullUrl[ MAX_PATH ];
- if( ( 0 == wcsstr( pszUrl, L"\\\\" ) )
- && ( 0 == wcsstr( pszUrl, L":\\" ) )
- && ( 0 == wcsstr( pszUrl, L"://" ) ) )
- {
- //
- // Expand to a full path name
- //
- LPWSTR pszCheck = _wfullpath( wszFullUrl, pszUrl, MAX_PATH );
- if( NULL == pszCheck )
- {
- DEBUG_LOG(( "internal error %lu\n" , GetLastError() ));
- return E_UNEXPECTED ;
- }
- pszUrl = wszFullUrl;
- }
- //
- // Save a copy of the URL
- //
- delete[] m_pszUrl;
- m_pszUrl = new WCHAR[ wcslen( pszUrl ) + 1 ];
- if( NULL == m_pszUrl )
- {
- DEBUG_LOG(( "insufficient Memory\n" )) ;
- return( E_OUTOFMEMORY );
- }
- wcscpy( m_pszUrl, pszUrl );
- //
- // Attempt to open the URL
- //
- m_hCompletionEvent = hCompletionEvent;
- m_phrCompletion = phrCompletion;
- #ifdef SUPPORT_DRM
- hr = WMCreateReader( NULL, WMT_RIGHT_PLAYBACK, &m_pReader );
- #else
- hr = WMCreateReader( NULL, 0, &m_pReader );
- #endif
- if( FAILED( hr ) )
- {
- DEBUG_LOG(( "failed to create audio reader (hr=0x%08x)\n" , hr ));
- return( hr );
- }
- //
- // Open the file
- //
- hr = m_pReader->Open( m_pszUrl, this, NULL );
- if ( SUCCEEDED( hr ) )
- {
- WaitForSingleObject( m_hOpenEvent, INFINITE );
- hr = m_hrOpen;
- }
- if ( NS_E_NO_STREAM == hr )
- {
- DEBUG_LOG(( "Waiting for transmission to begin...\n" ));
- WaitForSingleObject( m_hOpenEvent, INFINITE );
- hr = m_hrOpen;
- }
- if ( FAILED( hr ) )
- {
- DEBUG_LOG(( "failed to open (hr=0x%08x)\n", hr ));
- return( hr );
- }
-
- //
- // It worked! Display various attributes
- //
- hr = m_pReader->QueryInterface( IID_IWMHeaderInfo, ( VOID ** )&m_pHeader );
- if ( FAILED( hr ) )
- {
- DEBUG_LOG(( "failed to qi for header interface (hr=0x%08x)\n" , hr ));
- return( hr );
- }
- WORD i, wAttrCnt;
- hr = m_pHeader->GetAttributeCount( 0, &wAttrCnt );
- if ( FAILED( hr ) )
- {
- DEBUG_LOG(( "GetAttributeCount Failed (hr=0x%08x)\n" , hr ));
- return( hr );
- }
- WCHAR *pwszName = NULL;
- BYTE *pValue = NULL;
- for ( i = 0; i < wAttrCnt ; i++ )
- {
- WORD wStream = 0;
- WORD cchNamelen = 0;
- WMT_ATTR_DATATYPE type;
- WORD cbLength = 0;
- hr = m_pHeader->GetAttributeByIndex( i, &wStream, NULL, &cchNamelen, &type, NULL, &cbLength );
- if ( FAILED( hr ) )
- {
- DEBUG_LOG(( "GetAttributeByIndex Failed (hr=0x%08x)\n" , hr ));
- break;
- }
- pwszName = new WCHAR[ cchNamelen ];
- pValue = new BYTE[ cbLength ];
- if( NULL == pwszName || NULL == pValue )
- {
- hr = E_OUTOFMEMORY;
- break;
- }
- hr = m_pHeader->GetAttributeByIndex( i, &wStream, pwszName, &cchNamelen, &type, pValue, &cbLength );
- if ( FAILED( hr ) )
- {
- DEBUG_LOG(( "GetAttributeByIndex Failed (hr=0x%08x)\n" , hr ));
- break;
- }
- switch ( type )
- {
- case WMT_TYPE_DWORD:
- DEBUG_LOG(("%ws: %u\n" , pwszName, *((DWORD *) pValue) ));
- break;
- case WMT_TYPE_STRING:
- DEBUG_LOG(("%ws: %ws\n" , pwszName, (WCHAR *) pValue ));
- break;
- case WMT_TYPE_BINARY:
- DEBUG_LOG(("%ws: Type = Binary of Length %u\n" , pwszName, cbLength ));
- break;
- case WMT_TYPE_BOOL:
- DEBUG_LOG(("%ws: %s\n" , pwszName, ( * ( ( BOOL * ) pValue) ? _T( "true" ) : _T( "false" ) ) ));
- break;
- case WMT_TYPE_WORD:
- DEBUG_LOG(("%ws: %hu\n" , pwszName, *((WORD *) pValue) ));
- break;
- case WMT_TYPE_QWORD:
- DEBUG_LOG(("%ws: %I64u\n" , pwszName, *((QWORD *) pValue) ));
- break;
- case WMT_TYPE_GUID:
- DEBUG_LOG(("%ws: %I64x%I64x\n" , pwszName, *((QWORD *) pValue), *((QWORD *) pValue + 1) ));
- break;
- default:
- DEBUG_LOG(("%ws: Type = %d, Length %u\n" , pwszName, type, cbLength ));
- break;
- }
- delete pwszName;
- pwszName = NULL;
- delete pValue;
- pValue = NULL;
- }
- delete pwszName;
- pwszName = NULL;
- delete pValue;
- pValue = NULL;
- if ( FAILED( hr ) )
- {
- return( hr );
- }
- //
- // Make sure we're audio only
- //
- DWORD cOutputs;
- hr = m_pReader->GetOutputCount( &cOutputs );
- if ( FAILED( hr ) )
- {
- DEBUG_LOG(( "failed GetOutputCount(), (hr=0x%08x)\n" , hr ));
- return( hr );
- }
- if ( cOutputs != 1 )
- {
- DEBUG_LOG(( "Not audio only (cOutputs = %d).\n" , cOutputs ));
- // return( E_UNEXPECTED );
- }
- IWMOutputMediaProps *pProps;
- hr = m_pReader->GetOutputProps( 0, &pProps );
- if ( FAILED( hr ) )
- {
- DEBUG_LOG(( "failed GetOutputProps(), (hr=0x%08x)\n" , hr ));
- return( hr );
- }
- DWORD cbBuffer = 0;
- hr = pProps->GetMediaType( NULL, &cbBuffer );
- if ( FAILED( hr ) )
- {
- pProps->Release( );
- DEBUG_LOG(( "GetMediaType failed (hr=0x%08x)\n" , hr ));
- return( hr );
- }
-
- WM_MEDIA_TYPE *pMediaType = ( WM_MEDIA_TYPE * ) new BYTE[cbBuffer] ;
- hr = pProps->GetMediaType( pMediaType, &cbBuffer );
- if ( FAILED( hr ) )
- {
- pProps->Release( );
- DEBUG_LOG(( "GetMediaType failed (hr=0x%08x)\n" , hr ));
- return( hr );
- }
- pProps->Release( );
- if ( pMediaType->majortype != WMMEDIATYPE_Audio )
- {
- delete[] (BYTE *) pMediaType ;
- DEBUG_LOG(( "Not audio only (major type mismatch).\n" ));
- return( E_UNEXPECTED );
- }
-
- //
- // Set up for audio playback
- //
- WAVEFORMATEX *pwfx = ( WAVEFORMATEX * )pMediaType->pbFormat;
- memcpy( &m_wfx, pwfx, sizeof( WAVEFORMATEX ) + pwfx->cbSize );
-
- delete[] (BYTE *)pMediaType ;
- pMediaType = NULL ;
- MMRESULT mmr;
- mmr = waveOutOpen( &m_hwo,
- WAVE_MAPPER,
- &m_wfx,
- (DWORD)WaveProc,
- (DWORD)this,
- CALLBACK_FUNCTION );
- mmr = MMSYSERR_NOERROR;
- if( mmr != MMSYSERR_NOERROR )
- {
-
- DEBUG_LOG(( "failed to open wav output device, error=%lu\n" , mmr ));
- return( E_UNEXPECTED );
- }
- //
- // Start reading the data (and rendering the audio)
- //
- QWORD cnsDuration = ( QWORD ) dwSecDuration * 10000000;
- hr = m_pReader->Start( 0, cnsDuration, 1.0, NULL );
- if( FAILED( hr ) )
- {
- DEBUG_LOG(( "failed Start(), (hr=0x%08x)\n" , hr ));
- return( hr );
- }
- return( hr );
- }
- ///////////////////////////////////////////////////////////////////////////////
- HRESULT STDMETHODCALLTYPE CSimplePlayer::OnStatus(
- /* [in] */ WMT_STATUS Status,
- /* [in] */ HRESULT hr,
- /* [in] */ WMT_ATTR_DATATYPE dwType,
- /* [in] */ BYTE __RPC_FAR *pValue,
- /* [in] */ void __RPC_FAR *pvContext)
- {
- switch( Status )
- {
- case WMT_OPENED:
- DEBUG_LOG(( "OnStatus( WMT_OPENED )\n" ));
- m_hrOpen = hr;
- SetEvent( m_hOpenEvent );
- break;
- case WMT_SOURCE_SWITCH:
- DEBUG_LOG(( "OnStatus( WMT_SOURCE_SWITCH )\n" ));
- m_hrOpen = hr;
- SetEvent( m_hOpenEvent );
- break;
- case WMT_ERROR:
- DEBUG_LOG(( "OnStatus( WMT_ERROR )\n" ));
- break;
- case WMT_STARTED:
- DEBUG_LOG(( "OnStatus( WMT_STARTED )\n" ));
- break;
- case WMT_STOPPED:
- DEBUG_LOG(( "OnStatus( WMT_STOPPED )\n" ));
- break;
- case WMT_BUFFERING_START:
- DEBUG_LOG(( "OnStatus( WMT_BUFFERING START)\n" ));
- break;
- case WMT_BUFFERING_STOP:
- DEBUG_LOG(( "OnStatus( WMT_BUFFERING STOP)\n" ));
- break;
- case WMT_EOF:
- DEBUG_LOG(( "OnStatus( WMT_EOF )\n" ));
- //
- // cleanup and exit
- //
- m_fEof = TRUE;
- if( 0 == m_cBuffersOutstanding )
- {
- SetEvent( m_hCompletionEvent );
- }
- break;
- case WMT_END_OF_SEGMENT:
- DEBUG_LOG(( "OnStatus( WMT_END_OF_SEGMENT )\n" ));
- //
- // cleanup and exit
- //
- m_fEof = TRUE;
- if( 0 == m_cBuffersOutstanding )
- {
- SetEvent( m_hCompletionEvent );
- }
- break;
- case WMT_LOCATING:
- DEBUG_LOG(( "OnStatus( WMT_LOCATING )\n" ));
- break;
- case WMT_CONNECTING:
- DEBUG_LOG(( "OnStatus( WMT_CONNECTING )\n" ));
- break;
- case WMT_NO_RIGHTS:
- {
- LPWSTR pwszEscapedURL = NULL;
- hr = MakeEscapedURL( m_pszUrl, &pwszEscapedURL );
- if( SUCCEEDED( hr ) )
- {
- WCHAR wszURL[ 0x1000 ];
- swprintf( wszURL, L"%s&filename=%s&embedded=false", pValue, pwszEscapedURL );
- hr = LaunchURL( wszURL );
- if( FAILED( hr ) )
- {
- DEBUG_LOG(( "Unable to launch web browser to retrieve playback license (hr=0x%08x)\n" , hr ));
- }
- delete [] pwszEscapedURL;
- pwszEscapedURL = NULL ;
- }
- }
- break;
- case WMT_MISSING_CODEC:
- {
- DEBUG_LOG(( "Missing codec: (hr=0x%08x)\n" , hr ));
- break;
- }
- case WMT_CLOSED:
- SetEvent( m_hCloseEvent );
- break;
- };
- return( S_OK );
- }
- ///////////////////////////////////////////////////////////////////////////////
- HRESULT CSimplePlayer::Close()
- {
- HRESULT hr = S_OK;
- if( NULL != m_pReader )
- {
- hr = m_pReader->Close();
- if( SUCCEEDED( hr ) )
- {
- WaitForSingleObject( m_hCloseEvent, INFINITE );
- }
- }
- return( hr );
- }
- ///////////////////////////////////////////////////////////////////////////////
- void CSimplePlayer::OnWaveOutMsg( UINT uMsg, DWORD dwParam1, DWORD dwParam2 )
- {
- if( WOM_DONE == uMsg )
- {
- //
- // add the wave header to ready-to-free list for the caller
- // to pick up and free in the next OnSample call
- //
- AddWaveHeader( ( LPWAVEHDR )dwParam1 );
- InterlockedDecrement( &m_cBuffersOutstanding );
- if( m_fEof && ( 0 == m_cBuffersOutstanding ) )
- {
- SetEvent( m_hCompletionEvent );
- }
- }
- }
- ///////////////////////////////////////////////////////////////////////////////
- void CALLBACK CSimplePlayer::WaveProc(
- HWAVEOUT hwo,
- UINT uMsg,
- DWORD dwInstance,
- DWORD dwParam1,
- DWORD dwParam2 )
- {
- CSimplePlayer *pThis = (CSimplePlayer*)dwInstance;
- pThis->OnWaveOutMsg( uMsg, dwParam1, dwParam2 );
- }
- //////////////////////////////////////////////////////////////////////////////
- HRESULT CSimplePlayer::AddWaveHeader( LPWAVEHDR pwh )
- {
- WAVEHDR_LIST *tmp = new WAVEHDR_LIST;
- if( NULL == tmp )
- {
- return( E_OUTOFMEMORY );
- }
- tmp->pwh = pwh;
- EnterCriticalSection( &m_CriSec );
- tmp->next = m_whdrHead;
- m_whdrHead = tmp;
- LeaveCriticalSection( &m_CriSec );
- return( S_OK );
- }
- //////////////////////////////////////////////////////////////////////////////
- void CSimplePlayer::RemoveWaveHeaders( )
- {
- WAVEHDR_LIST *tmp;
- EnterCriticalSection( &m_CriSec );
- while( NULL != m_whdrHead )
- {
- tmp = m_whdrHead->next;
- DEBUG_ASSERTCRASH( m_whdrHead->pwh->dwFlags & WHDR_DONE, ("RemoveWaveHeaders!") );
- waveOutUnprepareHeader( m_hwo, m_whdrHead->pwh, sizeof( WAVEHDR ) );
- delete m_whdrHead->pwh;
- delete m_whdrHead;
- m_whdrHead = tmp;
- }
- LeaveCriticalSection( &m_CriSec );
- }
|