wine/dlls/winegstreamer/mfplat.c
Zebediah Figura 6c11a4af58 winegstreamer: Move flipping based on RGB to the frontends.
Give the backend a more simple and self-consistent API.

This commit also changes behaviour, by virtue of *not* changing some frontends.
In specific, this commit does not modify the AVI splitter, which is known to
output top-down RGB samples on Windows if the original video uses them (this is
trivial to test by modifying test.avi in quartz to use "bgra" instead of
"yuv420p"). It also does not modify the Media Foundation color converter DMO,
whose tests imply that the MF_MT_DEFAULT_STRIDE attribute is always positive.
2023-04-07 13:26:58 +02:00

904 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, &winegstreamer_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 (!IsRectEmpty(&format->u.video.padding))
{
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));
}