mirror of https://github.com/FreeRDP/FreeRDP
libfreerdp-codec: initial Media Foundation H.264 decoder support
This commit is contained in:
parent
32d1053abe
commit
d952502285
|
@ -65,6 +65,7 @@ static H264_CONTEXT_SUBSYSTEM g_Subsystem_dummy =
|
|||
#ifdef _WIN32
|
||||
|
||||
#include <mfapi.h>
|
||||
#include <mferror.h>
|
||||
#include <codecapi.h>
|
||||
#include <wmcodecdsp.h>
|
||||
#include <mftransform.h>
|
||||
|
@ -74,12 +75,20 @@ static H264_CONTEXT_SUBSYSTEM g_Subsystem_dummy =
|
|||
#include <initguid.h>
|
||||
|
||||
DEFINE_GUID(CLSID_CMSH264DecoderMFT,0x62CE7E72,0x4C71,0x4d20,0xB1,0x5D,0x45,0x28,0x31,0xA8,0x7D,0x9D);
|
||||
DEFINE_GUID(CLSID_VideoProcessorMFT,0x88753b26,0x5b24,0x49bd,0xb2,0xe7,0x0c,0x44,0x5c,0x78,0xc9,0x82);
|
||||
DEFINE_GUID(IID_IMFTransform,0xbf94c121,0x5b05,0x4e6f,0x80,0x00,0xba,0x59,0x89,0x61,0x41,0x4d);
|
||||
DEFINE_GUID(MF_MT_MAJOR_TYPE,0x48eba18e,0xf8c9,0x4687,0xbf,0x11,0x0a,0x74,0xc9,0xf9,0x6a,0x8f);
|
||||
DEFINE_GUID(MF_MT_FRAME_SIZE,0x1652c33d,0xd6b2,0x4012,0xb8,0x34,0x72,0x03,0x08,0x49,0xa3,0x7d);
|
||||
DEFINE_GUID(MF_MT_DEFAULT_STRIDE,0x644b4e48,0x1e02,0x4516,0xb0,0xeb,0xc0,0x1c,0xa9,0xd4,0x9a,0xc6);
|
||||
DEFINE_GUID(MF_MT_SUBTYPE,0xf7e34c9a,0x42e8,0x4714,0xb7,0x4b,0xcb,0x29,0xd7,0x2c,0x35,0xe5);
|
||||
DEFINE_GUID(MF_XVP_DISABLE_FRC,0x2c0afa19,0x7a97,0x4d5a,0x9e,0xe8,0x16,0xd4,0xfc,0x51,0x8d,0x8c);
|
||||
DEFINE_GUID(MFMediaType_Video,0x73646976,0x0000,0x0010,0x80,0x00,0x00,0xAA,0x00,0x38,0x9B,0x71);
|
||||
DEFINE_GUID(MFVideoFormat_RGB32,22,0x0000,0x0010,0x80,0x00,0x00,0xAA,0x00,0x38,0x9B,0x71);
|
||||
DEFINE_GUID(MFVideoFormat_ARGB32,21,0x0000,0x0010,0x80,0x00,0x00,0xAA,0x00,0x38,0x9B,0x71);
|
||||
DEFINE_GUID(IID_ICodecAPI,0x901db4c7,0x31ce,0x41a2,0x85,0xdc,0x8f,0xa0,0xbf,0x41,0xb8,0xda);
|
||||
DEFINE_GUID(CODECAPI_AVLowLatencyMode,0x9c27891a,0xed7a,0x40e1,0x88,0xe8,0xb2,0x27,0x27,0xa0,0x24,0xee);
|
||||
DEFINE_GUID(CODECAPI_AVDecVideoMaxCodedWidth,0x5ae557b8,0x77af,0x41f5,0x9f,0xa6,0x4d,0xb2,0xfe,0x1d,0x4b,0xca);
|
||||
DEFINE_GUID(CODECAPI_AVDecVideoMaxCodedHeight,0x7262a16a,0xd2dc,0x4e75,0x9b,0xa8,0x65,0xc0,0xc6,0xd3,0x2b,0x13);
|
||||
|
||||
#ifndef FCC
|
||||
#define FCC(ch4) ((((DWORD)(ch4) & 0xFF) << 24) | \
|
||||
|
@ -113,6 +122,8 @@ struct _H264_CONTEXT_MF
|
|||
IMFMediaType* inputType;
|
||||
IMFMediaType* outputType;
|
||||
IMFSample* sample;
|
||||
UINT32 frameWidth;
|
||||
UINT32 frameHeight;
|
||||
HMODULE mfplat;
|
||||
pfnMFStartup MFStartup;
|
||||
pfnMFShutdown MFShutdown;
|
||||
|
@ -123,69 +134,6 @@ struct _H264_CONTEXT_MF
|
|||
};
|
||||
typedef struct _H264_CONTEXT_MF H264_CONTEXT_MF;
|
||||
|
||||
static int mf_decompress(H264_CONTEXT* h264, BYTE* pSrcData, UINT32 SrcSize)
|
||||
{
|
||||
HRESULT hr;
|
||||
BYTE* pbBuffer = NULL;
|
||||
DWORD cbMaxLength = 0;
|
||||
DWORD cbCurrentLength = 0;
|
||||
IMFMediaBuffer* mediaBuffer = NULL;
|
||||
H264_CONTEXT_MF* sys = (H264_CONTEXT_MF*) h264->pSystemData;
|
||||
|
||||
hr = sys->MFCreateMemoryBuffer(SrcSize, &mediaBuffer);
|
||||
|
||||
if (FAILED(hr))
|
||||
goto error;
|
||||
|
||||
hr = mediaBuffer->lpVtbl->Lock(mediaBuffer, &pbBuffer, &cbMaxLength, &cbCurrentLength);
|
||||
|
||||
if (FAILED(hr))
|
||||
goto error;
|
||||
|
||||
CopyMemory(pbBuffer, pSrcData, SrcSize);
|
||||
|
||||
hr = mediaBuffer->lpVtbl->SetCurrentLength(mediaBuffer, SrcSize);
|
||||
|
||||
if (FAILED(hr))
|
||||
goto error;
|
||||
|
||||
hr = mediaBuffer->lpVtbl->Unlock(mediaBuffer);
|
||||
|
||||
if (FAILED(hr))
|
||||
goto error;
|
||||
|
||||
hr = sys->MFCreateSample(&sys->sample);
|
||||
|
||||
if (FAILED(hr))
|
||||
goto error;
|
||||
|
||||
sys->sample->lpVtbl->AddBuffer(sys->sample, mediaBuffer);
|
||||
|
||||
if (FAILED(hr))
|
||||
goto error;
|
||||
|
||||
hr = sys->transform->lpVtbl->ProcessInput(sys->transform, 0, sys->sample, 0);
|
||||
|
||||
if (FAILED(hr))
|
||||
goto error;
|
||||
|
||||
//mediaBuffer->lpVtbl->Release(mediaBuffer);
|
||||
//sys->sample->lpVtbl->Release(sys->sample);
|
||||
|
||||
return 1;
|
||||
|
||||
error:
|
||||
fprintf(stderr, "mf_decompress error\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int mf_compress(H264_CONTEXT* h264, BYTE** ppDstData, UINT32* pDstSize)
|
||||
{
|
||||
H264_CONTEXT_MF* sys = (H264_CONTEXT_MF*) h264->pSystemData;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static HRESULT mf_find_output_type(H264_CONTEXT_MF* sys, const GUID* guid, IMFMediaType** ppMediaType)
|
||||
{
|
||||
DWORD idx = 0;
|
||||
|
@ -214,6 +162,264 @@ static HRESULT mf_find_output_type(H264_CONTEXT_MF* sys, const GUID* guid, IMFMe
|
|||
return hr;
|
||||
}
|
||||
|
||||
static int mf_decompress(H264_CONTEXT* h264, BYTE* pSrcData, UINT32 SrcSize)
|
||||
{
|
||||
HRESULT hr;
|
||||
BYTE* pbBuffer = NULL;
|
||||
DWORD cbMaxLength = 0;
|
||||
DWORD cbCurrentLength = 0;
|
||||
DWORD outputStatus = 0;
|
||||
IMFSample* inputSample = NULL;
|
||||
IMFSample* outputSample = NULL;
|
||||
IMFMediaBuffer* inputBuffer = NULL;
|
||||
IMFMediaBuffer* outputBuffer = NULL;
|
||||
MFT_OUTPUT_STREAM_INFO streamInfo;
|
||||
MFT_OUTPUT_DATA_BUFFER outputDataBuffer;
|
||||
H264_CONTEXT_MF* sys = (H264_CONTEXT_MF*) h264->pSystemData;
|
||||
|
||||
hr = sys->MFCreateMemoryBuffer(SrcSize, &inputBuffer);
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
WLog_ERR(TAG, "MFCreateMemoryBuffer failure: 0x%04X", hr);
|
||||
goto error;
|
||||
}
|
||||
|
||||
hr = inputBuffer->lpVtbl->Lock(inputBuffer, &pbBuffer, &cbMaxLength, &cbCurrentLength);
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
WLog_ERR(TAG, "Lock failure: 0x%04X", hr);
|
||||
goto error;
|
||||
}
|
||||
|
||||
CopyMemory(pbBuffer, pSrcData, SrcSize);
|
||||
|
||||
hr = inputBuffer->lpVtbl->SetCurrentLength(inputBuffer, SrcSize);
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
WLog_ERR(TAG, "SetCurrentLength failure: 0x%04X", hr);
|
||||
goto error;
|
||||
}
|
||||
|
||||
hr = inputBuffer->lpVtbl->Unlock(inputBuffer);
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
WLog_ERR(TAG, "Unlock failure: 0x%04X", hr);
|
||||
goto error;
|
||||
}
|
||||
|
||||
hr = sys->MFCreateSample(&inputSample);
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
WLog_ERR(TAG, "MFCreateSample failure: 0x%04X", hr);
|
||||
goto error;
|
||||
}
|
||||
|
||||
inputSample->lpVtbl->AddBuffer(inputSample, inputBuffer);
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
WLog_ERR(TAG, "AddBuffer failure: 0x%04X", hr);
|
||||
goto error;
|
||||
}
|
||||
|
||||
hr = sys->transform->lpVtbl->ProcessInput(sys->transform, 0, inputSample, 0);
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
WLog_ERR(TAG, "ProcessInput failure: 0x%04X", hr);
|
||||
goto error;
|
||||
}
|
||||
|
||||
hr = sys->MFCreateSample(&outputSample);
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
WLog_ERR(TAG, "MFCreateSample failure: 0x%04X", hr);
|
||||
goto error;
|
||||
}
|
||||
|
||||
hr = sys->transform->lpVtbl->GetOutputStreamInfo(sys->transform, 0, &streamInfo);
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
WLog_ERR(TAG, "GetOutputStreamInfo failure: 0x%04X", hr);
|
||||
goto error;
|
||||
}
|
||||
|
||||
hr = sys->MFCreateMemoryBuffer(streamInfo.cbSize, &outputBuffer);
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
WLog_ERR(TAG, "MFCreateMemoryBuffer failure: 0x%04X", hr);
|
||||
goto error;
|
||||
}
|
||||
|
||||
outputSample->lpVtbl->AddBuffer(outputSample, outputBuffer);
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
WLog_ERR(TAG, "AddBuffer failure: 0x%04X", hr);
|
||||
goto error;
|
||||
}
|
||||
|
||||
outputDataBuffer.dwStreamID = 0;
|
||||
outputDataBuffer.dwStatus = 0;
|
||||
outputDataBuffer.pEvents = NULL;
|
||||
outputDataBuffer.pSample = outputSample;
|
||||
|
||||
hr = sys->transform->lpVtbl->ProcessOutput(sys->transform, 0, 1, &outputDataBuffer, &outputStatus);
|
||||
|
||||
if (hr == MF_E_TRANSFORM_STREAM_CHANGE)
|
||||
{
|
||||
BYTE* pYUVData;
|
||||
int offset = 0;
|
||||
UINT32 stride = 0;
|
||||
UINT64 frameSize = 0;
|
||||
IMFAttributes* attributes = NULL;
|
||||
|
||||
fprintf(stderr, "MF_E_TRANSFORM_STREAM_CHANGE\n");
|
||||
|
||||
hr = mf_find_output_type(sys, &MFVideoFormat_IYUV, &sys->outputType);
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
WLog_ERR(TAG, "mf_find_output_type failure: 0x%04X", hr);
|
||||
goto error;
|
||||
}
|
||||
|
||||
hr = sys->transform->lpVtbl->SetOutputType(sys->transform, 0, sys->outputType, 0);
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
WLog_ERR(TAG, "SetOutputType failure: 0x%04X", hr);
|
||||
goto error;
|
||||
}
|
||||
|
||||
hr = sys->outputType->lpVtbl->GetUINT64(sys->outputType, &MF_MT_FRAME_SIZE, &frameSize);
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
WLog_ERR(TAG, "GetUINT64(MF_MT_FRAME_SIZE) failure: 0x%04X", hr);
|
||||
goto error;
|
||||
}
|
||||
|
||||
sys->frameWidth = (UINT32) (frameSize >> 32);
|
||||
sys->frameHeight = (UINT32) frameSize;
|
||||
|
||||
hr = sys->outputType->lpVtbl->GetUINT32(sys->outputType, &MF_MT_DEFAULT_STRIDE, &stride);
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
WLog_ERR(TAG, "GetUINT32(MF_MT_DEFAULT_STRIDE) failure: 0x%04X", hr);
|
||||
goto error;
|
||||
}
|
||||
|
||||
fprintf(stderr, "frame width: %d height: %d stride: %d\n", sys->frameWidth, sys->frameHeight, stride);
|
||||
|
||||
h264->iStride[0] = stride;
|
||||
h264->iStride[1] = stride / 2;
|
||||
h264->iStride[2] = stride / 2;
|
||||
|
||||
pYUVData = (BYTE*) calloc(1, 2 * stride * sys->frameHeight);
|
||||
|
||||
h264->pYUVData[0] = &pYUVData[offset];
|
||||
pYUVData += h264->iStride[0] * sys->frameHeight;
|
||||
|
||||
h264->pYUVData[1] = &pYUVData[offset];
|
||||
pYUVData += h264->iStride[1] * (sys->frameHeight / 2);
|
||||
|
||||
h264->pYUVData[2] = &pYUVData[offset];
|
||||
pYUVData += h264->iStride[2] * (sys->frameHeight / 2);
|
||||
|
||||
h264->width = sys->frameWidth;
|
||||
h264->height = sys->frameHeight;
|
||||
}
|
||||
else if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT)
|
||||
{
|
||||
fprintf(stderr, "MF_E_TRANSFORM_NEED_MORE_INPUT\n");
|
||||
}
|
||||
else if (FAILED(hr))
|
||||
{
|
||||
WLog_ERR(TAG, "ProcessOutput failure: 0x%04X", hr);
|
||||
goto error;
|
||||
}
|
||||
else
|
||||
{
|
||||
int offset = 0;
|
||||
BYTE* buffer = NULL;
|
||||
DWORD bufferCount = 0;
|
||||
DWORD cbMaxLength = 0;
|
||||
DWORD cbCurrentLength = 0;
|
||||
|
||||
hr = outputSample->lpVtbl->GetBufferCount(outputSample, &bufferCount);
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
WLog_ERR(TAG, "GetBufferCount failure: 0x%04X", hr);
|
||||
goto error;
|
||||
}
|
||||
|
||||
hr = outputSample->lpVtbl->ConvertToContiguousBuffer(outputSample, &outputBuffer);
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
WLog_ERR(TAG, "ConvertToContiguousBuffer failure: 0x%04X", hr);
|
||||
goto error;
|
||||
}
|
||||
|
||||
hr = outputBuffer->lpVtbl->Lock(outputBuffer, &buffer, &cbMaxLength, &cbCurrentLength);
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
WLog_ERR(TAG, "Lock failure: 0x%04X", hr);
|
||||
goto error;
|
||||
}
|
||||
|
||||
CopyMemory(h264->pYUVData[0], &buffer[offset], h264->iStride[0] * sys->frameHeight);
|
||||
offset += h264->iStride[0] * sys->frameHeight;
|
||||
|
||||
CopyMemory(h264->pYUVData[1], &buffer[offset], h264->iStride[1] * (sys->frameHeight / 2));
|
||||
offset += h264->iStride[1] * (sys->frameHeight / 2);
|
||||
|
||||
CopyMemory(h264->pYUVData[2], &buffer[offset], h264->iStride[2] * (sys->frameHeight / 2));
|
||||
offset += h264->iStride[2] * (sys->frameHeight / 2);
|
||||
|
||||
hr = outputBuffer->lpVtbl->Unlock(outputBuffer);
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
WLog_ERR(TAG, "Unlock failure: 0x%04X", hr);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
inputSample->lpVtbl->DeleteAllItems(inputSample);
|
||||
inputSample->lpVtbl->Release(inputSample);
|
||||
inputBuffer->lpVtbl->Release(inputBuffer);
|
||||
|
||||
outputSample->lpVtbl->DeleteAllItems(outputSample);
|
||||
outputSample->lpVtbl->Release(outputSample);
|
||||
outputBuffer->lpVtbl->Release(outputBuffer);
|
||||
|
||||
return 1;
|
||||
|
||||
error:
|
||||
fprintf(stderr, "mf_decompress error\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int mf_compress(H264_CONTEXT* h264, BYTE** ppDstData, UINT32* pDstSize)
|
||||
{
|
||||
H264_CONTEXT_MF* sys = (H264_CONTEXT_MF*) h264->pSystemData;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void mf_uninit(H264_CONTEXT* h264)
|
||||
{
|
||||
H264_CONTEXT_MF* sys = (H264_CONTEXT_MF*) h264->pSystemData;
|
||||
|
@ -250,6 +456,10 @@ static void mf_uninit(H264_CONTEXT* h264)
|
|||
sys->mfplat = NULL;
|
||||
}
|
||||
|
||||
free(h264->pYUVData[0]);
|
||||
h264->pYUVData[0] = h264->pYUVData[1] = h264->pYUVData[2] = NULL;
|
||||
h264->iStride[0] = h264->iStride[1] = h264->iStride[2] = 0;
|
||||
|
||||
sys->MFShutdown();
|
||||
|
||||
free(sys);
|
||||
|
@ -265,7 +475,7 @@ static BOOL mf_init(H264_CONTEXT* h264)
|
|||
sys = (H264_CONTEXT_MF*) calloc(1, sizeof(H264_CONTEXT_MF));
|
||||
|
||||
if (!sys)
|
||||
goto EXCEPTION;
|
||||
goto error;
|
||||
|
||||
h264->pSystemData = (void*) sys;
|
||||
|
||||
|
@ -274,7 +484,7 @@ static BOOL mf_init(H264_CONTEXT* h264)
|
|||
sys->mfplat = LoadLibraryA("mfplat.dll");
|
||||
|
||||
if (!sys->mfplat)
|
||||
goto EXCEPTION;
|
||||
goto error;
|
||||
|
||||
sys->MFStartup = (pfnMFStartup) GetProcAddress(sys->mfplat, "MFStartup");
|
||||
sys->MFShutdown = (pfnMFShutdown) GetProcAddress(sys->mfplat, "MFShutdown");
|
||||
|
@ -283,9 +493,9 @@ static BOOL mf_init(H264_CONTEXT* h264)
|
|||
sys->MFCreateMediaType = (pfnMFCreateMediaType) GetProcAddress(sys->mfplat, "MFCreateMediaType");
|
||||
sys->MFCreateDXGIDeviceManager = (pfnMFCreateDXGIDeviceManager) GetProcAddress(sys->mfplat, "MFCreateDXGIDeviceManager");
|
||||
|
||||
if (!sys->MFStartup || !sys->MFShutdown || !sys->MFCreateSample ||
|
||||
!sys->MFCreateMemoryBuffer || !sys->MFCreateMediaType || !sys->MFCreateDXGIDeviceManager)
|
||||
goto EXCEPTION;
|
||||
if (!sys->MFStartup || !sys->MFShutdown || !sys->MFCreateSample || !sys->MFCreateMemoryBuffer ||
|
||||
!sys->MFCreateMediaType || !sys->MFCreateDXGIDeviceManager)
|
||||
goto error;
|
||||
|
||||
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
|
||||
|
||||
|
@ -300,17 +510,17 @@ static BOOL mf_init(H264_CONTEXT* h264)
|
|||
hr = sys->MFStartup(MF_VERSION, 0);
|
||||
|
||||
if (FAILED(hr))
|
||||
goto EXCEPTION;
|
||||
goto error;
|
||||
|
||||
hr = CoCreateInstance(&CLSID_CMSH264DecoderMFT, NULL, CLSCTX_INPROC_SERVER, &IID_IMFTransform, (void**) &sys->transform);
|
||||
|
||||
if (FAILED(hr))
|
||||
goto EXCEPTION;
|
||||
goto error;
|
||||
|
||||
hr = sys->transform->lpVtbl->QueryInterface(sys->transform, &IID_ICodecAPI, (void**) &sys->codecApi);
|
||||
|
||||
if (FAILED(hr))
|
||||
goto EXCEPTION;
|
||||
goto error;
|
||||
|
||||
var.vt = VT_UI4;
|
||||
var.ulVal = 1;
|
||||
|
@ -318,41 +528,41 @@ static BOOL mf_init(H264_CONTEXT* h264)
|
|||
hr = sys->codecApi->lpVtbl->SetValue(sys->codecApi, &CODECAPI_AVLowLatencyMode, &var);
|
||||
|
||||
if (FAILED(hr))
|
||||
goto EXCEPTION;
|
||||
goto error;
|
||||
|
||||
hr = sys->MFCreateMediaType(&sys->inputType);
|
||||
|
||||
if (FAILED(hr))
|
||||
goto EXCEPTION;
|
||||
goto error;
|
||||
|
||||
hr = sys->inputType->lpVtbl->SetGUID(sys->inputType, &MF_MT_MAJOR_TYPE, &MFMediaType_Video);
|
||||
|
||||
if (FAILED(hr))
|
||||
goto EXCEPTION;
|
||||
goto error;
|
||||
|
||||
hr = sys->inputType->lpVtbl->SetGUID(sys->inputType, &MF_MT_SUBTYPE, &MFVideoFormat_H264);
|
||||
|
||||
if (FAILED(hr))
|
||||
goto EXCEPTION;
|
||||
goto error;
|
||||
|
||||
hr = sys->transform->lpVtbl->SetInputType(sys->transform, 0, sys->inputType, 0);
|
||||
|
||||
if (FAILED(hr))
|
||||
goto EXCEPTION;
|
||||
goto error;
|
||||
|
||||
hr = mf_find_output_type(sys, &MFVideoFormat_IYUV, &sys->outputType);
|
||||
|
||||
if (FAILED(hr))
|
||||
goto EXCEPTION;
|
||||
goto error;
|
||||
|
||||
hr = sys->transform->lpVtbl->SetOutputType(sys->transform, 0, sys->outputType, 0);
|
||||
|
||||
if (FAILED(hr))
|
||||
goto EXCEPTION;
|
||||
goto error;
|
||||
}
|
||||
return TRUE;
|
||||
|
||||
EXCEPTION:
|
||||
error:
|
||||
fprintf(stderr, "mf_init failure\n");
|
||||
mf_uninit(h264);
|
||||
return FALSE;
|
||||
|
|
Loading…
Reference in New Issue