Browse Source

libobs: Add encoder ROI functions

Rodney 1 year ago
parent
commit
d96ad4ac98
4 changed files with 136 additions and 0 deletions
  1. 92 0
      libobs/obs-encoder.c
  2. 18 0
      libobs/obs-encoder.h
  3. 5 0
      libobs/obs-internal.h
  4. 21 0
      libobs/obs.h

+ 92 - 0
libobs/obs-encoder.c

@@ -52,6 +52,7 @@ static bool init_encoder(struct obs_encoder *encoder, const char *name,
 	pthread_mutex_init_value(&encoder->callbacks_mutex);
 	pthread_mutex_init_value(&encoder->callbacks_mutex);
 	pthread_mutex_init_value(&encoder->outputs_mutex);
 	pthread_mutex_init_value(&encoder->outputs_mutex);
 	pthread_mutex_init_value(&encoder->pause.mutex);
 	pthread_mutex_init_value(&encoder->pause.mutex);
+	pthread_mutex_init_value(&encoder->roi_mutex);
 
 
 	if (!obs_context_data_init(&encoder->context, OBS_OBJ_TYPE_ENCODER,
 	if (!obs_context_data_init(&encoder->context, OBS_OBJ_TYPE_ENCODER,
 				   settings, name, NULL, hotkey_data, false))
 				   settings, name, NULL, hotkey_data, false))
@@ -64,6 +65,8 @@ static bool init_encoder(struct obs_encoder *encoder, const char *name,
 		return false;
 		return false;
 	if (pthread_mutex_init(&encoder->pause.mutex, NULL) != 0)
 	if (pthread_mutex_init(&encoder->pause.mutex, NULL) != 0)
 		return false;
 		return false;
+	if (pthread_mutex_init(&encoder->roi_mutex, NULL) != 0)
+		return false;
 
 
 	if (encoder->orig_info.get_defaults) {
 	if (encoder->orig_info.get_defaults) {
 		encoder->orig_info.get_defaults(encoder->context.settings);
 		encoder->orig_info.get_defaults(encoder->context.settings);
@@ -377,10 +380,12 @@ static void obs_encoder_actually_destroy(obs_encoder_t *encoder)
 		if (encoder->context.data)
 		if (encoder->context.data)
 			encoder->info.destroy(encoder->context.data);
 			encoder->info.destroy(encoder->context.data);
 		da_free(encoder->callbacks);
 		da_free(encoder->callbacks);
+		da_free(encoder->roi);
 		pthread_mutex_destroy(&encoder->init_mutex);
 		pthread_mutex_destroy(&encoder->init_mutex);
 		pthread_mutex_destroy(&encoder->callbacks_mutex);
 		pthread_mutex_destroy(&encoder->callbacks_mutex);
 		pthread_mutex_destroy(&encoder->outputs_mutex);
 		pthread_mutex_destroy(&encoder->outputs_mutex);
 		pthread_mutex_destroy(&encoder->pause.mutex);
 		pthread_mutex_destroy(&encoder->pause.mutex);
+		pthread_mutex_destroy(&encoder->roi_mutex);
 		obs_context_data_free(&encoder->context);
 		obs_context_data_free(&encoder->context);
 		if (encoder->owns_info_id)
 		if (encoder->owns_info_id)
 			bfree((void *)encoder->info.id);
 			bfree((void *)encoder->info.id);
@@ -1874,3 +1879,90 @@ uint64_t obs_encoder_get_pause_offset(const obs_encoder_t *encoder)
 {
 {
 	return encoder ? encoder->pause.ts_offset : 0;
 	return encoder ? encoder->pause.ts_offset : 0;
 }
 }
+
+bool obs_encoder_has_roi(const obs_encoder_t *encoder)
+{
+	return encoder->roi.num > 0;
+}
+
+bool obs_encoder_add_roi(obs_encoder_t *encoder,
+			 const struct obs_encoder_roi *roi)
+{
+	if (!roi)
+		return false;
+	if (!(encoder->info.caps & OBS_ENCODER_CAP_ROI))
+		return false;
+	/* Area smaller than the smallest possible block (16x16) */
+	if (roi->bottom - roi->top < 16 || roi->right - roi->left < 16)
+		return false;
+	/* Other invalid ROIs */
+	if (roi->priority < -1.0f || roi->priority > 1.0f)
+		return false;
+
+	pthread_mutex_lock(&encoder->roi_mutex);
+	da_push_back(encoder->roi, roi);
+	encoder->roi_increment++;
+	pthread_mutex_unlock(&encoder->roi_mutex);
+
+	return true;
+}
+
+void obs_encoder_clear_roi(obs_encoder_t *encoder)
+{
+	if (!encoder->roi.num)
+		return;
+	pthread_mutex_lock(&encoder->roi_mutex);
+	da_clear(encoder->roi);
+	encoder->roi_increment++;
+	pthread_mutex_unlock(&encoder->roi_mutex);
+}
+
+void obs_encoder_enum_roi(obs_encoder_t *encoder,
+			  void (*enum_proc)(void *, struct obs_encoder_roi *),
+			  void *param)
+{
+	float scale_x = 0;
+	float scale_y = 0;
+
+	/* Scale ROI passed to callback to output size */
+	if (encoder->scaled_height && encoder->scaled_width) {
+		const uint32_t width = video_output_get_width(encoder->media);
+		const uint32_t height = video_output_get_height(encoder->media);
+
+		if (!width || !height)
+			return;
+
+		scale_x = (float)encoder->scaled_width / (float)width;
+		scale_y = (float)encoder->scaled_height / (float)height;
+	}
+
+	pthread_mutex_lock(&encoder->roi_mutex);
+
+	size_t idx = encoder->roi.num;
+	while (idx) {
+		struct obs_encoder_roi *roi = &encoder->roi.array[--idx];
+
+		if (scale_x > 0 && scale_y > 0) {
+			struct obs_encoder_roi scaled_roi = {
+				.top = (uint32_t)((float)roi->top * scale_y),
+				.bottom = (uint32_t)((float)roi->bottom *
+						     scale_y),
+				.left = (uint32_t)((float)roi->left * scale_x),
+				.right =
+					(uint32_t)((float)roi->right * scale_x),
+				.priority = roi->priority,
+			};
+
+			enum_proc(param, &scaled_roi);
+		} else {
+			enum_proc(param, roi);
+		}
+	}
+
+	pthread_mutex_unlock(&encoder->roi_mutex);
+}
+
+uint32_t obs_encoder_get_roi_increment(const obs_encoder_t *encoder)
+{
+	return encoder->roi_increment;
+}

+ 18 - 0
libobs/obs-encoder.h

@@ -33,6 +33,7 @@ extern "C" {
 #define OBS_ENCODER_CAP_PASS_TEXTURE (1 << 1)
 #define OBS_ENCODER_CAP_PASS_TEXTURE (1 << 1)
 #define OBS_ENCODER_CAP_DYN_BITRATE (1 << 2)
 #define OBS_ENCODER_CAP_DYN_BITRATE (1 << 2)
 #define OBS_ENCODER_CAP_INTERNAL (1 << 3)
 #define OBS_ENCODER_CAP_INTERNAL (1 << 3)
+#define OBS_ENCODER_CAP_ROI (1 << 4)
 
 
 /** Specifies the encoder type */
 /** Specifies the encoder type */
 enum obs_encoder_type {
 enum obs_encoder_type {
@@ -102,6 +103,23 @@ struct encoder_frame {
 	int64_t pts;
 	int64_t pts;
 };
 };
 
 
+/** Encoder region of interest */
+struct obs_encoder_roi {
+	/* The rectangle edges of the region are specified as number of pixels
+	 * from the input video's top and left edges (i.e. row/column 0). */
+	uint32_t top;
+	uint32_t bottom;
+	uint32_t left;
+	uint32_t right;
+
+	/* Priority is specified as a float value between -1 and 1.
+	 * These are converted to encoder-specific values by the encoder.
+	 * Values above 0 tell the encoder to increase quality for that region,
+	 * values below tell it to worsen it.
+	 * Not all encoders support negative values and they may be ignored. */
+	float priority;
+};
+
 /**
 /**
  * Encoder interface
  * Encoder interface
  *
  *

+ 5 - 0
libobs/obs-internal.h

@@ -1263,6 +1263,11 @@ struct obs_encoder {
 	uint32_t frame_rate_divisor_counter; // only used for GPU encoders
 	uint32_t frame_rate_divisor_counter; // only used for GPU encoders
 	video_t *fps_override;
 	video_t *fps_override;
 
 
+	/* Regions of interest to prioritize during encoding */
+	pthread_mutex_t roi_mutex;
+	DARRAY(struct obs_encoder_roi) roi;
+	uint32_t roi_increment;
+
 	int64_t cur_pts;
 	int64_t cur_pts;
 
 
 	struct circlebuf audio_input_buffer[MAX_AV_PLANES];
 	struct circlebuf audio_input_buffer[MAX_AV_PLANES];

+ 21 - 0
libobs/obs.h

@@ -2457,6 +2457,27 @@ EXPORT void obs_encoder_set_gpu_scale_type(obs_encoder_t *encoder,
 EXPORT bool obs_encoder_set_frame_rate_divisor(obs_encoder_t *encoder,
 EXPORT bool obs_encoder_set_frame_rate_divisor(obs_encoder_t *encoder,
 					       uint32_t divisor);
 					       uint32_t divisor);
 
 
+/**
+ * Adds region of interest (ROI) for an encoder. This allows prioritizing
+ * quality of regions of the frame.
+ * If regions overlap, regions added earlier take precedence.
+ *
+ * Returns false if the encoder does not support ROI or region is invalid.
+ */
+EXPORT bool obs_encoder_add_roi(obs_encoder_t *encoder,
+				const struct obs_encoder_roi *roi);
+/** For video encoders, returns true if any ROIs were set */
+EXPORT bool obs_encoder_has_roi(const obs_encoder_t *encoder);
+/** Clear all regions */
+EXPORT void obs_encoder_clear_roi(obs_encoder_t *encoder);
+/** Enumerate regions with callback (reverse order of addition) */
+EXPORT void obs_encoder_enum_roi(obs_encoder_t *encoder,
+				 void (*enum_proc)(void *,
+						   struct obs_encoder_roi *),
+				 void *param);
+/** Get ROI increment, encoders must rebuild their ROI map if it has changed */
+EXPORT uint32_t obs_encoder_get_roi_increment(const obs_encoder_t *encoder);
+
 /** For video encoders, returns true if pre-encode scaling is enabled */
 /** For video encoders, returns true if pre-encode scaling is enabled */
 EXPORT bool obs_encoder_scaling_enabled(const obs_encoder_t *encoder);
 EXPORT bool obs_encoder_scaling_enabled(const obs_encoder_t *encoder);