#include "media/gpu/svc_layers.h"
#include <array>
#include <variant>
#include "base/logging.h"
namespace media {
namespace {
constexpr static size_t kMaxNumUsedRefFramesEachSpatialLayer = 2;
static_assert(kMaxNumUsedRefFramesEachSpatialLayer == 2u,
"SVCLayers uses two reference frames for each spatial layer");
constexpr static size_t kMaxNumUsedReferenceFrames =
kMaxNumUsedRefFramesEachSpatialLayer * SVCLayers::kMaxSpatialLayers;
static_assert(kMaxNumUsedReferenceFrames == 6u,
"SVCLayers uses six reference frames");
enum FrameFlags : uint8_t {
kNone = 0,
kReference = 1,
kUpdate = 2,
kReferenceAndUpdate = kReference | kUpdate,
};
struct FrameConfig {
constexpr FrameConfig(size_t layer_index,
FrameFlags first,
FrameFlags second,
bool temporal_up_switch)
: layer_index_(layer_index),
buffer_flags_{first, second},
temporal_up_switch_(temporal_up_switch) {}
std::vector<uint8_t> GetRefFrameIndices(size_t spatial_idx) const {
std::vector<uint8_t> indices;
for (size_t i = 0; i < kMaxNumUsedRefFramesEachSpatialLayer; ++i) {
if (buffer_flags_[i] & FrameFlags::kReference) {
indices.push_back(i +
kMaxNumUsedRefFramesEachSpatialLayer * spatial_idx);
}
}
return indices;
}
std::vector<uint8_t> GetRefreshIndices(size_t spatial_idx) const {
std::vector<uint8_t> indices;
for (size_t i = 0; i < kMaxNumUsedRefFramesEachSpatialLayer; ++i) {
if (buffer_flags_[i] & FrameFlags::kUpdate) {
indices.push_back(i +
kMaxNumUsedRefFramesEachSpatialLayer * spatial_idx);
}
}
return indices;
}
size_t layer_index() const { return layer_index_; }
bool temporal_up_switch() const { return temporal_up_switch_; }
private:
const size_t layer_index_;
const std::array<FrameFlags, kMaxNumUsedRefFramesEachSpatialLayer>
buffer_flags_;
const bool temporal_up_switch_;
};
FrameConfig GetFrameConfig(size_t num_temporal_layers, size_t frame_num) {
switch (num_temporal_layers) {
case 1:
return FrameConfig(0, kReferenceAndUpdate, kNone, true);
case 2: {
constexpr auto TL2Pattern = std::to_array<FrameConfig>({
FrameConfig(0, kReferenceAndUpdate, kNone, true),
FrameConfig(1, kReference, kNone, true),
});
return TL2Pattern[frame_num % std::size(TL2Pattern)];
}
case 3: {
constexpr auto TL3Pattern = std::to_array<FrameConfig>({
FrameConfig(0, kReferenceAndUpdate, kNone, true),
FrameConfig(2, kReference, kNone, true),
FrameConfig(1, kReference, kUpdate, true),
FrameConfig(2, kNone, kReference, false),
});
return TL3Pattern[frame_num % std::size(TL3Pattern)];
}
default:
NOTREACHED();
}
}
bool ValidateBitrates(const VideoBitrateAllocation& bitrate_allocation,
size_t begin_active_spatial_layer,
size_t end_active_spatial_layer,
size_t num_temporal_layers) {
for (size_t sid = 0; sid < VideoBitrateAllocation::kMaxSpatialLayers; ++sid) {
for (size_t tid = 0; tid < VideoBitrateAllocation::kMaxTemporalLayers;
++tid) {
const bool is_active = bitrate_allocation.GetBitrateBps(sid, tid) > 0;
const bool expected_active = begin_active_spatial_layer <= sid &&
sid < end_active_spatial_layer &&
tid < num_temporal_layers;
if (is_active != expected_active) {
DVLOG(1) << "Invalid bitrate, sid=" << sid << ", tid=" << tid
<< " : bitrate_allocation=" << bitrate_allocation.ToString();
return false;
}
}
}
return true;
}
bool ValidateAndGetActiveLayers(
const VideoBitrateAllocation& bitrate_allocation,
size_t& begin_active_spatial_layer,
size_t& end_active_spatial_layer,
size_t& num_temporal_layers) {
if (bitrate_allocation.GetSumBps() == 0) {
DVLOG(1) << "No active bitrate: bitrate_allocation="
<< bitrate_allocation.ToString();
return false;
}
begin_active_spatial_layer = 0;
end_active_spatial_layer = 0;
num_temporal_layers = 0;
for (size_t sid = 0; sid < VideoBitrateAllocation::kMaxSpatialLayers; ++sid) {
if (bitrate_allocation.GetBitrateBps(sid, 0) != 0) {
begin_active_spatial_layer = sid;
break;
}
}
for (int sid = VideoBitrateAllocation::kMaxSpatialLayers - 1;
sid >= base::checked_cast<int>(begin_active_spatial_layer); --sid) {
if (bitrate_allocation.GetBitrateBps(sid, 0) != 0) {
end_active_spatial_layer = sid + 1;
break;
}
}
if (end_active_spatial_layer == 0) {
DVLOG(1) << "Invalid bitrate: bitrate_allocation="
<< bitrate_allocation.ToString();
return false;
}
for (int tid = VideoBitrateAllocation::kMaxTemporalLayers - 1; tid >= 0;
--tid) {
if (bitrate_allocation.GetBitrateBps(begin_active_spatial_layer, tid) !=
0) {
num_temporal_layers = tid + 1;
break;
}
}
return ValidateBitrates(bitrate_allocation, begin_active_spatial_layer,
end_active_spatial_layer, num_temporal_layers);
}
}
SVCLayers::Config::Config(
const std::vector<gfx::Size>& spatial_layer_resolutions,
size_t begin_active_layer,
size_t end_active_layer,
size_t num_temporal_layers,
SVCInterLayerPredMode inter_layer_pred)
: spatial_layer_resolutions(spatial_layer_resolutions),
begin_active_layer(begin_active_layer),
end_active_layer(end_active_layer),
num_temporal_layers(num_temporal_layers),
active_spatial_layer_resolutions(
spatial_layer_resolutions.begin() + begin_active_layer,
spatial_layer_resolutions.begin() + end_active_layer),
inter_layer_pred(inter_layer_pred) {}
SVCLayers::Config::~Config() = default;
SVCLayers::Config::Config(const Config&) = default;
SVCLayers::PictureParam::PictureParam() = default;
SVCLayers::PictureParam::~PictureParam() = default;
SVCLayers::PictureParam::PictureParam(const PictureParam&) = default;
SVCLayers::SVCLayers(const Config& config) : config_(config) {}
std::pair<bool, std::optional<std::unique_ptr<SVCLayers>>>
SVCLayers::RecreateSVCLayersIfNeeded(
VideoBitrateAllocation& bitrate_allocation) {
size_t begin_active_spatial_layer;
size_t end_active_spatial_layer;
size_t num_temporal_layers;
if (!ValidateAndGetActiveLayers(
bitrate_allocation, begin_active_spatial_layer,
end_active_spatial_layer, num_temporal_layers)) {
return std::make_pair(false, std::nullopt);
}
const auto& old_config = config();
if (end_active_spatial_layer > old_config.spatial_layer_resolutions.size() ||
end_active_spatial_layer - begin_active_spatial_layer >
old_config.spatial_layer_resolutions.size()) {
DVLOG(1) << "Requested spatial layer exceeds the initial spatial layer "
<< "configuration: " << bitrate_allocation.ToString();
return std::make_pair(false, std::nullopt);
}
if (begin_active_spatial_layer > 0) {
for (size_t sid = begin_active_spatial_layer;
sid < end_active_spatial_layer; sid++) {
for (size_t tid = 0; tid < num_temporal_layers; tid++) {
const uint32_t bitrate = bitrate_allocation.GetBitrateBps(sid, tid);
CHECK_NE(bitrate, 0u);
bitrate_allocation.SetBitrate(sid - begin_active_spatial_layer, tid,
bitrate);
bitrate_allocation.SetBitrate(sid, tid, 0u);
}
}
}
if (old_config.begin_active_layer != begin_active_spatial_layer ||
old_config.end_active_layer != end_active_spatial_layer ||
old_config.num_temporal_layers != num_temporal_layers) {
std::optional<std::unique_ptr<SVCLayers>> svc_layers =
std::make_unique<SVCLayers>(SVCLayers::Config(
old_config.spatial_layer_resolutions, begin_active_spatial_layer,
end_active_spatial_layer, num_temporal_layers,
old_config.inter_layer_pred));
return std::make_pair(true, std::move(svc_layers));
}
return std::make_pair(true, std::nullopt);
}
void SVCLayers::Reset() {
CHECK_EQ(spatial_idx_, 0u);
frame_num_ = 0;
frame_num_ref_frames_.fill(0);
}
void SVCLayers::PostEncode(uint8_t refresh_frame_flags) {
for (size_t i = 0; i < kVp9NumRefFrames; ++i) {
if (refresh_frame_flags & (1 << i)) {
frame_num_ref_frames_[i] = frame_num_;
}
}
spatial_idx_ += 1;
if (spatial_idx_ == config_.active_spatial_layer_resolutions.size()) {
spatial_idx_ = 0;
frame_num_ += 1;
}
}
bool SVCLayers::IsKeyFrame() const {
if (frame_num_ != 0) {
return false;
}
if (config_.inter_layer_pred == SVCInterLayerPredMode::kOnKeyPic) {
return spatial_idx_ == 0;
}
CHECK(config_.active_spatial_layer_resolutions.size() == 1 ||
config_.inter_layer_pred == SVCInterLayerPredMode::kOff);
return true;
}
void SVCLayers::GetPictureParamAndMetadata(
PictureParam& picture_param,
std::variant<Vp9Metadata*, SVCGenericMetadata*> metadata) const {
picture_param.frame_size =
config_.active_spatial_layer_resolutions[spatial_idx_];
if (auto* svc_metadata = std::get_if<SVCGenericMetadata*>(&metadata)) {
(*svc_metadata)->follow_svc_spec = true;
}
if (frame_num_ == 0) {
FillMetadataForFirstFrame(metadata, picture_param.key_frame,
picture_param.refresh_frame_flags,
picture_param.reference_frame_indices);
return;
}
picture_param.key_frame = false;
FillMetadataForNonFirstFrame(metadata, picture_param.refresh_frame_flags,
picture_param.reference_frame_indices);
}
void SVCLayers::FillMetadataForFirstFrame(
std::variant<Vp9Metadata*, SVCGenericMetadata*> metadata,
bool& key_frame,
uint8_t& refresh_frame_flags,
std::vector<uint8_t>& reference_frame_indices) const {
CHECK_EQ(frame_num_, 0u);
if (spatial_idx_ == 0) {
key_frame = true;
refresh_frame_flags = 0xff;
reference_frame_indices = {};
} else {
key_frame = config_.inter_layer_pred == SVCInterLayerPredMode::kOff;
refresh_frame_flags =
1 << (spatial_idx_ * kMaxNumUsedRefFramesEachSpatialLayer);
reference_frame_indices = {};
if (config_.inter_layer_pred == SVCInterLayerPredMode::kOnKeyPic) {
reference_frame_indices = {base::checked_cast<uint8_t>(
(spatial_idx_ - 1) * kMaxNumUsedRefFramesEachSpatialLayer)};
}
}
if (auto* svc_metadata = std::get_if<SVCGenericMetadata*>(&metadata)) {
(*svc_metadata)->temporal_idx = 0;
(*svc_metadata)->spatial_idx = spatial_idx_;
} else {
CHECK(std::holds_alternative<Vp9Metadata*>(metadata));
auto& vp9_metadata = std::get<Vp9Metadata*>(metadata);
vp9_metadata->inter_pic_predicted = false;
vp9_metadata->temporal_up_switch = true;
vp9_metadata->end_of_picture =
spatial_idx_ == config_.active_spatial_layer_resolutions.size() - 1;
if (config_.inter_layer_pred == SVCInterLayerPredMode::kOnKeyPic) {
vp9_metadata->referenced_by_upper_spatial_layers =
!vp9_metadata->end_of_picture;
vp9_metadata->reference_lower_spatial_layers = spatial_idx_ != 0;
} else {
vp9_metadata->referenced_by_upper_spatial_layers = false;
vp9_metadata->reference_lower_spatial_layers = false;
}
vp9_metadata->temporal_idx = 0;
vp9_metadata->spatial_idx = spatial_idx_;
if (key_frame) {
vp9_metadata->spatial_layer_resolutions =
config_.active_spatial_layer_resolutions;
vp9_metadata->begin_active_spatial_layer_index =
base::checked_cast<uint8_t>(config_.begin_active_layer);
vp9_metadata->end_active_spatial_layer_index =
base::checked_cast<uint8_t>(config_.end_active_layer);
}
}
}
void SVCLayers::FillMetadataForNonFirstFrame(
std::variant<Vp9Metadata*, SVCGenericMetadata*> metadata,
uint8_t& refresh_frame_flags,
std::vector<uint8_t>& reference_frame_indices) const {
CHECK_NE(frame_num_, 0u);
const FrameConfig frame_config =
GetFrameConfig(config_.num_temporal_layers, frame_num_);
refresh_frame_flags = 0;
for (const uint8_t i : frame_config.GetRefreshIndices(spatial_idx_)) {
refresh_frame_flags |= 1 << i;
}
reference_frame_indices = frame_config.GetRefFrameIndices(spatial_idx_);
if (auto* svc_metadata = std::get_if<SVCGenericMetadata*>(&metadata)) {
(*svc_metadata)->temporal_idx = frame_config.layer_index();
(*svc_metadata)->spatial_idx = spatial_idx_;
} else {
CHECK(std::holds_alternative<Vp9Metadata*>(metadata));
auto& vp9_metadata = std::get<Vp9Metadata*>(metadata);
vp9_metadata->inter_pic_predicted = !reference_frame_indices.empty();
vp9_metadata->temporal_up_switch = frame_config.temporal_up_switch();
vp9_metadata->referenced_by_upper_spatial_layers = false;
vp9_metadata->reference_lower_spatial_layers = false;
vp9_metadata->end_of_picture =
spatial_idx_ == config_.active_spatial_layer_resolutions.size() - 1;
vp9_metadata->temporal_idx = frame_config.layer_index();
vp9_metadata->spatial_idx = spatial_idx_;
for (const uint8_t i : reference_frame_indices) {
const uint8_t p_diff =
base::checked_cast<uint8_t>(frame_num_ - frame_num_ref_frames_[i]);
vp9_metadata->p_diffs.push_back(p_diff);
}
}
}
}