Browse Source

obs-ffmpeg: Add Pre-Analysis checkbox for AMD encoder

Huts, Roman 2 years ago
parent
commit
c518f69c84
2 changed files with 96 additions and 56 deletions
  1. 2 0
      plugins/obs-ffmpeg/data/locale/en-US.ini
  2. 94 56
      plugins/obs-ffmpeg/texture-amf.cpp

+ 2 - 0
plugins/obs-ffmpeg/data/locale/en-US.ini

@@ -25,6 +25,8 @@ FilePath="File Path"
 
 AMFOpts="AMF/FFmpeg Options"
 AMFOpts.ToolTip="Use to specify custom AMF or FFmpeg options. For example, \"level=5.2 profile=main\". Check the AMF encoder docs for more details."
+AMF.PreAnalysis="Pre-Analysis"
+AMF.PreAnalysis.ToolTip="Enables improved encoding quality at the cost of performance by calculating additional metrics such as activity and spatial complexity."
 
 GPU="GPU"
 BFrames="Max B-frames"

+ 94 - 56
plugins/obs-ffmpeg/texture-amf.cpp

@@ -543,6 +543,38 @@ static inline int64_t convert_to_obs_ts(amf_base *enc, int64_t ts)
 	return ts * (int64_t)enc->fps_den / amf_timebase;
 }
 
+static inline const char *convert_amf_memory_type_to_string(AMF_MEMORY_TYPE type)
+{
+	switch (type) {
+	case AMF_MEMORY_UNKNOWN:
+		return "Unknown";
+	case AMF_MEMORY_HOST:
+		return "Host";
+	case AMF_MEMORY_DX9:
+		return "DX9";
+	case AMF_MEMORY_DX11:
+		return "DX11";
+	case AMF_MEMORY_OPENCL:
+		return "OpenCL";
+	case AMF_MEMORY_OPENGL:
+		return "OpenGL";
+	case AMF_MEMORY_XV:
+		return "XV";
+	case AMF_MEMORY_GRALLOC:
+		return "Gralloc";
+	case AMF_MEMORY_COMPUTE_FOR_DX9:
+		return "Compute For DX9";
+	case AMF_MEMORY_COMPUTE_FOR_DX11:
+		return "Compute For DX11";
+	case AMF_MEMORY_VULKAN:
+		return "Vulkan";
+	case AMF_MEMORY_DX12:
+		return "DX12";
+	default:
+		return "Invalid";
+	}
+}
+
 static void convert_to_encoder_packet(amf_base *enc, AMFDataPtr &data, encoder_packet *packet)
 {
 	if (!data)
@@ -1160,10 +1192,10 @@ static void check_texture_encode_capability(obs_encoder_t *encoder, amf_codec_ty
 
 #include "texture-amf-opts.hpp"
 
+/* 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_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);
@@ -1172,26 +1204,6 @@ static void amf_avc_defaults(obs_data_t *settings)
 	obs_data_set_default_int(settings, "bf", 2);
 }
 
-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)
 {
 	const char *rc = obs_data_get_string(settings, "rate_control");
@@ -1256,6 +1268,9 @@ static obs_properties_t *amf_properties_internal(amf_codec_type codec)
 #undef add_profile
 	}
 
+	p = obs_properties_add_bool(props, "pre_analysis", obs_module_text("AMF.PreAnalysis"));
+	obs_property_set_long_description(p, obs_module_text("AMF.PreAnalysis.ToolTip"));
+
 	if (amf_codec_type::AVC == codec || amf_codec_type::AV1 == codec) {
 		obs_properties_add_int(props, "bf", obs_module_text("BFrames"), 0, 5, 1);
 	}
@@ -1352,11 +1367,9 @@ static void amf_avc_update_data(amf_base *enc, int rc, int64_t bitrate, int64_t
 			   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) {
+		} 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);
+			set_avc_property(enc, VBV_BUFFER_SIZE, (bitrate / framerate) * 1.1);
 		}
 	} else {
 		set_avc_property(enc, QP_I, qp);
@@ -1399,12 +1412,10 @@ try {
 	return false;
 }
 
-static inline void adjust_recommended_avc_defaults(amf_base *enc,
-						   obs_data_t *settings)
+static inline void adjust_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);
@@ -1512,6 +1523,11 @@ static bool amf_avc_init(void *data, obs_data_t *settings)
 	const char *profile = obs_data_get_string(settings, "profile");
 	const char *rc_str = obs_data_get_string(settings, "rate_control");
 	int64_t bf = obs_data_get_int(settings, "bf");
+	bool pa_enabled = obs_data_get_bool(settings, "pre_analysis");
+
+	if (pa_enabled) {
+		set_avc_property(enc, PRE_ANALYSIS_ENABLE, pa_enabled);
+	}
 
 	if (enc->bframes_supported) {
 		set_avc_property(enc, MAX_CONSECUTIVE_BPICTURES, bf);
@@ -1521,7 +1537,7 @@ static bool amf_avc_init(void *data, obs_data_t *settings)
 		 * as those with high motion. This only takes effect if
 		 * Pre-Analysis is enabled.
 		 */
-		if (bf > 0) {
+		if (bf > 0 && pa_enabled == true) {
 			set_avc_property(enc, ADAPTIVE_MINIGOP, true);
 		}
 
@@ -1537,8 +1553,9 @@ static bool amf_avc_init(void *data, obs_data_t *settings)
 	set_avc_property(enc, RATE_CONTROL_METHOD, rc);
 	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)
+	    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);
 
@@ -1595,8 +1612,10 @@ static bool amf_avc_init(void *data, obs_data_t *settings)
 	     "\tb-frames:     %d\n"
 	     "\twidth:        %d\n"
 	     "\theight:       %d\n"
+	     "\tpre-analysis: %s\n"
 	     "\tparams:       %s",
-	     rc_str, bitrate, qp, gop_size, preset, profile, level_str, bf, enc->cx, enc->cy, ffmpeg_opts);
+	     rc_str, bitrate, qp, gop_size, preset, profile, level_str, bf, enc->cx, enc->cy,
+	     pa_enabled ? "true" : "false", ffmpeg_opts);
 
 	return true;
 }
@@ -1806,11 +1825,9 @@ static void amf_hevc_update_data(amf_base *enc, int rc, int64_t bitrate, int64_t
 			   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) {
+		} 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);
+			set_hevc_property(enc, VBV_BUFFER_SIZE, (bitrate / framerate) * 1.1);
 		}
 	} else {
 		set_hevc_property(enc, QP_I, qp);
@@ -1852,13 +1869,11 @@ try {
 	return false;
 }
 
-static inline void adjust_recommended_hevc_defaults(amf_base *enc,
-						    obs_data_t *settings)
+static inline void adjust_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);
@@ -1881,12 +1896,18 @@ static bool amf_hevc_init(void *data, obs_data_t *settings)
 	const char *profile = obs_data_get_string(settings, "profile");
 	const char *rc_str = obs_data_get_string(settings, "rate_control");
 	int rc = get_hevc_rate_control(rc_str);
+	bool pa_enabled = obs_data_get_bool(settings, "pre_analysis");
+
+	if (pa_enabled) {
+		set_hevc_property(enc, PRE_ANALYSIS_ENABLE, pa_enabled);
+	}
 
 	set_hevc_property(enc, RATE_CONTROL_METHOD, rc);
 	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)
+	    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);
 
@@ -1936,8 +1957,10 @@ static bool amf_hevc_init(void *data, obs_data_t *settings)
 	     "\tlevel:        %s\n"
 	     "\twidth:        %d\n"
 	     "\theight:       %d\n"
+	     "\tpre-analysis: %s\n"
 	     "\tparams:       %s",
-	     rc_str, bitrate, qp, gop_size, preset, profile, level_str, enc->cx, enc->cy, ffmpeg_opts);
+	     rc_str, bitrate, qp, gop_size, preset, profile, level_str, enc->cx, enc->cy, pa_enabled ? "true" : "false",
+	     ffmpeg_opts);
 
 	return true;
 }
@@ -2098,9 +2121,8 @@ 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.
- */
+/* 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");
@@ -2205,11 +2227,9 @@ static void amf_av1_update_data(amf_base *enc, int rc, int64_t bitrate, int64_t
 			   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) {
+		} 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);
+			set_av1_property(enc, VBV_BUFFER_SIZE, (bitrate / framerate) * 1.1);
 		}
 	} else {
 		int64_t qp = cq_value * 4;
@@ -2252,13 +2272,11 @@ try {
 	return false;
 }
 
-static inline void adjust_recommended_av1_defaults(amf_base *enc,
-						   obs_data_t *settings)
+static inline void adjust_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);
@@ -2282,11 +2300,27 @@ static bool amf_av1_init(void *data, obs_data_t *settings)
 	const char *preset = obs_data_get_string(settings, "preset");
 	const char *profile = obs_data_get_string(settings, "profile");
 	const char *rc_str = obs_data_get_string(settings, "rate_control");
+	bool pa_enabled = obs_data_get_bool(settings, "pre_analysis");
+	bool screen_content_tools_enabled = obs_data_get_bool(settings, "screen_content_tools");
+	bool palette_mode_enabled = obs_data_get_bool(settings, "palette_mode");
 	int64_t bf = obs_data_get_int(settings, "bf");
 
+	if (pa_enabled) {
+		set_av1_property(enc, PRE_ANALYSIS_ENABLE, pa_enabled);
+	}
+
 	if (enc->bframes_supported) {
 		set_av1_property(enc, MAX_CONSECUTIVE_BPICTURES, bf);
 		set_av1_property(enc, B_PIC_PATTERN, bf);
+
+		/* AdaptiveMiniGOP is suggested for some types of content such
+		 * as those with high motion. This only takes effect if
+		 * Pre-Analysis is enabled.
+		 */
+		if (bf > 0 && pa_enabled == true) {
+			set_av1_property(enc, ADAPTIVE_MINIGOP, true);
+		}
+
 	} else if (bf != 0) {
 		warn("B-Frames set to %lld but b-frames are not supported by this device", bf);
 		bf = 0;
@@ -2377,7 +2411,11 @@ 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");
+	const bool enable_screen_content_tools = true;
+	const bool enable_palette_mode = true;
 	obs_data_set_string(settings, "profile", "main");
+	obs_data_set_bool(settings, "screen_content_tools", enable_screen_content_tools);
+	obs_data_set_bool(settings, "palette_mode", enable_palette_mode);
 
 	set_av1_property(enc, FRAMESIZE, AMFConstructSize(enc->cx, enc->cy));
 	set_av1_property(enc, USAGE, AMF_VIDEO_ENCODER_USAGE_TRANSCODING);
@@ -2390,8 +2428,9 @@ static void amf_av1_create_internal(amf_base *enc, obs_data_t *settings)
 	set_av1_property(enc, OUTPUT_COLOR_PROFILE, enc->amf_color_profile);
 	set_av1_property(enc, OUTPUT_TRANSFER_CHARACTERISTIC, enc->amf_characteristic);
 	set_av1_property(enc, OUTPUT_COLOR_PRIMARIES, enc->amf_primaries);
-	set_av1_property(enc, SCREEN_CONTENT_TOOLS, true);
-	set_av1_property(enc, PALETTE_MODE, true);
+	set_av1_property(enc, FRAMERATE, enc->amf_frame_rate);
+	set_av1_property(enc, SCREEN_CONTENT_TOOLS, enable_screen_content_tools);
+	set_av1_property(enc, PALETTE_MODE, enable_palette_mode);
 
 	amf_av1_init(enc, settings);
 
@@ -2481,9 +2520,8 @@ 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.
- */
+/* 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);