517 lines
13 KiB
C++
517 lines
13 KiB
C++
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// MediaController.cpp : CMediaController class implementation.
|
|
//
|
|
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
|
|
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
|
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
|
|
// PARTICULAR PURPOSE.
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
#include "MediaController.h"
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
// Name: CreateInstance
|
|
// Description: Static class method to create the CMediaController object.
|
|
//
|
|
// ppDecoder: Receives an AddRef's pointer to the CMediaController object.
|
|
// The caller must release the pointer.
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT CMediaController::CreateInstance(CMediaController **ppMediaController)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
//Instantiate the class
|
|
CMediaController *pMediaController = new CMediaController(&hr);
|
|
|
|
if (!pMediaController)
|
|
{
|
|
LOG_MSG_IF_FAILED(L"CMediaController creation failed.\n", E_OUTOFMEMORY);
|
|
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
//Return the pointer to the caller
|
|
if (SUCCEEDED (hr))
|
|
{
|
|
*ppMediaController = pMediaController;
|
|
(*ppMediaController)->AddRef();
|
|
|
|
TRACE((L"CMediaController created.\n"));
|
|
}
|
|
|
|
LOG_MSG_IF_FAILED(L"CMediaController creation failed.\n", E_FAIL);
|
|
|
|
SAFE_RELEASE (pMediaController);
|
|
|
|
return hr;
|
|
}
|
|
|
|
// ----- Public Methods -----------------------------------------------
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Name: CMediaController
|
|
// Description: Constructor
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
CMediaController::CMediaController(HRESULT* hr)
|
|
:
|
|
m_nRefCount (1),
|
|
m_gdiplusToken (0),
|
|
m_pAudioTestSample (NULL),
|
|
m_pBitmap (NULL),
|
|
m_hWaveOut (NULL),
|
|
m_fHasTestMedia (FALSE),
|
|
m_fAudioDeviceBusy (FALSE)
|
|
{
|
|
//Load the GDI+ platform, this will be used for displaying the bitmap
|
|
Gdiplus::Status status;
|
|
GdiplusStartupInput gdiplusStartupInput;
|
|
|
|
status = GdiplusStartup(&m_gdiplusToken, &gdiplusStartupInput, NULL);
|
|
|
|
if (status == Ok)
|
|
{
|
|
*hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
*hr = E_FAIL;
|
|
}
|
|
};
|
|
|
|
// ----- Public Methods -----------------------------------------------
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Name: CMediaController
|
|
// Description: Destructor
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
CMediaController::~CMediaController()
|
|
{
|
|
//Release resources
|
|
Reset();
|
|
|
|
//Shutdown GDI+
|
|
if (m_gdiplusToken!=0)
|
|
{
|
|
GdiplusShutdown(m_gdiplusToken);
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
// Name: CreateBitmapForKeyFrame
|
|
//
|
|
// Creates a Bitmap object from pixel data
|
|
//
|
|
// pPixelData: Pixel data for the key frame.
|
|
// pMediaType: Pointer to the media type of the stream.
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT CMediaController::CreateBitmapForKeyFrame(BYTE* pPixelData, IMFMediaType* pMediaType)
|
|
{
|
|
if(!pPixelData || !pMediaType)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
INT32 stride = 0;
|
|
|
|
//Get the Frame size and stride through Media Type attributes
|
|
|
|
CHECK_HR (hr = MFGetAttributeSize(pMediaType, MF_MT_FRAME_SIZE, &m_Width, &m_Height));
|
|
|
|
CHECK_HR (pMediaType->GetUINT32(MF_MT_DEFAULT_STRIDE, (UINT32*)&stride));
|
|
|
|
SAFE_DELETE(m_pBitmap);
|
|
|
|
//Create the bitmap with the given size
|
|
m_pBitmap = new Bitmap(m_Width, m_Height, (INT32)stride, PixelFormat32bppRGB, pPixelData);
|
|
|
|
if(!m_pBitmap)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto done;
|
|
}
|
|
else
|
|
{
|
|
//Bitmap was created, set the flag
|
|
m_fHasTestMedia = TRUE;
|
|
TRACE((L"Bitmap for the key frame created.\n"));
|
|
}
|
|
|
|
done:
|
|
LOG_MSG_IF_FAILED(L"Bitmap could not be created.\n", hr);
|
|
|
|
return hr;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
// Name: GetBitmapDimensions
|
|
//
|
|
// Gets the width and heigh of the Bitmap object.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT CMediaController::GetBitmapDimensions(UINT32 *pWidth, UINT32 *pHeight)
|
|
{
|
|
if (m_pBitmap == NULL)
|
|
{
|
|
return MF_E_NOT_INITIALIZED;
|
|
}
|
|
|
|
if (!pWidth || !pHeight)
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
*pWidth = m_Width;
|
|
*pHeight = m_Height;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
// Name: DrawKeyFrame
|
|
//
|
|
// Draws the Bitmap object on the specified Window.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT CMediaController::DrawKeyFrame(HWND hWnd)
|
|
{
|
|
if (!m_pBitmap)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
Graphics* grph = new Graphics(hWnd);
|
|
|
|
if (!grph)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
Status st = grph->DrawImage( m_pBitmap, 0, 0);
|
|
|
|
delete grph;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
// Name: AddToAudioTestSample
|
|
//
|
|
// Adds the audio sample buffers to a test sample that the class maintains.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT CMediaController::AddToAudioTestSample (IMFSample *pSample)
|
|
{
|
|
if(!pSample)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
IMFMediaBuffer* pBuffer = NULL;
|
|
|
|
if (! m_pAudioTestSample)
|
|
{
|
|
CHECK_HR ( hr = MFCreateSample(&m_pAudioTestSample));
|
|
}
|
|
|
|
CHECK_HR (hr = pSample->ConvertToContiguousBuffer(&pBuffer));
|
|
|
|
CHECK_HR (hr = m_pAudioTestSample->AddBuffer(pBuffer));
|
|
|
|
this->m_fHasTestMedia = TRUE;
|
|
|
|
done:
|
|
|
|
SAFE_RELEASE (pBuffer);
|
|
return hr;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
// Name: Reset
|
|
//
|
|
// Resets the audio test sample by removing all the buffers from it.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT CMediaController::Reset()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
SAFE_DELETE (m_pBitmap);
|
|
|
|
SAFE_RELEASE (m_pAudioTestSample);
|
|
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
m_fHasTestMedia = FALSE;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
// Name: CloseAudioDevice
|
|
//
|
|
// Closes the default waveOut device, if open.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT CMediaController::CloseAudioDevice()
|
|
{
|
|
if (m_hWaveOut != NULL)
|
|
{
|
|
if (waveOutClose(m_hWaveOut) != MMSYSERR_NOERROR)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
m_hWaveOut = NULL;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
// Name: OpenAudioDevice
|
|
//
|
|
// Opens the default waveOut device.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT CMediaController::OpenAudioDevice(IMFMediaType* pMediaType)
|
|
{
|
|
if (!pMediaType)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
WAVEFORMATEX* pWavOutputFormat = NULL;
|
|
UINT32 cbWavOutputFormatSize = 0;
|
|
|
|
//Create the WAVEFORMATEX structure from the media type
|
|
CHECK_HR (hr = MFCreateWaveFormatExFromMFMediaType(pMediaType, &pWavOutputFormat, &cbWavOutputFormatSize));
|
|
|
|
if (!pWavOutputFormat)
|
|
{
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
//Query if the format is supported
|
|
MMRESULT mmr = waveOutOpen(
|
|
NULL,
|
|
WAVE_MAPPER, // select the device for me
|
|
pWavOutputFormat, // format
|
|
0,
|
|
0,
|
|
WAVE_FORMAT_QUERY // Query if the format is OK.
|
|
);
|
|
|
|
if (mmr == MMSYSERR_NOERROR)
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
hr = MF_E_INVALIDMEDIATYPE;
|
|
goto done;
|
|
}
|
|
|
|
//if the format is supported, open the audio device
|
|
if (SUCCEEDED(hr) )
|
|
{
|
|
//make sure the device is not in use
|
|
hr = CloseAudioDevice();
|
|
|
|
// Create the thread that will handle messages from the waveOut device.
|
|
DWORD dwThreadId;
|
|
|
|
m_hThread = CreateThread( NULL, 0, WaveOutThreadProc, (void*)this, NULL, &dwThreadId );
|
|
|
|
if( NULL == m_hThread )
|
|
{
|
|
hr =( HRESULT_FROM_WIN32( GetLastError() ) );
|
|
goto done;
|
|
}
|
|
|
|
// Open the device.
|
|
MMRESULT mmr = waveOutOpen(
|
|
&m_hWaveOut, // receives the handle to the device
|
|
WAVE_MAPPER, // select the device for me
|
|
pWavOutputFormat, // format
|
|
(DWORD)dwThreadId, // thread ID to get waveOut messages
|
|
(DWORD_PTR)this, // instance data
|
|
CALLBACK_THREAD
|
|
);
|
|
|
|
if (mmr != MMSYSERR_NOERROR)
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
}
|
|
|
|
TRACE((L"Audio device opened.\n"));
|
|
|
|
done:
|
|
//free the WAVEFORMATEX structure
|
|
CoTaskMemFree(pWavOutputFormat);
|
|
|
|
return hr;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
// Name: PlayAudio
|
|
//
|
|
// Delivers an audio buffer to the waveOut device.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT CMediaController::PlayAudio()
|
|
{
|
|
|
|
if (! m_hWaveOut || ! m_pAudioTestSample)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
//Check if the device is busy
|
|
if (m_fAudioDeviceBusy)
|
|
{
|
|
return E_ACCESSDENIED;
|
|
}
|
|
|
|
HRESULT hr = S_OK;
|
|
MMRESULT mmt;
|
|
|
|
//WAVEHDR pWaveHeader;
|
|
IMFMediaBuffer* pAudioBuffer = NULL;
|
|
|
|
BYTE *pData = NULL;
|
|
DWORD cbData = 0;
|
|
|
|
//Get all the buffers in the test sample in one buffer
|
|
CHECK_HR (hr = m_pAudioTestSample->ConvertToContiguousBuffer(&pAudioBuffer));
|
|
|
|
// Get a pointer to the buffer.
|
|
CHECK_HR (hr = pAudioBuffer->Lock( &pData, NULL, &cbData ));
|
|
|
|
// Prepare the header for playing.
|
|
m_WaveHeader.lpData = (LPSTR)pData;
|
|
m_WaveHeader.dwBufferLength = cbData;
|
|
m_WaveHeader.dwBytesRecorded = cbData;
|
|
m_WaveHeader.dwUser = (DWORD_PTR)pAudioBuffer; // Store the sample pointer as user data. This will be released in the MM_WOM_DONE handler
|
|
m_WaveHeader.dwLoops = 0;
|
|
m_WaveHeader.dwFlags = 0;
|
|
|
|
mmt = waveOutPrepareHeader( m_hWaveOut, &m_WaveHeader, sizeof( WAVEHDR ) );
|
|
|
|
if (mmt == MMSYSERR_NOERROR)
|
|
{
|
|
// Send the sample to the waveOut device.
|
|
mmt = waveOutWrite( m_hWaveOut, &m_WaveHeader, sizeof( WAVEHDR ) );
|
|
|
|
hr = S_OK;
|
|
}
|
|
|
|
if (mmt != MMSYSERR_NOERROR)
|
|
{
|
|
SAFE_RELEASE(pAudioBuffer);
|
|
CHECK_HR(hr = E_FAIL);
|
|
}
|
|
else
|
|
{
|
|
hr = S_OK;
|
|
|
|
//Set the device to busy
|
|
m_fAudioDeviceBusy = TRUE;
|
|
}
|
|
|
|
TRACE((L"Playing test audio.\n"));
|
|
|
|
done:
|
|
if (FAILED(hr))
|
|
{
|
|
pAudioBuffer->Unlock();
|
|
SAFE_RELEASE (pAudioBuffer);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: WaveOutThreadProc
|
|
// Desc: ThreadProc for the worker thread that handles waveOut messages.
|
|
//
|
|
// Note: This is a static method. It calls through to a member function.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
DWORD WINAPI CMediaController::WaveOutThreadProc( LPVOID lpParameter )
|
|
{
|
|
CMediaController* pThis = ( CMediaController* )lpParameter;
|
|
|
|
// Redirect the processing to a non-static member function.
|
|
|
|
pThis->DoWaveOutThread();
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: DoWaveOutThread
|
|
// Desc: Implements the ThreadProc (see WaveOutThreadProc)
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void CMediaController::DoWaveOutThread()
|
|
{
|
|
MSG uMsg;
|
|
|
|
while( 0 != GetMessage( &uMsg, NULL, 0, 0 ) )
|
|
{
|
|
switch( uMsg.message )
|
|
{
|
|
case MM_WOM_DONE: // waveOut has finished using an audio buffer.
|
|
{
|
|
WAVEHDR *pwh = (WAVEHDR*)uMsg.lParam;
|
|
|
|
// (1) Unprepare the wave header.
|
|
MMRESULT mmr = waveOutUnprepareHeader(m_hWaveOut, pwh, sizeof(WAVEHDR));
|
|
|
|
// (2) Release the buffer pointer.
|
|
IMFMediaBuffer *pBuffer= (IMFMediaBuffer*)pwh->dwUser;
|
|
pBuffer->Unlock();
|
|
pBuffer->Release();
|
|
|
|
// (3) Reset the WAVEHDR structure
|
|
ZeroMemory(pwh, sizeof(WAVEHDR));
|
|
|
|
// (4) Set the busy flag
|
|
m_fAudioDeviceBusy = FALSE;
|
|
}
|
|
break;
|
|
|
|
case MM_WOM_CLOSE: // the waveOut device has closed.
|
|
// Tell the thread to quit:
|
|
PostQuitMessage( 0 ); // This causes GetMessage to return 0, so we exit the loop
|
|
break;
|
|
}
|
|
}
|
|
} |