blob: 3c5b89e5d6b735ab01115a19fe2ae9804f32e664 [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <algorithm>
#include "base/logging.h"
#include "base/metrics/histogram_functions.h"
#include "base/notreached.h"
#include "media/base/limits.h"
#include "media/gpu/h265_decoder.h"
namespace media {
namespace {
struct POCAscCompare {
bool operator()(const scoped_refptr<H265Picture>& a,
const scoped_refptr<H265Picture>& b) const {
return a->pic_order_cnt_val_ < b->pic_order_cnt_val_;
}
};
bool ParseBitDepth(const H265SPS& sps, uint8_t& bit_depth) {
// Spec 7.4.3.2.1
if (sps.bit_depth_y != sps.bit_depth_c) {
DVLOG(1) << "Different bit depths among planes is not supported";
return false;
}
bit_depth = base::checked_cast<uint8_t>(sps.bit_depth_y);
return true;
}
bool IsValidBitDepth(uint8_t bit_depth, VideoCodecProfile profile) {
switch (profile) {
// Spec A.3.2
case HEVCPROFILE_MAIN:
return bit_depth == 8u;
// Spec A.3.3
case HEVCPROFILE_MAIN10:
return bit_depth == 8u || bit_depth == 10u;
// Spec A.3.4
case HEVCPROFILE_MAIN_STILL_PICTURE:
return bit_depth == 8u;
// Spec A.3.5
case HEVCPROFILE_REXT:
return bit_depth == 8u || bit_depth == 10u || bit_depth == 12u ||
bit_depth == 14u || bit_depth == 16u;
// Spec A.3.6
case HEVCPROFILE_HIGH_THROUGHPUT:
return bit_depth == 8u || bit_depth == 10u || bit_depth == 14u ||
bit_depth == 16u;
// Spec G.11.1.1
case HEVCPROFILE_MULTIVIEW_MAIN:
return bit_depth == 8u;
// Spec H.11.1.1
case HEVCPROFILE_SCALABLE_MAIN:
return bit_depth == 8u || bit_depth == 10u;
// Spec I.11.1.1
case HEVCPROFILE_3D_MAIN:
return bit_depth == 8u;
// Spec A.3.7
case HEVCPROFILE_SCREEN_EXTENDED:
return bit_depth == 8u || bit_depth == 10u;
// Spec H.11.1.2
case HEVCPROFILE_SCALABLE_REXT:
return bit_depth == 8u || bit_depth == 12u || bit_depth == 16u;
// Spec A.3.8
case HEVCPROFILE_HIGH_THROUGHPUT_SCREEN_EXTENDED:
return bit_depth == 8u || bit_depth == 10u || bit_depth == 14u;
default:
DVLOG(1) << "Invalid profile specified for H265";
return false;
}
}
} // namespace
H265Decoder::H265Accelerator::H265Accelerator() = default;
H265Decoder::H265Accelerator::~H265Accelerator() = default;
H265Decoder::H265Accelerator::Status H265Decoder::H265Accelerator::SetStream(
base::span<const uint8_t> stream,
const DecryptConfig* decrypt_config) {
return H265Decoder::H265Accelerator::Status::kNotSupported;
}
H265Decoder::H265Decoder(std::unique_ptr<H265Accelerator> accelerator,
VideoCodecProfile profile,
const VideoColorSpace& container_color_space)
: state_(kAfterReset),
container_color_space_(container_color_space),
profile_(profile),
accelerator_(std::move(accelerator)) {
DCHECK(accelerator_);
Reset();
}
H265Decoder::~H265Decoder() = default;
#define SET_ERROR_AND_RETURN() \
do { \
DVLOG(1) << "Error during decode"; \
state_ = kError; \
return H265Decoder::kDecodeError; \
} while (0)
#define CHECK_ACCELERATOR_RESULT(func) \
do { \
H265Accelerator::Status result = (func); \
switch (result) { \
case H265Accelerator::Status::kOk: \
break; \
case H265Accelerator::Status::kTryAgain: \
DVLOG(1) << #func " needs to try again"; \
return H265Decoder::kTryAgain; \
case H265Accelerator::Status::kFail: /* fallthrough */ \
case H265Accelerator::Status::kNotSupported: \
SET_ERROR_AND_RETURN(); \
} \
} while (0)
void H265Decoder::SetStream(int32_t id, const DecoderBuffer& decoder_buffer) {
const uint8_t* ptr = decoder_buffer.data();
const size_t size = decoder_buffer.data_size();
const DecryptConfig* decrypt_config = decoder_buffer.decrypt_config();
DCHECK(ptr);
DCHECK(size);
DVLOG(4) << "New input stream id: " << id << " at: " << (void*)ptr
<< " size: " << size;
stream_id_ = id;
current_stream_ = ptr;
current_stream_size_ = size;
current_stream_has_been_changed_ = true;
if (decrypt_config) {
parser_.SetEncryptedStream(ptr, size, decrypt_config->subsamples());
current_decrypt_config_ = decrypt_config->Clone();
} else {
parser_.SetStream(ptr, size);
current_decrypt_config_ = nullptr;
}
}
void H265Decoder::Reset() {
curr_pic_ = nullptr;
curr_nalu_ = nullptr;
curr_slice_hdr_ = nullptr;
last_slice_hdr_ = nullptr;
curr_sps_id_ = -1;
curr_pps_id_ = -1;
prev_tid0_pic_ = nullptr;
ref_pic_list_.clear();
ref_pic_list0_.clear();
ref_pic_list1_.clear();
ref_pic_set_lt_curr_.clear();
ref_pic_set_st_curr_after_.clear();
ref_pic_set_st_curr_before_.clear();
dpb_.Clear();
parser_.Reset();
accelerator_->Reset();
state_ = kAfterReset;
}
H265Decoder::DecodeResult H265Decoder::Decode() {
if (state_ == kError) {
DVLOG(1) << "Decoder in error state";
return kDecodeError;
}
if (current_stream_has_been_changed_) {
// Calling H265Accelerator::SetStream() here instead of when the stream is
// originally set in case the accelerator needs to return kTryAgain.
H265Accelerator::Status result = accelerator_->SetStream(
base::span<const uint8_t>(current_stream_, current_stream_size_),
current_decrypt_config_.get());
switch (result) {
case H265Accelerator::Status::kOk: // fallthrough
case H265Accelerator::Status::kNotSupported:
// kNotSupported means the accelerator can't handle this stream,
// so everything will be done through the parser.
break;
case H265Accelerator::Status::kTryAgain:
DVLOG(1) << "SetStream() needs to try again";
return H265Decoder::kTryAgain;
case H265Accelerator::Status::kFail:
SET_ERROR_AND_RETURN();
}
// Reset the flag so that this is only called again next time SetStream()
// is called.
current_stream_has_been_changed_ = false;
}
while (true) {
H265Parser::Result par_res;
if (!curr_nalu_) {
curr_nalu_ = std::make_unique<H265NALU>();
par_res = parser_.AdvanceToNextNALU(curr_nalu_.get());
if (par_res == H265Parser::kEOStream) {
curr_nalu_.reset();
// We receive one frame per buffer, so we can output the frame now.
CHECK_ACCELERATOR_RESULT(FinishPrevFrameIfPresent());
return kRanOutOfStreamData;
}
if (par_res != H265Parser::kOk) {
curr_nalu_.reset();
SET_ERROR_AND_RETURN();
}
DVLOG(4) << "New NALU: " << static_cast<int>(curr_nalu_->nal_unit_type);
}
// 8.1.2 We only want nuh_layer_id of zero.
if (curr_nalu_->nuh_layer_id) {
DVLOG(4) << "Skipping NALU with nuh_layer_id="
<< curr_nalu_->nuh_layer_id;
curr_nalu_.reset();
continue;
}
switch (curr_nalu_->nal_unit_type) {
case H265NALU::BLA_W_LP: // fallthrough
case H265NALU::BLA_W_RADL:
case H265NALU::BLA_N_LP:
case H265NALU::IDR_W_RADL:
case H265NALU::IDR_N_LP:
case H265NALU::TRAIL_N:
case H265NALU::TRAIL_R:
case H265NALU::TSA_N:
case H265NALU::TSA_R:
case H265NALU::STSA_N:
case H265NALU::STSA_R:
case H265NALU::RADL_N:
case H265NALU::RADL_R:
case H265NALU::RASL_N:
case H265NALU::RASL_R:
case H265NALU::CRA_NUT:
if (!curr_slice_hdr_) {
curr_slice_hdr_.reset(new H265SliceHeader());
par_res = parser_.ParseSliceHeader(*curr_nalu_, curr_slice_hdr_.get(),
last_slice_hdr_.get());
if (par_res == H265Parser::kMissingParameterSet) {
// We may still be able to recover if we skip until we find the
// SPS/PPS.
curr_slice_hdr_.reset();
last_slice_hdr_.reset();
break;
}
if (par_res != H265Parser::kOk)
SET_ERROR_AND_RETURN();
if (!curr_slice_hdr_->irap_pic && state_ == kAfterReset) {
// We can't resume from a non-IRAP picture.
curr_slice_hdr_.reset();
last_slice_hdr_.reset();
break;
}
state_ = kTryPreprocessCurrentSlice;
if (curr_slice_hdr_->irap_pic) {
bool need_new_buffers = false;
if (!ProcessPPS(curr_slice_hdr_->slice_pic_parameter_set_id,
&need_new_buffers)) {
SET_ERROR_AND_RETURN();
}
if (need_new_buffers) {
curr_pic_ = nullptr;
return kConfigChange;
}
}
}
if (state_ == kTryPreprocessCurrentSlice) {
CHECK_ACCELERATOR_RESULT(PreprocessCurrentSlice());
state_ = kEnsurePicture;
}
if (state_ == kEnsurePicture) {
if (curr_pic_) {
// |curr_pic_| already exists, so skip to ProcessCurrentSlice().
state_ = kTryCurrentSlice;
} else {
// New picture, try to start a new one or tell client we need more
// surfaces.
curr_pic_ = accelerator_->CreateH265Picture();
if (!curr_pic_)
return kRanOutOfSurfaces;
if (current_decrypt_config_)
curr_pic_->set_decrypt_config(current_decrypt_config_->Clone());
if (hdr_metadata_.has_value())
curr_pic_->set_hdr_metadata(hdr_metadata_);
curr_pic_->first_picture_ = first_picture_;
first_picture_ = false;
state_ = kTryNewFrame;
}
}
if (state_ == kTryNewFrame) {
CHECK_ACCELERATOR_RESULT(StartNewFrame(curr_slice_hdr_.get()));
state_ = kTryCurrentSlice;
}
DCHECK_EQ(state_, kTryCurrentSlice);
CHECK_ACCELERATOR_RESULT(ProcessCurrentSlice());
state_ = kDecoding;
last_slice_hdr_.swap(curr_slice_hdr_);
curr_slice_hdr_.reset();
break;
case H265NALU::SPS_NUT:
CHECK_ACCELERATOR_RESULT(FinishPrevFrameIfPresent());
int sps_id;
par_res = parser_.ParseSPS(&sps_id);
if (par_res != H265Parser::kOk)
SET_ERROR_AND_RETURN();
break;
case H265NALU::PPS_NUT:
CHECK_ACCELERATOR_RESULT(FinishPrevFrameIfPresent());
int pps_id;
par_res = parser_.ParsePPS(*curr_nalu_, &pps_id);
if (par_res != H265Parser::kOk)
SET_ERROR_AND_RETURN();
// For ARC CTS tests they expect us to request the buffers after only
// processing the SPS/PPS, we can't wait until we get the first IDR. To
// resolve the problem that was created by originally doing that, only
// do it if we don't have an active PPS set yet so it won't disturb an
// active stream.
if (curr_pps_id_ == -1) {
bool need_new_buffers = false;
if (!ProcessPPS(pps_id, &need_new_buffers)) {
SET_ERROR_AND_RETURN();
}
if (need_new_buffers) {
curr_nalu_.reset();
return kConfigChange;
}
}
break;
case H265NALU::EOS_NUT:
first_picture_ = true;
[[fallthrough]];
case H265NALU::EOB_NUT: // fallthrough
case H265NALU::AUD_NUT:
case H265NALU::RSV_NVCL41:
case H265NALU::RSV_NVCL42:
case H265NALU::RSV_NVCL43:
case H265NALU::RSV_NVCL44:
case H265NALU::UNSPEC48:
case H265NALU::UNSPEC49:
case H265NALU::UNSPEC50:
case H265NALU::UNSPEC51:
case H265NALU::UNSPEC52:
case H265NALU::UNSPEC53:
case H265NALU::UNSPEC54:
case H265NALU::UNSPEC55:
CHECK_ACCELERATOR_RESULT(FinishPrevFrameIfPresent());
break;
case H265NALU::PREFIX_SEI_NUT: {
H265SEI sei;
if (parser_.ParseSEI(&sei) != H265Parser::kOk)
break;
for (auto& sei_msg : sei.msgs) {
switch (sei_msg.type) {
case H265SEIMessage::kSEIContentLightLevelInfo:
// HEVC HDR metadata may appears in the below places:
// 1. Container.
// 2. Bitstream.
// 3. Both container and bitstream.
// Thus we should also extract HDR metadata here in case we
// miss the information.
if (!hdr_metadata_)
hdr_metadata_ = gfx::HDRMetadata();
sei_msg.content_light_level_info.PopulateHDRMetadata(
hdr_metadata_.value());
break;
case H265SEIMessage::kSEIMasteringDisplayInfo:
if (!hdr_metadata_)
hdr_metadata_ = gfx::HDRMetadata();
sei_msg.mastering_display_info.PopulateColorVolumeMetadata(
hdr_metadata_->color_volume_metadata);
break;
default:
break;
}
}
break;
}
default:
DVLOG(4) << "Skipping NALU type: " << curr_nalu_->nal_unit_type;
break;
}
DVLOG(4) << "NALU done";
curr_nalu_.reset();
}
}
gfx::Size H265Decoder::GetPicSize() const {
return pic_size_;
}
gfx::Rect H265Decoder::GetVisibleRect() const {
return visible_rect_;
}
VideoCodecProfile H265Decoder::GetProfile() const {
return profile_;
}
uint8_t H265Decoder::GetBitDepth() const {
return bit_depth_;
}
VideoChromaSampling H265Decoder::GetChromaSampling() const {
return chroma_sampling_;
}
absl::optional<gfx::HDRMetadata> H265Decoder::GetHDRMetadata() const {
return hdr_metadata_;
}
size_t H265Decoder::GetRequiredNumOfPictures() const {
constexpr size_t kPicsInPipeline = limits::kMaxVideoFrames + 1;
return GetNumReferenceFrames() + kPicsInPipeline;
}
size_t H265Decoder::GetNumReferenceFrames() const {
// Use the maximum number of pictures in the Decoded Picture Buffer.
return dpb_.max_num_pics();
}
bool H265Decoder::ProcessPPS(int pps_id, bool* need_new_buffers) {
DVLOG(4) << "Processing PPS id:" << pps_id;
const H265PPS* pps = parser_.GetPPS(pps_id);
// Slice header parsing already verified this should exist.
DCHECK(pps);
const H265SPS* sps = parser_.GetSPS(pps->pps_seq_parameter_set_id);
// PPS parsing already verified this should exist.
DCHECK(sps);
if (need_new_buffers)
*need_new_buffers = false;
gfx::Size new_pic_size = sps->GetCodedSize();
gfx::Rect new_visible_rect = sps->GetVisibleRect();
if (visible_rect_ != new_visible_rect) {
DVLOG(2) << "New visible rect: " << new_visible_rect.ToString();
visible_rect_ = new_visible_rect;
}
VideoChromaSampling new_chroma_sampling = sps->GetChromaSampling();
if (new_chroma_sampling != chroma_sampling_) {
base::UmaHistogramEnumeration("Media.PlatformVideoDecoding.ChromaSampling",
new_chroma_sampling);
}
if (!accelerator_->IsChromaSamplingSupported(new_chroma_sampling)) {
DVLOG(1) << "Only YUV 4:2:0 is supported";
return false;
}
// Equation 7-8
max_pic_order_cnt_lsb_ =
std::pow(2, sps->log2_max_pic_order_cnt_lsb_minus4 + 4);
VideoCodecProfile new_profile = H265Parser::ProfileIDCToVideoCodecProfile(
sps->profile_tier_level.general_profile_idc);
uint8_t new_bit_depth = 0;
if (!ParseBitDepth(*sps, new_bit_depth))
return false;
if (!IsValidBitDepth(new_bit_depth, new_profile)) {
DVLOG(1) << "Invalid bit depth=" << base::strict_cast<int>(new_bit_depth)
<< ", profile=" << GetProfileName(new_profile);
return false;
}
if (pic_size_ != new_pic_size || dpb_.max_num_pics() != sps->max_dpb_size ||
profile_ != new_profile || bit_depth_ != new_bit_depth ||
chroma_sampling_ != new_chroma_sampling) {
if (!Flush())
return false;
DVLOG(1) << "Codec profile: " << GetProfileName(new_profile)
<< ", level(x30): " << sps->profile_tier_level.general_level_idc
<< ", DPB size: " << sps->max_dpb_size
<< ", Picture size: " << new_pic_size.ToString()
<< ", bit_depth: " << base::strict_cast<int>(new_bit_depth)
<< ", chroma_sampling_format: "
<< VideoChromaSamplingToString(new_chroma_sampling);
profile_ = new_profile;
bit_depth_ = new_bit_depth;
pic_size_ = new_pic_size;
chroma_sampling_ = new_chroma_sampling;
dpb_.set_max_num_pics(sps->max_dpb_size);
if (need_new_buffers)
*need_new_buffers = true;
}
return true;
}
H265Decoder::H265Accelerator::Status H265Decoder::PreprocessCurrentSlice() {
const H265SliceHeader* slice_hdr = curr_slice_hdr_.get();
DCHECK(slice_hdr);
if (slice_hdr->first_slice_segment_in_pic_flag) {
// New picture, so first finish the previous one before processing it.
H265Accelerator::Status result = FinishPrevFrameIfPresent();
if (result != H265Accelerator::Status::kOk)
return result;
DCHECK(!curr_pic_);
}
return H265Accelerator::Status::kOk;
}
H265Decoder::H265Accelerator::Status H265Decoder::ProcessCurrentSlice() {
DCHECK(curr_pic_);
const H265SliceHeader* slice_hdr = curr_slice_hdr_.get();
DCHECK(slice_hdr);
const H265SPS* sps = parser_.GetSPS(curr_sps_id_);
DCHECK(sps);
const H265PPS* pps = parser_.GetPPS(curr_pps_id_);
DCHECK(pps);
return accelerator_->SubmitSlice(
sps, pps, slice_hdr, ref_pic_list0_, ref_pic_list1_, ref_pic_set_lt_curr_,
ref_pic_set_st_curr_after_, ref_pic_set_st_curr_before_, curr_pic_.get(),
slice_hdr->nalu_data, slice_hdr->nalu_size,
parser_.GetCurrentSubsamples());
}
void H265Decoder::CalcPicOutputFlags(const H265SliceHeader* slice_hdr) {
if (slice_hdr->irap_pic) {
// 8.1.3
curr_pic_->no_rasl_output_flag_ =
(curr_nalu_->nal_unit_type >= H265NALU::BLA_W_LP &&
curr_nalu_->nal_unit_type <= H265NALU::IDR_N_LP) ||
curr_pic_->first_picture_;
} else {
curr_pic_->no_rasl_output_flag_ = false;
}
// C.5.2.2
if (slice_hdr->irap_pic && curr_pic_->no_rasl_output_flag_ &&
!curr_pic_->first_picture_) {
curr_pic_->no_output_of_prior_pics_flag_ =
(slice_hdr->nal_unit_type == H265NALU::CRA_NUT) ||
slice_hdr->no_output_of_prior_pics_flag;
} else {
curr_pic_->no_output_of_prior_pics_flag_ = false;
}
if ((slice_hdr->nal_unit_type == H265NALU::RASL_N ||
slice_hdr->nal_unit_type == H265NALU::RASL_R) &&
curr_pic_->no_rasl_output_flag_) {
curr_pic_->pic_output_flag_ = false;
} else {
curr_pic_->pic_output_flag_ = slice_hdr->pic_output_flag;
}
}
void H265Decoder::CalcPictureOrderCount(const H265PPS* pps,
const H265SliceHeader* slice_hdr) {
// 8.3.1 Decoding process for picture order count.
curr_pic_->valid_for_prev_tid0_pic_ =
!pps->temporal_id && (slice_hdr->nal_unit_type < H265NALU::RADL_N ||
slice_hdr->nal_unit_type > H265NALU::RSV_VCL_N14);
curr_pic_->slice_pic_order_cnt_lsb_ = slice_hdr->slice_pic_order_cnt_lsb;
// Calculate POC for current picture.
if ((!slice_hdr->irap_pic || !curr_pic_->no_rasl_output_flag_) &&
prev_tid0_pic_) {
const int prev_pic_order_cnt_lsb = prev_tid0_pic_->slice_pic_order_cnt_lsb_;
const int prev_pic_order_cnt_msb = prev_tid0_pic_->pic_order_cnt_msb_;
if ((slice_hdr->slice_pic_order_cnt_lsb < prev_pic_order_cnt_lsb) &&
((prev_pic_order_cnt_lsb - slice_hdr->slice_pic_order_cnt_lsb) >=
(max_pic_order_cnt_lsb_ / 2))) {
curr_pic_->pic_order_cnt_msb_ =
prev_pic_order_cnt_msb + max_pic_order_cnt_lsb_;
} else if ((slice_hdr->slice_pic_order_cnt_lsb > prev_pic_order_cnt_lsb) &&
((slice_hdr->slice_pic_order_cnt_lsb - prev_pic_order_cnt_lsb) >
(max_pic_order_cnt_lsb_ / 2))) {
curr_pic_->pic_order_cnt_msb_ =
prev_pic_order_cnt_msb - max_pic_order_cnt_lsb_;
} else {
curr_pic_->pic_order_cnt_msb_ = prev_pic_order_cnt_msb;
}
} else {
curr_pic_->pic_order_cnt_msb_ = 0;
}
curr_pic_->pic_order_cnt_val_ =
curr_pic_->pic_order_cnt_msb_ + slice_hdr->slice_pic_order_cnt_lsb;
}
bool H265Decoder::CalcRefPicPocs(const H265SPS* sps,
const H265PPS* pps,
const H265SliceHeader* slice_hdr) {
if (slice_hdr->nal_unit_type == H265NALU::IDR_W_RADL ||
slice_hdr->nal_unit_type == H265NALU::IDR_N_LP) {
num_poc_st_curr_before_ = num_poc_st_curr_after_ = num_poc_st_foll_ =
num_poc_lt_curr_ = num_poc_lt_foll_ = 0;
return true;
}
// 8.3.2 - NOTE 2
const H265StRefPicSet& curr_st_ref_pic_set = slice_hdr->GetStRefPicSet(sps);
// Equation 8-5.
int i, j, k;
for (i = 0, j = 0, k = 0; i < curr_st_ref_pic_set.num_negative_pics; ++i) {
base::CheckedNumeric<int> poc = curr_pic_->pic_order_cnt_val_;
poc += curr_st_ref_pic_set.delta_poc_s0[i];
if (!poc.IsValid()) {
DVLOG(1) << "Invalid POC";
return false;
}
if (curr_st_ref_pic_set.used_by_curr_pic_s0[i])
poc_st_curr_before_[j++] = poc.ValueOrDefault(0);
else
poc_st_foll_[k++] = poc.ValueOrDefault(0);
}
num_poc_st_curr_before_ = j;
for (i = 0, j = 0; i < curr_st_ref_pic_set.num_positive_pics; ++i) {
base::CheckedNumeric<int> poc = curr_pic_->pic_order_cnt_val_;
poc += curr_st_ref_pic_set.delta_poc_s1[i];
if (!poc.IsValid()) {
DVLOG(1) << "Invalid POC";
return false;
}
if (curr_st_ref_pic_set.used_by_curr_pic_s1[i])
poc_st_curr_after_[j++] = poc.ValueOrDefault(0);
else
poc_st_foll_[k++] = poc.ValueOrDefault(0);
}
num_poc_st_curr_after_ = j;
num_poc_st_foll_ = k;
for (i = 0, j = 0, k = 0;
i < slice_hdr->num_long_term_sps + slice_hdr->num_long_term_pics; ++i) {
base::CheckedNumeric<int> poc_lt = slice_hdr->poc_lsb_lt[i];
if (slice_hdr->delta_poc_msb_present_flag[i]) {
poc_lt += curr_pic_->pic_order_cnt_val_;
base::CheckedNumeric<int> poc_delta =
slice_hdr->delta_poc_msb_cycle_lt[i];
poc_delta *= max_pic_order_cnt_lsb_;
if (!poc_delta.IsValid()) {
DVLOG(1) << "Invalid POC";
return false;
}
poc_lt -= poc_delta.ValueOrDefault(0);
poc_lt -= curr_pic_->pic_order_cnt_val_ & (max_pic_order_cnt_lsb_ - 1);
}
if (!poc_lt.IsValid()) {
DVLOG(1) << "Invalid POC";
return false;
}
if (slice_hdr->used_by_curr_pic_lt[i]) {
poc_lt_curr_[j] = poc_lt.ValueOrDefault(0);
curr_delta_poc_msb_present_flag_[j++] =
slice_hdr->delta_poc_msb_present_flag[i];
} else {
poc_lt_foll_[k] = poc_lt.ValueOrDefault(0);
foll_delta_poc_msb_present_flag_[k++] =
slice_hdr->delta_poc_msb_present_flag[i];
}
}
num_poc_lt_curr_ = j;
num_poc_lt_foll_ = k;
// Check conformance for |num_pic_total_curr|.
if (slice_hdr->nal_unit_type == H265NALU::CRA_NUT ||
(slice_hdr->nal_unit_type >= H265NALU::BLA_W_LP &&
slice_hdr->nal_unit_type <= H265NALU::BLA_N_LP)) {
if (slice_hdr->num_pic_total_curr) {
DVLOG(1) << "Invalid value for num_pic_total_curr";
return false;
}
} else if ((slice_hdr->IsBSlice() || slice_hdr->IsPSlice()) &&
!slice_hdr->num_pic_total_curr) {
DVLOG(1) << "Invalid value for num_pic_total_curr";
return false;
}
return true;
}
bool H265Decoder::BuildRefPicLists(const H265SPS* sps,
const H265PPS* pps,
const H265SliceHeader* slice_hdr) {
ref_pic_set_lt_curr_.clear();
ref_pic_set_lt_curr_.resize(kMaxDpbSize);
ref_pic_set_st_curr_after_.clear();
ref_pic_set_st_curr_after_.resize(kMaxDpbSize);
ref_pic_set_st_curr_before_.clear();
ref_pic_set_st_curr_before_.resize(kMaxDpbSize);
scoped_refptr<H265Picture> ref_pic_set_lt_foll[kMaxDpbSize];
scoped_refptr<H265Picture> ref_pic_set_st_foll[kMaxDpbSize];
// Mark everything in the DPB as unused for reference now. When we determine
// the pics in the ref list, then we will mark them appropriately.
dpb_.MarkAllUnusedForReference();
// Equation 8-6.
// We may be missing reference pictures, if so then we just don't specify
// them and let the accelerator deal with the missing reference pictures
// which is covered in the spec.
int total_ref_pics = 0;
for (int i = 0; i < num_poc_lt_curr_; ++i) {
if (!curr_delta_poc_msb_present_flag_[i])
ref_pic_set_lt_curr_[i] = dpb_.GetPicByPocMaskedAndMark(
poc_lt_curr_[i], sps->max_pic_order_cnt_lsb - 1,
H265Picture::kLongTermCurr);
else
ref_pic_set_lt_curr_[i] =
dpb_.GetPicByPocAndMark(poc_lt_curr_[i], H265Picture::kLongTermCurr);
if (ref_pic_set_lt_curr_[i])
total_ref_pics++;
}
for (int i = 0; i < num_poc_lt_foll_; ++i) {
if (!foll_delta_poc_msb_present_flag_[i])
ref_pic_set_lt_foll[i] = dpb_.GetPicByPocMaskedAndMark(
poc_lt_foll_[i], sps->max_pic_order_cnt_lsb - 1,
H265Picture::kLongTermFoll);
else
ref_pic_set_lt_foll[i] =
dpb_.GetPicByPocAndMark(poc_lt_foll_[i], H265Picture::kLongTermFoll);
if (ref_pic_set_lt_foll[i])
total_ref_pics++;
}
// Equation 8-7.
for (int i = 0; i < num_poc_st_curr_before_; ++i) {
ref_pic_set_st_curr_before_[i] = dpb_.GetPicByPocAndMark(
poc_st_curr_before_[i], H265Picture::kShortTermCurrBefore);
if (ref_pic_set_st_curr_before_[i])
total_ref_pics++;
}
for (int i = 0; i < num_poc_st_curr_after_; ++i) {
ref_pic_set_st_curr_after_[i] = dpb_.GetPicByPocAndMark(
poc_st_curr_after_[i], H265Picture::kShortTermCurrAfter);
if (ref_pic_set_st_curr_after_[i])
total_ref_pics++;
}
for (int i = 0; i < num_poc_st_foll_; ++i) {
ref_pic_set_st_foll[i] =
dpb_.GetPicByPocAndMark(poc_st_foll_[i], H265Picture::kShortTermFoll);
if (ref_pic_set_st_foll[i])
total_ref_pics++;
}
// Verify that the total number of reference pictures in the DPB matches the
// total count of reference pics. This ensures that a picture is not in more
// than one list, per the spec.
if (dpb_.GetReferencePicCount() != total_ref_pics) {
DVLOG(1) << "Conformance problem, reference pic is in more than one list";
return false;
}
ref_pic_list_.clear();
dpb_.AppendReferencePics(&ref_pic_list_);
ref_pic_list0_.clear();
ref_pic_list1_.clear();
// 8.3.3 Generation of unavailable reference pictures is something we do not
// need to handle here. It's handled by the accelerator itself when we do not
// specify a reference picture that it needs.
if (slice_hdr->IsPSlice() || slice_hdr->IsBSlice()) {
// 8.3.4 Decoding process for reference picture lists construction
int num_rps_curr_temp_list0 =
std::max(slice_hdr->num_ref_idx_l0_active_minus1 + 1,
slice_hdr->num_pic_total_curr);
scoped_refptr<H265Picture> ref_pic_list_temp0[kMaxDpbSize];
// Equation 8-8.
int r_idx = 0;
while (r_idx < num_rps_curr_temp_list0) {
for (int i = 0;
i < num_poc_st_curr_before_ && r_idx < num_rps_curr_temp_list0;
++i, ++r_idx) {
ref_pic_list_temp0[r_idx] = ref_pic_set_st_curr_before_[i];
}
for (int i = 0;
i < num_poc_st_curr_after_ && r_idx < num_rps_curr_temp_list0;
++i, ++r_idx) {
ref_pic_list_temp0[r_idx] = ref_pic_set_st_curr_after_[i];
}
for (int i = 0; i < num_poc_lt_curr_ && r_idx < num_rps_curr_temp_list0;
++i, ++r_idx) {
ref_pic_list_temp0[r_idx] = ref_pic_set_lt_curr_[i];
}
}
// Equation 8-9.
for (r_idx = 0; r_idx <= slice_hdr->num_ref_idx_l0_active_minus1; ++r_idx) {
ref_pic_list0_.push_back(
slice_hdr->ref_pic_lists_modification
.ref_pic_list_modification_flag_l0
? ref_pic_list_temp0[slice_hdr->ref_pic_lists_modification
.list_entry_l0[r_idx]]
: ref_pic_list_temp0[r_idx]);
}
if (slice_hdr->IsBSlice()) {
int num_rps_curr_temp_list1 =
std::max(slice_hdr->num_ref_idx_l1_active_minus1 + 1,
slice_hdr->num_pic_total_curr);
scoped_refptr<H265Picture> ref_pic_list_temp1[kMaxDpbSize];
// Equation 8-10.
r_idx = 0;
while (r_idx < num_rps_curr_temp_list1) {
for (int i = 0;
i < num_poc_st_curr_after_ && r_idx < num_rps_curr_temp_list1;
++i, r_idx++) {
ref_pic_list_temp1[r_idx] = ref_pic_set_st_curr_after_[i];
}
for (int i = 0;
i < num_poc_st_curr_before_ && r_idx < num_rps_curr_temp_list1;
++i, r_idx++) {
ref_pic_list_temp1[r_idx] = ref_pic_set_st_curr_before_[i];
}
for (int i = 0; i < num_poc_lt_curr_ && r_idx < num_rps_curr_temp_list1;
++i, r_idx++) {
ref_pic_list_temp1[r_idx] = ref_pic_set_lt_curr_[i];
}
}
// Equation 8-11.
for (r_idx = 0; r_idx <= slice_hdr->num_ref_idx_l1_active_minus1;
++r_idx) {
ref_pic_list1_.push_back(
slice_hdr->ref_pic_lists_modification
.ref_pic_list_modification_flag_l1
? ref_pic_list_temp1[slice_hdr->ref_pic_lists_modification
.list_entry_l1[r_idx]]
: ref_pic_list_temp1[r_idx]);
}
}
}
return true;
}
H265Decoder::H265Accelerator::Status H265Decoder::StartNewFrame(
const H265SliceHeader* slice_hdr) {
CHECK(curr_pic_.get());
DCHECK(slice_hdr);
curr_pps_id_ = slice_hdr->slice_pic_parameter_set_id;
const H265PPS* pps = parser_.GetPPS(curr_pps_id_);
// Slice header parsing already verified this should exist.
DCHECK(pps);
curr_sps_id_ = pps->pps_seq_parameter_set_id;
const H265SPS* sps = parser_.GetSPS(curr_sps_id_);
// Slice header parsing already verified this should exist.
DCHECK(sps);
// If this is from a retry on SubmitFrameMetadata, we should not redo all of
// these calculations.
if (!curr_pic_->processed_) {
// Copy slice/pps variables we need to the picture.
curr_pic_->nal_unit_type_ = curr_nalu_->nal_unit_type;
curr_pic_->irap_pic_ = slice_hdr->irap_pic;
curr_pic_->set_visible_rect(visible_rect_);
curr_pic_->set_bitstream_id(stream_id_);
if (sps->GetColorSpace().IsSpecified())
curr_pic_->set_colorspace(sps->GetColorSpace());
else
curr_pic_->set_colorspace(container_color_space_);
CalcPicOutputFlags(slice_hdr);
CalcPictureOrderCount(pps, slice_hdr);
if (!CalcRefPicPocs(sps, pps, slice_hdr)) {
return H265Accelerator::Status::kFail;
}
if (!BuildRefPicLists(sps, pps, slice_hdr)) {
return H265Accelerator::Status::kFail;
}
if (!PerformDpbOperations(sps)) {
return H265Accelerator::Status::kFail;
}
curr_pic_->processed_ = true;
}
return accelerator_->SubmitFrameMetadata(
sps, pps, slice_hdr, ref_pic_list_, ref_pic_set_lt_curr_,
ref_pic_set_st_curr_after_, ref_pic_set_st_curr_before_, curr_pic_);
}
H265Decoder::H265Accelerator::Status H265Decoder::FinishPrevFrameIfPresent() {
// If we already have a frame waiting to be decoded, decode it and finish.
if (curr_pic_) {
H265Accelerator::Status result = DecodePicture();
if (result != H265Accelerator::Status::kOk)
return result;
scoped_refptr<H265Picture> pic = curr_pic_;
curr_pic_ = nullptr;
FinishPicture(pic);
}
return H265Accelerator::Status::kOk;
}
bool H265Decoder::PerformDpbOperations(const H265SPS* sps) {
// C.5.2.2
if (curr_pic_->irap_pic_ && curr_pic_->no_rasl_output_flag_ &&
!curr_pic_->first_picture_) {
if (!curr_pic_->no_output_of_prior_pics_flag_) {
OutputAllRemainingPics();
}
dpb_.Clear();
} else {
int num_to_output;
do {
dpb_.DeleteUnused();
// Get all pictures that haven't been outputted yet.
H265Picture::Vector not_outputted;
dpb_.AppendPendingOutputPics(&not_outputted);
// Sort in output order.
std::sort(not_outputted.begin(), not_outputted.end(), POCAscCompare());
// Calculate how many pictures we need to output.
num_to_output = 0;
int highest_tid = sps->sps_max_sub_layers_minus1;
num_to_output = std::max(num_to_output,
static_cast<int>(not_outputted.size()) -
sps->sps_max_num_reorder_pics[highest_tid]);
num_to_output =
std::max(num_to_output,
static_cast<int>(dpb_.size()) -
sps->sps_max_dec_pic_buffering_minus1[highest_tid]);
num_to_output =
std::min(num_to_output, static_cast<int>(not_outputted.size()));
if (!num_to_output && dpb_.IsFull()) {
// This is wrong, we should try to output pictures until we can clear
// one from the DPB. This is better than failing, but we then may end up
// with something out of order.
DVLOG(1) << "Forcibly outputting pictures to make room in DPB.";
for (const auto& pic : not_outputted) {
num_to_output++;
if (pic->ref_ == H265Picture::kUnused)
break;
}
}
// TODO(jkardatzke): There's another output picture requirement regarding
// the sps_max_latency_increase_plus1, but I have yet to understand how
// that could be larger than the sps_max_num_reorder_pics since the actual
// latency value used is the sum of both.
not_outputted.resize(num_to_output);
for (auto& pic : not_outputted) {
if (!OutputPic(pic))
return false;
}
dpb_.DeleteUnused();
} while (dpb_.IsFull() && num_to_output);
}
if (dpb_.IsFull()) {
DVLOG(1) << "Could not free up space in DPB for current picture";
return false;
}
// Put the current pic in the DPB.
dpb_.StorePicture(curr_pic_, H265Picture::kShortTermFoll);
return true;
}
void H265Decoder::FinishPicture(scoped_refptr<H265Picture> pic) {
// 8.3.1
if (pic->valid_for_prev_tid0_pic_)
prev_tid0_pic_ = pic;
ref_pic_list_.clear();
ref_pic_list0_.clear();
ref_pic_list1_.clear();
ref_pic_set_lt_curr_.clear();
ref_pic_set_st_curr_after_.clear();
ref_pic_set_st_curr_before_.clear();
last_slice_hdr_.reset();
}
H265Decoder::H265Accelerator::Status H265Decoder::DecodePicture() {
DCHECK(curr_pic_.get());
return accelerator_->SubmitDecode(curr_pic_);
}
bool H265Decoder::OutputPic(scoped_refptr<H265Picture> pic) {
DCHECK(!pic->outputted_);
pic->outputted_ = true;
DVLOG(4) << "Posting output task for POC: " << pic->pic_order_cnt_val_;
return accelerator_->OutputPicture(std::move(pic));
}
bool H265Decoder::OutputAllRemainingPics() {
// Output all pictures that are waiting to be outputted.
H265Picture::Vector to_output;
dpb_.AppendPendingOutputPics(&to_output);
// Sort them by ascending POC to output in order.
std::sort(to_output.begin(), to_output.end(), POCAscCompare());
for (auto& pic : to_output) {
if (!OutputPic(std::move(pic)))
return false;
}
return true;
}
bool H265Decoder::Flush() {
DVLOG(2) << "Decoder flush";
if (!OutputAllRemainingPics())
return false;
dpb_.Clear();
prev_tid0_pic_ = nullptr;
return true;
}
} // namespace media