mirror of https://github.com/FFmpeg/FFmpeg.git
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.
649 lines
20 KiB
649 lines
20 KiB
/* |
|
* This file is part of FFmpeg. |
|
* |
|
* FFmpeg 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. |
|
* |
|
* FFmpeg 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 FFmpeg; if not, write to the Free Software |
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
|
*/ |
|
|
|
#define COBJMACROS |
|
#if !defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0602 |
|
#undef _WIN32_WINNT |
|
#define _WIN32_WINNT 0x0602 |
|
#endif |
|
|
|
#include "mf_utils.h" |
|
#include "libavutil/pixdesc.h" |
|
|
|
HRESULT ff_MFGetAttributeSize(IMFAttributes *pattr, REFGUID guid, |
|
UINT32 *pw, UINT32 *ph) |
|
{ |
|
UINT64 t; |
|
HRESULT hr = IMFAttributes_GetUINT64(pattr, guid, &t); |
|
if (!FAILED(hr)) { |
|
*pw = t >> 32; |
|
*ph = (UINT32)t; |
|
} |
|
return hr; |
|
} |
|
|
|
HRESULT ff_MFSetAttributeSize(IMFAttributes *pattr, REFGUID guid, |
|
UINT32 uw, UINT32 uh) |
|
{ |
|
UINT64 t = (((UINT64)uw) << 32) | uh; |
|
return IMFAttributes_SetUINT64(pattr, guid, t); |
|
} |
|
|
|
#define ff_MFSetAttributeRatio ff_MFSetAttributeSize |
|
#define ff_MFGetAttributeRatio ff_MFGetAttributeSize |
|
|
|
char *ff_hr_str_buf(char *buf, size_t size, HRESULT hr) |
|
{ |
|
#define HR(x) case x: return (char *) # x; |
|
switch (hr) { |
|
HR(S_OK) |
|
HR(E_UNEXPECTED) |
|
HR(MF_E_INVALIDMEDIATYPE) |
|
HR(MF_E_INVALIDSTREAMNUMBER) |
|
HR(MF_E_INVALIDTYPE) |
|
HR(MF_E_TRANSFORM_CANNOT_CHANGE_MEDIATYPE_WHILE_PROCESSING) |
|
HR(MF_E_TRANSFORM_TYPE_NOT_SET) |
|
HR(MF_E_UNSUPPORTED_D3D_TYPE) |
|
HR(MF_E_TRANSFORM_NEED_MORE_INPUT) |
|
HR(MF_E_TRANSFORM_STREAM_CHANGE) |
|
HR(MF_E_NOTACCEPTING) |
|
HR(MF_E_NO_SAMPLE_TIMESTAMP) |
|
HR(MF_E_NO_SAMPLE_DURATION) |
|
#undef HR |
|
} |
|
snprintf(buf, size, "%x", (unsigned)hr); |
|
return buf; |
|
} |
|
|
|
// If fill_data!=NULL, initialize the buffer and set the length. (This is a |
|
// subtle but important difference: some decoders want CurrentLength==0 on |
|
// provided output buffers.) |
|
IMFSample *ff_create_memory_sample(MFFunctions *f,void *fill_data, size_t size, |
|
size_t align) |
|
{ |
|
HRESULT hr; |
|
IMFSample *sample; |
|
IMFMediaBuffer *buffer; |
|
|
|
hr = f->MFCreateSample(&sample); |
|
if (FAILED(hr)) |
|
return NULL; |
|
|
|
align = FFMAX(align, 16); // 16 is "recommended", even if not required |
|
|
|
hr = f->MFCreateAlignedMemoryBuffer(size, align - 1, &buffer); |
|
if (FAILED(hr)) |
|
return NULL; |
|
|
|
if (fill_data) { |
|
BYTE *tmp; |
|
|
|
hr = IMFMediaBuffer_Lock(buffer, &tmp, NULL, NULL); |
|
if (FAILED(hr)) { |
|
IMFMediaBuffer_Release(buffer); |
|
IMFSample_Release(sample); |
|
return NULL; |
|
} |
|
memcpy(tmp, fill_data, size); |
|
|
|
IMFMediaBuffer_SetCurrentLength(buffer, size); |
|
IMFMediaBuffer_Unlock(buffer); |
|
} |
|
|
|
IMFSample_AddBuffer(sample, buffer); |
|
IMFMediaBuffer_Release(buffer); |
|
|
|
return sample; |
|
} |
|
|
|
enum AVSampleFormat ff_media_type_to_sample_fmt(IMFAttributes *type) |
|
{ |
|
HRESULT hr; |
|
UINT32 bits; |
|
GUID subtype; |
|
|
|
hr = IMFAttributes_GetUINT32(type, &MF_MT_AUDIO_BITS_PER_SAMPLE, &bits); |
|
if (FAILED(hr)) |
|
return AV_SAMPLE_FMT_NONE; |
|
|
|
hr = IMFAttributes_GetGUID(type, &MF_MT_SUBTYPE, &subtype); |
|
if (FAILED(hr)) |
|
return AV_SAMPLE_FMT_NONE; |
|
|
|
if (IsEqualGUID(&subtype, &MFAudioFormat_PCM)) { |
|
switch (bits) { |
|
case 8: return AV_SAMPLE_FMT_U8; |
|
case 16: return AV_SAMPLE_FMT_S16; |
|
case 32: return AV_SAMPLE_FMT_S32; |
|
} |
|
} else if (IsEqualGUID(&subtype, &MFAudioFormat_Float)) { |
|
switch (bits) { |
|
case 32: return AV_SAMPLE_FMT_FLT; |
|
case 64: return AV_SAMPLE_FMT_DBL; |
|
} |
|
} |
|
|
|
return AV_SAMPLE_FMT_NONE; |
|
} |
|
|
|
struct mf_pix_fmt_entry { |
|
const GUID *guid; |
|
enum AVPixelFormat pix_fmt; |
|
}; |
|
|
|
static const struct mf_pix_fmt_entry mf_pix_fmts[] = { |
|
{&MFVideoFormat_IYUV, AV_PIX_FMT_YUV420P}, |
|
{&MFVideoFormat_I420, AV_PIX_FMT_YUV420P}, |
|
{&MFVideoFormat_NV12, AV_PIX_FMT_NV12}, |
|
{&MFVideoFormat_P010, AV_PIX_FMT_P010}, |
|
{&MFVideoFormat_P016, AV_PIX_FMT_P010}, // not equal, but compatible |
|
{&MFVideoFormat_YUY2, AV_PIX_FMT_YUYV422}, |
|
}; |
|
|
|
enum AVPixelFormat ff_media_type_to_pix_fmt(IMFAttributes *type) |
|
{ |
|
HRESULT hr; |
|
GUID subtype; |
|
int i; |
|
|
|
hr = IMFAttributes_GetGUID(type, &MF_MT_SUBTYPE, &subtype); |
|
if (FAILED(hr)) |
|
return AV_PIX_FMT_NONE; |
|
|
|
for (i = 0; i < FF_ARRAY_ELEMS(mf_pix_fmts); i++) { |
|
if (IsEqualGUID(&subtype, mf_pix_fmts[i].guid)) |
|
return mf_pix_fmts[i].pix_fmt; |
|
} |
|
|
|
return AV_PIX_FMT_NONE; |
|
} |
|
|
|
const GUID *ff_pix_fmt_to_guid(enum AVPixelFormat pix_fmt) |
|
{ |
|
int i; |
|
|
|
for (i = 0; i < FF_ARRAY_ELEMS(mf_pix_fmts); i++) { |
|
if (mf_pix_fmts[i].pix_fmt == pix_fmt) |
|
return mf_pix_fmts[i].guid; |
|
} |
|
|
|
return NULL; |
|
} |
|
|
|
// If this GUID is of the form XXXXXXXX-0000-0010-8000-00AA00389B71, then |
|
// extract the XXXXXXXX prefix as FourCC (oh the pain). |
|
int ff_fourcc_from_guid(const GUID *guid, uint32_t *out_fourcc) |
|
{ |
|
if (guid->Data2 == 0 && guid->Data3 == 0x0010 && |
|
guid->Data4[0] == 0x80 && |
|
guid->Data4[1] == 0x00 && |
|
guid->Data4[2] == 0x00 && |
|
guid->Data4[3] == 0xAA && |
|
guid->Data4[4] == 0x00 && |
|
guid->Data4[5] == 0x38 && |
|
guid->Data4[6] == 0x9B && |
|
guid->Data4[7] == 0x71) { |
|
*out_fourcc = guid->Data1; |
|
return 0; |
|
} |
|
|
|
*out_fourcc = 0; |
|
return AVERROR_UNKNOWN; |
|
} |
|
|
|
struct GUID_Entry { |
|
const GUID *guid; |
|
const char *name; |
|
}; |
|
|
|
#define GUID_ENTRY(var) {&(var), # var} |
|
|
|
static struct GUID_Entry guid_names[] = { |
|
GUID_ENTRY(MFT_FRIENDLY_NAME_Attribute), |
|
GUID_ENTRY(MFT_TRANSFORM_CLSID_Attribute), |
|
GUID_ENTRY(MFT_ENUM_HARDWARE_URL_Attribute), |
|
GUID_ENTRY(MFT_CONNECTED_STREAM_ATTRIBUTE), |
|
GUID_ENTRY(MFT_CONNECTED_TO_HW_STREAM), |
|
GUID_ENTRY(MF_SA_D3D_AWARE), |
|
GUID_ENTRY(ff_MF_SA_MINIMUM_OUTPUT_SAMPLE_COUNT), |
|
GUID_ENTRY(ff_MF_SA_MINIMUM_OUTPUT_SAMPLE_COUNT_PROGRESSIVE), |
|
GUID_ENTRY(ff_MF_SA_D3D11_BINDFLAGS), |
|
GUID_ENTRY(ff_MF_SA_D3D11_USAGE), |
|
GUID_ENTRY(ff_MF_SA_D3D11_AWARE), |
|
GUID_ENTRY(ff_MF_SA_D3D11_SHARED), |
|
GUID_ENTRY(ff_MF_SA_D3D11_SHARED_WITHOUT_MUTEX), |
|
GUID_ENTRY(MF_MT_SUBTYPE), |
|
GUID_ENTRY(MF_MT_MAJOR_TYPE), |
|
GUID_ENTRY(MF_MT_AUDIO_SAMPLES_PER_SECOND), |
|
GUID_ENTRY(MF_MT_AUDIO_NUM_CHANNELS), |
|
GUID_ENTRY(MF_MT_AUDIO_CHANNEL_MASK), |
|
GUID_ENTRY(MF_MT_FRAME_SIZE), |
|
GUID_ENTRY(MF_MT_INTERLACE_MODE), |
|
GUID_ENTRY(MF_MT_USER_DATA), |
|
GUID_ENTRY(MF_MT_PIXEL_ASPECT_RATIO), |
|
GUID_ENTRY(MFMediaType_Audio), |
|
GUID_ENTRY(MFMediaType_Video), |
|
GUID_ENTRY(MFAudioFormat_PCM), |
|
GUID_ENTRY(MFAudioFormat_Float), |
|
GUID_ENTRY(MFVideoFormat_H264), |
|
GUID_ENTRY(MFVideoFormat_H264_ES), |
|
GUID_ENTRY(ff_MFVideoFormat_HEVC), |
|
GUID_ENTRY(ff_MFVideoFormat_HEVC_ES), |
|
GUID_ENTRY(MFVideoFormat_MPEG2), |
|
GUID_ENTRY(MFVideoFormat_MP43), |
|
GUID_ENTRY(MFVideoFormat_MP4V), |
|
GUID_ENTRY(MFVideoFormat_WMV1), |
|
GUID_ENTRY(MFVideoFormat_WMV2), |
|
GUID_ENTRY(MFVideoFormat_WMV3), |
|
GUID_ENTRY(MFVideoFormat_WVC1), |
|
GUID_ENTRY(MFAudioFormat_Dolby_AC3), |
|
GUID_ENTRY(MFAudioFormat_Dolby_DDPlus), |
|
GUID_ENTRY(MFAudioFormat_AAC), |
|
GUID_ENTRY(MFAudioFormat_MP3), |
|
GUID_ENTRY(MFAudioFormat_MSP1), |
|
GUID_ENTRY(MFAudioFormat_WMAudioV8), |
|
GUID_ENTRY(MFAudioFormat_WMAudioV9), |
|
GUID_ENTRY(MFAudioFormat_WMAudio_Lossless), |
|
GUID_ENTRY(MF_MT_ALL_SAMPLES_INDEPENDENT), |
|
GUID_ENTRY(MF_MT_COMPRESSED), |
|
GUID_ENTRY(MF_MT_FIXED_SIZE_SAMPLES), |
|
GUID_ENTRY(MF_MT_SAMPLE_SIZE), |
|
GUID_ENTRY(MF_MT_WRAPPED_TYPE), |
|
GUID_ENTRY(MF_MT_AAC_AUDIO_PROFILE_LEVEL_INDICATION), |
|
GUID_ENTRY(MF_MT_AAC_PAYLOAD_TYPE), |
|
GUID_ENTRY(MF_MT_AUDIO_AVG_BYTES_PER_SECOND), |
|
GUID_ENTRY(MF_MT_AUDIO_BITS_PER_SAMPLE), |
|
GUID_ENTRY(MF_MT_AUDIO_BLOCK_ALIGNMENT), |
|
GUID_ENTRY(MF_MT_AUDIO_CHANNEL_MASK), |
|
GUID_ENTRY(MF_MT_AUDIO_FLOAT_SAMPLES_PER_SECOND), |
|
GUID_ENTRY(MF_MT_AUDIO_FOLDDOWN_MATRIX), |
|
GUID_ENTRY(MF_MT_AUDIO_NUM_CHANNELS), |
|
GUID_ENTRY(MF_MT_AUDIO_PREFER_WAVEFORMATEX), |
|
GUID_ENTRY(MF_MT_AUDIO_SAMPLES_PER_BLOCK), |
|
GUID_ENTRY(MF_MT_AUDIO_SAMPLES_PER_SECOND), |
|
GUID_ENTRY(MF_MT_AUDIO_VALID_BITS_PER_SAMPLE), |
|
GUID_ENTRY(MF_MT_AUDIO_WMADRC_AVGREF), |
|
GUID_ENTRY(MF_MT_AUDIO_WMADRC_AVGTARGET), |
|
GUID_ENTRY(MF_MT_AUDIO_WMADRC_PEAKREF), |
|
GUID_ENTRY(MF_MT_AUDIO_WMADRC_PEAKTARGET), |
|
GUID_ENTRY(MF_MT_AVG_BIT_ERROR_RATE), |
|
GUID_ENTRY(MF_MT_AVG_BITRATE), |
|
GUID_ENTRY(MF_MT_DEFAULT_STRIDE), |
|
GUID_ENTRY(MF_MT_DRM_FLAGS), |
|
GUID_ENTRY(MF_MT_FRAME_RATE), |
|
GUID_ENTRY(MF_MT_FRAME_RATE_RANGE_MAX), |
|
GUID_ENTRY(MF_MT_FRAME_RATE_RANGE_MIN), |
|
GUID_ENTRY(MF_MT_FRAME_SIZE), |
|
GUID_ENTRY(MF_MT_GEOMETRIC_APERTURE), |
|
GUID_ENTRY(MF_MT_INTERLACE_MODE), |
|
GUID_ENTRY(MF_MT_MAX_KEYFRAME_SPACING), |
|
GUID_ENTRY(MF_MT_MINIMUM_DISPLAY_APERTURE), |
|
GUID_ENTRY(MF_MT_MPEG_SEQUENCE_HEADER), |
|
GUID_ENTRY(MF_MT_MPEG_START_TIME_CODE), |
|
GUID_ENTRY(MF_MT_MPEG2_FLAGS), |
|
GUID_ENTRY(MF_MT_MPEG2_LEVEL), |
|
GUID_ENTRY(MF_MT_MPEG2_PROFILE), |
|
GUID_ENTRY(MF_MT_PAD_CONTROL_FLAGS), |
|
GUID_ENTRY(MF_MT_PALETTE), |
|
GUID_ENTRY(MF_MT_PAN_SCAN_APERTURE), |
|
GUID_ENTRY(MF_MT_PAN_SCAN_ENABLED), |
|
GUID_ENTRY(MF_MT_PIXEL_ASPECT_RATIO), |
|
GUID_ENTRY(MF_MT_SOURCE_CONTENT_HINT), |
|
GUID_ENTRY(MF_MT_TRANSFER_FUNCTION), |
|
GUID_ENTRY(MF_MT_VIDEO_CHROMA_SITING), |
|
GUID_ENTRY(MF_MT_VIDEO_LIGHTING), |
|
GUID_ENTRY(MF_MT_VIDEO_NOMINAL_RANGE), |
|
GUID_ENTRY(MF_MT_VIDEO_PRIMARIES), |
|
GUID_ENTRY(MF_MT_VIDEO_ROTATION), |
|
GUID_ENTRY(MF_MT_YUV_MATRIX), |
|
GUID_ENTRY(ff_CODECAPI_AVDecVideoThumbnailGenerationMode), |
|
GUID_ENTRY(ff_CODECAPI_AVDecVideoDropPicWithMissingRef), |
|
GUID_ENTRY(ff_CODECAPI_AVDecVideoSoftwareDeinterlaceMode), |
|
GUID_ENTRY(ff_CODECAPI_AVDecVideoFastDecodeMode), |
|
GUID_ENTRY(ff_CODECAPI_AVLowLatencyMode), |
|
GUID_ENTRY(ff_CODECAPI_AVDecVideoH264ErrorConcealment), |
|
GUID_ENTRY(ff_CODECAPI_AVDecVideoMPEG2ErrorConcealment), |
|
GUID_ENTRY(ff_CODECAPI_AVDecVideoCodecType), |
|
GUID_ENTRY(ff_CODECAPI_AVDecVideoDXVAMode), |
|
GUID_ENTRY(ff_CODECAPI_AVDecVideoDXVABusEncryption), |
|
GUID_ENTRY(ff_CODECAPI_AVDecVideoSWPowerLevel), |
|
GUID_ENTRY(ff_CODECAPI_AVDecVideoMaxCodedWidth), |
|
GUID_ENTRY(ff_CODECAPI_AVDecVideoMaxCodedHeight), |
|
GUID_ENTRY(ff_CODECAPI_AVDecNumWorkerThreads), |
|
GUID_ENTRY(ff_CODECAPI_AVDecSoftwareDynamicFormatChange), |
|
GUID_ENTRY(ff_CODECAPI_AVDecDisableVideoPostProcessing), |
|
}; |
|
|
|
char *ff_guid_str_buf(char *buf, size_t buf_size, const GUID *guid) |
|
{ |
|
uint32_t fourcc; |
|
int n; |
|
for (n = 0; n < FF_ARRAY_ELEMS(guid_names); n++) { |
|
if (IsEqualGUID(guid, guid_names[n].guid)) { |
|
snprintf(buf, buf_size, "%s", guid_names[n].name); |
|
return buf; |
|
} |
|
} |
|
|
|
if (ff_fourcc_from_guid(guid, &fourcc) >= 0) { |
|
snprintf(buf, buf_size, "<FourCC %s>", av_fourcc2str(fourcc)); |
|
return buf; |
|
} |
|
|
|
snprintf(buf, buf_size, |
|
"{%8.8x-%4.4x-%4.4x-%2.2x%2.2x-%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x}", |
|
(unsigned) guid->Data1, guid->Data2, guid->Data3, |
|
guid->Data4[0], guid->Data4[1], |
|
guid->Data4[2], guid->Data4[3], |
|
guid->Data4[4], guid->Data4[5], |
|
guid->Data4[6], guid->Data4[7]); |
|
return buf; |
|
} |
|
|
|
void ff_attributes_dump(void *log, IMFAttributes *attrs) |
|
{ |
|
HRESULT hr; |
|
UINT32 count; |
|
int n; |
|
|
|
hr = IMFAttributes_GetCount(attrs, &count); |
|
if (FAILED(hr)) |
|
return; |
|
|
|
for (n = 0; n < count; n++) { |
|
GUID key; |
|
MF_ATTRIBUTE_TYPE type; |
|
char extra[80] = {0}; |
|
const char *name = NULL; |
|
|
|
hr = IMFAttributes_GetItemByIndex(attrs, n, &key, NULL); |
|
if (FAILED(hr)) |
|
goto err; |
|
|
|
name = ff_guid_str(&key); |
|
|
|
if (IsEqualGUID(&key, &MF_MT_AUDIO_CHANNEL_MASK)) { |
|
UINT32 v; |
|
hr = IMFAttributes_GetUINT32(attrs, &key, &v); |
|
if (FAILED(hr)) |
|
goto err; |
|
snprintf(extra, sizeof(extra), " (0x%x)", (unsigned)v); |
|
} else if (IsEqualGUID(&key, &MF_MT_FRAME_SIZE)) { |
|
UINT32 w, h; |
|
|
|
hr = ff_MFGetAttributeSize(attrs, &MF_MT_FRAME_SIZE, &w, &h); |
|
if (FAILED(hr)) |
|
goto err; |
|
snprintf(extra, sizeof(extra), " (%dx%d)", (int)w, (int)h); |
|
} else if (IsEqualGUID(&key, &MF_MT_PIXEL_ASPECT_RATIO) || |
|
IsEqualGUID(&key, &MF_MT_FRAME_RATE)) { |
|
UINT32 num, den; |
|
|
|
hr = ff_MFGetAttributeRatio(attrs, &key, &num, &den); |
|
if (FAILED(hr)) |
|
goto err; |
|
snprintf(extra, sizeof(extra), " (%d:%d)", (int)num, (int)den); |
|
} |
|
|
|
hr = IMFAttributes_GetItemType(attrs, &key, &type); |
|
if (FAILED(hr)) |
|
goto err; |
|
|
|
switch (type) { |
|
case MF_ATTRIBUTE_UINT32: { |
|
UINT32 v; |
|
hr = IMFAttributes_GetUINT32(attrs, &key, &v); |
|
if (FAILED(hr)) |
|
goto err; |
|
av_log(log, AV_LOG_VERBOSE, " %s=%d%s\n", name, (int)v, extra); |
|
break; |
|
case MF_ATTRIBUTE_UINT64: { |
|
UINT64 v; |
|
hr = IMFAttributes_GetUINT64(attrs, &key, &v); |
|
if (FAILED(hr)) |
|
goto err; |
|
av_log(log, AV_LOG_VERBOSE, " %s=%lld%s\n", name, (long long)v, extra); |
|
break; |
|
} |
|
case MF_ATTRIBUTE_DOUBLE: { |
|
DOUBLE v; |
|
hr = IMFAttributes_GetDouble(attrs, &key, &v); |
|
if (FAILED(hr)) |
|
goto err; |
|
av_log(log, AV_LOG_VERBOSE, " %s=%f%s\n", name, (double)v, extra); |
|
break; |
|
} |
|
case MF_ATTRIBUTE_STRING: { |
|
wchar_t s[512]; // being lazy here |
|
hr = IMFAttributes_GetString(attrs, &key, s, sizeof(s), NULL); |
|
if (FAILED(hr)) |
|
goto err; |
|
av_log(log, AV_LOG_VERBOSE, " %s='%ls'%s\n", name, s, extra); |
|
break; |
|
} |
|
case MF_ATTRIBUTE_GUID: { |
|
GUID v; |
|
hr = IMFAttributes_GetGUID(attrs, &key, &v); |
|
if (FAILED(hr)) |
|
goto err; |
|
av_log(log, AV_LOG_VERBOSE, " %s=%s%s\n", name, ff_guid_str(&v), extra); |
|
break; |
|
} |
|
case MF_ATTRIBUTE_BLOB: { |
|
UINT32 sz; |
|
UINT8 buffer[100]; |
|
hr = IMFAttributes_GetBlobSize(attrs, &key, &sz); |
|
if (FAILED(hr)) |
|
goto err; |
|
if (sz <= sizeof(buffer)) { |
|
// hex-dump it |
|
char str[512] = {0}; |
|
size_t pos = 0; |
|
hr = IMFAttributes_GetBlob(attrs, &key, buffer, sizeof(buffer), &sz); |
|
if (FAILED(hr)) |
|
goto err; |
|
for (pos = 0; pos < sz; pos++) { |
|
const char *hex = "0123456789ABCDEF"; |
|
if (pos * 3 + 3 > sizeof(str)) |
|
break; |
|
str[pos * 3 + 0] = hex[buffer[pos] >> 4]; |
|
str[pos * 3 + 1] = hex[buffer[pos] & 15]; |
|
str[pos * 3 + 2] = ' '; |
|
} |
|
str[pos * 3 + 0] = 0; |
|
av_log(log, AV_LOG_VERBOSE, " %s=<blob size %d: %s>%s\n", name, (int)sz, str, extra); |
|
} else { |
|
av_log(log, AV_LOG_VERBOSE, " %s=<blob size %d>%s\n", name, (int)sz, extra); |
|
} |
|
break; |
|
} |
|
case MF_ATTRIBUTE_IUNKNOWN: { |
|
av_log(log, AV_LOG_VERBOSE, " %s=<IUnknown>%s\n", name, extra); |
|
break; |
|
} |
|
default: |
|
av_log(log, AV_LOG_VERBOSE, " %s=<unknown type>%s\n", name, extra); |
|
break; |
|
} |
|
} |
|
|
|
if (IsEqualGUID(&key, &MF_MT_SUBTYPE)) { |
|
const char *fmt; |
|
fmt = av_get_sample_fmt_name(ff_media_type_to_sample_fmt(attrs)); |
|
if (fmt) |
|
av_log(log, AV_LOG_VERBOSE, " FF-sample-format=%s\n", fmt); |
|
|
|
fmt = av_get_pix_fmt_name(ff_media_type_to_pix_fmt(attrs)); |
|
if (fmt) |
|
av_log(log, AV_LOG_VERBOSE, " FF-pixel-format=%s\n", fmt); |
|
} |
|
|
|
continue; |
|
err: |
|
av_log(log, AV_LOG_VERBOSE, " %s=<failed to get value>\n", name ? name : "?"); |
|
} |
|
} |
|
|
|
void ff_media_type_dump(void *log, IMFMediaType *type) |
|
{ |
|
ff_attributes_dump(log, (IMFAttributes *)type); |
|
} |
|
|
|
const CLSID *ff_codec_to_mf_subtype(enum AVCodecID codec) |
|
{ |
|
switch (codec) { |
|
case AV_CODEC_ID_H264: return &MFVideoFormat_H264; |
|
case AV_CODEC_ID_HEVC: return &ff_MFVideoFormat_HEVC; |
|
case AV_CODEC_ID_AC3: return &MFAudioFormat_Dolby_AC3; |
|
case AV_CODEC_ID_AAC: return &MFAudioFormat_AAC; |
|
case AV_CODEC_ID_MP3: return &MFAudioFormat_MP3; |
|
default: return NULL; |
|
} |
|
} |
|
|
|
static int init_com_mf(void *log, MFFunctions *f) |
|
{ |
|
HRESULT hr; |
|
|
|
hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); |
|
if (hr == RPC_E_CHANGED_MODE) { |
|
av_log(log, AV_LOG_ERROR, "COM must not be in STA mode\n"); |
|
return AVERROR(EINVAL); |
|
} else if (FAILED(hr)) { |
|
av_log(log, AV_LOG_ERROR, "could not initialize COM\n"); |
|
return AVERROR(ENOSYS); |
|
} |
|
|
|
hr = f->MFStartup(MF_VERSION, MFSTARTUP_FULL); |
|
if (FAILED(hr)) { |
|
av_log(log, AV_LOG_ERROR, "could not initialize MediaFoundation\n"); |
|
CoUninitialize(); |
|
return AVERROR(ENOSYS); |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
static void uninit_com_mf(MFFunctions *f) |
|
{ |
|
f->MFShutdown(); |
|
CoUninitialize(); |
|
} |
|
|
|
// Find and create a IMFTransform with the given input/output types. When done, |
|
// you should use ff_free_mf() to destroy it, which will also uninit COM. |
|
int ff_instantiate_mf(void *log, |
|
MFFunctions *f, |
|
GUID category, |
|
MFT_REGISTER_TYPE_INFO *in_type, |
|
MFT_REGISTER_TYPE_INFO *out_type, |
|
int use_hw, |
|
IMFTransform **res) |
|
{ |
|
HRESULT hr; |
|
int n; |
|
int ret; |
|
IMFActivate **activate; |
|
UINT32 num_activate; |
|
IMFActivate *winner = 0; |
|
UINT32 flags; |
|
|
|
ret = init_com_mf(log, f); |
|
if (ret < 0) |
|
return ret; |
|
|
|
flags = MFT_ENUM_FLAG_SORTANDFILTER; |
|
|
|
if (use_hw) { |
|
flags |= MFT_ENUM_FLAG_HARDWARE; |
|
} else { |
|
flags |= MFT_ENUM_FLAG_SYNCMFT; |
|
} |
|
|
|
hr = f->MFTEnumEx(category, flags, in_type, out_type, &activate, |
|
&num_activate); |
|
if (FAILED(hr)) |
|
goto error_uninit_mf; |
|
|
|
if (log) { |
|
if (!num_activate) |
|
av_log(log, AV_LOG_ERROR, "could not find any MFT for the given media type\n"); |
|
|
|
for (n = 0; n < num_activate; n++) { |
|
av_log(log, AV_LOG_VERBOSE, "MF %d attributes:\n", n); |
|
ff_attributes_dump(log, (IMFAttributes *)activate[n]); |
|
} |
|
} |
|
|
|
*res = NULL; |
|
for (n = 0; n < num_activate; n++) { |
|
if (log) |
|
av_log(log, AV_LOG_VERBOSE, "activate MFT %d\n", n); |
|
hr = IMFActivate_ActivateObject(activate[n], &IID_IMFTransform, |
|
(void **)res); |
|
if (*res) { |
|
winner = activate[n]; |
|
IMFActivate_AddRef(winner); |
|
break; |
|
} |
|
} |
|
|
|
for (n = 0; n < num_activate; n++) |
|
IMFActivate_Release(activate[n]); |
|
CoTaskMemFree(activate); |
|
|
|
if (!*res) { |
|
if (log) |
|
av_log(log, AV_LOG_ERROR, "could not create MFT\n"); |
|
goto error_uninit_mf; |
|
} |
|
|
|
if (log) { |
|
wchar_t s[512]; // being lazy here |
|
IMFAttributes *attrs; |
|
hr = IMFTransform_GetAttributes(*res, &attrs); |
|
if (!FAILED(hr) && attrs) { |
|
|
|
av_log(log, AV_LOG_VERBOSE, "MFT attributes\n"); |
|
ff_attributes_dump(log, attrs); |
|
IMFAttributes_Release(attrs); |
|
} |
|
|
|
hr = IMFActivate_GetString(winner, &MFT_FRIENDLY_NAME_Attribute, s, |
|
sizeof(s), NULL); |
|
if (!FAILED(hr)) |
|
av_log(log, AV_LOG_INFO, "MFT name: '%ls'\n", s); |
|
|
|
} |
|
|
|
IMFActivate_Release(winner); |
|
|
|
return 0; |
|
|
|
error_uninit_mf: |
|
uninit_com_mf(f); |
|
return AVERROR(ENOSYS); |
|
} |
|
|
|
void ff_free_mf(MFFunctions *f, IMFTransform **mft) |
|
{ |
|
if (*mft) |
|
IMFTransform_Release(*mft); |
|
*mft = NULL; |
|
uninit_com_mf(f); |
|
}
|
|
|