wine/dlls/winegstreamer/mfplat.c

905 lines
31 KiB
C

/*
* Copyright 2019 Nikolay Sivov for CodeWeavers
* Copyright 2020 Zebediah Figura for CodeWeavers
*
* This library 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 library 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 library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "gst_private.h"
#include "ks.h"
#include "ksmedia.h"
#include "wmcodecdsp.h"
#include "initguid.h"
#include "d3d9types.h"
#include "mfapi.h"
#include "wine/debug.h"
#include "wine/list.h"
WINE_DEFAULT_DEBUG_CHANNEL(mfplat);
DEFINE_GUID(DMOVideoFormat_RGB32,D3DFMT_X8R8G8B8,0x524f,0x11ce,0x9f,0x53,0x00,0x20,0xaf,0x0b,0xa7,0x70);
DEFINE_GUID(DMOVideoFormat_RGB24,D3DFMT_R8G8B8,0x524f,0x11ce,0x9f,0x53,0x00,0x20,0xaf,0x0b,0xa7,0x70);
DEFINE_GUID(DMOVideoFormat_RGB565,D3DFMT_R5G6B5,0x524f,0x11ce,0x9f,0x53,0x00,0x20,0xaf,0x0b,0xa7,0x70);
DEFINE_GUID(DMOVideoFormat_RGB555,D3DFMT_X1R5G5B5,0x524f,0x11ce,0x9f,0x53,0x00,0x20,0xaf,0x0b,0xa7,0x70);
DEFINE_GUID(DMOVideoFormat_RGB8,D3DFMT_P8,0x524f,0x11ce,0x9f,0x53,0x00,0x20,0xaf,0x0b,0xa7,0x70);
DEFINE_MEDIATYPE_GUID(MFAudioFormat_RAW_AAC,WAVE_FORMAT_RAW_AAC1);
DEFINE_MEDIATYPE_GUID(MFVideoFormat_VC1S,MAKEFOURCC('V','C','1','S'));
DEFINE_MEDIATYPE_GUID(MFVideoFormat_IV50,MAKEFOURCC('I','V','5','0'));
struct class_factory
{
IClassFactory IClassFactory_iface;
LONG refcount;
HRESULT (*create_instance)(REFIID riid, void **obj);
};
static struct class_factory *impl_from_IClassFactory(IClassFactory *iface)
{
return CONTAINING_RECORD(iface, struct class_factory, IClassFactory_iface);
}
static HRESULT WINAPI class_factory_QueryInterface(IClassFactory *iface, REFIID riid, void **obj)
{
TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj);
if (IsEqualGUID(riid, &IID_IClassFactory) ||
IsEqualGUID(riid, &IID_IUnknown))
{
*obj = iface;
IClassFactory_AddRef(iface);
return S_OK;
}
WARN("%s is not supported.\n", debugstr_guid(riid));
*obj = NULL;
return E_NOINTERFACE;
}
static ULONG WINAPI class_factory_AddRef(IClassFactory *iface)
{
struct class_factory *factory = impl_from_IClassFactory(iface);
return InterlockedIncrement(&factory->refcount);
}
static ULONG WINAPI class_factory_Release(IClassFactory *iface)
{
struct class_factory *factory = impl_from_IClassFactory(iface);
ULONG refcount = InterlockedDecrement(&factory->refcount);
if (!refcount)
free(factory);
return refcount;
}
static HRESULT WINAPI class_factory_CreateInstance(IClassFactory *iface, IUnknown *outer, REFIID riid, void **obj)
{
struct class_factory *factory = impl_from_IClassFactory(iface);
TRACE("%p, %p, %s, %p.\n", iface, outer, debugstr_guid(riid), obj);
if (outer)
{
*obj = NULL;
return CLASS_E_NOAGGREGATION;
}
return factory->create_instance(riid, obj);
}
static HRESULT WINAPI class_factory_LockServer(IClassFactory *iface, BOOL dolock)
{
TRACE("%p, %d.\n", iface, dolock);
return S_OK;
}
static const IClassFactoryVtbl class_factory_vtbl =
{
class_factory_QueryInterface,
class_factory_AddRef,
class_factory_Release,
class_factory_CreateInstance,
class_factory_LockServer,
};
static const GUID CLSID_GStreamerByteStreamHandler = {0x317df618, 0x5e5a, 0x468a, {0x9f, 0x15, 0xd8, 0x27, 0xa9, 0xa0, 0x81, 0x62}};
static const struct class_object
{
const GUID *clsid;
HRESULT (*create_instance)(REFIID riid, void **obj);
}
class_objects[] =
{
{ &CLSID_VideoProcessorMFT, &video_processor_create },
{ &CLSID_GStreamerByteStreamHandler, &gstreamer_byte_stream_handler_create },
{ &CLSID_MSAACDecMFT, &aac_decoder_create },
{ &CLSID_MSH264DecoderMFT, &h264_decoder_create },
};
HRESULT mfplat_get_class_object(REFCLSID rclsid, REFIID riid, void **obj)
{
struct class_factory *factory;
unsigned int i;
HRESULT hr;
for (i = 0; i < ARRAY_SIZE(class_objects); ++i)
{
if (IsEqualGUID(class_objects[i].clsid, rclsid))
{
if (!(factory = malloc(sizeof(*factory))))
return E_OUTOFMEMORY;
factory->IClassFactory_iface.lpVtbl = &class_factory_vtbl;
factory->refcount = 1;
factory->create_instance = class_objects[i].create_instance;
hr = IClassFactory_QueryInterface(&factory->IClassFactory_iface, riid, obj);
IClassFactory_Release(&factory->IClassFactory_iface);
return hr;
}
}
return CLASS_E_CLASSNOTAVAILABLE;
}
HRESULT mfplat_DllRegisterServer(void)
{
MFT_REGISTER_TYPE_INFO resampler_types[] =
{
{MFMediaType_Audio, MFAudioFormat_PCM},
{MFMediaType_Audio, MFAudioFormat_Float},
};
MFT_REGISTER_TYPE_INFO aac_decoder_input_types[] =
{
{MFMediaType_Audio, MFAudioFormat_AAC},
{MFMediaType_Audio, MFAudioFormat_RAW_AAC},
{MFMediaType_Audio, MFAudioFormat_ADTS},
};
MFT_REGISTER_TYPE_INFO aac_decoder_output_types[] =
{
{MFMediaType_Audio, MFAudioFormat_Float},
{MFMediaType_Audio, MFAudioFormat_PCM},
};
MFT_REGISTER_TYPE_INFO wma_decoder_input_types[] =
{
{MFMediaType_Audio, MEDIASUBTYPE_MSAUDIO1},
{MFMediaType_Audio, MFAudioFormat_WMAudioV8},
{MFMediaType_Audio, MFAudioFormat_WMAudioV9},
{MFMediaType_Audio, MFAudioFormat_WMAudio_Lossless},
};
MFT_REGISTER_TYPE_INFO wma_decoder_output_types[] =
{
{MFMediaType_Audio, MFAudioFormat_PCM},
{MFMediaType_Audio, MFAudioFormat_Float},
};
MFT_REGISTER_TYPE_INFO h264_decoder_input_types[] =
{
{MFMediaType_Video, MFVideoFormat_H264},
{MFMediaType_Video, MFVideoFormat_H264_ES},
};
MFT_REGISTER_TYPE_INFO h264_decoder_output_types[] =
{
{MFMediaType_Video, MFVideoFormat_NV12},
{MFMediaType_Video, MFVideoFormat_YV12},
{MFMediaType_Video, MFVideoFormat_IYUV},
{MFMediaType_Video, MFVideoFormat_I420},
{MFMediaType_Video, MFVideoFormat_YUY2},
};
MFT_REGISTER_TYPE_INFO video_processor_input_types[] =
{
{MFMediaType_Video, MFVideoFormat_IYUV},
{MFMediaType_Video, MFVideoFormat_YV12},
{MFMediaType_Video, MFVideoFormat_NV12},
{MFMediaType_Video, MFVideoFormat_YUY2},
{MFMediaType_Video, MFVideoFormat_ARGB32},
{MFMediaType_Video, MFVideoFormat_RGB32},
{MFMediaType_Video, MFVideoFormat_NV11},
{MFMediaType_Video, MFVideoFormat_AYUV},
{MFMediaType_Video, MFVideoFormat_UYVY},
{MFMediaType_Video, MEDIASUBTYPE_P208},
{MFMediaType_Video, MFVideoFormat_RGB24},
{MFMediaType_Video, MFVideoFormat_RGB555},
{MFMediaType_Video, MFVideoFormat_RGB565},
{MFMediaType_Video, MFVideoFormat_RGB8},
{MFMediaType_Video, MFVideoFormat_I420},
{MFMediaType_Video, MFVideoFormat_Y216},
{MFMediaType_Video, MFVideoFormat_v410},
{MFMediaType_Video, MFVideoFormat_Y41P},
{MFMediaType_Video, MFVideoFormat_Y41T},
{MFMediaType_Video, MFVideoFormat_Y42T},
{MFMediaType_Video, MFVideoFormat_YVYU},
{MFMediaType_Video, MFVideoFormat_420O},
};
MFT_REGISTER_TYPE_INFO video_processor_output_types[] =
{
{MFMediaType_Video, MFVideoFormat_IYUV},
{MFMediaType_Video, MFVideoFormat_YV12},
{MFMediaType_Video, MFVideoFormat_NV12},
{MFMediaType_Video, MFVideoFormat_YUY2},
{MFMediaType_Video, MFVideoFormat_ARGB32},
{MFMediaType_Video, MFVideoFormat_RGB32},
{MFMediaType_Video, MFVideoFormat_NV11},
{MFMediaType_Video, MFVideoFormat_AYUV},
{MFMediaType_Video, MFVideoFormat_UYVY},
{MFMediaType_Video, MEDIASUBTYPE_P208},
{MFMediaType_Video, MFVideoFormat_RGB24},
{MFMediaType_Video, MFVideoFormat_RGB555},
{MFMediaType_Video, MFVideoFormat_RGB565},
{MFMediaType_Video, MFVideoFormat_RGB8},
{MFMediaType_Video, MFVideoFormat_I420},
{MFMediaType_Video, MFVideoFormat_Y216},
{MFMediaType_Video, MFVideoFormat_v410},
{MFMediaType_Video, MFVideoFormat_Y41P},
{MFMediaType_Video, MFVideoFormat_Y41T},
{MFMediaType_Video, MFVideoFormat_Y42T},
{MFMediaType_Video, MFVideoFormat_YVYU},
};
MFT_REGISTER_TYPE_INFO wmv_decoder_input_types[] =
{
{MFMediaType_Video, MFVideoFormat_WMV1},
{MFMediaType_Video, MFVideoFormat_WMV2},
{MFMediaType_Video, MFVideoFormat_WMV3},
{MFMediaType_Video, MEDIASUBTYPE_WMVP},
{MFMediaType_Video, MEDIASUBTYPE_WVP2},
{MFMediaType_Video, MEDIASUBTYPE_WMVR},
{MFMediaType_Video, MEDIASUBTYPE_WMVA},
{MFMediaType_Video, MFVideoFormat_WVC1},
{MFMediaType_Video, MFVideoFormat_VC1S},
};
MFT_REGISTER_TYPE_INFO wmv_decoder_output_types[] =
{
{MFMediaType_Video, MFVideoFormat_YV12},
{MFMediaType_Video, MFVideoFormat_YUY2},
{MFMediaType_Video, MFVideoFormat_UYVY},
{MFMediaType_Video, MFVideoFormat_YVYU},
{MFMediaType_Video, MFVideoFormat_NV11},
{MFMediaType_Video, MFVideoFormat_NV12},
{MFMediaType_Video, DMOVideoFormat_RGB32},
{MFMediaType_Video, DMOVideoFormat_RGB24},
{MFMediaType_Video, DMOVideoFormat_RGB565},
{MFMediaType_Video, DMOVideoFormat_RGB555},
{MFMediaType_Video, DMOVideoFormat_RGB8},
};
MFT_REGISTER_TYPE_INFO color_convert_input_types[] =
{
{MFMediaType_Video, MFVideoFormat_YV12},
{MFMediaType_Video, MFVideoFormat_YUY2},
{MFMediaType_Video, MFVideoFormat_UYVY},
{MFMediaType_Video, MFVideoFormat_AYUV},
{MFMediaType_Video, MFVideoFormat_NV12},
{MFMediaType_Video, DMOVideoFormat_RGB32},
{MFMediaType_Video, DMOVideoFormat_RGB565},
{MFMediaType_Video, MFVideoFormat_I420},
{MFMediaType_Video, MFVideoFormat_IYUV},
{MFMediaType_Video, MFVideoFormat_YVYU},
{MFMediaType_Video, DMOVideoFormat_RGB24},
{MFMediaType_Video, DMOVideoFormat_RGB555},
{MFMediaType_Video, DMOVideoFormat_RGB8},
{MFMediaType_Video, MEDIASUBTYPE_V216},
{MFMediaType_Video, MEDIASUBTYPE_V410},
{MFMediaType_Video, MFVideoFormat_NV11},
{MFMediaType_Video, MFVideoFormat_Y41P},
{MFMediaType_Video, MFVideoFormat_Y41T},
{MFMediaType_Video, MFVideoFormat_Y42T},
{MFMediaType_Video, MFVideoFormat_YVU9},
};
MFT_REGISTER_TYPE_INFO color_convert_output_types[] =
{
{MFMediaType_Video, MFVideoFormat_YV12},
{MFMediaType_Video, MFVideoFormat_YUY2},
{MFMediaType_Video, MFVideoFormat_UYVY},
{MFMediaType_Video, MFVideoFormat_AYUV},
{MFMediaType_Video, MFVideoFormat_NV12},
{MFMediaType_Video, DMOVideoFormat_RGB32},
{MFMediaType_Video, DMOVideoFormat_RGB565},
{MFMediaType_Video, MFVideoFormat_I420},
{MFMediaType_Video, MFVideoFormat_IYUV},
{MFMediaType_Video, MFVideoFormat_YVYU},
{MFMediaType_Video, DMOVideoFormat_RGB24},
{MFMediaType_Video, DMOVideoFormat_RGB555},
{MFMediaType_Video, DMOVideoFormat_RGB8},
{MFMediaType_Video, MEDIASUBTYPE_V216},
{MFMediaType_Video, MEDIASUBTYPE_V410},
{MFMediaType_Video, MFVideoFormat_NV11},
};
struct mft
{
GUID clsid;
GUID category;
WCHAR name[MAX_PATH];
UINT32 flags;
UINT32 input_types_count;
MFT_REGISTER_TYPE_INFO *input_types;
UINT32 output_types_count;
MFT_REGISTER_TYPE_INFO *output_types;
}
mfts[] =
{
{
CLSID_MSAACDecMFT,
MFT_CATEGORY_AUDIO_DECODER,
L"Microsoft AAC Audio Decoder MFT",
MFT_ENUM_FLAG_SYNCMFT,
ARRAY_SIZE(aac_decoder_input_types),
aac_decoder_input_types,
ARRAY_SIZE(aac_decoder_output_types),
aac_decoder_output_types,
},
{
CLSID_WMADecMediaObject,
MFT_CATEGORY_AUDIO_DECODER,
L"WMAudio Decoder MFT",
MFT_ENUM_FLAG_SYNCMFT,
ARRAY_SIZE(wma_decoder_input_types),
wma_decoder_input_types,
ARRAY_SIZE(wma_decoder_output_types),
wma_decoder_output_types,
},
{
CLSID_MSH264DecoderMFT,
MFT_CATEGORY_VIDEO_DECODER,
L"Microsoft H264 Video Decoder MFT",
MFT_ENUM_FLAG_SYNCMFT,
ARRAY_SIZE(h264_decoder_input_types),
h264_decoder_input_types,
ARRAY_SIZE(h264_decoder_output_types),
h264_decoder_output_types,
},
{
CLSID_WMVDecoderMFT,
MFT_CATEGORY_VIDEO_DECODER,
L"WMVideo Decoder MFT",
MFT_ENUM_FLAG_SYNCMFT,
ARRAY_SIZE(wmv_decoder_input_types),
wmv_decoder_input_types,
ARRAY_SIZE(wmv_decoder_output_types),
wmv_decoder_output_types,
},
{
CLSID_VideoProcessorMFT,
MFT_CATEGORY_VIDEO_PROCESSOR,
L"Microsoft Video Processor MFT",
MFT_ENUM_FLAG_SYNCMFT,
ARRAY_SIZE(video_processor_input_types),
video_processor_input_types,
ARRAY_SIZE(video_processor_output_types),
video_processor_output_types,
},
{
CLSID_CResamplerMediaObject,
MFT_CATEGORY_AUDIO_EFFECT,
L"Resampler MFT",
MFT_ENUM_FLAG_SYNCMFT,
ARRAY_SIZE(resampler_types),
resampler_types,
ARRAY_SIZE(resampler_types),
resampler_types,
},
{
CLSID_CColorConvertDMO,
MFT_CATEGORY_VIDEO_EFFECT,
L"Color Converter MFT",
MFT_ENUM_FLAG_SYNCMFT,
ARRAY_SIZE(color_convert_input_types),
color_convert_input_types,
ARRAY_SIZE(color_convert_output_types),
color_convert_output_types,
},
};
unsigned int i;
HRESULT hr;
for (i = 0; i < ARRAY_SIZE(mfts); i++)
{
hr = MFTRegister(mfts[i].clsid, mfts[i].category, mfts[i].name, mfts[i].flags, mfts[i].input_types_count,
mfts[i].input_types, mfts[i].output_types_count, mfts[i].output_types, NULL);
if (FAILED(hr))
{
FIXME("Failed to register MFT, hr %#lx.\n", hr);
return hr;
}
}
return S_OK;
}
static const struct
{
const GUID *subtype;
enum wg_video_format format;
}
video_formats[] =
{
{&MFVideoFormat_ARGB32, WG_VIDEO_FORMAT_BGRA},
{&MFVideoFormat_RGB32, WG_VIDEO_FORMAT_BGRx},
{&MFVideoFormat_RGB24, WG_VIDEO_FORMAT_BGR},
{&MFVideoFormat_RGB555, WG_VIDEO_FORMAT_RGB15},
{&MFVideoFormat_RGB565, WG_VIDEO_FORMAT_RGB16},
{&MFVideoFormat_AYUV, WG_VIDEO_FORMAT_AYUV},
{&MFVideoFormat_I420, WG_VIDEO_FORMAT_I420},
{&MFVideoFormat_IYUV, WG_VIDEO_FORMAT_I420},
{&MFVideoFormat_NV12, WG_VIDEO_FORMAT_NV12},
{&MFVideoFormat_UYVY, WG_VIDEO_FORMAT_UYVY},
{&MFVideoFormat_YUY2, WG_VIDEO_FORMAT_YUY2},
{&MFVideoFormat_YV12, WG_VIDEO_FORMAT_YV12},
{&MFVideoFormat_YVYU, WG_VIDEO_FORMAT_YVYU},
};
static const struct
{
const GUID *subtype;
UINT32 depth;
enum wg_audio_format format;
}
audio_formats[] =
{
{&MFAudioFormat_PCM, 8, WG_AUDIO_FORMAT_U8},
{&MFAudioFormat_PCM, 16, WG_AUDIO_FORMAT_S16LE},
{&MFAudioFormat_PCM, 24, WG_AUDIO_FORMAT_S24LE},
{&MFAudioFormat_PCM, 32, WG_AUDIO_FORMAT_S32LE},
{&MFAudioFormat_Float, 32, WG_AUDIO_FORMAT_F32LE},
{&MFAudioFormat_Float, 64, WG_AUDIO_FORMAT_F64LE},
};
static inline UINT64 make_uint64(UINT32 high, UINT32 low)
{
return ((UINT64)high << 32) | low;
}
static IMFMediaType *mf_media_type_from_wg_format_audio(const struct wg_format *format)
{
unsigned int i, block_align;
IMFMediaType *type;
for (i = 0; i < ARRAY_SIZE(audio_formats); ++i)
{
if (format->u.audio.format == audio_formats[i].format)
{
if (FAILED(MFCreateMediaType(&type)))
return NULL;
IMFMediaType_SetGUID(type, &MF_MT_MAJOR_TYPE, &MFMediaType_Audio);
IMFMediaType_SetGUID(type, &MF_MT_SUBTYPE, audio_formats[i].subtype);
IMFMediaType_SetUINT32(type, &MF_MT_AUDIO_BITS_PER_SAMPLE, audio_formats[i].depth);
IMFMediaType_SetUINT32(type, &MF_MT_AUDIO_SAMPLES_PER_SECOND, format->u.audio.rate);
IMFMediaType_SetUINT32(type, &MF_MT_AUDIO_NUM_CHANNELS, format->u.audio.channels);
IMFMediaType_SetUINT32(type, &MF_MT_AUDIO_CHANNEL_MASK, format->u.audio.channel_mask);
IMFMediaType_SetUINT32(type, &MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE);
block_align = format->u.audio.channels * audio_formats[i].depth / 8;
IMFMediaType_SetUINT32(type, &MF_MT_AUDIO_BLOCK_ALIGNMENT, block_align);
IMFMediaType_SetUINT32(type, &MF_MT_AUDIO_AVG_BYTES_PER_SECOND, block_align * format->u.audio.rate);
return type;
}
}
FIXME("Unknown audio format %#x.\n", format->u.audio.format);
return NULL;
}
static IMFMediaType *mf_media_type_from_wg_format_video(const struct wg_format *format)
{
IMFMediaType *type;
unsigned int i;
for (i = 0; i < ARRAY_SIZE(video_formats); ++i)
{
if (format->u.video.format == video_formats[i].format)
{
unsigned int stride = wg_format_get_stride(format);
int32_t height = abs(format->u.video.height);
int32_t width = format->u.video.width;
if (FAILED(MFCreateMediaType(&type)))
return NULL;
IMFMediaType_SetGUID(type, &MF_MT_MAJOR_TYPE, &MFMediaType_Video);
IMFMediaType_SetGUID(type, &MF_MT_SUBTYPE, video_formats[i].subtype);
IMFMediaType_SetUINT64(type, &MF_MT_FRAME_SIZE, make_uint64(width, height));
IMFMediaType_SetUINT64(type, &MF_MT_FRAME_RATE,
make_uint64(format->u.video.fps_n, format->u.video.fps_d));
IMFMediaType_SetUINT32(type, &MF_MT_COMPRESSED, FALSE);
IMFMediaType_SetUINT32(type, &MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE);
IMFMediaType_SetUINT32(type, &MF_MT_VIDEO_ROTATION, MFVideoRotationFormat_0);
if (format->u.video.height < 0)
stride = -stride;
IMFMediaType_SetUINT32(type, &MF_MT_DEFAULT_STRIDE, stride);
if (format->u.video.padding.left || format->u.video.padding.right
|| format->u.video.padding.top || format->u.video.padding.bottom)
{
MFVideoArea aperture =
{
.OffsetX = {.value = format->u.video.padding.left},
.OffsetY = {.value = format->u.video.padding.top},
.Area.cx = width - format->u.video.padding.right - format->u.video.padding.left,
.Area.cy = height - format->u.video.padding.bottom - format->u.video.padding.top,
};
IMFMediaType_SetBlob(type, &MF_MT_MINIMUM_DISPLAY_APERTURE,
(BYTE *)&aperture, sizeof(aperture));
}
return type;
}
}
FIXME("Unknown video format %#x.\n", format->u.video.format);
return NULL;
}
IMFMediaType *mf_media_type_from_wg_format(const struct wg_format *format)
{
switch (format->major_type)
{
case WG_MAJOR_TYPE_AUDIO_MPEG1:
case WG_MAJOR_TYPE_AUDIO_MPEG4:
case WG_MAJOR_TYPE_AUDIO_WMA:
case WG_MAJOR_TYPE_VIDEO_CINEPAK:
case WG_MAJOR_TYPE_VIDEO_H264:
case WG_MAJOR_TYPE_VIDEO_WMV:
case WG_MAJOR_TYPE_VIDEO_INDEO:
FIXME("Format %u not implemented!\n", format->major_type);
/* fallthrough */
case WG_MAJOR_TYPE_UNKNOWN:
return NULL;
case WG_MAJOR_TYPE_AUDIO:
return mf_media_type_from_wg_format_audio(format);
case WG_MAJOR_TYPE_VIDEO:
return mf_media_type_from_wg_format_video(format);
}
assert(0);
return NULL;
}
static void mf_media_type_to_wg_format_audio(IMFMediaType *type, const GUID *subtype, struct wg_format *format)
{
UINT32 rate, channels, channel_mask, depth;
unsigned int i;
if (FAILED(IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_SAMPLES_PER_SECOND, &rate)))
{
FIXME("Sample rate is not set.\n");
return;
}
if (FAILED(IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_NUM_CHANNELS, &channels)))
{
FIXME("Channel count is not set.\n");
return;
}
if (FAILED(IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_BITS_PER_SAMPLE, &depth)))
{
FIXME("Depth is not set.\n");
return;
}
if (FAILED(IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_CHANNEL_MASK, &channel_mask)))
{
if (channels == 1)
channel_mask = KSAUDIO_SPEAKER_MONO;
else if (channels == 2)
channel_mask = KSAUDIO_SPEAKER_STEREO;
else
{
FIXME("Channel mask is not set.\n");
return;
}
}
format->major_type = WG_MAJOR_TYPE_AUDIO;
format->u.audio.channels = channels;
format->u.audio.channel_mask = channel_mask;
format->u.audio.rate = rate;
for (i = 0; i < ARRAY_SIZE(audio_formats); ++i)
{
if (IsEqualGUID(subtype, audio_formats[i].subtype) && depth == audio_formats[i].depth)
{
format->u.audio.format = audio_formats[i].format;
return;
}
}
FIXME("Unrecognized audio subtype %s, depth %u.\n", debugstr_guid(subtype), depth);
}
static void mf_media_type_to_wg_format_audio_mpeg4(IMFMediaType *type, const GUID *subtype, struct wg_format *format)
{
/* Audio specific config is stored at after HEAACWAVEINFO in MF_MT_USER_DATA
* https://docs.microsoft.com/en-us/windows/win32/api/mmreg/ns-mmreg-heaacwaveformat
*/
typedef struct
{
WORD wPayloadType;
WORD wAudioProfileLevelIndication;
WORD wStructType;
WORD wReserved1;
DWORD dwReserved2;
} HEAACWAVEINFO;
typedef struct
{
HEAACWAVEINFO wfInfo;
BYTE pbAudioSpecificConfig[1];
} HEAACWAVEFORMAT;
BYTE buffer[64];
HEAACWAVEFORMAT *user_data = (HEAACWAVEFORMAT *)buffer;
UINT32 codec_data_size;
BOOL raw_aac;
if (FAILED(IMFMediaType_GetBlob(type, &MF_MT_USER_DATA, buffer, sizeof(buffer), &codec_data_size)))
{
FIXME("Codec data is not set.\n");
return;
}
raw_aac = IsEqualGUID(subtype, &MFAudioFormat_RAW_AAC);
if (!raw_aac)
codec_data_size -= min(codec_data_size, offsetof(HEAACWAVEFORMAT, pbAudioSpecificConfig));
if (codec_data_size > sizeof(format->u.audio_mpeg4.codec_data))
{
FIXME("Codec data needs %u bytes.\n", codec_data_size);
return;
}
if (raw_aac)
memcpy(format->u.audio_mpeg4.codec_data, buffer, codec_data_size);
else
memcpy(format->u.audio_mpeg4.codec_data, user_data->pbAudioSpecificConfig, codec_data_size);
format->major_type = WG_MAJOR_TYPE_AUDIO_MPEG4;
if (FAILED(IMFMediaType_GetUINT32(type, &MF_MT_AAC_PAYLOAD_TYPE, &format->u.audio_mpeg4.payload_type)))
format->u.audio_mpeg4.payload_type = 0;
format->u.audio_mpeg4.codec_data_len = codec_data_size;
}
static enum wg_video_format mf_video_format_to_wg(const GUID *subtype)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(video_formats); ++i)
{
if (IsEqualGUID(subtype, video_formats[i].subtype))
return video_formats[i].format;
}
FIXME("Unrecognized video subtype %s.\n", debugstr_guid(subtype));
return WG_VIDEO_FORMAT_UNKNOWN;
}
static void mf_media_type_to_wg_format_video(IMFMediaType *type, const GUID *subtype, struct wg_format *format)
{
UINT64 frame_rate, frame_size;
MFVideoArea aperture;
UINT32 size, stride;
if (FAILED(IMFMediaType_GetUINT64(type, &MF_MT_FRAME_SIZE, &frame_size)))
{
FIXME("Frame size is not set.\n");
return;
}
format->major_type = WG_MAJOR_TYPE_VIDEO;
format->u.video.width = (UINT32)(frame_size >> 32);
format->u.video.height = (UINT32)frame_size;
format->u.video.fps_n = 1;
format->u.video.fps_d = 1;
if (SUCCEEDED(IMFMediaType_GetBlob(type, &MF_MT_MINIMUM_DISPLAY_APERTURE, (BYTE *)&aperture,
sizeof(aperture), &size)) && size == sizeof(aperture))
{
format->u.video.padding.left = aperture.OffsetX.value;
format->u.video.padding.top = aperture.OffsetY.value;
format->u.video.padding.right = format->u.video.width - aperture.Area.cx - aperture.OffsetX.value;
format->u.video.padding.bottom = format->u.video.height - aperture.Area.cy - aperture.OffsetY.value;
}
if (SUCCEEDED(IMFMediaType_GetUINT64(type, &MF_MT_FRAME_RATE, &frame_rate)) && (UINT32)frame_rate)
{
format->u.video.fps_n = (UINT32)(frame_rate >> 32);
format->u.video.fps_d = (UINT32)frame_rate;
}
format->u.video.format = mf_video_format_to_wg(subtype);
if (SUCCEEDED(IMFMediaType_GetUINT32(type, &MF_MT_DEFAULT_STRIDE, &stride)))
{
if ((int)stride < 0)
format->u.video.height = -format->u.video.height;
}
else if (wg_video_format_is_rgb(format->u.video.format))
{
format->u.video.height = -format->u.video.height;
}
}
static void mf_media_type_to_wg_format_audio_wma(IMFMediaType *type, const GUID *subtype, struct wg_format *format)
{
UINT32 rate, depth, channels, block_align, bytes_per_second, codec_data_len;
BYTE codec_data[64];
UINT32 version;
if (FAILED(IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_SAMPLES_PER_SECOND, &rate)))
{
FIXME("Sample rate is not set.\n");
return;
}
if (FAILED(IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_NUM_CHANNELS, &channels)))
{
FIXME("Channel count is not set.\n");
return;
}
if (FAILED(IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_BLOCK_ALIGNMENT, &block_align)))
{
FIXME("Block alignment is not set.\n");
return;
}
if (FAILED(IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_BITS_PER_SAMPLE, &depth)))
{
FIXME("Depth is not set.\n");
return;
}
if (FAILED(IMFMediaType_GetBlob(type, &MF_MT_USER_DATA, codec_data, sizeof(codec_data), &codec_data_len)))
{
FIXME("Codec data is not set.\n");
return;
}
if (FAILED(IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_AVG_BYTES_PER_SECOND, &bytes_per_second)))
{
FIXME("Bitrate is not set.\n");
bytes_per_second = 0;
}
if (IsEqualGUID(subtype, &MEDIASUBTYPE_MSAUDIO1))
version = 1;
else if (IsEqualGUID(subtype, &MFAudioFormat_WMAudioV8))
version = 2;
else if (IsEqualGUID(subtype, &MFAudioFormat_WMAudioV9))
version = 3;
else if (IsEqualGUID(subtype, &MFAudioFormat_WMAudio_Lossless))
version = 4;
else
{
assert(0);
return;
}
format->major_type = WG_MAJOR_TYPE_AUDIO_WMA;
format->u.audio_wma.version = version;
format->u.audio_wma.bitrate = bytes_per_second * 8;
format->u.audio_wma.rate = rate;
format->u.audio_wma.depth = depth;
format->u.audio_wma.channels = channels;
format->u.audio_wma.block_align = block_align;
format->u.audio_wma.codec_data_len = codec_data_len;
memcpy(format->u.audio_wma.codec_data, codec_data, codec_data_len);
}
static void mf_media_type_to_wg_format_video_h264(IMFMediaType *type, struct wg_format *format)
{
UINT64 frame_rate, frame_size;
UINT32 profile, level;
memset(format, 0, sizeof(*format));
format->major_type = WG_MAJOR_TYPE_VIDEO_H264;
if (SUCCEEDED(IMFMediaType_GetUINT64(type, &MF_MT_FRAME_SIZE, &frame_size)))
{
format->u.video_h264.width = frame_size >> 32;
format->u.video_h264.height = (UINT32)frame_size;
}
if (SUCCEEDED(IMFMediaType_GetUINT64(type, &MF_MT_FRAME_RATE, &frame_rate)) && (UINT32)frame_rate)
{
format->u.video_h264.fps_n = frame_rate >> 32;
format->u.video_h264.fps_d = (UINT32)frame_rate;
}
else
{
format->u.video_h264.fps_n = 1;
format->u.video_h264.fps_d = 1;
}
if (SUCCEEDED(IMFMediaType_GetUINT32(type, &MF_MT_MPEG2_PROFILE, &profile)))
format->u.video_h264.profile = profile;
if (SUCCEEDED(IMFMediaType_GetUINT32(type, &MF_MT_MPEG2_LEVEL, &level)))
format->u.video_h264.level = level;
}
static void mf_media_type_to_wg_format_video_indeo(IMFMediaType *type, uint32_t version, struct wg_format *format)
{
UINT64 frame_rate, frame_size;
memset(format, 0, sizeof(*format));
format->major_type = WG_MAJOR_TYPE_VIDEO_INDEO;
if (SUCCEEDED(IMFMediaType_GetUINT64(type, &MF_MT_FRAME_SIZE, &frame_size)))
{
format->u.video_indeo.width = frame_size >> 32;
format->u.video_indeo.height = (UINT32)frame_size;
}
if (SUCCEEDED(IMFMediaType_GetUINT64(type, &MF_MT_FRAME_RATE, &frame_rate)) && (UINT32)frame_rate)
{
format->u.video_indeo.fps_n = frame_rate >> 32;
format->u.video_indeo.fps_d = (UINT32)frame_rate;
}
else
{
format->u.video_indeo.fps_n = 1;
format->u.video_indeo.fps_d = 1;
}
format->u.video_indeo.version = version;
}
void mf_media_type_to_wg_format(IMFMediaType *type, struct wg_format *format)
{
GUID major_type, subtype;
memset(format, 0, sizeof(*format));
if (FAILED(IMFMediaType_GetMajorType(type, &major_type)))
{
FIXME("Major type is not set.\n");
return;
}
if (FAILED(IMFMediaType_GetGUID(type, &MF_MT_SUBTYPE, &subtype)))
{
FIXME("Subtype is not set.\n");
return;
}
if (IsEqualGUID(&major_type, &MFMediaType_Audio))
{
if (IsEqualGUID(&subtype, &MEDIASUBTYPE_MSAUDIO1) ||
IsEqualGUID(&subtype, &MFAudioFormat_WMAudioV8) ||
IsEqualGUID(&subtype, &MFAudioFormat_WMAudioV9) ||
IsEqualGUID(&subtype, &MFAudioFormat_WMAudio_Lossless))
mf_media_type_to_wg_format_audio_wma(type, &subtype, format);
else if (IsEqualGUID(&subtype, &MFAudioFormat_AAC) || IsEqualGUID(&subtype, &MFAudioFormat_RAW_AAC))
mf_media_type_to_wg_format_audio_mpeg4(type, &subtype, format);
else
mf_media_type_to_wg_format_audio(type, &subtype, format);
}
else if (IsEqualGUID(&major_type, &MFMediaType_Video))
{
if (IsEqualGUID(&subtype, &MFVideoFormat_H264))
mf_media_type_to_wg_format_video_h264(type, format);
else if (IsEqualGUID(&subtype, &MFVideoFormat_IV50))
mf_media_type_to_wg_format_video_indeo(type, 5, format);
else
mf_media_type_to_wg_format_video(type, &subtype, format);
}
else
FIXME("Unrecognized major type %s.\n", debugstr_guid(&major_type));
}