Richard Boegli's CnC_Generals_Zero_Hour Fork WIP
This is documentation of Richard Boegil's Zero Hour Fork
 
Loading...
Searching...
No Matches
simpleplayer.cpp
Go to the documentation of this file.
1/*
2** Command & Conquer Generals Zero Hour(tm)
3** Copyright 2025 Electronic Arts Inc.
4**
5** This program is free software: you can redistribute it and/or modify
6** it under the terms of the GNU General Public License as published by
7** the Free Software Foundation, either version 3 of the License, or
8** (at your option) any later version.
9**
10** This program is distributed in the hope that it will be useful,
11** but WITHOUT ANY WARRANTY; without even the implied warranty of
12** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13** GNU General Public License for more details.
14**
15** You should have received a copy of the GNU General Public License
16** along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19
20#include "Common/SimplePlayer.h"
21#include "Common/URLLaunch.h"
22
23
26{
27 m_cRef = 1;
29
31
33 m_hwo = NULL;
34
35 m_fEof = FALSE;
36 m_pszUrl = NULL;
37
38 *phr = S_OK;
39
41 if ( NULL == m_hOpenEvent )
42 {
43 *phr = E_OUTOFMEMORY;
44 }
46 if ( NULL == m_hCloseEvent )
47 {
48 *phr = E_OUTOFMEMORY;
49 }
50
51 m_hrOpen = S_OK;
52
54
55 InitializeCriticalSection( &m_CriSec );
57}
58
59
62{
63 DEBUG_ASSERTCRASH( 0 == m_cBuffersOutstanding,("CSimplePlayer destructor m_cBuffersOutstanding != 0") );
64
65 Close();
66
67 //
68 // final remove of everything in the wave header list
69 //
71 DeleteCriticalSection( &m_CriSec );
72
73 if( m_pHeader != NULL )
74 {
75 m_pHeader->Release();
77 }
78
79 if( m_pReader != NULL )
80 {
81 m_pReader->Release();
83 }
84
85 if( m_hwo != NULL )
86 {
87 waveOutClose( m_hwo );
88 }
89
90 delete [] m_pszUrl;
91
92 if ( m_hOpenEvent )
93 {
94 CloseHandle( m_hOpenEvent );
95 }
96 if ( m_hCloseEvent )
97 {
98 CloseHandle( m_hCloseEvent );
99 }
100}
101
102
104HRESULT STDMETHODCALLTYPE CSimplePlayer::QueryInterface(
105 REFIID riid,
106 void **ppvObject )
107{
108 return( E_NOINTERFACE );
109}
110
111
113ULONG STDMETHODCALLTYPE CSimplePlayer::AddRef()
114{
115 return( InterlockedIncrement( &m_cRef ) );
116}
117
118
120ULONG STDMETHODCALLTYPE CSimplePlayer::Release()
121{
122 ULONG uRet = InterlockedDecrement( &m_cRef );
123
124 if( 0 == uRet )
125 {
126 delete this;
127 }
128
129 return( uRet );
130}
131
132
134HRESULT STDMETHODCALLTYPE CSimplePlayer::OnSample(
135 /* [in] */ DWORD dwOutputNum,
136 /* [in] */ QWORD cnsSampleTime,
137 /* [in] */ QWORD cnsSampleDuration,
138 /* [in] */ DWORD dwFlags,
139 /* [in] */ INSSBuffer __RPC_FAR *pSample,
140 /* [in] */ VOID *pvContext )
141{
142 if( 0 != dwOutputNum )
143 {
144 return( S_OK );
145 }
146
147 HRESULT hr = S_OK;
148 BYTE *pData;
149 DWORD cbData;
150
151 //
152 // first UnprepareHeader and remove everthing in the ready list
153 //
155
156 hr = pSample->GetBufferAndLength( &pData, &cbData );
157 if ( FAILED( hr ) )
158 {
159 return( E_UNEXPECTED );
160 }
161
162 DEBUG_LOG(( " New Sample of length %d and PS time %d ms\n",
163 cbData, ( DWORD ) ( cnsSampleTime / 10000 ) ));
164
165 LPWAVEHDR pwh = (LPWAVEHDR) new BYTE[ sizeof( WAVEHDR ) + cbData ];
166
167 if( NULL == pwh )
168 {
169 DEBUG_LOG(( "OnSample OUT OF MEMORY! \n"));
170
171 *m_phrCompletion = E_OUTOFMEMORY;
172 SetEvent( m_hCompletionEvent );
173 return( E_UNEXPECTED );
174 }
175
176 pwh->lpData = (LPSTR)&pwh[1];
177 pwh->dwBufferLength = cbData;
178 pwh->dwBytesRecorded = cbData;
179 pwh->dwUser = 0;
180 pwh->dwLoops = 0;
181 pwh->dwFlags = 0;
182
183 CopyMemory( pwh->lpData, pData, cbData );
184
185 MMRESULT mmr;
186
187 mmr = waveOutPrepareHeader( m_hwo, pwh, sizeof(WAVEHDR) );
188 mmr = MMSYSERR_NOERROR;
189
190 if( mmr != MMSYSERR_NOERROR )
191 {
192 DEBUG_LOG(( "failed to prepare wave buffer, error=%lu\n" , mmr ));
193 *m_phrCompletion = E_UNEXPECTED;
194 SetEvent( m_hCompletionEvent );
195 return( E_UNEXPECTED );
196 }
197
198 mmr = waveOutWrite( m_hwo, pwh, sizeof(WAVEHDR) );
199 mmr = MMSYSERR_NOERROR;
200
201 if( mmr != MMSYSERR_NOERROR )
202 {
203 delete pwh;
204
205 DEBUG_LOG(( "failed to write wave sample, error=%lu\n" , mmr ));
206 *m_phrCompletion = E_UNEXPECTED;
207 SetEvent( m_hCompletionEvent );
208 return( E_UNEXPECTED );
209 }
210
211 InterlockedIncrement( &m_cBuffersOutstanding );
212
213 return( S_OK );
214}
215
217HRESULT CSimplePlayer::Play( LPCWSTR pszUrl, DWORD dwSecDuration, HANDLE hCompletionEvent, HRESULT *phrCompletion )
218{
219 HRESULT hr;
220
221 //
222 // If the URL is not a UNC path, a full path, or an Internet-style URL then assume it is
223 // a relative local file name that needs to be expanded to a full path.
224 //
225 WCHAR wszFullUrl[ MAX_PATH ];
226
227 if( ( 0 == wcsstr( pszUrl, L"\\\\" ) )
228 && ( 0 == wcsstr( pszUrl, L":\\" ) )
229 && ( 0 == wcsstr( pszUrl, L"://" ) ) )
230 {
231 //
232 // Expand to a full path name
233 //
234 LPWSTR pszCheck = _wfullpath( wszFullUrl, pszUrl, MAX_PATH );
235
236 if( NULL == pszCheck )
237 {
238 DEBUG_LOG(( "internal error %lu\n" , GetLastError() ));
239 return E_UNEXPECTED ;
240 }
241
242 pszUrl = wszFullUrl;
243 }
244
245 //
246 // Save a copy of the URL
247 //
248 delete[] m_pszUrl;
249
250 m_pszUrl = new WCHAR[ wcslen( pszUrl ) + 1 ];
251
252 if( NULL == m_pszUrl )
253 {
254 DEBUG_LOG(( "insufficient Memory\n" )) ;
255 return( E_OUTOFMEMORY );
256 }
257
258 wcscpy( m_pszUrl, pszUrl );
259
260 //
261 // Attempt to open the URL
262 //
263 m_hCompletionEvent = hCompletionEvent;
264
265 m_phrCompletion = phrCompletion;
266
267#ifdef SUPPORT_DRM
268
269 hr = WMCreateReader( NULL, WMT_RIGHT_PLAYBACK, &m_pReader );
270
271#else
272
273 hr = WMCreateReader( NULL, 0, &m_pReader );
274
275#endif
276
277 if( FAILED( hr ) )
278 {
279 DEBUG_LOG(( "failed to create audio reader (hr=0x%08x)\n" , hr ));
280 return( hr );
281 }
282
283 //
284 // Open the file
285 //
286 hr = m_pReader->Open( m_pszUrl, this, NULL );
287 if ( SUCCEEDED( hr ) )
288 {
289 WaitForSingleObject( m_hOpenEvent, INFINITE );
290 hr = m_hrOpen;
291 }
292 if ( NS_E_NO_STREAM == hr )
293 {
294 DEBUG_LOG(( "Waiting for transmission to begin...\n" ));
295 WaitForSingleObject( m_hOpenEvent, INFINITE );
296 hr = m_hrOpen;
297 }
298 if ( FAILED( hr ) )
299 {
300 DEBUG_LOG(( "failed to open (hr=0x%08x)\n", hr ));
301 return( hr );
302 }
303
304
305 //
306 // It worked! Display various attributes
307 //
308 hr = m_pReader->QueryInterface( IID_IWMHeaderInfo, ( VOID ** )&m_pHeader );
309 if ( FAILED( hr ) )
310 {
311 DEBUG_LOG(( "failed to qi for header interface (hr=0x%08x)\n" , hr ));
312 return( hr );
313 }
314
315 WORD i, wAttrCnt;
316
317 hr = m_pHeader->GetAttributeCount( 0, &wAttrCnt );
318 if ( FAILED( hr ) )
319 {
320 DEBUG_LOG(( "GetAttributeCount Failed (hr=0x%08x)\n" , hr ));
321 return( hr );
322 }
323
324 WCHAR *pwszName = NULL;
325 BYTE *pValue = NULL;
326
327 for ( i = 0; i < wAttrCnt ; i++ )
328 {
329 WORD wStream = 0;
330 WORD cchNamelen = 0;
331 WMT_ATTR_DATATYPE type;
332 WORD cbLength = 0;
333
334 hr = m_pHeader->GetAttributeByIndex( i, &wStream, NULL, &cchNamelen, &type, NULL, &cbLength );
335 if ( FAILED( hr ) )
336 {
337 DEBUG_LOG(( "GetAttributeByIndex Failed (hr=0x%08x)\n" , hr ));
338 break;
339 }
340
341 pwszName = new WCHAR[ cchNamelen ];
342 pValue = new BYTE[ cbLength ];
343
344 if( NULL == pwszName || NULL == pValue )
345 {
346 hr = E_OUTOFMEMORY;
347 break;
348 }
349
350 hr = m_pHeader->GetAttributeByIndex( i, &wStream, pwszName, &cchNamelen, &type, pValue, &cbLength );
351 if ( FAILED( hr ) )
352 {
353 DEBUG_LOG(( "GetAttributeByIndex Failed (hr=0x%08x)\n" , hr ));
354 break;
355 }
356
357 switch ( type )
358 {
359 case WMT_TYPE_DWORD:
360 DEBUG_LOG(("%ws: %u\n" , pwszName, *((DWORD *) pValue) ));
361 break;
362 case WMT_TYPE_STRING:
363 DEBUG_LOG(("%ws: %ws\n" , pwszName, (WCHAR *) pValue ));
364 break;
365 case WMT_TYPE_BINARY:
366 DEBUG_LOG(("%ws: Type = Binary of Length %u\n" , pwszName, cbLength ));
367 break;
368 case WMT_TYPE_BOOL:
369 DEBUG_LOG(("%ws: %s\n" , pwszName, ( * ( ( BOOL * ) pValue) ? _T( "true" ) : _T( "false" ) ) ));
370 break;
371 case WMT_TYPE_WORD:
372 DEBUG_LOG(("%ws: %hu\n" , pwszName, *((WORD *) pValue) ));
373 break;
374 case WMT_TYPE_QWORD:
375 DEBUG_LOG(("%ws: %I64u\n" , pwszName, *((QWORD *) pValue) ));
376 break;
377 case WMT_TYPE_GUID:
378 DEBUG_LOG(("%ws: %I64x%I64x\n" , pwszName, *((QWORD *) pValue), *((QWORD *) pValue + 1) ));
379 break;
380 default:
381 DEBUG_LOG(("%ws: Type = %d, Length %u\n" , pwszName, type, cbLength ));
382 break;
383 }
384
385 delete pwszName;
386 pwszName = NULL;
387
388 delete pValue;
389 pValue = NULL;
390 }
391
392 delete pwszName;
393 pwszName = NULL;
394
395 delete pValue;
396 pValue = NULL;
397
398 if ( FAILED( hr ) )
399 {
400 return( hr );
401 }
402
403 //
404 // Make sure we're audio only
405 //
406 DWORD cOutputs;
407
408 hr = m_pReader->GetOutputCount( &cOutputs );
409 if ( FAILED( hr ) )
410 {
411 DEBUG_LOG(( "failed GetOutputCount(), (hr=0x%08x)\n" , hr ));
412 return( hr );
413 }
414
415 if ( cOutputs != 1 )
416 {
417 DEBUG_LOG(( "Not audio only (cOutputs = %d).\n" , cOutputs ));
418 // return( E_UNEXPECTED );
419 }
420
421 IWMOutputMediaProps *pProps;
422 hr = m_pReader->GetOutputProps( 0, &pProps );
423 if ( FAILED( hr ) )
424 {
425 DEBUG_LOG(( "failed GetOutputProps(), (hr=0x%08x)\n" , hr ));
426 return( hr );
427 }
428
429 DWORD cbBuffer = 0;
430
431 hr = pProps->GetMediaType( NULL, &cbBuffer );
432 if ( FAILED( hr ) )
433 {
434 pProps->Release( );
435 DEBUG_LOG(( "GetMediaType failed (hr=0x%08x)\n" , hr ));
436 return( hr );
437 }
438
439 WM_MEDIA_TYPE *pMediaType = ( WM_MEDIA_TYPE * ) new BYTE[cbBuffer] ;
440
441 hr = pProps->GetMediaType( pMediaType, &cbBuffer );
442 if ( FAILED( hr ) )
443 {
444 pProps->Release( );
445 DEBUG_LOG(( "GetMediaType failed (hr=0x%08x)\n" , hr ));
446 return( hr );
447 }
448
449 pProps->Release( );
450
451 if ( pMediaType->majortype != WMMEDIATYPE_Audio )
452 {
453 delete[] (BYTE *) pMediaType ;
454 DEBUG_LOG(( "Not audio only (major type mismatch).\n" ));
455 return( E_UNEXPECTED );
456 }
457
458 //
459 // Set up for audio playback
460 //
461 WAVEFORMATEX *pwfx = ( WAVEFORMATEX * )pMediaType->pbFormat;
462 memcpy( &m_wfx, pwfx, sizeof( WAVEFORMATEX ) + pwfx->cbSize );
463
464 delete[] (BYTE *)pMediaType ;
465 pMediaType = NULL ;
466
467 MMRESULT mmr;
468
469 mmr = waveOutOpen( &m_hwo,
470 WAVE_MAPPER,
471 &m_wfx,
472 (DWORD)WaveProc,
473 (DWORD)this,
474 CALLBACK_FUNCTION );
475 mmr = MMSYSERR_NOERROR;
476
477 if( mmr != MMSYSERR_NOERROR )
478 {
479
480 DEBUG_LOG(( "failed to open wav output device, error=%lu\n" , mmr ));
481 return( E_UNEXPECTED );
482 }
483
484 //
485 // Start reading the data (and rendering the audio)
486 //
487 QWORD cnsDuration = ( QWORD ) dwSecDuration * 10000000;
488 hr = m_pReader->Start( 0, cnsDuration, 1.0, NULL );
489
490 if( FAILED( hr ) )
491 {
492 DEBUG_LOG(( "failed Start(), (hr=0x%08x)\n" , hr ));
493 return( hr );
494 }
495
496 return( hr );
497}
498
499
501HRESULT STDMETHODCALLTYPE CSimplePlayer::OnStatus(
502 /* [in] */ WMT_STATUS Status,
503 /* [in] */ HRESULT hr,
504 /* [in] */ WMT_ATTR_DATATYPE dwType,
505 /* [in] */ BYTE __RPC_FAR *pValue,
506 /* [in] */ void __RPC_FAR *pvContext)
507{
508 switch( Status )
509 {
510 case WMT_OPENED:
511 DEBUG_LOG(( "OnStatus( WMT_OPENED )\n" ));
512 m_hrOpen = hr;
513 SetEvent( m_hOpenEvent );
514 break;
515
516 case WMT_SOURCE_SWITCH:
517 DEBUG_LOG(( "OnStatus( WMT_SOURCE_SWITCH )\n" ));
518 m_hrOpen = hr;
519 SetEvent( m_hOpenEvent );
520 break;
521
522 case WMT_ERROR:
523 DEBUG_LOG(( "OnStatus( WMT_ERROR )\n" ));
524 break;
525
526 case WMT_STARTED:
527 DEBUG_LOG(( "OnStatus( WMT_STARTED )\n" ));
528 break;
529
530 case WMT_STOPPED:
531 DEBUG_LOG(( "OnStatus( WMT_STOPPED )\n" ));
532 break;
533
534 case WMT_BUFFERING_START:
535 DEBUG_LOG(( "OnStatus( WMT_BUFFERING START)\n" ));
536 break;
537
538 case WMT_BUFFERING_STOP:
539 DEBUG_LOG(( "OnStatus( WMT_BUFFERING STOP)\n" ));
540 break;
541
542 case WMT_EOF:
543 DEBUG_LOG(( "OnStatus( WMT_EOF )\n" ));
544
545 //
546 // cleanup and exit
547 //
548
549 m_fEof = TRUE;
550
551 if( 0 == m_cBuffersOutstanding )
552 {
553 SetEvent( m_hCompletionEvent );
554 }
555
556 break;
557
558 case WMT_END_OF_SEGMENT:
559 DEBUG_LOG(( "OnStatus( WMT_END_OF_SEGMENT )\n" ));
560
561 //
562 // cleanup and exit
563 //
564
565 m_fEof = TRUE;
566
567 if( 0 == m_cBuffersOutstanding )
568 {
569 SetEvent( m_hCompletionEvent );
570 }
571
572 break;
573
574 case WMT_LOCATING:
575 DEBUG_LOG(( "OnStatus( WMT_LOCATING )\n" ));
576 break;
577
578 case WMT_CONNECTING:
579 DEBUG_LOG(( "OnStatus( WMT_CONNECTING )\n" ));
580 break;
581
582 case WMT_NO_RIGHTS:
583 {
584 LPWSTR pwszEscapedURL = NULL;
585
586 hr = MakeEscapedURL( m_pszUrl, &pwszEscapedURL );
587
588 if( SUCCEEDED( hr ) )
589 {
590 WCHAR wszURL[ 0x1000 ];
591
592 swprintf( wszURL, L"%s&filename=%s&embedded=false", pValue, pwszEscapedURL );
593
594 hr = LaunchURL( wszURL );
595
596 if( FAILED( hr ) )
597 {
598 DEBUG_LOG(( "Unable to launch web browser to retrieve playback license (hr=0x%08x)\n" , hr ));
599 }
600
601 delete [] pwszEscapedURL;
602 pwszEscapedURL = NULL ;
603 }
604 }
605 break;
606
607 case WMT_MISSING_CODEC:
608 {
609 DEBUG_LOG(( "Missing codec: (hr=0x%08x)\n" , hr ));
610 break;
611 }
612
613 case WMT_CLOSED:
614 SetEvent( m_hCloseEvent );
615 break;
616 };
617
618 return( S_OK );
619}
620
621
624{
625 HRESULT hr = S_OK;
626
627 if( NULL != m_pReader )
628 {
629 hr = m_pReader->Close();
630
631 if( SUCCEEDED( hr ) )
632 {
633 WaitForSingleObject( m_hCloseEvent, INFINITE );
634 }
635 }
636
637 return( hr );
638}
639
640
642void CSimplePlayer::OnWaveOutMsg( UINT uMsg, DWORD dwParam1, DWORD dwParam2 )
643{
644 if( WOM_DONE == uMsg )
645 {
646 //
647 // add the wave header to ready-to-free list for the caller
648 // to pick up and free in the next OnSample call
649 //
650 AddWaveHeader( ( LPWAVEHDR )dwParam1 );
651
652 InterlockedDecrement( &m_cBuffersOutstanding );
653
654 if( m_fEof && ( 0 == m_cBuffersOutstanding ) )
655 {
656 SetEvent( m_hCompletionEvent );
657 }
658 }
659}
660
661
664 HWAVEOUT hwo,
665 UINT uMsg,
666 DWORD dwInstance,
667 DWORD dwParam1,
668 DWORD dwParam2 )
669{
670 CSimplePlayer *pThis = (CSimplePlayer*)dwInstance;
671
672 pThis->OnWaveOutMsg( uMsg, dwParam1, dwParam2 );
673}
674
676HRESULT CSimplePlayer::AddWaveHeader( LPWAVEHDR pwh )
677{
678 WAVEHDR_LIST *tmp = new WAVEHDR_LIST;
679 if( NULL == tmp )
680 {
681 return( E_OUTOFMEMORY );
682 }
683 tmp->pwh = pwh;
684
685 EnterCriticalSection( &m_CriSec );
686 tmp->next = m_whdrHead;
687 m_whdrHead = tmp;
688 LeaveCriticalSection( &m_CriSec );
689 return( S_OK );
690}
691
694{
695 WAVEHDR_LIST *tmp;
696
697 EnterCriticalSection( &m_CriSec );
698 while( NULL != m_whdrHead )
699 {
700 tmp = m_whdrHead->next;
701 DEBUG_ASSERTCRASH( m_whdrHead->pwh->dwFlags & WHDR_DONE, ("RemoveWaveHeaders!") );
702 waveOutUnprepareHeader( m_hwo, m_whdrHead->pwh, sizeof( WAVEHDR ) );
703 delete m_whdrHead->pwh;
704 delete m_whdrHead;
705 m_whdrHead = tmp;
706 }
707 LeaveCriticalSection( &m_CriSec );
708}
#define NULL
Definition BaseType.h:92
#define TRUE
Definition BaseType.h:109
#define FALSE
Definition BaseType.h:113
#define DEBUG_ASSERTCRASH(c, m)
Definition Debug.h:193
#define DEBUG_LOG(m)
Definition Debug.h:157
unsigned short WORD
Definition bittype.h:58
unsigned int UINT
Definition bittype.h:63
unsigned char BYTE
Definition bittype.h:59
unsigned long DWORD
Definition bittype.h:57
unsigned long ULONG
Definition bittype.h:64
VOID(__stdcall *SnmpUtilMemFreePtr)(IN LPVOID pMem)
#define BOOL
Definition Wnd_File.h:57
HANDLE m_hOpenEvent
IWMReader * m_pReader
HRESULT AddWaveHeader(LPWAVEHDR pwh)
virtual ULONG STDMETHODCALLTYPE Release()
CSimplePlayer(HRESULT *phr)
void OnWaveOutMsg(UINT uMsg, DWORD dwParam1, DWORD dwParam2)
LONG m_cBuffersOutstanding
HANDLE m_hCloseEvent
IWMHeaderInfo * m_pHeader
virtual HRESULT Play(LPCWSTR pszUrl, DWORD dwSecDuration, HANDLE hCompletionEvent, HRESULT *phrCompletion)
virtual ULONG STDMETHODCALLTYPE AddRef()
static void CALLBACK WaveProc(HWAVEOUT hwo, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2)
HRESULT * m_phrCompletion
WAVEFORMATEX m_wfx
virtual HRESULT STDMETHODCALLTYPE OnStatus(WMT_STATUS Status, HRESULT hr, WMT_ATTR_DATATYPE dwType, BYTE __RPC_FAR *pValue, void __RPC_FAR *pvContext)
CRITICAL_SECTION m_CriSec
void RemoveWaveHeaders(void)
WAVEHDR_LIST * m_whdrHead
HANDLE m_hCompletionEvent
virtual HRESULT STDMETHODCALLTYPE OnSample(DWORD dwOutputNum, QWORD cnsSampleTime, QWORD cnsSampleDuration, DWORD dwFlags, INSSBuffer __RPC_FAR *pSample, void __RPC_FAR *pvContext)
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject)
#define SIMPLE_PLAYER_CLOSE_EVENT
#define SIMPLE_PLAYER_OPEN_EVENT
struct WAVEHDR_LIST * next
LPWAVEHDR pwh
HRESULT LaunchURL(LPCWSTR pszURL)
HRESULT MakeEscapedURL(LPWSTR pszInURL, LPWSTR *ppszOutURL)
Definition urllaunch.cpp:26