You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1680 lines
52 KiB
1680 lines
52 KiB
/*****************************************************************************
|
|
* mft.cpp : Media Foundation Transform audio/video decoder
|
|
*****************************************************************************
|
|
* Copyright (C) 2014 VLC authors and VideoLAN
|
|
*
|
|
* Author: Felix Abecassis <felix.abecassis@gmail.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU Lesser General Public License as published by
|
|
* the Free Software Foundation; either version 2.1 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
|
|
*****************************************************************************/
|
|
|
|
#include <winapifamily.h>
|
|
#undef WINAPI_FAMILY
|
|
#define WINAPI_FAMILY WINAPI_FAMILY_DESKTOP_APP
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
#include <cassert>
|
|
|
|
#include <vlc_common.h>
|
|
#include <vlc_plugin.h>
|
|
#include <vlc_codec.h>
|
|
#include <vlc_charset.h> /* FromWide */
|
|
extern "C" {
|
|
#include "hxxx_helper.h"
|
|
}
|
|
|
|
#include "../video_chroma/d3d11_fmt.h"
|
|
|
|
#include <initguid.h>
|
|
#include <mfapi.h>
|
|
#include <mftransform.h>
|
|
#include <mferror.h>
|
|
#include <mfobjects.h>
|
|
#include <codecapi.h>
|
|
|
|
|
|
#include <vlc_codecs.h>
|
|
|
|
#include <algorithm>
|
|
#include <atomic>
|
|
#include <new>
|
|
|
|
#include <wrl/client.h>
|
|
using Microsoft::WRL::ComPtr;
|
|
|
|
static int Open(vlc_object_t *);
|
|
static void Close(vlc_object_t *);
|
|
|
|
vlc_module_begin()
|
|
set_description(N_("Media Foundation Transform decoder"))
|
|
add_shortcut("mft")
|
|
set_capability("video decoder", 1)
|
|
set_callbacks(Open, Close)
|
|
set_subcategory(SUBCAT_INPUT_VCODEC)
|
|
|
|
add_submodule()
|
|
add_shortcut("mft")
|
|
set_capability("audio decoder", 1)
|
|
set_callbacks(Open, Close)
|
|
vlc_module_end()
|
|
|
|
typedef HRESULT (WINAPI *pf_MFCreateDXGIDeviceManager)(UINT *, IMFDXGIDeviceManager **);
|
|
|
|
class mft_dec_sys_t
|
|
{
|
|
public:
|
|
ComPtr<IMFTransform> mft;
|
|
|
|
~mft_dec_sys_t()
|
|
{
|
|
assert(!streamStarted);
|
|
}
|
|
|
|
// Direct3D
|
|
vlc_video_context *vctx_out = nullptr;
|
|
const d3d_format_t *cfg = nullptr;
|
|
HRESULT (WINAPI *fptr_MFCreateDXGIDeviceManager)(UINT *resetToken, IMFDXGIDeviceManager **ppDeviceManager) = nullptr;
|
|
UINT dxgi_token = 0;
|
|
ComPtr<IMFDXGIDeviceManager> dxgi_manager;
|
|
HANDLE d3d_handle = INVALID_HANDLE_VALUE;
|
|
|
|
// D3D11
|
|
ComPtr<ID3D11Texture2D> cached_tex;
|
|
ID3D11ShaderResourceView *cachedSRV[32][DXGI_MAX_SHADER_VIEW] = {{nullptr}};
|
|
|
|
/* For asynchronous MFT */
|
|
bool is_async = false;
|
|
ComPtr<IMFMediaEventGenerator> event_generator;
|
|
int pending_input_events = 0;
|
|
int pending_output_events = 0;
|
|
|
|
/* Input stream */
|
|
DWORD input_stream_id = 0;
|
|
ComPtr<IMFMediaType> input_type;
|
|
|
|
/* Output stream */
|
|
DWORD output_stream_id = 0;
|
|
ComPtr<IMFSample> output_sample;
|
|
|
|
/* H264 only. */
|
|
struct hxxx_helper hh = {};
|
|
bool b_xps_pushed = false; ///< (for xvcC) parameter sets pushed (SPS/PPS/VPS)
|
|
|
|
std::atomic<size_t> refcount{1};
|
|
|
|
|
|
void AddRef()
|
|
{
|
|
refcount++;
|
|
}
|
|
|
|
bool Release()
|
|
{
|
|
if (--refcount == 0)
|
|
{
|
|
DoRelease();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// Required for Async MFTs
|
|
HRESULT startStream()
|
|
{
|
|
assert(!streamStarted);
|
|
HRESULT hr = mft->ProcessMessage(MFT_MESSAGE_NOTIFY_START_OF_STREAM, (ULONG_PTR)0);
|
|
if (SUCCEEDED(hr))
|
|
streamStarted = true;
|
|
return hr;
|
|
}
|
|
/// Used for Async MFTs
|
|
HRESULT endStream()
|
|
{
|
|
assert(streamStarted);
|
|
HRESULT hr = mft->ProcessMessage(MFT_MESSAGE_NOTIFY_END_OF_STREAM, (ULONG_PTR)0);
|
|
if (SUCCEEDED(hr))
|
|
streamStarted = false;
|
|
return hr;
|
|
}
|
|
|
|
HRESULT flushStream()
|
|
{
|
|
HRESULT hr = mft->ProcessMessage(MFT_MESSAGE_COMMAND_FLUSH, 0);
|
|
if (SUCCEEDED(hr))
|
|
streamStarted = false;
|
|
return hr;
|
|
}
|
|
|
|
private:
|
|
|
|
void DoRelease()
|
|
{
|
|
if (output_sample.Get())
|
|
output_sample->RemoveAllBuffers();
|
|
|
|
if (mft.Get())
|
|
{
|
|
// mft->SetInputType(input_stream_id, nullptr, 0);
|
|
// mft->SetOutputType(output_stream_id, nullptr, 0);
|
|
|
|
if (vctx_out)
|
|
mft->ProcessMessage(MFT_MESSAGE_SET_D3D_MANAGER, (ULONG_PTR)0);
|
|
}
|
|
|
|
for (size_t i=0; i < ARRAY_SIZE(cachedSRV); i++)
|
|
{
|
|
for (size_t j=0; j < ARRAY_SIZE(cachedSRV[i]); j++)
|
|
{
|
|
if (cachedSRV[i][j] != nullptr)
|
|
cachedSRV[i][j]->Release();
|
|
}
|
|
}
|
|
|
|
if (vctx_out && dxgi_manager.Get())
|
|
{
|
|
if (d3d_handle != INVALID_HANDLE_VALUE)
|
|
dxgi_manager->CloseDeviceHandle(d3d_handle);
|
|
}
|
|
|
|
if (vctx_out)
|
|
vlc_video_context_Release(vctx_out);
|
|
|
|
delete this;
|
|
|
|
MFShutdown();
|
|
}
|
|
|
|
bool streamStarted = false;
|
|
};
|
|
|
|
struct mf_d3d11_pic_ctx
|
|
{
|
|
struct d3d11_pic_context ctx;
|
|
IMFMediaBuffer *out_media;
|
|
mft_dec_sys_t *mfdec;
|
|
};
|
|
#define MF_D3D11_PICCONTEXT_FROM_PICCTX(pic_ctx) \
|
|
container_of(pic_ctx, mf_d3d11_pic_ctx, ctx.s)
|
|
|
|
static const int pi_channels_maps[9] =
|
|
{
|
|
0,
|
|
AOUT_CHAN_CENTER,
|
|
AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT,
|
|
AOUT_CHAN_CENTER | AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT,
|
|
AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_REARLEFT
|
|
| AOUT_CHAN_REARRIGHT,
|
|
AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
|
|
| AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT,
|
|
AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
|
|
| AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT | AOUT_CHAN_LFE,
|
|
AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
|
|
| AOUT_CHAN_REARCENTER | AOUT_CHAN_MIDDLELEFT
|
|
| AOUT_CHAN_MIDDLERIGHT | AOUT_CHAN_LFE,
|
|
AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER | AOUT_CHAN_REARLEFT
|
|
| AOUT_CHAN_REARRIGHT | AOUT_CHAN_MIDDLELEFT | AOUT_CHAN_MIDDLERIGHT
|
|
| AOUT_CHAN_LFE,
|
|
};
|
|
|
|
/* Possibly missing from mingw headers */
|
|
#ifndef MF_E_NO_EVENTS_AVAILABLE
|
|
# define MF_E_NO_EVENTS_AVAILABLE _HRESULT_TYPEDEF_(0xC00D3E80L)
|
|
#endif
|
|
|
|
/*
|
|
* The MFTransformXXX values might not be defined in mingw headers,
|
|
* thus we use our own enum with the VLC prefix.
|
|
*/
|
|
enum
|
|
{
|
|
VLC_METransformUnknown = 600,
|
|
VLC_METransformNeedInput,
|
|
VLC_METransformHaveOutput,
|
|
VLC_METransformDrainComplete,
|
|
VLC_METransformMarker,
|
|
};
|
|
|
|
typedef struct
|
|
{
|
|
vlc_fourcc_t fourcc;
|
|
const GUID guid;
|
|
} pair_format_guid;
|
|
|
|
#if defined(__MINGW64_VERSION_MAJOR)
|
|
# if __MINGW64_VERSION_MAJOR < 10
|
|
// 8-bit luminance only
|
|
// Older versions of mingw-w64 lack this GUID, but it was added in mingw-w64
|
|
// git on 2021-07-11 (during __MINGW64_VERSION_MAJOR 10). Use a local
|
|
// redefinition of this GUID with a custom prefix, to let the same code build
|
|
// with both older and newer versions of mingw-w64 (and earlier git snapshots
|
|
// with __MINGW64_VERSION_MAJOR == 10).
|
|
DEFINE_MEDIATYPE_GUID (MFVideoFormat_L8, 50); // D3DFMT_L8
|
|
|
|
DEFINE_MEDIATYPE_GUID (MFVideoFormat_AV1, FCC('AV01'));
|
|
# endif // __MINGW64_VERSION_MAJOR >= 0
|
|
#endif // __MINGW64_VERSION_MAJOR
|
|
|
|
/*
|
|
* We need this table since the FOURCC used for GUID is not the same
|
|
* as the FOURCC used by VLC, for instance h264 vs H264.
|
|
*/
|
|
static const pair_format_guid video_format_table[] =
|
|
{
|
|
{ VLC_CODEC_H264, MFVideoFormat_H264 },
|
|
{ VLC_CODEC_MPGV, MFVideoFormat_MPEG2 },
|
|
{ VLC_CODEC_MP2V, MFVideoFormat_MPEG2 },
|
|
{ VLC_CODEC_MP1V, MFVideoFormat_MPG1 },
|
|
{ VLC_CODEC_MJPG, MFVideoFormat_MJPG },
|
|
{ VLC_CODEC_WMV1, MFVideoFormat_WMV1 },
|
|
{ VLC_CODEC_WMV2, MFVideoFormat_WMV2 },
|
|
{ VLC_CODEC_WMV3, MFVideoFormat_WMV3 },
|
|
{ VLC_CODEC_VC1, MFVideoFormat_WVC1 },
|
|
{ VLC_CODEC_AV1, MFVideoFormat_AV1 },
|
|
{ 0, GUID_NULL }
|
|
};
|
|
|
|
/*
|
|
* Table to map MF Transform raw 3D3 output formats to native VLC FourCC
|
|
*/
|
|
static const pair_format_guid d3d_format_table[] = {
|
|
{ VLC_CODEC_BGRX, MFVideoFormat_RGB32 },
|
|
{ VLC_CODEC_BGR24, MFVideoFormat_RGB24 },
|
|
{ VLC_CODEC_BGRA, MFVideoFormat_ARGB32 },
|
|
{ VLC_CODEC_GREY, MFVideoFormat_L8 },
|
|
{ 0, GUID_NULL }
|
|
};
|
|
|
|
/*
|
|
* We cannot use the FOURCC code for audio either since the
|
|
* WAVE_FORMAT value is used to create the GUID.
|
|
*/
|
|
static const pair_format_guid audio_format_table[] =
|
|
{
|
|
{ VLC_CODEC_MPGA, MFAudioFormat_MPEG },
|
|
{ VLC_CODEC_MP3, MFAudioFormat_MP3 },
|
|
{ VLC_CODEC_DTS, MFAudioFormat_DTS },
|
|
{ VLC_CODEC_MP4A, MFAudioFormat_AAC },
|
|
{ VLC_CODEC_WMA2, MFAudioFormat_WMAudioV8 },
|
|
{ VLC_CODEC_A52, MFAudioFormat_Dolby_AC3 },
|
|
{ 0, GUID_NULL }
|
|
};
|
|
|
|
static const GUID & FormatToGUID(const pair_format_guid table[], vlc_fourcc_t fourcc)
|
|
{
|
|
for (int i = 0; table[i].fourcc; ++i)
|
|
if (table[i].fourcc == fourcc)
|
|
return table[i].guid;
|
|
|
|
return GUID_NULL;
|
|
}
|
|
|
|
static vlc_fourcc_t GUIDToFormat(const pair_format_guid table[], const GUID & guid)
|
|
{
|
|
for (int i = 0; table[i].fourcc; ++i)
|
|
if (table[i].guid == guid)
|
|
return table[i].fourcc;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int SetInputType(decoder_t *p_dec, DWORD stream_id, const GUID & mSubtype, ComPtr<IMFMediaType> & result)
|
|
{
|
|
mft_dec_sys_t *p_sys = static_cast<mft_dec_sys_t*>(p_dec->p_sys);
|
|
HRESULT hr;
|
|
|
|
result.Reset();
|
|
|
|
ComPtr<IMFMediaType> input_media_type;
|
|
|
|
/* Search a suitable input type for the MFT. */
|
|
for (int i = 0;; ++i)
|
|
{
|
|
hr = p_sys->mft->GetInputAvailableType(stream_id, i, input_media_type.ReleaseAndGetAddressOf());
|
|
if (hr == MF_E_NO_MORE_TYPES)
|
|
goto error;
|
|
else if (hr == MF_E_TRANSFORM_TYPE_NOT_SET)
|
|
{
|
|
/* The output type must be set before setting the input type for this MFT. */
|
|
return VLC_SUCCESS;
|
|
}
|
|
else if (FAILED(hr))
|
|
goto error;
|
|
|
|
GUID subtype;
|
|
hr = input_media_type->GetGUID(MF_MT_SUBTYPE, &subtype);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
if (subtype == mSubtype)
|
|
break;
|
|
}
|
|
|
|
if (p_dec->fmt_in->i_cat == VIDEO_ES)
|
|
{
|
|
UINT64 width = p_dec->fmt_in->video.i_width;
|
|
UINT64 height = p_dec->fmt_in->video.i_height;
|
|
hr = MFSetAttributeSize(input_media_type.Get(), MF_MT_FRAME_SIZE, width, height);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
/* Some transforms like to know the frame rate and may reject the input type otherwise. */
|
|
UINT64 frame_ratio_num = p_dec->fmt_in->video.i_frame_rate;
|
|
UINT64 frame_ratio_dem = p_dec->fmt_in->video.i_frame_rate_base;
|
|
if(frame_ratio_num && frame_ratio_dem) {
|
|
hr = MFSetAttributeRatio(input_media_type.Get(), MF_MT_FRAME_RATE, frame_ratio_num, frame_ratio_dem);
|
|
if(FAILED(hr))
|
|
goto error;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = input_media_type->SetUINT32(MF_MT_ORIGINAL_WAVE_FORMAT_TAG, mSubtype.Data1);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
if (p_dec->fmt_in->audio.i_rate)
|
|
{
|
|
hr = input_media_type->SetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, p_dec->fmt_in->audio.i_rate);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
}
|
|
if (p_dec->fmt_in->audio.i_channels)
|
|
{
|
|
hr = input_media_type->SetUINT32(MF_MT_AUDIO_NUM_CHANNELS, p_dec->fmt_in->audio.i_channels);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
}
|
|
if (p_dec->fmt_in->audio.i_bitspersample)
|
|
{
|
|
hr = input_media_type->SetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, p_dec->fmt_in->audio.i_bitspersample);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
}
|
|
if (p_dec->fmt_in->audio.i_blockalign)
|
|
{
|
|
hr = input_media_type->SetUINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, p_dec->fmt_in->audio.i_blockalign);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
}
|
|
if (p_dec->fmt_in->i_bitrate)
|
|
{
|
|
hr = input_media_type->SetUINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, p_dec->fmt_in->i_bitrate / 8);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
if (p_dec->fmt_in->i_extra > 0)
|
|
{
|
|
UINT32 blob_size = 0;
|
|
hr = input_media_type->GetBlobSize(MF_MT_USER_DATA, &blob_size);
|
|
/*
|
|
* Do not overwrite existing user data in the input type, this
|
|
* can cause the MFT to reject the type.
|
|
*/
|
|
if (hr == MF_E_ATTRIBUTENOTFOUND)
|
|
{
|
|
hr = input_media_type->SetBlob(MF_MT_USER_DATA,
|
|
static_cast<const UINT8*>(p_dec->fmt_in->p_extra), p_dec->fmt_in->i_extra);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
hr = p_sys->mft->SetInputType(stream_id, input_media_type.Get(), 0);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
result.Swap(input_media_type);
|
|
|
|
return VLC_SUCCESS;
|
|
|
|
error:
|
|
msg_Err(p_dec, "Error in SetInputType(). (hr=0x%lX)", hr);
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
static int SetOutputType(decoder_t *p_dec, DWORD stream_id)
|
|
{
|
|
mft_dec_sys_t *p_sys = static_cast<mft_dec_sys_t*>(p_dec->p_sys);
|
|
HRESULT hr;
|
|
|
|
ComPtr<IMFMediaType> output_media_type;
|
|
|
|
/*
|
|
* Enumerate available output types. The list is ordered by
|
|
* preference thus we will use the first one unless YV12/I420 is
|
|
* available for video or float32 for audio.
|
|
*/
|
|
GUID subtype;
|
|
int output_type_index = -1;
|
|
for (int i = 0; output_type_index == -1; ++i)
|
|
{
|
|
hr = p_sys->mft->GetOutputAvailableType(stream_id, i, output_media_type.ReleaseAndGetAddressOf());
|
|
if (hr == MF_E_NO_MORE_TYPES)
|
|
{
|
|
/*
|
|
* It's not an error if we don't find the output type we were
|
|
* looking for, in this case we use the first available type.
|
|
*/
|
|
/* No output format found we prefer, just pick the first one preferred
|
|
* by the MFT */
|
|
output_type_index = 0;
|
|
break;
|
|
}
|
|
else if (hr == MF_E_TRANSFORM_TYPE_NOT_SET)
|
|
{
|
|
/* The input type must be set before setting the output type for this MFT. */
|
|
return VLC_SUCCESS;
|
|
}
|
|
else if (FAILED(hr))
|
|
goto error;
|
|
|
|
hr = output_media_type->GetGUID(MF_MT_SUBTYPE, &subtype);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
if (p_dec->fmt_in->i_cat == VIDEO_ES)
|
|
{
|
|
if (subtype == MFVideoFormat_NV12 || subtype == MFVideoFormat_YV12
|
|
|| subtype == MFVideoFormat_I420 || subtype == MFVideoFormat_IYUV)
|
|
output_type_index = i;
|
|
/* Transform might offer output in a D3DFMT proprietary FCC. If we can
|
|
* use it, fall back to it in case we do not find YV12 or I420 */
|
|
else if(output_type_index < 0 && GUIDToFormat(d3d_format_table, subtype) > 0)
|
|
output_type_index = i;
|
|
}
|
|
else
|
|
{
|
|
UINT32 bits_per_sample;
|
|
hr = output_media_type->GetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, &bits_per_sample);
|
|
if (FAILED(hr))
|
|
continue;
|
|
if (bits_per_sample == 32 && subtype == MFAudioFormat_Float)
|
|
output_type_index = i;
|
|
}
|
|
}
|
|
|
|
hr = p_sys->mft->GetOutputAvailableType(stream_id, output_type_index, output_media_type.ReleaseAndGetAddressOf());
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
hr = output_media_type->GetGUID(MF_MT_SUBTYPE, &subtype);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
hr = p_sys->mft->SetOutputType(stream_id, output_media_type.Get(), 0);
|
|
if (FAILED(hr))
|
|
{
|
|
msg_Err(p_dec, "Failed to set the output. (hr=0x%lX)", hr);
|
|
goto error;
|
|
}
|
|
|
|
if (p_dec->fmt_in->i_cat == VIDEO_ES)
|
|
{
|
|
video_format_Copy( &p_dec->fmt_out.video, &p_dec->fmt_in->video );
|
|
|
|
/* Transform might offer output in a D3DFMT proprietary FCC */
|
|
vlc_fourcc_t fcc = GUIDToFormat(d3d_format_table, subtype);
|
|
if(fcc) {
|
|
/* D3D formats are upside down */
|
|
p_dec->fmt_out.video.orientation = ORIENT_VFLIPPED;
|
|
} else {
|
|
if (subtype == MFVideoFormat_IYUV)
|
|
subtype = MFVideoFormat_I420;
|
|
fcc = vlc_fourcc_GetCodec(p_dec->fmt_in->i_cat, subtype.Data1);
|
|
}
|
|
|
|
p_dec->fmt_out.i_codec = fcc;
|
|
}
|
|
else
|
|
{
|
|
p_dec->fmt_out.audio = p_dec->fmt_in->audio;
|
|
|
|
UINT32 bitspersample = 0;
|
|
hr = output_media_type->GetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, &bitspersample);
|
|
if (SUCCEEDED(hr) && bitspersample)
|
|
p_dec->fmt_out.audio.i_bitspersample = bitspersample;
|
|
|
|
UINT32 channels = 0;
|
|
hr = output_media_type->GetUINT32(MF_MT_AUDIO_NUM_CHANNELS, &channels);
|
|
if (SUCCEEDED(hr) && channels)
|
|
p_dec->fmt_out.audio.i_channels = channels;
|
|
|
|
UINT32 rate = 0;
|
|
hr = output_media_type->GetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, &rate);
|
|
if (SUCCEEDED(hr) && rate)
|
|
p_dec->fmt_out.audio.i_rate = rate;
|
|
|
|
vlc_fourcc_t fourcc;
|
|
wf_tag_to_fourcc(subtype.Data1, &fourcc, NULL);
|
|
p_dec->fmt_out.i_codec = vlc_fourcc_GetCodecAudio(fourcc, p_dec->fmt_out.audio.i_bitspersample);
|
|
|
|
p_dec->fmt_out.audio.i_physical_channels = pi_channels_maps[p_dec->fmt_out.audio.i_channels];
|
|
}
|
|
|
|
return VLC_SUCCESS;
|
|
|
|
error:
|
|
msg_Err(p_dec, "Error in SetOutputType()");
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
static int AllocateInputSample(decoder_t *p_dec, DWORD stream_id, ComPtr<IMFSample> & result, DWORD size)
|
|
{
|
|
mft_dec_sys_t *p_sys = static_cast<mft_dec_sys_t*>(p_dec->p_sys);
|
|
HRESULT hr;
|
|
|
|
result.Reset();
|
|
|
|
ComPtr<IMFSample> input_sample;
|
|
ComPtr<IMFMediaBuffer> input_media_buffer;
|
|
DWORD allocation_size;
|
|
|
|
MFT_INPUT_STREAM_INFO input_info;
|
|
hr = p_sys->mft->GetInputStreamInfo(stream_id, &input_info);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
hr = MFCreateSample(&input_sample);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
allocation_size = std::max<DWORD>(input_info.cbSize, size);
|
|
hr = MFCreateMemoryBuffer(allocation_size, &input_media_buffer);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
hr = input_sample->AddBuffer(input_media_buffer.Get());
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
result.Swap(input_sample);
|
|
|
|
return VLC_SUCCESS;
|
|
|
|
error:
|
|
msg_Err(p_dec, "Error in AllocateInputSample()");
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
static int AllocateOutputSample(decoder_t *p_dec, DWORD stream_id, ComPtr<IMFSample> & result)
|
|
{
|
|
mft_dec_sys_t *p_sys = static_cast<mft_dec_sys_t*>(p_dec->p_sys);
|
|
HRESULT hr;
|
|
|
|
result.Reset();
|
|
|
|
ComPtr<IMFSample> output_sample;
|
|
|
|
MFT_OUTPUT_STREAM_INFO output_info;
|
|
ComPtr<IMFMediaBuffer> output_media_buffer;
|
|
DWORD allocation_size;
|
|
DWORD alignment;
|
|
|
|
hr = p_sys->mft->GetOutputStreamInfo(stream_id, &output_info);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
if (output_info.dwFlags & (MFT_OUTPUT_STREAM_PROVIDES_SAMPLES | MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES))
|
|
{
|
|
/* The MFT will provide an allocated sample. */
|
|
return VLC_SUCCESS;
|
|
}
|
|
|
|
if (p_dec->fmt_in->i_cat == VIDEO_ES)
|
|
{
|
|
const DWORD expected_flags =
|
|
MFT_OUTPUT_STREAM_WHOLE_SAMPLES
|
|
| MFT_OUTPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER
|
|
| MFT_OUTPUT_STREAM_FIXED_SAMPLE_SIZE;
|
|
if ((output_info.dwFlags & expected_flags) != expected_flags)
|
|
goto error;
|
|
}
|
|
|
|
hr = MFCreateSample(&output_sample);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
allocation_size = output_info.cbSize;
|
|
alignment = output_info.cbAlignment;
|
|
if (alignment > 0)
|
|
hr = MFCreateAlignedMemoryBuffer(allocation_size, alignment - 1, &output_media_buffer);
|
|
else
|
|
hr = MFCreateMemoryBuffer(allocation_size, &output_media_buffer);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
hr = output_sample->AddBuffer(output_media_buffer.Get());
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
result.Swap(output_sample);
|
|
|
|
return VLC_SUCCESS;
|
|
|
|
error:
|
|
msg_Err(p_dec, "Error in AllocateOutputSample()");
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
static int ProcessInputStream(decoder_t *p_dec, DWORD stream_id, block_t *p_block)
|
|
{
|
|
mft_dec_sys_t *p_sys = static_cast<mft_dec_sys_t*>(p_dec->p_sys);
|
|
HRESULT hr = S_OK;
|
|
ComPtr<IMFSample> input_sample;
|
|
|
|
block_t *p_xps_blocks = NULL;
|
|
DWORD alloc_size = p_block->i_buffer;
|
|
vlc_tick_t ts;
|
|
ComPtr<IMFMediaBuffer> input_media_buffer;
|
|
|
|
if (p_dec->fmt_in->i_codec == VLC_CODEC_H264)
|
|
{
|
|
/* in-place NAL to annex B conversion. */
|
|
p_block = hxxx_helper_process_block(&p_sys->hh, p_block);
|
|
|
|
if (p_sys->hh.i_input_nal_length_size && !p_sys->b_xps_pushed)
|
|
{
|
|
p_xps_blocks = hxxx_helper_get_extradata_block(&p_sys->hh);
|
|
if (p_xps_blocks)
|
|
{
|
|
size_t extrasize;
|
|
block_ChainProperties(p_xps_blocks, NULL, &extrasize, NULL);
|
|
alloc_size += extrasize;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (AllocateInputSample(p_dec, stream_id, input_sample, alloc_size))
|
|
goto error;
|
|
|
|
hr = input_sample->GetBufferByIndex(0, &input_media_buffer);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
BYTE *buffer_start;
|
|
hr = input_media_buffer->Lock(&buffer_start, NULL, NULL);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
if (p_xps_blocks) {
|
|
buffer_start += block_ChainExtract(p_xps_blocks, buffer_start, alloc_size);
|
|
p_sys->b_xps_pushed = true;
|
|
}
|
|
memcpy(buffer_start, p_block->p_buffer, p_block->i_buffer);
|
|
|
|
hr = input_media_buffer->Unlock();
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
hr = input_media_buffer->SetCurrentLength(p_block->i_buffer);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
ts = p_block->i_pts == VLC_TICK_INVALID ? p_block->i_dts : p_block->i_pts;
|
|
|
|
/* Convert from microseconds to 100 nanoseconds unit. */
|
|
hr = input_sample->SetSampleTime(MSFTIME_FROM_VLC_TICK(ts));
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
hr = p_sys->mft->ProcessInput(stream_id, input_sample.Get(), 0);
|
|
if (FAILED(hr))
|
|
{
|
|
msg_Dbg(p_dec, "Failed to process input stream %lu (error 0x%lX)", stream_id, hr);
|
|
goto error;
|
|
}
|
|
|
|
block_ChainRelease(p_xps_blocks);
|
|
|
|
return VLC_SUCCESS;
|
|
|
|
error:
|
|
msg_Err(p_dec, "Error in ProcessInputStream(). (hr=0x%lX)", hr);
|
|
block_ChainRelease(p_xps_blocks);
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
/* Copy a packed buffer (no padding) to a picture_t */
|
|
static void CopyPackedBufferToPicture(picture_t *p_pic, const uint8_t *p_src)
|
|
{
|
|
for (int i = 0; i < p_pic->i_planes; ++i)
|
|
{
|
|
uint8_t *p_dst = p_pic->p[i].p_pixels;
|
|
|
|
if (p_pic->p[i].i_visible_pitch == p_pic->p[i].i_pitch)
|
|
{
|
|
/* Plane is packed, only one memcpy is needed. */
|
|
uint32_t plane_size = p_pic->p[i].i_pitch * p_pic->p[i].i_visible_lines;
|
|
memcpy(p_dst, p_src, plane_size);
|
|
p_src += plane_size;
|
|
continue;
|
|
}
|
|
|
|
for (int i_line = 0; i_line < p_pic->p[i].i_visible_lines; i_line++)
|
|
{
|
|
memcpy(p_dst, p_src, p_pic->p[i].i_visible_pitch);
|
|
p_src += p_pic->p[i].i_visible_pitch;
|
|
p_dst += p_pic->p[i].i_pitch;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void d3d11mf_pic_context_destroy(picture_context_t *ctx)
|
|
{
|
|
mf_d3d11_pic_ctx *pic_ctx = MF_D3D11_PICCONTEXT_FROM_PICCTX(ctx);
|
|
mft_dec_sys_t *mfdec = pic_ctx->mfdec;
|
|
pic_ctx->out_media->Release();
|
|
static_assert(offsetof(mf_d3d11_pic_ctx, ctx.s) == 0, "Cast assumption failure");
|
|
d3d11_pic_context_destroy(ctx);
|
|
mfdec->Release();
|
|
}
|
|
|
|
static picture_context_t *d3d11mf_pic_context_copy(picture_context_t *ctx)
|
|
{
|
|
mf_d3d11_pic_ctx *src_ctx = MF_D3D11_PICCONTEXT_FROM_PICCTX(ctx);
|
|
mf_d3d11_pic_ctx *pic_ctx = static_cast<mf_d3d11_pic_ctx *>(malloc(sizeof(*pic_ctx)));
|
|
if (unlikely(pic_ctx==nullptr))
|
|
return nullptr;
|
|
*pic_ctx = *src_ctx;
|
|
vlc_video_context_Hold(pic_ctx->ctx.s.vctx);
|
|
pic_ctx->out_media->AddRef();
|
|
pic_ctx->mfdec->AddRef();
|
|
for (int i=0;i<DXGI_MAX_SHADER_VIEW; i++)
|
|
{
|
|
pic_ctx->ctx.picsys.resource[i] = src_ctx->ctx.picsys.resource[i];
|
|
pic_ctx->ctx.picsys.renderSrc[i] = src_ctx->ctx.picsys.renderSrc[i];
|
|
}
|
|
AcquireD3D11PictureSys(&pic_ctx->ctx.picsys);
|
|
return &pic_ctx->ctx.s;
|
|
}
|
|
|
|
static mf_d3d11_pic_ctx *CreatePicContext(ID3D11Texture2D *texture, UINT slice,
|
|
ComPtr<IMFMediaBuffer> &media_buffer,
|
|
mft_dec_sys_t *mfdec,
|
|
ID3D11ShaderResourceView *renderSrc[DXGI_MAX_SHADER_VIEW],
|
|
vlc_video_context *vctx)
|
|
{
|
|
mf_d3d11_pic_ctx *pic_ctx = static_cast<mf_d3d11_pic_ctx *>(calloc(1, sizeof(*pic_ctx)));
|
|
if (unlikely(pic_ctx==nullptr))
|
|
return nullptr;
|
|
|
|
media_buffer.CopyTo(&pic_ctx->out_media);
|
|
pic_ctx->mfdec = mfdec;
|
|
pic_ctx->mfdec->AddRef();
|
|
|
|
pic_ctx->ctx.s.copy = d3d11mf_pic_context_copy;
|
|
pic_ctx->ctx.s.destroy = d3d11mf_pic_context_destroy;
|
|
pic_ctx->ctx.s.vctx = vlc_video_context_Hold(vctx);
|
|
|
|
pic_ctx->ctx.picsys.slice_index = slice;
|
|
pic_ctx->ctx.picsys.sharedHandle = INVALID_HANDLE_VALUE;
|
|
for (int i=0;i<DXGI_MAX_SHADER_VIEW; i++)
|
|
{
|
|
pic_ctx->ctx.picsys.texture[i] = texture;
|
|
pic_ctx->ctx.picsys.renderSrc[i] = renderSrc ? renderSrc[i] : NULL;
|
|
}
|
|
AcquireD3D11PictureSys(&pic_ctx->ctx.picsys);
|
|
return pic_ctx;
|
|
}
|
|
|
|
static int ProcessOutputStream(decoder_t *p_dec, DWORD stream_id, bool & keep_reading)
|
|
{
|
|
mft_dec_sys_t *p_sys = static_cast<mft_dec_sys_t*>(p_dec->p_sys);
|
|
HRESULT hr;
|
|
|
|
DWORD output_status = 0;
|
|
MFT_OUTPUT_DATA_BUFFER output_buffer = { stream_id, p_sys->output_sample.Get(), 0, NULL };
|
|
hr = p_sys->mft->ProcessOutput(0, 1, &output_buffer, &output_status);
|
|
if (output_buffer.pEvents)
|
|
output_buffer.pEvents->Release();
|
|
|
|
keep_reading = false;
|
|
if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT)
|
|
return VLC_SUCCESS;
|
|
|
|
if (hr == MF_E_TRANSFORM_STREAM_CHANGE || hr == MF_E_TRANSFORM_TYPE_NOT_SET)
|
|
{
|
|
if (SetOutputType(p_dec, p_sys->output_stream_id))
|
|
return VLC_EGENERIC;
|
|
|
|
/* Reallocate output sample. */
|
|
if (AllocateOutputSample(p_dec, p_sys->output_stream_id, p_sys->output_sample))
|
|
return VLC_EGENERIC;
|
|
// there's an output ready, keep trying
|
|
keep_reading = hr == MF_E_TRANSFORM_STREAM_CHANGE;
|
|
return VLC_SUCCESS;
|
|
}
|
|
|
|
/* An error not listed above occurred */
|
|
if (FAILED(hr))
|
|
{
|
|
msg_Dbg(p_dec, "Failed to process output stream %lu (error 0x%lX)", stream_id, hr);
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
if (output_buffer.pSample == nullptr)
|
|
return VLC_SUCCESS;
|
|
|
|
LONGLONG sample_time;
|
|
hr = output_buffer.pSample->GetSampleTime(&sample_time);
|
|
if (FAILED(hr))
|
|
return VLC_EGENERIC;
|
|
/* Convert from 100 nanoseconds unit to vlc ticks. */
|
|
vlc_tick_t samp_time = VLC_TICK_FROM_MSFTIME(sample_time);
|
|
|
|
DWORD output_count = 0;
|
|
hr = output_buffer.pSample->GetBufferCount(&output_count);
|
|
if (unlikely(FAILED(hr)))
|
|
return VLC_EGENERIC;
|
|
|
|
ComPtr<IMFSample> output_sample = output_buffer.pSample;
|
|
|
|
for (DWORD buf_index = 0; buf_index < output_count; buf_index++)
|
|
{
|
|
picture_t *picture = NULL;
|
|
ComPtr<IMFMediaBuffer> output_media_buffer;
|
|
hr = output_sample->GetBufferByIndex(buf_index, &output_media_buffer);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
if (p_dec->fmt_in->i_cat == VIDEO_ES)
|
|
{
|
|
mf_d3d11_pic_ctx *pic_ctx = nullptr;
|
|
UINT sliceIndex = 0;
|
|
ComPtr<IMFDXGIBuffer> spDXGIBuffer;
|
|
hr = output_media_buffer.As(&spDXGIBuffer);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
ID3D11Texture2D *d3d11Res;
|
|
hr = spDXGIBuffer->GetResource(IID_PPV_ARGS(&d3d11Res));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
D3D11_TEXTURE2D_DESC desc;
|
|
d3d11Res->GetDesc(&desc);
|
|
|
|
hr = spDXGIBuffer->GetSubresourceIndex(&sliceIndex);
|
|
if (!p_sys->vctx_out)
|
|
{
|
|
vlc_decoder_device *dec_dev = decoder_GetDecoderDevice(p_dec);
|
|
d3d11_decoder_device_t *dev_sys = GetD3D11OpaqueDevice(dec_dev);
|
|
if (dev_sys != NULL)
|
|
{
|
|
p_sys->vctx_out = D3D11CreateVideoContext( dec_dev, desc.Format );
|
|
vlc_decoder_device_Release(dec_dev);
|
|
if (unlikely(p_sys->vctx_out == NULL))
|
|
{
|
|
msg_Err(p_dec, "failed to create a video context");
|
|
d3d11Res->Release();
|
|
return VLC_EGENERIC;
|
|
}
|
|
p_dec->fmt_out.video.i_width = desc.Width;
|
|
p_dec->fmt_out.video.i_height = desc.Height;
|
|
|
|
p_sys->cfg = D3D11_RenderFormat(desc.Format ,true);
|
|
|
|
p_dec->fmt_out.i_codec = p_sys->cfg->fourcc;
|
|
p_dec->fmt_out.video.i_chroma = p_sys->cfg->fourcc;
|
|
p_dec->fmt_out.video.i_rmask = 0;
|
|
p_dec->fmt_out.video.i_gmask = 0;
|
|
p_dec->fmt_out.video.i_bmask = 0;
|
|
|
|
// pre allocate all the SRV for that texture
|
|
for (size_t slice=0; slice < desc.ArraySize; slice++)
|
|
{
|
|
ID3D11Texture2D *tex[DXGI_MAX_SHADER_VIEW] = {
|
|
d3d11Res, d3d11Res, d3d11Res, d3d11Res
|
|
};
|
|
|
|
if (D3D11_AllocateResourceView(vlc_object_logger(p_dec), dev_sys->d3d_dev.d3ddevice, p_sys->cfg,
|
|
tex, slice, p_sys->cachedSRV[slice]) != VLC_SUCCESS)
|
|
{
|
|
d3d11Res->Release();
|
|
goto error;
|
|
}
|
|
}
|
|
p_sys->cached_tex = d3d11Res;
|
|
}
|
|
}
|
|
else if (desc.ArraySize == 1)
|
|
{
|
|
assert(sliceIndex == 0);
|
|
|
|
d3d11_decoder_device_t *dev_sys = GetD3D11OpaqueContext(p_sys->vctx_out);
|
|
|
|
ID3D11Texture2D *tex[DXGI_MAX_SHADER_VIEW] = {
|
|
d3d11Res, d3d11Res, d3d11Res, d3d11Res
|
|
};
|
|
|
|
for (size_t j=0; j < ARRAY_SIZE(p_sys->cachedSRV[sliceIndex]); j++)
|
|
{
|
|
if (p_sys->cachedSRV[sliceIndex][j] != nullptr)
|
|
p_sys->cachedSRV[sliceIndex][j]->Release();
|
|
}
|
|
|
|
if (D3D11_AllocateResourceView(vlc_object_logger(p_dec), dev_sys->d3d_dev.d3ddevice, p_sys->cfg,
|
|
tex, sliceIndex, p_sys->cachedSRV[sliceIndex]) != VLC_SUCCESS)
|
|
{
|
|
d3d11Res->Release();
|
|
goto error;
|
|
}
|
|
}
|
|
else if (p_sys->cached_tex.Get() != d3d11Res)
|
|
{
|
|
msg_Err(p_dec, "separate texture not supported");
|
|
d3d11Res->Release();
|
|
goto error;
|
|
}
|
|
|
|
pic_ctx = CreatePicContext(d3d11Res, sliceIndex, output_media_buffer, p_sys, p_sys->cachedSRV[sliceIndex], p_sys->vctx_out);
|
|
d3d11Res->Release();
|
|
|
|
if (unlikely(pic_ctx == nullptr))
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
if (decoder_UpdateVideoOutput(p_dec, p_sys->vctx_out))
|
|
{
|
|
if (pic_ctx)
|
|
d3d11mf_pic_context_destroy(&pic_ctx->ctx.s);
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
picture = decoder_NewPicture(p_dec);
|
|
if (!picture)
|
|
{
|
|
if (pic_ctx)
|
|
d3d11mf_pic_context_destroy(&pic_ctx->ctx.s);
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
UINT32 interlaced = FALSE;
|
|
hr = output_sample->GetUINT32(MFSampleExtension_Interlaced, &interlaced);
|
|
if (FAILED(hr))
|
|
picture->b_progressive = true;
|
|
else
|
|
picture->b_progressive = !interlaced;
|
|
|
|
picture->date = samp_time;
|
|
|
|
if (pic_ctx)
|
|
{
|
|
picture->context = &pic_ctx->ctx.s;
|
|
}
|
|
else
|
|
{
|
|
BYTE *buffer_start;
|
|
hr = output_media_buffer->Lock(&buffer_start, NULL, NULL);
|
|
if (FAILED(hr))
|
|
{
|
|
picture_Release(picture);
|
|
goto error;
|
|
}
|
|
|
|
CopyPackedBufferToPicture(picture, buffer_start);
|
|
|
|
hr = output_media_buffer->Unlock();
|
|
if (FAILED(hr))
|
|
{
|
|
picture_Release(picture);
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
decoder_QueueVideo(p_dec, picture);
|
|
}
|
|
else
|
|
{
|
|
block_t *aout_buffer = NULL;
|
|
if (decoder_UpdateAudioFormat(p_dec))
|
|
goto error;
|
|
if (p_dec->fmt_out.audio.i_bitspersample == 0 || p_dec->fmt_out.audio.i_channels == 0)
|
|
goto error;
|
|
|
|
DWORD total_length = 0;
|
|
hr = output_media_buffer->GetCurrentLength(&total_length);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
int samples = total_length / (p_dec->fmt_out.audio.i_bitspersample * p_dec->fmt_out.audio.i_channels / 8);
|
|
aout_buffer = decoder_NewAudioBuffer(p_dec, samples);
|
|
if (!aout_buffer)
|
|
return VLC_SUCCESS;
|
|
if (aout_buffer->i_buffer < total_length)
|
|
{
|
|
block_Release(aout_buffer);
|
|
goto error;
|
|
}
|
|
|
|
aout_buffer->i_pts = samp_time;
|
|
|
|
BYTE *buffer_start;
|
|
hr = output_media_buffer->Lock(&buffer_start, NULL, NULL);
|
|
if (FAILED(hr))
|
|
{
|
|
block_Release(aout_buffer);
|
|
goto error;
|
|
}
|
|
|
|
memcpy(aout_buffer->p_buffer, buffer_start, total_length);
|
|
|
|
hr = output_media_buffer->Unlock();
|
|
if (FAILED(hr))
|
|
{
|
|
block_Release(aout_buffer);
|
|
goto error;
|
|
}
|
|
|
|
decoder_QueueAudio(p_dec, aout_buffer);
|
|
}
|
|
|
|
if (p_sys->output_sample.Get())
|
|
{
|
|
/* Sample is not provided by the MFT: clear its content. */
|
|
hr = output_media_buffer->SetCurrentLength(0);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
if (p_sys->output_sample.Get() == nullptr)
|
|
{
|
|
/* Sample is provided by the MFT: decrease refcount. */
|
|
output_sample->Release();
|
|
}
|
|
|
|
keep_reading = true;
|
|
return VLC_SUCCESS;
|
|
|
|
error:
|
|
msg_Err(p_dec, "Error in ProcessOutputStream()");
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
static void Flush(decoder_t *p_dec)
|
|
{
|
|
mft_dec_sys_t *p_sys = static_cast<mft_dec_sys_t*>(p_dec->p_sys);
|
|
if (SUCCEEDED(p_sys->flushStream()))
|
|
p_sys->startStream();
|
|
}
|
|
|
|
static int DecodeSync(decoder_t *p_dec, block_t *p_block)
|
|
{
|
|
mft_dec_sys_t *p_sys = static_cast<mft_dec_sys_t*>(p_dec->p_sys);
|
|
|
|
if (p_block && p_block->i_flags & (BLOCK_FLAG_CORRUPTED))
|
|
{
|
|
block_Release(p_block);
|
|
return VLCDEC_SUCCESS;
|
|
}
|
|
|
|
if (p_block == NULL)
|
|
{
|
|
HRESULT hr;
|
|
hr = p_sys->mft->ProcessMessage(MFT_MESSAGE_COMMAND_DRAIN, 0);
|
|
if (FAILED(hr))
|
|
{
|
|
msg_Warn(p_dec, "draining failed (hr=0x%lX)", hr);
|
|
return VLC_EGENERIC;
|
|
}
|
|
}
|
|
|
|
/* Drain the output stream before sending the input packet. */
|
|
bool keep_reading;
|
|
int err;
|
|
do {
|
|
err = ProcessOutputStream(p_dec, p_sys->output_stream_id, keep_reading);
|
|
} while (err == VLC_SUCCESS && keep_reading);
|
|
if (err != VLC_SUCCESS)
|
|
goto error;
|
|
|
|
if (p_block != NULL )
|
|
{
|
|
if (ProcessInputStream(p_dec, p_sys->input_stream_id, p_block))
|
|
goto error;
|
|
block_Release(p_block);
|
|
}
|
|
|
|
return VLCDEC_SUCCESS;
|
|
|
|
error:
|
|
msg_Err(p_dec, "Error in DecodeSync()");
|
|
if (p_block)
|
|
block_Release(p_block);
|
|
return VLCDEC_SUCCESS;
|
|
}
|
|
|
|
static HRESULT DequeueMediaEvent(decoder_t *p_dec)
|
|
{
|
|
mft_dec_sys_t *p_sys = static_cast<mft_dec_sys_t*>(p_dec->p_sys);
|
|
HRESULT hr;
|
|
|
|
ComPtr<IMFMediaEvent> event;
|
|
hr = p_sys->event_generator->GetEvent(MF_EVENT_FLAG_NO_WAIT, &event);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
MediaEventType event_type;
|
|
hr = event->GetType(&event_type);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
if (event_type == VLC_METransformNeedInput)
|
|
p_sys->pending_input_events += 1;
|
|
else if (event_type == VLC_METransformHaveOutput)
|
|
p_sys->pending_output_events += 1;
|
|
else
|
|
msg_Err(p_dec, "Unsupported asynchronous event.");
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
static int DecodeAsync(decoder_t *p_dec, block_t *p_block)
|
|
{
|
|
mft_dec_sys_t *p_sys = static_cast<mft_dec_sys_t*>(p_dec->p_sys);
|
|
HRESULT hr;
|
|
|
|
if (!p_block) /* No Drain */
|
|
return VLCDEC_SUCCESS;
|
|
|
|
if (p_block->i_flags & (BLOCK_FLAG_CORRUPTED))
|
|
{
|
|
block_Release(p_block);
|
|
return VLCDEC_SUCCESS;
|
|
}
|
|
|
|
/* Dequeue all pending media events. */
|
|
while ((hr = DequeueMediaEvent(p_dec)) == S_OK)
|
|
continue;
|
|
if (hr != MF_E_NO_EVENTS_AVAILABLE && FAILED(hr))
|
|
goto error;
|
|
|
|
/* Drain the output stream of the MFT before sending the input packet. */
|
|
if (p_sys->pending_output_events > 0)
|
|
{
|
|
p_sys->pending_output_events -= 1;
|
|
bool keep_reading;
|
|
int err;
|
|
do {
|
|
err = ProcessOutputStream(p_dec, p_sys->output_stream_id, keep_reading);
|
|
} while (err == VLC_SUCCESS && keep_reading);
|
|
if (err != VLC_SUCCESS)
|
|
goto error;
|
|
}
|
|
|
|
/* Poll the MFT and return decoded frames until the input stream is ready. */
|
|
while (p_sys->pending_input_events == 0)
|
|
{
|
|
hr = DequeueMediaEvent(p_dec);
|
|
if (hr == MF_E_NO_EVENTS_AVAILABLE)
|
|
{
|
|
/* Sleep for 1 ms to avoid excessive polling. */
|
|
Sleep(1);
|
|
continue;
|
|
}
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
if (p_sys->pending_output_events > 0)
|
|
{
|
|
p_sys->pending_output_events -= 1;
|
|
bool keep_reading;
|
|
int err;
|
|
do {
|
|
err = ProcessOutputStream(p_dec, p_sys->output_stream_id, keep_reading);
|
|
} while (err == VLC_SUCCESS && keep_reading);
|
|
if (err != VLC_SUCCESS)
|
|
goto error;
|
|
break;
|
|
}
|
|
}
|
|
|
|
p_sys->pending_input_events -= 1;
|
|
if (ProcessInputStream(p_dec, p_sys->input_stream_id, p_block))
|
|
goto error;
|
|
|
|
block_Release(p_block);
|
|
|
|
return VLCDEC_SUCCESS;
|
|
|
|
error:
|
|
msg_Err(p_dec, "Error in DecodeAsync()");
|
|
block_Release(p_block);
|
|
return VLCDEC_SUCCESS;
|
|
}
|
|
|
|
static int EnableHardwareAcceleration(decoder_t *p_dec, ComPtr<IMFAttributes> & attributes)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
#if defined(STATIC_CODECAPI_AVDecVideoAcceleration_H264)
|
|
switch (p_dec->fmt_in->i_codec)
|
|
{
|
|
case VLC_CODEC_H264:
|
|
hr = attributes->SetUINT32(CODECAPI_AVDecVideoAcceleration_H264, TRUE);
|
|
break;
|
|
case VLC_CODEC_WMV1:
|
|
case VLC_CODEC_WMV2:
|
|
case VLC_CODEC_WMV3:
|
|
case VLC_CODEC_VC1:
|
|
hr = attributes->SetUINT32(CODECAPI_AVDecVideoAcceleration_VC1, TRUE);
|
|
break;
|
|
case VLC_CODEC_MPGV:
|
|
case VLC_CODEC_MP2V:
|
|
hr = attributes->SetUINT32(CODECAPI_AVDecVideoAcceleration_MPEG2, TRUE);
|
|
break;
|
|
default:
|
|
hr = S_OK;
|
|
break;
|
|
}
|
|
#else
|
|
VLC_UNUSED(p_dec);
|
|
VLC_UNUSED(attributes);
|
|
#endif // STATIC_CODECAPI_AVDecVideoAcceleration_H264
|
|
|
|
return SUCCEEDED(hr) ? VLC_SUCCESS : VLC_EGENERIC;
|
|
}
|
|
|
|
static void DestroyMFT(decoder_t *p_dec);
|
|
|
|
static int SetD3D11(decoder_t *p_dec, d3d11_device_t *d3d_dev)
|
|
{
|
|
if (!(d3d_dev->d3ddevice->GetCreationFlags() & D3D11_CREATE_DEVICE_VIDEO_SUPPORT))
|
|
{
|
|
msg_Warn(p_dec, "the provided D3D11 device doesn't support decoding");
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
mft_dec_sys_t *p_sys = static_cast<mft_dec_sys_t*>(p_dec->p_sys);
|
|
HRESULT hr;
|
|
hr = p_sys->fptr_MFCreateDXGIDeviceManager(&p_sys->dxgi_token, &p_sys->dxgi_manager);
|
|
if (FAILED(hr))
|
|
return VLC_EGENERIC;
|
|
|
|
hr = p_sys->dxgi_manager->ResetDevice(d3d_dev->d3ddevice, p_sys->dxgi_token);
|
|
if (FAILED(hr))
|
|
return VLC_EGENERIC;
|
|
|
|
hr = p_sys->dxgi_manager->OpenDeviceHandle(&p_sys->d3d_handle);
|
|
if (FAILED(hr))
|
|
return VLC_EGENERIC;
|
|
|
|
hr = p_sys->mft->ProcessMessage(MFT_MESSAGE_SET_D3D_MANAGER, (ULONG_PTR)p_sys->dxgi_manager.Get());
|
|
if (FAILED(hr))
|
|
return VLC_EGENERIC;
|
|
|
|
return VLC_SUCCESS;
|
|
}
|
|
|
|
static int InitializeMFT(decoder_t *p_dec, const GUID & mSubtype)
|
|
{
|
|
mft_dec_sys_t *p_sys = static_cast<mft_dec_sys_t*>(p_dec->p_sys);
|
|
HRESULT hr;
|
|
|
|
ComPtr<IMFAttributes> attributes;
|
|
hr = p_sys->mft->GetAttributes(&attributes);
|
|
if (hr != E_NOTIMPL && FAILED(hr))
|
|
goto error;
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
UINT32 is_async = FALSE;
|
|
hr = attributes->GetUINT32(MF_TRANSFORM_ASYNC, &is_async);
|
|
if (hr != MF_E_ATTRIBUTENOTFOUND && FAILED(hr))
|
|
goto error;
|
|
p_sys->is_async = is_async;
|
|
if (p_sys->is_async)
|
|
{
|
|
hr = attributes->SetUINT32(MF_TRANSFORM_ASYNC_UNLOCK, TRUE);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
hr = p_sys->mft.As(&p_sys->event_generator);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
DWORD input_streams_count;
|
|
DWORD output_streams_count;
|
|
hr = p_sys->mft->GetStreamCount(&input_streams_count, &output_streams_count);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
if (input_streams_count != 1 || output_streams_count != 1)
|
|
{
|
|
msg_Err(p_dec, "MFT decoder should have 1 input stream and 1 output stream.");
|
|
goto error;
|
|
}
|
|
|
|
hr = p_sys->mft->GetStreamIDs(1, &p_sys->input_stream_id, 1, &p_sys->output_stream_id);
|
|
if (hr == E_NOTIMPL)
|
|
{
|
|
/*
|
|
* This is not an error, it happens if:
|
|
* - there is a fixed number of streams.
|
|
* AND
|
|
* - streams are numbered consecutively from 0 to N-1.
|
|
*/
|
|
p_sys->input_stream_id = 0;
|
|
p_sys->output_stream_id = 0;
|
|
}
|
|
else if (FAILED(hr))
|
|
goto error;
|
|
|
|
if (SetInputType(p_dec, p_sys->input_stream_id, mSubtype, p_sys->input_type))
|
|
goto error;
|
|
|
|
if (attributes.Get() && p_dec->fmt_in->i_cat == VIDEO_ES)
|
|
{
|
|
EnableHardwareAcceleration(p_dec, attributes);
|
|
if (p_sys->fptr_MFCreateDXGIDeviceManager)
|
|
{
|
|
vlc_decoder_device *dec_dev = decoder_GetDecoderDevice(p_dec);
|
|
if (dec_dev != nullptr)
|
|
{
|
|
d3d11_decoder_device_t *devsys11 = GetD3D11OpaqueDevice(dec_dev);
|
|
if (devsys11 != nullptr)
|
|
{
|
|
UINT32 can_d3d11;
|
|
hr = attributes->GetUINT32(MF_SA_D3D11_AWARE, &can_d3d11);
|
|
if (SUCCEEDED(hr) && can_d3d11)
|
|
{
|
|
if (SetD3D11(p_dec, &devsys11->d3d_dev) == VLC_SUCCESS)
|
|
{
|
|
IMFAttributes *outputAttr = NULL;
|
|
hr = p_sys->mft->GetOutputStreamAttributes(p_sys->output_stream_id, &outputAttr);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = outputAttr->SetUINT32(MF_SA_D3D11_BINDFLAGS, D3D11_BIND_SHADER_RESOURCE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
vlc_decoder_device_Release(dec_dev);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (SetOutputType(p_dec, p_sys->output_stream_id))
|
|
goto error;
|
|
|
|
/*
|
|
* The input type was not set by the previous call to
|
|
* SetInputType, try again after setting the output type.
|
|
*/
|
|
if (p_sys->input_type.Get() == nullptr)
|
|
if (SetInputType(p_dec, p_sys->input_stream_id, mSubtype, p_sys->input_type) || p_sys->input_type.Get() == nullptr)
|
|
goto error;
|
|
|
|
/* This event is required for asynchronous MFTs, optional otherwise. */
|
|
hr = p_sys->startStream();
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
if (attributes.Get() && p_dec->fmt_in->i_codec == VLC_CODEC_H264)
|
|
{
|
|
/* It's not an error if the following call fails. */
|
|
#if (_WIN32_WINNT < _WIN32_WINNT_WIN8)
|
|
attributes->SetUINT32(CODECAPI_AVLowLatencyMode, TRUE);
|
|
#else
|
|
attributes->SetUINT32(MF_LOW_LATENCY, TRUE);
|
|
#endif
|
|
|
|
hxxx_helper_init(&p_sys->hh, VLC_OBJECT(p_dec), p_dec->fmt_in->i_codec, 0, 0);
|
|
hxxx_helper_set_extra(&p_sys->hh, p_dec->fmt_in->p_extra, p_dec->fmt_in->i_extra);
|
|
}
|
|
return VLC_SUCCESS;
|
|
|
|
error:
|
|
msg_Err(p_dec, "Error in InitializeMFT()");
|
|
DestroyMFT(p_dec);
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
static void DestroyMFT(decoder_t *p_dec)
|
|
{
|
|
mft_dec_sys_t *p_sys = static_cast<mft_dec_sys_t*>(p_dec->p_sys);
|
|
|
|
if (p_sys->mft.Get())
|
|
{
|
|
p_sys->endStream();
|
|
|
|
if (p_sys->output_sample.Get() == nullptr)
|
|
{
|
|
// the MFT produces the output and may still have some left, we need to drain them
|
|
HRESULT hr;
|
|
hr = p_sys->mft->ProcessMessage(MFT_MESSAGE_COMMAND_DRAIN, 0);
|
|
if (FAILED(hr))
|
|
{
|
|
msg_Warn(p_dec, "exit draining failed (hr=0x%lX)", hr);
|
|
}
|
|
else
|
|
{
|
|
for (;;)
|
|
{
|
|
DWORD output_status = 0;
|
|
MFT_OUTPUT_DATA_BUFFER output_buffer = { p_sys->output_stream_id, p_sys->output_sample.Get(), 0, NULL };
|
|
hr = p_sys->mft->ProcessOutput(0, 1, &output_buffer, &output_status);
|
|
if (output_buffer.pEvents)
|
|
output_buffer.pEvents->Release();
|
|
if (output_buffer.pSample)
|
|
{
|
|
output_buffer.pSample->Release();
|
|
}
|
|
if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT)
|
|
break;
|
|
if (hr == MF_E_TRANSFORM_TYPE_NOT_SET)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// make sure don't have any input pending
|
|
p_sys->flushStream();
|
|
}
|
|
|
|
if (p_dec->fmt_in->i_codec == VLC_CODEC_H264)
|
|
hxxx_helper_clean(&p_sys->hh);
|
|
}
|
|
|
|
static int ListTransforms(decoder_t *p_dec, GUID category, const char *type)
|
|
{
|
|
HRESULT hr;
|
|
|
|
UINT32 flags = MFT_ENUM_FLAG_SORTANDFILTER | MFT_ENUM_FLAG_LOCALMFT
|
|
| MFT_ENUM_FLAG_SYNCMFT | MFT_ENUM_FLAG_ASYNCMFT
|
|
| MFT_ENUM_FLAG_HARDWARE;
|
|
IMFActivate **activate_objects = NULL;
|
|
UINT32 count = 0;
|
|
hr = MFTEnumEx(category, flags, nullptr, nullptr, &activate_objects, &count);
|
|
msg_Dbg(p_dec, "Listing %u %s%s", count, type, (count?"s":""));
|
|
if (FAILED(hr))
|
|
return VLC_EGENERIC;
|
|
|
|
for (UINT32 o=0; o<count; o++)
|
|
{
|
|
WCHAR Name[256];
|
|
hr = activate_objects[o]->GetString(MFT_FRIENDLY_NAME_Attribute, Name, ARRAY_SIZE(Name), nullptr);
|
|
if (FAILED(hr))
|
|
wcscpy(Name,L"<unknown>");
|
|
char *name = FromWide(Name);
|
|
|
|
#ifndef NDEBUG
|
|
ComPtr<IMFTransform> mft;
|
|
hr = activate_objects[o]->ActivateObject(IID_PPV_ARGS(mft.GetAddressOf()));
|
|
msg_Dbg(p_dec, "%s '%s' is%s available", type, name, (FAILED(hr)?" not":""));
|
|
mft.Reset();
|
|
#else
|
|
msg_Dbg(p_dec, "found %s '%s'", type, name);
|
|
#endif
|
|
free(name);
|
|
activate_objects[o]->ShutdownObject();
|
|
}
|
|
|
|
return VLC_SUCCESS;
|
|
}
|
|
|
|
static int FindMFT(decoder_t *p_dec)
|
|
{
|
|
mft_dec_sys_t *p_sys = static_cast<mft_dec_sys_t*>(p_dec->p_sys);
|
|
HRESULT hr;
|
|
|
|
/* Try to create a MFT using MFTEnumEx. */
|
|
GUID category;
|
|
MFT_REGISTER_TYPE_INFO input_type;
|
|
if (p_dec->fmt_in->i_cat == VIDEO_ES)
|
|
{
|
|
category = MFT_CATEGORY_VIDEO_DECODER;
|
|
input_type.guidMajorType = MFMediaType_Video;
|
|
input_type.guidSubtype = FormatToGUID(video_format_table, p_dec->fmt_in->i_codec);
|
|
if(input_type.guidSubtype == GUID_NULL) {
|
|
/* Codec is not well known. Construct a MF transform subtype from the fourcc */
|
|
input_type.guidSubtype = MFVideoFormat_Base;
|
|
input_type.guidSubtype.Data1 = p_dec->fmt_in->i_codec;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
category = MFT_CATEGORY_AUDIO_DECODER;
|
|
input_type.guidMajorType = MFMediaType_Audio;
|
|
input_type.guidSubtype = FormatToGUID(audio_format_table, p_dec->fmt_in->i_codec);
|
|
}
|
|
if (input_type.guidSubtype == GUID_NULL)
|
|
return VLC_EGENERIC;
|
|
|
|
if (p_dec->fmt_in->i_cat == VIDEO_ES)
|
|
ListTransforms(p_dec, MFT_CATEGORY_VIDEO_DECODER, "video decoder");
|
|
else
|
|
ListTransforms(p_dec, MFT_CATEGORY_VIDEO_DECODER, "audio decoder");
|
|
|
|
UINT32 flags = MFT_ENUM_FLAG_SORTANDFILTER | MFT_ENUM_FLAG_LOCALMFT
|
|
| MFT_ENUM_FLAG_SYNCMFT | MFT_ENUM_FLAG_ASYNCMFT
|
|
| MFT_ENUM_FLAG_HARDWARE;
|
|
IMFActivate **activate_objects = NULL;
|
|
UINT32 activate_objects_count = 0;
|
|
hr = MFTEnumEx(category, flags, &input_type, NULL, &activate_objects, &activate_objects_count);
|
|
if (FAILED(hr))
|
|
return VLC_EGENERIC;
|
|
|
|
msg_Dbg(p_dec, "Found %d available MFT module(s) for %4.4s", activate_objects_count, (const char*)&p_dec->fmt_in->i_codec);
|
|
if (activate_objects_count == 0)
|
|
return VLC_EGENERIC;
|
|
|
|
for (UINT32 i = 0; i < activate_objects_count; ++i)
|
|
{
|
|
hr = activate_objects[i]->ActivateObject(IID_PPV_ARGS(p_sys->mft.ReleaseAndGetAddressOf()));
|
|
activate_objects[i]->Release();
|
|
if (FAILED(hr))
|
|
continue;
|
|
|
|
if (InitializeMFT(p_dec, input_type.guidSubtype) == VLC_SUCCESS)
|
|
{
|
|
for (++i; i < activate_objects_count; ++i)
|
|
activate_objects[i]->Release();
|
|
CoTaskMemFree(activate_objects);
|
|
return VLC_SUCCESS;
|
|
}
|
|
}
|
|
CoTaskMemFree(activate_objects);
|
|
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
static int LoadMFTLibrary(decoder_t *p_dec)
|
|
{
|
|
mft_dec_sys_t *p_sys = static_cast<mft_dec_sys_t*>(p_dec->p_sys);
|
|
|
|
HRESULT hr = MFStartup(MF_VERSION, MFSTARTUP_NOSOCKET);
|
|
if (FAILED(hr))
|
|
return VLC_EGENERIC;
|
|
|
|
if (p_dec->fmt_in->i_cat != VIDEO_ES) // nothing left to do
|
|
return VLC_SUCCESS;
|
|
|
|
if (p_dec->fmt_in->video.i_width == 0) // don't consume D3D resource for a fake decoder
|
|
{
|
|
msg_Dbg(p_dec, "skip D3D handling for dummy decoder");
|
|
return VLC_SUCCESS;
|
|
}
|
|
|
|
#if _WIN32_WINNT < _WIN32_WINNT_WIN8
|
|
HINSTANCE mfplat_dll = LoadLibrary(TEXT("mfplat.dll"));
|
|
if (mfplat_dll)
|
|
{
|
|
p_sys->fptr_MFCreateDXGIDeviceManager = reinterpret_cast<pf_MFCreateDXGIDeviceManager>(
|
|
GetProcAddress(mfplat_dll, "MFCreateDXGIDeviceManager") );
|
|
// we still have the DLL automatically loaded after this
|
|
FreeLibrary(mfplat_dll);
|
|
}
|
|
#else // Win8+
|
|
p_sys->fptr_MFCreateDXGIDeviceManager = &MFCreateDXGIDeviceManager;
|
|
#endif // Win8+
|
|
|
|
return VLC_SUCCESS;
|
|
}
|
|
|
|
static int Open(vlc_object_t *p_this)
|
|
{
|
|
decoder_t *p_dec = (decoder_t *)p_this;
|
|
mft_dec_sys_t *p_sys;
|
|
|
|
p_sys = new (std::nothrow) mft_dec_sys_t();
|
|
if (unlikely(p_sys == nullptr))
|
|
return VLC_ENOMEM;
|
|
p_dec->p_sys = p_sys;
|
|
|
|
if( FAILED(CoInitializeEx(NULL, COINIT_MULTITHREADED)) )
|
|
{
|
|
delete p_sys;
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
if (LoadMFTLibrary(p_dec))
|
|
{
|
|
msg_Err(p_dec, "Failed to load MFT library.");
|
|
goto error;
|
|
}
|
|
|
|
if (FindMFT(p_dec))
|
|
{
|
|
msg_Err(p_dec, "Could not find suitable MFT decoder for %4.4s", (char*)&p_dec->fmt_in->i_codec);
|
|
goto error;
|
|
}
|
|
|
|
/* Only one output sample is needed, we can allocate one and reuse it. */
|
|
if (AllocateOutputSample(p_dec, p_sys->output_stream_id, p_sys->output_sample))
|
|
goto error;
|
|
|
|
p_dec->pf_decode = p_sys->is_async ? DecodeAsync : DecodeSync;
|
|
p_dec->pf_flush = p_sys->is_async ? NULL : Flush;
|
|
|
|
return VLC_SUCCESS;
|
|
|
|
error:
|
|
Close(p_this);
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
static void Close(vlc_object_t *p_this)
|
|
{
|
|
decoder_t *p_dec = (decoder_t *)p_this;
|
|
mft_dec_sys_t *p_sys = static_cast<mft_dec_sys_t*>(p_dec->p_sys);
|
|
|
|
DestroyMFT(p_dec);
|
|
|
|
p_sys->Release();
|
|
|
|
CoUninitialize();
|
|
}
|
|
|