#include "media/gpu/windows/d3d12_video_encode_av1_delegate.h"
#include <algorithm>
#include <bit>
#include <optional>
#include "base/containers/fixed_flat_map.h"
#include "base/numerics/safe_conversions.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "media/gpu/windows/d3d12_video_helpers.h"
#include "media/gpu/windows/format_utils.h"
#include "third_party/libaom/source/libaom/av1/ratectrl_rtc.h"
namespace media {
namespace {
constexpr uint32_t kDefaultOrderHintBitsMinus1 = 7;
constexpr uint32_t kPrimaryRefNone = 7;
constexpr uint8_t kAV1MinQuantizer = 10;
constexpr uint8_t kAV1MaxQuantizer = 56;
constexpr std::array<uint8_t, 8> kCdefYPriStrength = {9, 12, 0, 6, 2, 4, 1, 2};
constexpr std::array<uint8_t, 8> KCdefYSecStrength = {0, 2, 0, 0, 0, 1, 0, 1};
constexpr std::array<uint8_t, 8> kCdefUVPriStrength = {9, 12, 0, 6, 2, 4, 1, 2};
constexpr std::array<uint8_t, 8> kCdefUvSecStrength = {0, 2, 0, 0, 0, 1, 0, 1};
constexpr auto kVideoCodecProfileToD3D12Profile =
base::MakeFixedFlatMap<VideoCodecProfile, D3D12_VIDEO_ENCODER_AV1_PROFILE>(
{{AV1PROFILE_PROFILE_MAIN, D3D12_VIDEO_ENCODER_AV1_PROFILE_MAIN},
{AV1PROFILE_PROFILE_HIGH, D3D12_VIDEO_ENCODER_AV1_PROFILE_HIGH},
{AV1PROFILE_PROFILE_PRO,
D3D12_VIDEO_ENCODER_AV1_PROFILE_PROFESSIONAL}});
constexpr std::array<int16_t, 256> kAcQuantizerLookup = {
4, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42,
43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,
55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66,
67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78,
79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90,
91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102,
104, 106, 108, 110, 112, 114, 116, 118, 120, 122, 124, 126,
128, 130, 132, 134, 136, 138, 140, 142, 144, 146, 148, 150,
152, 155, 158, 161, 164, 167, 170, 173, 176, 179, 182, 185,
188, 191, 194, 197, 200, 203, 207, 211, 215, 219, 223, 227,
231, 235, 239, 243, 247, 251, 255, 260, 265, 270, 275, 280,
285, 290, 295, 300, 305, 311, 317, 323, 329, 335, 341, 347,
353, 359, 366, 373, 380, 387, 394, 401, 408, 416, 424, 432,
440, 448, 456, 465, 474, 483, 492, 501, 510, 520, 530, 540,
550, 560, 571, 582, 593, 604, 615, 627, 639, 651, 663, 676,
689, 702, 715, 729, 743, 757, 771, 786, 801, 816, 832, 848,
864, 881, 898, 915, 933, 951, 969, 988, 1007, 1026, 1046, 1066,
1087, 1108, 1129, 1151, 1173, 1196, 1219, 1243, 1267, 1292, 1317, 1343,
1369, 1396, 1423, 1451, 1479, 1508, 1537, 1567, 1597, 1628, 1660, 1692,
1725, 1759, 1793, 1828,
};
uint8_t AV1QPtoQindex(uint8_t avenc_qp) {
uint8_t q_index = avenc_qp * 4;
if (q_index == 248) {
q_index = 249;
} else if (q_index == 252) {
q_index = 255;
}
return q_index;
}
AV1BitstreamBuilder::SequenceHeader FillAV1BuilderSequenceHeader(
uint8_t num_temporal_layers,
D3D12_VIDEO_ENCODER_AV1_PROFILE profile,
const D3D12_VIDEO_ENCODER_PICTURE_RESOLUTION_DESC& input_size,
const D3D12_VIDEO_ENCODER_AV1_LEVEL_TIER_CONSTRAINTS& tier_level,
const D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAGS& enabled_features) {
AV1BitstreamBuilder::SequenceHeader sequence_header{};
sequence_header.profile = profile;
sequence_header.operating_points_cnt_minus_1 = num_temporal_layers - 1;
for (uint8_t i = 0; i <= sequence_header.operating_points_cnt_minus_1; i++) {
sequence_header.level[i] = tier_level.Level;
sequence_header.tier[i] = tier_level.Tier;
}
sequence_header.frame_width_bits_minus_1 = 15;
sequence_header.frame_height_bits_minus_1 = 15;
sequence_header.width = input_size.Width;
sequence_header.height = input_size.Height;
sequence_header.order_hint_bits_minus_1 = kDefaultOrderHintBitsMinus1;
sequence_header.use_128x128_superblock = false;
sequence_header.enable_filter_intra = false;
sequence_header.enable_intra_edge_filter = false;
sequence_header.enable_interintra_compound = false;
sequence_header.enable_masked_compound = false;
sequence_header.enable_warped_motion = false;
sequence_header.enable_dual_filter = false;
sequence_header.enable_order_hint = true;
sequence_header.enable_jnt_comp = false;
sequence_header.enable_ref_frame_mvs = false;
sequence_header.enable_superres = false;
sequence_header.enable_cdef = true;
sequence_header.enable_restoration =
!!(enabled_features &
D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_LOOP_RESTORATION_FILTER);
return sequence_header;
}
AV1BitstreamBuilder::FrameHeader FillAV1BuilderFrameHeader(
const D3D12VideoEncodeAV1Delegate::PictureControlFlags& picture_ctrl,
const D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_CODEC_DATA& pic_params,
const AV1BitstreamBuilder::SequenceHeader& sequence_header) {
AV1BitstreamBuilder::FrameHeader frame_header{};
frame_header.frame_type =
pic_params.FrameType == D3D12_VIDEO_ENCODER_AV1_FRAME_TYPE_KEY_FRAME
? libgav1::FrameType::kFrameKey
: libgav1::FrameType::kFrameInter;
frame_header.error_resilient_mode = false;
frame_header.disable_cdf_update = false;
frame_header.disable_frame_end_update_cdf = false;
frame_header.base_qindex = pic_params.Quantization.BaseQIndex;
frame_header.order_hint = pic_params.OrderHint;
frame_header.filter_level[0] =
base::span(pic_params.LoopFilter.LoopFilterLevel)[0];
frame_header.filter_level[1] =
base::span(pic_params.LoopFilter.LoopFilterLevel)[1];
frame_header.filter_level_u = pic_params.LoopFilter.LoopFilterLevelU;
frame_header.filter_level_v = pic_params.LoopFilter.LoopFilterLevelV;
frame_header.sharpness_level = 0;
frame_header.loop_filter_delta_enabled = false;
frame_header.primary_ref_frame = pic_params.PrimaryRefFrame;
for (uint32_t i = 0; i < std::size(pic_params.ReferenceIndices); i++) {
frame_header.ref_frame_idx[i] = base::span(pic_params.ReferenceIndices)[i];
}
frame_header.refresh_frame_flags = pic_params.RefreshFrameFlags;
base::span descriptors = pic_params.ReferenceFramesReconPictureDescriptors;
for (uint32_t i = 0; i < std::size(descriptors); i++) {
frame_header.ref_order_hint[i] = descriptors[i].OrderHint;
}
const auto& cdef = pic_params.CDEF;
CHECK_LE(1u << cdef.CdefBits, std::size(cdef.CdefYPriStrength));
frame_header.cdef_damping_minus_3 = cdef.CdefDampingMinus3 & 0x3;
frame_header.cdef_bits = cdef.CdefBits;
for (uint32_t i = 0; i < (1 << cdef.CdefBits); i++) {
frame_header.cdef_y_pri_strength[i] = base::span(cdef.CdefYPriStrength)[i];
frame_header.cdef_y_sec_strength[i] = base::span(cdef.CdefYSecStrength)[i];
frame_header.cdef_uv_pri_strength[i] =
base::span(cdef.CdefUVPriStrength)[i];
frame_header.cdef_uv_sec_strength[i] =
base::span(cdef.CdefUVSecStrength)[i];
}
frame_header.tx_mode = pic_params.TxMode;
frame_header.reduced_tx_set = false;
frame_header.segmentation_enabled = picture_ctrl.enable_auto_segmentation;
frame_header.allow_screen_content_tools =
picture_ctrl.allow_screen_content_tools;
frame_header.allow_intrabc = picture_ctrl.allow_intrabc;
frame_header.interpolation_filter =
static_cast<libgav1::InterpolationFilter>(pic_params.InterpolationFilter);
if (sequence_header.enable_restoration) {
const auto& restoration_config = pic_params.FrameRestorationConfig;
uint8_t lr_unit_shift = 0;
uint8_t lr_uv_shift = 0;
frame_header.restoration_type[0] =
static_cast<libgav1::LoopRestorationType>(
restoration_config.FrameRestorationType[0]);
frame_header.restoration_type[1] =
static_cast<libgav1::LoopRestorationType>(
restoration_config.FrameRestorationType[1]);
frame_header.restoration_type[2] =
static_cast<libgav1::LoopRestorationType>(
restoration_config.FrameRestorationType[2]);
auto restoration_y_tile_size =
restoration_config.LoopRestorationPixelSize[0];
auto restoration_u_tile_size =
restoration_config.LoopRestorationPixelSize[1];
auto resotration_v_tile_size =
restoration_config.LoopRestorationPixelSize[2];
auto restoration_size_max = std::max({
restoration_y_tile_size,
restoration_u_tile_size,
resotration_v_tile_size,
});
switch (restoration_size_max) {
case D3D12_VIDEO_ENCODER_AV1_RESTORATION_TILESIZE_256x256:
lr_unit_shift = 2;
break;
case D3D12_VIDEO_ENCODER_AV1_RESTORATION_TILESIZE_128x128:
lr_unit_shift = 1;
break;
case D3D12_VIDEO_ENCODER_AV1_RESTORATION_TILESIZE_64x64:
case D3D12_VIDEO_ENCODER_AV1_RESTORATION_TILESIZE_DISABLED:
lr_unit_shift = 0;
break;
default:
NOTREACHED();
}
if (restoration_u_tile_size == restoration_y_tile_size ||
resotration_v_tile_size == restoration_y_tile_size) {
lr_uv_shift = 0;
} else {
lr_uv_shift = 1;
}
frame_header.lr_unit_shift = lr_unit_shift;
frame_header.lr_uv_shift = lr_uv_shift;
}
return frame_header;
}
std::string PrintPostEncodeValues(
const D3D12_VIDEO_ENCODER_AV1_POST_ENCODE_VALUES& post_encode_values) {
auto join_uint8_array = [](const auto& arr) {
std::string result;
for (size_t i = 0; i < std::size(arr); ++i) {
if (i > 0) {
result += ", ";
}
result += base::NumberToString(static_cast<int32_t>(arr[i]));
}
return result;
};
auto print_segments_enabled_features = [&]() {
std::string segs;
for (size_t i = 0;
i < std::size(post_encode_values.SegmentationConfig.SegmentsData);
++i) {
segs += "\n [" + base::NumberToString(i) + "]: ";
segs += base::NumberToString(static_cast<uint32_t>(
base::span(post_encode_values.SegmentationConfig.SegmentsData)[i]
.EnabledFeatures));
segs += " FeatureValue[";
for (size_t j = 0;
j <
std::size(
base::span(post_encode_values.SegmentationConfig.SegmentsData)[i]
.FeatureValue);
++j) {
if (j > 0) {
segs += ", ";
}
segs += base::NumberToString(static_cast<int32_t>(base::span(
base::span(post_encode_values.SegmentationConfig.SegmentsData)[i]
.FeatureValue)[j]));
}
segs += "]";
}
return segs;
};
auto print_reference_indices = [&]() {
std::string refs;
for (size_t i = 0; i < std::size(post_encode_values.ReferenceIndices);
++i) {
refs += "\n [" + base::NumberToString(i) + "]: ";
refs += base::NumberToString(static_cast<uint32_t>(
base::span(post_encode_values.ReferenceIndices)[i]));
}
return refs;
};
return base::StringPrintf(
"\n[Post Encode Values]:\n"
"CDEF:\n"
" CdefBits=%llu\n"
" CdefDampingMinus3=%llu\n"
" CdefYPriStrength=%s\n"
" CdefYSecStrength=%s\n"
" CdefUVPriStrength=%s\n"
" CdefUVSecStrength=%s\n"
"LoopFilter:\n"
" LoopFilterLevel=%s\n"
" LoopFilterLevelU=%llu\n"
" LoopFilterLevelV=%llu\n"
" LoopFilterSharpnessLevel=%llu\n"
" LoopFilterDeltaEnabled=%llu\n"
" UpdateRefDelta=%llu\n"
" RefDeltas=%s\n"
" UpdateModeDelta=%llu\n"
" ModeDeltas=%s\n"
"Quantization:\n"
" BaseQIndex=%llu\n"
" YDCDeltaQ=%lld\n"
" UDCDeltaQ=%lld\n"
" UACDeltaQ=%lld\n"
" VDCDeltaQ=%lld\n"
" VACDeltaQ=%lld\n"
"QuantizationDelta:\n"
" DeltaQPresent=%llu\n"
" DeltaQRes=%llu\n"
"CompoundPredictionType: %llu\n"
"SegmentationConfig:\n"
" NumSegments=%llu\n"
" UpdateMap=%llu\n"
" TemporalUpdate=%llu\n"
" UpdateData=%llu\n"
" SegmentsData.EnabledFeatures:%s\n"
"PrimaryRefFrame: %llu\n"
"ReferenceIndices:%s\n",
post_encode_values.CDEF.CdefBits,
post_encode_values.CDEF.CdefDampingMinus3,
join_uint8_array(post_encode_values.CDEF.CdefYPriStrength).c_str(),
join_uint8_array(post_encode_values.CDEF.CdefYSecStrength).c_str(),
join_uint8_array(post_encode_values.CDEF.CdefUVPriStrength).c_str(),
join_uint8_array(post_encode_values.CDEF.CdefUVSecStrength).c_str(),
join_uint8_array(post_encode_values.LoopFilter.LoopFilterLevel).c_str(),
post_encode_values.LoopFilter.LoopFilterLevelU,
post_encode_values.LoopFilter.LoopFilterLevelV,
post_encode_values.LoopFilter.LoopFilterSharpnessLevel,
post_encode_values.LoopFilter.LoopFilterDeltaEnabled,
post_encode_values.LoopFilter.UpdateRefDelta,
join_uint8_array(post_encode_values.LoopFilter.RefDeltas).c_str(),
post_encode_values.LoopFilter.UpdateModeDelta,
join_uint8_array(post_encode_values.LoopFilter.ModeDeltas).c_str(),
post_encode_values.Quantization.BaseQIndex,
post_encode_values.Quantization.YDCDeltaQ,
post_encode_values.Quantization.UDCDeltaQ,
post_encode_values.Quantization.UACDeltaQ,
post_encode_values.Quantization.VDCDeltaQ,
post_encode_values.Quantization.VACDeltaQ,
static_cast<uint64_t>(post_encode_values.QuantizationDelta.DeltaQPresent),
static_cast<uint64_t>(post_encode_values.QuantizationDelta.DeltaQRes),
static_cast<uint64_t>(post_encode_values.CompoundPredictionType),
static_cast<uint64_t>(post_encode_values.SegmentationConfig.NumSegments),
static_cast<uint64_t>(post_encode_values.SegmentationConfig.UpdateMap),
static_cast<uint64_t>(
post_encode_values.SegmentationConfig.TemporalUpdate),
static_cast<uint64_t>(post_encode_values.SegmentationConfig.UpdateData),
print_segments_enabled_features().c_str(),
static_cast<uint64_t>(post_encode_values.PrimaryRefFrame),
print_reference_indices().c_str());
}
aom::AV1RateControlRtcConfig ConvertToRateControlConfig(
bool is_screen,
const VideoBitrateAllocation& bitrate_allocation,
const D3D12_VIDEO_ENCODER_PICTURE_RESOLUTION_DESC& resolution,
uint32_t frame_rate,
int num_temporal_layers) {
aom::AV1RateControlRtcConfig rc_config{};
rc_config.buf_initial_sz = 600;
rc_config.buf_optimal_sz = 600;
rc_config.buf_sz = 1000;
rc_config.undershoot_pct = 50;
rc_config.overshoot_pct = 50;
rc_config.aq_mode = 0;
rc_config.max_intra_bitrate_pct = 50;
rc_config.max_inter_bitrate_pct = 0;
rc_config.width = resolution.Width;
rc_config.height = resolution.Height;
rc_config.target_bandwidth = bitrate_allocation.GetSumBps() / 1000.;
rc_config.framerate = frame_rate;
rc_config.max_quantizer = kAV1MaxQuantizer;
rc_config.min_quantizer = kAV1MinQuantizer;
rc_config.ss_number_layers = 1;
rc_config.ts_number_layers = num_temporal_layers;
int bitrate_sum = 0;
CHECK_LT(static_cast<size_t>(rc_config.ts_number_layers),
VideoBitrateAllocation::kMaxTemporalLayers);
for (int tid = 0; tid < rc_config.ts_number_layers; ++tid) {
bitrate_sum += bitrate_allocation.GetBitrateBps(0, tid);
base::span(rc_config.layer_target_bitrate)[tid] = bitrate_sum / 1000;
base::span(rc_config.ts_rate_decimator)[tid] =
1u << (num_temporal_layers - tid - 1);
base::span(rc_config.max_quantizers)[tid] = rc_config.max_quantizer;
base::span(rc_config.min_quantizers)[tid] = rc_config.min_quantizer;
}
rc_config.is_screen = is_screen;
return rc_config;
}
EncoderStatus::Or<D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAGS> GetEnabledAV1Features(
bool is_screen,
const D3D12_VIDEO_ENCODER_AV1_CODEC_CONFIGURATION_SUPPORT&
config_support_limit) {
const auto& supported_features = config_support_limit.SupportedFeatureFlags;
const auto& required_features = config_support_limit.RequiredFeatureFlags;
if ((supported_features & required_features) != required_features) {
return {EncoderStatus::Codes::kEncoderHardwareDriverError,
base::StringPrintf(" d3d12 driver doesn't support %x .",
required_features)};
}
const std::array<D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAGS, 2> expected_flgs = {
D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_CDEF_FILTERING,
D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_ORDER_HINT_TOOLS,
};
auto enabled_features = required_features;
for (const auto feature : expected_flgs) {
if (!(supported_features & feature)) {
return {
EncoderStatus::Codes::kEncoderHardwareDriverError,
base::StringPrintf(" d3d12 driver doesn't support %x .", feature)};
}
enabled_features |= feature;
}
if (is_screen) {
auto scc_tools = D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_PALETTE_ENCODING |
D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_INTRA_BLOCK_COPY;
if ((supported_features & scc_tools) == scc_tools) {
enabled_features |= scc_tools;
}
}
return enabled_features;
}
std::pair<D3D12_VIDEO_ENCODER_AV1_RESTORATION_TYPE,
D3D12_VIDEO_ENCODER_AV1_RESTORATION_TILESIZE>
SelectBestRestoration(
base::span<const D3D12_VIDEO_ENCODER_AV1_RESTORATION_SUPPORT_FLAGS>
supported_per_type) {
static constexpr std::array<D3D12_VIDEO_ENCODER_AV1_RESTORATION_TYPE, 3>
restoration_type_preferences = {
D3D12_VIDEO_ENCODER_AV1_RESTORATION_TYPE_WIENER,
D3D12_VIDEO_ENCODER_AV1_RESTORATION_TYPE_SGRPROJ,
D3D12_VIDEO_ENCODER_AV1_RESTORATION_TYPE_SWITCHABLE};
static constexpr std::array<
std::pair<D3D12_VIDEO_ENCODER_AV1_RESTORATION_SUPPORT_FLAGS,
D3D12_VIDEO_ENCODER_AV1_RESTORATION_TILESIZE>,
4>
restoration_tile_size_preferences = {{
{D3D12_VIDEO_ENCODER_AV1_RESTORATION_SUPPORT_FLAG_256x256,
D3D12_VIDEO_ENCODER_AV1_RESTORATION_TILESIZE_256x256},
{D3D12_VIDEO_ENCODER_AV1_RESTORATION_SUPPORT_FLAG_128x128,
D3D12_VIDEO_ENCODER_AV1_RESTORATION_TILESIZE_128x128},
{D3D12_VIDEO_ENCODER_AV1_RESTORATION_SUPPORT_FLAG_64x64,
D3D12_VIDEO_ENCODER_AV1_RESTORATION_TILESIZE_64x64},
{D3D12_VIDEO_ENCODER_AV1_RESTORATION_SUPPORT_FLAG_32x32,
D3D12_VIDEO_ENCODER_AV1_RESTORATION_TILESIZE_32x32},
}};
for (const auto& type : restoration_type_preferences) {
uint32_t mask =
supported_per_type[type -
D3D12_VIDEO_ENCODER_AV1_RESTORATION_TYPE_SWITCHABLE];
for (const auto& pref : restoration_tile_size_preferences) {
if (mask & pref.first) {
return std::make_pair(type, pref.second);
}
}
}
return std::make_pair(D3D12_VIDEO_ENCODER_AV1_RESTORATION_TYPE_DISABLED,
D3D12_VIDEO_ENCODER_AV1_RESTORATION_TILESIZE_DISABLED);
}
EncoderStatus::Or<D3D12VideoEncodeAV1Delegate::D3D12EncodingCapabilities>
GetAV1EncodingCapabilities(
const D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAGS enabled_features,
const D3D12_VIDEO_ENCODER_AV1_CODEC_CONFIGURATION_SUPPORT&
config_support_limit) {
D3D12VideoEncodeAV1Delegate::D3D12EncodingCapabilities encoding_caps{};
encoding_caps.post_value_flags = config_support_limit.PostEncodeValuesFlags;
static constexpr std::array kInterpolationFilters = {
D3D12_VIDEO_ENCODER_AV1_INTERPOLATION_FILTERS_EIGHTTAP,
D3D12_VIDEO_ENCODER_AV1_INTERPOLATION_FILTERS_EIGHTTAP_SMOOTH,
D3D12_VIDEO_ENCODER_AV1_INTERPOLATION_FILTERS_EIGHTTAP_SHARP,
D3D12_VIDEO_ENCODER_AV1_INTERPOLATION_FILTERS_BILINEAR,
D3D12_VIDEO_ENCODER_AV1_INTERPOLATION_FILTERS_SWITCHABLE,
};
auto interpolation_filter_it = std::ranges::find_if(
kInterpolationFilters, [config_support_limit](int filter) {
return config_support_limit.SupportedInterpolationFilters &
(1u << filter);
});
if (interpolation_filter_it == kInterpolationFilters.end()) {
return {EncoderStatus::Codes::kEncoderInitializationError,
"No interpolation filters available."};
}
encoding_caps.interpolation_filter = *interpolation_filter_it;
const base::span supported_tx_modes = config_support_limit.SupportedTxModes;
for (int i = 0; i < 2; ++i) {
if (supported_tx_modes[i] & D3D12_VIDEO_ENCODER_AV1_TX_MODE_FLAG_SELECT) {
encoding_caps.tx_modes[i] = D3D12_VIDEO_ENCODER_AV1_TX_MODE_SELECT;
} else {
encoding_caps.tx_modes[i] = D3D12_VIDEO_ENCODER_AV1_TX_MODE_LARGEST;
}
}
base::span restoration_type =
encoding_caps.loop_restoration.FrameRestorationType;
base::span restoration_pixel_size =
encoding_caps.loop_restoration.LoopRestorationPixelSize;
if (enabled_features &
D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_LOOP_RESTORATION_FILTER) {
for (int plane = 0; plane < 3; ++plane) {
const std::array<D3D12_VIDEO_ENCODER_AV1_RESTORATION_SUPPORT_FLAGS, 3>
supported_per_type = {
UNSAFE_BUFFERS(
config_support_limit.SupportedRestorationParams[0][plane]),
UNSAFE_BUFFERS(
config_support_limit.SupportedRestorationParams[1][plane]),
UNSAFE_BUFFERS(
config_support_limit.SupportedRestorationParams[2][plane])};
const auto& [type, tile_size] =
SelectBestRestoration(base::span(supported_per_type));
restoration_type[plane] = type;
restoration_pixel_size[plane] = tile_size;
}
} else {
std::ranges::fill(restoration_type,
D3D12_VIDEO_ENCODER_AV1_RESTORATION_TYPE_DISABLED);
std::ranges::fill(restoration_pixel_size,
D3D12_VIDEO_ENCODER_AV1_RESTORATION_TILESIZE_DISABLED);
}
return encoding_caps;
}
D3D12VideoEncodeAV1Delegate::PictureControlFlags GetAV1PictureControl(
bool is_keyframe,
const D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAGS enabled_features) {
D3D12VideoEncodeAV1Delegate::PictureControlFlags picture_ctrl;
picture_ctrl.allow_screen_content_tools =
enabled_features & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_PALETTE_ENCODING;
picture_ctrl.allow_intrabc =
is_keyframe ? enabled_features &
D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_INTRA_BLOCK_COPY
: false;
picture_ctrl.enable_auto_segmentation =
enabled_features & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_AUTO_SEGMENTATION;
return picture_ctrl;
}
}
std::vector<std::pair<VideoCodecProfile, std::vector<VideoPixelFormat>>>
D3D12VideoEncodeAV1Delegate::GetSupportedProfiles(
ID3D12VideoDevice3* video_device) {
CHECK(video_device);
std::vector<std::pair<VideoCodecProfile, std::vector<VideoPixelFormat>>>
profiles;
D3D12_FEATURE_DATA_VIDEO_ENCODER_CODEC codec{
.Codec = D3D12_VIDEO_ENCODER_CODEC_AV1};
if (!CheckD3D12VideoEncoderCodec(video_device, &codec).is_ok()) {
return profiles;
}
for (auto [codec_profile, av1_profile] : kVideoCodecProfileToD3D12Profile) {
D3D12_VIDEO_ENCODER_AV1_LEVEL_TIER_CONSTRAINTS min_level;
D3D12_VIDEO_ENCODER_AV1_LEVEL_TIER_CONSTRAINTS max_level;
D3D12_FEATURE_DATA_VIDEO_ENCODER_PROFILE_LEVEL profile_level = {
.Codec = D3D12_VIDEO_ENCODER_CODEC_AV1,
.Profile = {.DataSize = sizeof(av1_profile),
.pAV1Profile = &av1_profile},
.MinSupportedLevel = {.DataSize = sizeof(min_level),
.pAV1LevelSetting = &min_level},
.MaxSupportedLevel = {.DataSize = sizeof(max_level),
.pAV1LevelSetting = &max_level},
};
if (!CheckD3D12VideoEncoderProfileLevel(video_device, &profile_level)
.is_ok()) {
continue;
}
std::vector<VideoPixelFormat> formats;
for (VideoPixelFormat format :
{PIXEL_FORMAT_NV12, PIXEL_FORMAT_P010LE, PIXEL_FORMAT_ABGR}) {
D3D12_FEATURE_DATA_VIDEO_ENCODER_INPUT_FORMAT input_format{
.Codec = D3D12_VIDEO_ENCODER_CODEC_AV1,
.Profile = profile_level.Profile,
.Format = (format == PIXEL_FORMAT_ABGR)
? DXGI_FORMAT_AYUV
: VideoPixelFormatToDxgiFormat(format),
};
if (CheckD3D12VideoEncoderInputFormat(video_device, &input_format)
.is_ok()) {
formats.push_back(format);
}
}
if (!formats.empty()) {
profiles.emplace_back(codec_profile, formats);
}
}
return profiles;
}
D3D12VideoEncodeAV1Delegate::D3D12VideoEncodeAV1Delegate(
Microsoft::WRL::ComPtr<ID3D12VideoDevice3> video_device)
: D3D12VideoEncodeDelegate(std::move(video_device)) {
input_arguments_.SequenceControlDesc.CodecGopSequence = {
.DataSize = sizeof(gop_sequence_),
.pAV1SequenceStructure = &gop_sequence_};
input_arguments_.PictureControlDesc.PictureControlCodecData = {
.DataSize = sizeof(picture_params_), .pAV1PicData = &picture_params_};
}
D3D12VideoEncodeAV1Delegate::~D3D12VideoEncodeAV1Delegate() = default;
size_t D3D12VideoEncodeAV1Delegate::GetMaxNumOfRefFrames() const {
return max_num_ref_frames_;
}
size_t D3D12VideoEncodeAV1Delegate::GetMaxNumOfManualRefBuffers() const {
CHECK_GT(max_num_ref_frames_, 0u);
return max_num_ref_frames_;
}
bool D3D12VideoEncodeAV1Delegate::ReportsAverageQp() const {
return true;
}
EncoderStatus D3D12VideoEncodeAV1Delegate::InitializeVideoEncoder(
const VideoEncodeAccelerator::Config& config) {
DVLOG(3) << base::StringPrintf("%s: config = %s", __func__,
config.AsHumanReadableString());
CHECK_EQ(VideoCodecProfileToVideoCodec(config.output_profile),
VideoCodec::kAV1);
CHECK(!config.HasSpatialLayer());
max_num_ref_frames_ = GetNumTemporalLayers() == 3 ? 2 : 1;
D3D12_FEATURE_DATA_VIDEO_ENCODER_CODEC codec{
.Codec = D3D12_VIDEO_ENCODER_CODEC_AV1};
EncoderStatus status =
CheckD3D12VideoEncoderCodec(video_device_.Get(), &codec);
if (!status.is_ok()) {
return status;
}
auto supported_profiles = GetSupportedProfiles(video_device_.Get());
const auto supported_profile = std::ranges::find_if(
supported_profiles,
[config](
const std::pair<VideoCodecProfile, std::vector<VideoPixelFormat>>&
profile) { return profile.first == config.output_profile; });
if (supported_profiles.end() == supported_profile) {
return {EncoderStatus::Codes::kEncoderUnsupportedProfile,
base::StringPrintf("D3D12VideoEncoder got unsupportted profile: %s",
GetProfileName(config.output_profile))};
}
D3D12_VIDEO_ENCODER_AV1_PROFILE profile =
kVideoCodecProfileToD3D12Profile.at(config.output_profile);
D3D12_VIDEO_ENCODER_AV1_CODEC_CONFIGURATION_SUPPORT config_support_limit{};
D3D12_FEATURE_DATA_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT
codec_config_support{
.Codec = D3D12_VIDEO_ENCODER_CODEC_AV1,
.Profile = {.DataSize = sizeof(profile), .pAV1Profile = &profile},
.CodecSupportLimits = {
.DataSize = sizeof(config_support_limit),
.pAV1Support = &config_support_limit}};
status = CheckD3D12VideoEncoderCodecConfigurationSupport(
video_device_.Get(), &codec_config_support);
if (!status.is_ok()) {
return status;
}
is_screen_ = config.content_type ==
VideoEncodeAccelerator::Config::ContentType::kDisplay;
auto features_or_error =
GetEnabledAV1Features(is_screen_, config_support_limit);
if (!features_or_error.has_value()) {
return std::move(features_or_error).error();
}
enabled_features_ = std::move(features_or_error).value();
DVLOG(3) << base::StringPrintf("Enabled d3d12 encoding feature : %x.",
enabled_features_);
auto enc_caps_or_error =
GetAV1EncodingCapabilities(enabled_features_, config_support_limit);
if (!enc_caps_or_error.has_value()) {
return std::move(enc_caps_or_error).error();
}
enc_caps_ = std::move(enc_caps_or_error).value();
D3D12_VIDEO_ENCODER_AV1_CODEC_CONFIGURATION codec_config = {
.FeatureFlags = enabled_features_,
.OrderHintBitsMinus1 = kDefaultOrderHintBitsMinus1};
if (config.bitrate.mode() == Bitrate::Mode::kConstant ||
config.bitrate.mode() == Bitrate::Mode::kVariable) {
software_brc_ = aom::AV1RateControlRTC::Create(
ConvertToRateControlConfig(is_screen_, bitrate_allocation_, input_size_,
config.framerate, GetNumTemporalLayers()));
rate_control_ = D3D12VideoEncoderRateControl::CreateCqp(
26 , 30 , 30 );
}
CHECK(config.gop_length.has_value());
gop_sequence_ = {.IntraDistance = 0,
.InterFramePeriod = config.gop_length.value()};
D3D12_VIDEO_ENCODER_AV1_LEVEL_TIER_CONSTRAINTS tier_level;
D3D12_FEATURE_DATA_VIDEO_ENCODER_RESOLUTION_SUPPORT_LIMITS
resolution_limits[1];
D3D12_FEATURE_DATA_VIDEO_ENCODER_SUPPORT1 support{
.Codec = D3D12_VIDEO_ENCODER_CODEC_AV1,
.InputFormat = input_format_,
.CodecConfiguration = {.DataSize = sizeof(codec_config),
.pAV1Config = &codec_config},
.CodecGopSequence = {.DataSize = sizeof(gop_sequence_),
.pAV1SequenceStructure = &gop_sequence_},
.RateControl = rate_control_.GetD3D12VideoEncoderRateControl(),
.IntraRefresh = D3D12_VIDEO_ENCODER_INTRA_REFRESH_MODE_NONE,
.SubregionFrameEncoding =
D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_FULL_FRAME,
.ResolutionsListCount = 1,
.pResolutionList = &input_size_,
.MaxReferenceFramesInDPB = max_num_ref_frames_,
.SuggestedProfile = {.DataSize = sizeof(profile),
.pAV1Profile = &profile},
.SuggestedLevel = {.DataSize = sizeof(tier_level),
.pAV1LevelSetting = &tier_level},
.pResolutionDependentSupport = resolution_limits,
.SubregionFrameEncodingData = {.DataSize = sizeof(sub_layout_),
.pTilesPartition_AV1 = &sub_layout_}};
status = CheckD3D12VideoEncoderSupport1(video_device_.Get(), &support);
if (auto subregion_block_size =
resolution_limits[0].SubregionBlockPixelsSize) {
sub_layout_.ColWidths[0] =
(input_size_.Width + subregion_block_size - 1) / subregion_block_size;
sub_layout_.RowHeights[0] =
(input_size_.Height + subregion_block_size - 1) / subregion_block_size;
sub_layout_.RowCount = 1;
sub_layout_.ColCount = 1;
sub_layout_.ContextUpdateTileId = 0;
}
if (support.ValidationFlags &
D3D12_VIDEO_ENCODER_VALIDATION_FLAG_SUBREGION_LAYOUT_DATA_NOT_SUPPORTED) {
support.SubregionFrameEncodingData = {.DataSize = sizeof(sub_layout_),
.pTilesPartition_AV1 = &sub_layout_};
status = CheckD3D12VideoEncoderSupport1(video_device_.Get(), &support);
}
if (!status.is_ok()) {
return status;
}
video_encoder_wrapper_ = video_encoder_wrapper_factory_.Run(
video_device_.Get(), D3D12_VIDEO_ENCODER_CODEC_AV1,
{.DataSize = sizeof(profile), .pAV1Profile = &profile},
{.DataSize = sizeof(tier_level), .pAV1LevelSetting = &tier_level},
input_format_,
{.DataSize = sizeof(codec_config), .pAV1Config = &codec_config},
input_size_);
if (!video_encoder_wrapper_->Initialize(1)) {
return {EncoderStatus::Codes::kEncoderInitializationError,
" Failed to initialize D3D12VideoEncoderWrapper."};
}
bool use_texture_array =
support.SupportFlags &
D3D12_VIDEO_ENCODER_SUPPORT_FLAG_RECONSTRUCTED_FRAMES_REQUIRE_TEXTURE_ARRAYS;
if (!dpb_.InitializeTextureResources(device_.Get(), config.input_visible_size,
input_format_, max_num_ref_frames_,
use_texture_array)) {
return {EncoderStatus::Codes::kEncoderInitializationError,
"Failed to initialize DPB."};
}
if (svc_layers_) {
metadata_.svc_generic.emplace();
}
sequence_header_ =
FillAV1BuilderSequenceHeader(GetNumTemporalLayers(), profile, input_size_,
tier_level, enabled_features_);
picture_id_ = -1;
current_rate_control_ = rate_control_;
return EncoderStatus::Codes::kOk;
}
bool D3D12VideoEncodeAV1Delegate::SupportsRateControlReconfiguration() const {
return false;
}
bool D3D12VideoEncodeAV1Delegate::UpdateRateControl(
const VideoBitrateAllocation& bitrate_allocation,
uint32_t framerate) {
DVLOG(3) << base::StringPrintf("%s: bitrate = %s, framerate = %d.", __func__,
bitrate_allocation.ToString(), framerate);
if (!software_brc_) {
return D3D12VideoEncodeDelegate::UpdateRateControl(bitrate_allocation,
framerate);
}
if (bitrate_allocation.GetMode() != Bitrate::Mode::kConstant &&
bitrate_allocation.GetMode() != Bitrate::Mode::kVariable) {
return false;
}
if (bitrate_allocation != bitrate_allocation_ || framerate != framerate_) {
software_brc_->UpdateRateControl(
ConvertToRateControlConfig(is_screen_, bitrate_allocation, input_size_,
framerate, GetNumTemporalLayers()));
bitrate_allocation_ = bitrate_allocation;
framerate_ = framerate;
}
return true;
}
void D3D12VideoEncodeAV1Delegate::FillPictureControlParams(
const VideoEncoder::EncodeOptions& options) {
base::span picture_params_span = UNSAFE_BUFFERS(base::span(
reinterpret_cast<uint8_t*>(&picture_params_), sizeof(picture_params_)));
std::ranges::fill(picture_params_span, 0);
if (++picture_id_ == static_cast<int>(gop_sequence_.InterFramePeriod) ||
options.key_frame) {
picture_id_ = 0;
}
bool request_keyframe = picture_id_ == 0;
picture_params_.PictureIndex = picture_id_;
picture_ctrl_ = GetAV1PictureControl(request_keyframe, enabled_features_);
picture_params_.Flags = D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAG_NONE;
if (picture_ctrl_.allow_screen_content_tools) {
picture_params_.Flags |=
D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAG_ENABLE_PALETTE_ENCODING;
}
if (picture_ctrl_.allow_intrabc) {
picture_params_.Flags |=
D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAG_ALLOW_INTRA_BLOCK_COPY;
}
if (picture_ctrl_.enable_auto_segmentation) {
picture_params_.Flags |=
D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAG_ENABLE_FRAME_SEGMENTATION_AUTO;
}
picture_params_.FrameType =
request_keyframe ? D3D12_VIDEO_ENCODER_AV1_FRAME_TYPE_KEY_FRAME
: D3D12_VIDEO_ENCODER_AV1_FRAME_TYPE_INTER_FRAME;
picture_params_.CompoundPredictionType =
D3D12_VIDEO_ENCODER_AV1_COMP_PREDICTION_TYPE_SINGLE_REFERENCE;
picture_params_.InterpolationFilter = enc_caps_.interpolation_filter;
picture_params_.TxMode = enc_caps_.tx_modes[request_keyframe ? 0 : 1];
picture_params_.FrameRestorationConfig = enc_caps_.loop_restoration;
picture_params_.SuperResDenominator = 8 ;
picture_params_.OrderHint =
picture_params_.PictureIndex % (1 << (kDefaultOrderHintBitsMinus1 + 1));
picture_params_.TemporalLayerIndexPlus1 = 0;
picture_params_.SpatialLayerIndexPlus1 = 0;
if (request_keyframe) {
reference_descriptors_.fill({.ReconstructedPictureResourceIndex = 0xFF});
picture_params_.PrimaryRefFrame = kPrimaryRefNone;
}
std::copy(reference_descriptors_.begin(), reference_descriptors_.end(),
picture_params_.ReferenceFramesReconPictureDescriptors);
if (svc_layers_) {
CHECK(metadata_.svc_generic.has_value());
if (request_keyframe) {
svc_layers_->Reset();
}
SVCLayers::PictureParam svc_layer_params{};
svc_layers_->GetPictureParamAndMetadata(svc_layer_params,
&metadata_.svc_generic.value());
picture_params_.RefreshFrameFlags = svc_layer_params.refresh_frame_flags;
if (!request_keyframe) {
CHECK_EQ(svc_layer_params.reference_frame_indices.size(), 1ull);
std::ranges::fill(picture_params_.ReferenceIndices,
svc_layer_params.reference_frame_indices[0]);
picture_params_.PrimaryRefFrame =
svc_layer_params.reference_frame_indices[0];
}
} else {
picture_params_.PrimaryRefFrame = request_keyframe ? kPrimaryRefNone : 0;
std::ranges::fill(picture_params_.ReferenceIndices, 0);
picture_params_.RefreshFrameFlags =
request_keyframe ? 0xFF : 1 << (libgav1::kReferenceFrameLast - 1);
}
std::optional<int> qindex;
if (software_brc_) {
aom::AV1FrameParamsRTC frame_params{
.frame_type = request_keyframe ? aom::kKeyFrame : aom::kInterFrame,
.spatial_layer_id = 0,
.temporal_layer_id =
metadata_.svc_generic ? metadata_.svc_generic->temporal_idx : 0};
software_brc_->ComputeQP(frame_params);
qindex = software_brc_->GetQP();
} else if (options.quantizer.has_value()) {
qindex = AV1QPtoQindex(
std::clamp(static_cast<uint8_t>(options.quantizer.value()),
kAV1MinQuantizer, kAV1MaxQuantizer));
}
const int base_q_idx =
std::clamp(qindex.value_or(AV1QPtoQindex(kAV1MaxQuantizer)), 0, 255);
picture_params_.Quantization.BaseQIndex = base_q_idx;
DVLOG(4) << base::StringPrintf(
"Encoding picture: %d, is_keyframe = %d, QP = %d", picture_id_,
request_keyframe, base_q_idx);
if (qindex.has_value()) {
CHECK_EQ(input_arguments_.SequenceControlDesc.RateControl.Mode,
D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_CQP);
current_rate_control_.SetCQP(
request_keyframe ? D3D12VideoEncoderRateControl::FrameType::kIntra
: D3D12VideoEncoderRateControl::FrameType::kInterPrev,
qindex.value());
input_arguments_.SequenceControlDesc.RateControl =
current_rate_control_.GetD3D12VideoEncoderRateControl();
} else if (rate_control_ != current_rate_control_) {
if (rate_control_.GetMode() != current_rate_control_.GetMode()) {
CHECK(SupportsRateControlReconfiguration());
input_arguments_.SequenceControlDesc.Flags |=
D3D12_VIDEO_ENCODER_SEQUENCE_CONTROL_FLAG_RATE_CONTROL_CHANGE;
}
current_rate_control_ = rate_control_;
input_arguments_.SequenceControlDesc.RateControl =
current_rate_control_.GetD3D12VideoEncoderRateControl();
}
if (!picture_ctrl_.allow_intrabc) {
if (software_brc_) {
const aom::AV1LoopfilterLevel lf = software_brc_->GetLoopfilterLevel();
base::span(picture_params_.LoopFilter.LoopFilterLevel)[0] =
base::span(lf.filter_level)[0];
base::span(picture_params_.LoopFilter.LoopFilterLevel)[1] =
base::span(lf.filter_level)[1];
picture_params_.LoopFilter.LoopFilterLevelU = lf.filter_level_u;
picture_params_.LoopFilter.LoopFilterLevelV = lf.filter_level_v;
} else {
int filter_level = 0;
const int q = kAcQuantizerLookup[base_q_idx];
int inter_frame_multiplier =
input_size_.Width * input_size_.Height > 352 * 288 ? 12034 : 6017;
if (request_keyframe) {
filter_level = (q * 17563 - 421574 + (1 << 17)) >> 18;
} else {
filter_level = (q * inter_frame_multiplier + 650707 + (1 << 17)) >> 18;
}
filter_level = std::clamp(filter_level, 0, 63);
picture_params_.LoopFilter.LoopFilterLevel[0] = filter_level;
picture_params_.LoopFilter.LoopFilterLevel[1] = filter_level;
if (filter_level > 0) {
picture_params_.LoopFilter.LoopFilterLevelU = filter_level;
picture_params_.LoopFilter.LoopFilterLevelV = filter_level;
}
}
auto& cdef = picture_params_.CDEF;
cdef.CdefDampingMinus3 = 2;
cdef.CdefBits = 3;
for (uint32_t i = 0; i < (1 << cdef.CdefBits); i++) {
base::span(cdef.CdefYPriStrength)[i] = kCdefYPriStrength[i];
base::span(cdef.CdefUVPriStrength)[i] = kCdefUVPriStrength[i];
base::span(cdef.CdefYSecStrength)[i] = KCdefYSecStrength[i];
base::span(cdef.CdefUVSecStrength)[i] = kCdefUvSecStrength[i];
}
}
}
EncoderStatus D3D12VideoEncodeAV1Delegate::EncodeImpl(
ID3D12Resource* input_frame,
UINT input_frame_subresource,
const VideoEncoder::EncodeOptions& options,
const gfx::ColorSpace& input_color_space) {
input_arguments_.SequenceControlDesc.Flags =
D3D12_VIDEO_ENCODER_SEQUENCE_CONTROL_FLAG_NONE;
input_arguments_.SequenceControlDesc.RateControl =
rate_control_.GetD3D12VideoEncoderRateControl();
input_arguments_.SequenceControlDesc.PictureTargetResolution = input_size_;
input_arguments_.SequenceControlDesc.SelectedLayoutMode =
D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_FULL_FRAME;
input_arguments_.SequenceControlDesc.FrameSubregionsLayoutData = {
.DataSize = sizeof(sub_layout_), .pTilesPartition_AV1 = &sub_layout_};
FillPictureControlParams(options);
bool used_as_ref = picture_params_.RefreshFrameFlags != 0;
input_arguments_.PictureControlDesc.Flags =
used_as_ref
? D3D12_VIDEO_ENCODER_PICTURE_CONTROL_FLAG_USED_AS_REFERENCE_PICTURE
: D3D12_VIDEO_ENCODER_PICTURE_CONTROL_FLAG_NONE;
auto reconstructed_buffer = dpb_.GetCurrentFrame();
D3D12_VIDEO_ENCODE_REFERENCE_FRAMES reference_frames{};
if (!IsKeyFrame()) {
reference_frames = dpb_.ToD3D12VideoEncodeReferenceFrames();
}
input_arguments_.PictureControlDesc.ReferenceFrames = reference_frames;
input_arguments_.pInputFrame = input_frame;
input_arguments_.InputFrameSubresource = input_frame_subresource;
D3D12_VIDEO_ENCODER_RECONSTRUCTED_PICTURE reconstructed_picture = {
.pReconstructedPicture =
used_as_ref ? reconstructed_buffer.resource_ : nullptr,
.ReconstructedPictureSubresource =
used_as_ref ? reconstructed_buffer.subresource_ : 0,
};
if (EncoderStatus result = video_encoder_wrapper_->Encode(
input_arguments_, reconstructed_picture);
!result.is_ok()) {
return result;
}
if (IsKeyFrame()) {
sequence_header_.color_range =
input_color_space.GetRangeID() == gfx::ColorSpace::RangeID::FULL
? kLibgav1ColorRangeFull
: kLibgav1ColorRangeStudio;
if (IsRec601(input_color_space)) {
sequence_header_.color_primaries = kLibgav1ColorPrimaryBt601;
sequence_header_.transfer_characteristics =
kLibgav1TransferCharacteristicsBt601;
sequence_header_.matrix_coefficients = kLibgav1MatrixCoefficientsBt601;
sequence_header_.color_description_present_flag = true;
} else if (IsRec709(input_color_space)) {
sequence_header_.color_primaries = kLibgav1ColorPrimaryBt709;
sequence_header_.transfer_characteristics =
kLibgav1TransferCharacteristicsBt709;
sequence_header_.matrix_coefficients = kLibgav1MatrixCoefficientsBt709;
sequence_header_.color_description_present_flag = true;
}
}
return EncoderStatus::Codes::kOk;
}
EncoderStatus::Or<size_t>
D3D12VideoEncodeAV1Delegate::GetEncodedBitstreamWrittenBytesCount(
const ScopedD3D12ResourceMap& metadata) {
if (metadata.data().size() <
sizeof(D3D12_VIDEO_ENCODER_OUTPUT_METADATA) +
sizeof(D3D12_VIDEO_ENCODER_FRAME_SUBREGION_METADATA) +
sizeof(
D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA_TILES) +
sizeof(D3D12_VIDEO_ENCODER_AV1_POST_ENCODE_VALUES)) {
return EncoderStatus::Codes::kEncoderHardwareDriverError;
}
size_t compressed_size =
reinterpret_cast<const D3D12_VIDEO_ENCODER_OUTPUT_METADATA*>(
metadata.data().data())
->EncodedBitstreamWrittenBytesCount;
auto subregions =
reinterpret_cast<const D3D12_VIDEO_ENCODER_OUTPUT_METADATA*>(
metadata.data().data())
->WrittenSubregionsCount;
if (subregions != 1) {
return {EncoderStatus::Codes::kEncoderHardwareDriverError,
"D3D12VideoEncodeAV1Delegate: unexpected number of subregions."};
}
size_t suregion_size =
reinterpret_cast<const D3D12_VIDEO_ENCODER_FRAME_SUBREGION_METADATA*>(
metadata.data()
.subspan(sizeof(D3D12_VIDEO_ENCODER_OUTPUT_METADATA))
.data())
->bSize;
if (suregion_size != 0 && suregion_size != compressed_size) {
compressed_size = suregion_size;
}
return compressed_size;
}
void D3D12VideoEncodeAV1Delegate::RefreshDPBAndDescriptors() {
if (svc_layers_) {
svc_layers_->PostEncode(picture_params_.RefreshFrameFlags);
}
if (picture_params_.RefreshFrameFlags == 0) {
return;
}
uint8_t refreshed_dpb_idx =
std::countr_zero((picture_params_.RefreshFrameFlags));
CHECK_LT(refreshed_dpb_idx, max_num_ref_frames_);
dpb_.ReplaceWithCurrentFrame(refreshed_dpb_idx);
D3D12_VIDEO_ENCODER_AV1_REFERENCE_PICTURE_DESCRIPTOR a_descriptor = {
.ReconstructedPictureResourceIndex = static_cast<UINT>(refreshed_dpb_idx),
.TemporalLayerIndexPlus1 = picture_params_.TemporalLayerIndexPlus1,
.SpatialLayerIndexPlus1 = picture_params_.SpatialLayerIndexPlus1,
.FrameType = picture_params_.FrameType,
.OrderHint = picture_params_.OrderHint,
.PictureIndex = picture_params_.PictureIndex};
for (size_t i = 0; i < reference_descriptors_.size(); i++) {
if (picture_params_.RefreshFrameFlags & (1 << i)) {
reference_descriptors_[i] = a_descriptor;
}
}
}
size_t D3D12VideoEncodeAV1Delegate::PackAV1BitstreamHeader(
const AV1BitstreamBuilder::FrameHeader& frame_header,
size_t compressed_size,
base::span<uint8_t> bitstream_buffer) {
AV1BitstreamBuilder pack_header;
uint8_t temporal_idx =
metadata_.svc_generic ? metadata_.svc_generic->temporal_idx : 0;
bool has_extension = GetNumTemporalLayers() > 1;
pack_header.WriteOBUHeader(
libgav1::kObuTemporalDelimiter,
true, has_extension && !IsKeyFrame(), temporal_idx);
pack_header.WriteValueInLeb128(0);
if (IsKeyFrame()) {
pack_header.WriteOBUHeader(libgav1::kObuSequenceHeader,
true);
AV1BitstreamBuilder seq_obu =
AV1BitstreamBuilder::BuildSequenceHeaderOBU(sequence_header_);
CHECK_EQ(seq_obu.OutstandingBits() % 8, 0ull);
pack_header.WriteValueInLeb128(seq_obu.OutstandingBits() / 8);
pack_header.AppendBitstreamBuffer(std::move(seq_obu));
}
pack_header.WriteOBUHeader(libgav1::kObuFrame, true,
has_extension, temporal_idx);
AV1BitstreamBuilder frame_obu =
AV1BitstreamBuilder::BuildFrameHeaderOBU(sequence_header_, frame_header);
CHECK_EQ(frame_obu.OutstandingBits() % 8, 0ull);
pack_header.WriteValueInLeb128(frame_obu.OutstandingBits() / 8 +
compressed_size);
pack_header.AppendBitstreamBuffer(std::move(frame_obu));
std::vector<uint8_t> packed_frame_header = std::move(pack_header).Flush();
size_t packed_header_size = packed_frame_header.size();
bitstream_buffer.first(packed_header_size)
.copy_from(base::span(packed_frame_header));
return packed_header_size;
}
EncoderStatus::Or<size_t> D3D12VideoEncodeAV1Delegate::ReadbackBitstream(
base::span<uint8_t> bitstream_buffer) {
auto metadata_or_error = video_encoder_wrapper_->GetEncoderOutputMetadata();
if (!metadata_or_error.has_value()) {
return std::move(metadata_or_error).error();
}
ScopedD3D12ResourceMap metadata = std::move(metadata_or_error).value();
auto compressed_size_or_size = GetEncodedBitstreamWrittenBytesCount(metadata);
if (!compressed_size_or_size.has_value()) {
return std::move(compressed_size_or_size).error();
}
size_t compressed_size = std::move(compressed_size_or_size).value();
DVLOG(4) << base::StringPrintf("%s: compressed_size = %lu", __func__,
compressed_size);
size_t post_encode_values_offset =
sizeof(D3D12_VIDEO_ENCODER_OUTPUT_METADATA) +
1 * sizeof(D3D12_VIDEO_ENCODER_FRAME_SUBREGION_METADATA) +
sizeof(
D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA_TILES);
if (metadata.data().size() <
post_encode_values_offset +
sizeof(D3D12_VIDEO_ENCODER_AV1_POST_ENCODE_VALUES)) {
return {EncoderStatus::Codes::kEncoderHardwareDriverError,
"D3D12VideoEncodeAV1Delegate: metadata buffer is too small."};
}
UNSAFE_BUFFERS(
const D3D12_VIDEO_ENCODER_AV1_POST_ENCODE_VALUES& post_encode_values =
*reinterpret_cast<const D3D12_VIDEO_ENCODER_AV1_POST_ENCODE_VALUES*>(
metadata.data().subspan(post_encode_values_offset).data()));
DVLOG(4) << PrintPostEncodeValues(post_encode_values);
auto frame_header = FillAV1BuilderFrameHeader(picture_ctrl_, picture_params_,
sequence_header_);
if (!UpdateFrameHeaderPostEncode(enc_caps_.post_value_flags,
post_encode_values, frame_header)) {
return {EncoderStatus::Codes::kEncoderHardwareDriverError,
"D3D12VideoEncodeAV1Delegate: invalid post encode values."};
}
D3D12_RANGE written_range{};
metadata.Commit(&written_range);
size_t packed_header_size =
PackAV1BitstreamHeader(frame_header, compressed_size, bitstream_buffer);
auto size_or_error = D3D12VideoEncodeDelegate::ReadbackBitstream(
bitstream_buffer.subspan(packed_header_size));
if (!size_or_error.has_value()) {
return std::move(size_or_error).error();
}
if (software_brc_) {
software_brc_->PostEncodeUpdate(packed_header_size + compressed_size);
}
RefreshDPBAndDescriptors();
metadata_.key_frame = IsKeyFrame();
metadata_.qp = frame_header.base_qindex;
return packed_header_size + compressed_size;
}
bool D3D12VideoEncodeAV1Delegate::UpdateFrameHeaderPostEncode(
const D3D12_VIDEO_ENCODER_AV1_POST_ENCODE_VALUES_FLAGS& post_encode_flags,
const D3D12_VIDEO_ENCODER_AV1_POST_ENCODE_VALUES& post_encode_values,
AV1BitstreamBuilder::FrameHeader& frame_header) {
if (post_encode_flags ==
D3D12_VIDEO_ENCODER_AV1_POST_ENCODE_VALUES_FLAG_NONE) {
return true;
}
if (post_encode_flags &
D3D12_VIDEO_ENCODER_AV1_POST_ENCODE_VALUES_FLAG_CDEF_DATA) {
const auto& cdef = post_encode_values.CDEF;
if ((1u << cdef.CdefBits) > std::size(cdef.CdefYPriStrength) ||
cdef.CdefDampingMinus3 > 3) {
LOG(ERROR) << "Invalid CDEF params in output metadata.";
return false;
}
frame_header.cdef_damping_minus_3 = cdef.CdefDampingMinus3;
frame_header.cdef_bits = cdef.CdefBits;
for (uint32_t i = 0; i < (1 << cdef.CdefBits); i++) {
frame_header.cdef_y_pri_strength[i] =
base::span(cdef.CdefYPriStrength)[i] & 0xf;
auto cdef_y_sec_strength = base::span(cdef.CdefYSecStrength)[i];
if (cdef_y_sec_strength == 4) {
frame_header.cdef_y_sec_strength[i] = 3;
} else {
frame_header.cdef_y_sec_strength[i] = cdef_y_sec_strength & 0x3;
}
frame_header.cdef_uv_pri_strength[i] =
base::span(cdef.CdefUVPriStrength)[i] & 0xf;
auto cdef_uv_sec_strength = base::span(cdef.CdefUVSecStrength)[i];
if (cdef_uv_sec_strength == 4) {
frame_header.cdef_uv_sec_strength[i] = 3;
} else {
frame_header.cdef_uv_sec_strength[i] = cdef_uv_sec_strength & 0x3;
}
}
}
if (post_encode_flags &
D3D12_VIDEO_ENCODER_AV1_POST_ENCODE_VALUES_FLAG_LOOP_FILTER) {
const auto& loop_filter = post_encode_values.LoopFilter;
frame_header.filter_level[0] =
base::span(loop_filter.LoopFilterLevel)[0] & 0x3f;
frame_header.filter_level[1] =
base::span(loop_filter.LoopFilterLevel)[1] & 0x3f;
frame_header.filter_level_u = loop_filter.LoopFilterLevelU & 0x3f;
frame_header.filter_level_v = loop_filter.LoopFilterLevelV & 0x3f;
frame_header.sharpness_level = loop_filter.LoopFilterSharpnessLevel & 0x7;
frame_header.loop_filter_delta_enabled =
loop_filter.LoopFilterDeltaEnabled & 0x1;
if (frame_header.loop_filter_delta_enabled) {
frame_header.update_ref_delta = loop_filter.UpdateRefDelta & 0x1;
frame_header.update_mode_delta = loop_filter.UpdateModeDelta & 0x1;
} else {
frame_header.update_ref_delta = false;
frame_header.update_mode_delta = false;
}
frame_header.loop_filter_delta_update =
frame_header.update_ref_delta | frame_header.update_mode_delta;
if (frame_header.update_ref_delta) {
for (uint32_t i = 0; i < std::size(loop_filter.RefDeltas); i++) {
frame_header.loop_filter_ref_deltas[i] =
base::saturated_cast<int8_t>(base::span(loop_filter.RefDeltas)[i]);
}
}
if (frame_header.update_mode_delta) {
for (uint32_t i = 0; i < std::size(loop_filter.ModeDeltas); i++) {
frame_header.loop_filter_mode_deltas[i] =
base::saturated_cast<int8_t>(base::span(loop_filter.ModeDeltas)[i]);
}
}
}
if (post_encode_flags &
D3D12_VIDEO_ENCODER_AV1_POST_ENCODE_VALUES_FLAG_LOOP_FILTER_DELTA) {
const auto& loop_filter_delta = post_encode_values.LoopFilterDelta;
frame_header.delta_lf_present = loop_filter_delta.DeltaLFPresent & 0x1;
frame_header.delta_lf_res = loop_filter_delta.DeltaLFMulti & 0x3;
frame_header.delta_lf_multi = loop_filter_delta.DeltaLFRes & 0x1;
}
if (post_encode_flags &
D3D12_VIDEO_ENCODER_AV1_POST_ENCODE_VALUES_FLAG_QUANTIZATION) {
const auto& quantization = post_encode_values.Quantization;
frame_header.base_qindex =
base::saturated_cast<uint32_t>(quantization.BaseQIndex);
frame_header.delta_q_y_dc =
base::saturated_cast<int8_t>(quantization.YDCDeltaQ);
frame_header.delta_q_u_dc =
base::saturated_cast<int8_t>(quantization.UDCDeltaQ);
frame_header.delta_q_u_ac =
base::saturated_cast<int8_t>(quantization.UACDeltaQ);
frame_header.delta_q_v_dc =
base::saturated_cast<int8_t>(quantization.VDCDeltaQ);
frame_header.delta_q_v_ac =
base::saturated_cast<int8_t>(quantization.VACDeltaQ);
frame_header.using_qmatrix = quantization.UsingQMatrix & 0x1;
frame_header.qm_y = base::saturated_cast<uint8_t>(quantization.QMY);
frame_header.qm_u = base::saturated_cast<uint8_t>(quantization.QMU);
frame_header.qm_v = base::saturated_cast<uint8_t>(quantization.QMV);
}
if (post_encode_flags &
D3D12_VIDEO_ENCODER_AV1_POST_ENCODE_VALUES_FLAG_QUANTIZATION_DELTA) {
const auto& quantization_delta = post_encode_values.QuantizationDelta;
frame_header.delta_q_present = quantization_delta.DeltaQPresent & 0x1;
frame_header.delta_q_res = quantization_delta.DeltaQRes & 0x3;
}
const auto& segmentation = post_encode_values.SegmentationConfig;
if (auto num_segments = segmentation.NumSegments) {
if (num_segments > std::size(segmentation.SegmentsData)) {
LOG(ERROR) << "Invalid number of segments in output metadata: "
<< num_segments;
return false;
}
frame_header.segment_number = num_segments;
frame_header.segmentation_enabled = true;
frame_header.segmentation_update_map = segmentation.UpdateMap & 0x1;
frame_header.segmentation_temporal_update =
segmentation.TemporalUpdate & 0x1;
frame_header.segmentation_update_data = segmentation.UpdateData;
for (uint32_t i = 0; i < std::size(segmentation.SegmentsData); i++) {
uint32_t enabled =
base::span(segmentation.SegmentsData)[i].EnabledFeatures & 0xFF;
if (enabled) {
for (uint32_t j = 0; j < 8; ++j) {
frame_header.feature_enabled[i][j] = (enabled & (1u << (j + 1))) != 0;
}
for (uint32_t j = 0;
j <
std::size(base::span(segmentation.SegmentsData)[i].FeatureValue);
j++) {
if (enabled & (1u << j)) {
frame_header.feature_data[j][i] =
base::saturated_cast<int16_t>(base::span(
base::span(segmentation.SegmentsData)[i].FeatureValue)[j]);
}
}
}
}
} else {
frame_header.segmentation_enabled = false;
}
if (post_encode_flags &
D3D12_VIDEO_ENCODER_AV1_POST_ENCODE_VALUES_FLAG_PRIMARY_REF_FRAME) {
frame_header.primary_ref_frame =
base::saturated_cast<uint8_t>(post_encode_values.PrimaryRefFrame);
}
if (post_encode_flags &
D3D12_VIDEO_ENCODER_AV1_POST_ENCODE_VALUES_FLAG_REFERENCE_INDICES) {
for (uint32_t i = 0; i < std::size(post_encode_values.ReferenceIndices);
i++) {
frame_header.ref_frame_idx[i] = base::saturated_cast<uint8_t>(
base::span(post_encode_values.ReferenceIndices)[i]);
}
}
if (post_encode_flags &
D3D12_VIDEO_ENCODER_AV1_POST_ENCODE_VALUES_FLAG_COMPOUND_PREDICTION_MODE) {
frame_header.reference_select =
post_encode_values.CompoundPredictionType & 0x1;
}
return true;
}
}