1
0
Эх сурвалжийг харах

libobs: Add video_format_get_parameters_for_format

Video matrix is slightly different based on the number of bits per
channel, so derive from video_format for convenience.
jpark37 3 жил өмнө
parent
commit
94fa6d064b

+ 13 - 0
docs/sphinx/reference-libobs-media-io.rst

@@ -131,6 +131,19 @@ Video Handler
 
 ---------------------
 
+.. function:: bool video_format_get_parameters_for_format(enum video_colorspace color_space, enum video_range_type range, enum video_format format, float matrix[16], float min_range[3], float max_range[3])
+
+   Converts a color space/range to matrix/min/max values for a given video format.
+
+   :param color_space: Color space to convert
+   :param range:       Color range to convert
+   :param format:      Video format
+   :param matrix:      Pointer to the matrix
+   :param min_range:   Pointer to get the minimum range value
+   :param max_range:   Pointer to get the maximum range value
+
+---------------------
+
 .. function:: bool video_output_connect(video_t *video, const struct video_scale_info *conversion, void (*callback)(void *param, struct video_data *frame), void *param)
 
    Connects a raw video callback to the video output handler.

+ 4 - 0
libobs/media-io/video-io.h

@@ -250,6 +250,10 @@ EXPORT bool video_format_get_parameters(enum video_colorspace color_space,
 					enum video_range_type range,
 					float matrix[16], float min_range[3],
 					float max_range[3]);
+EXPORT bool video_format_get_parameters_for_format(
+	enum video_colorspace color_space, enum video_range_type range,
+	enum video_format format, float matrix[16], float min_range[3],
+	float max_range[3]);
 
 #define VIDEO_OUTPUT_SUCCESS 0
 #define VIDEO_OUTPUT_INVALIDPARAM -1

+ 165 - 126
libobs/media-io/video-matrices.c

@@ -15,83 +15,49 @@
     along with this program.  If not, see <http://www.gnu.org/licenses/>.
 ******************************************************************************/
 
-#include "../util/bmem.h"
 #include "video-io.h"
 
-//#define COMPUTE_MATRICES
-
-#ifdef COMPUTE_MATRICES
+#include "../util/bmem.h"
 #include "../graphics/matrix3.h"
-#endif
+
+#include <assert.h>
+
+//#define LOG_MATRICES
 
 static struct {
-	enum video_colorspace const color_space;
-	float const Kb, Kr;
-	int const range_min[3];
-	int const range_max[3];
-	int const black_levels[2][3];
+	float range_min[3];
+	float range_max[3];
+	float black_levels[2][3];
 
 	float float_range_min[3];
 	float float_range_max[3];
-	float matrix[2][16];
+} bpp_info[9];
 
+static struct {
+	enum video_colorspace const color_space;
+	float const Kb, Kr;
+	float matrix[OBS_COUNTOF(bpp_info)][2][16];
 } format_info[] = {
-	{VIDEO_CS_601,
-	 0.114f,
-	 0.299f,
-	 {16, 16, 16},
-	 {235, 240, 240},
-	 {{16, 128, 128}, {0, 128, 128}},
-#ifndef COMPUTE_MATRICES
-	 {16.0f / 255.0f, 16.0f / 255.0f, 16.0f / 255.0f},
-	 {235.0f / 255.0f, 240.0f / 255.0f, 240.0f / 255.0f},
-	 {{1.164384f, 0.000000f, 1.596027f, -0.874202f, 1.164384f, -0.391762f,
-	   -0.812968f, 0.531668f, 1.164384f, 2.017232f, 0.000000f, -1.085631f,
-	   0.000000f, 0.000000f, 0.000000f, 1.000000f},
-	  {1.000000f, 0.000000f, 1.407520f, -0.706520f, 1.000000f, -0.345491f,
-	   -0.716948f, 0.533303f, 1.000000f, 1.778976f, 0.000000f, -0.892976f,
-	   0.000000f, 0.000000f, 0.000000f, 1.000000f}}
-#endif
+	{
+		VIDEO_CS_601,
+		0.114f,
+		0.299f,
 	},
-	{VIDEO_CS_709,
-	 0.0722f,
-	 0.2126f,
-	 {16, 16, 16},
-	 {235, 240, 240},
-	 {{16, 128, 128}, {0, 128, 128}},
-#ifndef COMPUTE_MATRICES
-	 {16.0f / 255.0f, 16.0f / 255.0f, 16.0f / 255.0f},
-	 {235.0f / 255.0f, 240.0f / 255.0f, 240.0f / 255.0f},
-	 {{1.164384f, 0.000000f, 1.792741f, -0.972945f, 1.164384f, -0.213249f,
-	   -0.532909f, 0.301483f, 1.164384f, 2.112402f, 0.000000f, -1.133402f,
-	   0.000000f, 0.000000f, 0.000000f, 1.000000f},
-	  {1.000000f, 0.000000f, 1.581000f, -0.793600f, 1.000000f, -0.188062f,
-	   -0.469967f, 0.330305f, 1.000000f, 1.862906f, 0.000000f, -0.935106f,
-	   0.000000f, 0.000000f, 0.000000f, 1.000000f}}
-#endif
+	{
+		VIDEO_CS_709,
+		0.0722f,
+		0.2126f,
 	},
-	{VIDEO_CS_2020_PQ,
-	 0.0593f,
-	 0.2627f,
-	 {64, 64, 64},
-	 {940, 960, 960},
-	 {{64, 512, 512}, {0, 512, 512}},
-#ifndef COMPUTE_MATRICES
-	 {64.0f / 1023.0f, 64.0f / 1023.0f, 64.0f / 1023.0f},
-	 {940.0f / 1023.0f, 960.0f / 1023.0f, 960.0f / 1023.0f},
-	 {{1.167808f, 0.000000f, 1.683611f, -0.915688f, 1.167808f, -0.187877f,
-	   -0.652337f, 0.347459f, 1.167808f, 2.148072f, 0.000000f, -1.148145f,
-	   0.000000f, 0.000000f, 0.000000f, 1.000000f},
-	  {1.000000f, 0.000000f, 1.476043f, -0.738743f, 1.000000f, -0.164714f,
-	   -0.571912f, 0.368673f, 1.000000f, 1.883241f, 0.000000f, -0.942541f,
-	   0.000000f, 0.000000f, 0.000000f, 1.000000f}}
-#endif
+	{
+		VIDEO_CS_2020_PQ,
+		0.0593f,
+		0.2627f,
 	},
 };
 
 #define NUM_FORMATS (sizeof(format_info) / sizeof(format_info[0]))
 
-#ifdef COMPUTE_MATRICES
+#ifdef LOG_MATRICES
 static void log_matrix(float const matrix[16])
 {
 	blog(LOG_DEBUG,
@@ -103,25 +69,29 @@ static void log_matrix(float const matrix[16])
 	     matrix[6], matrix[7], matrix[8], matrix[9], matrix[10], matrix[11],
 	     matrix[12], matrix[13], matrix[14], matrix[15]);
 }
+#endif
 
 static void initialize_matrix(float const Kb, float const Kr,
-			      float bit_range_max, int const range_min[3],
-			      int const range_max[3], int const black_levels[3],
-			      float matrix[16])
+			      float bit_range_max, float const range_min[3],
+			      float const range_max[3],
+			      float const black_levels[3], float matrix[16])
 {
 	struct matrix3 color_matrix;
 
-	int yvals = range_max[0] - range_min[0];
-	int uvals = (range_max[1] - range_min[1]) / 2;
-	int vvals = (range_max[2] - range_min[2]) / 2;
+	float const yvals = range_max[0] - range_min[0];
+	float const uvals = (range_max[1] - range_min[1]) / 2.f;
+	float const vvals = (range_max[2] - range_min[2]) / 2.f;
 
-	vec3_set(&color_matrix.x, bit_range_max / yvals, 0.,
-		 bit_range_max / vvals * (1.f - Kr));
-	vec3_set(&color_matrix.y, bit_range_max / yvals,
-		 bit_range_max / uvals * (Kb - 1.f) * Kb / (1.f - Kb - Kr),
-		 bit_range_max / vvals * (Kr - 1.f) * Kr / (1.f - Kb - Kr));
-	vec3_set(&color_matrix.z, bit_range_max / yvals,
-		 bit_range_max / uvals * (1.f - Kb), 0.);
+	float const yscale = bit_range_max / yvals;
+	float const uscale = bit_range_max / uvals;
+	float const vscale = bit_range_max / vvals;
+
+	float const Kg = (1.f - Kb - Kr);
+
+	vec3_set(&color_matrix.x, yscale, 0.f, vscale * (1.f - Kr));
+	vec3_set(&color_matrix.y, yscale, uscale * (Kb - 1.f) * Kb / Kg,
+		 vscale * (Kr - 1.f) * Kr / Kg);
+	vec3_set(&color_matrix.z, yscale, uscale * (1.f - Kb), 0.f);
 
 	struct vec3 offsets, multiplied;
 	vec3_set(&offsets, -black_levels[0] / bit_range_max,
@@ -147,87 +117,156 @@ static void initialize_matrix(float const Kb, float const Kr,
 	matrix[12] = matrix[13] = matrix[14] = 0.;
 	matrix[15] = 1.;
 
+#ifdef LOG_MATRICES
 	log_matrix(matrix);
+#endif
 }
 
 static void initialize_matrices()
 {
-	static const int range_min[] = {0, 0, 0};
-	static const int range_max_8bit[] = {255, 255, 255};
-	static const int range_max_10bit[] = {1023, 1023, 1023};
-
-	for (size_t i = 0; i < NUM_FORMATS; i++) {
-		const int *range_max =
-			(format_info[i].color_space == VIDEO_CS_2020_PQ)
-				? range_max_10bit
-				: range_max_8bit;
-		float f_r_max = (float)range_max[0];
-		initialize_matrix(format_info[i].Kb, format_info[i].Kr, f_r_max,
-				  range_min, range_max,
-				  format_info[i].black_levels[1],
-				  format_info[i].matrix[1]);
-
-		initialize_matrix(format_info[i].Kb, format_info[i].Kr, f_r_max,
-				  format_info[i].range_min,
-				  format_info[i].range_max,
-				  format_info[i].black_levels[0],
-				  format_info[i].matrix[0]);
-
-		for (int j = 0; j < 3; j++) {
-			format_info[i].float_range_min[j] =
-				format_info[i].range_min[j] / f_r_max;
-			format_info[i].float_range_max[j] =
-				format_info[i].range_max[j] / f_r_max;
+	static const float full_range_min3[] = {0, 0, 0};
+
+	float min_value = 16.f;
+	float max_luma = 235.f;
+	float max_chroma = 240.f;
+	float range = 256.f;
+	for (uint32_t bpp = 8; bpp <= 16; ++bpp) {
+		const uint32_t bpp_index = bpp - 8;
+		bpp_info[bpp_index].range_min[0] = min_value;
+		bpp_info[bpp_index].range_min[1] = min_value;
+		bpp_info[bpp_index].range_min[2] = min_value;
+		bpp_info[bpp_index].range_max[0] = max_luma;
+		bpp_info[bpp_index].range_max[1] = max_chroma;
+		bpp_info[bpp_index].range_max[2] = max_chroma;
+		const float mid_chroma = 0.5f * (min_value + max_chroma);
+		bpp_info[bpp_index].black_levels[0][0] = min_value;
+		bpp_info[bpp_index].black_levels[0][1] = mid_chroma;
+		bpp_info[bpp_index].black_levels[0][2] = mid_chroma;
+		bpp_info[bpp_index].black_levels[1][0] = 0.f;
+		bpp_info[bpp_index].black_levels[1][1] = mid_chroma;
+		bpp_info[bpp_index].black_levels[1][2] = mid_chroma;
+		const float range_max = range - 1.f;
+		bpp_info[bpp_index].float_range_min[0] = min_value / range_max;
+		bpp_info[bpp_index].float_range_min[1] = min_value / range_max;
+		bpp_info[bpp_index].float_range_min[2] = min_value / range_max;
+		bpp_info[bpp_index].float_range_max[0] = max_luma / range_max;
+		bpp_info[bpp_index].float_range_max[1] = max_chroma / range_max;
+		bpp_info[bpp_index].float_range_max[2] = max_chroma / range_max;
+
+		for (size_t i = 0; i < NUM_FORMATS; i++) {
+			float full_range_max3[] = {range_max, range_max,
+						   range_max};
+			initialize_matrix(format_info[i].Kb, format_info[i].Kr,
+					  range_max, full_range_min3,
+					  full_range_max3,
+					  bpp_info[bpp_index].black_levels[1],
+					  format_info[i].matrix[bpp_index][1]);
+
+			initialize_matrix(format_info[i].Kb, format_info[i].Kr,
+					  range_max,
+					  bpp_info[bpp_index].range_min,
+					  bpp_info[bpp_index].range_max,
+					  bpp_info[bpp_index].black_levels[0],
+					  format_info[i].matrix[bpp_index][0]);
 		}
+
+		min_value *= 2.f;
+		max_luma *= 2.f;
+		max_chroma *= 2.f;
+		range *= 2.f;
 	}
 }
 
 static bool matrices_initialized = false;
-#endif
 
 static const float full_min[3] = {0.0f, 0.0f, 0.0f};
 static const float full_max[3] = {1.0f, 1.0f, 1.0f};
 
-bool video_format_get_parameters(enum video_colorspace color_space,
-				 enum video_range_type range, float matrix[16],
-				 float range_min[3], float range_max[3])
+static bool video_format_get_parameters_for_bpc(
+	enum video_colorspace color_space, enum video_range_type range,
+	float matrix[16], float range_min[3], float range_max[3], uint32_t bpc)
 {
-#ifdef COMPUTE_MATRICES
 	if (!matrices_initialized) {
 		initialize_matrices();
 		matrices_initialized = true;
 	}
-#endif
+
 	if ((color_space == VIDEO_CS_DEFAULT) || (color_space == VIDEO_CS_SRGB))
 		color_space = VIDEO_CS_709;
 	else if (color_space == VIDEO_CS_2020_HLG)
 		color_space = VIDEO_CS_2020_PQ;
 
+	if (bpc < 8)
+		bpc = 8;
+	if (bpc > 16)
+		bpc = 16;
+	const uint32_t bpc_index = bpc - 8;
+	assert(bpc_index < OBS_COUNTOF(bpp_info));
+
+	bool success = false;
+
 	for (size_t i = 0; i < NUM_FORMATS; i++) {
-		if (format_info[i].color_space != color_space)
-			continue;
-
-		int full_range = range == VIDEO_RANGE_FULL ? 1 : 0;
-		memcpy(matrix, format_info[i].matrix[full_range],
-		       sizeof(float) * 16);
-
-		if (range == VIDEO_RANGE_FULL) {
-			if (range_min)
-				memcpy(range_min, full_min, sizeof(float) * 3);
-			if (range_max)
-				memcpy(range_max, full_max, sizeof(float) * 3);
-			return true;
+		success = format_info[i].color_space == color_space;
+		if (success) {
+			const bool full_range = range == VIDEO_RANGE_FULL;
+			memcpy(matrix,
+			       format_info[i].matrix[bpc_index][full_range],
+			       sizeof(float) * 16);
+
+			if (range_min) {
+				const float *src_range_min =
+					full_range ? full_min
+						   : bpp_info[bpc_index]
+							     .float_range_min;
+				memcpy(range_min, src_range_min,
+				       sizeof(float) * 3);
+			}
+
+			if (range_max) {
+				const float *src_range_max =
+					full_range ? full_max
+						   : bpp_info[bpc_index]
+							     .float_range_max;
+				memcpy(range_max, src_range_max,
+				       sizeof(float) * 3);
+			}
+
+			break;
 		}
+	}
 
-		if (range_min)
-			memcpy(range_min, format_info[i].float_range_min,
-			       sizeof(float) * 3);
+	return success;
+}
+
+bool video_format_get_parameters(enum video_colorspace color_space,
+				 enum video_range_type range, float matrix[16],
+				 float range_min[3], float range_max[3])
+{
+	uint32_t bpc = 8;
+	switch (color_space) {
+	case VIDEO_CS_2020_PQ:
+	case VIDEO_CS_2020_HLG:
+		bpc = 10;
+	}
 
-		if (range_max)
-			memcpy(range_max, format_info[i].float_range_max,
-			       sizeof(float) * 3);
+	return video_format_get_parameters_for_bpc(color_space, range, matrix,
+						   range_min, range_max, bpc);
+}
 
-		return true;
+bool video_format_get_parameters_for_format(enum video_colorspace color_space,
+					    enum video_range_type range,
+					    enum video_format format,
+					    float matrix[16],
+					    float range_min[3],
+					    float range_max[3])
+{
+	uint32_t bpc = 8;
+	switch (format) {
+	case VIDEO_FORMAT_I010:
+	case VIDEO_FORMAT_P010:
+		bpc = 10;
 	}
-	return false;
+
+	return video_format_get_parameters_for_bpc(color_space, range, matrix,
+						   range_min, range_max, bpc);
 }