Skip to content

Commit

Permalink
Merge pull request obsproject#10039 from derrod/roi-libobs
Browse files Browse the repository at this point in the history
libobs/plugins: Add region of interest (ROI) encoder feature
  • Loading branch information
Lain-B authored Jan 14, 2024
2 parents 71d963a + 0510d67 commit 1e09f5a
Show file tree
Hide file tree
Showing 13 changed files with 605 additions and 18 deletions.
58 changes: 58 additions & 0 deletions docs/sphinx/reference-encoders.rst
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ Encoder Definition Structure (obs_encoder_info)
values:

- **OBS_ENCODER_CAP_DEPRECATED** - Encoder is deprecated
- **OBS_ENCODER_CAP_ROI** - Encoder supports region of interest feature


Encoder Packet Structure (encoder_packet)
Expand Down Expand Up @@ -265,6 +266,27 @@ Raw Frame Data Structure (encoder_frame)
Presentation timestamp.


Encoder Region of Interest Structure (obs_encoder_roi)
-----------------------------------------

.. struct:: obs_encoder_roi

Encoder region of interest structure.

.. member:: uint32_t top
uint32_t bottom
uint32_t left
uint32_t right

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).

.. member:: float priority

Priority is specified as a float value between *-1.0f* 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.

General Encoder Functions
-------------------------

Expand Down Expand Up @@ -517,6 +539,42 @@ General Encoder Functions

---------------------

.. function:: bool obs_encoder_add_roi(obs_encoder_t *encoder, const struct obs_encoder_roi *roi)

Adds a new region of interest to the encoder if ROI feature is supported.

:return: *true* if adding succeeded, *false* otherwise.

---------------------

.. function:: bool obs_encoder_has_roi(obs_encoder_t *encoder)

:return: *true* if encoder has ROI regions set, *false* otherwise.

---------------------

.. function:: void obs_encoder_clear_roi(obs_encoder_t *encoder)

Clear region of interest list, if any.

---------------------

.. function:: void obs_encoder_enum_roi(obs_encoder_t *encoder, void (*enum_proc)(void *, struct obs_encoder_roi *), void *param)

Enumerate currently configured ROIs by invoking callback for each entry, in reverse order of addition (i.e. most recent to oldest).

**Note:** If the encoder has scaling enabled the struct passed to the callback will be scaled accordingly.

---------------------

.. function:: uint32_t obs_encoder_get_roi_increment(const obs_encoder_t *encoder)

Encoders shall refresh their ROI configuration if the increment value changes.

:return: Increment/revision of ROI list

---------------------


Functions used by encoders
--------------------------
Expand Down
92 changes: 92 additions & 0 deletions libobs/obs-encoder.c
Original file line number Diff line number Diff line change
Expand Up @@ -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->outputs_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,
settings, name, NULL, hotkey_data, false))
Expand All @@ -64,6 +65,8 @@ static bool init_encoder(struct obs_encoder *encoder, const char *name,
return false;
if (pthread_mutex_init(&encoder->pause.mutex, NULL) != 0)
return false;
if (pthread_mutex_init(&encoder->roi_mutex, NULL) != 0)
return false;

if (encoder->orig_info.get_defaults) {
encoder->orig_info.get_defaults(encoder->context.settings);
Expand Down Expand Up @@ -377,10 +380,12 @@ static void obs_encoder_actually_destroy(obs_encoder_t *encoder)
if (encoder->context.data)
encoder->info.destroy(encoder->context.data);
da_free(encoder->callbacks);
da_free(encoder->roi);
pthread_mutex_destroy(&encoder->init_mutex);
pthread_mutex_destroy(&encoder->callbacks_mutex);
pthread_mutex_destroy(&encoder->outputs_mutex);
pthread_mutex_destroy(&encoder->pause.mutex);
pthread_mutex_destroy(&encoder->roi_mutex);
obs_context_data_free(&encoder->context);
if (encoder->owns_info_id)
bfree((void *)encoder->info.id);
Expand Down Expand Up @@ -1874,3 +1879,90 @@ uint64_t obs_encoder_get_pause_offset(const obs_encoder_t *encoder)
{
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 changes: 18 additions & 0 deletions libobs/obs-encoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ extern "C" {
#define OBS_ENCODER_CAP_PASS_TEXTURE (1 << 1)
#define OBS_ENCODER_CAP_DYN_BITRATE (1 << 2)
#define OBS_ENCODER_CAP_INTERNAL (1 << 3)
#define OBS_ENCODER_CAP_ROI (1 << 4)

/** Specifies the encoder type */
enum obs_encoder_type {
Expand Down Expand Up @@ -102,6 +103,23 @@ struct encoder_frame {
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
*
Expand Down
5 changes: 5 additions & 0 deletions libobs/obs-internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -1263,6 +1263,11 @@ struct obs_encoder {
uint32_t frame_rate_divisor_counter; // only used for GPU encoders
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;

struct circlebuf audio_input_buffer[MAX_AV_PLANES];
Expand Down
21 changes: 21 additions & 0 deletions libobs/obs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
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 */
EXPORT bool obs_encoder_scaling_enabled(const obs_encoder_t *encoder);

Expand Down
Loading

0 comments on commit 1e09f5a

Please sign in to comment.