2025-11-28 00:35:46 +09:00

1334 lines
31 KiB
C++

//*****************************************************************************
//
// Microsoft Windows Media
// Copyright (C) Microsoft Corporation. All rights reserved.
//
// FileName: AudioPlay.cpp
//
// Abstract: Implementation of the CAudioPlay class.
//
//*****************************************************************************
#include "stdafx.h"
#include <stdio.h>
#include <string.h>
#include "AudioPlay.h"
#include "asferr.h"
#include "nserror.h"
#include "AudioDlg.h"
#ifdef SUPPORT_DRM
#include "DRM.h"
CDRM objDRM; // to handle protected content
#endif
//------------------------------------------------------------------------------
// Name: CAudioPlay::CAudioPlay()
// Desc: Constructor.
//------------------------------------------------------------------------------
CAudioPlay::CAudioPlay()
{
m_cRef = 1;
#ifdef SUPPORT_DRM
m_bProcessingDRMOps = FALSE;
#endif
m_bClosed = TRUE;
m_bIsSeekable = FALSE;
m_bIsBroadcast = FALSE;
m_bEOF = FALSE;
m_dwAudioOutputNum = 0xFFFFFFFF;
m_dwThreadID = 0;
m_hAsyncEvent = INVALID_HANDLE_VALUE;
m_hrAsync = S_OK;
m_hWaveOut = NULL;
m_pReader = NULL;
m_pHeaderInfo = NULL;
m_cHeadersLeft = 0;
m_pwszURL = NULL;
m_cnsFileDuration = 0;
m_pWfx = NULL;
}
//------------------------------------------------------------------------------
// Name: CAudioPlay::~CAudioPlay()
// Desc: Destructor.
//------------------------------------------------------------------------------
CAudioPlay::~CAudioPlay()
{
Exit();
CoUninitialize();
}
//------------------------------------------------------------------------------
// Name: CAudioPlay::QueryInterface()
// Desc: IUnknown method.
//------------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE CAudioPlay::QueryInterface( /* [in] */ REFIID riid,
/* [out] */ void __RPC_FAR *__RPC_FAR *ppvObject )
{
if( ( IID_IWMReaderCallback == riid ) ||
( IID_IUnknown == riid ) )
{
*ppvObject = static_cast< IWMReaderCallback* >( this );
AddRef();
return( S_OK );
}
*ppvObject = NULL;
return( E_NOINTERFACE );
}
//------------------------------------------------------------------------------
// Name: CAudioPlay::AddRef()
// Desc: IUnknown method.
//------------------------------------------------------------------------------
ULONG STDMETHODCALLTYPE CAudioPlay::AddRef()
{
return( InterlockedIncrement( &m_cRef ) );
}
//------------------------------------------------------------------------------
// Name: CAudioPlay::Release()
// Desc: IUnknown method.
//------------------------------------------------------------------------------
ULONG STDMETHODCALLTYPE CAudioPlay::Release()
{
if( 0 == InterlockedDecrement( &m_cRef ) )
{
delete this;
return( 0 );
}
return( m_cRef );
}
//------------------------------------------------------------------------------
// Name: CAudioPlay::OnStatus()
// Desc: IWMReaderCallback method to process status notifications.
//------------------------------------------------------------------------------
HRESULT CAudioPlay::OnStatus( /* [in] */ WMT_STATUS Status,
/* [in] */ HRESULT hr,
/* [in] */ WMT_ATTR_DATATYPE dwType,
/* [in] */ BYTE __RPC_FAR *pValue,
/* [in] */ void __RPC_FAR *pvContext )
{
// This switch checks for the important messages sent by the reader object.
switch( Status )
{
// The reader is finished opening a file.
case WMT_OPENED:
#ifdef SUPPORT_DRM
//
// Set the event if DRM operation is not going on
//
if( !m_bProcessingDRMOps )
#endif
{
SetAsyncEvent( hr );
}
break;
// Playback of the opened file has begun.
case WMT_STARTED:
m_bEOF = FALSE;
SetCurrentStatus( PLAY );
break;
// The reader is finished closing a file.
case WMT_CLOSED:
SetAsyncEvent( hr );
break;
// The previously playing reader has stopped.
case WMT_STOPPED:
SetAsyncEvent( hr );
SetCurrentStatus( STOP );
break;
// This class reacts to any errors by changing its state to stopped.
case WMT_ERROR:
case WMT_EOF:
case WMT_MISSING_CODEC:
m_bEOF = TRUE;
//
// Set to STOP when no buffer is left for playback
//
if( 0 == m_cHeadersLeft )
{
SetCurrentStatus( STOP );
}
break;
// The reader has begun buffering.
case WMT_BUFFERING_START:
SetCurrentStatus( BUFFERING );
break;
// the reader has completed buffering.
case WMT_BUFFERING_STOP:
SetCurrentStatus( PLAY );
break;
case WMT_LOCATING:
break;
//
// License and DRM related messages
//
case WMT_NO_RIGHTS:
case WMT_NO_RIGHTS_EX:
case WMT_NEEDS_INDIVIDUALIZATION:
case WMT_LICENSEURL_SIGNATURE_STATE:
#ifdef SUPPORT_DRM
m_bProcessingDRMOps = TRUE;
#endif
case WMT_ACQUIRE_LICENSE:
case WMT_INDIVIDUALIZE:
#ifdef SUPPORT_DRM
objDRM.OnDRMStatus( Status, hr, dwType, pValue, pvContext );
#endif
break;
default:
break;
}
return( S_OK );
}
//------------------------------------------------------------------------------
// Name: CAudioPlay::OnSample()
// Desc: IWMReaderCallback method to process samples.
//------------------------------------------------------------------------------
HRESULT CAudioPlay::OnSample( /* [in] */ DWORD dwOutputNum,
/* [in] */ QWORD cnsSampleTime,
/* [in] */ QWORD cnsSampleDuration,
/* [in] */ DWORD dwFlags,
/* [in] */ INSSBuffer __RPC_FAR *pSample,
/* [in] */ void __RPC_FAR *pvContext )
{
// Check the output number of the sample against the stored output number.
// Because only the first audio output is stored, all other outputs,
// regardless of type, will be ignored.
if( dwOutputNum != m_dwAudioOutputNum )
{
return( S_OK );
}
BYTE *pData = NULL;
DWORD cbData = 0;
HRESULT hr = S_OK;
MMRESULT mmr;
// Get the sample from the buffer object.
hr = pSample->GetBufferAndLength( &pData, &cbData );
if( FAILED( hr ) )
{
return( hr );
}
// The rest of the code in this method uses Windows Multimedia wave
// handling functions to play the content.
//
// Allocate memory for header and data.
//
LPWAVEHDR pwh = ( LPWAVEHDR ) new BYTE[ sizeof( WAVEHDR ) + cbData ];
if( NULL == pwh )
{
return( HRESULT_FROM_WIN32( GetLastError() ) );
}
pwh->lpData = ( LPSTR )&pwh[1];
pwh->dwBufferLength = cbData;
pwh->dwBytesRecorded = cbData;
pwh->dwUser = ( DWORD )cnsSampleTime;
pwh->dwLoops = 0;
pwh->dwFlags = 0;
CopyMemory( pwh->lpData, pData, cbData );
do
{
//
// Prepare the header for playing.
//
mmr = waveOutPrepareHeader( m_hWaveOut, pwh, sizeof( WAVEHDR ) );
if( MMSYSERR_NOERROR != mmr )
{
break;
}
//
// Send the sample to the wave output device.
//
hr = waveOutWrite( m_hWaveOut, pwh, sizeof( WAVEHDR ) );
if( MMSYSERR_NOERROR != hr )
{
break;
}
InterlockedIncrement( &m_cHeadersLeft );
if( m_bIsBroadcast )
{
SetTime( cnsSampleTime, 0 );
}
else
{
SetTime( cnsSampleTime, m_cnsFileDuration );
}
}
while( FALSE );
if( MMSYSERR_NOERROR != mmr )
{
delete [] pwh;
//
// Stop the player.
//
MessageBox( g_hwndDialog, _T( "Wave function failed" ), ERROR_DIALOG_TITLE, MB_OK );
Stop();
}
return( S_OK );
}
//------------------------------------------------------------------------------
// Name: CAudioPlay::Init()
// Desc: Initializes the audio player.
//------------------------------------------------------------------------------
HRESULT CAudioPlay::Init()
{
HRESULT hr = S_OK;
TCHAR tszErrMsg[ 256 ];
do
{
hr = CoInitialize( NULL );
if( FAILED( hr ) )
{
(void)StringCchCopy( tszErrMsg, ARRAYSIZE(tszErrMsg), _T( "CoInitialize failed" ) );
break;
}
// Create an event for asynchronous calls.
// When code in this class makes a call to an asynchronous
// method, it will wait for the event to be set to resume
// processing.
m_hAsyncEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
if( NULL == m_hAsyncEvent )
{
(void)StringCchCopy( tszErrMsg, ARRAYSIZE(tszErrMsg), _T( "Could not create the event" ) );
hr = E_FAIL;
break;
}
// Create a reader object, requesting only playback rights.
hr = WMCreateReader( NULL, WMT_RIGHT_PLAYBACK, &m_pReader );
if( FAILED( hr ) )
{
(void)StringCchCopy( tszErrMsg, ARRAYSIZE(tszErrMsg), _T( "Could not create Reader" ) );
break;
}
}
while( FALSE );
if( FAILED( hr ) )
{
(void)StringCchPrintf( tszErrMsg, ARRAYSIZE(tszErrMsg), _T("%s (hr=%#X)"), tszErrMsg, hr );
MessageBox( g_hwndDialog, tszErrMsg, ERROR_DIALOG_TITLE, MB_OK );
Exit();
}
return( hr );
}
//------------------------------------------------------------------------------
// Name: CAudioPlay::Exit()
// Desc: Cleanup.
//------------------------------------------------------------------------------
HRESULT CAudioPlay::Exit()
{
SAFE_RELEASE( m_pHeaderInfo );
SAFE_RELEASE( m_pReader );
if( NULL != m_hWaveOut )
{
waveOutClose( m_hWaveOut );
m_hWaveOut = NULL;
}
if( NULL != m_hAsyncEvent )
{
CloseHandle( m_hAsyncEvent );
m_hAsyncEvent = NULL;
}
SAFE_ARRAYDELETE( m_pwszURL );
if( NULL != m_pWfx )
{
delete [] ( BYTE* )m_pWfx;
m_pWfx = NULL;
}
return( S_OK );
}
//------------------------------------------------------------------------------
// Name: CAudioPlay::Open()
// Desc: Opens a file.
//------------------------------------------------------------------------------
HRESULT CAudioPlay::Open( LPCWSTR pwszUrl )
{
const INT MAX_ERROR_LENGTH = 256;
// Check that the parameter is not NULL and that the reader is initialized.
if( NULL == pwszUrl )
{
return( E_INVALIDARG );
}
if( NULL == m_pReader )
{
return( E_UNEXPECTED );
}
HRESULT hr = S_OK;
TCHAR tszErrMsg[ MAX_ERROR_LENGTH ];
do
{
ZeroMemory( tszErrMsg, MAX_ERROR_LENGTH );
ResetEvent( m_hAsyncEvent );
// Close previously opened file, if any.
Close();
// Delete the old file name.
SAFE_ARRAYDELETE( m_pwszURL );
// Set the new file name.
m_pwszURL = new WCHAR[ wcslen( pwszUrl ) + 1 ];
if( NULL == m_pwszURL )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
(void)StringCchCopy( tszErrMsg, ARRAYSIZE(tszErrMsg), _T( "Insufficient memory" ) );
break;
}
(void)StringCchCopyW( m_pwszURL, wcslen( pwszUrl ) + 1, pwszUrl );
#ifdef SUPPORT_DRM
hr = objDRM.Init( this, m_pReader, m_pwszURL );
if( FAILED( hr ) )
{
break;
}
m_bProcessingDRMOps = FALSE;
#endif
// Open the file with the reader object. This method call also sets
// the status callback that the reader will use.
hr = m_pReader->Open( pwszUrl, this, NULL );
if( FAILED( hr ) )
{
(void)StringCchCopy( tszErrMsg, ARRAYSIZE(tszErrMsg), _T( "Could not open the specified file" ) );
break;
}
// Wait for the Open call to complete. The event is set in the OnStatus
// callback when the reader reports completion.
WaitForEvent( m_hAsyncEvent );
#ifdef SUPPORT_DRM
if( ( NS_S_DRM_ACQUIRE_CANCELLED == m_hrAsync ) ||
( NS_E_DRM_INDIVIDUALIZATION_INCOMPLETE == m_hrAsync ) )
{
//
// DRM operation has been canceled. So the file has not been opened yet.
//
hr = m_hrAsync;
return( hr );
}
//
// Check to see if license acquisition failed for some reason
//
BOOL fAcquiringLicenseNonSilently = FALSE;
if( 7 == objDRM.GetLastDRMVersion() &&
( NS_E_DRM_LICENSE_NOTACQUIRED == m_hrAsync ||
NS_E_DRM_LICENSE_STORE_ERROR == m_hrAsync ) )
{
//
// Try to acquire the license non-silently.
//
fAcquiringLicenseNonSilently = TRUE;
hr = objDRM.AcquireLastV7LicenseNonSilently();
if ( FAILED( hr ) )
{
return hr;
}
}
//
// Check to see if the license is being acquired non-silently
//
if( NS_E_DRM_NO_RIGHTS == m_hrAsync || // All DRMv1 licenses are gotten non-silently
fAcquiringLicenseNonSilently )
{
//
// The license for this file is being acquired non-silently,
// and it cannot be played until the license is acquired.
//
MessageBox( g_hwndDialog, _T( "After acquiring the license, re-open the file." ), ERROR_DIALOG_TITLE, MB_OK );
SetCurrentStatus( CLOSED );
SetCurrentStatus( READY );
hr = m_hrAsync;
return( hr );
}
#endif
// Check the HRESULT reported by the reader object to the OnStatus
// callback. Most errors in opening files will be reported this way.
if( FAILED( m_hrAsync ) )
{
hr = m_hrAsync;
(void)StringCchCopy( tszErrMsg, ARRAYSIZE(tszErrMsg), _T( "Could not open the specified file" ) );
break;
}
SAFE_RELEASE( m_pHeaderInfo );
hr = m_pReader->QueryInterface( IID_IWMHeaderInfo, ( VOID ** )&m_pHeaderInfo );
if( FAILED (hr) )
{
(void)StringCchCopy( tszErrMsg, ARRAYSIZE(tszErrMsg), _T( "Could not QI for IWMHeaderInfo" ) );
break;
}
hr = RetrieveAndDisplayAttributes();
if( FAILED( hr ) )
{
break;
}
// Set the audio ouput number for the current file.
// Only the first audio output is retrieved, regardless of the
// number of audio outputs in the file.
hr = GetAudioOutput();
if( FAILED( hr ) )
{
break;
}
}
while( FALSE );
if( FAILED( hr ) )
{
Close();
if( _tcslen( tszErrMsg ) > 0 )
{
(void)StringCchPrintf( tszErrMsg, ARRAYSIZE(tszErrMsg), _T("%s (hr=%#X)"), tszErrMsg, hr );
MessageBox( g_hwndDialog, tszErrMsg, ERROR_DIALOG_TITLE, MB_OK );
}
}
return( hr );
}
//------------------------------------------------------------------------------
// Name: CAudioPlay::Close()
// Desc: Closes the file.
//------------------------------------------------------------------------------
HRESULT CAudioPlay::Close()
{
HRESULT hr = S_OK;
if( NULL != m_pReader )
{
hr = m_pReader->Close();
if( FAILED ( hr ) )
{
return( hr );
}
}
// Wait for the reader to deliver a WMT_CLOSED event to the OnStatus
// callback.
WaitForEvent( m_hAsyncEvent );
//
// Close the wave output device.
//
if( m_hWaveOut )
{
if( MMSYSERR_NOERROR != waveOutReset( m_hWaveOut ) )
{
return( E_FAIL );
}
if( MMSYSERR_NOERROR != waveOutClose( m_hWaveOut ) )
{
return( E_FAIL );
}
m_hWaveOut = NULL;
}
return( S_OK );
}
//------------------------------------------------------------------------------
// Name: CAudioPlay::Start()
// Desc: Configures output device and starts reader.
//------------------------------------------------------------------------------
HRESULT CAudioPlay::Start( QWORD cnsStart )
{
HRESULT hr = S_OK;
//
// Ensure that a reader object has been instantiated.
//
if( NULL == m_pReader )
{
return( E_UNEXPECTED );
}
//
// Configure the wave output device.
//
if( NULL != m_hWaveOut )
{
if( MMSYSERR_NOERROR != waveOutReset( m_hWaveOut ) )
{
return( E_FAIL );
}
}
else
{
MMRESULT mmr = waveOutOpen( &m_hWaveOut,
WAVE_MAPPER,
m_pWfx,
( DWORD_PTR )WaveProc,
( DWORD_PTR )this,
CALLBACK_FUNCTION );
if( MMSYSERR_NOERROR != mmr )
{
return( E_FAIL );
}
//
// Use another thread for wave output.
//
HANDLE hThread = CreateThread( NULL, 0, OnWaveOutThread,
( LPVOID )this, NULL, &m_dwThreadID );
if( NULL == hThread )
{
return( HRESULT_FROM_WIN32( GetLastError() ) );
}
else
{
//
// Close the thread handle, as it's no longer required in this thread.
//
CloseHandle( hThread );
}
}
return( m_pReader->Start( cnsStart, 0, 1.0, NULL ) );
}
//------------------------------------------------------------------------------
// Name: CAudioPlay::Stop()
// Desc: Stops the reader.
//------------------------------------------------------------------------------
HRESULT CAudioPlay::Stop()
{
HRESULT hr = S_OK;
//
// Ensure that a reader object has been instantiated.
//
if( NULL == m_pReader )
{
return( E_UNEXPECTED );
}
#ifdef SUPPORT_DRM
//
// Cancel current DRM operation, if any.
//
hr = objDRM.Cancel();
if( FAILED( hr ) )
{
//
// The cancellation of the DRM operation has failed.
//
return( hr );
}
if( S_OK == hr )
{
//
// Since the DRM operation has been successfully canceled, the file is not yet opened.
// So no need to stop.
//
SetCurrentStatus( CLOSED );
SetCurrentStatus( READY );
return( hr );
}
#endif
hr = m_pReader->Stop();
if( FAILED( hr ) )
{
return( hr );
}
//
// Reset the wave output device
//
if( NULL != m_hWaveOut )
{
if( MMSYSERR_NOERROR != waveOutReset( m_hWaveOut ) )
{
return( E_FAIL );
}
//
// Wait for all audio headers to be unprepared.
//
WaitForEvent( m_hAsyncEvent );
}
return( S_OK );
}
//------------------------------------------------------------------------------
// Name: CAudioPlay::Pause()
// Desc: Pauses the reader and the output device.
//------------------------------------------------------------------------------
HRESULT CAudioPlay::Pause()
{
//
// Sanity check
//
if( NULL == m_pReader )
{
return( E_UNEXPECTED );
}
if( NULL != m_hWaveOut )
{
//
// Pause the wave output device
//
if( MMSYSERR_NOERROR != waveOutPause( m_hWaveOut ) )
{
return( E_FAIL );
}
}
return( m_pReader->Pause() );
}
//------------------------------------------------------------------------------
// Name: CAudioPlay::Resume()
// Desc: Resumes after pause.
//------------------------------------------------------------------------------
HRESULT CAudioPlay::Resume()
{
//
// Sanity check
//
if( NULL == m_pReader )
{
return( E_UNEXPECTED );
}
if( NULL != m_hWaveOut )
{
//
// Resume the wave output device
//
if( MMSYSERR_NOERROR != waveOutRestart( m_hWaveOut ) )
{
return( E_FAIL );
}
}
return( m_pReader->Resume() );
}
//------------------------------------------------------------------------------
// Name: CAudioPlay::ReopenReader()
// Desc: Opens reader after DRM operations.
//------------------------------------------------------------------------------
#ifdef SUPPORT_DRM
HRESULT CAudioPlay::ReopenReader( void *pvContext )
{
if( NULL == m_pReader )
{
return( E_UNEXPECTED );
}
m_bProcessingDRMOps = FALSE;
return m_pReader->Open( m_pwszURL, this, pvContext );
}
#endif
//------------------------------------------------------------------------------
// Name: CAudioPlay::SetAsyncEvent()
// Desc: This method sets the asynchronous event. When an asynchronous method
// is called in the other methods of this class, execution of that thread waits
// for the asynchronous event to be set.
//------------------------------------------------------------------------------
void CAudioPlay::SetAsyncEvent( HRESULT hrAsync )
{
m_hrAsync = hrAsync;
SetEvent( m_hAsyncEvent );
}
//------------------------------------------------------------------------------
// Name: CAudioPlay::GetFileDuration()
// Desc: Get the value of m_cnsFileDuration.
//------------------------------------------------------------------------------
QWORD CAudioPlay::GetFileDuration()
{
return( m_cnsFileDuration );
}
//------------------------------------------------------------------------------
// Name: CAudioPlay::IsSeekable()
// Desc: Get the value of m_bIsSeekable.
//------------------------------------------------------------------------------
BOOL CAudioPlay::IsSeekable()
{
return( m_bIsSeekable );
}
//------------------------------------------------------------------------------
// Name: CAudioPlay::IsBroadcast()
// Desc: Get the value of m_bIsBroadcast.
//------------------------------------------------------------------------------
BOOL CAudioPlay::IsBroadcast()
{
return( m_bIsBroadcast );
}
//------------------------------------------------------------------------------
// Name: CAudioPlay::OnWaveOutThread()
// Desc: Start thread for processing WaveOut messages.
//------------------------------------------------------------------------------
DWORD WINAPI CAudioPlay::OnWaveOutThread( LPVOID lpParameter )
{
CAudioPlay* pThis = ( CAudioPlay* )lpParameter;
//
// Redirect the processing to a non-static member
// function of the CAudioPlay class
//
pThis->OnWaveOutMsg();
return( 0 );
}
//------------------------------------------------------------------------------
// Name: CAudioPlay::WaveProc()
// Desc: Callback passed to waveOutOpen.
//------------------------------------------------------------------------------
void CALLBACK CAudioPlay::WaveProc( HWAVEOUT hwo,
UINT uMsg,
DWORD_PTR dwInstance,
DWORD dwParam1,
DWORD dwParam2 )
{
CAudioPlay* pThis = ( CAudioPlay* )dwInstance;
//
// Redirect the processing to a different thread
//
PostThreadMessage( pThis->m_dwThreadID, uMsg, dwParam1, dwParam2 );
}
//------------------------------------------------------------------------------
// Name: CAudioPlay::OnWaveOutMsg()
// Desc: Message pump for WaveOut messages.
//------------------------------------------------------------------------------
void CAudioPlay::OnWaveOutMsg()
{
HRESULT hr = S_OK;
LPWAVEHDR pwh = NULL;
MMRESULT mmr = MMSYSERR_NOERROR;
MSG uMsg;
//
// Create the message queue
//
PeekMessage( &uMsg, NULL, WM_USER, WM_USER, PM_NOREMOVE );
//
// Message queue has been created. Let's get the messages
//
while( 0 != GetMessage( &uMsg, NULL, 0, 0 ) )
{
switch( uMsg.message )
{
case WOM_DONE:
//
// Unprepare the wave header, as it has already been played
//
pwh = ( LPWAVEHDR )uMsg.wParam;
mmr = waveOutUnprepareHeader( m_hWaveOut, pwh, sizeof( WAVEHDR ) );
if( MMSYSERR_NOERROR == mmr )
{
InterlockedDecrement( &m_cHeadersLeft );
}
else if( WHDR_ENDLOOP == mmr )
{
SetEvent( m_hAsyncEvent );
}
else
{
//
// Stop playing as error occurs
//
Stop();
MessageBox( g_hwndDialog, _T("Wave function (waveOutUnprepareHeader) failed"), ERROR_DIALOG_TITLE, MB_OK );
}
//
// Set to STOP if no buffer is left
//
if( m_bEOF && ( 0 == m_cHeadersLeft ) )
{
SetCurrentStatus( STOP );
}
break;
case WOM_CLOSE:
PostQuitMessage( 0 );
break;
}
}
return;
}
//------------------------------------------------------------------------------
// Name: CAudioPlay::WaitForEvent()
// Desc: Waits for the specified event to be signaled.
// The maximum wait time is msMaxWaitTime.
//------------------------------------------------------------------------------
void CAudioPlay::WaitForEvent( HANDLE hEvent, DWORD msMaxWaitTime )
{
DWORD i;
MSG msg;
for( i = 0; i < msMaxWaitTime; i += 10 )
{
if( PeekMessage( &msg, ( HWND ) NULL, 0, 0, PM_REMOVE ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
if( WAIT_TIMEOUT != WaitForSingleObject( hEvent, 10 ) )
{
break;
}
}
return;
}
//------------------------------------------------------------------------------
// Name: CAudioPlay::GetAudioOutput()
// Desc: Gets the wave format for the first audio output.
//------------------------------------------------------------------------------
HRESULT CAudioPlay::GetAudioOutput()
{
DWORD cOutputs = 0;
DWORD i;
TCHAR tszErrMsg[256];
HRESULT hr = S_OK;
IWMOutputMediaProps* pProps = NULL;
WM_MEDIA_TYPE* pMediaType = NULL;
ULONG cbType = 0;
//
// Sanity check
//
if( NULL == m_pReader )
{
return( E_UNEXPECTED );
}
do
{
//
// Find out the output count
//
hr = m_pReader->GetOutputCount( &cOutputs );
if( FAILED( hr ) )
{
(void)StringCchCopy( tszErrMsg, ARRAYSIZE(tszErrMsg), _T( "Could not get output count" ) );
break;
}
//
// Find out one audio output.
// Note: This sample only shows how to handle one audio output.
// If there is more than one audio output, the first one will be picked.
//
for( i = 0; i < cOutputs; i++ )
{
SAFE_RELEASE( pProps );
SAFE_ARRAYDELETE( pMediaType );
hr = m_pReader->GetOutputProps( i, &pProps );
if( FAILED( hr ) )
{
(void)StringCchCopy( tszErrMsg, ARRAYSIZE(tszErrMsg), _T( "Could not get output props" ) );
break;
}
//
// Find out the space needed for pMediaType
//
hr = pProps->GetMediaType( NULL, &cbType );
if( FAILED( hr ) )
{
(void)StringCchCopy( tszErrMsg, ARRAYSIZE(tszErrMsg), _T( "Could not query for the space needed for media type" ) );
break;
}
pMediaType = ( WM_MEDIA_TYPE* ) new BYTE[ cbType ];
if( NULL == pMediaType )
{
hr = HRESULT_FROM_WIN32( GetLastError() ) ;
(void)StringCchCopy( tszErrMsg, ARRAYSIZE(tszErrMsg), _T( "Insufficient memory" ) );
break;
}
//
// Get the value for MediaType
//
hr = pProps->GetMediaType( pMediaType, &cbType );
if( FAILED( hr ) )
{
(void)StringCchCopy( tszErrMsg, ARRAYSIZE(tszErrMsg), _T( "Could not get media type" ) );
break;
}
if( WMMEDIATYPE_Audio == pMediaType->majortype )
{
break;
}
}
if( FAILED( hr ) )
{
break;
}
if( i == cOutputs )
{
//
// Couldn't find any audio output number in the file
//
hr = E_UNEXPECTED;
(void)StringCchCopy( tszErrMsg, ARRAYSIZE(tszErrMsg), _T( "Could not find an audio stream in the specified file" ) );
break;
}
//
// Store the wave format for this output
//
m_dwAudioOutputNum = i;
if( NULL != m_pWfx )
{
delete [] ( BYTE* )m_pWfx;
m_pWfx = NULL;
}
m_pWfx = ( WAVEFORMATEX * )new BYTE[ pMediaType->cbFormat ];
if( NULL == m_pWfx )
{
hr = HRESULT_FROM_WIN32( GetLastError() ) ;
(void)StringCchCopy( tszErrMsg, ARRAYSIZE(tszErrMsg), _T( "Insufficient memory" ) );
break;
}
CopyMemory( m_pWfx, pMediaType->pbFormat, pMediaType->cbFormat );
}
while( FALSE );
SAFE_ARRAYDELETE( pMediaType );
SAFE_RELEASE( pProps );
if( FAILED( hr ) )
{
(void)StringCchPrintf( tszErrMsg, ARRAYSIZE(tszErrMsg), _T("%s (hr=%#X)"), tszErrMsg, hr );
MessageBox( g_hwndDialog, tszErrMsg, ERROR_DIALOG_TITLE, MB_OK );
}
return( hr );
}
//------------------------------------------------------------------------------
// Name: CAudioPlay::RetrieveAndDisplayAttributes()
// Desc: Retrieves and displays title, author, copyright and duration info.
// Retrieves Seekable and Broadcast attributes
//------------------------------------------------------------------------------
HRESULT CAudioPlay::RetrieveAndDisplayAttributes()
{
BYTE* pbValue = NULL;
HRESULT hr = S_OK;
TCHAR tszErrMsg[256];
WCHAR wszNoData[] = L"No Data";
do
{
//
// Get attribute "Title"
//
hr = GetHeaderAttribute( g_wszWMTitle, &pbValue );
if( FAILED( hr ) )
{
(void)StringCchCopy( tszErrMsg, ARRAYSIZE(tszErrMsg), _T( "Could not get the Title attribute" ) );
break;
}
if( NULL != pbValue )
{
SetItemText( IDC_CLIP, ( LPWSTR )pbValue );
SAFE_ARRAYDELETE( pbValue );
}
else
{
SetItemText( IDC_CLIP, wszNoData );
}
//
// Get attribute "Author"
//
hr = GetHeaderAttribute( g_wszWMAuthor, &pbValue );
if( FAILED( hr ) )
{
(void)StringCchCopy( tszErrMsg, ARRAYSIZE(tszErrMsg), _T( "Could not get the Author attribute" ) );
break;
}
if( NULL != pbValue )
{
SetItemText( IDC_AUTHOR, ( LPWSTR )pbValue );
SAFE_ARRAYDELETE( pbValue );
}
else
{
SetItemText( IDC_AUTHOR, wszNoData );
}
//
// Get attribute "Copyright"
//
hr = GetHeaderAttribute( g_wszWMCopyright, &pbValue );
if( FAILED( hr ) )
{
(void)StringCchCopy( tszErrMsg, ARRAYSIZE(tszErrMsg), _T( "Could not get the Copyright attribute" ) );
break;
}
if( NULL != pbValue )
{
SetItemText( IDC_COPYRIGHT, ( LPWSTR )pbValue );
SAFE_ARRAYDELETE( pbValue );
}
else
{
SetItemText( IDC_COPYRIGHT, wszNoData );
}
//
// Get attribute "Duration"
//
hr = GetHeaderAttribute( g_wszWMDuration, &pbValue );
if( FAILED( hr ) )
{
(void)StringCchCopy( tszErrMsg, ARRAYSIZE(tszErrMsg), _T( "Could not get the Duration attribute" ) );
break;
}
if( NULL != pbValue )
{
m_cnsFileDuration = *( QWORD* )pbValue;
SAFE_ARRAYDELETE( pbValue );
}
else
{
m_cnsFileDuration = 0;
}
SetTime( 0, m_cnsFileDuration );
//
// Retrieve Seekable attribute
//
hr = GetHeaderAttribute( g_wszWMSeekable, &pbValue );
if( FAILED( hr ) )
{
(void)StringCchCopy( tszErrMsg, ARRAYSIZE(tszErrMsg), _T( "Could not get the Seekable attribute" ) );
break;
}
if( NULL != pbValue )
{
m_bIsSeekable = *( BOOL* )pbValue;
}
else
{
m_bIsSeekable = FALSE;
}
//
// Retrieve Broadcast attribute
//
hr = GetHeaderAttribute( g_wszWMBroadcast, &pbValue );
if( FAILED( hr ) )
{
(void)StringCchCopy( tszErrMsg, ARRAYSIZE(tszErrMsg), _T( "Could not get the Broadcast attribute" ) );
break;
}
if( NULL != pbValue )
{
m_bIsBroadcast = *( BOOL* )pbValue;
}
else
{
m_bIsBroadcast = FALSE;
}
}
while (FALSE );
if( FAILED( hr ) )
{
(void)StringCchPrintf( tszErrMsg, ARRAYSIZE(tszErrMsg), _T("%s (hr=%#X)"), tszErrMsg, hr );
MessageBox( g_hwndDialog, tszErrMsg, ERROR_DIALOG_TITLE, MB_OK );
}
return( hr );
}
//------------------------------------------------------------------------------
// Name: CAudioPlay::GetHeaderAttribute()
// Desc: Retrieves the specified header attribute from the reader.
//------------------------------------------------------------------------------
HRESULT CAudioPlay::GetHeaderAttribute( LPCWSTR pwszName, BYTE** ppbValue )
{
BYTE *pbValue = NULL;
HRESULT hr = S_OK;
WMT_ATTR_DATATYPE wmtType;
WORD wStreamNum = 0;
WORD cbLength = 0;
//
// Sanity check
//
if( NULL == m_pHeaderInfo )
{
return( E_UNEXPECTED );
}
if( NULL == ppbValue )
{
return( E_INVALIDARG );
}
//
// Get the count of bytes to be allocated for pbValue
//
hr = m_pHeaderInfo->GetAttributeByName( &wStreamNum,
pwszName,
&wmtType,
NULL,
&cbLength );
if( FAILED( hr ) && ( ASF_E_NOTFOUND != hr ) )
{
return( hr );
}
//
// No such an attribute, so return
//
if( ASF_E_NOTFOUND == hr )
{
*ppbValue = NULL;
return( S_OK );
}
pbValue = new BYTE[ cbLength ];
if( NULL == pbValue )
{
return( HRESULT_FROM_WIN32( GetLastError() ) );
}
//
// Get the actual value
//
hr = m_pHeaderInfo->GetAttributeByName( &wStreamNum,
pwszName,
&wmtType,
pbValue,
&cbLength );
*ppbValue = pbValue;
return( S_OK );
}