|
|
@@ -72,6 +72,77 @@ static AMFTrace *amf_trace = nullptr;
|
|
|
static HMODULE amf_module = nullptr;
|
|
|
static uint64_t amf_version = 0;
|
|
|
|
|
|
+/* ================================================================================================================= */
|
|
|
+/* The structure and tables below are used to determine the appropriate minimum encoding level for the codecs. AMF
|
|
|
+ * defaults to the highest level for each codec (AVC, HEVC, AV1), and some client devices will reject playback if the
|
|
|
+ * codec level is higher than its decode abilities.
|
|
|
+ */
|
|
|
+
|
|
|
+struct codec_level_entry {
|
|
|
+ const char *level_str;
|
|
|
+ uint64_t max_luma_sample_rate;
|
|
|
+ uint64_t max_luma_picture_size;
|
|
|
+ amf_int64 amf_level;
|
|
|
+};
|
|
|
+
|
|
|
+// Ensure the table entries are ordered from lowest to highest
|
|
|
+static std::vector<codec_level_entry> avc_levels = {{"1", (uint64_t)1485 * 256, 99 * 256, AMF_H264_LEVEL__1},
|
|
|
+ {"1.1", (uint64_t)3000 * 256, 396 * 256, AMF_H264_LEVEL__1_1},
|
|
|
+ {"1.2", (uint64_t)6000 * 256, 396 * 256, AMF_H264_LEVEL__1_2},
|
|
|
+ {"1.3", (uint64_t)11880 * 256, 396 * 256, AMF_H264_LEVEL__1_3},
|
|
|
+ {"2", (uint64_t)11880 * 256, 396 * 256, AMF_H264_LEVEL__2},
|
|
|
+ {"2.1", (uint64_t)19800 * 256, 792 * 256, AMF_H264_LEVEL__2_1},
|
|
|
+ {"2.2", (uint64_t)20250 * 256, 1620 * 256, AMF_H264_LEVEL__2_2},
|
|
|
+ {"3", (uint64_t)40500 * 256, 1620 * 256, AMF_H264_LEVEL__3},
|
|
|
+ {"3.1", (uint64_t)108000 * 256, 3600 * 256, AMF_H264_LEVEL__3_1},
|
|
|
+ {"3.2", (uint64_t)216000 * 256, 5120 * 256, AMF_H264_LEVEL__3_2},
|
|
|
+ {"4", (uint64_t)245760 * 256, 8192 * 256, AMF_H264_LEVEL__4},
|
|
|
+ {"4.1", (uint64_t)245760 * 256, 8192 * 256, AMF_H264_LEVEL__4_1},
|
|
|
+ {"4.2", (uint64_t)522240 * 256, 8704 * 256, AMF_H264_LEVEL__4_2},
|
|
|
+ {"5", (uint64_t)589824 * 256, 22080 * 256, AMF_H264_LEVEL__5},
|
|
|
+ {"5.1", (uint64_t)983040 * 256, 36864 * 256, AMF_H264_LEVEL__5_1},
|
|
|
+ {"5.2", (uint64_t)2073600 * 256, 36864 * 256, AMF_H264_LEVEL__5_2},
|
|
|
+ {"6", (uint64_t)4177920 * 256, 139264 * 256, AMF_H264_LEVEL__6},
|
|
|
+ {"6.1", (uint64_t)8355840 * 256, 139264 * 256, AMF_H264_LEVEL__6_1},
|
|
|
+ {"6.2", (uint64_t)16711680 * 256, 139264 * 256,
|
|
|
+ AMF_H264_LEVEL__6_2}};
|
|
|
+
|
|
|
+// Ensure the table entries are ordered from lowest to highest
|
|
|
+static std::vector<codec_level_entry> hevc_levels = {
|
|
|
+ {"1", 552960, 36864, AMF_LEVEL_1}, {"2", 3686400, 122880, AMF_LEVEL_2},
|
|
|
+ {"2.1", 7372800, 245760, AMF_LEVEL_2_1}, {"3", 16588800, 552960, AMF_LEVEL_3},
|
|
|
+ {"3.1", 33177600, 983040, AMF_LEVEL_3_1}, {"4", 66846720, 2228224, AMF_LEVEL_4},
|
|
|
+ {"4.1", 133693440, 2228224, AMF_LEVEL_4_1}, {"5", 267386880, 8912896, AMF_LEVEL_5},
|
|
|
+ {"5.1", 534773760, 8912896, AMF_LEVEL_5_1}, {"5.2", 1069547520, 8912896, AMF_LEVEL_5_2},
|
|
|
+ {"6", 1069547520, 35651584, AMF_LEVEL_6}, {"6.1", 2139095040, 35651584, AMF_LEVEL_6_1},
|
|
|
+ {"6.2", 4278190080, 35651584, AMF_LEVEL_6_2}};
|
|
|
+
|
|
|
+/* Ensure the table entries are ordered from lowest to highest.
|
|
|
+ *
|
|
|
+ * The AV1 specification currently defines 14 levels, even though more are available (reserved) such as 4.3 and 7.0.
|
|
|
+ *
|
|
|
+ * AV1 defines MaxDisplayRate and MaxDecodeRate, which correspond to TotalDisplayLumaSampleRate and
|
|
|
+ * TotalDecodedLumaSampleRate, respectively, defined in the specification. For the table below, MaxDecodeRate is being
|
|
|
+ * used because it corresponds to all frames with show_existing_frame=0.
|
|
|
+ *
|
|
|
+ * Refer to the following for more information: https://github.com/AOMediaCodec/av1-spec/blob/master/annex.a.levels.md
|
|
|
+ */
|
|
|
+static std::vector<codec_level_entry> av1_levels = {
|
|
|
+ {"2.0", (uint64_t)5529600, 147456, AMF_VIDEO_ENCODER_AV1_LEVEL_2_0},
|
|
|
+ {"2.1", (uint64_t)10454400, 278784, AMF_VIDEO_ENCODER_AV1_LEVEL_2_1},
|
|
|
+ {"3.0", (uint64_t)24969600, 665856, AMF_VIDEO_ENCODER_AV1_LEVEL_3_0},
|
|
|
+ {"3.1", (uint64_t)39938400, 1065024, AMF_VIDEO_ENCODER_AV1_LEVEL_3_1},
|
|
|
+ {"4.0", (uint64_t)77856768, 2359296, AMF_VIDEO_ENCODER_AV1_LEVEL_4_0},
|
|
|
+ {"4.1", (uint64_t)155713536, 2359296, AMF_VIDEO_ENCODER_AV1_LEVEL_4_1},
|
|
|
+ {"5.0", (uint64_t)273715200, 8912896, AMF_VIDEO_ENCODER_AV1_LEVEL_5_0},
|
|
|
+ {"5.1", (uint64_t)547430400, 8912896, AMF_VIDEO_ENCODER_AV1_LEVEL_5_1},
|
|
|
+ {"5.2", (uint64_t)1094860800, 8912896, AMF_VIDEO_ENCODER_AV1_LEVEL_5_2},
|
|
|
+ {"5.3", (uint64_t)1176502272, 8912896, AMF_VIDEO_ENCODER_AV1_LEVEL_5_3},
|
|
|
+ {"6.0", (uint64_t)1176502272, 35651584, AMF_VIDEO_ENCODER_AV1_LEVEL_6_0},
|
|
|
+ {"6.1", (uint64_t)2189721600, 35651584, AMF_VIDEO_ENCODER_AV1_LEVEL_6_1},
|
|
|
+ {"6.2", (uint64_t)4379443200, 35651584, AMF_VIDEO_ENCODER_AV1_LEVEL_6_2},
|
|
|
+ {"6.3", (uint64_t)4706009088, 35651584, AMF_VIDEO_ENCODER_AV1_LEVEL_6_3}};
|
|
|
+
|
|
|
/* ========================================================================= */
|
|
|
/* Main Implementation */
|
|
|
|
|
|
@@ -1297,6 +1368,57 @@ try {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
+static void amf_set_codec_level(amf_base *enc)
|
|
|
+{
|
|
|
+ uint64_t luma_pic_size = enc->cx * enc->cy;
|
|
|
+ uint64_t luma_sample_rate = luma_pic_size * (enc->fps_num / enc->fps_den);
|
|
|
+ std::vector<codec_level_entry> *levels;
|
|
|
+
|
|
|
+ if (enc->codec == amf_codec_type::AVC) {
|
|
|
+ levels = &avc_levels;
|
|
|
+ } else if (enc->codec == amf_codec_type::HEVC) {
|
|
|
+ levels = &hevc_levels;
|
|
|
+ } else if (enc->codec == amf_codec_type::AV1) {
|
|
|
+ levels = &av1_levels;
|
|
|
+ } else {
|
|
|
+ blog(LOG_ERROR, "%s: Unknown amf_codec_type", __FUNCTION__);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ std::vector<codec_level_entry>::const_iterator level_it = levels->begin();
|
|
|
+
|
|
|
+ // First check if the requested sample rate and/or picture size is too large for the maximum level.
|
|
|
+ if ((luma_sample_rate > levels->back().max_luma_sample_rate) ||
|
|
|
+ (luma_pic_size > levels->back().max_luma_picture_size)) {
|
|
|
+ /* If the calculated sample rate is greater than the highest value supported by the codec, clamp to the
|
|
|
+ * upper limit and log an error.
|
|
|
+ */
|
|
|
+ level_it = --(levels->end());
|
|
|
+ blog(LOG_ERROR,
|
|
|
+ "%s: Luma sample rate %u or luma pic size %u is greater than maximum "
|
|
|
+ "allowed. Setting to level %s.",
|
|
|
+ __FUNCTION__, luma_sample_rate, luma_pic_size, level_it->level_str);
|
|
|
+ } else {
|
|
|
+ // Walk the table and find the lowest codec level value suitable for the given luma sample rate.
|
|
|
+ while (level_it != levels->end()) {
|
|
|
+ if ((luma_sample_rate <= level_it->max_luma_sample_rate) &&
|
|
|
+ (luma_pic_size <= level_it->max_luma_picture_size)) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ ++level_it;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Set the level for the encoder
|
|
|
+ if (enc->codec == amf_codec_type::AVC) {
|
|
|
+ set_avc_property(enc, PROFILE_LEVEL, level_it->amf_level);
|
|
|
+ } else if (enc->codec == amf_codec_type::HEVC) {
|
|
|
+ set_hevc_property(enc, PROFILE_LEVEL, level_it->amf_level);
|
|
|
+ } else if (enc->codec == amf_codec_type::AV1) {
|
|
|
+ set_av1_property(enc, LEVEL, level_it->amf_level);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
static bool amf_avc_init(void *data, obs_data_t *settings)
|
|
|
{
|
|
|
amf_base *enc = (amf_base *)data;
|
|
|
@@ -1349,6 +1471,9 @@ static bool amf_avc_init(void *data, obs_data_t *settings)
|
|
|
|
|
|
set_avc_property(enc, DE_BLOCKING_FILTER, true);
|
|
|
|
|
|
+ // Determine and set the appropriate AVC level
|
|
|
+ amf_set_codec_level(enc);
|
|
|
+
|
|
|
check_preset_compatibility(enc, preset);
|
|
|
|
|
|
const char *ffmpeg_opts = obs_data_get_string(settings, "ffmpeg_opts");
|
|
|
@@ -1646,6 +1771,9 @@ static bool amf_hevc_init(void *data, obs_data_t *settings)
|
|
|
|
|
|
set_hevc_property(enc, GOP_SIZE, gop_size);
|
|
|
|
|
|
+ // Determine and set the appropriate HEVC level
|
|
|
+ amf_set_codec_level(enc);
|
|
|
+
|
|
|
check_preset_compatibility(enc, preset);
|
|
|
|
|
|
const char *ffmpeg_opts = obs_data_get_string(settings, "ffmpeg_opts");
|
|
|
@@ -1993,6 +2121,9 @@ static bool amf_av1_init(void *data, obs_data_t *settings)
|
|
|
|
|
|
set_av1_property(enc, ENFORCE_HRD, true);
|
|
|
|
|
|
+ // Determine and set the appropriate AV1 level
|
|
|
+ amf_set_codec_level(enc);
|
|
|
+
|
|
|
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;
|
|
|
set_av1_property(enc, GOP_SIZE, gop_size);
|