|
|
@@ -95,6 +95,7 @@ struct amf_base {
|
|
|
AMFBufferPtr packet_data;
|
|
|
AMFRate amf_frame_rate;
|
|
|
AMFBufferPtr header;
|
|
|
+ AMFSurfacePtr roi_map;
|
|
|
|
|
|
std::deque<AMFDataPtr> queued_packets;
|
|
|
|
|
|
@@ -110,11 +111,13 @@ struct amf_base {
|
|
|
uint32_t cx;
|
|
|
uint32_t cy;
|
|
|
uint32_t linesize = 0;
|
|
|
+ uint32_t roi_increment = 0;
|
|
|
int fps_num;
|
|
|
int fps_den;
|
|
|
bool full_range;
|
|
|
bool bframes_supported = false;
|
|
|
bool first_update = true;
|
|
|
+ bool roi_supported = false;
|
|
|
|
|
|
inline amf_base(bool fallback) : fallback(fallback) {}
|
|
|
virtual ~amf_base() = default;
|
|
|
@@ -580,6 +583,100 @@ static void convert_to_encoder_packet(amf_base *enc, AMFDataPtr &data,
|
|
|
#define SEC_TO_NSEC 1000000000ULL
|
|
|
#endif
|
|
|
|
|
|
+struct roi_params {
|
|
|
+ uint32_t mb_width;
|
|
|
+ uint32_t mb_height;
|
|
|
+ amf_int32 pitch;
|
|
|
+ bool h264;
|
|
|
+ amf_uint32 *buf;
|
|
|
+};
|
|
|
+
|
|
|
+static void roi_cb(void *param, obs_encoder_roi *roi)
|
|
|
+{
|
|
|
+ const roi_params *rp = static_cast<roi_params *>(param);
|
|
|
+
|
|
|
+ /* AMF does not support negative priority */
|
|
|
+ if (roi->priority < 0)
|
|
|
+ return;
|
|
|
+
|
|
|
+ const uint32_t mb_size = rp->h264 ? 16 : 64;
|
|
|
+ const uint32_t roi_left = roi->left / mb_size;
|
|
|
+ const uint32_t roi_top = roi->top / mb_size;
|
|
|
+ const uint32_t roi_right = (roi->right - 1) / mb_size;
|
|
|
+ const uint32_t roi_bottom = (roi->bottom - 1) / mb_size;
|
|
|
+ /* Importance value range is 0..10 */
|
|
|
+ const amf_uint32 priority = (amf_uint32)(10.0f * roi->priority);
|
|
|
+
|
|
|
+ for (uint32_t mb_y = 0; mb_y < rp->mb_height; mb_y++) {
|
|
|
+ if (mb_y < roi_top || mb_y > roi_bottom)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ for (uint32_t mb_x = 0; mb_x < rp->mb_width; mb_x++) {
|
|
|
+ if (mb_x < roi_left || mb_x > roi_right)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ rp->buf[mb_y * rp->pitch / sizeof(amf_uint32) + mb_x] =
|
|
|
+ priority;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static void create_roi(amf_base *enc, AMFSurface *amf_surf)
|
|
|
+{
|
|
|
+ uint32_t mb_size = 16; /* H.264 is always 16x16 */
|
|
|
+ if (enc->codec == amf_codec_type::HEVC ||
|
|
|
+ enc->codec == amf_codec_type::AV1)
|
|
|
+ mb_size = 64; /* AMF HEVC & AV1 use 64x64 blocks */
|
|
|
+
|
|
|
+ const uint32_t mb_width = (enc->cx + mb_size - 1) / mb_size;
|
|
|
+ const uint32_t mb_height = (enc->cy + mb_size - 1) / mb_size;
|
|
|
+
|
|
|
+ if (!enc->roi_map) {
|
|
|
+ AMFContext1Ptr context1(enc->amf_context);
|
|
|
+ AMF_RESULT res = context1->AllocSurfaceEx(
|
|
|
+ AMF_MEMORY_HOST, AMF_SURFACE_GRAY32, mb_width,
|
|
|
+ mb_height,
|
|
|
+ AMF_SURFACE_USAGE_DEFAULT | AMF_SURFACE_USAGE_LINEAR,
|
|
|
+ AMF_MEMORY_CPU_DEFAULT, &enc->roi_map);
|
|
|
+
|
|
|
+ if (res != AMF_OK) {
|
|
|
+ warn("Failed allocating surface for ROI map!");
|
|
|
+ /* Clear ROI to prevent failure the next frame */
|
|
|
+ obs_encoder_clear_roi(enc->encoder);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* This is just following the SimpleROI example. */
|
|
|
+ amf_uint32 *pBuf =
|
|
|
+ (amf_uint32 *)enc->roi_map->GetPlaneAt(0)->GetNative();
|
|
|
+ amf_int32 pitch = enc->roi_map->GetPlaneAt(0)->GetHPitch();
|
|
|
+ memset(pBuf, 0, pitch * mb_height);
|
|
|
+
|
|
|
+ roi_params par{mb_width, mb_height, pitch,
|
|
|
+ enc->codec == amf_codec_type::AVC, pBuf};
|
|
|
+ obs_encoder_enum_roi(enc->encoder, roi_cb, &par);
|
|
|
+
|
|
|
+ enc->roi_increment = obs_encoder_get_roi_increment(enc->encoder);
|
|
|
+}
|
|
|
+
|
|
|
+static void add_roi(amf_base *enc, AMFSurface *amf_surf)
|
|
|
+{
|
|
|
+ const uint32_t increment = obs_encoder_get_roi_increment(enc->encoder);
|
|
|
+
|
|
|
+ if (increment != enc->roi_increment || !enc->roi_increment)
|
|
|
+ create_roi(enc, amf_surf);
|
|
|
+
|
|
|
+ if (enc->codec == amf_codec_type::AVC)
|
|
|
+ amf_surf->SetProperty(AMF_VIDEO_ENCODER_ROI_DATA, enc->roi_map);
|
|
|
+ else if (enc->codec == amf_codec_type::HEVC)
|
|
|
+ amf_surf->SetProperty(AMF_VIDEO_ENCODER_HEVC_ROI_DATA,
|
|
|
+ enc->roi_map);
|
|
|
+ else if (enc->codec == amf_codec_type::AV1)
|
|
|
+ amf_surf->SetProperty(AMF_VIDEO_ENCODER_AV1_ROI_DATA,
|
|
|
+ enc->roi_map);
|
|
|
+}
|
|
|
+
|
|
|
static void amf_encode_base(amf_base *enc, AMFSurface *amf_surf,
|
|
|
encoder_packet *packet, bool *received_packet)
|
|
|
{
|
|
|
@@ -591,6 +688,11 @@ static void amf_encode_base(amf_base *enc, AMFSurface *amf_surf,
|
|
|
|
|
|
bool waiting = true;
|
|
|
while (waiting) {
|
|
|
+ /* ----------------------------------- */
|
|
|
+ /* add ROI data (if any) */
|
|
|
+ if (enc->roi_supported && obs_encoder_has_roi(enc->encoder))
|
|
|
+ add_roi(enc, amf_surf);
|
|
|
+
|
|
|
/* ----------------------------------- */
|
|
|
/* submit frame */
|
|
|
|
|
|
@@ -1377,6 +1479,8 @@ static void amf_avc_create_internal(amf_base *enc, obs_data_t *settings)
|
|
|
&enc->max_throughput);
|
|
|
caps->GetProperty(AMF_VIDEO_ENCODER_CAP_REQUESTED_THROUGHPUT,
|
|
|
&enc->requested_throughput);
|
|
|
+ caps->GetProperty(AMF_VIDEO_ENCODER_CAP_ROI,
|
|
|
+ &enc->roi_supported);
|
|
|
}
|
|
|
|
|
|
const char *preset = obs_data_get_string(settings, "preset");
|
|
|
@@ -1507,13 +1611,15 @@ static void register_avc()
|
|
|
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 |
|
|
|
- OBS_ENCODER_CAP_DYN_BITRATE;
|
|
|
+ OBS_ENCODER_CAP_DYN_BITRATE |
|
|
|
+ OBS_ENCODER_CAP_ROI;
|
|
|
|
|
|
obs_register_encoder(&amf_encoder_info);
|
|
|
|
|
|
amf_encoder_info.id = "h264_fallback_amf";
|
|
|
amf_encoder_info.caps = OBS_ENCODER_CAP_INTERNAL |
|
|
|
- OBS_ENCODER_CAP_DYN_BITRATE;
|
|
|
+ OBS_ENCODER_CAP_DYN_BITRATE |
|
|
|
+ OBS_ENCODER_CAP_ROI;
|
|
|
amf_encoder_info.encode_texture = nullptr;
|
|
|
amf_encoder_info.create = amf_avc_create_fallback;
|
|
|
amf_encoder_info.encode = amf_encode_fallback;
|
|
|
@@ -1713,6 +1819,8 @@ static void amf_hevc_create_internal(amf_base *enc, obs_data_t *settings)
|
|
|
caps->GetProperty(
|
|
|
AMF_VIDEO_ENCODER_HEVC_CAP_REQUESTED_THROUGHPUT,
|
|
|
&enc->requested_throughput);
|
|
|
+ caps->GetProperty(AMF_VIDEO_ENCODER_HEVC_CAP_ROI,
|
|
|
+ &enc->roi_supported);
|
|
|
}
|
|
|
|
|
|
const bool is10bit = enc->amf_format == AMF_SURFACE_P010;
|
|
|
@@ -1861,13 +1969,15 @@ static void register_hevc()
|
|
|
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 |
|
|
|
- OBS_ENCODER_CAP_DYN_BITRATE;
|
|
|
+ OBS_ENCODER_CAP_DYN_BITRATE |
|
|
|
+ OBS_ENCODER_CAP_ROI;
|
|
|
|
|
|
obs_register_encoder(&amf_encoder_info);
|
|
|
|
|
|
amf_encoder_info.id = "h265_fallback_amf";
|
|
|
amf_encoder_info.caps = OBS_ENCODER_CAP_INTERNAL |
|
|
|
- OBS_ENCODER_CAP_DYN_BITRATE;
|
|
|
+ OBS_ENCODER_CAP_DYN_BITRATE |
|
|
|
+ OBS_ENCODER_CAP_ROI;
|
|
|
amf_encoder_info.encode_texture = nullptr;
|
|
|
amf_encoder_info.create = amf_hevc_create_fallback;
|
|
|
amf_encoder_info.encode = amf_encode_fallback;
|
|
|
@@ -2055,6 +2165,8 @@ static void amf_av1_create_internal(amf_base *enc, obs_data_t *settings)
|
|
|
caps->GetProperty(
|
|
|
AMF_VIDEO_ENCODER_AV1_CAP_REQUESTED_THROUGHPUT,
|
|
|
&enc->requested_throughput);
|
|
|
+ /* For some reason there's no specific CAP for AV1, but should always be supported */
|
|
|
+ enc->roi_supported = true;
|
|
|
}
|
|
|
|
|
|
const bool is10bit = enc->amf_format == AMF_SURFACE_P010;
|
|
|
@@ -2187,13 +2299,15 @@ static void register_av1()
|
|
|
amf_encoder_info.get_properties = amf_av1_properties;
|
|
|
amf_encoder_info.get_extra_data = amf_extra_data;
|
|
|
amf_encoder_info.caps = OBS_ENCODER_CAP_PASS_TEXTURE |
|
|
|
- OBS_ENCODER_CAP_DYN_BITRATE;
|
|
|
+ OBS_ENCODER_CAP_DYN_BITRATE |
|
|
|
+ OBS_ENCODER_CAP_ROI;
|
|
|
|
|
|
obs_register_encoder(&amf_encoder_info);
|
|
|
|
|
|
amf_encoder_info.id = "av1_fallback_amf";
|
|
|
amf_encoder_info.caps = OBS_ENCODER_CAP_INTERNAL |
|
|
|
- OBS_ENCODER_CAP_DYN_BITRATE;
|
|
|
+ OBS_ENCODER_CAP_DYN_BITRATE |
|
|
|
+ OBS_ENCODER_CAP_ROI;
|
|
|
amf_encoder_info.encode_texture = nullptr;
|
|
|
amf_encoder_info.create = amf_av1_create_fallback;
|
|
|
amf_encoder_info.encode = amf_encode_fallback;
|