mirror of
https://github.com/obsproject/obs-studio.git
synced 2024-09-20 04:42:18 +02:00
mac-videotoolbox: Add support platform hardware and software ProRes 422
Utilize the systems ProRes software and hardware encoders on supported configurations
This commit is contained in:
parent
641ec29a00
commit
761530d34b
@ -3,6 +3,8 @@ VTH264EncSW="Apple VT H264 Software Encoder"
|
||||
VTHEVCEncHW="Apple VT HEVC Hardware Encoder"
|
||||
VTHEVCEncT2="Apple VT HEVC T2 Hardware Encoder"
|
||||
VTHEVCEncSW="Apple VT HEVC Software Encoder"
|
||||
VTProResEncHW="Apple VT ProRes Hardware Encoder"
|
||||
VTProResEncSW="Apple VT ProRes Software Encoder"
|
||||
VTEncoder="VideoToolbox Encoder"
|
||||
Bitrate="Bitrate"
|
||||
Quality="Quality"
|
||||
@ -14,3 +16,8 @@ Profile="Profile"
|
||||
UseBFrames="Use B-Frames"
|
||||
RateControl="Rate Control"
|
||||
ColorFormatUnsupportedH264="The selected color format is not supported by the Apple VT H.264 encoder. Select a compatible color format in Settings -> Advanced or use a different encoder."
|
||||
ProResCodec="ProRes Codec"
|
||||
ProRes422Proxy="ProRes 422 Proxy"
|
||||
ProRes422LT="ProRes 422 LT"
|
||||
ProRes422="ProRes 422"
|
||||
ProRes422HQ="ProRes 422 HQ"
|
||||
|
@ -29,6 +29,13 @@ struct vt_encoder_type_data {
|
||||
bool hardware_accelerated;
|
||||
};
|
||||
|
||||
struct vt_prores_encoder_data {
|
||||
FourCharCode codec_type;
|
||||
CFStringRef encoder_id;
|
||||
};
|
||||
static DARRAY(struct vt_prores_encoder_data) vt_prores_hardware_encoder_list;
|
||||
static DARRAY(struct vt_prores_encoder_data) vt_prores_software_encoder_list;
|
||||
|
||||
struct vt_encoder {
|
||||
obs_encoder_t *encoder;
|
||||
|
||||
@ -65,6 +72,14 @@ static const char *codec_type_to_print_fmt(CMVideoCodecType codec_type)
|
||||
return "h264";
|
||||
case kCMVideoCodecType_HEVC:
|
||||
return "hevc";
|
||||
case kCMVideoCodecType_AppleProRes422Proxy:
|
||||
return "apco";
|
||||
case kCMVideoCodecType_AppleProRes422LT:
|
||||
return "apcs";
|
||||
case kCMVideoCodecType_AppleProRes422:
|
||||
return "apcn";
|
||||
case kCMVideoCodecType_AppleProRes422HQ:
|
||||
return "apch";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
@ -345,6 +360,37 @@ create_encoder_spec(const char *vt_encoder_id)
|
||||
|
||||
return encoder_spec;
|
||||
}
|
||||
|
||||
static inline CFMutableDictionaryRef
|
||||
create_prores_encoder_spec(CMVideoCodecType target_codec_type,
|
||||
bool hardware_accelerated)
|
||||
{
|
||||
CFStringRef encoder_id = NULL;
|
||||
|
||||
size_t size = 0;
|
||||
struct vt_prores_encoder_data *encoder_list = NULL;
|
||||
if (hardware_accelerated) {
|
||||
size = vt_prores_hardware_encoder_list.num;
|
||||
encoder_list = vt_prores_hardware_encoder_list.array;
|
||||
} else {
|
||||
size = vt_prores_software_encoder_list.num;
|
||||
encoder_list = vt_prores_software_encoder_list.array;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
if (target_codec_type == encoder_list[i].codec_type) {
|
||||
encoder_id = encoder_list[i].encoder_id;
|
||||
}
|
||||
}
|
||||
|
||||
CFMutableDictionaryRef encoder_spec = CFDictionaryCreateMutable(
|
||||
kCFAllocatorDefault, 1, &kCFTypeDictionaryKeyCallBacks,
|
||||
&kCFTypeDictionaryValueCallBacks);
|
||||
|
||||
CFDictionaryAddValue(encoder_spec, ENCODER_ID, encoder_id);
|
||||
|
||||
return encoder_spec;
|
||||
}
|
||||
#undef ENCODER_ID
|
||||
#undef REQUIRE_HW_ACCEL
|
||||
#undef ENABLE_HW_ACCEL
|
||||
@ -377,7 +423,19 @@ static bool create_encoder(struct vt_encoder *enc)
|
||||
|
||||
VTCompressionSessionRef s;
|
||||
|
||||
CFDictionaryRef encoder_spec = create_encoder_spec(enc->vt_encoder_id);
|
||||
const char *codec_name = obs_encoder_get_codec(enc->encoder);
|
||||
|
||||
CFDictionaryRef encoder_spec;
|
||||
if (strcmp(codec_name, "prores") == 0) {
|
||||
struct vt_encoder_type_data *type_data =
|
||||
(struct vt_encoder_type_data *)
|
||||
obs_encoder_get_type_data(enc->encoder);
|
||||
encoder_spec = create_prores_encoder_spec(
|
||||
enc->codec_type, type_data->hardware_accelerated);
|
||||
} else {
|
||||
encoder_spec = create_encoder_spec(enc->vt_encoder_id);
|
||||
}
|
||||
|
||||
CFDictionaryRef pixbuf_spec = create_pixbuf_spec(enc);
|
||||
|
||||
STATUS_CHECK(VTCompressionSessionCreate(
|
||||
@ -402,26 +460,38 @@ static bool create_encoder(struct vt_encoder *enc)
|
||||
if (b != NULL)
|
||||
CFRelease(b);
|
||||
|
||||
// This can fail when using GPU HEVC hardware encoding
|
||||
code = session_set_prop_int(
|
||||
s, kVTCompressionPropertyKey_MaxKeyFrameIntervalDuration,
|
||||
enc->keyint);
|
||||
if (code != noErr)
|
||||
log_osstatus(
|
||||
LOG_WARNING, enc,
|
||||
"setting kVTCompressionPropertyKey_MaxKeyFrameIntervalDuration failed, "
|
||||
"keyframe interval might be incorrect",
|
||||
code);
|
||||
if (enc->codec_type == kCMVideoCodecType_H264 ||
|
||||
enc->codec_type == kCMVideoCodecType_HEVC) {
|
||||
|
||||
STATUS_CHECK(session_set_prop_int(
|
||||
s, kVTCompressionPropertyKey_MaxKeyFrameInterval,
|
||||
enc->keyint * ((float)enc->fps_num / enc->fps_den)));
|
||||
STATUS_CHECK(session_set_prop_float(
|
||||
s, kVTCompressionPropertyKey_ExpectedFrameRate,
|
||||
(float)enc->fps_num / enc->fps_den));
|
||||
STATUS_CHECK(session_set_prop(
|
||||
s, kVTCompressionPropertyKey_AllowFrameReordering,
|
||||
enc->bframes ? kCFBooleanTrue : kCFBooleanFalse));
|
||||
// This can fail when using GPU hardware encoding
|
||||
code = session_set_prop_int(
|
||||
s,
|
||||
kVTCompressionPropertyKey_MaxKeyFrameIntervalDuration,
|
||||
enc->keyint);
|
||||
if (code != noErr)
|
||||
log_osstatus(
|
||||
LOG_WARNING, enc,
|
||||
"setting kVTCompressionPropertyKey_MaxKeyFrameIntervalDuration failed, "
|
||||
"keyframe interval might be incorrect",
|
||||
code);
|
||||
|
||||
STATUS_CHECK(session_set_prop_int(
|
||||
s, kVTCompressionPropertyKey_MaxKeyFrameInterval,
|
||||
enc->keyint * ((float)enc->fps_num / enc->fps_den)));
|
||||
STATUS_CHECK(session_set_prop_float(
|
||||
s, kVTCompressionPropertyKey_ExpectedFrameRate,
|
||||
(float)enc->fps_num / enc->fps_den));
|
||||
STATUS_CHECK(session_set_prop(
|
||||
s, kVTCompressionPropertyKey_AllowFrameReordering,
|
||||
enc->bframes ? kCFBooleanTrue : kCFBooleanFalse));
|
||||
STATUS_CHECK(session_set_prop(
|
||||
s, kVTCompressionPropertyKey_ProfileLevel,
|
||||
obs_to_vt_profile(enc->codec_type, enc->profile)));
|
||||
STATUS_CHECK(session_set_bitrate(
|
||||
s, enc->rate_control, enc->bitrate, enc->quality,
|
||||
enc->limit_bitrate, enc->rc_max_bitrate,
|
||||
enc->rc_max_bitrate_window));
|
||||
}
|
||||
|
||||
// This can fail depending on hardware configuration
|
||||
code = session_set_prop(s, kVTCompressionPropertyKey_RealTime,
|
||||
@ -433,15 +503,6 @@ static bool create_encoder(struct vt_encoder *enc)
|
||||
"frame delay might be increased",
|
||||
code);
|
||||
|
||||
STATUS_CHECK(session_set_prop(s, kVTCompressionPropertyKey_ProfileLevel,
|
||||
obs_to_vt_profile(enc->codec_type,
|
||||
enc->profile)));
|
||||
|
||||
STATUS_CHECK(session_set_bitrate(s, enc->rate_control, enc->bitrate,
|
||||
enc->quality, enc->limit_bitrate,
|
||||
enc->rc_max_bitrate,
|
||||
enc->rc_max_bitrate_window));
|
||||
|
||||
STATUS_CHECK(session_set_colorspace(s, enc->colorspace));
|
||||
|
||||
STATUS_CHECK(VTCompressionSessionPrepareToEncodeFrames(s));
|
||||
@ -556,10 +617,15 @@ static bool update_params(struct vt_encoder *enc, obs_data_t *settings)
|
||||
const char *codec = obs_encoder_get_codec(enc->encoder);
|
||||
if (strcmp(codec, "h264") == 0) {
|
||||
enc->codec_type = kCMVideoCodecType_H264;
|
||||
obs_data_set_int(settings, "codec_type", enc->codec_type);
|
||||
#ifdef ENABLE_HEVC
|
||||
} else if (strcmp(codec, "hevc") == 0) {
|
||||
enc->codec_type = kCMVideoCodecType_HEVC;
|
||||
obs_data_set_int(settings, "codec_type", enc->codec_type);
|
||||
#endif
|
||||
} else {
|
||||
enc->codec_type = (CMVideoCodecType)obs_data_get_int(
|
||||
settings, "codec_type");
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -630,6 +696,32 @@ static void packet_put_startcode(struct darray *packet, int size)
|
||||
packet_put(packet, &annexb_startcode[4 - size], size);
|
||||
}
|
||||
|
||||
static bool handle_prores_packet(struct vt_encoder *enc,
|
||||
CMSampleBufferRef buffer)
|
||||
{
|
||||
OSStatus err = 0;
|
||||
size_t block_size = 0;
|
||||
uint8_t *block_buf = NULL;
|
||||
|
||||
CMBlockBufferRef block = CMSampleBufferGetDataBuffer(buffer);
|
||||
if (block == NULL) {
|
||||
VT_BLOG(LOG_ERROR,
|
||||
"Failed to get block buffer for ProRes frame.");
|
||||
return false;
|
||||
}
|
||||
err = CMBlockBufferGetDataPointer(block, 0, NULL, &block_size,
|
||||
(char **)&block_buf);
|
||||
if (err != 0) {
|
||||
VT_BLOG(LOG_ERROR,
|
||||
"Failed to get data buffer pointer for ProRes frame.");
|
||||
return false;
|
||||
}
|
||||
|
||||
packet_put(&enc->packet_data.da, block_buf, block_size);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void convert_block_nals_to_annexb(struct vt_encoder *enc,
|
||||
struct darray *packet,
|
||||
CMBlockBufferRef block,
|
||||
@ -781,6 +873,12 @@ static bool parse_sample(struct vt_encoder *enc, CMSampleBufferRef buffer,
|
||||
struct encoder_packet *packet, CMTime off)
|
||||
{
|
||||
int type;
|
||||
bool should_edit_nal = false;
|
||||
|
||||
if (enc->codec_type == kCMVideoCodecType_H264 ||
|
||||
enc->codec_type == kCMVideoCodecType_HEVC) {
|
||||
should_edit_nal = true;
|
||||
}
|
||||
|
||||
CMTime pts = CMSampleBufferGetPresentationTimeStamp(buffer);
|
||||
CMTime dts = CMSampleBufferGetDecodeTimeStamp(buffer);
|
||||
@ -794,7 +892,11 @@ static bool parse_sample(struct vt_encoder *enc, CMSampleBufferRef buffer,
|
||||
pts = CMTimeMultiply(pts, enc->fps_num);
|
||||
dts = CMTimeMultiply(dts, enc->fps_num);
|
||||
|
||||
bool keyframe = is_sample_keyframe(buffer);
|
||||
// All ProRes frames are "keyframes"
|
||||
bool keyframe = true;
|
||||
if (should_edit_nal) {
|
||||
keyframe = is_sample_keyframe(buffer);
|
||||
}
|
||||
|
||||
da_resize(enc->packet_data, 0);
|
||||
|
||||
@ -803,9 +905,14 @@ static bool parse_sample(struct vt_encoder *enc, CMSampleBufferRef buffer,
|
||||
if (enc->extra_data.num == 0)
|
||||
extra_data = &enc->extra_data.da;
|
||||
|
||||
if (!convert_sample_to_annexb(enc, &enc->packet_data.da, extra_data,
|
||||
buffer, keyframe))
|
||||
goto fail;
|
||||
if (should_edit_nal) {
|
||||
if (!convert_sample_to_annexb(enc, &enc->packet_data.da,
|
||||
extra_data, buffer, keyframe))
|
||||
goto fail;
|
||||
} else {
|
||||
if (!handle_prores_packet(enc, buffer))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
packet->type = OBS_ENCODER_VIDEO;
|
||||
packet->pts = (int64_t)(CMTimeGetSeconds(pts));
|
||||
@ -814,35 +921,40 @@ static bool parse_sample(struct vt_encoder *enc, CMSampleBufferRef buffer,
|
||||
packet->size = enc->packet_data.num;
|
||||
packet->keyframe = keyframe;
|
||||
|
||||
// VideoToolbox produces packets with priority lower than the RTMP code
|
||||
// expects, which causes it to be unable to recover from frame drops.
|
||||
// Fix this by manually adjusting the priority.
|
||||
uint8_t *start = enc->packet_data.array;
|
||||
uint8_t *end = start + enc->packet_data.num;
|
||||
|
||||
start = (uint8_t *)obs_avc_find_startcode(start, end);
|
||||
while (true) {
|
||||
while (start < end && !*(start++))
|
||||
;
|
||||
|
||||
if (start == end)
|
||||
break;
|
||||
|
||||
type = start[0] & 0x1F;
|
||||
if (type == OBS_NAL_SLICE_IDR || type == OBS_NAL_SLICE) {
|
||||
uint8_t prev_type = (start[0] >> 5) & 0x3;
|
||||
start[0] &= ~(3 << 5);
|
||||
|
||||
if (type == OBS_NAL_SLICE_IDR)
|
||||
start[0] |= OBS_NAL_PRIORITY_HIGHEST << 5;
|
||||
else if (type == OBS_NAL_SLICE &&
|
||||
prev_type != OBS_NAL_PRIORITY_DISPOSABLE)
|
||||
start[0] |= OBS_NAL_PRIORITY_HIGH << 5;
|
||||
else
|
||||
start[0] |= prev_type << 5;
|
||||
}
|
||||
if (should_edit_nal) {
|
||||
// VideoToolbox produces packets with priority lower than the RTMP code
|
||||
// expects, which causes it to be unable to recover from frame drops.
|
||||
// Fix this by manually adjusting the priority.
|
||||
uint8_t *start = enc->packet_data.array;
|
||||
uint8_t *end = start + enc->packet_data.num;
|
||||
|
||||
start = (uint8_t *)obs_avc_find_startcode(start, end);
|
||||
while (true) {
|
||||
while (start < end && !*(start++))
|
||||
;
|
||||
|
||||
if (start == end)
|
||||
break;
|
||||
|
||||
type = start[0] & 0x1F;
|
||||
if (type == OBS_NAL_SLICE_IDR ||
|
||||
type == OBS_NAL_SLICE) {
|
||||
uint8_t prev_type = (start[0] >> 5) & 0x3;
|
||||
start[0] &= ~(3 << 5);
|
||||
|
||||
if (type == OBS_NAL_SLICE_IDR)
|
||||
start[0] |= OBS_NAL_PRIORITY_HIGHEST
|
||||
<< 5;
|
||||
else if (type == OBS_NAL_SLICE &&
|
||||
prev_type !=
|
||||
OBS_NAL_PRIORITY_DISPOSABLE)
|
||||
start[0] |= OBS_NAL_PRIORITY_HIGH << 5;
|
||||
else
|
||||
start[0] |= prev_type << 5;
|
||||
}
|
||||
|
||||
start = (uint8_t *)obs_avc_find_startcode(start, end);
|
||||
}
|
||||
}
|
||||
|
||||
CFRelease(buffer);
|
||||
@ -969,6 +1081,10 @@ static const char *vt_getname(void *data)
|
||||
} else if (strcmp("Apple HEVC (SW)", type_data->disp_name) == 0) {
|
||||
return obs_module_text("VTHEVCEncSW");
|
||||
#endif
|
||||
} else if (strncmp("AppleProResHW", type_data->disp_name, 13) == 0) {
|
||||
return obs_module_text("VTProResEncHW");
|
||||
} else if (strncmp("Apple ProRes", type_data->disp_name, 12) == 0) {
|
||||
return obs_module_text("VTProResEncSW");
|
||||
}
|
||||
return type_data->disp_name;
|
||||
}
|
||||
@ -982,6 +1098,7 @@ static const char *vt_getname(void *data)
|
||||
#define TEXT_PROFILE obs_module_text("Profile")
|
||||
#define TEXT_BFRAMES obs_module_text("UseBFrames")
|
||||
#define TEXT_RATE_CONTROL obs_module_text("RateControl")
|
||||
#define TEXT_PRORES_CODEC obs_module_text("ProResCodec")
|
||||
|
||||
static bool rate_control_limit_bitrate_modified(obs_properties_t *ppts,
|
||||
obs_property_t *p,
|
||||
@ -1095,6 +1212,64 @@ static obs_properties_t *vt_properties_h26x(void *unused, void *data)
|
||||
return props;
|
||||
}
|
||||
|
||||
static obs_properties_t *vt_properties_prores(void *unused, void *data)
|
||||
{
|
||||
UNUSED_PARAMETER(unused);
|
||||
struct vt_encoder_type_data *type_data = data;
|
||||
|
||||
obs_properties_t *props = obs_properties_create();
|
||||
obs_property_t *p;
|
||||
|
||||
p = obs_properties_add_list(props, "codec_type", TEXT_PRORES_CODEC,
|
||||
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
|
||||
|
||||
uint32_t codec_availability_flags = 0;
|
||||
|
||||
size_t size = 0;
|
||||
struct vt_prores_encoder_data *encoder_list = NULL;
|
||||
if (type_data->hardware_accelerated) {
|
||||
size = vt_prores_hardware_encoder_list.num;
|
||||
encoder_list = vt_prores_hardware_encoder_list.array;
|
||||
} else {
|
||||
size = vt_prores_software_encoder_list.num;
|
||||
encoder_list = vt_prores_software_encoder_list.array;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
|
||||
switch (encoder_list[i].codec_type) {
|
||||
case kCMVideoCodecType_AppleProRes422Proxy:
|
||||
codec_availability_flags |= (1 << 0);
|
||||
break;
|
||||
case kCMVideoCodecType_AppleProRes422LT:
|
||||
codec_availability_flags |= (1 << 1);
|
||||
break;
|
||||
case kCMVideoCodecType_AppleProRes422:
|
||||
codec_availability_flags |= (1 << 2);
|
||||
break;
|
||||
case kCMVideoCodecType_AppleProRes422HQ:
|
||||
codec_availability_flags |= (1 << 3);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (codec_availability_flags & (1 << 0))
|
||||
obs_property_list_add_int(
|
||||
p, obs_module_text("ProRes422Proxy"),
|
||||
kCMVideoCodecType_AppleProRes422Proxy);
|
||||
if (codec_availability_flags & (1 << 1))
|
||||
obs_property_list_add_int(p, obs_module_text("ProRes422LT"),
|
||||
kCMVideoCodecType_AppleProRes422LT);
|
||||
if (codec_availability_flags & (1 << 2))
|
||||
obs_property_list_add_int(p, obs_module_text("ProRes422"),
|
||||
kCMVideoCodecType_AppleProRes422);
|
||||
if (codec_availability_flags & (1 << 3))
|
||||
obs_property_list_add_int(p, obs_module_text("ProRes422HQ"),
|
||||
kCMVideoCodecType_AppleProRes422HQ);
|
||||
|
||||
return props;
|
||||
}
|
||||
|
||||
static void vt_defaults(obs_data_t *settings, void *data)
|
||||
{
|
||||
struct vt_encoder_type_data *type_data = data;
|
||||
@ -1115,6 +1290,8 @@ static void vt_defaults(obs_data_t *settings, void *data)
|
||||
obs_data_set_default_double(settings, "max_bitrate_window", 1.5f);
|
||||
obs_data_set_default_int(settings, "keyint_sec", 0);
|
||||
obs_data_set_default_string(settings, "profile", "main");
|
||||
obs_data_set_default_int(settings, "codec_type",
|
||||
kCMVideoCodecType_AppleProRes422);
|
||||
obs_data_set_default_bool(settings, "bframes", true);
|
||||
}
|
||||
|
||||
@ -1127,6 +1304,58 @@ static void vt_free_type_data(void *data)
|
||||
bfree(type_data);
|
||||
}
|
||||
|
||||
static inline void
|
||||
vt_add_prores_encoder_data_to_list(CFDictionaryRef encoder_dict,
|
||||
FourCharCode codec_type)
|
||||
{
|
||||
struct vt_prores_encoder_data *encoder_data = NULL;
|
||||
|
||||
CFBooleanRef hardware_accelerated = CFDictionaryGetValue(
|
||||
encoder_dict, kVTVideoEncoderList_IsHardwareAccelerated);
|
||||
if (hardware_accelerated == kCFBooleanTrue)
|
||||
encoder_data =
|
||||
da_push_back_new(vt_prores_hardware_encoder_list);
|
||||
else
|
||||
encoder_data =
|
||||
da_push_back_new(vt_prores_software_encoder_list);
|
||||
|
||||
encoder_data->encoder_id = CFDictionaryGetValue(
|
||||
encoder_dict, kVTVideoEncoderList_EncoderID);
|
||||
|
||||
encoder_data->codec_type = codec_type;
|
||||
}
|
||||
|
||||
static CFComparisonResult
|
||||
compare_encoder_list(const void *left_val, const void *right_val, void *unused)
|
||||
{
|
||||
UNUSED_PARAMETER(unused);
|
||||
|
||||
CFDictionaryRef left = (CFDictionaryRef)left_val;
|
||||
CFDictionaryRef right = (CFDictionaryRef)right_val;
|
||||
|
||||
CFNumberRef left_codec_num =
|
||||
CFDictionaryGetValue(left, kVTVideoEncoderList_CodecType);
|
||||
CFNumberRef right_codec_num =
|
||||
CFDictionaryGetValue(right, kVTVideoEncoderList_CodecType);
|
||||
CFComparisonResult result =
|
||||
CFNumberCompare(left_codec_num, right_codec_num, NULL);
|
||||
|
||||
if (result != kCFCompareEqualTo)
|
||||
return result;
|
||||
|
||||
CFBooleanRef left_hardware_accel = CFDictionaryGetValue(
|
||||
left, kVTVideoEncoderList_IsHardwareAccelerated);
|
||||
CFBooleanRef right_hardware_accel = CFDictionaryGetValue(
|
||||
right, kVTVideoEncoderList_IsHardwareAccelerated);
|
||||
|
||||
if (left_hardware_accel == right_hardware_accel)
|
||||
return kCFCompareEqualTo;
|
||||
else if (left_hardware_accel == kCFBooleanTrue)
|
||||
return kCFCompareGreaterThan;
|
||||
else
|
||||
return kCFCompareLessThan;
|
||||
}
|
||||
|
||||
OBS_DECLARE_MODULE()
|
||||
OBS_MODULE_USE_DEFAULT_LOCALE("mac-videotoolbox", "en-US")
|
||||
|
||||
@ -1145,9 +1374,19 @@ bool obs_module_load(void)
|
||||
.caps = OBS_ENCODER_CAP_DYN_BITRATE,
|
||||
};
|
||||
|
||||
CFArrayRef encoder_list;
|
||||
VTCopyVideoEncoderList(NULL, &encoder_list);
|
||||
CFIndex size = CFArrayGetCount(encoder_list);
|
||||
da_init(vt_prores_hardware_encoder_list);
|
||||
da_init(vt_prores_software_encoder_list);
|
||||
|
||||
CFArrayRef encoder_list_const;
|
||||
VTCopyVideoEncoderList(NULL, &encoder_list_const);
|
||||
CFIndex size = CFArrayGetCount(encoder_list_const);
|
||||
|
||||
CFMutableArrayRef encoder_list = CFArrayCreateMutableCopy(
|
||||
kCFAllocatorDefault, size, encoder_list_const);
|
||||
CFRelease(encoder_list_const);
|
||||
|
||||
CFArraySortValues(encoder_list, CFRangeMake(0, size),
|
||||
&compare_encoder_list, NULL);
|
||||
|
||||
for (CFIndex i = 0; i < size; i++) {
|
||||
CFDictionaryRef encoder_dict =
|
||||
@ -1169,13 +1408,31 @@ bool obs_module_load(void)
|
||||
|
||||
switch (codec_type) {
|
||||
case kCMVideoCodecType_H264:
|
||||
info.get_properties2 = vt_properties_h26x;
|
||||
info.codec = "h264";
|
||||
break;
|
||||
#ifdef ENABLE_HEVC
|
||||
case kCMVideoCodecType_HEVC:
|
||||
info.get_properties2 = vt_properties_h26x;
|
||||
info.codec = "hevc";
|
||||
break;
|
||||
#endif
|
||||
// 422 is used as a marker for all ProRes types,
|
||||
// since the type is stored as a profile
|
||||
case kCMVideoCodecType_AppleProRes422:
|
||||
info.get_properties2 = vt_properties_prores;
|
||||
info.codec = "prores";
|
||||
vt_add_prores_encoder_data_to_list(encoder_dict,
|
||||
codec_type);
|
||||
break;
|
||||
|
||||
case kCMVideoCodecType_AppleProRes422Proxy:
|
||||
case kCMVideoCodecType_AppleProRes422LT:
|
||||
case kCMVideoCodecType_AppleProRes422HQ:
|
||||
vt_add_prores_encoder_data_to_list(encoder_dict,
|
||||
codec_type);
|
||||
continue;
|
||||
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
@ -1198,7 +1455,6 @@ bool obs_module_load(void)
|
||||
type_data->codec_type = codec_type;
|
||||
type_data->hardware_accelerated = hardware_accelerated;
|
||||
info.type_data = type_data;
|
||||
info.get_properties2 = vt_properties_h26x;
|
||||
|
||||
obs_register_encoder(&info);
|
||||
#undef VT_DICTSTR
|
||||
@ -1210,3 +1466,9 @@ bool obs_module_load(void)
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void obs_module_unload(void)
|
||||
{
|
||||
da_free(vt_prores_hardware_encoder_list);
|
||||
da_free(vt_prores_software_encoder_list);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user