videotoolboxenc: enable constant quality with -q:v on Apple Silicon Macs and use b-frames für HEVC and H264 and b-pyramid for HEVC.

Signed-off-by: Simone Karin Lehmann <simone@lisanet.de>
Signed-off-by: Rick Kern <kernrj@gmail.com>
This commit is contained in:
Simone Karin Lehmann 2021-01-22 21:21:33 +01:00 committed by Rick Kern
parent f47d7a3b42
commit efece4442f
1 changed files with 42 additions and 12 deletions

View File

@ -224,7 +224,7 @@ typedef struct VTEncContext {
int64_t require_sw; int64_t require_sw;
bool flushing; bool flushing;
bool has_b_frames; int has_b_frames;
bool warned_color_range; bool warned_color_range;
/* can't be bool type since AVOption will access it as int */ /* can't be bool type since AVOption will access it as int */
@ -1014,6 +1014,12 @@ static int get_cv_ycbcr_matrix(AVCodecContext *avctx, CFStringRef *matrix) {
return 0; return 0;
} }
// constant quality only on Macs with Apple Silicon
static bool vtenc_qscale_enabled(void)
{
return TARGET_OS_OSX && TARGET_CPU_ARM64;
}
static int vtenc_create_encoder(AVCodecContext *avctx, static int vtenc_create_encoder(AVCodecContext *avctx,
CMVideoCodecType codec_type, CMVideoCodecType codec_type,
CFStringRef profile_level, CFStringRef profile_level,
@ -1025,7 +1031,9 @@ static int vtenc_create_encoder(AVCodecContext *avctx,
VTEncContext *vtctx = avctx->priv_data; VTEncContext *vtctx = avctx->priv_data;
SInt32 bit_rate = avctx->bit_rate; SInt32 bit_rate = avctx->bit_rate;
SInt32 max_rate = avctx->rc_max_rate; SInt32 max_rate = avctx->rc_max_rate;
Float32 quality = avctx->global_quality / FF_QP2LAMBDA;
CFNumberRef bit_rate_num; CFNumberRef bit_rate_num;
CFNumberRef quality_num;
CFNumberRef bytes_per_second; CFNumberRef bytes_per_second;
CFNumberRef one_second; CFNumberRef one_second;
CFArrayRef data_rate_limits; CFArrayRef data_rate_limits;
@ -1056,15 +1064,33 @@ static int vtenc_create_encoder(AVCodecContext *avctx,
return AVERROR_EXTERNAL; return AVERROR_EXTERNAL;
} }
bit_rate_num = CFNumberCreate(kCFAllocatorDefault, if (avctx->flags & AV_CODEC_FLAG_QSCALE && !vtenc_qscale_enabled()) {
kCFNumberSInt32Type, av_log(avctx, AV_LOG_ERROR, "Error: -q:v qscale not available for encoder. Use -b:v bitrate instead.\n");
&bit_rate); return AVERROR_EXTERNAL;
if (!bit_rate_num) return AVERROR(ENOMEM); }
status = VTSessionSetProperty(vtctx->session, if (avctx->flags & AV_CODEC_FLAG_QSCALE) {
kVTCompressionPropertyKey_AverageBitRate, quality = quality >= 100 ? 1.0 : quality / 100;
bit_rate_num); quality_num = CFNumberCreate(kCFAllocatorDefault,
CFRelease(bit_rate_num); kCFNumberFloat32Type,
&quality);
if (!quality_num) return AVERROR(ENOMEM);
status = VTSessionSetProperty(vtctx->session,
kVTCompressionPropertyKey_Quality,
quality_num);
CFRelease(quality_num);
} else {
bit_rate_num = CFNumberCreate(kCFAllocatorDefault,
kCFNumberSInt32Type,
&bit_rate);
if (!bit_rate_num) return AVERROR(ENOMEM);
status = VTSessionSetProperty(vtctx->session,
kVTCompressionPropertyKey_AverageBitRate,
bit_rate_num);
CFRelease(bit_rate_num);
}
if (status) { if (status) {
av_log(avctx, AV_LOG_ERROR, "Error setting bitrate property: %d\n", status); av_log(avctx, AV_LOG_ERROR, "Error setting bitrate property: %d\n", status);
@ -1333,6 +1359,7 @@ static int vtenc_configure_encoder(AVCodecContext *avctx)
} }
vtctx->codec_id = avctx->codec_id; vtctx->codec_id = avctx->codec_id;
avctx->max_b_frames = 16;
if (vtctx->codec_id == AV_CODEC_ID_H264) { if (vtctx->codec_id == AV_CODEC_ID_H264) {
vtctx->get_param_set_func = CMVideoFormatDescriptionGetH264ParameterSetAtIndex; vtctx->get_param_set_func = CMVideoFormatDescriptionGetH264ParameterSetAtIndex;
@ -1340,7 +1367,7 @@ static int vtenc_configure_encoder(AVCodecContext *avctx)
vtctx->has_b_frames = avctx->max_b_frames > 0; vtctx->has_b_frames = avctx->max_b_frames > 0;
if(vtctx->has_b_frames && vtctx->profile == H264_PROF_BASELINE){ if(vtctx->has_b_frames && vtctx->profile == H264_PROF_BASELINE){
av_log(avctx, AV_LOG_WARNING, "Cannot use B-frames with baseline profile. Output will not contain B-frames.\n"); av_log(avctx, AV_LOG_WARNING, "Cannot use B-frames with baseline profile. Output will not contain B-frames.\n");
vtctx->has_b_frames = false; vtctx->has_b_frames = 0;
} }
if (vtctx->entropy == VT_CABAC && vtctx->profile == H264_PROF_BASELINE) { if (vtctx->entropy == VT_CABAC && vtctx->profile == H264_PROF_BASELINE) {
@ -1353,6 +1380,8 @@ static int vtenc_configure_encoder(AVCodecContext *avctx)
vtctx->get_param_set_func = compat_keys.CMVideoFormatDescriptionGetHEVCParameterSetAtIndex; vtctx->get_param_set_func = compat_keys.CMVideoFormatDescriptionGetHEVCParameterSetAtIndex;
if (!vtctx->get_param_set_func) return AVERROR(EINVAL); if (!vtctx->get_param_set_func) return AVERROR(EINVAL);
if (!get_vt_hevc_profile_level(avctx, &profile_level)) return AVERROR(EINVAL); if (!get_vt_hevc_profile_level(avctx, &profile_level)) return AVERROR(EINVAL);
// HEVC has b-byramid
vtctx->has_b_frames = avctx->max_b_frames > 0 ? 2 : 0;
} }
enc_info = CFDictionaryCreateMutable( enc_info = CFDictionaryCreateMutable(
@ -1448,7 +1477,8 @@ static av_cold int vtenc_init(AVCodecContext *avctx)
if (!status && has_b_frames_cfbool) { if (!status && has_b_frames_cfbool) {
//Some devices don't output B-frames for main profile, even if requested. //Some devices don't output B-frames for main profile, even if requested.
vtctx->has_b_frames = CFBooleanGetValue(has_b_frames_cfbool); // HEVC has b-pyramid
vtctx->has_b_frames = (CFBooleanGetValue(has_b_frames_cfbool) && avctx->codec_id == AV_CODEC_ID_HEVC) ? 2 : 1;
CFRelease(has_b_frames_cfbool); CFRelease(has_b_frames_cfbool);
} }
avctx->has_b_frames = vtctx->has_b_frames; avctx->has_b_frames = vtctx->has_b_frames;
@ -2356,7 +2386,7 @@ static av_cold int vtenc_frame(
if (vtctx->frame_ct_in == 0) { if (vtctx->frame_ct_in == 0) {
vtctx->first_pts = frame->pts; vtctx->first_pts = frame->pts;
} else if(vtctx->frame_ct_in == 1 && vtctx->has_b_frames) { } else if(vtctx->frame_ct_in == vtctx->has_b_frames) {
vtctx->dts_delta = frame->pts - vtctx->first_pts; vtctx->dts_delta = frame->pts - vtctx->first_pts;
} }