////////////////////////////////////////////////////////////////////////// // // Decoder.cpp : CDecoder 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 "Decoder.h" /////////////////////////////////////////////////////////////////////// // Name: CreateInstance // Description: Static class method to create the CDecoder object. // // ppDecoder: Receives an AddRef's pointer to the CDecoder object. // The caller must release the pointer. ///////////////////////////////////////////////////////////////////////// HRESULT CDecoder::CreateInstance(CDecoder **ppDecoder) { CDecoder *pDecoder = new CDecoder(); if (!pDecoder) { return E_OUTOFMEMORY; } *ppDecoder = pDecoder; (*ppDecoder)->AddRef(); TRACE((L"CDecoder created.\n")); SAFE_RELEASE (pDecoder); return S_OK; } // ----- Public Methods ----------------------------------------------- ////////////////////////////////////////////////////////////////////////// // Name: CDecoder // Description: Constructor // ///////////////////////////////////////////////////////////////////////// CDecoder::CDecoder() : m_nRefCount (1), m_pMFT (NULL), m_dwInputID (0), m_dwOutputID (0), m_DecoderState (0), m_pMediaController (NULL) { }; // ----- Public Methods ----------------------------------------------- ////////////////////////////////////////////////////////////////////////// // Name: CDecoder // Description: Destructor // ///////////////////////////////////////////////////////////////////////// CDecoder::~CDecoder() { (void)UnLoad(); } ///////////////////////////////////////////////////////////////////// // Name: Initialize // // Initializes the MFT with decoder object specified by the CLSID. // // pclsid: Path name of the file // pMediaType: Pointer to the media type of the stream that the // the MFT will decode. ///////////////////////////////////////////////////////////////////// HRESULT CDecoder::Initialize(CLSID clsid, IMFMediaType *pMediaType) { if (!pMediaType || clsid == GUID_NULL) { return E_INVALIDARG; } HRESULT hr = S_OK; //Unload the existing MFT. if (m_pMFT) { CHECK_HR (hr = UnLoad()); } //Create the MFT decoder CHECK_HR (hr = CoCreateInstance( clsid, NULL, CLSCTX_INPROC_SERVER, __uuidof(IMFTransform), (void**)&m_pMFT)); //Create the media controller that will work with uncompressed data that the decoder generates if (!m_pMediaController) { CHECK_HR (hr = CMediaController::CreateInstance(&m_pMediaController)); } CHECK_HR (hr = ConfigureDecoder( pMediaType)); TRACE((L"MFT initialized.\n")); done: if (FAILED(hr)) { LOG_MSG_IF_FAILED(L"MFT creation failed\n", hr); hr = UnLoad(); } return hr; } ///////////////////////////////////////////////////////////////////// // Name: UnLoad // // Unloads the MFT. // ///////////////////////////////////////////////////////////////////// HRESULT CDecoder::UnLoad() { HRESULT hr = S_OK; if (m_pMFT) { if (m_pMediaController) { CHECK_HR (hr = m_pMediaController->Reset()); } SAFE_RELEASE(m_pMFT); } TRACE((L"MFT unloaded.\n")); done: LOG_MSG_IF_FAILED(L"MFT could not be unloaded.\n", hr); return hr; } ///////////////////////////////////////////////////////////////////// // Name: ConfigureDecoder // // Configures the MFT with the currently loaded decoder. // // pMediaType: Pointer to the media type of the stream that will the // input type of the decoder. ///////////////////////////////////////////////////////////////////// HRESULT CDecoder::ConfigureDecoder(IMFMediaType *pMediaType) { if (!pMediaType) { return E_INVALIDARG; } if (! m_pMFT) { return MF_E_NOT_INITIALIZED; } HRESULT hr = S_OK, hrRes = S_OK; GUID guidMajorType = GUID_NULL, guidSubType = GUID_NULL; IMFMediaType* pOutputType = NULL; //Because this is a decoder transform, the number of input=output=1 //Get the input and output stream ids. This is different from the stream numbers hr = m_pMFT->GetStreamIDs( 1, &m_dwInputID, 1, &m_dwOutputID ); //Set the input type to the one that is received if (SUCCEEDED(hr) || hr == E_NOTIMPL) { CHECK_HR (hr = m_pMFT->SetInputType( m_dwInputID, pMediaType, 0 )); } if (SUCCEEDED(hr)) { //Loop through the available output type until we find: //For audio media type: PCM audio //For video media type: uncompressed RGB32 for ( DWORD dwTypeIndex = 0; (hrRes != MF_E_NO_MORE_TYPES) ; dwTypeIndex++ ) { hrRes = m_pMFT->GetOutputAvailableType( m_dwOutputID, dwTypeIndex, &pOutputType); if (pOutputType && SUCCEEDED(hrRes)) { CHECK_HR (hr = pOutputType->GetMajorType( &guidMajorType )); CHECK_HR (hr = pOutputType->GetGUID( MF_MT_SUBTYPE, &guidSubType )); if ((guidMajorType == MFMediaType_Audio) && (guidSubType == MFAudioFormat_PCM)) { CHECK_HR (hr = m_pMFT->SetOutputType(m_dwOutputID, pOutputType, 0)); CHECK_HR (hr = m_pMediaController->OpenAudioDevice(pOutputType)); break; } else if((guidMajorType == MFMediaType_Video) && (guidSubType == MFVideoFormat_RGB32)) { CHECK_HR (hr = m_pMFT->SetOutputType(m_dwOutputID, pOutputType, 0)); break; } SAFE_RELEASE(pOutputType); } else { //Output type not found hr = E_FAIL; break; } } } done: LOG_MSG_IF_FAILED(L"MFT could not be configured.\n", hr); SAFE_RELEASE(pOutputType); return hr; } ///////////////////////////////////////////////////////////////////// // Name: ProcessAudio // // Passes the input sample through the decoder and sends the output samples // to the CMediaController class. This class adds the buffers of the // output sample to the audio test sample that it maintains. When ready, the // caller can play the test sample through methods on the CMediaController //class. // // pSample: Pointer to a compressed sample that needs to be decoded ///////////////////////////////////////////////////////////////////// HRESULT CDecoder::ProcessAudio(IMFSample *pSample) { if (!pSample) { return E_INVALIDARG; } if (! m_pMFT || ! m_pMediaController) { return MF_E_NOT_INITIALIZED; } HRESULT hr = S_OK, hrRes = S_OK; DWORD dwStatus = 0; IMFMediaBuffer* pBufferOut = NULL; IMFSample* pSampleOut = NULL; //get the size of the output buffer processed by the decoder. //Again, there is only one output so the output stream id is 0. MFT_OUTPUT_STREAM_INFO mftStreamInfo; ZeroMemory(&mftStreamInfo, sizeof(MFT_OUTPUT_STREAM_INFO)); CHECK_HR (hr = m_pMFT->GetOutputStreamInfo(m_dwOutputID, &mftStreamInfo)); MFT_OUTPUT_DATA_BUFFER mftOutputData; ZeroMemory(&mftOutputData, sizeof(mftOutputData)); CHECK_HR (hr = m_pMFT->ProcessInput(m_dwInputID, pSample, 0)); //Request output samples from the decoder do { //create a buffer for the output sample CHECK_HR (hr = MFCreateMemoryBuffer(mftStreamInfo.cbSize, &pBufferOut)); //Create the output sample CHECK_HR (hr = MFCreateSample(&pSampleOut)); //Add the output buffer CHECK_HR (hr = pSampleOut->AddBuffer(pBufferOut)); //Set the output sample mftOutputData.pSample = pSampleOut; //Set the output id mftOutputData.dwStreamID = m_dwOutputID; //Generate the output sample hrRes = m_pMFT->ProcessOutput(0, 1, &mftOutputData, &dwStatus); //Send it to the media controller so that it can collect the test sample CHECK_HR (hr = m_pMediaController->AddToAudioTestSample(mftOutputData.pSample)); SAFE_RELEASE(pBufferOut); SAFE_RELEASE(pSampleOut); }while(hrRes != MF_E_TRANSFORM_NEED_MORE_INPUT); done: SAFE_RELEASE(pBufferOut); SAFE_RELEASE(pSampleOut); return hr; } ///////////////////////////////////////////////////////////////////// // Name: ProcessVideo // // Passes the input sample through the decoder and sends the output sample data // to the CMediaController class. This class creates a bitmap for the sample. // When ready, the caller can display the bitmap through methods on // the CMediaController class. // // pSample: Pointer to a compressed sample that needs to be decoded ///////////////////////////////////////////////////////////////////// HRESULT CDecoder::ProcessVideo(IMFSample *pSample) { if (!pSample) { return E_INVALIDARG; } if (! m_pMFT || ! m_pMediaController) { return MF_E_NOT_INITIALIZED; } HRESULT hr = S_OK, hrRes = S_OK; DWORD dwStatus = 0; DWORD cbTotalLength = 0, cbCurrentLength = 0; BYTE *pData = NULL; IMFMediaBuffer* pBufferOut = NULL; IMFSample* pSampleOut = NULL; IMFSample* pBitmapSample = NULL; IMFMediaType* pMediaType = NULL; //Create a buffer for the transform output MFT_OUTPUT_STREAM_INFO mftStreamInfo; ZeroMemory(&mftStreamInfo, sizeof(MFT_OUTPUT_STREAM_INFO)); //get the size of the output buffer processed by the decoder. //Again, there is only one output so the output stream id is 0. CHECK_HR (hr = m_pMFT->GetOutputStreamInfo(0, &mftStreamInfo)); //Request samples from the decoder MFT_OUTPUT_DATA_BUFFER mftOutputData; ZeroMemory(&mftOutputData, sizeof(mftOutputData)); //Create the bitmap sample that the media controller will use to create the bitmap CHECK_HR (hr = MFCreateSample(&pBitmapSample)); //Send input to the decoder. There is only one input stream so the ID is 0. CHECK_HR (hr = m_pMFT->ProcessInput(m_dwInputID, pSample, 0)); //Request output samples from the decoder do { //create a buffer for the output sample CHECK_HR (hr = MFCreateMemoryBuffer(mftStreamInfo.cbSize, &pBufferOut)); //Create the output sample CHECK_HR (hr = MFCreateSample(&pSampleOut)); //Add the output buffer CHECK_HR (hr = pSampleOut->AddBuffer(pBufferOut)); //Set the output sample mftOutputData.pSample = pSampleOut; mftOutputData.dwStreamID = m_dwOutputID; //Generate the output sample hrRes = m_pMFT->ProcessOutput(0, 1, &mftOutputData, &dwStatus); //Add the buffer to the bitmap sample CHECK_HR (hr = pBitmapSample->AddBuffer(pBufferOut)); SAFE_RELEASE(pBufferOut); SAFE_RELEASE(pSampleOut); }while(hrRes != MF_E_TRANSFORM_NEED_MORE_INPUT); //Get all bitmap data in one buffer CHECK_HR (hr = pBitmapSample->ConvertToContiguousBuffer(&pBufferOut)); CHECK_HR (hr = m_pMFT->GetOutputCurrentType(m_dwOutputID, &pMediaType)); //Get a pointer to the memory CHECK_HR (hr = pBufferOut->Lock(&pData, &cbTotalLength, &cbCurrentLength)); //Send it to the media controller to create the bitmap CHECK_HR (hr = m_pMediaController->CreateBitmapForKeyFrame(pData, pMediaType)); CHECK_HR (hr = pBufferOut->Unlock()); pData = NULL; done: if (pData && FAILED(hr)) { pBufferOut->Unlock(); } SAFE_RELEASE(pBufferOut); SAFE_RELEASE(pSampleOut); SAFE_RELEASE(pMediaType); return hr; } HRESULT CDecoder::StartDecoding(void) { if(! m_pMFT) { return MF_E_NOT_INITIALIZED; } HRESULT hr = m_pMFT->ProcessMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, 0); if (SUCCEEDED(hr)) { m_DecoderState = STREAMING; } return hr; } HRESULT CDecoder::StopDecoding(void) { if(! m_pMFT) { return MF_E_NOT_INITIALIZED; } HRESULT hr = m_pMFT->ProcessMessage(MFT_MESSAGE_NOTIFY_END_STREAMING, 0); if (SUCCEEDED(hr)) { m_DecoderState = NOT_STREAMING; } return hr; }