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

486 lines
13 KiB
C++

//////////////////////////////////////////////////////////////////////////
//
// 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;
}