0
0
mirror of https://github.com/obsproject/obs-studio.git synced 2024-09-19 20:32:15 +02:00

obs-ffmpeg: Add ROI support to AMF

This commit is contained in:
Rodney 2023-12-24 10:15:37 +01:00 committed by derrod
parent 5e9b2d6322
commit 730ab5e6d1

View File

@ -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;