Skip to content

Commit

Permalink
media/gpu/v4l2/test: Implement H265Decoder::ProcessPPS()
Browse files Browse the repository at this point in the history
This CL implements ProcessPPS(), which processes PPS (picture parameter
set) for each frame for a given H.265 bitstream. It also adds helper
functions needed for ProcessPPS() function.

- ParseBitDepth()
- IsValidBitDepth()
- OutputAllRemainingPics()
- Flush()

Bug: b:261127809
TEST: v4l2_stateless_decoder --video=test-25fps.hevc --frames=1 --v=4 on cherry
Change-Id: I954418fef1fe6fa9291af7bf3994ec87d5a7d470
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4698114
Commit-Queue: Steve Cho <stevecho@chromium.org>
Reviewed-by: Nathan Hebert <nhebert@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1172434}
  • Loading branch information
Steve Cho authored and Chromium LUCI CQ committed Jul 19, 2023
1 parent 56ca1ff commit e5cb50a
Show file tree
Hide file tree
Showing 2 changed files with 166 additions and 2 deletions.
140 changes: 139 additions & 1 deletion media/gpu/v4l2/test/h265_decoder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,62 @@ namespace v4l2_test {

namespace {
constexpr uint32_t kDriverCodecFourcc = V4L2_PIX_FMT_HEVC_SLICE;

// Gets bit depth info from SPS
bool ParseBitDepth(const H265SPS& sps, uint8_t& bit_depth) {
// Spec 7.4.3.2.1
// See spec at http://www.itu.int/rec/T-REC-H.265
if (sps.bit_depth_y != sps.bit_depth_c) {
LOG(ERROR) << "Different bit depths among planes is not supported";
return false;
}
bit_depth = base::checked_cast<uint8_t>(sps.bit_depth_y);
return true;
}

// Checks bit depth is supported with the given HEVC profile
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:
LOG(ERROR) << "Invalid profile specified for H265";
return false;
}
}
}

H265Decoder::H265Decoder(std::unique_ptr<V4L2IoctlShim> v4l2_ioctl,
Expand Down Expand Up @@ -69,11 +125,93 @@ std::unique_ptr<H265Decoder> H265Decoder::Create(
new H265Decoder(std::move(v4l2_ioctl), coded_size.value(), stream));
}

bool H265Decoder::ProcessPPS(int pps_id, bool* need_new_buffers) {
bool H265Decoder::OutputAllRemainingPics() {
NOTIMPLEMENTED();
return false;
}

bool H265Decoder::Flush() {
VLOGF(4) << "Decoder flush";

if (!OutputAllRemainingPics()) {
return false;
}

dpb_.Clear();
prev_tid0_pic_ = nullptr;

return true;
}

bool H265Decoder::ProcessPPS(int pps_id, bool* need_new_buffers) {
VLOGF(4) << "Processing PPS id:" << pps_id;

const H265PPS* pps = parser_->GetPPS(pps_id);
DCHECK(pps);

const H265SPS* sps = parser_->GetSPS(pps->pps_seq_parameter_set_id);
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) {
VLOGF(4) << "New visible rect: " << new_visible_rect.ToString();
visible_rect_ = new_visible_rect;
}

VideoChromaSampling new_chroma_sampling = sps->GetChromaSampling();
if (new_chroma_sampling != VideoChromaSampling::k420) {
LOG(ERROR) << "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)) {
LOG(ERROR) << "Invalid bit depth=" << base::strict_cast<int>(new_bit_depth)
<< ", profile=" << GetProfileName(new_profile);
return false;
}

if (pic_size_ != new_pic_size || dpb_.MaxNumPics() != sps->max_dpb_size ||
profile_ != new_profile || bit_depth_ != new_bit_depth ||
chroma_sampling_ != new_chroma_sampling) {
CHECK(Flush()) << "Failed to flush the decoder.";

LOG(INFO) << "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_.SetMaxNumPics(sps->max_dpb_size);
if (need_new_buffers) {
*need_new_buffers = true;
}
}

return true;
}

bool H265Decoder::PreprocessCurrentSlice() {
NOTIMPLEMENTED();
return false;
Expand Down
28 changes: 27 additions & 1 deletion media/gpu/v4l2/test/h265_decoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include "media/gpu/v4l2/test/video_decoder.h"

#include "media/base/video_codecs.h"
#include "media/gpu/v4l2/test/h265_dpb.h"
#include "media/video/h265_parser.h"

Expand Down Expand Up @@ -67,7 +68,13 @@ class H265Decoder : public VideoDecoder {
kRanOutOfStreamData, // Need more stream data to proceed.
};

// Process H265 stream structures.
// Output all pictures in DPB that have not been outputted yet.
bool OutputAllRemainingPics();

// Output all pictures in DPB and clear the DPB.
bool Flush();

// Process H265 bitstream PPS (picture parameter set).
bool ProcessPPS(int pps_id, bool* need_new_buffers);

// Process current slice header to discover if we need to start a new picture,
Expand Down Expand Up @@ -95,6 +102,9 @@ class H265Decoder : public VideoDecoder {

std::unique_ptr<H265Parser> parser_;

// DPB in use.
H265DPB dpb_;

// Picture currently being processed/decoded.
scoped_refptr<H265Picture> curr_pic_;

Expand All @@ -104,6 +114,10 @@ class H265Decoder : public VideoDecoder {

const base::MemoryMappedFile& data_stream_;

// Global state values needed for decoding. See spec.
scoped_refptr<H265Picture> prev_tid0_pic_;
int max_pic_order_cnt_lsb_;

// Currently active PPS.
int curr_pps_id_ = -1;

Expand All @@ -112,6 +126,18 @@ class H265Decoder : public VideoDecoder {
std::unique_ptr<H265SliceHeader> curr_slice_hdr_;
std::unique_ptr<H265SliceHeader> last_slice_hdr_;

// Output picture size.
gfx::Size pic_size_;
// Output visible cropping rect.
gfx::Rect visible_rect_;

// Profile of input bitstream.
VideoCodecProfile profile_;
// Bit depth of input bitstream.
uint8_t bit_depth_ = 0;
// Chroma sampling format of input bitstream.
VideoChromaSampling chroma_sampling_ = VideoChromaSampling::kUnknown;

// If this is true, then the entire steam has been parsed.
bool is_stream_over_ = false;
};
Expand Down

0 comments on commit e5cb50a

Please sign in to comment.