diff --git a/plugins/obs-ffmpeg/texture-amf.cpp b/plugins/obs-ffmpeg/texture-amf.cpp index 0bdc71cc5..ad029897c 100644 --- a/plugins/obs-ffmpeg/texture-amf.cpp +++ b/plugins/obs-ffmpeg/texture-amf.cpp @@ -1160,16 +1160,38 @@ static void check_texture_encode_capability(obs_encoder_t *encoder, #include "texture-amf-opts.hpp" -static void amf_defaults(obs_data_t *settings) +static void amf_avc_defaults(obs_data_t *settings) { + // These are initial recommended settings that may be lowered later + // once we know more info such as the resolution and frame rate. + obs_data_set_default_string(settings, "rate_control", "CBR"); obs_data_set_default_int(settings, "bitrate", 2500); obs_data_set_default_int(settings, "cqp", 20); - obs_data_set_default_string(settings, "rate_control", "CBR"); obs_data_set_default_string(settings, "preset", "quality"); obs_data_set_default_string(settings, "profile", "high"); obs_data_set_default_int(settings, "bf", 3); } +static void amf_hevc_defaults(obs_data_t *settings) +{ + // These are initial recommended settings that may be lowered later + // once we know more info such as the resolution and frame rate. + obs_data_set_default_string(settings, "rate_control", "VBR"); + obs_data_set_default_int(settings, "bitrate", 2500); + obs_data_set_default_int(settings, "cqp", 20); + obs_data_set_default_string(settings, "preset", "quality"); +} + +static void amf_av1_defaults(obs_data_t *settings) +{ + // These are initial recommended settings that may be lowered later + // once we know more info such as the resolution and frame rate. + obs_data_set_default_int(settings, "bitrate", 2500); + obs_data_set_default_int(settings, "cqp", 20); + obs_data_set_default_string(settings, "rate_control", "VBR"); + obs_data_set_default_string(settings, "preset", "highQuality"); +} + static bool rate_control_modified(obs_properties_t *ppts, obs_property_t *p, obs_data_t *settings) { @@ -1229,7 +1251,7 @@ static obs_properties_t *amf_properties_internal(amf_codec_type codec) add_preset("speed"); #undef add_preset - if (amf_codec_type::AVC == codec || amf_codec_type::AV1 == codec) { + if (amf_codec_type::AVC == codec) { p = obs_properties_add_list(props, "profile", obs_module_text("Profile"), OBS_COMBO_TYPE_LIST, @@ -1339,8 +1361,19 @@ static void amf_avc_update_data(amf_base *enc, int rc, int64_t bitrate, set_avc_property(enc, PEAK_BITRATE, bitrate); set_avc_property(enc, VBV_BUFFER_SIZE, bitrate); - if (rc == AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_CBR) { + if (rc == AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_CBR || + rc == AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_HIGH_QUALITY_CBR) { set_avc_property(enc, FILLER_DATA_ENABLE, true); + } else if ( + rc == AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_PEAK_CONSTRAINED_VBR || + rc == AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_HIGH_QUALITY_VBR) { + set_avc_property(enc, PEAK_BITRATE, bitrate * 1.5); + set_avc_property(enc, VBV_BUFFER_SIZE, bitrate * 1.5); + } else if (rc == + AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_LATENCY_CONSTRAINED_VBR) { + int64_t framerate = enc->fps_num / enc->fps_den; + set_avc_property(enc, VBV_BUFFER_SIZE, + (bitrate / framerate) * 1.1); } } else { set_avc_property(enc, QP_I, qp); @@ -1384,10 +1417,50 @@ try { return false; } +static inline void check_recommended_avc_defaults(amf_base *enc, + obs_data_t *settings) +{ + int64_t framerate = enc->fps_num / enc->fps_den; + if ((enc->cx * enc->cy > 1920 * 1088) || (framerate > 60)) { + + // Recommended base defaults + obs_data_set_default_int(settings, "bitrate", 2500); + obs_data_set_default_int(settings, "cqp", 20); + obs_data_set_default_string(settings, "rate_control", "CBR"); + obs_data_set_default_string(settings, "preset", "quality"); + obs_data_set_default_string(settings, "profile", "high"); + obs_data_set_default_int(settings, "bf", 0); + info("Original default settings were lowered according to resolution" + " and framerate."); + } else { + // Recommended high perceptual quality defaults + // that are supported up to 1080p60fps + set_avc_property(enc, PRE_ANALYSIS_ENABLE, true); + set_avc_property(enc, ADAPTIVE_MINIGOP, true); + set_amf_property(enc, AMF_PA_LOOKAHEAD_BUFFER_DEPTH, 20); + set_amf_property(enc, AMF_PA_TAQ_MODE, AMF_PA_TAQ_MODE_2); + set_amf_property(enc, AMF_PA_ENGINE_TYPE, AMF_MEMORY_OPENCL); + info("High perceptual quality defaults:\n" + "\tEnablePreAnalysis: %s\n" + "\tAdaptiveMiniGOP: %s\n" + "\tPALookAheadBufferDepth: %d\n" + "\tPATemporalAQMode: %d\n" + "\tPAEngineType: %s\n", + "true", "true", 20, AMF_PA_TAQ_MODE_2, "OpenCL"); + } +} + static bool amf_avc_init(void *data, obs_data_t *settings) { amf_base *enc = (amf_base *)data; + // We originally set the recommended defaults for high perceptual quality in + // the UI settings. Once the resolution and framerate are available here + // during init, we check if the defaults need to be lowered to the base + // defaults or the remaining recommended high perceptual quality settings can + // be set. + check_recommended_avc_defaults(enc, settings); + int64_t bitrate = obs_data_get_int(settings, "bitrate"); int64_t qp = obs_data_get_int(settings, "cqp"); const char *preset = obs_data_get_string(settings, "preset"); @@ -1417,12 +1490,13 @@ static bool amf_avc_init(void *data, obs_data_t *settings) int rc = get_avc_rate_control(rc_str); set_avc_property(enc, RATE_CONTROL_METHOD, rc); - if (rc != AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_CONSTANT_QP) + if (rc != AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_CONSTANT_QP && + rc != AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_HIGH_QUALITY_VBR && + rc != AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_HIGH_QUALITY_CBR) set_avc_property(enc, ENABLE_VBAQ, true); amf_avc_update_data(enc, rc, bitrate * 1000, qp); - set_avc_property(enc, ENFORCE_HRD, true); set_avc_property(enc, HIGH_MOTION_QUALITY_BOOST_ENABLE, false); int keyint_sec = (int)obs_data_get_int(settings, "keyint_sec"); @@ -1451,17 +1525,17 @@ static bool amf_avc_init(void *data, obs_data_t *settings) if (!ffmpeg_opts || !*ffmpeg_opts) ffmpeg_opts = "(none)"; - info("settings:\n" - "\trate_control: %s\n" - "\tbitrate: %d\n" - "\tcqp: %d\n" - "\tkeyint: %d\n" - "\tpreset: %s\n" - "\tprofile: %s\n" - "\tb-frames: %d\n" - "\twidth: %d\n" - "\theight: %d\n" - "\tparams: %s", + info("Settings:\n" + "\trate_control: %s\n" + "\tbitrate: %d\n" + "\tcqp: %d\n" + "\tkeyint: %d\n" + "\tpreset: %s\n" + "\tprofile: %s\n" + "\tmax b-frames: %d\n" + "\twidth: %d\n" + "\theight: %d\n" + "\toverriding params: %s", rc_str, bitrate, qp, gop_size, preset, profile, bf, enc->cx, enc->cy, ffmpeg_opts); @@ -1614,7 +1688,7 @@ static void register_avc() amf_encoder_info.destroy = amf_destroy; amf_encoder_info.update = amf_avc_update; amf_encoder_info.encode_texture = amf_encode_tex; - amf_encoder_info.get_defaults = amf_defaults; + amf_encoder_info.get_defaults = amf_avc_defaults; amf_encoder_info.get_properties = amf_avc_properties; amf_encoder_info.get_extra_data = amf_extra_data; amf_encoder_info.caps = OBS_ENCODER_CAP_PASS_TEXTURE | @@ -1675,6 +1749,18 @@ static inline int get_hevc_rate_control(const char *rc_str) return AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_CBR; } +static inline int get_hevc_profile(obs_data_t *settings) +{ + const char *profile = obs_data_get_string(settings, "profile"); + + if (astrcmpi(profile, "main") == 0) + return AMF_VIDEO_ENCODER_HEVC_PROFILE_MAIN; + else if (astrcmpi(profile, "main10") == 0) + return AMF_VIDEO_ENCODER_HEVC_PROFILE_MAIN_10; + + return AMF_VIDEO_ENCODER_HEVC_PROFILE_MAIN; +} + static void amf_hevc_update_data(amf_base *enc, int rc, int64_t bitrate, int64_t qp) { @@ -1684,8 +1770,19 @@ static void amf_hevc_update_data(amf_base *enc, int rc, int64_t bitrate, set_hevc_property(enc, PEAK_BITRATE, bitrate); set_hevc_property(enc, VBV_BUFFER_SIZE, bitrate); - if (rc == AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_CBR) { + if (rc == AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_CBR || + rc == AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_HIGH_QUALITY_CBR) { set_hevc_property(enc, FILLER_DATA_ENABLE, true); + } else if ( + rc == AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_PEAK_CONSTRAINED_VBR || + rc == AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_HIGH_QUALITY_VBR) { + set_hevc_property(enc, PEAK_BITRATE, bitrate * 1.5); + set_hevc_property(enc, VBV_BUFFER_SIZE, bitrate * 1.5); + } else if (rc == + AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_LATENCY_CONSTRAINED_VBR) { + int64_t framerate = enc->fps_num / enc->fps_den; + set_hevc_property(enc, VBV_BUFFER_SIZE, + (bitrate / framerate) * 1.1); } } else { set_hevc_property(enc, QP_I, qp); @@ -1728,10 +1825,46 @@ try { return false; } +static inline void check_recommended_hevc_defaults(amf_base *enc, + obs_data_t *settings) +{ + const bool is10bit = enc->amf_format == AMF_SURFACE_P010; + const int64_t framerate = enc->fps_num / enc->fps_den; + if ((enc->cx * enc->cy > 1920 * 1088) || is10bit || (framerate > 60)) { + + // Recommended base defaults + obs_data_set_default_int(settings, "bitrate", 2500); + obs_data_set_default_int(settings, "cqp", 20); + obs_data_set_default_string(settings, "preset", "quality"); + info("Original default settings were lowered according to resolution" + " and framerate."); + } else { + // Recommended high perceptual quality defaults + // that are supported up to 1080p60fps + set_hevc_property(enc, PRE_ANALYSIS_ENABLE, true); + set_amf_property(enc, AMF_PA_LOOKAHEAD_BUFFER_DEPTH, 20); + set_amf_property(enc, AMF_PA_TAQ_MODE, AMF_PA_TAQ_MODE_2); + set_amf_property(enc, AMF_PA_ENGINE_TYPE, AMF_MEMORY_OPENCL); + info("High perceptual quality defaults:\n" + "\tHevcEnablePreAnalysis: %s\n" + "\tPALookAheadBufferDepth: %d\n" + "\tPATemporalAQMode: %d\n" + "\tPAEngineType: %s\n", + "true", 20, AMF_PA_TAQ_MODE_2, "OpenCL"); + } +} + static bool amf_hevc_init(void *data, obs_data_t *settings) { amf_base *enc = (amf_base *)data; + // We originally set the recommended defaults for high perceptual quality in + // the UI settings. Once the resolution and framerate are available here + // during init, we check if the defaults need to be lowered to the base + // defaults or the remaining recommended high perceptual quality settings can + // be set. + check_recommended_hevc_defaults(enc, settings); + int64_t bitrate = obs_data_get_int(settings, "bitrate"); int64_t qp = obs_data_get_int(settings, "cqp"); const char *preset = obs_data_get_string(settings, "preset"); @@ -1740,12 +1873,13 @@ static bool amf_hevc_init(void *data, obs_data_t *settings) int rc = get_hevc_rate_control(rc_str); set_hevc_property(enc, RATE_CONTROL_METHOD, rc); - if (rc != AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_CONSTANT_QP) + if (rc != AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_CONSTANT_QP && + rc != AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_HIGH_QUALITY_VBR && + rc != AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_HIGH_QUALITY_CBR) set_hevc_property(enc, ENABLE_VBAQ, true); amf_hevc_update_data(enc, rc, bitrate * 1000, qp); - set_hevc_property(enc, ENFORCE_HRD, true); set_hevc_property(enc, HIGH_MOTION_QUALITY_BOOST_ENABLE, false); int keyint_sec = (int)obs_data_get_int(settings, "keyint_sec"); @@ -1768,16 +1902,16 @@ static bool amf_hevc_init(void *data, obs_data_t *settings) if (!ffmpeg_opts || !*ffmpeg_opts) ffmpeg_opts = "(none)"; - info("settings:\n" - "\trate_control: %s\n" - "\tbitrate: %d\n" - "\tcqp: %d\n" - "\tkeyint: %d\n" - "\tpreset: %s\n" - "\tprofile: %s\n" - "\twidth: %d\n" - "\theight: %d\n" - "\tparams: %s", + info("Settings:\n" + "\trate_control: %s\n" + "\tbitrate: %d\n" + "\tcqp: %d\n" + "\tkeyint: %d\n" + "\tpreset: %s\n" + "\tprofile: %s\n" + "\twidth: %d\n" + "\theight: %d\n" + "\toverriding params: %s", rc_str, bitrate, qp, gop_size, preset, profile, enc->cx, enc->cy, ffmpeg_opts); @@ -1835,6 +1969,7 @@ static void amf_hevc_create_internal(amf_base *enc, obs_data_t *settings) const bool hlg = is_hlg(enc); const bool is_hdr = pq || hlg; const char *preset = obs_data_get_string(settings, "preset"); + obs_data_set_string(settings, "profile", is10bit ? "main10" : "main"); set_hevc_property(enc, FRAMESIZE, AMFConstructSize(enc->cx, enc->cy)); set_hevc_property(enc, USAGE, AMF_VIDEO_ENCODER_USAGE_TRANSCODING); @@ -1842,9 +1977,7 @@ static void amf_hevc_create_internal(amf_base *enc, obs_data_t *settings) set_hevc_property(enc, COLOR_BIT_DEPTH, is10bit ? AMF_COLOR_BIT_DEPTH_10 : AMF_COLOR_BIT_DEPTH_8); - set_hevc_property(enc, PROFILE, - is10bit ? AMF_VIDEO_ENCODER_HEVC_PROFILE_MAIN_10 - : AMF_VIDEO_ENCODER_HEVC_PROFILE_MAIN); + set_hevc_property(enc, PROFILE, get_hevc_profile(settings)); set_hevc_property(enc, LOWLATENCY_MODE, false); set_hevc_property(enc, OUTPUT_COLOR_PROFILE, enc->amf_color_profile); set_hevc_property(enc, OUTPUT_TRANSFER_CHARACTERISTIC, @@ -1960,6 +2093,17 @@ try { return nullptr; } +/* These are initial recommended settings that may be lowered later + * once we know more info such as the resolution and frame rate. + */ +static void amf_hevc_defaults(obs_data_t *settings) +{ + obs_data_set_default_string(settings, "rate_control", "CBR"); + obs_data_set_default_int(settings, "bitrate", 2500); + obs_data_set_default_int(settings, "cqp", 20); + obs_data_set_default_string(settings, "preset", "quality"); +} + static void register_hevc() { struct obs_encoder_info amf_encoder_info = {}; @@ -1971,7 +2115,7 @@ static void register_hevc() amf_encoder_info.destroy = amf_destroy; amf_encoder_info.update = amf_hevc_update; amf_encoder_info.encode_texture = amf_encode_tex; - amf_encoder_info.get_defaults = amf_defaults; + amf_encoder_info.get_defaults = amf_hevc_defaults; amf_encoder_info.get_properties = amf_hevc_properties; amf_encoder_info.get_extra_data = amf_extra_data; amf_encoder_info.caps = OBS_ENCODER_CAP_PASS_TEXTURE | @@ -2055,12 +2199,19 @@ static void amf_av1_update_data(amf_base *enc, int rc, int64_t bitrate, set_av1_property(enc, PEAK_BITRATE, bitrate); set_av1_property(enc, VBV_BUFFER_SIZE, bitrate); - if (rc == AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_CBR) { + if (rc == AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_CBR || + rc == AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_HIGH_QUALITY_CBR) { set_av1_property(enc, FILLER_DATA, true); } else if ( rc == AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_PEAK_CONSTRAINED_VBR || rc == AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_HIGH_QUALITY_VBR) { set_av1_property(enc, PEAK_BITRATE, bitrate * 1.5); + set_av1_property(enc, VBV_BUFFER_SIZE, bitrate * 1.5); + } else if (rc == + AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_LATENCY_CONSTRAINED_VBR) { + int64_t framerate = enc->fps_num / enc->fps_den; + set_av1_property(enc, VBV_BUFFER_SIZE, + (bitrate / framerate) * 1.1); } } else { int64_t qp = cq_value * 4; @@ -2104,10 +2255,47 @@ try { return false; } +static inline void check_recommended_av1_defaults(amf_base *enc, + obs_data_t *settings) +{ + const bool is10bit = enc->amf_format == AMF_SURFACE_P010; + const int64_t framerate = enc->fps_num / enc->fps_den; + if ((enc->cx * enc->cy > 1920 * 1088) || is10bit || (framerate > 60)) { + + // Recommended base defaults + obs_data_set_default_int(settings, "bitrate", 2500); + obs_data_set_default_int(settings, "cqp", 20); + obs_data_set_default_string(settings, "preset", "balanced"); + obs_data_set_default_string(settings, "profile", "main"); + info("Original default settings were lowered according to resolution" + " and framerate."); + } else { + // Recommended high perceptual quality defaults + // that are supported up to 1080p60fps + set_av1_property(enc, PRE_ANALYSIS_ENABLE, true); + set_amf_property(enc, AMF_PA_LOOKAHEAD_BUFFER_DEPTH, 20); + set_amf_property(enc, AMF_PA_TAQ_MODE, AMF_PA_TAQ_MODE_2); + set_amf_property(enc, AMF_PA_ENGINE_TYPE, AMF_MEMORY_OPENCL); + info("High perceptual quality defaults:\n" + "\tAv1EnablePreAnalysis: %s\n" + "\tPALookAheadBufferDepth: %d\n" + "\tPATemporalAQMode: %d\n" + "\tPAEngineType: %s\n", + "true", 20, AMF_PA_TAQ_MODE_2, "OpenCL"); + } +} + static bool amf_av1_init(void *data, obs_data_t *settings) { amf_base *enc = (amf_base *)data; + // We originally set the recommended defaults for high perceptual quality in + // the UI settings. Once the resolution and framerate are available here + // during init, we check if the defaults need to be lowered to the base + // defaults or the remaining recommended high perceptual quality settings can + // be set. + check_recommended_av1_defaults(enc, settings); + int64_t bitrate = obs_data_get_int(settings, "bitrate"); int64_t qp = obs_data_get_int(settings, "cqp"); const char *preset = obs_data_get_string(settings, "preset"); @@ -2119,8 +2307,6 @@ static bool amf_av1_init(void *data, obs_data_t *settings) amf_av1_update_data(enc, rc, bitrate * 1000, qp); - set_av1_property(enc, ENFORCE_HRD, true); - int keyint_sec = (int)obs_data_get_int(settings, "keyint_sec"); int gop_size = (keyint_sec) ? keyint_sec * enc->fps_num / enc->fps_den : 250; @@ -2140,18 +2326,20 @@ static bool amf_av1_init(void *data, obs_data_t *settings) if (!ffmpeg_opts || !*ffmpeg_opts) ffmpeg_opts = "(none)"; - info("settings:\n" - "\trate_control: %s\n" - "\tbitrate: %d\n" - "\tcqp: %d\n" - "\tkeyint: %d\n" - "\tpreset: %s\n" - "\tprofile: %s\n" - "\twidth: %d\n" - "\theight: %d\n" - "\tparams: %s", + info("Settings:\n" + "\trate_control: %s\n" + "\tbitrate: %d\n" + "\tcqp: %d\n" + "\tkeyint: %d\n" + "\tpreset: %s\n" + "\tprofile: %s\n" + "\twidth: %d\n" + "\theight: %d\n" + "\tscreen content tools: %s\n" + "\tpalette mode: %s\n" + "\toverriding params: %s", rc_str, bitrate, qp, gop_size, preset, profile, enc->cx, enc->cy, - ffmpeg_opts); + "true", "true", ffmpeg_opts); return true; } @@ -2177,6 +2365,7 @@ static void amf_av1_create_internal(amf_base *enc, obs_data_t *settings) const bool is10bit = enc->amf_format == AMF_SURFACE_P010; const char *preset = obs_data_get_string(settings, "preset"); + obs_data_set_string(settings, "profile", "main"); set_av1_property(enc, FRAMESIZE, AMFConstructSize(enc->cx, enc->cy)); set_av1_property(enc, USAGE, AMF_VIDEO_ENCODER_USAGE_TRANSCODING); @@ -2194,7 +2383,8 @@ static void amf_av1_create_internal(amf_base *enc, obs_data_t *settings) set_av1_property(enc, OUTPUT_TRANSFER_CHARACTERISTIC, enc->amf_characteristic); set_av1_property(enc, OUTPUT_COLOR_PRIMARIES, enc->amf_primaries); - set_av1_property(enc, FRAMERATE, enc->amf_frame_rate); + set_av1_property(enc, SCREEN_CONTENT_TOOLS, true); + set_av1_property(enc, PALETTE_MODE, true); amf_av1_init(enc, settings); @@ -2280,13 +2470,15 @@ try { return nullptr; } +/* These are initial recommended settings that may be lowered later + * once we know more info such as the resolution and frame rate. + */ static void amf_av1_defaults(obs_data_t *settings) { obs_data_set_default_int(settings, "bitrate", 2500); obs_data_set_default_int(settings, "cqp", 20); obs_data_set_default_string(settings, "rate_control", "CBR"); - obs_data_set_default_string(settings, "preset", "quality"); - obs_data_set_default_string(settings, "profile", "high"); + obs_data_set_default_string(settings, "preset", "highQuality"); } static void register_av1()