mac-videotoolbox: Add CBR support on Apple silicon / macOS 13

Implementation adjusted by the OBS Project.

Co-Authored-By: Sebastian Beckmann <beckmann.sebastian@outlook.de>
This commit is contained in:
Developer-Ecosystem-Engineering 2022-06-22 15:51:02 +02:00 committed by Sebastian Beckmann
parent 8c987a86ac
commit 9e0d7b3123
2 changed files with 86 additions and 22 deletions

View file

@ -10,4 +10,4 @@ Profile="Profile"
None="(None)"
DefaultEncoder="(Default Encoder)"
UseBFrames="Use B-Frames"
RateControl="Rate Control"

View file

@ -1,5 +1,6 @@
#include <obs-module.h>
#include <util/darray.h>
#include <util/platform.h>
#include <obs-avc.h>
#include <CoreFoundation/CoreFoundation.h>
@ -34,6 +35,7 @@ struct vt_encoder {
uint32_t keyint;
uint32_t fps_num;
uint32_t fps_den;
const char *rate_control;
uint32_t bitrate;
bool limit_bitrate;
uint32_t rc_max_bitrate;
@ -144,16 +146,41 @@ static OSStatus session_set_prop(VTCompressionSessionRef session,
}
static OSStatus session_set_bitrate(VTCompressionSessionRef session,
int new_bitrate, bool limit_bitrate,
int max_bitrate, float max_bitrate_window)
const char *rate_control, int new_bitrate,
bool limit_bitrate, int max_bitrate,
float max_bitrate_window)
{
OSStatus code;
SESSION_CHECK(session_set_prop_int(
session, kVTCompressionPropertyKey_AverageBitRate,
new_bitrate * 1000));
bool can_limit_bitrate;
if (strcmp(rate_control, "CBR") == 0) {
if (__builtin_available(macOS 13.0, *)) {
#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 130000
SESSION_CHECK(session_set_prop_int(
session,
kVTCompressionPropertyKey_ConstantBitRate,
new_bitrate * 1000));
can_limit_bitrate = false;
#else
VT_LOG(LOG_ERROR,
"OBS was compiled without CBR support.");
#endif
} else {
VT_LOG(LOG_ERROR,
"CBR is only available on macOS 13 or newer.");
}
} else if (strcmp(rate_control, "ABR") == 0) {
SESSION_CHECK(session_set_prop_int(
session, kVTCompressionPropertyKey_AverageBitRate,
new_bitrate * 1000));
can_limit_bitrate = true;
} else {
VT_LOG(LOG_ERROR,
"Selected rate control method is not supported: %s",
rate_control);
}
if (limit_bitrate) {
if (limit_bitrate && can_limit_bitrate) {
int32_t cpb_size = max_bitrate * 125 * max_bitrate_window;
CFNumberRef cf_cpb_size =
@ -328,9 +355,9 @@ static bool create_encoder(struct vt_encoder *enc)
STATUS_CHECK(session_set_prop(s, kVTCompressionPropertyKey_ProfileLevel,
obs_to_vt_profile(enc->profile)));
STATUS_CHECK(session_set_bitrate(s, enc->bitrate, enc->limit_bitrate,
enc->rc_max_bitrate,
enc->rc_max_bitrate_window));
STATUS_CHECK(session_set_bitrate(
s, enc->rate_control, enc->bitrate, enc->limit_bitrate,
enc->rc_max_bitrate, enc->rc_max_bitrate_window));
STATUS_CHECK(session_set_colorspace(s, enc->colorspace));
@ -369,6 +396,7 @@ static void dump_encoder_info(struct vt_encoder *enc)
VT_BLOG(LOG_INFO,
"settings:\n"
"\tvt_encoder_id %s\n"
"\trate_control: %s\n"
"\tbitrate: %d (kbps)\n"
"\tfps_num: %d\n"
"\tfps_den: %d\n"
@ -380,10 +408,11 @@ static void dump_encoder_info(struct vt_encoder *enc)
"\trc_max_bitrate_window: %f (s)\n"
"\thw_enc: %s\n"
"\tprofile: %s\n",
enc->vt_encoder_id, enc->bitrate, enc->fps_num, enc->fps_den,
enc->width, enc->height, enc->keyint,
enc->limit_bitrate ? "on" : "off", enc->rc_max_bitrate,
enc->rc_max_bitrate_window, enc->hw_enc ? "on" : "off",
enc->vt_encoder_id, enc->rate_control, enc->bitrate,
enc->fps_num, enc->fps_den, enc->width, enc->height,
enc->keyint, enc->limit_bitrate ? "on" : "off",
enc->rc_max_bitrate, enc->rc_max_bitrate_window,
enc->hw_enc ? "on" : "off",
(enc->profile != NULL && !!strlen(enc->profile)) ? enc->profile
: "default");
}
@ -433,6 +462,7 @@ static void update_params(struct vt_encoder *enc, obs_data_t *settings)
enc->fps_num = voi->fps_num;
enc->fps_den = voi->fps_den;
enc->keyint = (uint32_t)obs_data_get_int(settings, "keyint_sec");
enc->rate_control = obs_data_get_string(settings, "rate_control");
enc->bitrate = (uint32_t)obs_data_get_int(settings, "bitrate");
enc->profile = obs_data_get_string(settings, "profile");
enc->limit_bitrate = obs_data_get_bool(settings, "limit_bitrate");
@ -455,8 +485,8 @@ static bool vt_update(void *data, obs_data_t *settings)
old_limit_bitrate == enc->limit_bitrate)
return true;
OSStatus code = session_set_bitrate(enc->session, enc->bitrate,
enc->limit_bitrate,
OSStatus code = session_set_bitrate(enc->session, enc->rate_control,
enc->bitrate, enc->limit_bitrate,
enc->rc_max_bitrate,
enc->rc_max_bitrate_window);
if (code != noErr)
@ -832,15 +862,28 @@ static const char *vt_getname(void *data)
#define TEXT_NONE obs_module_text("None")
#define TEXT_DEFAULT obs_module_text("DefaultEncoder")
#define TEXT_BFRAMES obs_module_text("UseBFrames")
#define TEXT_RATE_CONTROL obs_module_text("RateControl")
static bool limit_bitrate_modified(obs_properties_t *ppts, obs_property_t *p,
obs_data_t *settings)
static bool rate_control_limit_bitrate_modified(obs_properties_t *ppts,
obs_property_t *p,
obs_data_t *settings)
{
bool use_max_bitrate = obs_data_get_bool(settings, "limit_bitrate");
bool can_limit_bitrate = true;
bool use_limit_bitrate = obs_data_get_bool(settings, "limit_bitrate");
const char *rate_control =
obs_data_get_string(settings, "rate_control");
if (strcmp(rate_control, "CBR") == 0)
can_limit_bitrate = false;
else if (strcmp(rate_control, "ABR") == 0)
can_limit_bitrate = true;
p = obs_properties_get(ppts, "limit_bitrate");
obs_property_set_visible(p, can_limit_bitrate);
p = obs_properties_get(ppts, "max_bitrate");
obs_property_set_visible(p, use_max_bitrate);
obs_property_set_visible(p, can_limit_bitrate && use_limit_bitrate);
p = obs_properties_get(ppts, "max_bitrate_window");
obs_property_set_visible(p, use_max_bitrate);
obs_property_set_visible(p, can_limit_bitrate && use_limit_bitrate);
return true;
}
@ -851,13 +894,27 @@ static obs_properties_t *vt_properties(void *unused)
obs_properties_t *props = obs_properties_create();
obs_property_t *p;
p = obs_properties_add_list(props, "rate_control", TEXT_RATE_CONTROL,
OBS_COMBO_TYPE_LIST,
OBS_COMBO_FORMAT_STRING);
if (__builtin_available(macOS 13.0, *))
#ifndef __aarch64__
if (os_get_emulation_status() == true)
#endif
obs_property_list_add_string(p, "CBR", "CBR");
obs_property_list_add_string(p, "ABR", "ABR");
obs_property_set_modified_callback(p,
rate_control_limit_bitrate_modified);
p = obs_properties_add_int(props, "bitrate", TEXT_BITRATE, 50, 10000000,
50);
obs_property_int_set_suffix(p, " Kbps");
p = obs_properties_add_bool(props, "limit_bitrate",
TEXT_USE_MAX_BITRATE);
obs_property_set_modified_callback(p, limit_bitrate_modified);
obs_property_set_modified_callback(p,
rate_control_limit_bitrate_modified);
p = obs_properties_add_int(props, "max_bitrate", TEXT_MAX_BITRATE, 50,
10000000, 50);
@ -883,6 +940,13 @@ static obs_properties_t *vt_properties(void *unused)
static void vt_defaults(obs_data_t *settings)
{
obs_data_set_default_string(settings, "rate_control", "ABR");
if (__builtin_available(macOS 13.0, *))
#ifndef __aarch64__
if (os_get_emulation_status() == true)
#endif
obs_data_set_default_string(settings, "rate_control",
"CBR");
obs_data_set_default_int(settings, "bitrate", 2500);
obs_data_set_default_bool(settings, "limit_bitrate", false);
obs_data_set_default_int(settings, "max_bitrate", 2500);