浏览代码

libobs: Add better default source color range handling

Fixes handling of the `obs_source_frame::full_range` member variable,
which is often set to false by default by many plugins even when using
RGB, which would cause RGB to be marked as "partial range".  This change
is crucial for when partial range RBG support is implemented.

Adds `obs_source_frame2` structure that replaces the `full_range` member
variable with a `range` variable, which uses the `video_range_type` enum
to allow handling default range values.  This member variable treats
VIDEO_RANGE_DEFAULT as full range if the format is RGB, and partial
range if the format is YUV.

Also adds `obs_source_output_video2` and `obs_source_preload_video2`
functions which use the `obs_source_frame2` structure instead of the
`obs_source_frame` structure.

When using the original `obs_source_frame`, `obs_source_output_video`,
and `obs_source_preload_video` functions, RGB will always be full range
by default for backward compatibility purposes.
jp9000 6 年之前
父节点
当前提交
8d125dc01d
共有 4 个文件被更改,包括 155 次插入11 次删除
  1. 14 6
      libobs/media-io/video-io.h
  2. 102 2
      libobs/obs-source.c
  3. 2 1
      libobs/obs.c
  4. 37 2
      libobs/obs.h

+ 14 - 6
libobs/media-io/video-io.h

@@ -135,15 +135,23 @@ static inline const char *get_video_colorspace_name(enum video_colorspace cs)
 	return "601";
 }
 
-static inline const char *get_video_range_name(enum video_range_type range)
+static inline enum video_range_type resolve_video_range(
+		enum video_format format, enum video_range_type range)
 {
-	switch (range) {
-	case VIDEO_RANGE_FULL: return "Full";
-	case VIDEO_RANGE_PARTIAL:
-	case VIDEO_RANGE_DEFAULT:;
+	if (range == VIDEO_RANGE_DEFAULT) {
+		range = format_is_yuv(format)
+			? VIDEO_RANGE_PARTIAL
+			: VIDEO_RANGE_FULL;
 	}
 
-	return "Partial";
+	return range;
+}
+
+static inline const char *get_video_range_name(enum video_format format,
+		enum video_range_type range)
+{
+	range = resolve_video_range(format, range);
+	return range == VIDEO_RANGE_FULL ? "Full" : "Partial";
 }
 
 enum video_scale_type {

+ 102 - 2
libobs/obs-source.c

@@ -2436,7 +2436,7 @@ static inline struct obs_source_frame *cache_video(struct obs_source *source,
 	return new_frame;
 }
 
-void obs_source_output_video(obs_source_t *source,
+static void obs_source_output_video_internal(obs_source_t *source,
 		const struct obs_source_frame *frame)
 {
 	if (!obs_source_valid(source, "obs_source_output_video"))
@@ -2464,6 +2464,56 @@ void obs_source_output_video(obs_source_t *source,
 	pthread_mutex_unlock(&source->async_mutex);
 }
 
+void obs_source_output_video(obs_source_t *source,
+		const struct obs_source_frame *frame)
+{
+	if (!frame) {
+		obs_source_output_video_internal(source, NULL);
+		return;
+	}
+
+	struct obs_source_frame new_frame = *frame;
+	new_frame.full_range = format_is_yuv(frame->format)
+		? new_frame.full_range
+		: true;
+
+	obs_source_output_video_internal(source, &new_frame);
+}
+
+void obs_source_output_video2(obs_source_t *source,
+		const struct obs_source_frame2 *frame)
+{
+	if (!frame) {
+		obs_source_output_video_internal(source, NULL);
+		return;
+	}
+
+	struct obs_source_frame new_frame;
+	enum video_range_type range = resolve_video_range(frame->format,
+			frame->range);
+
+	for (size_t i = 0; i < MAX_AV_PLANES; i++) {
+		new_frame.data[i] = frame->data[i];
+		new_frame.linesize[i] = frame->linesize[i];
+	}
+
+	new_frame.width = frame->width;
+	new_frame.height = frame->height;
+	new_frame.timestamp = frame->timestamp;
+	new_frame.format = frame->format;
+	new_frame.full_range = range == VIDEO_RANGE_FULL;
+	new_frame.flip = frame->flip;
+
+	memcpy(&new_frame.color_matrix, &frame->color_matrix,
+			sizeof(frame->color_matrix));
+	memcpy(&new_frame.color_range_min, &frame->color_range_min,
+			sizeof(frame->color_range_min));
+	memcpy(&new_frame.color_range_max, &frame->color_range_max,
+			sizeof(frame->color_range_max));
+
+	obs_source_output_video_internal(source, &new_frame);
+}
+
 static inline bool preload_frame_changed(obs_source_t *source,
 		const struct obs_source_frame *in)
 {
@@ -2475,7 +2525,7 @@ static inline bool preload_frame_changed(obs_source_t *source,
 	       in->format != source->async_preload_frame->format;
 }
 
-void obs_source_preload_video(obs_source_t *source,
+static void obs_source_preload_video_internal(obs_source_t *source,
 		const struct obs_source_frame *frame)
 {
 	if (!obs_source_valid(source, "obs_source_preload_video"))
@@ -2504,6 +2554,56 @@ void obs_source_preload_video(obs_source_t *source,
 	obs_leave_graphics();
 }
 
+void obs_source_preload_video(obs_source_t *source,
+		const struct obs_source_frame *frame)
+{
+	if (!frame) {
+		obs_source_preload_video_internal(source, NULL);
+		return;
+	}
+
+	struct obs_source_frame new_frame = *frame;
+	new_frame.full_range = format_is_yuv(frame->format)
+		? new_frame.full_range
+		: true;
+
+	obs_source_preload_video_internal(source, &new_frame);
+}
+
+void obs_source_preload_video2(obs_source_t *source,
+		const struct obs_source_frame2 *frame)
+{
+	if (!frame) {
+		obs_source_preload_video_internal(source, NULL);
+		return;
+	}
+
+	struct obs_source_frame new_frame;
+	enum video_range_type range = resolve_video_range(frame->format,
+			frame->range);
+
+	for (size_t i = 0; i < MAX_AV_PLANES; i++) {
+		new_frame.data[i] = frame->data[i];
+		new_frame.linesize[i] = frame->linesize[i];
+	}
+
+	new_frame.width = frame->width;
+	new_frame.height = frame->height;
+	new_frame.timestamp = frame->timestamp;
+	new_frame.format = frame->format;
+	new_frame.full_range = range == VIDEO_RANGE_FULL;
+	new_frame.flip = frame->flip;
+
+	memcpy(&new_frame.color_matrix, &frame->color_matrix,
+			sizeof(frame->color_matrix));
+	memcpy(&new_frame.color_range_min, &frame->color_range_min,
+			sizeof(frame->color_range_min));
+	memcpy(&new_frame.color_range_max, &frame->color_range_max,
+			sizeof(frame->color_range_max));
+
+	obs_source_preload_video_internal(source, &new_frame);
+}
+
 void obs_source_show_preloaded_video(obs_source_t *source)
 {
 	uint64_t sys_ts;

+ 2 - 1
libobs/obs.c

@@ -1129,7 +1129,8 @@ int obs_reset_video(struct obs_video_info *ovi)
 
 	bool yuv = format_is_yuv(ovi->output_format);
 	const char *yuv_format = get_video_colorspace_name(ovi->colorspace);
-	const char *yuv_range = get_video_range_name(ovi->range);
+	const char *yuv_range = get_video_range_name(ovi->output_format,
+			ovi->range);
 
 	blog(LOG_INFO, "---------------------------------");
 	blog(LOG_INFO, "video settings reset:\n"

+ 37 - 2
libobs/obs.h

@@ -220,6 +220,10 @@ struct obs_source_audio {
  *
  * If a YUV format is specified, it will be automatically upsampled and
  * converted to RGB via shader on the graphics processor.
+ *
+ * NOTE: Non-YUV formats will always be treated as full range with this
+ * structure!  Use obs_source_frame2 along with obs_source_output_video2
+ * instead if partial range support is desired for non-YUV video formats.
  */
 struct obs_source_frame {
 	uint8_t             *data[MAX_AV_PLANES];
@@ -240,6 +244,21 @@ struct obs_source_frame {
 	bool                prev_frame;
 };
 
+struct obs_source_frame2 {
+	uint8_t               *data[MAX_AV_PLANES];
+	uint32_t              linesize[MAX_AV_PLANES];
+	uint32_t              width;
+	uint32_t              height;
+	uint64_t              timestamp;
+
+	enum video_format     format;
+	enum video_range_type range;
+	float                 color_matrix[16];
+	float                 color_range_min[3];
+	float                 color_range_max[3];
+	bool                  flip;
+};
+
 /** Access to the argc/argv used to start OBS. What you see is what you get. */
 struct obs_cmdline_args {
 	int argc;
@@ -1117,13 +1136,29 @@ EXPORT void obs_source_draw_set_color_matrix(
 EXPORT void obs_source_draw(gs_texture_t *image, int x, int y,
 		uint32_t cx, uint32_t cy, bool flip);
 
-/** Outputs asynchronous video data.  Set to NULL to deactivate the texture */
+/**
+ * Outputs asynchronous video data.  Set to NULL to deactivate the texture
+ *
+ * NOTE: Non-YUV formats will always be treated as full range with this
+ * function!  Use obs_source_output_video2 instead if partial range support is
+ * desired for non-YUV video formats.
+ */
 EXPORT void obs_source_output_video(obs_source_t *source,
 		const struct obs_source_frame *frame);
+EXPORT void obs_source_output_video2(obs_source_t *source,
+		const struct obs_source_frame2 *frame);
 
-/** Preloads asynchronous video data to allow instantaneous playback */
+/**
+ * Preloads asynchronous video data to allow instantaneous playback
+ *
+ * NOTE: Non-YUV formats will always be treated as full range with this
+ * function!  Use obs_source_preload_video2 instead if partial range support is
+ * desired for non-YUV video formats.
+ */
 EXPORT void obs_source_preload_video(obs_source_t *source,
 		const struct obs_source_frame *frame);
+EXPORT void obs_source_preload_video2(obs_source_t *source,
+		const struct obs_source_frame2 *frame);
 
 /** Shows any preloaded video data */
 EXPORT void obs_source_show_preloaded_video(obs_source_t *source);