/* GStreamer * Copyright (C) 2019 Seungha Yang * Copyright (C) 2020 Seungha Yang * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library 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. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "gstmfconfig.h" #include "gstmfutils.h" #include /* *INDENT-OFF* */ using namespace Microsoft::WRL; GST_DEBUG_CATEGORY_EXTERN (gst_mf_utils_debug); #define GST_CAT_DEFAULT gst_mf_utils_debug #define MAKE_RAW_FORMAT_CAPS(format) \ "video/x-raw, format = (string) " format /* No GUID is defined for "Y16 " in mfapi.h, but it's used by several devices */ DEFINE_MEDIATYPE_GUID (MFVideoFormat_Y16, FCC ('Y16 ')); static struct { const GUID &mf_format; const gchar *caps_string; GstVideoFormat format; } raw_video_format_map[] = { /* NOTE: when adding new format, gst_mf_update_video_info_with_stride() must * be updated as well */ {MFVideoFormat_RGB32, MAKE_RAW_FORMAT_CAPS ("BGRx"), GST_VIDEO_FORMAT_BGRx}, {MFVideoFormat_ARGB32, MAKE_RAW_FORMAT_CAPS ("BGRA"), GST_VIDEO_FORMAT_BGRA}, {MFVideoFormat_RGB565, MAKE_RAW_FORMAT_CAPS ("RGB16"), GST_VIDEO_FORMAT_RGB16}, {MFVideoFormat_RGB555, MAKE_RAW_FORMAT_CAPS ("RGB15"), GST_VIDEO_FORMAT_RGB15}, {MFVideoFormat_RGB24, MAKE_RAW_FORMAT_CAPS ("BGR"), GST_VIDEO_FORMAT_BGR}, /* packed YUV */ {MFVideoFormat_YUY2, MAKE_RAW_FORMAT_CAPS ("YUY2"), GST_VIDEO_FORMAT_YUY2}, {MFVideoFormat_YVYU, MAKE_RAW_FORMAT_CAPS ("YVYU"), GST_VIDEO_FORMAT_YVYU}, {MFVideoFormat_UYVY, MAKE_RAW_FORMAT_CAPS ("UYVY"), GST_VIDEO_FORMAT_UYVY}, {MFVideoFormat_AYUV, MAKE_RAW_FORMAT_CAPS ("VUYA"), GST_VIDEO_FORMAT_VUYA}, /* semi-planar */ {MFVideoFormat_NV12, MAKE_RAW_FORMAT_CAPS ("NV12"), GST_VIDEO_FORMAT_NV12}, {MFVideoFormat_P010, MAKE_RAW_FORMAT_CAPS ("P010_10LE"), GST_VIDEO_FORMAT_P010_10LE}, {MFVideoFormat_P016, MAKE_RAW_FORMAT_CAPS ("P016_LE"), GST_VIDEO_FORMAT_P016_LE}, /* planar */ {MFVideoFormat_I420, MAKE_RAW_FORMAT_CAPS ("I420"), GST_VIDEO_FORMAT_I420}, {MFVideoFormat_IYUV, MAKE_RAW_FORMAT_CAPS ("I420"), GST_VIDEO_FORMAT_I420}, {MFVideoFormat_YV12, MAKE_RAW_FORMAT_CAPS ("YV12"), GST_VIDEO_FORMAT_YV12}, /* complex format */ {MFVideoFormat_v210, MAKE_RAW_FORMAT_CAPS ("v210"), GST_VIDEO_FORMAT_v210}, {MFVideoFormat_v216, MAKE_RAW_FORMAT_CAPS ("v216"), GST_VIDEO_FORMAT_v216}, /* gray */ {MFVideoFormat_Y16, MAKE_RAW_FORMAT_CAPS ("GRAY16_LE"), GST_VIDEO_FORMAT_GRAY16_LE}, }; static struct { const GUID &mf_format; const gchar *caps_string; } encoded_video_format_map[] = { {MFVideoFormat_H264, "video/x-h264"}, {MFVideoFormat_HEVC, "video/x-h265"}, {MFVideoFormat_H265, "video/x-h265"}, {MFVideoFormat_VP80, "video/x-vp8"}, {MFVideoFormat_VP90, "video/x-vp9"}, {MFVideoFormat_MJPG, "image/jpeg"}, }; /* *INDENT-ON* */ GstVideoFormat gst_mf_video_subtype_to_video_format (const GUID * subtype) { gint i; for (i = 0; i < G_N_ELEMENTS (raw_video_format_map); i++) { if (IsEqualGUID (raw_video_format_map[i].mf_format, *subtype)) return raw_video_format_map[i].format; } return GST_VIDEO_FORMAT_UNKNOWN; } const GUID * gst_mf_video_subtype_from_video_format (GstVideoFormat format) { gint i; for (i = 0; i < G_N_ELEMENTS (raw_video_format_map); i++) { if (raw_video_format_map[i].format == format) return &raw_video_format_map[i].mf_format; } return nullptr; } static GstCaps * gst_mf_media_type_to_video_caps (IMFMediaType * media_type) { HRESULT hr; GstCaps *caps = nullptr; gint i; guint32 width = 0; guint32 height = 0; guint32 num, den; guint32 val; gchar *str; GUID subtype; GstVideoChromaSite chroma_site; GstVideoColorimetry colorimetry; gboolean raw_format = TRUE; hr = media_type->GetGUID (MF_MT_SUBTYPE, &subtype); if (FAILED (hr)) { GST_WARNING ("Failed to get subtype, hr: 0x%x", (guint) hr); return nullptr; } for (i = 0; i < G_N_ELEMENTS (raw_video_format_map); i++) { if (IsEqualGUID (raw_video_format_map[i].mf_format, subtype)) { caps = gst_caps_from_string (raw_video_format_map[i].caps_string); break; } } if (!caps) { for (i = 0; i < G_N_ELEMENTS (encoded_video_format_map); i++) { if (IsEqualGUID (encoded_video_format_map[i].mf_format, subtype)) { caps = gst_caps_from_string (encoded_video_format_map[i].caps_string); raw_format = FALSE; break; } } } if (!caps) { GST_WARNING ("Unknown format %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (subtype.Data1)); return nullptr; } hr = MFGetAttributeSize (media_type, MF_MT_FRAME_SIZE, &width, &height); if (FAILED (hr) || !width || !height) { GST_WARNING ("Couldn't get frame size, hr: 0x%x", (guint) hr); if (raw_format) { gst_caps_unref (caps); return nullptr; } } if (width > 0 && height > 0) { gst_caps_set_simple (caps, "width", G_TYPE_INT, width, "height", G_TYPE_INT, height, nullptr); } hr = MFGetAttributeRatio (media_type, MF_MT_FRAME_RATE, &num, &den); if (SUCCEEDED (hr) && num > 0 && den > 0) gst_caps_set_simple (caps, "framerate", GST_TYPE_FRACTION, num, den, nullptr); hr = MFGetAttributeRatio (media_type, MF_MT_PIXEL_ASPECT_RATIO, &num, &den); if (SUCCEEDED (hr) && num > 0 && den > 0) gst_caps_set_simple (caps, "pixel-aspect-ratio", GST_TYPE_FRACTION, num, den, nullptr); colorimetry.range = GST_VIDEO_COLOR_RANGE_UNKNOWN; colorimetry.matrix = GST_VIDEO_COLOR_MATRIX_UNKNOWN; colorimetry.transfer = GST_VIDEO_TRANSFER_UNKNOWN; colorimetry.primaries = GST_VIDEO_COLOR_PRIMARIES_UNKNOWN; hr = media_type->GetUINT32 (MF_MT_VIDEO_NOMINAL_RANGE, &val); if (SUCCEEDED (hr)) { switch (val) { case MFNominalRange_0_255: colorimetry.range = GST_VIDEO_COLOR_RANGE_0_255; break; case MFNominalRange_16_235: colorimetry.range = GST_VIDEO_COLOR_RANGE_16_235; break; default: break; } } hr = media_type->GetUINT32 (MF_MT_VIDEO_PRIMARIES, &val); if (SUCCEEDED (hr)) { switch (val) { case MFVideoPrimaries_BT709: colorimetry.primaries = GST_VIDEO_COLOR_PRIMARIES_BT709; break; case MFVideoPrimaries_BT470_2_SysM: colorimetry.primaries = GST_VIDEO_COLOR_PRIMARIES_BT470M; break; case MFVideoPrimaries_BT470_2_SysBG: colorimetry.primaries = GST_VIDEO_COLOR_PRIMARIES_BT470BG; break; case MFVideoPrimaries_SMPTE170M: colorimetry.primaries = GST_VIDEO_COLOR_PRIMARIES_SMPTE170M; break; case MFVideoPrimaries_SMPTE240M: colorimetry.primaries = GST_VIDEO_COLOR_PRIMARIES_SMPTE240M; break; case MFVideoPrimaries_EBU3213: colorimetry.primaries = GST_VIDEO_COLOR_PRIMARIES_EBU3213; break; case MFVideoPrimaries_BT2020: colorimetry.primaries = GST_VIDEO_COLOR_PRIMARIES_BT2020; break; default: GST_FIXME ("unhandled color primaries %d", val); break; } } hr = media_type->GetUINT32 (MF_MT_YUV_MATRIX, &val); if (SUCCEEDED (hr)) { switch (val) { case MFVideoTransferMatrix_BT709: colorimetry.matrix = GST_VIDEO_COLOR_MATRIX_BT709; break; case MFVideoTransferMatrix_BT601: colorimetry.matrix = GST_VIDEO_COLOR_MATRIX_BT601; break; case MFVideoTransferMatrix_SMPTE240M: colorimetry.matrix = GST_VIDEO_COLOR_MATRIX_SMPTE240M; break; case MFVideoTransferMatrix_BT2020_10: case MFVideoTransferMatrix_BT2020_12: colorimetry.matrix = GST_VIDEO_COLOR_MATRIX_BT2020; break; default: GST_FIXME ("unhandled color matrix %d", val); break; } } hr = media_type->GetUINT32 (MF_MT_TRANSFER_FUNCTION, &val); if (SUCCEEDED (hr)) { switch (val) { case MFVideoTransFunc_10: colorimetry.transfer = GST_VIDEO_TRANSFER_GAMMA10; break; case MFVideoTransFunc_18: colorimetry.transfer = GST_VIDEO_TRANSFER_GAMMA18; break; case MFVideoTransFunc_20: colorimetry.transfer = GST_VIDEO_TRANSFER_GAMMA20; break; case MFVideoTransFunc_22: colorimetry.transfer = GST_VIDEO_TRANSFER_GAMMA22; break; case MFVideoTransFunc_709: case MFVideoTransFunc_709_sym: colorimetry.transfer = GST_VIDEO_TRANSFER_BT709; break; case MFVideoTransFunc_240M: colorimetry.transfer = GST_VIDEO_TRANSFER_SMPTE240M; break; case MFVideoTransFunc_sRGB: colorimetry.transfer = GST_VIDEO_TRANSFER_SRGB; break; case MFVideoTransFunc_28: colorimetry.transfer = GST_VIDEO_TRANSFER_GAMMA28; break; case MFVideoTransFunc_Log_100: colorimetry.transfer = GST_VIDEO_TRANSFER_LOG100; break; case MFVideoTransFunc_Log_316: colorimetry.transfer = GST_VIDEO_TRANSFER_LOG316; break; case MFVideoTransFunc_2020_const: case MFVideoTransFunc_2020: colorimetry.transfer = GST_VIDEO_TRANSFER_BT2020_10; break; case MFVideoTransFunc_2084: colorimetry.transfer = GST_VIDEO_TRANSFER_SMPTE2084; break; case MFVideoTransFunc_HLG: colorimetry.transfer = GST_VIDEO_TRANSFER_ARIB_STD_B67; break; default: GST_FIXME ("unhandled color transfer %d", val); break; } } str = gst_video_colorimetry_to_string (&colorimetry); if (str) { gst_caps_set_simple (caps, "colorimetry", G_TYPE_STRING, str, nullptr); g_free (str); str = nullptr; } chroma_site = GST_VIDEO_CHROMA_SITE_UNKNOWN; hr = media_type->GetUINT32 (MF_MT_VIDEO_CHROMA_SITING, &val); if (SUCCEEDED (hr)) { gboolean known_value = TRUE; if ((val & MFVideoChromaSubsampling_MPEG2) == MFVideoChromaSubsampling_MPEG2) { chroma_site = GST_VIDEO_CHROMA_SITE_MPEG2; } else if ((val & MFVideoChromaSubsampling_DV_PAL) == MFVideoChromaSubsampling_DV_PAL) { chroma_site = GST_VIDEO_CHROMA_SITE_DV; } else if ((val & MFVideoChromaSubsampling_Cosited) == MFVideoChromaSubsampling_Cosited) { chroma_site = GST_VIDEO_CHROMA_SITE_COSITED; } else { known_value = FALSE; } GST_LOG ("have %s chroma site value 0x%x", known_value ? "known" : "unknown", val); } if (chroma_site != GST_VIDEO_CHROMA_SITE_UNKNOWN) gst_caps_set_simple (caps, "chroma-site", G_TYPE_STRING, gst_video_chroma_to_string (chroma_site), nullptr); return caps; } /* Desktop only defines */ #ifndef KSAUDIO_SPEAKER_MONO #define KSAUDIO_SPEAKER_MONO (SPEAKER_FRONT_CENTER) #endif #ifndef KSAUDIO_SPEAKER_1POINT1 #define KSAUDIO_SPEAKER_1POINT1 (SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY) #endif #ifndef KSAUDIO_SPEAKER_STEREO #define KSAUDIO_SPEAKER_STEREO (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT) #endif #ifndef KSAUDIO_SPEAKER_2POINT1 #define KSAUDIO_SPEAKER_2POINT1 (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_LOW_FREQUENCY) #endif #ifndef KSAUDIO_SPEAKER_3POINT0 #define KSAUDIO_SPEAKER_3POINT0 (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER) #endif #ifndef KSAUDIO_SPEAKER_3POINT1 #define KSAUDIO_SPEAKER_3POINT1 (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | \ SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY) #endif #ifndef KSAUDIO_SPEAKER_QUAD #define KSAUDIO_SPEAKER_QUAD (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | \ SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT) #endif #define KSAUDIO_SPEAKER_SURROUND (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | \ SPEAKER_FRONT_CENTER | SPEAKER_BACK_CENTER) #ifndef KSAUDIO_SPEAKER_5POINT0 #define KSAUDIO_SPEAKER_5POINT0 (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | \ SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT) #endif #define KSAUDIO_SPEAKER_5POINT1 (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | \ SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | \ SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT) #ifndef KSAUDIO_SPEAKER_7POINT0 #define KSAUDIO_SPEAKER_7POINT0 (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | \ SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | \ SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT) #endif #ifndef KSAUDIO_SPEAKER_7POINT1 #define KSAUDIO_SPEAKER_7POINT1 (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | \ SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | \ SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | \ SPEAKER_FRONT_LEFT_OF_CENTER | SPEAKER_FRONT_RIGHT_OF_CENTER) #endif /* *INDENT-OFF* */ static struct { guint64 mf_pos; GstAudioChannelPosition gst_pos; } mf_to_gst_pos[] = { {SPEAKER_FRONT_LEFT, GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT}, {SPEAKER_FRONT_RIGHT, GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT}, {SPEAKER_FRONT_CENTER, GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER}, {SPEAKER_LOW_FREQUENCY, GST_AUDIO_CHANNEL_POSITION_LFE1}, {SPEAKER_BACK_LEFT, GST_AUDIO_CHANNEL_POSITION_REAR_LEFT}, {SPEAKER_BACK_RIGHT, GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT}, {SPEAKER_FRONT_LEFT_OF_CENTER, GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER}, {SPEAKER_FRONT_RIGHT_OF_CENTER, GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER}, {SPEAKER_BACK_CENTER, GST_AUDIO_CHANNEL_POSITION_REAR_CENTER}, /* Enum values diverge from this point onwards */ {SPEAKER_SIDE_LEFT, GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT}, {SPEAKER_SIDE_RIGHT, GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT}, {SPEAKER_TOP_CENTER, GST_AUDIO_CHANNEL_POSITION_TOP_CENTER}, {SPEAKER_TOP_FRONT_LEFT, GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_LEFT}, {SPEAKER_TOP_FRONT_CENTER, GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_CENTER}, {SPEAKER_TOP_FRONT_RIGHT, GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_RIGHT}, {SPEAKER_TOP_BACK_LEFT, GST_AUDIO_CHANNEL_POSITION_TOP_REAR_LEFT}, {SPEAKER_TOP_BACK_CENTER, GST_AUDIO_CHANNEL_POSITION_TOP_REAR_CENTER}, {SPEAKER_TOP_BACK_RIGHT, GST_AUDIO_CHANNEL_POSITION_TOP_REAR_RIGHT} }; static DWORD default_ch_masks[] = { 0, KSAUDIO_SPEAKER_MONO, /* 2ch */ KSAUDIO_SPEAKER_STEREO, /* 2.1ch */ /* KSAUDIO_SPEAKER_3POINT0 ? */ KSAUDIO_SPEAKER_2POINT1, /* 4ch */ /* KSAUDIO_SPEAKER_3POINT1 or KSAUDIO_SPEAKER_SURROUND ? */ KSAUDIO_SPEAKER_QUAD, /* 5ch */ KSAUDIO_SPEAKER_5POINT0, /* 5.1ch */ KSAUDIO_SPEAKER_5POINT1, /* 7ch */ KSAUDIO_SPEAKER_7POINT0, /* 7.1ch */ KSAUDIO_SPEAKER_7POINT1, }; /* *INDENT-ON* */ static void gst_mf_media_audio_channel_mask_to_position (guint channels, DWORD mask, GstAudioChannelPosition * position) { guint i, ch; for (i = 0, ch = 0; i < G_N_ELEMENTS (mf_to_gst_pos) && ch < channels; i++) { if ((mask & mf_to_gst_pos[i].mf_pos) == 0) continue; position[ch] = mf_to_gst_pos[i].gst_pos; ch++; } } static GstCaps * gst_mf_media_type_to_audio_caps (IMFMediaType * media_type) { GUID subtype; HRESULT hr; UINT32 bps; GstAudioFormat format = GST_AUDIO_FORMAT_UNKNOWN; GstAudioInfo info; UINT32 rate, channels, mask; GstAudioChannelPosition position[64]; hr = media_type->GetGUID (MF_MT_SUBTYPE, &subtype); if (FAILED (hr)) { GST_WARNING ("failed to get subtype, hr: 0x%x", (guint) hr); return nullptr; } if (!IsEqualGUID (subtype, MFAudioFormat_PCM) && !IsEqualGUID (subtype, MFAudioFormat_Float)) { GST_FIXME ("Unknown subtype"); return nullptr; } hr = media_type->GetUINT32 (MF_MT_AUDIO_BITS_PER_SAMPLE, &bps); if (FAILED (hr)) { GST_WARNING ("Failed to get bps, hr: 0x%x", (guint) hr); return nullptr; } if (IsEqualGUID (subtype, MFAudioFormat_PCM)) { format = gst_audio_format_build_integer (TRUE, G_LITTLE_ENDIAN, bps, bps); } else if (bps == 32) { format = GST_AUDIO_FORMAT_F32LE; } else if (bps == 64) { format = GST_AUDIO_FORMAT_F64LE; } if (format == GST_AUDIO_FORMAT_UNKNOWN) { GST_WARNING ("Unknown audio format"); return nullptr; } hr = media_type->GetUINT32 (MF_MT_AUDIO_NUM_CHANNELS, &channels); if (FAILED (hr) || channels == 0) { GST_WARNING ("Unknown channels"); return nullptr; } hr = media_type->GetUINT32 (MF_MT_AUDIO_SAMPLES_PER_SECOND, &rate); if (FAILED (hr) || rate == 0) { GST_WARNING ("Unknown rate"); return nullptr; } for (guint i = 0; i < G_N_ELEMENTS (position); i++) position[i] = GST_AUDIO_CHANNEL_POSITION_NONE; hr = media_type->GetUINT32 (MF_MT_AUDIO_CHANNEL_MASK, &mask); if (FAILED (hr)) { if (channels == 1) { position[0] = GST_AUDIO_CHANNEL_POSITION_MONO; } else if (channels == 2) { position[0] = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT; position[1] = GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT; } else if (channels <= 8) { GST_WARNING ("Unknown channel position, use default value"); gst_mf_media_audio_channel_mask_to_position (channels, default_ch_masks[channels], position); } else { GST_WARNING ("Failed to determine channel position"); return nullptr; } } else { gst_mf_media_audio_channel_mask_to_position (channels, mask, position); } gst_audio_info_set_format (&info, format, rate, channels, position); return gst_audio_info_to_caps (&info); } GstCaps * gst_mf_media_type_to_caps (IMFMediaType * media_type) { GUID major_type; HRESULT hr; g_return_val_if_fail (media_type != nullptr, nullptr); hr = media_type->GetMajorType (&major_type); if (FAILED (hr)) { GST_WARNING ("failed to get major type, hr: 0x%x", (guint) hr); return nullptr; } if (IsEqualGUID (major_type, MFMediaType_Video)) { return gst_mf_media_type_to_video_caps (media_type); } else if (IsEqualGUID (major_type, MFMediaType_Audio)) { return gst_mf_media_type_to_audio_caps (media_type); } return nullptr; } void gst_mf_media_type_release (IMFMediaType * media_type) { if (media_type) media_type->Release (); } gboolean gst_mf_update_video_info_with_stride (GstVideoInfo * info, gint stride) { guint width, height, cr_h; g_return_val_if_fail (info != nullptr, FALSE); g_return_val_if_fail (stride > 0, FALSE); g_return_val_if_fail (GST_VIDEO_INFO_FORMAT (info) != GST_VIDEO_FORMAT_UNKNOWN, FALSE); if (GST_VIDEO_INFO_FORMAT (info) == GST_VIDEO_FORMAT_ENCODED) return TRUE; width = GST_VIDEO_INFO_WIDTH (info); height = GST_VIDEO_INFO_HEIGHT (info); /* copied from video-info */ switch (GST_VIDEO_INFO_FORMAT (info)) { /* RGB */ case GST_VIDEO_FORMAT_RGBx: case GST_VIDEO_FORMAT_RGBA: case GST_VIDEO_FORMAT_RGB16: case GST_VIDEO_FORMAT_BGR15: case GST_VIDEO_FORMAT_BGR: info->stride[0] = stride; info->offset[0] = 0; info->size = info->stride[0] * height; break; /* packed YUV */ case GST_VIDEO_FORMAT_YUY2: case GST_VIDEO_FORMAT_YVYU: case GST_VIDEO_FORMAT_UYVY: case GST_VIDEO_FORMAT_VUYA: info->stride[0] = stride; info->offset[0] = 0; info->size = info->stride[0] * height; break; /* semi-planar */ case GST_VIDEO_FORMAT_NV12: case GST_VIDEO_FORMAT_P010_10LE: case GST_VIDEO_FORMAT_P016_LE: if (height % 2) { GST_ERROR ("Height must be even number"); return FALSE; } cr_h = height / 2; info->stride[0] = stride; info->stride[1] = info->stride[0]; info->offset[0] = 0; info->offset[1] = info->stride[0] * height; info->size = info->offset[1] + info->stride[0] * cr_h; break; /* planar */ case GST_VIDEO_FORMAT_I420: case GST_VIDEO_FORMAT_YV12: if (stride % 2) { GST_ERROR ("Stride must be even number"); return FALSE; } if (height % 2) { GST_ERROR ("Height must be even number"); return FALSE; } cr_h = height / 2; info->stride[0] = stride; info->stride[1] = stride / 2; info->stride[2] = info->stride[1]; info->offset[0] = 0; info->offset[1] = info->stride[0] * height; info->offset[2] = info->offset[1] + info->stride[1] * cr_h; info->size = info->offset[2] + info->stride[2] * cr_h; break; /* complex */ case GST_VIDEO_FORMAT_v210: case GST_VIDEO_FORMAT_v216: info->stride[0] = stride; info->offset[0] = 0; info->size = info->stride[0] * height; break; /* gray */ case GST_VIDEO_FORMAT_GRAY16_LE: info->stride[0] = stride; info->offset[0] = 0; info->size = info->stride[0] * height; break; default: GST_ERROR ("Unhandled format %s", gst_video_format_to_string (GST_VIDEO_INFO_FORMAT (info))); return FALSE; } return TRUE; } gboolean _gst_mf_result (HRESULT hr, GstDebugCategory * cat, const gchar * file, const gchar * function, gint line) { #ifndef GST_DISABLE_GST_DEBUG gboolean ret = TRUE; if (FAILED (hr)) { gchar *error_text = nullptr; error_text = g_win32_error_message ((gint) hr); /* g_win32_error_message() doesn't cover all HERESULT return code, * so it could be empty string, or null if there was an error * in g_utf16_to_utf8() */ gst_debug_log (cat, GST_LEVEL_WARNING, file, function, line, nullptr, "MediaFoundation call failed: 0x%x, %s", (guint) hr, GST_STR_NULL (error_text)); g_free (error_text); ret = FALSE; } return ret; #else return SUCCEEDED (hr); #endif } /* Reference: * https://docs.microsoft.com/en-us/windows/win32/medfound/media-type-debugging-code */ #define GST_MF_IF_EQUAL_RETURN(guid,val) G_STMT_START { \ if (IsEqualGUID (guid, val)) \ return G_STRINGIFY (val); \ } G_STMT_END static const gchar * gst_mf_guid_to_static_string (const GUID & guid) { GST_MF_IF_EQUAL_RETURN (guid, MF_MT_MAJOR_TYPE); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_MAJOR_TYPE); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_SUBTYPE); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_ALL_SAMPLES_INDEPENDENT); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_FIXED_SIZE_SAMPLES); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_COMPRESSED); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_SAMPLE_SIZE); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_WRAPPED_TYPE); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_AUDIO_NUM_CHANNELS); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_AUDIO_SAMPLES_PER_SECOND); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_AUDIO_FLOAT_SAMPLES_PER_SECOND); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_AUDIO_AVG_BYTES_PER_SECOND); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_AUDIO_BLOCK_ALIGNMENT); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_AUDIO_BITS_PER_SAMPLE); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_AUDIO_VALID_BITS_PER_SAMPLE); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_AUDIO_SAMPLES_PER_BLOCK); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_AUDIO_CHANNEL_MASK); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_AUDIO_FOLDDOWN_MATRIX); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_AUDIO_WMADRC_PEAKREF); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_AUDIO_WMADRC_PEAKTARGET); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_AUDIO_WMADRC_AVGREF); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_AUDIO_WMADRC_AVGTARGET); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_AUDIO_PREFER_WAVEFORMATEX); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_AAC_PAYLOAD_TYPE); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_AAC_AUDIO_PROFILE_LEVEL_INDICATION); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_FRAME_SIZE); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_FRAME_RATE); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_FRAME_RATE_RANGE_MAX); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_FRAME_RATE_RANGE_MIN); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_PIXEL_ASPECT_RATIO); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_DRM_FLAGS); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_PAD_CONTROL_FLAGS); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_SOURCE_CONTENT_HINT); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_VIDEO_CHROMA_SITING); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_INTERLACE_MODE); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_TRANSFER_FUNCTION); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_VIDEO_PRIMARIES); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_YUV_MATRIX); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_VIDEO_LIGHTING); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_VIDEO_NOMINAL_RANGE); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_GEOMETRIC_APERTURE); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_MINIMUM_DISPLAY_APERTURE); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_PAN_SCAN_APERTURE); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_PAN_SCAN_ENABLED); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_AVG_BITRATE); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_AVG_BIT_ERROR_RATE); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_MAX_KEYFRAME_SPACING); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_DEFAULT_STRIDE); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_PALETTE); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_USER_DATA); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_MPEG_START_TIME_CODE); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_MPEG2_PROFILE); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_MPEG2_LEVEL); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_MPEG2_FLAGS); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_MPEG_SEQUENCE_HEADER); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_DV_AAUX_SRC_PACK_0); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_DV_AAUX_CTRL_PACK_0); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_DV_AAUX_SRC_PACK_1); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_DV_AAUX_CTRL_PACK_1); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_DV_VAUX_SRC_PACK); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_DV_VAUX_CTRL_PACK); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_IMAGE_LOSS_TOLERANT); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_MPEG4_SAMPLE_DESCRIPTION); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_MPEG4_CURRENT_SAMPLE_ENTRY); GST_MF_IF_EQUAL_RETURN (guid, MFMediaType_Audio); GST_MF_IF_EQUAL_RETURN (guid, MFMediaType_Video); GST_MF_IF_EQUAL_RETURN (guid, MFMediaType_Protected); GST_MF_IF_EQUAL_RETURN (guid, MFMediaType_SAMI); GST_MF_IF_EQUAL_RETURN (guid, MFMediaType_Script); GST_MF_IF_EQUAL_RETURN (guid, MFMediaType_Image); GST_MF_IF_EQUAL_RETURN (guid, MFMediaType_HTML); GST_MF_IF_EQUAL_RETURN (guid, MFMediaType_Binary); GST_MF_IF_EQUAL_RETURN (guid, MFMediaType_FileTransfer); GST_MF_IF_EQUAL_RETURN (guid, MFVideoFormat_AI44); GST_MF_IF_EQUAL_RETURN (guid, MFVideoFormat_ARGB32); GST_MF_IF_EQUAL_RETURN (guid, MFVideoFormat_AYUV); GST_MF_IF_EQUAL_RETURN (guid, MFVideoFormat_DV25); GST_MF_IF_EQUAL_RETURN (guid, MFVideoFormat_DV50); GST_MF_IF_EQUAL_RETURN (guid, MFVideoFormat_DVH1); GST_MF_IF_EQUAL_RETURN (guid, MFVideoFormat_DVSD); GST_MF_IF_EQUAL_RETURN (guid, MFVideoFormat_DVSL); GST_MF_IF_EQUAL_RETURN (guid, MFVideoFormat_H264); GST_MF_IF_EQUAL_RETURN (guid, MFVideoFormat_H265); GST_MF_IF_EQUAL_RETURN (guid, MFVideoFormat_HEVC); GST_MF_IF_EQUAL_RETURN (guid, MFVideoFormat_HEVC_ES); GST_MF_IF_EQUAL_RETURN (guid, MFVideoFormat_I420); GST_MF_IF_EQUAL_RETURN (guid, MFVideoFormat_IYUV); GST_MF_IF_EQUAL_RETURN (guid, MFVideoFormat_M4S2); GST_MF_IF_EQUAL_RETURN (guid, MFVideoFormat_MJPG); GST_MF_IF_EQUAL_RETURN (guid, MFVideoFormat_MP43); GST_MF_IF_EQUAL_RETURN (guid, MFVideoFormat_MP4S); GST_MF_IF_EQUAL_RETURN (guid, MFVideoFormat_MP4V); GST_MF_IF_EQUAL_RETURN (guid, MFVideoFormat_MPG1); GST_MF_IF_EQUAL_RETURN (guid, MFVideoFormat_MSS1); GST_MF_IF_EQUAL_RETURN (guid, MFVideoFormat_MSS2); GST_MF_IF_EQUAL_RETURN (guid, MFVideoFormat_NV11); GST_MF_IF_EQUAL_RETURN (guid, MFVideoFormat_NV12); GST_MF_IF_EQUAL_RETURN (guid, MFVideoFormat_P010); GST_MF_IF_EQUAL_RETURN (guid, MFVideoFormat_P016); GST_MF_IF_EQUAL_RETURN (guid, MFVideoFormat_P210); GST_MF_IF_EQUAL_RETURN (guid, MFVideoFormat_P216); GST_MF_IF_EQUAL_RETURN (guid, MFVideoFormat_RGB24); GST_MF_IF_EQUAL_RETURN (guid, MFVideoFormat_RGB32); GST_MF_IF_EQUAL_RETURN (guid, MFVideoFormat_RGB555); GST_MF_IF_EQUAL_RETURN (guid, MFVideoFormat_RGB565); GST_MF_IF_EQUAL_RETURN (guid, MFVideoFormat_RGB8); GST_MF_IF_EQUAL_RETURN (guid, MFVideoFormat_UYVY); GST_MF_IF_EQUAL_RETURN (guid, MFVideoFormat_v210); GST_MF_IF_EQUAL_RETURN (guid, MFVideoFormat_v410); GST_MF_IF_EQUAL_RETURN (guid, MFVideoFormat_VP80); GST_MF_IF_EQUAL_RETURN (guid, MFVideoFormat_VP90); GST_MF_IF_EQUAL_RETURN (guid, MFVideoFormat_WMV1); GST_MF_IF_EQUAL_RETURN (guid, MFVideoFormat_WMV2); GST_MF_IF_EQUAL_RETURN (guid, MFVideoFormat_WMV3); GST_MF_IF_EQUAL_RETURN (guid, MFVideoFormat_WVC1); GST_MF_IF_EQUAL_RETURN (guid, MFVideoFormat_Y210); GST_MF_IF_EQUAL_RETURN (guid, MFVideoFormat_Y216); GST_MF_IF_EQUAL_RETURN (guid, MFVideoFormat_Y410); GST_MF_IF_EQUAL_RETURN (guid, MFVideoFormat_Y416); GST_MF_IF_EQUAL_RETURN (guid, MFVideoFormat_Y41P); GST_MF_IF_EQUAL_RETURN (guid, MFVideoFormat_Y41T); GST_MF_IF_EQUAL_RETURN (guid, MFVideoFormat_YUY2); GST_MF_IF_EQUAL_RETURN (guid, MFVideoFormat_YV12); GST_MF_IF_EQUAL_RETURN (guid, MFVideoFormat_YVYU); /* WAVE_FORMAT_PCM */ GST_MF_IF_EQUAL_RETURN (guid, MFAudioFormat_PCM); /* WAVE_FORMAT_IEEE_FLOAT */ GST_MF_IF_EQUAL_RETURN (guid, MFAudioFormat_Float); /* WAVE_FORMAT_DTS */ GST_MF_IF_EQUAL_RETURN (guid, MFAudioFormat_DTS); /* WAVE_FORMAT_DOLBY_AC3_SPDIF */ GST_MF_IF_EQUAL_RETURN (guid, MFAudioFormat_Dolby_AC3_SPDIF); /* WAVE_FORMAT_DRM */ GST_MF_IF_EQUAL_RETURN (guid, MFAudioFormat_DRM); /* WAVE_FORMAT_WMAUDIO2 */ GST_MF_IF_EQUAL_RETURN (guid, MFAudioFormat_WMAudioV8); /* WAVE_FORMAT_WMAUDIO3 */ GST_MF_IF_EQUAL_RETURN (guid, MFAudioFormat_WMAudioV9); /* WAVE_FORMAT_WMAUDIO_LOSSLESS */ GST_MF_IF_EQUAL_RETURN (guid, MFAudioFormat_WMAudio_Lossless); /* WAVE_FORMAT_WMASPDIF */ GST_MF_IF_EQUAL_RETURN (guid, MFAudioFormat_WMASPDIF); /* WAVE_FORMAT_WMAVOICE9 */ GST_MF_IF_EQUAL_RETURN (guid, MFAudioFormat_MSP1); /* WAVE_FORMAT_MPEGLAYER3 */ GST_MF_IF_EQUAL_RETURN (guid, MFAudioFormat_MP3); /* WAVE_FORMAT_MPEG */ GST_MF_IF_EQUAL_RETURN (guid, MFAudioFormat_MPEG); /* WAVE_FORMAT_MPEG_HEAAC */ GST_MF_IF_EQUAL_RETURN (guid, MFAudioFormat_AAC); /* WAVE_FORMAT_MPEG_ADTS_AAC */ GST_MF_IF_EQUAL_RETURN (guid, MFAudioFormat_ADTS); #if GST_MF_WINAPI_DESKTOP GST_MF_IF_EQUAL_RETURN (guid, MF_MT_CUSTOM_VIDEO_PRIMARIES); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_AM_FORMAT_TYPE); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_ARBITRARY_HEADER); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_ARBITRARY_FORMAT); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_ORIGINAL_4CC); GST_MF_IF_EQUAL_RETURN (guid, MF_MT_ORIGINAL_WAVE_FORMAT_TAG); #endif return nullptr; } static gchar * gst_mf_guid_to_string (const GUID & guid) { const gchar *str = nullptr; HRESULT hr; WCHAR *name = nullptr; gchar *ret = nullptr; str = gst_mf_guid_to_static_string (guid); if (str) return g_strdup (str); hr = StringFromCLSID (guid, &name); if (gst_mf_result (hr) && name) { ret = g_utf16_to_utf8 ((const gunichar2 *) name, -1, nullptr, nullptr, nullptr); CoTaskMemFree (name); if (ret) return ret; } ret = g_strdup_printf ("%8.8x-%4.4x-%4.4x-%2.2x%2.2x-%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x", (guint) guid.Data1, (guint) guid.Data2, (guint) 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 ret; } static gchar * gst_mf_attribute_value_to_string (const GUID & guid, const PROPVARIANT & var) { if (IsEqualGUID (guid, MF_MT_FRAME_RATE) || IsEqualGUID (guid, MF_MT_FRAME_RATE_RANGE_MAX) || IsEqualGUID (guid, MF_MT_FRAME_RATE_RANGE_MIN) || IsEqualGUID (guid, MF_MT_FRAME_SIZE) || IsEqualGUID (guid, MF_MT_PIXEL_ASPECT_RATIO)) { UINT32 high = 0, low = 0; Unpack2UINT32AsUINT64 (var.uhVal.QuadPart, &high, &low); return g_strdup_printf ("%dx%d", high, low); } if (IsEqualGUID (guid, MF_MT_GEOMETRIC_APERTURE) || IsEqualGUID (guid, MF_MT_MINIMUM_DISPLAY_APERTURE) || IsEqualGUID (guid, MF_MT_PAN_SCAN_APERTURE)) { /* FIXME: Not our usecase for now */ return g_strdup ("Not parsed"); } switch (var.vt) { case VT_UI4: return g_strdup_printf ("%d", var.ulVal); case VT_UI8: return g_strdup_printf ("%" G_GUINT64_FORMAT, var.uhVal); case VT_R8: return g_strdup_printf ("%f", var.dblVal); case VT_CLSID: return gst_mf_guid_to_string (*var.puuid); case VT_LPWSTR: return g_utf16_to_utf8 ((const gunichar2 *) var.pwszVal, -1, nullptr, nullptr, nullptr); case VT_UNKNOWN: return g_strdup ("IUnknown"); default: return g_strdup_printf ("Unhandled type (vt = %d)", var.vt); } return nullptr; } static void gst_mf_dump_attribute_value_by_index (IMFAttributes * attr, const gchar * msg, guint index, GstDebugLevel level, GstDebugCategory * cat, const gchar * file, const gchar * function, gint line) { gchar *guid_name = nullptr; gchar *value = nullptr; GUID guid = GUID_NULL; HRESULT hr; PROPVARIANT var; PropVariantInit (&var); hr = attr->GetItemByIndex (index, &guid, &var); if (!gst_mf_result (hr)) goto done; guid_name = gst_mf_guid_to_string (guid); if (!guid_name) goto done; value = gst_mf_attribute_value_to_string (guid, var); if (!value) goto done; gst_debug_log (cat, level, file, function, line, nullptr, "%s attribute %d, %s: %s", msg ? msg : "", index, guid_name, value); done: PropVariantClear (&var); g_free (guid_name); g_free (value); } void _gst_mf_dump_attributes (IMFAttributes * attr, const gchar * msg, GstDebugLevel level, GstDebugCategory * cat, const gchar * file, const gchar * function, gint line) { #ifndef GST_DISABLE_GST_DEBUG HRESULT hr; UINT32 count = 0, i; if (!attr) return; hr = attr->GetCount (&count); if (!gst_mf_result (hr) || count == 0) return; for (i = 0; i < count; i++) { gst_mf_dump_attribute_value_by_index (attr, msg, i, level, cat, file, function, line); } #endif }