Commit 96330378 authored by Hirokazu Honda's avatar Hirokazu Honda Committed by Chromium LUCI CQ

media/gpu: Add GetBitDepth() to AcceleratedVideoDecoder interface

This adds the getter function of the bit depth of a stream
to AcceleratedVideoDecoder interface.

Bug: b:172223934
Test: media_unittests
Test: video.DecodeAccelVD.* on trogdor
Change-Id: Id3a5a3f84736a64d229366c0f352272483fae4c6
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2562798
Commit-Queue: Hirokazu Honda <hiroh@chromium.org>
Reviewed-by: default avatarAndres Calderon Jaramillo <andrescj@chromium.org>
Reviewed-by: default avatarDale Curtis <dalecurtis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#833169}
parent 0649a638
......@@ -69,13 +69,14 @@ class MEDIA_GPU_EXPORT AcceleratedVideoDecoder {
// we need a new set of them, or when an error occurs.
virtual DecodeResult Decode() WARN_UNUSED_RESULT = 0;
// Return dimensions/visible rectangle/profile/required number of pictures
// that client should be ready to provide for the decoder to function properly
// (of which up to GetNumReferenceFrames() might be needed for internal
// decoding). To be used after Decode() returns kConfigChange.
// Return dimensions/visible rectangle/profile/bit depth/required number of
// pictures that client should be ready to provide for the decoder to function
// properly (of which up to GetNumReferenceFrames() might be needed for
// internal decoding). To be used after Decode() returns kConfigChange.
virtual gfx::Size GetPicSize() const = 0;
virtual gfx::Rect GetVisibleRect() const = 0;
virtual VideoCodecProfile GetProfile() const = 0;
virtual uint8_t GetBitDepth() const = 0;
virtual size_t GetRequiredNumOfPictures() const = 0;
virtual size_t GetNumReferenceFrames() const = 0;
......
......@@ -59,6 +59,20 @@ bool IsYUV420Sequence(const libgav1::ColorConfig& color_config) {
return color_config.subsampling_x == 1 && color_config.subsampling_y == 1 &&
!color_config.is_monochrome;
}
bool IsValidBitDepth(uint8_t bit_depth, VideoCodecProfile profile) {
// Spec 6.4.1.
switch (profile) {
case AV1PROFILE_PROFILE_MAIN:
case AV1PROFILE_PROFILE_HIGH:
return bit_depth == 8u || bit_depth == 10u;
case AV1PROFILE_PROFILE_PRO:
return bit_depth == 8u || bit_depth == 10u || bit_depth == 12u;
default:
NOTREACHED();
return false;
}
}
} // namespace
AV1Decoder::AV1Decoder(std::unique_ptr<AV1Accelerator> accelerator,
......@@ -118,6 +132,7 @@ void AV1Decoder::Reset() {
visible_rect_ = gfx::Rect();
frame_size_ = gfx::Size();
profile_ = VideoCodecProfile::VIDEO_CODEC_PROFILE_UNKNOWN;
bit_depth_ = 0;
stream_id_ = 0;
stream_ = nullptr;
stream_size_ = 0;
......@@ -216,13 +231,6 @@ AcceleratedVideoDecoder::DecodeResult AV1Decoder::DecodeInternal() {
}
current_sequence_header_ = parser_->sequence_header();
// TODO(hiroh): Expose the bit depth to let the AV1Decoder client
// perform these checks.
if (current_sequence_header_->color_config.bitdepth != 8) {
// TODO(hiroh): Handle 10/12 bit depth.
DVLOG(1) << "10/12 bit depth are not supported";
return kDecodeError;
}
if (!IsYUV420Sequence(current_sequence_header_->color_config)) {
DVLOG(1) << "Only YUV 4:2:0 is supported";
return kDecodeError;
......@@ -236,6 +244,15 @@ AcceleratedVideoDecoder::DecodeResult AV1Decoder::DecodeInternal() {
base::strict_cast<int>(current_frame_header_->render_height));
const VideoCodecProfile new_profile =
AV1ProfileToVideoCodecProfile(current_sequence_header_->profile);
const uint8_t new_bit_depth = base::checked_cast<uint8_t>(
current_sequence_header_->color_config.bitdepth);
if (!IsValidBitDepth(new_bit_depth, new_profile)) {
DVLOG(1) << "Invalid bit depth="
<< base::strict_cast<int>(new_bit_depth)
<< ", profile=" << GetProfileName(new_profile);
return kDecodeError;
}
DCHECK(!new_frame_size.IsEmpty());
if (!gfx::Rect(new_frame_size).Contains(new_visible_rect)) {
DVLOG(1) << "Render size exceeds picture size. render size: "
......@@ -245,13 +262,15 @@ AcceleratedVideoDecoder::DecodeResult AV1Decoder::DecodeInternal() {
}
ClearReferenceFrames();
// Issues kConfigChange only if either the dimensions or profile is
// changed.
// Issues kConfigChange only if either the dimensions, profile or bit
// depth is changed.
if (frame_size_ != new_frame_size ||
visible_rect_ != new_visible_rect || profile_ != new_profile) {
visible_rect_ != new_visible_rect || profile_ != new_profile ||
bit_depth_ != new_bit_depth) {
frame_size_ = new_frame_size;
visible_rect_ = new_visible_rect;
profile_ = new_profile;
bit_depth_ = new_bit_depth;
clear_current_frame.ReplaceClosure(base::DoNothing());
return kConfigChange;
}
......@@ -436,6 +455,11 @@ VideoCodecProfile AV1Decoder::GetProfile() const {
return profile_;
}
uint8_t AV1Decoder::GetBitDepth() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return bit_depth_;
}
size_t AV1Decoder::GetRequiredNumOfPictures() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// TODO(hiroh): Double this value in the case of film grain sequence.
......
......@@ -106,6 +106,7 @@ class MEDIA_GPU_EXPORT AV1Decoder : public AcceleratedVideoDecoder {
gfx::Size GetPicSize() const override;
gfx::Rect GetVisibleRect() const override;
VideoCodecProfile GetProfile() const override;
uint8_t GetBitDepth() const override;
size_t GetRequiredNumOfPictures() const override;
size_t GetNumReferenceFrames() const override;
......@@ -137,6 +138,7 @@ class MEDIA_GPU_EXPORT AV1Decoder : public AcceleratedVideoDecoder {
gfx::Rect visible_rect_;
gfx::Size frame_size_;
VideoCodecProfile profile_;
uint8_t bit_depth_ = 0;
int32_t stream_id_ = 0;
const uint8_t* stream_ = nullptr;
......
......@@ -365,14 +365,38 @@ TEST_F(AV1DecoderTest, DecodeShowExistingPictureStream) {
}
TEST_F(AV1DecoderTest, Decode10bitStream) {
constexpr gfx::Size kFrameSize(320, 180);
constexpr gfx::Size kRenderSize(320, 180);
constexpr auto kProfile = libgav1::BitstreamProfile::kProfile0;
const std::string k10bitStream("bear-av1-320x180-10bit.webm");
std::vector<scoped_refptr<DecoderBuffer>> buffers = ReadWebm(k10bitStream);
ASSERT_FALSE(buffers.empty());
std::vector<DecodeResult> expected = {DecodeResult::kDecodeError};
EXPECT_EQ(Decode(buffers[0]), expected);
// Once AV1Decoder gets into an error state, Decode() returns kDecodeError
// until Reset().
EXPECT_EQ(Decode(buffers[1]), expected);
std::vector<DecodeResult> expected = {DecodeResult::kConfigChange};
std::vector<DecodeResult> results;
for (auto buffer : buffers) {
::testing::InSequence sequence;
auto av1_picture = base::MakeRefCounted<AV1Picture>();
EXPECT_CALL(*mock_accelerator_, CreateAV1Picture(/*apply_grain=*/false))
.WillOnce(Return(av1_picture));
EXPECT_CALL(
*mock_accelerator_,
SubmitDecode(
MatchesFrameHeader(kFrameSize, kRenderSize,
/*show_existing_frame=*/false,
/*show_frame=*/true),
MatchesYUV420SequenceHeader(kProfile, /*bitdepth=*/10, kFrameSize,
/*film_grain_params_present=*/false),
_, NonEmptyTileBuffers(), MatchesFrameData(buffer)))
.WillOnce(Return(true));
EXPECT_CALL(*mock_accelerator_,
OutputPicture(SameAV1PictureInstance(av1_picture)))
.WillOnce(Return(true));
for (DecodeResult r : Decode(buffer))
results.push_back(r);
expected.push_back(DecodeResult::kRanOutOfStreamData);
testing::Mock::VerifyAndClearExpectations(mock_accelerator_);
}
EXPECT_EQ(results, expected);
}
TEST_F(AV1DecoderTest, DecodeSVCStream) {
......
......@@ -17,6 +17,67 @@
#include "media/video/h264_level_limits.h"
namespace media {
namespace {
bool ParseBitDepth(const H264SPS& sps, uint8_t& bit_depth) {
// Spec 7.4.2.1.1
if (sps.bit_depth_luma_minus8 != sps.bit_depth_chroma_minus8) {
DVLOG(1) << "H264Decoder doesn't support different bit depths between luma"
<< "and chroma, bit_depth_luma_minus8="
<< sps.bit_depth_luma_minus8
<< ", bit_depth_chroma_minus8=" << sps.bit_depth_chroma_minus8;
return false;
}
DCHECK_GE(sps.bit_depth_luma_minus8, 0);
DCHECK_LE(sps.bit_depth_luma_minus8, 6);
switch (sps.bit_depth_luma_minus8) {
case 0:
bit_depth = 8u;
break;
case 2:
bit_depth = 10u;
break;
case 4:
bit_depth = 12u;
break;
case 6:
bit_depth = 14u;
break;
default:
DVLOG(1) << "Invalid bit depth: "
<< base::checked_cast<int>(sps.bit_depth_luma_minus8 + 8);
return false;
}
return true;
}
bool IsValidBitDepth(uint8_t bit_depth, VideoCodecProfile profile) {
// Spec A.2.
switch (profile) {
case H264PROFILE_BASELINE:
case H264PROFILE_MAIN:
case H264PROFILE_EXTENDED:
case H264PROFILE_HIGH:
return bit_depth == 8u;
case H264PROFILE_HIGH10PROFILE:
case H264PROFILE_HIGH422PROFILE:
return bit_depth == 8u || bit_depth == 10u;
case H264PROFILE_HIGH444PREDICTIVEPROFILE:
return bit_depth == 8u || bit_depth == 10u || bit_depth == 12u ||
bit_depth == 14u;
case H264PROFILE_SCALABLEBASELINE:
case H264PROFILE_SCALABLEHIGH:
// Spec G.10.1.
return bit_depth == 8u;
case H264PROFILE_STEREOHIGH:
case H264PROFILE_MULTIVIEWHIGH:
// Spec H.10.1.1 and H.10.1.2.
return bit_depth == 8u;
default:
NOTREACHED();
return false;
}
}
} // namespace
H264Decoder::H264Accelerator::H264Accelerator() = default;
......@@ -1086,18 +1147,28 @@ bool H264Decoder::ProcessSPS(int sps_id, bool* need_new_buffers) {
DVLOG(1) << "Invalid DPB size: " << max_dpb_size;
return false;
}
VideoCodecProfile new_profile =
H264Parser::ProfileIDCToVideoCodecProfile(sps->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() != max_dpb_size ||
profile_ != new_profile) {
profile_ != new_profile || bit_depth_ != new_bit_depth) {
if (!Flush())
return false;
DVLOG(1) << "Codec profile: " << GetProfileName(new_profile)
<< ", level: " << level << ", DPB size: " << max_dpb_size
<< ", Picture size: " << new_pic_size.ToString();
<< ", Picture size: " << new_pic_size.ToString()
<< ", bit depth: " << base::strict_cast<int>(new_bit_depth);
*need_new_buffers = true;
profile_ = new_profile;
bit_depth_ = new_bit_depth;
pic_size_ = new_pic_size;
dpb_.set_max_num_pics(max_dpb_size);
}
......@@ -1455,6 +1526,10 @@ VideoCodecProfile H264Decoder::GetProfile() const {
return profile_;
}
uint8_t H264Decoder::GetBitDepth() const {
return bit_depth_;
}
size_t H264Decoder::GetRequiredNumOfPictures() const {
constexpr size_t kPicsInPipeline = limits::kMaxVideoFrames + 1;
return GetNumReferenceFrames() + kPicsInPipeline;
......
......@@ -159,6 +159,7 @@ class MEDIA_GPU_EXPORT H264Decoder : public AcceleratedVideoDecoder {
gfx::Size GetPicSize() const override;
gfx::Rect GetVisibleRect() const override;
VideoCodecProfile GetProfile() const override;
uint8_t GetBitDepth() const override;
size_t GetRequiredNumOfPictures() const override;
size_t GetNumReferenceFrames() const override;
......@@ -357,6 +358,8 @@ class MEDIA_GPU_EXPORT H264Decoder : public AcceleratedVideoDecoder {
// Profile of input bitstream.
VideoCodecProfile profile_;
// Bit depth of input bitstream.
uint8_t bit_depth_ = 0;
// PicOrderCount of the previously outputted frame.
int last_output_poc_;
......
......@@ -43,6 +43,10 @@ const std::string kHighFrame0 = "bear-320x192-high-frame-0.h264";
const std::string kHighFrame1 = "bear-320x192-high-frame-1.h264";
const std::string kHighFrame2 = "bear-320x192-high-frame-2.h264";
const std::string kHighFrame3 = "bear-320x192-high-frame-3.h264";
const std::string k10BitFrame0 = "bear-320x180-10bit-frame-0.h264";
const std::string k10BitFrame1 = "bear-320x180-10bit-frame-1.h264";
const std::string k10BitFrame2 = "bear-320x180-10bit-frame-2.h264";
const std::string k10BitFrame3 = "bear-320x180-10bit-frame-3.h264";
// Checks whether the decrypt config in the picture matches the decrypt config
// passed to this matcher.
......@@ -237,6 +241,7 @@ TEST_F(H264DecoderTest, DecodeSingleFrame) {
ASSERT_EQ(AcceleratedVideoDecoder::kConfigChange, Decode());
EXPECT_EQ(gfx::Size(320, 192), decoder_->GetPicSize());
EXPECT_EQ(H264PROFILE_BASELINE, decoder_->GetProfile());
EXPECT_EQ(8u, decoder_->GetBitDepth());
EXPECT_LE(9u, decoder_->GetRequiredNumOfPictures());
EXPECT_CALL(*accelerator_, CreateH264Picture()).WillOnce(Return(nullptr));
......@@ -260,6 +265,7 @@ TEST_F(H264DecoderTest, SkipNonIDRFrames) {
ASSERT_EQ(AcceleratedVideoDecoder::kConfigChange, Decode());
EXPECT_EQ(gfx::Size(320, 192), decoder_->GetPicSize());
EXPECT_EQ(H264PROFILE_BASELINE, decoder_->GetProfile());
EXPECT_EQ(8u, decoder_->GetBitDepth());
EXPECT_LE(9u, decoder_->GetRequiredNumOfPictures());
{
InSequence sequence;
......@@ -280,6 +286,7 @@ TEST_F(H264DecoderTest, DecodeProfileBaseline) {
ASSERT_EQ(AcceleratedVideoDecoder::kConfigChange, Decode());
EXPECT_EQ(gfx::Size(320, 192), decoder_->GetPicSize());
EXPECT_EQ(H264PROFILE_BASELINE, decoder_->GetProfile());
EXPECT_EQ(8u, decoder_->GetBitDepth());
EXPECT_LE(9u, decoder_->GetRequiredNumOfPictures());
EXPECT_CALL(*accelerator_, CreateH264Picture()).Times(4);
......@@ -305,6 +312,40 @@ TEST_F(H264DecoderTest, DecodeProfileBaseline) {
ASSERT_TRUE(decoder_->Flush());
}
TEST_F(H264DecoderTest, Decode10BitStream) {
SetInputFrameFiles({k10BitFrame0, k10BitFrame1, k10BitFrame2, k10BitFrame3});
ASSERT_EQ(AcceleratedVideoDecoder::kConfigChange, Decode());
EXPECT_EQ(gfx::Size(320, 192), decoder_->GetPicSize());
EXPECT_EQ(gfx::Rect(320, 180), decoder_->GetVisibleRect());
EXPECT_EQ(H264PROFILE_HIGH10PROFILE, decoder_->GetProfile());
EXPECT_EQ(10u, decoder_->GetBitDepth());
EXPECT_LE(14u, decoder_->GetRequiredNumOfPictures());
// One picture will be kept in the DPB for reordering. The second picture
// should be outputted after feeding the third and fourth frames.
EXPECT_CALL(*accelerator_, CreateH264Picture()).Times(4);
EXPECT_CALL(*accelerator_, SubmitFrameMetadata(_, _, _, _, _, _, _)).Times(4);
EXPECT_CALL(*accelerator_, SubmitSlice(_, _, _, _, _, _, _, _)).Times(4);
Expectation decode_poc0, decode_poc2, decode_poc4, decode_poc6;
{
InSequence decode_order;
decode_poc0 = EXPECT_CALL(*accelerator_, SubmitDecode(WithPoc(0)));
decode_poc6 = EXPECT_CALL(*accelerator_, SubmitDecode(WithPoc(6)));
decode_poc2 = EXPECT_CALL(*accelerator_, SubmitDecode(WithPoc(2)));
decode_poc4 = EXPECT_CALL(*accelerator_, SubmitDecode(WithPoc(4)));
}
{
InSequence display_order;
EXPECT_CALL(*accelerator_, OutputPicture(WithPoc(0))).After(decode_poc0);
EXPECT_CALL(*accelerator_, OutputPicture(WithPoc(2))).After(decode_poc2);
EXPECT_CALL(*accelerator_, OutputPicture(WithPoc(4))).After(decode_poc4);
EXPECT_CALL(*accelerator_, OutputPicture(WithPoc(6))).After(decode_poc6);
}
ASSERT_EQ(AcceleratedVideoDecoder::kRanOutOfStreamData, Decode());
ASSERT_TRUE(decoder_->Flush());
}
// TODO(jkardatzke): Remove this test if we keep the flag for DecoderBuffers
// are complete frames because this code path will never get called.
TEST_F(H264DecoderTest, DISABLED_OutputPictureFailureCausesFlushToFail) {
......@@ -335,10 +376,11 @@ TEST_F(H264DecoderTest, DecodeProfileHigh) {
ASSERT_EQ(AcceleratedVideoDecoder::kConfigChange, Decode());
EXPECT_EQ(gfx::Size(320, 192), decoder_->GetPicSize());
EXPECT_EQ(H264PROFILE_HIGH, decoder_->GetProfile());
EXPECT_EQ(8u, decoder_->GetBitDepth());
EXPECT_LE(16u, decoder_->GetRequiredNumOfPictures());
// Two pictures will be kept in DPB for reordering. The first picture should
// be outputted after feeding the third frame.
// Two pictures will be kept in the DPB for reordering. The first picture
// should be outputted after feeding the third frame.
EXPECT_CALL(*accelerator_, CreateH264Picture()).Times(4);
EXPECT_CALL(*accelerator_, SubmitFrameMetadata(_, _, _, _, _, _, _)).Times(4);
EXPECT_CALL(*accelerator_, SubmitSlice(_, _, _, _, _, _, _, _)).Times(4);
......@@ -369,6 +411,7 @@ TEST_F(H264DecoderTest, SwitchBaselineToHigh) {
ASSERT_EQ(AcceleratedVideoDecoder::kConfigChange, Decode());
EXPECT_EQ(gfx::Size(320, 192), decoder_->GetPicSize());
EXPECT_EQ(H264PROFILE_BASELINE, decoder_->GetProfile());
EXPECT_EQ(8u, decoder_->GetBitDepth());
EXPECT_LE(9u, decoder_->GetRequiredNumOfPictures());
{
......@@ -382,6 +425,7 @@ TEST_F(H264DecoderTest, SwitchBaselineToHigh) {
ASSERT_EQ(AcceleratedVideoDecoder::kConfigChange, Decode());
EXPECT_EQ(gfx::Size(320, 192), decoder_->GetPicSize());
EXPECT_EQ(H264PROFILE_HIGH, decoder_->GetProfile());
EXPECT_EQ(8u, decoder_->GetBitDepth());
EXPECT_LE(16u, decoder_->GetRequiredNumOfPictures());
ASSERT_TRUE(Mock::VerifyAndClearExpectations(&*accelerator_));
......@@ -417,6 +461,7 @@ TEST_F(H264DecoderTest, SwitchHighToBaseline) {
ASSERT_EQ(AcceleratedVideoDecoder::kConfigChange, Decode());
EXPECT_EQ(gfx::Size(320, 192), decoder_->GetPicSize());
EXPECT_EQ(H264PROFILE_HIGH, decoder_->GetProfile());
EXPECT_EQ(8u, decoder_->GetBitDepth());
EXPECT_LE(16u, decoder_->GetRequiredNumOfPictures());
{
......@@ -430,6 +475,7 @@ TEST_F(H264DecoderTest, SwitchHighToBaseline) {
ASSERT_EQ(AcceleratedVideoDecoder::kConfigChange, Decode());
EXPECT_EQ(gfx::Size(320, 192), decoder_->GetPicSize());
EXPECT_EQ(H264PROFILE_BASELINE, decoder_->GetProfile());
EXPECT_EQ(8u, decoder_->GetBitDepth());
EXPECT_LE(9u, decoder_->GetRequiredNumOfPictures());
ASSERT_TRUE(Mock::VerifyAndClearExpectations(&*accelerator_));
......@@ -488,6 +534,7 @@ TEST_F(H264DecoderTest, SetEncryptedStream) {
decoder_->SetStream(0, *buffer);
EXPECT_EQ(AcceleratedVideoDecoder::kConfigChange, decoder_->Decode());
EXPECT_EQ(H264PROFILE_BASELINE, decoder_->GetProfile());
EXPECT_EQ(8u, decoder_->GetBitDepth());
EXPECT_EQ(AcceleratedVideoDecoder::kRanOutOfStreamData, decoder_->Decode());
EXPECT_TRUE(decoder_->Flush());
}
......@@ -497,6 +544,7 @@ TEST_F(H264DecoderTest, SubmitFrameMetadataRetry) {
ASSERT_EQ(AcceleratedVideoDecoder::kConfigChange, Decode());
EXPECT_EQ(gfx::Size(320, 192), decoder_->GetPicSize());
EXPECT_EQ(H264PROFILE_BASELINE, decoder_->GetProfile());
EXPECT_EQ(8u, decoder_->GetBitDepth());
EXPECT_LE(9u, decoder_->GetRequiredNumOfPictures());
{
......@@ -532,6 +580,7 @@ TEST_F(H264DecoderTest, SubmitSliceRetry) {
ASSERT_EQ(AcceleratedVideoDecoder::kConfigChange, Decode());
EXPECT_EQ(gfx::Size(320, 192), decoder_->GetPicSize());
EXPECT_EQ(H264PROFILE_BASELINE, decoder_->GetProfile());
EXPECT_EQ(8u, decoder_->GetBitDepth());
EXPECT_LE(9u, decoder_->GetRequiredNumOfPictures());
{
......@@ -567,6 +616,7 @@ TEST_F(H264DecoderTest, SubmitDecodeRetry) {
ASSERT_EQ(AcceleratedVideoDecoder::kConfigChange, Decode());
EXPECT_EQ(gfx::Size(320, 192), decoder_->GetPicSize());
EXPECT_EQ(H264PROFILE_BASELINE, decoder_->GetProfile());
EXPECT_EQ(8u, decoder_->GetBitDepth());
EXPECT_LE(9u, decoder_->GetRequiredNumOfPictures());
{
......@@ -615,6 +665,7 @@ TEST_F(H264DecoderTest, SetStreamRetry) {
ASSERT_EQ(AcceleratedVideoDecoder::kConfigChange, Decode());
EXPECT_EQ(gfx::Size(320, 192), decoder_->GetPicSize());
EXPECT_EQ(H264PROFILE_BASELINE, decoder_->GetProfile());
EXPECT_EQ(8u, decoder_->GetBitDepth());
EXPECT_LE(9u, decoder_->GetRequiredNumOfPictures());
{
......
......@@ -5,6 +5,7 @@
#include <algorithm>
#include "base/logging.h"
#include "base/notreached.h"
#include "media/base/limits.h"
#include "media/gpu/h265_decoder.h"
......@@ -19,6 +20,30 @@ struct POCAscCompare {
}
};
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) {
// Spec A.3.
switch (profile) {
case HEVCPROFILE_MAIN:
return bit_depth == 8u;
case HEVCPROFILE_MAIN10:
return bit_depth == 8u || bit_depth == 10u;
case HEVCPROFILE_MAIN_STILL_PICTURE:
return bit_depth == 8u;
default:
NOTREACHED();
return false;
}
}
} // namespace
H265Decoder::H265Accelerator::H265Accelerator() = default;
......@@ -310,6 +335,10 @@ VideoCodecProfile H265Decoder::GetProfile() const {
return profile_;
}
uint8_t H265Decoder::GetBitDepth() const {
return bit_depth_;
}
size_t H265Decoder::GetRequiredNumOfPictures() const {
constexpr size_t kPicsInPipeline = limits::kMaxVideoFrames + 1;
return GetNumReferenceFrames() + kPicsInPipeline;
......@@ -347,16 +376,25 @@ bool H265Decoder::ProcessPPS(int pps_id, bool* need_new_buffers) {
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) {
profile_ != new_profile || bit_depth_ != new_bit_depth) {
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();
<< ", Picture size: " << new_pic_size.ToString()
<< ", bit_depth: " << base::strict_cast<int>(new_bit_depth);
profile_ = new_profile;
bit_depth_ = new_bit_depth;
pic_size_ = new_pic_size;
dpb_.set_max_num_pics(sps->max_dpb_size);
if (need_new_buffers)
......
......@@ -166,6 +166,7 @@ class MEDIA_GPU_EXPORT H265Decoder final : public AcceleratedVideoDecoder {
gfx::Size GetPicSize() const override;
gfx::Rect GetVisibleRect() const override;
VideoCodecProfile GetProfile() const override;
uint8_t GetBitDepth() const override;
size_t GetRequiredNumOfPictures() const override;
size_t GetNumReferenceFrames() const override;
......@@ -320,10 +321,12 @@ class MEDIA_GPU_EXPORT H265Decoder final : public AcceleratedVideoDecoder {
// Profile of input bitstream.
VideoCodecProfile profile_;
// Bit depth of input bitstream.
uint8_t bit_depth_ = 0;
const std::unique_ptr<H265Accelerator> accelerator_;
};
} // namespace media
#endif // MEDIA_GPU_H265_DECODER_H_
\ No newline at end of file
#endif // MEDIA_GPU_H265_DECODER_H_
......@@ -37,6 +37,10 @@ constexpr char kFrame2[] = "bear-frame2.hevc";
constexpr char kFrame3[] = "bear-frame3.hevc";
constexpr char kFrame4[] = "bear-frame4.hevc";
constexpr char kFrame5[] = "bear-frame5.hevc";
constexpr char k10BitFrame0[] = "bear-320x180-10bit-frame-0.hevc";
constexpr char k10BitFrame1[] = "bear-320x180-10bit-frame-1.hevc";
constexpr char k10BitFrame2[] = "bear-320x180-10bit-frame-2.hevc";
constexpr char k10BitFrame3[] = "bear-320x180-10bit-frame-3.hevc";
// Checks whether the decrypt config in the picture matches the decrypt config
// passed to this matcher.
......@@ -194,6 +198,7 @@ TEST_F(H265DecoderTest, DecodeSingleFrame) {
EXPECT_EQ(AcceleratedVideoDecoder::kConfigChange, Decode());
EXPECT_EQ(gfx::Size(320, 184), decoder_->GetPicSize());
EXPECT_EQ(HEVCPROFILE_MAIN, decoder_->GetProfile());
EXPECT_EQ(8u, decoder_->GetBitDepth());
EXPECT_EQ(17u, decoder_->GetRequiredNumOfPictures());
// Also test running out of surfaces.
......@@ -218,6 +223,7 @@ TEST_F(H265DecoderTest, SkipNonIDRFrames) {
EXPECT_EQ(AcceleratedVideoDecoder::kConfigChange, Decode());
EXPECT_EQ(gfx::Size(320, 184), decoder_->GetPicSize());
EXPECT_EQ(HEVCPROFILE_MAIN, decoder_->GetProfile());
EXPECT_EQ(8u, decoder_->GetBitDepth());
EXPECT_EQ(17u, decoder_->GetRequiredNumOfPictures());
{
InSequence sequence;
......@@ -237,6 +243,7 @@ TEST_F(H265DecoderTest, DecodeProfileMain) {
EXPECT_EQ(AcceleratedVideoDecoder::kConfigChange, Decode());
EXPECT_EQ(gfx::Size(320, 184), decoder_->GetPicSize());
EXPECT_EQ(HEVCPROFILE_MAIN, decoder_->GetProfile());
EXPECT_EQ(8u, decoder_->GetBitDepth());
EXPECT_EQ(17u, decoder_->GetRequiredNumOfPictures());
EXPECT_CALL(*accelerator_, CreateH265Picture()).Times(6);
......@@ -268,6 +275,41 @@ TEST_F(H265DecoderTest, DecodeProfileMain) {
EXPECT_TRUE(decoder_->Flush());
}
TEST_F(H265DecoderTest, Decode10BitStream) {
SetInputFrameFiles({k10BitFrame0, k10BitFrame1, k10BitFrame2, k10BitFrame3});
EXPECT_EQ(AcceleratedVideoDecoder::kConfigChange, Decode());
EXPECT_EQ(gfx::Size(320, 184), decoder_->GetPicSize());
EXPECT_EQ(gfx::Rect(320, 180), decoder_->GetVisibleRect());
EXPECT_EQ(HEVCPROFILE_MAIN10, decoder_->GetProfile());
EXPECT_EQ(10u, decoder_->GetBitDepth());
EXPECT_EQ(17u, decoder_->GetRequiredNumOfPictures());
EXPECT_CALL(*accelerator_, CreateH265Picture()).Times(4);
EXPECT_CALL(*accelerator_, SubmitFrameMetadata(_, _, _, _, _)).Times(4);
EXPECT_CALL(*accelerator_, SubmitSlice(_, _, _, _, _, _, _, _, _)).Times(4);
// Two pictures will be kept in the DPB for reordering. The second and third
// pictures should be outputted after feeding the fourth frame.
Expectation decode_poc0, decode_poc1, decode_poc2, decode_poc3;
{
InSequence decode_order;
decode_poc0 = EXPECT_CALL(*accelerator_, SubmitDecode(HasPoc(0)));
decode_poc3 = EXPECT_CALL(*accelerator_, SubmitDecode(HasPoc(3)));
decode_poc2 = EXPECT_CALL(*accelerator_, SubmitDecode(HasPoc(2)));
decode_poc1 = EXPECT_CALL(*accelerator_, SubmitDecode(HasPoc(1)));
}
{
InSequence display_order;
EXPECT_CALL(*accelerator_, OutputPicture(HasPoc(0))).After(decode_poc0);
EXPECT_CALL(*accelerator_, OutputPicture(HasPoc(1))).After(decode_poc1);
EXPECT_CALL(*accelerator_, OutputPicture(HasPoc(2))).After(decode_poc2);
EXPECT_CALL(*accelerator_, OutputPicture(HasPoc(3))).After(decode_poc3);
}
EXPECT_EQ(AcceleratedVideoDecoder::kRanOutOfStreamData, Decode());
EXPECT_TRUE(decoder_->Flush());
}
TEST_F(H265DecoderTest, OutputPictureFailureCausesDecodeToFail) {
// Provide enough data that Decode() will try to output a frame.
SetInputFrameFiles({kSpsPps, kFrame0, kFrame1, kFrame2, kFrame3});
......@@ -314,6 +356,7 @@ TEST_F(H265DecoderTest, SetEncryptedStream) {
decoder_->SetStream(0, *buffer);
EXPECT_EQ(AcceleratedVideoDecoder::kConfigChange, decoder_->Decode());
EXPECT_EQ(HEVCPROFILE_MAIN, decoder_->GetProfile());
EXPECT_EQ(8u, decoder_->GetBitDepth());
EXPECT_EQ(AcceleratedVideoDecoder::kRanOutOfStreamData, decoder_->Decode());
EXPECT_TRUE(decoder_->Flush());
}
......@@ -323,6 +366,7 @@ TEST_F(H265DecoderTest, SubmitFrameMetadataRetry) {
EXPECT_EQ(AcceleratedVideoDecoder::kConfigChange, Decode());
EXPECT_EQ(gfx::Size(320, 184), decoder_->GetPicSize());
EXPECT_EQ(HEVCPROFILE_MAIN, decoder_->GetProfile());
EXPECT_EQ(8u, decoder_->GetBitDepth());
EXPECT_EQ(17u, decoder_->GetRequiredNumOfPictures());
{
......@@ -358,6 +402,7 @@ TEST_F(H265DecoderTest, SubmitSliceRetry) {
EXPECT_EQ(AcceleratedVideoDecoder::kConfigChange, Decode());
EXPECT_EQ(gfx::Size(320, 184), decoder_->GetPicSize());
EXPECT_EQ(HEVCPROFILE_MAIN, decoder_->GetProfile());
EXPECT_EQ(8u, decoder_->GetBitDepth());
EXPECT_EQ(17u, decoder_->GetRequiredNumOfPictures());
{
......@@ -393,6 +438,7 @@ TEST_F(H265DecoderTest, SubmitDecodeRetry) {
EXPECT_EQ(AcceleratedVideoDecoder::kConfigChange, Decode());
EXPECT_EQ(gfx::Size(320, 184), decoder_->GetPicSize());
EXPECT_EQ(HEVCPROFILE_MAIN, decoder_->GetProfile());
EXPECT_EQ(8u, decoder_->GetBitDepth());
EXPECT_EQ(17u, decoder_->GetRequiredNumOfPictures());
{
......@@ -442,6 +488,7 @@ TEST_F(H265DecoderTest, SetStreamRetry) {
EXPECT_EQ(AcceleratedVideoDecoder::kConfigChange, Decode(false));
EXPECT_EQ(gfx::Size(320, 184), decoder_->GetPicSize());
EXPECT_EQ(HEVCPROFILE_MAIN, decoder_->GetProfile());
EXPECT_EQ(8u, decoder_->GetBitDepth());
EXPECT_EQ(17u, decoder_->GetRequiredNumOfPictures());
{
......
......@@ -61,6 +61,7 @@ class MockAcceleratedVideoDecoder : public AcceleratedVideoDecoder {
MOCK_METHOD0(Decode, DecodeResult());
MOCK_CONST_METHOD0(GetPicSize, gfx::Size());
MOCK_CONST_METHOD0(GetProfile, VideoCodecProfile());
MOCK_CONST_METHOD0(GetBitDepth, uint8_t());
MOCK_CONST_METHOD0(GetVisibleRect, gfx::Rect());
MOCK_CONST_METHOD0(GetRequiredNumOfPictures, size_t());
MOCK_CONST_METHOD0(GetNumReferenceFrames, size_t());
......
......@@ -180,6 +180,10 @@ VideoCodecProfile VP8Decoder::GetProfile() const {
return VP8PROFILE_ANY;
}
uint8_t VP8Decoder::GetBitDepth() const {
return 8u;
}
size_t VP8Decoder::GetRequiredNumOfPictures() const {
constexpr size_t kPicsInPipeline = limits::kMaxVideoFrames + 1;
return kVP8NumFramesActive + kPicsInPipeline;
......
......@@ -70,6 +70,7 @@ class MEDIA_GPU_EXPORT VP8Decoder : public AcceleratedVideoDecoder {
gfx::Size GetPicSize() const override;
gfx::Rect GetVisibleRect() const override;
VideoCodecProfile GetProfile() const override;
uint8_t GetBitDepth() const override;
size_t GetRequiredNumOfPictures() const override;
size_t GetNumReferenceFrames() const override;
......
......@@ -80,6 +80,7 @@ void VP8DecoderTest::SetUp() {
void VP8DecoderTest::DecodeFirstIFrame() {
ASSERT_EQ(AcceleratedVideoDecoder::kRanOutOfStreamData, Decode(kNullFrame));
ASSERT_EQ(AcceleratedVideoDecoder::kConfigChange, Decode(kIFrame));
EXPECT_EQ(8u, decoder_->GetBitDepth());
EXPECT_EQ(kVideoSize, decoder_->GetPicSize());
EXPECT_LE(kRequiredNumOfPictures, decoder_->GetRequiredNumOfPictures());
}
......@@ -119,6 +120,8 @@ AcceleratedVideoDecoder::DecodeResult VP8DecoderTest::Decode(
AcceleratedVideoDecoder::DecodeResult::kRanOutOfStreamData ||
result == AcceleratedVideoDecoder::DecodeResult::kConfigChange ||
result == AcceleratedVideoDecoder::DecodeResult::kDecodeError);
if (result != AcceleratedVideoDecoder::DecodeResult::kDecodeError)
EXPECT_EQ(8u, decoder_->GetBitDepth());
return result;
}
......
......@@ -56,6 +56,20 @@ VideoCodecProfile VP9ProfileToVideoCodecProfile(uint8_t profile) {
}
}
bool IsValidBitDepth(uint8_t bit_depth, VideoCodecProfile profile) {
// Spec 7.2.
switch (profile) {
case VP9PROFILE_PROFILE0:
case VP9PROFILE_PROFILE1:
return bit_depth == 8u;
case VP9PROFILE_PROFILE2:
case VP9PROFILE_PROFILE3:
return bit_depth == 10u || bit_depth == 12u;
default:
NOTREACHED();
return false;
}
}
} // namespace
VP9Decoder::VP9Accelerator::VP9Accelerator() {}
......@@ -217,11 +231,20 @@ VP9Decoder::DecodeResult VP9Decoder::Decode() {
VLOG(1) << "Invalid profile: " << curr_frame_hdr_->profile;
return kDecodeError;
}
if (!IsValidBitDepth(curr_frame_hdr_->bit_depth, new_profile)) {
DVLOG(1) << "Invalid bit depth="
<< base::strict_cast<int>(curr_frame_hdr_->bit_depth)
<< ", profile=" << GetProfileName(new_profile);
return kDecodeError;
}
DCHECK(!new_pic_size.IsEmpty());
if (new_pic_size != pic_size_ || new_profile != profile_) {
if (new_pic_size != pic_size_ || new_profile != profile_ ||
curr_frame_hdr_->bit_depth != bit_depth_) {
DVLOG(1) << "New profile: " << GetProfileName(new_profile)
<< ", New resolution: " << new_pic_size.ToString();
<< ", New resolution: " << new_pic_size.ToString()
<< ", New bit depth: "
<< base::strict_cast<int>(curr_frame_hdr_->bit_depth);
if (!curr_frame_hdr_->IsKeyframe() &&
!(curr_frame_hdr_->IsIntra() && pic_size_.IsEmpty())) {
......@@ -249,6 +272,7 @@ VP9Decoder::DecodeResult VP9Decoder::Decode() {
pic_size_ = new_pic_size;
visible_rect_ = new_render_rect;
profile_ = new_profile;
bit_depth_ = curr_frame_hdr_->bit_depth;
size_change_failure_counter_ = 0;
return kConfigChange;
}
......@@ -347,6 +371,10 @@ VideoCodecProfile VP9Decoder::GetProfile() const {
return profile_;
}
uint8_t VP9Decoder::GetBitDepth() const {
return bit_depth_;
}
size_t VP9Decoder::GetRequiredNumOfPictures() const {
constexpr size_t kPicsInPipeline = limits::kMaxVideoFrames + 1;
return kPicsInPipeline + GetNumReferenceFrames();
......
......@@ -125,6 +125,7 @@ class MEDIA_GPU_EXPORT VP9Decoder : public AcceleratedVideoDecoder {
gfx::Size GetPicSize() const override;
gfx::Rect GetVisibleRect() const override;
VideoCodecProfile GetProfile() const override;
uint8_t GetBitDepth() const override;
size_t GetRequiredNumOfPictures() const override;
size_t GetNumReferenceFrames() const override;
......@@ -173,6 +174,8 @@ class MEDIA_GPU_EXPORT VP9Decoder : public AcceleratedVideoDecoder {
gfx::Rect visible_rect_;
// Profile of input bitstream.
VideoCodecProfile profile_;
// Bit depth of input bitstream.
uint8_t bit_depth_ = 0;
// Pending picture for decode when accelerator returns kTryAgain.
scoped_refptr<VP9Picture> pending_pic_;
......
......@@ -298,6 +298,17 @@ mp4edit.exe --insert moov/trak/mdia/minf/stbl/stsd/vp09:smdm.bin \
smdm.bin and coll.bin generated with program from https://crbug.com/1123430#c5.
#### bear-320x180-10bit-frame-\{0,1,2,3\}.h264
The first four frames of the H.264 version of bear-av1-320x180-10bit.mp4 created
using the following command.
`ffmpeg -i bear-av1-320x180-10bit.mp4 -vcodec h264 -vframes 4 bear-320x180-10bit-4frames.h264`
The file is then split into bitstreams each of which contains a single frame, so
that they contain frames as below.
bear-320x180-10bit-frame-0.h264: SPS+PPS+Single IDR
bear-320x180-10bit-frame-1.h264: B
bear-320x180-10bit-frame-2.h264: B
bear-320x180-10bit-frame-3.h264: P
### AAC test data from MPEG-DASH demoplayer (44100 Hz, stereo)
Duration of each packet is (1024/44100 Hz), approximately 23.22 ms.
......@@ -903,6 +914,17 @@ SPS and PPS from bear.hevc for h265_decoder_unittest.cc.
Single IDR, P, B, B, B, P frames respectively from bear.hevc for
h265_decoder_unittest.cc.
#### bear-320x180-10bit-frame-\{0,1,2,3\}.hevc
The first four frames of the HEVC version of bear-av1-320x180-10bit.mp4 created
using the following command.
`ffmpeg -i bear-av1-320x180-10bit.mp4 -vcodec hevc -vframes 4 bear-320x180-10bit-4frames.hevc`
The file is then split into bitstreams each of which contains a single frame, so
that they contain frames as below.
bear-320x180-10bit-frame-0.hevc: SPS+PPS+Single IDR
bear-320x180-10bit-frame-1.hevc: B
bear-320x180-10bit-frame-2.hevc: B
bear-320x180-10bit-frame-3.hevc: P
### WebM files for testing multiple tracks.
#### green-a300hz.webm
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment