mirror of
https://github.com/obsproject/obs-studio.git
synced 2024-07-14 15:24:07 +00:00
libobs: Allow configuring frame rate divisor for encoders
This allows encoders/outputs to output at a frame rate that is lower than the configured base frame rate
This commit is contained in:
parent
290570b819
commit
6cdfb0a8b9
|
@ -46,6 +46,16 @@ struct video_input {
|
|||
struct video_frame frame[MAX_CONVERT_BUFFERS];
|
||||
int cur_frame;
|
||||
|
||||
// allow outputting at fractions of main composition FPS,
|
||||
// e.g. 60 FPS with frame_rate_divisor = 1 turns into 30 FPS
|
||||
//
|
||||
// a separate counter is used in favor of using remainder calculations
|
||||
// to allow "inputs" started at the same time to start on the same frame
|
||||
// whereas with remainder calculation the frame alignment would depend on
|
||||
// the total frame count at the time the encoder was started
|
||||
uint32_t frame_rate_divisor;
|
||||
uint32_t frame_rate_divisor_counter;
|
||||
|
||||
void (*callback)(void *param, struct video_data *frame);
|
||||
void *param;
|
||||
};
|
||||
|
@ -77,6 +87,8 @@ struct video_output {
|
|||
size_t last_added;
|
||||
struct cached_frame_info cache[MAX_CACHE_SIZE];
|
||||
|
||||
struct video_output *parent;
|
||||
|
||||
volatile bool raw_active;
|
||||
volatile long gpu_refs;
|
||||
};
|
||||
|
@ -136,6 +148,17 @@ static inline bool video_output_cur_frame(struct video_output *video)
|
|||
struct video_input *input = video->inputs.array + i;
|
||||
struct video_data frame = frame_info->frame;
|
||||
|
||||
// an explicit counter is used instead of remainder calculation
|
||||
// to allow multiple encoders started at the same time to start on
|
||||
// the same frame
|
||||
uint32_t skip = input->frame_rate_divisor_counter++;
|
||||
if (input->frame_rate_divisor_counter ==
|
||||
input->frame_rate_divisor)
|
||||
input->frame_rate_divisor_counter = 0;
|
||||
|
||||
if (skip)
|
||||
continue;
|
||||
|
||||
if (scale_video_output(input, &frame))
|
||||
input->callback(input->param, &frame);
|
||||
}
|
||||
|
@ -371,13 +394,37 @@ static inline void reset_frames(video_t *video)
|
|||
os_atomic_set_long(&video->total_frames, 0);
|
||||
}
|
||||
|
||||
static const video_t *get_const_root(const video_t *video)
|
||||
{
|
||||
while (video->parent)
|
||||
video = video->parent;
|
||||
return video;
|
||||
}
|
||||
|
||||
static video_t *get_root(video_t *video)
|
||||
{
|
||||
while (video->parent)
|
||||
video = video->parent;
|
||||
return video;
|
||||
}
|
||||
|
||||
bool video_output_connect(
|
||||
video_t *video, const struct video_scale_info *conversion,
|
||||
void (*callback)(void *param, struct video_data *frame), void *param)
|
||||
{
|
||||
return video_output_connect2(video, conversion, 1, callback, param);
|
||||
}
|
||||
|
||||
bool video_output_connect2(
|
||||
video_t *video, const struct video_scale_info *conversion,
|
||||
uint32_t frame_rate_divisor,
|
||||
void (*callback)(void *param, struct video_data *frame), void *param)
|
||||
{
|
||||
bool success = false;
|
||||
|
||||
if (!video || !callback)
|
||||
video = get_root(video);
|
||||
|
||||
if (!video || !callback || frame_rate_divisor == 0)
|
||||
return false;
|
||||
|
||||
pthread_mutex_lock(&video->input_mutex);
|
||||
|
@ -389,6 +436,8 @@ bool video_output_connect(
|
|||
input.callback = callback;
|
||||
input.param = param;
|
||||
|
||||
input.frame_rate_divisor = frame_rate_divisor;
|
||||
|
||||
if (conversion) {
|
||||
input.conversion = *conversion;
|
||||
} else {
|
||||
|
@ -446,6 +495,8 @@ void video_output_disconnect(video_t *video,
|
|||
if (!video || !callback)
|
||||
return;
|
||||
|
||||
video = get_root(video);
|
||||
|
||||
pthread_mutex_lock(&video->input_mutex);
|
||||
|
||||
size_t idx = video_get_input_idx(video, callback, param);
|
||||
|
@ -468,7 +519,7 @@ bool video_output_active(const video_t *video)
|
|||
{
|
||||
if (!video)
|
||||
return false;
|
||||
return os_atomic_load_bool(&video->raw_active);
|
||||
return os_atomic_load_bool(&get_const_root(video)->raw_active);
|
||||
}
|
||||
|
||||
const struct video_output_info *video_output_get_info(const video_t *video)
|
||||
|
@ -485,6 +536,8 @@ bool video_output_lock_frame(video_t *video, struct video_frame *frame,
|
|||
if (!video)
|
||||
return false;
|
||||
|
||||
video = get_root(video);
|
||||
|
||||
pthread_mutex_lock(&video->data_mutex);
|
||||
|
||||
if (video->available_frames == 0) {
|
||||
|
@ -518,6 +571,8 @@ void video_output_unlock_frame(video_t *video)
|
|||
if (!video)
|
||||
return;
|
||||
|
||||
video = get_root(video);
|
||||
|
||||
pthread_mutex_lock(&video->data_mutex);
|
||||
|
||||
video->available_frames--;
|
||||
|
@ -538,6 +593,8 @@ void video_output_stop(video_t *video)
|
|||
if (!video)
|
||||
return;
|
||||
|
||||
video = get_root(video);
|
||||
|
||||
if (!video->stop) {
|
||||
video->stop = true;
|
||||
os_sem_post(video->update_semaphore);
|
||||
|
@ -550,22 +607,22 @@ bool video_output_stopped(video_t *video)
|
|||
if (!video)
|
||||
return true;
|
||||
|
||||
return video->stop;
|
||||
return get_root(video)->stop;
|
||||
}
|
||||
|
||||
enum video_format video_output_get_format(const video_t *video)
|
||||
{
|
||||
return video ? video->info.format : VIDEO_FORMAT_NONE;
|
||||
return video ? get_const_root(video)->info.format : VIDEO_FORMAT_NONE;
|
||||
}
|
||||
|
||||
uint32_t video_output_get_width(const video_t *video)
|
||||
{
|
||||
return video ? video->info.width : 0;
|
||||
return video ? get_const_root(video)->info.width : 0;
|
||||
}
|
||||
|
||||
uint32_t video_output_get_height(const video_t *video)
|
||||
{
|
||||
return video ? video->info.height : 0;
|
||||
return video ? get_const_root(video)->info.height : 0;
|
||||
}
|
||||
|
||||
double video_output_get_frame_rate(const video_t *video)
|
||||
|
@ -573,17 +630,21 @@ double video_output_get_frame_rate(const video_t *video)
|
|||
if (!video)
|
||||
return 0.0;
|
||||
|
||||
video = get_const_root(video);
|
||||
|
||||
return (double)video->info.fps_num / (double)video->info.fps_den;
|
||||
}
|
||||
|
||||
uint32_t video_output_get_skipped_frames(const video_t *video)
|
||||
{
|
||||
return (uint32_t)os_atomic_load_long(&video->skipped_frames);
|
||||
return (uint32_t)os_atomic_load_long(
|
||||
&get_const_root(video)->skipped_frames);
|
||||
}
|
||||
|
||||
uint32_t video_output_get_total_frames(const video_t *video)
|
||||
{
|
||||
return (uint32_t)os_atomic_load_long(&video->total_frames);
|
||||
return (uint32_t)os_atomic_load_long(
|
||||
&get_const_root(video)->total_frames);
|
||||
}
|
||||
|
||||
/* Note: These four functions below are a very slight bit of a hack. If the
|
||||
|
@ -594,6 +655,8 @@ uint32_t video_output_get_total_frames(const video_t *video)
|
|||
|
||||
void video_output_inc_texture_encoders(video_t *video)
|
||||
{
|
||||
video = get_root(video);
|
||||
|
||||
if (os_atomic_inc_long(&video->gpu_refs) == 1 &&
|
||||
!os_atomic_load_bool(&video->raw_active)) {
|
||||
reset_frames(video);
|
||||
|
@ -602,6 +665,8 @@ void video_output_inc_texture_encoders(video_t *video)
|
|||
|
||||
void video_output_dec_texture_encoders(video_t *video)
|
||||
{
|
||||
video = get_root(video);
|
||||
|
||||
if (os_atomic_dec_long(&video->gpu_refs) == 0 &&
|
||||
!os_atomic_load_bool(&video->raw_active)) {
|
||||
log_skipped(video);
|
||||
|
@ -610,10 +675,32 @@ void video_output_dec_texture_encoders(video_t *video)
|
|||
|
||||
void video_output_inc_texture_frames(video_t *video)
|
||||
{
|
||||
os_atomic_inc_long(&video->total_frames);
|
||||
os_atomic_inc_long(&get_root(video)->total_frames);
|
||||
}
|
||||
|
||||
void video_output_inc_texture_skipped_frames(video_t *video)
|
||||
{
|
||||
os_atomic_inc_long(&video->skipped_frames);
|
||||
os_atomic_inc_long(&get_root(video)->skipped_frames);
|
||||
}
|
||||
|
||||
video_t *video_output_create_with_frame_rate_divisor(video_t *video,
|
||||
uint32_t divisor)
|
||||
{
|
||||
// `divisor == 1` would result in the same frame rate,
|
||||
// resulting in an unnecessary additional video output
|
||||
if (!video || divisor == 0 || divisor == 1)
|
||||
return NULL;
|
||||
|
||||
video_t *new_video = bzalloc(sizeof(video_t));
|
||||
memcpy(new_video, video, sizeof(*new_video));
|
||||
new_video->parent = video;
|
||||
new_video->info.fps_den *= divisor;
|
||||
|
||||
return new_video;
|
||||
}
|
||||
|
||||
void video_output_free_frame_rate_divisor(video_t *video)
|
||||
{
|
||||
if (video && video->parent)
|
||||
bfree(video);
|
||||
}
|
||||
|
|
|
@ -302,6 +302,11 @@ EXPORT bool
|
|||
video_output_connect(video_t *video, const struct video_scale_info *conversion,
|
||||
void (*callback)(void *param, struct video_data *frame),
|
||||
void *param);
|
||||
EXPORT bool
|
||||
video_output_connect2(video_t *video, const struct video_scale_info *conversion,
|
||||
uint32_t frame_rate_divisor,
|
||||
void (*callback)(void *param, struct video_data *frame),
|
||||
void *param);
|
||||
EXPORT void video_output_disconnect(video_t *video,
|
||||
void (*callback)(void *param,
|
||||
struct video_data *frame),
|
||||
|
@ -331,6 +336,10 @@ extern void video_output_dec_texture_encoders(video_t *video);
|
|||
extern void video_output_inc_texture_frames(video_t *video);
|
||||
extern void video_output_inc_texture_skipped_frames(video_t *video);
|
||||
|
||||
extern video_t *video_output_create_with_frame_rate_divisor(video_t *video,
|
||||
uint32_t divisor);
|
||||
extern void video_output_free_frame_rate_divisor(video_t *video);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -112,6 +112,10 @@ create_encoder(const char *id, enum obs_encoder_type type, const char *name,
|
|||
obs_context_data_insert(&encoder->context, &obs->data.encoders_mutex,
|
||||
&obs->data.first_encoder);
|
||||
|
||||
if (type == OBS_ENCODER_VIDEO) {
|
||||
encoder->frame_rate_divisor = 1;
|
||||
}
|
||||
|
||||
blog(LOG_DEBUG, "encoder '%s' (%s) created", name, id);
|
||||
return encoder;
|
||||
}
|
||||
|
@ -309,8 +313,9 @@ static void add_connection(struct obs_encoder *encoder)
|
|||
if (gpu_encode_available(encoder)) {
|
||||
start_gpu_encode(encoder);
|
||||
} else {
|
||||
start_raw_video(encoder->media, &info, receive_video,
|
||||
encoder);
|
||||
start_raw_video(encoder->media, &info,
|
||||
encoder->frame_rate_divisor,
|
||||
receive_video, encoder);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -379,6 +384,9 @@ static void obs_encoder_actually_destroy(obs_encoder_t *encoder)
|
|||
bfree((void *)encoder->info.id);
|
||||
if (encoder->last_error_message)
|
||||
bfree(encoder->last_error_message);
|
||||
if (encoder->fps_override)
|
||||
video_output_free_frame_rate_divisor(
|
||||
encoder->fps_override);
|
||||
bfree(encoder);
|
||||
}
|
||||
}
|
||||
|
@ -660,6 +668,7 @@ void obs_encoder_shutdown(obs_encoder_t *encoder)
|
|||
encoder->first_received = false;
|
||||
encoder->offset_usec = 0;
|
||||
encoder->start_ts = 0;
|
||||
encoder->frame_rate_divisor_counter = 1;
|
||||
maybe_clear_encoder_core_video_mix(encoder);
|
||||
}
|
||||
obs_encoder_set_last_error(encoder, NULL);
|
||||
|
@ -871,6 +880,52 @@ void obs_encoder_set_gpu_scale_type(obs_encoder_t *encoder,
|
|||
encoder->gpu_scale_type = gpu_scale_type;
|
||||
}
|
||||
|
||||
bool obs_encoder_set_frame_rate_divisor(obs_encoder_t *encoder,
|
||||
uint32_t frame_rate_divisor)
|
||||
{
|
||||
if (!obs_encoder_valid(encoder, "obs_encoder_set_frame_rate_divisor"))
|
||||
return false;
|
||||
|
||||
if (encoder->info.type != OBS_ENCODER_VIDEO) {
|
||||
blog(LOG_WARNING,
|
||||
"obs_encoder_set_frame_rate_divisor: "
|
||||
"encoder '%s' is not a video encoder",
|
||||
obs_encoder_get_name(encoder));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (encoder_active(encoder)) {
|
||||
blog(LOG_WARNING,
|
||||
"encoder '%s': Cannot set frame rate divisor "
|
||||
"while the encoder is active",
|
||||
obs_encoder_get_name(encoder));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (frame_rate_divisor == 0) {
|
||||
blog(LOG_WARNING,
|
||||
"encoder '%s': Cannot set frame "
|
||||
"rate divisor to 0",
|
||||
obs_encoder_get_name(encoder));
|
||||
return false;
|
||||
}
|
||||
|
||||
encoder->frame_rate_divisor = frame_rate_divisor;
|
||||
|
||||
if (encoder->fps_override) {
|
||||
video_output_free_frame_rate_divisor(encoder->fps_override);
|
||||
encoder->fps_override = NULL;
|
||||
}
|
||||
|
||||
if (encoder->media) {
|
||||
encoder->fps_override =
|
||||
video_output_create_with_frame_rate_divisor(
|
||||
encoder->media, encoder->frame_rate_divisor);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool obs_encoder_scaling_enabled(const obs_encoder_t *encoder)
|
||||
{
|
||||
if (!obs_encoder_valid(encoder, "obs_encoder_scaling_enabled"))
|
||||
|
@ -947,6 +1002,22 @@ enum obs_scale_type obs_encoder_get_scale_type(obs_encoder_t *encoder)
|
|||
return encoder->gpu_scale_type;
|
||||
}
|
||||
|
||||
uint32_t obs_encoder_get_frame_rate_divisor(const obs_encoder_t *encoder)
|
||||
{
|
||||
if (!obs_encoder_valid(encoder, "obs_encoder_set_frame_rate_divisor"))
|
||||
return 0;
|
||||
|
||||
if (encoder->info.type != OBS_ENCODER_VIDEO) {
|
||||
blog(LOG_WARNING,
|
||||
"obs_encoder_set_frame_rate_divisor: "
|
||||
"encoder '%s' is not a video encoder",
|
||||
obs_encoder_get_name(encoder));
|
||||
return 0;
|
||||
}
|
||||
|
||||
return encoder->frame_rate_divisor;
|
||||
}
|
||||
|
||||
uint32_t obs_encoder_get_sample_rate(const obs_encoder_t *encoder)
|
||||
{
|
||||
if (!obs_encoder_valid(encoder, "obs_encoder_get_sample_rate"))
|
||||
|
@ -1002,11 +1073,22 @@ void obs_encoder_set_video(obs_encoder_t *encoder, video_t *video)
|
|||
return;
|
||||
}
|
||||
|
||||
if (encoder->fps_override) {
|
||||
video_output_free_frame_rate_divisor(encoder->fps_override);
|
||||
encoder->fps_override = NULL;
|
||||
}
|
||||
|
||||
if (video) {
|
||||
voi = video_output_get_info(video);
|
||||
encoder->media = video;
|
||||
encoder->timebase_num = voi->fps_den;
|
||||
encoder->timebase_den = voi->fps_num;
|
||||
|
||||
if (encoder->frame_rate_divisor) {
|
||||
encoder->fps_override =
|
||||
video_output_create_with_frame_rate_divisor(
|
||||
video, encoder->frame_rate_divisor);
|
||||
}
|
||||
} else {
|
||||
encoder->media = NULL;
|
||||
encoder->timebase_num = 0;
|
||||
|
@ -1056,7 +1138,7 @@ video_t *obs_encoder_video(const obs_encoder_t *encoder)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
return encoder->media;
|
||||
return encoder->fps_override ? encoder->fps_override : encoder->media;
|
||||
}
|
||||
|
||||
audio_t *obs_encoder_audio(const obs_encoder_t *encoder)
|
||||
|
@ -1218,7 +1300,7 @@ bool do_encode(struct obs_encoder *encoder, struct encoder_frame *frame)
|
|||
encoder->context.settings);
|
||||
}
|
||||
|
||||
pkt.timebase_num = encoder->timebase_num;
|
||||
pkt.timebase_num = encoder->timebase_num * encoder->frame_rate_divisor;
|
||||
pkt.timebase_den = encoder->timebase_den;
|
||||
pkt.encoder = encoder;
|
||||
|
||||
|
|
|
@ -526,6 +526,7 @@ extern struct obs_core_video_mix *get_mix_for_video(video_t *video);
|
|||
|
||||
extern void
|
||||
start_raw_video(video_t *video, const struct video_scale_info *conversion,
|
||||
uint32_t frame_rate_divisor,
|
||||
void (*callback)(void *param, struct video_data *frame),
|
||||
void *param);
|
||||
extern void stop_raw_video(video_t *video,
|
||||
|
@ -1228,6 +1229,17 @@ struct obs_encoder {
|
|||
uint32_t timebase_num;
|
||||
uint32_t timebase_den;
|
||||
|
||||
// allow outputting at fractions of main composition FPS,
|
||||
// e.g. 60 FPS with frame_rate_divisor = 1 turns into 30 FPS
|
||||
//
|
||||
// a separate counter is used in favor of using remainder calculations
|
||||
// to allow "inputs" started at the same time to start on the same frame
|
||||
// whereas with remainder calculation the frame alignment would depend on
|
||||
// the total frame count at the time the encoder was started
|
||||
uint32_t frame_rate_divisor;
|
||||
uint32_t frame_rate_divisor_counter; // only used for GPU encoders
|
||||
video_t *fps_override;
|
||||
|
||||
int64_t cur_pts;
|
||||
|
||||
struct circlebuf audio_input_buffer[MAX_AV_PLANES];
|
||||
|
|
|
@ -2264,7 +2264,7 @@ static void hook_data_capture(struct obs_output *output)
|
|||
if (has_video)
|
||||
start_raw_video(output->video,
|
||||
obs_output_get_video_conversion(output),
|
||||
default_raw_video_callback, output);
|
||||
1, default_raw_video_callback, output);
|
||||
if (has_audio)
|
||||
start_raw_audio(output);
|
||||
}
|
||||
|
|
|
@ -70,11 +70,13 @@ static void *gpu_encode_thread(struct obs_core_video_mix *video)
|
|||
struct encoder_packet pkt = {0};
|
||||
bool received = false;
|
||||
bool success;
|
||||
uint32_t skip = 0;
|
||||
|
||||
obs_encoder_t *encoder = encoders.array[i];
|
||||
struct obs_encoder *pair = encoder->paired_encoder;
|
||||
|
||||
pkt.timebase_num = encoder->timebase_num;
|
||||
pkt.timebase_num = encoder->timebase_num *
|
||||
encoder->frame_rate_divisor;
|
||||
pkt.timebase_den = encoder->timebase_den;
|
||||
pkt.encoder = encoder;
|
||||
|
||||
|
@ -94,6 +96,16 @@ static void *gpu_encode_thread(struct obs_core_video_mix *video)
|
|||
encoder->context.settings);
|
||||
}
|
||||
|
||||
// an explicit counter is used instead of remainder calculation
|
||||
// to allow multiple encoders started at the same time to start on
|
||||
// the same frame
|
||||
skip = encoder->frame_rate_divisor_counter++;
|
||||
if (encoder->frame_rate_divisor_counter ==
|
||||
encoder->frame_rate_divisor)
|
||||
encoder->frame_rate_divisor_counter = 0;
|
||||
if (skip)
|
||||
continue;
|
||||
|
||||
if (!encoder->start_ts)
|
||||
encoder->start_ts = timestamp;
|
||||
|
||||
|
@ -111,7 +123,8 @@ static void *gpu_encode_thread(struct obs_core_video_mix *video)
|
|||
|
||||
lock_key = next_key;
|
||||
|
||||
encoder->cur_pts += encoder->timebase_num;
|
||||
encoder->cur_pts += encoder->timebase_num *
|
||||
encoder->frame_rate_divisor;
|
||||
}
|
||||
|
||||
/* -------------- */
|
||||
|
|
14
libobs/obs.c
14
libobs/obs.c
|
@ -3132,13 +3132,15 @@ struct obs_core_video_mix *get_mix_for_video(video_t *v)
|
|||
}
|
||||
|
||||
void start_raw_video(video_t *v, const struct video_scale_info *conversion,
|
||||
uint32_t frame_rate_divisor,
|
||||
void (*callback)(void *param, struct video_data *frame),
|
||||
void *param)
|
||||
{
|
||||
struct obs_core_video_mix *video = get_mix_for_video(v);
|
||||
if (video)
|
||||
os_atomic_inc_long(&video->raw_active);
|
||||
video_output_connect(v, conversion, callback, param);
|
||||
video_output_connect2(v, conversion, frame_rate_divisor, callback,
|
||||
param);
|
||||
}
|
||||
|
||||
void stop_raw_video(video_t *v,
|
||||
|
@ -3155,9 +3157,17 @@ void obs_add_raw_video_callback(const struct video_scale_info *conversion,
|
|||
void (*callback)(void *param,
|
||||
struct video_data *frame),
|
||||
void *param)
|
||||
{
|
||||
obs_add_raw_video_callback2(conversion, 1, callback, param);
|
||||
}
|
||||
|
||||
void obs_add_raw_video_callback2(
|
||||
const struct video_scale_info *conversion, uint32_t frame_rate_divisor,
|
||||
void (*callback)(void *param, struct video_data *frame), void *param)
|
||||
{
|
||||
struct obs_core_video_mix *video = obs->video.main_mix;
|
||||
start_raw_video(video->video, conversion, callback, param);
|
||||
start_raw_video(video->video, conversion, frame_rate_divisor, callback,
|
||||
param);
|
||||
}
|
||||
|
||||
void obs_remove_raw_video_callback(void (*callback)(void *param,
|
||||
|
|
16
libobs/obs.h
16
libobs/obs.h
|
@ -849,6 +849,9 @@ EXPORT void obs_remove_main_rendered_callback(void (*rendered)(void *param),
|
|||
EXPORT void obs_add_raw_video_callback(
|
||||
const struct video_scale_info *conversion,
|
||||
void (*callback)(void *param, struct video_data *frame), void *param);
|
||||
EXPORT void obs_add_raw_video_callback2(
|
||||
const struct video_scale_info *conversion, uint32_t frame_rate_divisor,
|
||||
void (*callback)(void *param, struct video_data *frame), void *param);
|
||||
EXPORT void obs_remove_raw_video_callback(
|
||||
void (*callback)(void *param, struct video_data *frame), void *param);
|
||||
|
||||
|
@ -2420,6 +2423,16 @@ EXPORT void obs_encoder_set_scaled_size(obs_encoder_t *encoder, uint32_t width,
|
|||
EXPORT void obs_encoder_set_gpu_scale_type(obs_encoder_t *encoder,
|
||||
enum obs_scale_type gpu_scale_type);
|
||||
|
||||
/**
|
||||
* Set frame rate divisor for a video encoder. This allows recording at
|
||||
* a partial frame rate compared to the base frame rate, e.g. 60 FPS with
|
||||
* divisor = 2 will record at 30 FPS, with divisor = 3 at 20, etc.
|
||||
*
|
||||
* Can only be called on stopped encoders, changing this on the fly is not supported
|
||||
*/
|
||||
EXPORT bool obs_encoder_set_frame_rate_divisor(obs_encoder_t *encoder,
|
||||
uint32_t divisor);
|
||||
|
||||
/** For video encoders, returns true if pre-encode scaling is enabled */
|
||||
EXPORT bool obs_encoder_scaling_enabled(const obs_encoder_t *encoder);
|
||||
|
||||
|
@ -2435,6 +2448,9 @@ EXPORT bool obs_encoder_gpu_scaling_enabled(obs_encoder_t *encoder);
|
|||
/** For video encoders, returns GPU scaling type */
|
||||
EXPORT enum obs_scale_type obs_encoder_get_scale_type(obs_encoder_t *encoder);
|
||||
|
||||
/** For video encoders, returns the frame rate divisor (default is 1) */
|
||||
EXPORT uint32_t obs_encoder_get_frame_rate_divisor(const obs_encoder_t *encoder);
|
||||
|
||||
/** For audio encoders, returns the sample rate of the audio */
|
||||
EXPORT uint32_t obs_encoder_get_sample_rate(const obs_encoder_t *encoder);
|
||||
|
||||
|
|
Loading…
Reference in a new issue