gstreamer/sys/d3d11/gstd3d11format.c
Seungha Yang f9f6916aa1 d3d11window: Reorganize display colorspace selection
The set of supported color space by DXGI is not full combination of
our colorimetry. That means we should convert color space to one
of supported color space by DXGI. This commit modifies the color space
selection step so that d3d11window can find the best matching DXGI color space
first and then the selected input/output color space will be referenced
by shader and/or d3d11videoprocessor.
2020-03-28 11:45:48 +00:00

494 lines
15 KiB
C

/* GStreamer
* Copyright (C) 2019 Seungha Yang <seungha.yang@navercorp.com>
*
* 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 "gstd3d11format.h"
#include "gstd3d11utils.h"
#include "gstd3d11device.h"
#include "gstd3d11memory.h"
#include <string.h>
GST_DEBUG_CATEGORY_EXTERN (gst_d3d11_format_debug);
#define GST_CAT_DEFAULT gst_d3d11_format_debug
guint
gst_d3d11_dxgi_format_n_planes (DXGI_FORMAT format)
{
switch (format) {
case DXGI_FORMAT_B8G8R8A8_UNORM:
case DXGI_FORMAT_R8G8B8A8_UNORM:
case DXGI_FORMAT_R10G10B10A2_UNORM:
case DXGI_FORMAT_AYUV:
case DXGI_FORMAT_R8_UNORM:
case DXGI_FORMAT_R8G8_UNORM:
case DXGI_FORMAT_R16_UNORM:
case DXGI_FORMAT_R16G16_UNORM:
return 1;
case DXGI_FORMAT_NV12:
case DXGI_FORMAT_P010:
case DXGI_FORMAT_P016:
return 2;
default:
break;
}
return 0;
}
gboolean
gst_d3d11_dxgi_format_get_size (DXGI_FORMAT format, guint width, guint height,
guint pitch, gsize offset[GST_VIDEO_MAX_PLANES],
gint stride[GST_VIDEO_MAX_PLANES], gsize * size)
{
g_return_val_if_fail (format != DXGI_FORMAT_UNKNOWN, FALSE);
switch (format) {
case DXGI_FORMAT_B8G8R8A8_UNORM:
case DXGI_FORMAT_R8G8B8A8_UNORM:
case DXGI_FORMAT_R10G10B10A2_UNORM:
case DXGI_FORMAT_AYUV:
case DXGI_FORMAT_R8_UNORM:
case DXGI_FORMAT_R8G8_UNORM:
case DXGI_FORMAT_R16_UNORM:
case DXGI_FORMAT_R16G16_UNORM:
offset[0] = 0;
stride[0] = pitch;
*size = pitch * height;
break;
case DXGI_FORMAT_NV12:
case DXGI_FORMAT_P010:
case DXGI_FORMAT_P016:
offset[0] = 0;
stride[0] = pitch;
offset[1] = offset[0] + stride[0] * height;
stride[1] = pitch;
*size = offset[1] + stride[1] * GST_ROUND_UP_2 (height / 2);
break;
default:
return FALSE;
}
GST_LOG ("Calculated buffer size: %" G_GSIZE_FORMAT
" (dxgi format:%d, %dx%d, Pitch %d)",
*size, format, width, height, pitch);
return TRUE;
}
/**
* gst_d3d11_device_get_supported_caps:
* @device: a #GstD3DDevice
* @flags: D3D11_FORMAT_SUPPORT flags
*
* Check supported format with given flags
*
* Returns: a #GstCaps representing supported format
*/
GstCaps *
gst_d3d11_device_get_supported_caps (GstD3D11Device * device,
D3D11_FORMAT_SUPPORT flags)
{
ID3D11Device *d3d11_device;
HRESULT hr;
gint i;
GValue v_list = G_VALUE_INIT;
GstCaps *supported_caps;
static const GstVideoFormat format_list[] = {
GST_VIDEO_FORMAT_BGRA,
GST_VIDEO_FORMAT_RGBA,
GST_VIDEO_FORMAT_RGB10A2_LE,
GST_VIDEO_FORMAT_VUYA,
GST_VIDEO_FORMAT_NV12,
GST_VIDEO_FORMAT_P010_10LE,
GST_VIDEO_FORMAT_P016_LE,
GST_VIDEO_FORMAT_I420,
GST_VIDEO_FORMAT_I420_10LE,
};
g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), NULL);
d3d11_device = gst_d3d11_device_get_device_handle (device);
g_value_init (&v_list, GST_TYPE_LIST);
for (i = 0; i < G_N_ELEMENTS (format_list); i++) {
UINT format_support = 0;
GstVideoFormat format;
const GstD3D11Format *d3d11_format;
d3d11_format = gst_d3d11_device_format_from_gst (device, format_list[i]);
if (!d3d11_format || d3d11_format->dxgi_format == DXGI_FORMAT_UNKNOWN)
continue;
format = d3d11_format->format;
hr = ID3D11Device_CheckFormatSupport (d3d11_device,
d3d11_format->dxgi_format, &format_support);
if (SUCCEEDED (hr) && ((format_support & flags) == flags)) {
GValue v_str = G_VALUE_INIT;
g_value_init (&v_str, G_TYPE_STRING);
GST_LOG_OBJECT (device, "d3d11 device can support %s with flags 0x%x",
gst_video_format_to_string (format), flags);
g_value_set_string (&v_str, gst_video_format_to_string (format));
gst_value_list_append_and_take_value (&v_list, &v_str);
}
}
supported_caps = gst_caps_new_simple ("video/x-raw",
"width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
"height", GST_TYPE_INT_RANGE, 1, G_MAXINT,
"framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
gst_caps_set_value (supported_caps, "format", &v_list);
g_value_unset (&v_list);
gst_caps_set_features_simple (supported_caps,
gst_caps_features_from_string (GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY));
return supported_caps;
}
#if (DXGI_HEADER_VERSION >= 5)
static inline UINT16
fraction_to_uint (guint num, guint den, guint scale)
{
gdouble val;
gst_util_fraction_to_double (num, den, &val);
return (UINT16) val *scale;
}
gboolean
gst_d3d11_hdr_meta_data_to_dxgi (GstVideoMasteringDisplayInfo * minfo,
GstVideoContentLightLevel * cll, DXGI_HDR_METADATA_HDR10 * dxgi_hdr10)
{
g_return_val_if_fail (dxgi_hdr10 != NULL, FALSE);
memset (dxgi_hdr10, 0, sizeof (DXGI_HDR_METADATA_HDR10));
if (minfo) {
dxgi_hdr10->RedPrimary[0] =
fraction_to_uint (minfo->Rx_n, minfo->Rx_d, 50000);
dxgi_hdr10->RedPrimary[1] =
fraction_to_uint (minfo->Ry_n, minfo->Ry_d, 50000);
dxgi_hdr10->GreenPrimary[0] =
fraction_to_uint (minfo->Gx_n, minfo->Gx_d, 50000);
dxgi_hdr10->GreenPrimary[1] =
fraction_to_uint (minfo->Gy_n, minfo->Gy_d, 50000);
dxgi_hdr10->BluePrimary[0] =
fraction_to_uint (minfo->Bx_n, minfo->Bx_d, 50000);
dxgi_hdr10->BluePrimary[1] =
fraction_to_uint (minfo->By_n, minfo->By_d, 50000);
dxgi_hdr10->WhitePoint[0] =
fraction_to_uint (minfo->Wx_n, minfo->Wx_d, 50000);
dxgi_hdr10->WhitePoint[1] =
fraction_to_uint (minfo->Wy_n, minfo->Wy_d, 50000);
dxgi_hdr10->MaxMasteringLuminance =
fraction_to_uint (minfo->max_luma_n, minfo->max_luma_d, 1);
dxgi_hdr10->MinMasteringLuminance =
fraction_to_uint (minfo->min_luma_n, minfo->min_luma_d, 1);
}
if (cll) {
dxgi_hdr10->MaxContentLightLevel =
fraction_to_uint (cll->maxCLL_n, cll->maxCLL_d, 1);
dxgi_hdr10->MaxFrameAverageLightLevel =
fraction_to_uint (cll->maxFALL_n, cll->maxFALL_d, 1);
}
return TRUE;
}
#endif
#if (DXGI_HEADER_VERSION >= 4)
typedef enum
{
GST_DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709 = 0,
GST_DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709 = 1,
GST_DXGI_COLOR_SPACE_RGB_STUDIO_G22_NONE_P709 = 2,
GST_DXGI_COLOR_SPACE_RGB_STUDIO_G22_NONE_P2020 = 3,
GST_DXGI_COLOR_SPACE_RESERVED = 4,
GST_DXGI_COLOR_SPACE_YCBCR_FULL_G22_NONE_P709_X601 = 5,
GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P601 = 6,
GST_DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P601 = 7,
GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709 = 8,
GST_DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P709 = 9,
GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P2020 = 10,
GST_DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P2020 = 11,
GST_DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020 = 12,
GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_G2084_LEFT_P2020 = 13,
GST_DXGI_COLOR_SPACE_RGB_STUDIO_G2084_NONE_P2020 = 14,
GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_TOPLEFT_P2020 = 15,
GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_G2084_TOPLEFT_P2020 = 16,
GST_DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P2020 = 17,
GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_GHLG_TOPLEFT_P2020 = 18,
GST_DXGI_COLOR_SPACE_YCBCR_FULL_GHLG_TOPLEFT_P2020 = 19,
GST_DXGI_COLOR_SPACE_RGB_STUDIO_G24_NONE_P709 = 20,
GST_DXGI_COLOR_SPACE_RGB_STUDIO_G24_NONE_P2020 = 21,
GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_G24_LEFT_P709 = 22,
GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_G24_LEFT_P2020 = 23,
GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_G24_TOPLEFT_P2020 = 24,
GST_DXGI_COLOR_SPACE_CUSTOM = 0xFFFFFFFF
} GST_DXGI_COLOR_SPACE_TYPE;
/* https://docs.microsoft.com/en-us/windows/win32/api/dxgicommon/ne-dxgicommon-dxgi_color_space_type */
#define MAKE_COLOR_MAP(d,r,m,t,p) \
{ GST_DXGI_COLOR_SPACE_ ##d, GST_VIDEO_COLOR_RANGE ##r, \
GST_VIDEO_COLOR_MATRIX_ ##m, GST_VIDEO_TRANSFER_ ##t, \
GST_VIDEO_COLOR_PRIMARIES_ ##p }
static const GstDxgiColorSpace rgb_colorspace_map[] = {
/* RGB_FULL_G22_NONE_P709 */
MAKE_COLOR_MAP (RGB_FULL_G22_NONE_P709, _0_255, UNKNOWN, BT709, BT709),
/* RGB_FULL_G10_NONE_P709 */
MAKE_COLOR_MAP (RGB_FULL_G22_NONE_P709, _0_255, UNKNOWN, GAMMA10, BT709),
/* RGB_STUDIO_G22_NONE_P709 */
MAKE_COLOR_MAP (RGB_FULL_G22_NONE_P709, _16_235, UNKNOWN, BT709, BT709),
/* RGB_STUDIO_G22_NONE_P2020 */
MAKE_COLOR_MAP (RGB_FULL_G22_NONE_P709, _16_235, UNKNOWN, BT2020_10, BT2020),
MAKE_COLOR_MAP (RGB_FULL_G22_NONE_P709, _16_235, UNKNOWN, BT2020_12, BT2020),
/* RGB_FULL_G2084_NONE_P2020 */
MAKE_COLOR_MAP (RGB_FULL_G2084_NONE_P2020, _0_255, UNKNOWN, SMPTE2084,
BT2020),
/* RGB_STUDIO_G2084_NONE_P2020 */
MAKE_COLOR_MAP (RGB_STUDIO_G2084_NONE_P2020,
_16_235, UNKNOWN, SMPTE2084, BT2020),
/* RGB_FULL_G22_NONE_P2020 */
MAKE_COLOR_MAP (RGB_FULL_G22_NONE_P2020, _0_255, UNKNOWN, BT2020_10, BT2020),
MAKE_COLOR_MAP (RGB_FULL_G22_NONE_P2020, _0_255, UNKNOWN, BT2020_12, BT2020),
/* RGB_STUDIO_G24_NONE_P709 */
MAKE_COLOR_MAP (RGB_STUDIO_G24_NONE_P709, _16_235, UNKNOWN, SRGB, BT709),
/* RGB_STUDIO_G24_NONE_P2020 */
MAKE_COLOR_MAP (RGB_STUDIO_G24_NONE_P709, _16_235, UNKNOWN, SRGB, BT2020),
};
static const GstDxgiColorSpace yuv_colorspace_map[] = {
/* YCBCR_FULL_G22_NONE_P709_X601 */
MAKE_COLOR_MAP (YCBCR_FULL_G22_NONE_P709_X601, _0_255, BT601, BT709, BT709),
/* YCBCR_STUDIO_G22_LEFT_P601 */
MAKE_COLOR_MAP (YCBCR_STUDIO_G22_LEFT_P601, _16_235, BT601, BT709, SMPTE170M),
/* YCBCR_FULL_G22_LEFT_P601 */
MAKE_COLOR_MAP (YCBCR_FULL_G22_LEFT_P601, _0_255, BT601, BT709, SMPTE170M),
/* YCBCR_STUDIO_G22_LEFT_P709 */
MAKE_COLOR_MAP (YCBCR_STUDIO_G22_LEFT_P709, _16_235, BT709, BT709, BT709),
/* YCBCR_FULL_G22_LEFT_P709 */
MAKE_COLOR_MAP (YCBCR_FULL_G22_LEFT_P709, _0_255, BT709, BT709, BT709),
/* YCBCR_STUDIO_G22_LEFT_P2020 */
MAKE_COLOR_MAP (YCBCR_STUDIO_G22_LEFT_P2020, _16_235, BT2020, BT2020_10,
BT2020),
MAKE_COLOR_MAP (YCBCR_STUDIO_G22_LEFT_P2020, _16_235, BT2020, BT2020_12,
BT2020),
/* YCBCR_FULL_G22_LEFT_P2020 */
MAKE_COLOR_MAP (YCBCR_FULL_G22_LEFT_P2020, _0_255, BT2020, BT2020_10, BT2020),
MAKE_COLOR_MAP (YCBCR_FULL_G22_LEFT_P2020, _0_255, BT2020, BT2020_12, BT2020),
/* YCBCR_STUDIO_G2084_LEFT_P2020 */
MAKE_COLOR_MAP (YCBCR_STUDIO_G2084_LEFT_P2020, _16_235, BT2020, SMPTE2084,
BT2020),
/* YCBCR_STUDIO_G22_TOPLEFT_P2020 */
MAKE_COLOR_MAP (YCBCR_STUDIO_G22_TOPLEFT_P2020, _16_235, BT2020, BT2020_10,
BT2020),
MAKE_COLOR_MAP (YCBCR_STUDIO_G22_TOPLEFT_P2020, _16_235, BT2020, BT2020_12,
BT2020),
/* YCBCR_STUDIO_G2084_TOPLEFT_P2020 */
/* FIXME: check chroma-site to differentiate this from
* YCBCR_STUDIO_G2084_LEFT_P2020 */
MAKE_COLOR_MAP (YCBCR_STUDIO_G2084_TOPLEFT_P2020, _16_235, BT2020, SMPTE2084,
BT2020),
/* YCBCR_STUDIO_GHLG_TOPLEFT_P2020 */
MAKE_COLOR_MAP (YCBCR_STUDIO_GHLG_TOPLEFT_P2020, _16_235, BT2020,
ARIB_STD_B67, BT2020),
/* YCBCR_STUDIO_GHLG_TOPLEFT_P2020 */
MAKE_COLOR_MAP (YCBCR_FULL_GHLG_TOPLEFT_P2020, _0_255, BT2020, ARIB_STD_B67,
BT2020),
/* YCBCR_STUDIO_G24_LEFT_P709 */
MAKE_COLOR_MAP (YCBCR_STUDIO_G22_LEFT_P709, _16_235, BT709, SRGB, BT709),
/* YCBCR_STUDIO_G24_LEFT_P2020 */
MAKE_COLOR_MAP (YCBCR_STUDIO_G24_LEFT_P2020, _16_235, BT2020, SRGB, BT2020),
/* YCBCR_STUDIO_G24_TOPLEFT_P2020 */
/* FIXME: check chroma-site to differentiate this from
* YCBCR_STUDIO_G24_LEFT_P2020 */
MAKE_COLOR_MAP (YCBCR_STUDIO_G24_TOPLEFT_P2020, _16_235, BT2020, SRGB,
BT2020),
};
#define SCORE_RANGE_MISMATCH 1
#define SCORE_MATRIX_MISMATCH 5
#define SCORE_TRANSFER_MISMATCH 5
#define SCORE_PRIMARY_MISMATCH 10
static gint
get_score (GstVideoInfo * info, const GstDxgiColorSpace * color_map,
gboolean is_yuv)
{
gint loss = 0;
GstVideoColorimetry *color = &info->colorimetry;
if (color->range != color_map->range)
loss += SCORE_RANGE_MISMATCH;
if (is_yuv && color->matrix != color_map->matrix)
loss += SCORE_MATRIX_MISMATCH;
if (color->transfer != color_map->transfer)
loss += SCORE_TRANSFER_MISMATCH;
if (color->primaries != color_map->primaries)
loss += SCORE_PRIMARY_MISMATCH;
return loss;
}
static const GstDxgiColorSpace *
gst_d3d11_video_info_to_dxgi_color_space_rgb (GstVideoInfo * info)
{
gint best_score = G_MAXINT;
gint score, i;
const GstDxgiColorSpace *colorspace = NULL;
for (i = 0; i < G_N_ELEMENTS (rgb_colorspace_map); i++) {
score = get_score (info, &rgb_colorspace_map[i], TRUE);
if (score < best_score) {
best_score = score;
colorspace = &rgb_colorspace_map[i];
if (score == 0)
break;
}
}
return colorspace;
}
static const GstDxgiColorSpace *
gst_d3d11_video_info_to_dxgi_color_space_yuv (GstVideoInfo * info)
{
gint best_score = G_MAXINT;
gint score, i;
const GstDxgiColorSpace *colorspace = NULL;
for (i = 0; i < G_N_ELEMENTS (yuv_colorspace_map); i++) {
score = get_score (info, &yuv_colorspace_map[i], TRUE);
if (score < best_score) {
best_score = score;
colorspace = &yuv_colorspace_map[i];
if (score == 0)
break;
}
}
return colorspace;
}
const GstDxgiColorSpace *
gst_d3d11_video_info_to_dxgi_color_space (GstVideoInfo * info)
{
g_return_val_if_fail (info != NULL, NULL);
if (GST_VIDEO_INFO_IS_RGB (info)) {
return gst_d3d11_video_info_to_dxgi_color_space_rgb (info);
} else if (GST_VIDEO_INFO_IS_YUV (info)) {
return gst_d3d11_video_info_to_dxgi_color_space_yuv (info);
}
return NULL;
}
const GstDxgiColorSpace *
gst_d3d11_find_swap_chain_color_space (GstVideoInfo * info,
IDXGISwapChain3 * swapchain, gboolean use_hdr10)
{
const GstDxgiColorSpace *colorspace = NULL;
gint best_score = G_MAXINT;
gint i;
g_return_val_if_fail (info != NULL, FALSE);
g_return_val_if_fail (swapchain != NULL, FALSE);
if (!GST_VIDEO_INFO_IS_RGB (info)) {
GST_WARNING ("Swapchain colorspace should be RGB format");
return FALSE;
}
for (i = 0; i < G_N_ELEMENTS (rgb_colorspace_map); i++) {
UINT can_support = 0;
HRESULT hr;
gint score;
GST_DXGI_COLOR_SPACE_TYPE cur_type =
rgb_colorspace_map[i].dxgi_color_space_type;
/* FIXME: Non-HDR colorspace with BT2020 primaries will break rendering.
* https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/issues/1175
* To workaround it, BT709 colorspace will be chosen for non-HDR case.
*/
if (!use_hdr10 &&
rgb_colorspace_map[i].primaries == GST_VIDEO_COLOR_PRIMARIES_BT2020)
continue;
hr = IDXGISwapChain3_CheckColorSpaceSupport (swapchain,
cur_type, &can_support);
if (FAILED (hr))
continue;
if ((can_support & DXGI_SWAP_CHAIN_COLOR_SPACE_SUPPORT_FLAG_PRESENT) ==
DXGI_SWAP_CHAIN_COLOR_SPACE_SUPPORT_FLAG_PRESENT) {
score = get_score (info, &rgb_colorspace_map[i], FALSE);
GST_DEBUG ("colorspace %d supported, score %d", cur_type, score);
if (score < best_score) {
best_score = score;
colorspace = &rgb_colorspace_map[i];
}
}
}
return colorspace;
}
#endif