Commit 2d854227 authored by Pin-chih Lin's avatar Pin-chih Lin Committed by Commit Bot

media: configure h264 encode level and check initial params validity

Initial config has both profile and H264 level now, we then apply the
configuration by passing to H264 encoder class for VAVEA, and setting up
v4l2 controls for V4L2VEA, rather than using fixed profile and level in
the past.

According to H264 spec, there are limitations for
framerate/bitrate/framesize combinations as the requested level, and we
may want to fail initization for excessive configuration. For VAVEA, we
add the check on H264Encoder::Initialize(). For V4L2VEA, we will let
driver judge the legality on configuration.

In video_encode_accelerator_unittest, a new property requested_h264_level
is added to test_stream to specify requested level, and ForceH264Level
test for checking output level meets expectation.

BUG=863327
BUG=865302
TEST=build, deploy Chrome, and run video_encoder_accelerator_unittest on Eve
TEST=build, deploy Chrome, and run video_encoder_accelerator_unittest on Elm

Cq-Include-Trybots: luci.chromium.try:android_optional_gpu_tests_rel;luci.chromium.try:linux_optional_gpu_tests_rel;luci.chromium.try:mac_optional_gpu_tests_rel;luci.chromium.try:win_optional_gpu_tests_rel
Change-Id: I003a4f5c1c8ef149ace3dd2ad18aebd84f60dd0c
Reviewed-on: https://chromium-review.googlesource.com/1209883
Commit-Queue: Pin-chih Lin <johnylin@chromium.org>
Reviewed-by: default avatarPawel Osciak <posciak@chromium.org>
Reviewed-by: default avatarDale Curtis <dalecurtis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#589466}
parent 41aadb51
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "base/optional.h" #include "base/optional.h"
#include "base/stl_util.h" #include "base/stl_util.h"
#include "media/gpu/h264_decoder.h" #include "media/gpu/h264_decoder.h"
#include "media/video/h264_level_limits.h"
namespace media { namespace media {
...@@ -964,42 +965,6 @@ bool H264Decoder::FinishPicture(scoped_refptr<H264Picture> pic) { ...@@ -964,42 +965,6 @@ bool H264Decoder::FinishPicture(scoped_refptr<H264Picture> pic) {
return true; return true;
} }
static int LevelToMaxDpbMbs(int level) {
// See table A-1 in spec.
switch (level) {
case 10:
return 396;
case 11:
return 900;
case 12: // fallthrough
case 13: // fallthrough
case 20:
return 2376;
case 21:
return 4752;
case 22: // fallthrough
case 30:
return 8100;
case 31:
return 18000;
case 32:
return 20480;
case 40: // fallthrough
case 41:
return 32768;
case 42:
return 34816;
case 50:
return 110400;
case 51: // fallthrough
case 52:
return 184320;
default:
DVLOG(1) << "Invalid codec level (" << level << ")";
return 0;
}
}
bool H264Decoder::UpdateMaxNumReorderFrames(const H264SPS* sps) { bool H264Decoder::UpdateMaxNumReorderFrames(const H264SPS* sps) {
if (sps->vui_parameters_present_flag && sps->bitstream_restriction_flag) { if (sps->vui_parameters_present_flag && sps->bitstream_restriction_flag) {
max_num_reorder_frames_ = max_num_reorder_frames_ =
...@@ -1066,8 +1031,17 @@ bool H264Decoder::ProcessSPS(int sps_id, bool* need_new_buffers) { ...@@ -1066,8 +1031,17 @@ bool H264Decoder::ProcessSPS(int sps_id, bool* need_new_buffers) {
return false; return false;
} }
int level = sps->level_idc; // Spec A.3.1 and A.3.2
int max_dpb_mbs = LevelToMaxDpbMbs(level); // For Baseline, Constrained Baseline and Main profile, the indicated level is
// Level 1b if level_idc is equal to 11 and constraint_set3_flag is equal to 1
uint8_t level = base::checked_cast<uint8_t>(sps->level_idc);
if ((sps->profile_idc == H264SPS::kProfileIDCBaseline ||
sps->profile_idc == H264SPS::kProfileIDCConstrainedBaseline ||
sps->profile_idc == H264SPS::kProfileIDCMain) &&
level == 11 && sps->constraint_set3_flag) {
level = 9; // Level 1b
}
int max_dpb_mbs = base::checked_cast<int>(H264LevelToMaxDpbMbs(level));
if (max_dpb_mbs == 0) if (max_dpb_mbs == 0)
return false; return false;
......
...@@ -225,6 +225,79 @@ uint32_t V4L2Device::V4L2PixFmtToDrmFormat(uint32_t format) { ...@@ -225,6 +225,79 @@ uint32_t V4L2Device::V4L2PixFmtToDrmFormat(uint32_t format) {
} }
} }
// static
int32_t V4L2Device::VideoCodecProfileToV4L2H264Profile(
VideoCodecProfile profile) {
switch (profile) {
case H264PROFILE_BASELINE:
return V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE;
case H264PROFILE_MAIN:
return V4L2_MPEG_VIDEO_H264_PROFILE_MAIN;
case H264PROFILE_EXTENDED:
return V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED;
case H264PROFILE_HIGH:
return V4L2_MPEG_VIDEO_H264_PROFILE_HIGH;
case H264PROFILE_HIGH10PROFILE:
return V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_10;
case H264PROFILE_HIGH422PROFILE:
return V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_422;
case H264PROFILE_HIGH444PREDICTIVEPROFILE:
return V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_444_PREDICTIVE;
case H264PROFILE_SCALABLEBASELINE:
return V4L2_MPEG_VIDEO_H264_PROFILE_SCALABLE_BASELINE;
case H264PROFILE_SCALABLEHIGH:
return V4L2_MPEG_VIDEO_H264_PROFILE_SCALABLE_HIGH;
case H264PROFILE_STEREOHIGH:
return V4L2_MPEG_VIDEO_H264_PROFILE_STEREO_HIGH;
case H264PROFILE_MULTIVIEWHIGH:
return V4L2_MPEG_VIDEO_H264_PROFILE_MULTIVIEW_HIGH;
default:
DVLOGF(1) << "Add more cases as needed";
return -1;
}
}
// static
int32_t V4L2Device::H264LevelIdcToV4L2H264Level(uint8_t level_idc) {
switch (level_idc) {
case 10:
return V4L2_MPEG_VIDEO_H264_LEVEL_1_0;
case 9:
return V4L2_MPEG_VIDEO_H264_LEVEL_1B;
case 11:
return V4L2_MPEG_VIDEO_H264_LEVEL_1_1;
case 12:
return V4L2_MPEG_VIDEO_H264_LEVEL_1_2;
case 13:
return V4L2_MPEG_VIDEO_H264_LEVEL_1_3;
case 20:
return V4L2_MPEG_VIDEO_H264_LEVEL_2_0;
case 21:
return V4L2_MPEG_VIDEO_H264_LEVEL_2_1;
case 22:
return V4L2_MPEG_VIDEO_H264_LEVEL_2_2;
case 30:
return V4L2_MPEG_VIDEO_H264_LEVEL_3_0;
case 31:
return V4L2_MPEG_VIDEO_H264_LEVEL_3_1;
case 32:
return V4L2_MPEG_VIDEO_H264_LEVEL_3_2;
case 40:
return V4L2_MPEG_VIDEO_H264_LEVEL_4_0;
case 41:
return V4L2_MPEG_VIDEO_H264_LEVEL_4_1;
case 42:
return V4L2_MPEG_VIDEO_H264_LEVEL_4_2;
case 50:
return V4L2_MPEG_VIDEO_H264_LEVEL_5_0;
case 51:
return V4L2_MPEG_VIDEO_H264_LEVEL_5_1;
default:
DVLOGF(1) << "Unrecognized level_idc: " << static_cast<int>(level_idc);
return -1;
}
}
// static // static
gfx::Size V4L2Device::CodedSizeFromV4L2Format(struct v4l2_format format) { gfx::Size V4L2Device::CodedSizeFromV4L2Format(struct v4l2_format format) {
gfx::Size coded_size; gfx::Size coded_size;
......
...@@ -49,6 +49,10 @@ class MEDIA_GPU_EXPORT V4L2Device ...@@ -49,6 +49,10 @@ class MEDIA_GPU_EXPORT V4L2Device
// Convert format requirements requested by a V4L2 device to gfx::Size. // Convert format requirements requested by a V4L2 device to gfx::Size.
static gfx::Size CodedSizeFromV4L2Format(struct v4l2_format format); static gfx::Size CodedSizeFromV4L2Format(struct v4l2_format format);
// Convert required H264 profile and level to V4L2 enums.
static int32_t VideoCodecProfileToV4L2H264Profile(VideoCodecProfile profile);
static int32_t H264LevelIdcToV4L2H264Level(uint8_t level_idc);
enum class Type { enum class Type {
kDecoder, kDecoder,
kEncoder, kEncoder,
......
...@@ -244,10 +244,7 @@ bool V4L2VideoEncodeAccelerator::Initialize(const Config& config, ...@@ -244,10 +244,7 @@ bool V4L2VideoEncodeAccelerator::Initialize(const Config& config,
free_image_processor_output_buffers_.push_back(i); free_image_processor_output_buffers_.push_back(i);
} }
// TODO(johnylin): pass |config.h264_output_level| to InitControl() for if (!InitControls(config))
// updating the correlative V4L2_CID_MPEG_VIDEO_H264_LEVEL
// ctrl value. https://crbug.com/863327
if (!InitControls())
return false; return false;
if (!CreateOutputBuffers()) if (!CreateOutputBuffers())
...@@ -259,8 +256,8 @@ bool V4L2VideoEncodeAccelerator::Initialize(const Config& config, ...@@ -259,8 +256,8 @@ bool V4L2VideoEncodeAccelerator::Initialize(const Config& config,
} }
RequestEncodingParametersChange( RequestEncodingParametersChange(
config.initial_bitrate, config.initial_bitrate, config.initial_framerate.value_or(
config.initial_framerate.value_or(kDefaultFramerate)); VideoEncodeAccelerator::kDefaultFramerate));
encoder_state_ = kInitialized; encoder_state_ = kInitialized;
...@@ -1205,7 +1202,7 @@ bool V4L2VideoEncodeAccelerator::SetExtCtrls( ...@@ -1205,7 +1202,7 @@ bool V4L2VideoEncodeAccelerator::SetExtCtrls(
return device_->Ioctl(VIDIOC_S_EXT_CTRLS, &ext_ctrls) == 0; return device_->Ioctl(VIDIOC_S_EXT_CTRLS, &ext_ctrls) == 0;
} }
bool V4L2VideoEncodeAccelerator::InitControls() { bool V4L2VideoEncodeAccelerator::InitControls(const Config& config) {
std::vector<struct v4l2_ext_control> ctrls; std::vector<struct v4l2_ext_control> ctrls;
struct v4l2_ext_control ctrl; struct v4l2_ext_control ctrl;
...@@ -1257,10 +1254,29 @@ bool V4L2VideoEncodeAccelerator::InitControls() { ...@@ -1257,10 +1254,29 @@ bool V4L2VideoEncodeAccelerator::InitControls() {
ctrl.value = 51; ctrl.value = 51;
ctrls.push_back(ctrl); ctrls.push_back(ctrl);
// Use H.264 level 4.0 to match the supported max resolution. // Set H.264 profile.
int32_t profile_value =
V4L2Device::VideoCodecProfileToV4L2H264Profile(config.output_profile);
if (profile_value < 0) {
NOTIFY_ERROR(kInvalidArgumentError);
return false;
}
memset(&ctrl, 0, sizeof(ctrl));
ctrl.id = V4L2_CID_MPEG_VIDEO_H264_PROFILE;
ctrl.value = profile_value;
ctrls.push_back(ctrl);
// Set H.264 output level from config. Use Level 4.0 as fallback default.
int32_t level_value = V4L2Device::H264LevelIdcToV4L2H264Level(
config.h264_output_level.value_or(
VideoEncodeAccelerator::kDefaultH264Level));
if (level_value < 0) {
NOTIFY_ERROR(kInvalidArgumentError);
return false;
}
memset(&ctrl, 0, sizeof(ctrl)); memset(&ctrl, 0, sizeof(ctrl));
ctrl.id = V4L2_CID_MPEG_VIDEO_H264_LEVEL; ctrl.id = V4L2_CID_MPEG_VIDEO_H264_LEVEL;
ctrl.value = V4L2_MPEG_VIDEO_H264_LEVEL_4_0; ctrl.value = level_value;
ctrls.push_back(ctrl); ctrls.push_back(ctrl);
// Ask not to put SPS and PPS into separate bitstream buffers. // Ask not to put SPS and PPS into separate bitstream buffers.
......
...@@ -92,7 +92,6 @@ class MEDIA_GPU_EXPORT V4L2VideoEncodeAccelerator ...@@ -92,7 +92,6 @@ class MEDIA_GPU_EXPORT V4L2VideoEncodeAccelerator
}; };
enum { enum {
kDefaultFramerate = 30,
// These are rather subjectively tuned. // These are rather subjectively tuned.
kInputBufferCount = 2, kInputBufferCount = 2,
kOutputBufferCount = 2, kOutputBufferCount = 2,
...@@ -195,8 +194,8 @@ class MEDIA_GPU_EXPORT V4L2VideoEncodeAccelerator ...@@ -195,8 +194,8 @@ class MEDIA_GPU_EXPORT V4L2VideoEncodeAccelerator
// Set up the device to the output format requested in Initialize(). // Set up the device to the output format requested in Initialize().
bool SetOutputFormat(VideoCodecProfile output_profile); bool SetOutputFormat(VideoCodecProfile output_profile);
// Initialize device controls with default values. // Initialize device controls with |config| or default values.
bool InitControls(); bool InitControls(const Config& config);
// Create the buffers we need. // Create the buffers we need.
bool CreateInputBuffers(); bool CreateInputBuffers();
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include "media/base/video_bitrate_allocation.h" #include "media/base/video_bitrate_allocation.h"
#include "media/base/video_codecs.h" #include "media/base/video_codecs.h"
#include "media/gpu/codec_picture.h" #include "media/gpu/codec_picture.h"
#include "media/video/video_encode_accelerator.h"
#include "ui/gfx/geometry/size.h" #include "ui/gfx/geometry/size.h"
namespace media { namespace media {
...@@ -116,14 +117,10 @@ class AcceleratedVideoEncoder { ...@@ -116,14 +117,10 @@ class AcceleratedVideoEncoder {
DISALLOW_COPY_AND_ASSIGN(EncodeJob); DISALLOW_COPY_AND_ASSIGN(EncodeJob);
}; };
// Initializes the encoder to encode frames of |visible_size| into a stream // Initializes the encoder with requested parameter set |config|.
// for |profile|, at |initial_bitrate| and |initial_framerate|.
// Returns false if the requested set of parameters is not supported, // Returns false if the requested set of parameters is not supported,
// true on success. // true on success.
virtual bool Initialize(const gfx::Size& visible_size, virtual bool Initialize(const VideoEncodeAccelerator::Config& config) = 0;
VideoCodecProfile profile,
uint32_t initial_bitrate,
uint32_t initial_framerate) = 0;
// Updates current framerate and/or bitrate to |framerate| in FPS // Updates current framerate and/or bitrate to |framerate| in FPS
// and the specified video bitrate allocation. // and the specified video bitrate allocation.
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include "base/bits.h" #include "base/bits.h"
#include "base/stl_util.h" #include "base/stl_util.h"
#include "media/video/h264_level_limits.h"
#define DVLOGF(level) DVLOG(level) << __func__ << "(): " #define DVLOGF(level) DVLOG(level) << __func__ << "(): "
...@@ -31,9 +32,6 @@ constexpr size_t kMaxRefIdxL1Size = 0; ...@@ -31,9 +32,6 @@ constexpr size_t kMaxRefIdxL1Size = 0;
constexpr int kBitRateScale = 0; // bit_rate_scale for SPS HRD parameters. constexpr int kBitRateScale = 0; // bit_rate_scale for SPS HRD parameters.
constexpr int kCPBSizeScale = 0; // cpb_size_scale for SPS HRD parameters. constexpr int kCPBSizeScale = 0; // cpb_size_scale for SPS HRD parameters.
// Default to H264 profile 4.1.
constexpr int kDefaultLevelIDC = 41;
// 4:2:0 // 4:2:0
constexpr int kChromaFormatIDC = 1; constexpr int kChromaFormatIDC = 1;
} // namespace } // namespace
...@@ -59,27 +57,30 @@ H264Encoder::~H264Encoder() { ...@@ -59,27 +57,30 @@ H264Encoder::~H264Encoder() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
} }
bool H264Encoder::Initialize(const gfx::Size& visible_size, bool H264Encoder::Initialize(const VideoEncodeAccelerator::Config& config) {
VideoCodecProfile profile,
uint32_t initial_bitrate,
uint32_t initial_framerate) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
switch (profile) { switch (config.output_profile) {
case H264PROFILE_BASELINE: case H264PROFILE_BASELINE:
case H264PROFILE_MAIN: case H264PROFILE_MAIN:
case H264PROFILE_HIGH: case H264PROFILE_HIGH:
break; break;
default: default:
NOTIMPLEMENTED() << "Unsupported profile " << GetProfileName(profile); NOTIMPLEMENTED() << "Unsupported profile "
<< GetProfileName(config.output_profile);
return false; return false;
} }
DCHECK(!visible_size.IsEmpty()); if (config.input_visible_size.IsEmpty()) {
visible_size_ = visible_size; DVLOGF(1) << "Input visible size could not be empty";
return false;
}
visible_size_ = config.input_visible_size;
// For 4:2:0, the pixel sizes have to be even. // For 4:2:0, the pixel sizes have to be even.
DCHECK_EQ(visible_size_.width() % 2, 0); if ((visible_size_.width() % 2 != 0) || (visible_size_.height() % 2 != 0)) {
DCHECK_EQ(visible_size_.height() % 2, 0); DVLOGF(1) << "The pixel sizes are not even: " << visible_size_.ToString();
return false;
}
constexpr size_t kH264MacroblockSizeInPixels = 16; constexpr size_t kH264MacroblockSizeInPixels = 16;
coded_size_ = gfx::Size( coded_size_ = gfx::Size(
base::bits::Align(visible_size_.width(), kH264MacroblockSizeInPixels), base::bits::Align(visible_size_.width(), kH264MacroblockSizeInPixels),
...@@ -87,9 +88,17 @@ bool H264Encoder::Initialize(const gfx::Size& visible_size, ...@@ -87,9 +88,17 @@ bool H264Encoder::Initialize(const gfx::Size& visible_size,
mb_width_ = coded_size_.width() / kH264MacroblockSizeInPixels; mb_width_ = coded_size_.width() / kH264MacroblockSizeInPixels;
mb_height_ = coded_size_.height() / kH264MacroblockSizeInPixels; mb_height_ = coded_size_.height() / kH264MacroblockSizeInPixels;
profile_ = profile; profile_ = config.output_profile;
level_ = config.h264_output_level.value_or(
VideoEncodeAccelerator::kDefaultH264Level);
uint32_t initial_framerate = config.initial_framerate.value_or(
VideoEncodeAccelerator::kDefaultFramerate);
if (!CheckH264LevelLimits(profile_, level_, config.initial_bitrate,
initial_framerate, mb_width_ * mb_height_))
return false;
VideoBitrateAllocation initial_bitrate_allocation; VideoBitrateAllocation initial_bitrate_allocation;
initial_bitrate_allocation.SetBitrate(0, 0, initial_bitrate); initial_bitrate_allocation.SetBitrate(0, 0, config.initial_bitrate);
if (!UpdateRates(initial_bitrate_allocation, initial_framerate)) if (!UpdateRates(initial_bitrate_allocation, initial_framerate))
return false; return false;
...@@ -234,7 +243,10 @@ void H264Encoder::UpdateSPS() { ...@@ -234,7 +243,10 @@ void H264Encoder::UpdateSPS() {
return; return;
} }
current_sps_.level_idc = kDefaultLevelIDC; H264SPS::GetLevelConfigFromProfileLevel(profile_, level_,
&current_sps_.level_idc,
&current_sps_.constraint_set3_flag);
current_sps_.seq_parameter_set_id = 0; current_sps_.seq_parameter_set_id = 0;
current_sps_.chroma_format_idc = kChromaFormatIDC; current_sps_.chroma_format_idc = kChromaFormatIDC;
......
...@@ -95,10 +95,7 @@ class H264Encoder : public AcceleratedVideoEncoder { ...@@ -95,10 +95,7 @@ class H264Encoder : public AcceleratedVideoEncoder {
~H264Encoder() override; ~H264Encoder() override;
// AcceleratedVideoEncoder implementation. // AcceleratedVideoEncoder implementation.
bool Initialize(const gfx::Size& visible_size, bool Initialize(const VideoEncodeAccelerator::Config& config) override;
VideoCodecProfile profile,
uint32_t initial_bitrate,
uint32_t initial_framerate) override;
bool UpdateRates(const VideoBitrateAllocation& bitrate_allocation, bool UpdateRates(const VideoBitrateAllocation& bitrate_allocation,
uint32_t framerate) override; uint32_t framerate) override;
gfx::Size GetCodedSize() const override; gfx::Size GetCodedSize() const override;
...@@ -115,6 +112,10 @@ class H264Encoder : public AcceleratedVideoEncoder { ...@@ -115,6 +112,10 @@ class H264Encoder : public AcceleratedVideoEncoder {
void GeneratePackedSPS(); void GeneratePackedSPS();
void GeneratePackedPPS(); void GeneratePackedPPS();
// Check if |bitrate| and |framerate| and current coded size are supported by
// current profile and level.
bool CheckConfigValidity(uint32_t bitrate, uint32_t framerate);
// Current SPS, PPS and their packed versions. Packed versions are NALUs // Current SPS, PPS and their packed versions. Packed versions are NALUs
// in AnnexB format *without* emulation prevention three-byte sequences // in AnnexB format *without* emulation prevention three-byte sequences
// (those are expected to be added by the client as needed). // (those are expected to be added by the client as needed).
...@@ -129,6 +130,9 @@ class H264Encoder : public AcceleratedVideoEncoder { ...@@ -129,6 +130,9 @@ class H264Encoder : public AcceleratedVideoEncoder {
// H264 profile currently used. // H264 profile currently used.
media::VideoCodecProfile profile_ = VIDEO_CODEC_PROFILE_UNKNOWN; media::VideoCodecProfile profile_ = VIDEO_CODEC_PROFILE_UNKNOWN;
// H264 level currently used.
uint8_t level_ = 0;
// Current visible and coded sizes in pixels. // Current visible and coded sizes in pixels.
gfx::Size visible_size_; gfx::Size visible_size_;
gfx::Size coded_size_; gfx::Size coded_size_;
......
...@@ -55,8 +55,6 @@ constexpr size_t kMinNumFramesInFlight = 4; ...@@ -55,8 +55,6 @@ constexpr size_t kMinNumFramesInFlight = 4;
// reconstructed picture, which is later used for reference. // reconstructed picture, which is later used for reference.
constexpr size_t kNumSurfacesPerFrame = 2; constexpr size_t kNumSurfacesPerFrame = 2;
constexpr int kDefaultFramerate = 30;
// Percentage of bitrate set to be targeted by the HW encoder. // Percentage of bitrate set to be targeted by the HW encoder.
constexpr unsigned int kTargetBitratePercentage = 90; constexpr unsigned int kTargetBitratePercentage = 90;
...@@ -293,12 +291,7 @@ void VaapiVideoEncodeAccelerator::InitializeTask(const Config& config) { ...@@ -293,12 +291,7 @@ void VaapiVideoEncodeAccelerator::InitializeTask(const Config& config) {
return; return;
} }
// TODO(johnylin): pass |config.h264_output_level| to H264Encoder. if (!encoder_->Initialize(config)) {
// https://crbug.com/863327
if (!encoder_->Initialize(
config.input_visible_size, config.output_profile,
config.initial_bitrate,
config.initial_framerate.value_or(kDefaultFramerate))) {
NOTIFY_ERROR(kInvalidArgumentError, "Failed initializing encoder"); NOTIFY_ERROR(kInvalidArgumentError, "Failed initializing encoder");
return; return;
} }
......
...@@ -48,27 +48,36 @@ VP8Encoder::~VP8Encoder() { ...@@ -48,27 +48,36 @@ VP8Encoder::~VP8Encoder() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
} }
bool VP8Encoder::Initialize(const gfx::Size& visible_size, bool VP8Encoder::Initialize(const VideoEncodeAccelerator::Config& config) {
VideoCodecProfile profile,
uint32_t initial_bitrate,
uint32_t initial_framerate) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(profile >= VP8PROFILE_MIN && profile <= VP8PROFILE_MAX); if (VideoCodecProfileToVideoCodec(config.output_profile) != kCodecVP8) {
DVLOGF(1) << "Invalid profile: " << GetProfileName(config.output_profile);
return false;
}
DCHECK(!visible_size.IsEmpty()); if (config.input_visible_size.IsEmpty()) {
DVLOGF(1) << "Input visible size could not be empty";
return false;
}
// 4:2:0 format has to be 2-aligned. // 4:2:0 format has to be 2-aligned.
DCHECK_EQ(visible_size.width() % 2, 0); if ((config.input_visible_size.width() % 2 != 0) ||
DCHECK_EQ(visible_size.height() % 2, 0); (config.input_visible_size.height() % 2 != 0)) {
DVLOGF(1) << "The pixel sizes are not even: "
<< config.input_visible_size.ToString();
return false;
}
visible_size_ = visible_size; visible_size_ = config.input_visible_size;
coded_size_ = gfx::Size(base::bits::Align(visible_size_.width(), 16), coded_size_ = gfx::Size(base::bits::Align(visible_size_.width(), 16),
base::bits::Align(visible_size_.height(), 16)); base::bits::Align(visible_size_.height(), 16));
Reset(); Reset();
VideoBitrateAllocation initial_bitrate_allocation; VideoBitrateAllocation initial_bitrate_allocation;
initial_bitrate_allocation.SetBitrate(0, 0, initial_bitrate); initial_bitrate_allocation.SetBitrate(0, 0, config.initial_bitrate);
return UpdateRates(initial_bitrate_allocation, initial_framerate); return UpdateRates(initial_bitrate_allocation,
config.initial_framerate.value_or(
VideoEncodeAccelerator::kDefaultFramerate));
} }
gfx::Size VP8Encoder::GetCodedSize() const { gfx::Size VP8Encoder::GetCodedSize() const {
......
...@@ -72,10 +72,7 @@ class VP8Encoder : public AcceleratedVideoEncoder { ...@@ -72,10 +72,7 @@ class VP8Encoder : public AcceleratedVideoEncoder {
~VP8Encoder() override; ~VP8Encoder() override;
// AcceleratedVideoEncoder implementation. // AcceleratedVideoEncoder implementation.
bool Initialize(const gfx::Size& visible_size, bool Initialize(const VideoEncodeAccelerator::Config& config) override;
VideoCodecProfile profile,
uint32_t initial_bitrate,
uint32_t initial_framerate) override;
bool UpdateRates(const VideoBitrateAllocation& bitrate_allocation, bool UpdateRates(const VideoBitrateAllocation& bitrate_allocation,
uint32_t framerate) override; uint32_t framerate) override;
gfx::Size GetCodedSize() const override; gfx::Size GetCodedSize() const override;
......
...@@ -21,6 +21,8 @@ source_set("video") { ...@@ -21,6 +21,8 @@ source_set("video") {
"gpu_video_accelerator_factories.h", "gpu_video_accelerator_factories.h",
"h264_bit_reader.cc", "h264_bit_reader.cc",
"h264_bit_reader.h", "h264_bit_reader.h",
"h264_level_limits.cc",
"h264_level_limits.h",
"h264_parser.cc", "h264_parser.cc",
"h264_parser.h", "h264_parser.h",
"h264_poc.cc", "h264_poc.cc",
......
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "media/video/h264_level_limits.h"
#include "base/logging.h"
#include "media/video/h264_parser.h"
namespace media {
namespace {
struct LevelLimits {
// All names and abbreviations are as in table A-1 in spec.
// MaxMBPS, Max. macroblock processing rate (MB/s)
uint32_t max_mbps;
// MaxFS, Max. frame size (MBs)
uint32_t max_fs;
// MaxDpbMbs, Max. decoded picture buffer size (MBs)
uint32_t max_dpb_mbs;
// MaxBR, Max. video bitrate for Baseline and Main Profiles (kbit/s)
uint32_t max_main_br;
};
LevelLimits LevelToLevelLimits(uint8_t level) {
// See table A-1 in spec
// { MaxMBPS, MaxFS, MaxDpbMbs, MaxBR}
switch (level) {
case H264SPS::kLevelIDC1p0:
return {1485, 99, 396, 64}; // Level 1.0
case H264SPS::kLevelIDC1B:
return {1485, 99, 396, 128}; // Level 1b
case H264SPS::kLevelIDC1p1:
return {3000, 396, 900, 192}; // Level 1.1
case H264SPS::kLevelIDC1p2:
return {6000, 396, 2376, 384}; // Level 1.2
case H264SPS::kLevelIDC1p3:
return {11800, 396, 2376, 768}; // Level 1.3
case H264SPS::kLevelIDC2p0:
return {11880, 396, 2376, 2000}; // Level 2.0
case H264SPS::kLevelIDC2p1:
return {19800, 792, 4752, 4000}; // Level 2.1
case H264SPS::kLevelIDC2p2:
return {20250, 1620, 8100, 4000}; // Level 2.2
case H264SPS::kLevelIDC3p0:
return {40500, 1620, 8100, 10000}; // Level 3.0
case H264SPS::kLevelIDC3p1:
return {108000, 3600, 18000, 14000}; // Level 3.1
case H264SPS::kLevelIDC3p2:
return {216000, 5120, 20480, 20000}; // Level 3.2
case H264SPS::kLevelIDC4p0:
return {245760, 8192, 32768, 20000}; // Level 4.0
case H264SPS::kLevelIDC4p1:
return {245760, 8192, 32768, 50000}; // Level 4.1
case H264SPS::kLevelIDC4p2:
return {522240, 8704, 34816, 50000}; // Level 4.2
case H264SPS::kLevelIDC5p0:
return {589824, 22080, 110400, 135000}; // Level 5.0
case H264SPS::kLevelIDC5p1:
return {983040, 36864, 184320, 240000}; // Level 5.1
case H264SPS::kLevelIDC5p2:
return {2073600, 36864, 184320, 240000}; // Level 5.2
case H264SPS::kLevelIDC6p0:
return {4177920, 139264, 696320, 240000}; // Level 6.0
case H264SPS::kLevelIDC6p1:
return {8355840, 139264, 696320, 480000}; // Level 6.1
case H264SPS::kLevelIDC6p2:
return {16711680, 139264, 696320, 800000}; // Level 6.2
default:
DVLOG(1) << "Invalid codec level (" << static_cast<int>(level) << ")";
return {0, 0, 0, 0};
}
}
} // namespace
uint32_t H264LevelToMaxMBPS(uint8_t level) {
return LevelToLevelLimits(level).max_mbps;
}
uint32_t H264LevelToMaxFS(uint8_t level) {
return LevelToLevelLimits(level).max_fs;
}
uint32_t H264LevelToMaxDpbMbs(uint8_t level) {
return LevelToLevelLimits(level).max_dpb_mbs;
}
uint32_t H264ProfileLevelToMaxBR(VideoCodecProfile profile, uint8_t level) {
uint32_t max_main_br = LevelToLevelLimits(level).max_main_br;
// See table A-2 in spec
// The maximum bit rate for High Profile is 1.25 times that of the
// Base/Extended/Main Profiles, 3 times for Hi10P, and 4 times for
// Hi422P/Hi444PP.
switch (profile) {
case H264PROFILE_BASELINE:
case H264PROFILE_MAIN:
case H264PROFILE_EXTENDED:
return max_main_br;
case H264PROFILE_HIGH:
return max_main_br * 5 / 4;
case H264PROFILE_HIGH10PROFILE:
return max_main_br * 3;
case H264PROFILE_HIGH422PROFILE:
case H264PROFILE_HIGH444PREDICTIVEPROFILE:
return max_main_br * 4;
default:
DVLOG(1) << "Failed to query MaxBR for profile: "
<< GetProfileName(profile);
return 0;
}
}
bool CheckH264LevelLimits(VideoCodecProfile profile,
uint8_t level,
uint32_t bitrate,
uint32_t framerate,
uint32_t framesize_in_mbs) {
uint32_t max_bitrate_kbs = H264ProfileLevelToMaxBR(profile, level);
DCHECK(base::IsValueInRangeForNumericType<uint32_t>(max_bitrate_kbs * 1000));
uint32_t max_bitrate = max_bitrate_kbs * 1000;
if (bitrate > max_bitrate) {
DVLOG(1) << "Target bitrate: " << bitrate << " exceeds Max: " << max_bitrate
<< " bit/s";
return false;
}
if (framesize_in_mbs > H264LevelToMaxFS(level)) {
DVLOG(1) << "Target frame size: " << framesize_in_mbs
<< " exceeds Max: " << H264LevelToMaxFS(level) << " Macroblocks";
return false;
}
uint32_t mbps = framesize_in_mbs * framerate;
if (mbps > H264LevelToMaxMBPS(level)) {
DVLOG(1) << "Target macroblock processing rate: " << mbps
<< " exceeds Max: " << H264LevelToMaxMBPS(level) << "Macroblock/s";
return false;
}
return true;
}
} // namespace media
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef MEDIA_VIDEO_H264_LEVEL_LIMITS_H_
#define MEDIA_VIDEO_H264_LEVEL_LIMITS_H_
#include <stddef.h>
#include "media/base/media_export.h"
#include "media/base/video_codecs.h"
namespace media {
// Get max macroblock processing rate in macroblocks per second (MaxMBPS) from
// level. The abbreviation is as per spec table A-1.
uint32_t MEDIA_EXPORT H264LevelToMaxMBPS(uint8_t level);
// Get max frame size in macroblocks (MaxFS) from level. The abbreviation is as
// per spec table A-1.
uint32_t MEDIA_EXPORT H264LevelToMaxFS(uint8_t level);
// Get max decoded picture buffer size in macroblocks (MaxDpbMbs) from level.
// The abbreviation is as per spec table A-1.
uint32_t MEDIA_EXPORT H264LevelToMaxDpbMbs(uint8_t level);
// Get max video bitrate in kbit per second (MaxBR) from profile and level. The
// abbreviation is as per spec table A-1.
uint32_t MEDIA_EXPORT H264ProfileLevelToMaxBR(VideoCodecProfile profile,
uint8_t level);
// Check whether |bitrate|, |framerate|, and |framesize_in_mbs| are valid from
// the limits of |profile| and |level| from Table A-1 in spec.
bool MEDIA_EXPORT CheckH264LevelLimits(VideoCodecProfile profile,
uint8_t level,
uint32_t bitrate,
uint32_t framerate,
uint32_t framesize_in_mbs);
} // namespace media
#endif // MEDIA_VIDEO_H264_LEVEL_LIMITS_H_
...@@ -79,6 +79,27 @@ H264NALU::H264NALU() { ...@@ -79,6 +79,27 @@ H264NALU::H264NALU() {
memset(this, 0, sizeof(*this)); memset(this, 0, sizeof(*this));
} }
// static
void H264SPS::GetLevelConfigFromProfileLevel(VideoCodecProfile profile,
uint8_t level,
int* level_idc,
bool* constraint_set3_flag) {
// Spec A.3.1.
// Note: we always use h264_output_level = 9 to indicate Level 1b in
// VideoEncodeAccelerator::Config, in order to tell apart from Level 1.1
// which level IDC is also 11.
// For Baseline and Main profile, if requested level is Level 1b, set
// level_idc to 11 and constraint_set3_flag to true. Otherwise, set level_idc
// to 9 for Level 1b, and ten times level number for others.
if ((profile == H264PROFILE_BASELINE || profile == H264PROFILE_MAIN) &&
level == kLevelIDC1B) {
*level_idc = 11;
*constraint_set3_flag = true;
} else {
*level_idc = level;
}
}
H264SPS::H264SPS() { H264SPS::H264SPS() {
memset(this, 0, sizeof(*this)); memset(this, 0, sizeof(*this));
} }
...@@ -173,6 +194,33 @@ VideoColorSpace H264SPS::GetColorSpace() const { ...@@ -173,6 +194,33 @@ VideoColorSpace H264SPS::GetColorSpace() const {
} }
} }
uint8_t H264SPS::GetIndicatedLevel() const {
// Spec A.3.1 and A.3.2
// For Baseline, Constrained Baseline and Main profile, the indicated level is
// Level 1b if level_idc is equal to 11 and constraint_set3_flag is true.
if ((profile_idc == H264SPS::kProfileIDCBaseline ||
profile_idc == H264SPS::kProfileIDCConstrainedBaseline ||
profile_idc == H264SPS::kProfileIDCMain) &&
level_idc == 11 && constraint_set3_flag) {
return kLevelIDC1B; // Level 1b
}
// Otherwise, the level_idc is equal to 9 for Level 1b, and others are equal
// to values of ten times the level numbers.
return base::checked_cast<uint8_t>(level_idc);
}
bool H264SPS::CheckIndicatedLevelWithinTarget(uint8_t target_level) const {
// See table A-1 in spec.
// Level 1.0 < 1b < 1.1 < 1.2 .... (in numeric order).
uint8_t level = GetIndicatedLevel();
if (target_level == kLevelIDC1p0)
return level == kLevelIDC1p0;
if (target_level == kLevelIDC1B)
return level == kLevelIDC1p0 || level == kLevelIDC1B;
return level <= target_level;
}
H264PPS::H264PPS() { H264PPS::H264PPS() {
memset(this, 0, sizeof(*this)); memset(this, 0, sizeof(*this));
} }
......
...@@ -92,6 +92,29 @@ struct MEDIA_EXPORT H264SPS { ...@@ -92,6 +92,29 @@ struct MEDIA_EXPORT H264SPS {
kProfileIDHigh444Predictive = 244, kProfileIDHigh444Predictive = 244,
}; };
enum H264LevelIDC : uint8_t {
kLevelIDC1p0 = 10,
kLevelIDC1B = 9,
kLevelIDC1p1 = 11,
kLevelIDC1p2 = 12,
kLevelIDC1p3 = 13,
kLevelIDC2p0 = 20,
kLevelIDC2p1 = 21,
kLevelIDC2p2 = 22,
kLevelIDC3p0 = 30,
kLevelIDC3p1 = 31,
kLevelIDC3p2 = 32,
kLevelIDC4p0 = 40,
kLevelIDC4p1 = 41,
kLevelIDC4p2 = 42,
kLevelIDC5p0 = 50,
kLevelIDC5p1 = 51,
kLevelIDC5p2 = 52,
kLevelIDC6p0 = 60,
kLevelIDC6p1 = 61,
kLevelIDC6p2 = 62,
};
enum AspectRatioIdc { enum AspectRatioIdc {
kExtendedSar = 255, kExtendedSar = 255,
}; };
...@@ -183,12 +206,27 @@ struct MEDIA_EXPORT H264SPS { ...@@ -183,12 +206,27 @@ struct MEDIA_EXPORT H264SPS {
int chroma_array_type; int chroma_array_type;
// Get corresponding SPS |level_idc| and |constraint_set3_flag| value from
// requested |profile| and |level| (see Spec A.3.1).
static void GetLevelConfigFromProfileLevel(VideoCodecProfile profile,
uint8_t level,
int* level_idc,
bool* constraint_set3_flag);
// Helpers to compute frequently-used values. These methods return // Helpers to compute frequently-used values. These methods return
// base::nullopt if they encounter integer overflow. They do not verify that // base::nullopt if they encounter integer overflow. They do not verify that
// the results are in-spec for the given profile or level. // the results are in-spec for the given profile or level.
base::Optional<gfx::Size> GetCodedSize() const; base::Optional<gfx::Size> GetCodedSize() const;
base::Optional<gfx::Rect> GetVisibleRect() const; base::Optional<gfx::Rect> GetVisibleRect() const;
VideoColorSpace GetColorSpace() const; VideoColorSpace GetColorSpace() const;
// Helper to compute indicated level from parsed SPS data. The value of
// indicated level would be included in H264LevelIDC enum representing the
// level as in name.
uint8_t GetIndicatedLevel() const;
// Helper to check if indicated level is lower than or equal to
// |target_level|.
bool CheckIndicatedLevelWithinTarget(uint8_t target_level) const;
}; };
struct MEDIA_EXPORT H264PPS { struct MEDIA_EXPORT H264PPS {
......
...@@ -47,8 +47,10 @@ VideoEncodeAccelerator::Config::Config( ...@@ -47,8 +47,10 @@ VideoEncodeAccelerator::Config::Config(
input_visible_size(input_visible_size), input_visible_size(input_visible_size),
output_profile(output_profile), output_profile(output_profile),
initial_bitrate(initial_bitrate), initial_bitrate(initial_bitrate),
initial_framerate(initial_framerate), initial_framerate(initial_framerate.value_or(
h264_output_level(h264_output_level), VideoEncodeAccelerator::kDefaultFramerate)),
h264_output_level(h264_output_level.value_or(
VideoEncodeAccelerator::kDefaultH264Level)),
content_type(content_type) {} content_type(content_type) {}
VideoEncodeAccelerator::Config::~Config() = default; VideoEncodeAccelerator::Config::~Config() = default;
...@@ -64,7 +66,8 @@ std::string VideoEncodeAccelerator::Config::AsHumanReadableString() const { ...@@ -64,7 +66,8 @@ std::string VideoEncodeAccelerator::Config::AsHumanReadableString() const {
str += base::StringPrintf(", initial_framerate: %u", str += base::StringPrintf(", initial_framerate: %u",
initial_framerate.value()); initial_framerate.value());
} }
if (h264_output_level) { if (h264_output_level &&
VideoCodecProfileToVideoCodec(output_profile) == kCodecH264) {
str += base::StringPrintf(", h264_output_level: %u", str += base::StringPrintf(", h264_output_level: %u",
h264_output_level.value()); h264_output_level.value());
} }
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include "media/base/video_bitrate_allocation.h" #include "media/base/video_bitrate_allocation.h"
#include "media/base/video_decoder_config.h" #include "media/base/video_decoder_config.h"
#include "media/base/video_frame.h" #include "media/base/video_frame.h"
#include "media/video/h264_parser.h"
namespace media { namespace media {
...@@ -93,6 +94,12 @@ class MEDIA_EXPORT VideoEncodeAccelerator { ...@@ -93,6 +94,12 @@ class MEDIA_EXPORT VideoEncodeAccelerator {
kErrorMax = kPlatformFailureError kErrorMax = kPlatformFailureError
}; };
// Unified default values for all VEA implementations.
enum {
kDefaultFramerate = 30,
kDefaultH264Level = H264SPS::kLevelIDC4p0,
};
// Parameters required for VEA initialization. // Parameters required for VEA initialization.
struct MEDIA_EXPORT Config { struct MEDIA_EXPORT Config {
// Indicates if video content should be treated as a "normal" camera feed // Indicates if video content should be treated as a "normal" camera feed
...@@ -129,14 +136,15 @@ class MEDIA_EXPORT VideoEncodeAccelerator { ...@@ -129,14 +136,15 @@ class MEDIA_EXPORT VideoEncodeAccelerator {
uint32_t initial_bitrate; uint32_t initial_bitrate;
// Initial encoding framerate in frames per second. This is optional and // Initial encoding framerate in frames per second. This is optional and
// VideoEncodeAccelerator should use default framerate if not given. // VideoEncodeAccelerator should use |kDefaultFramerate| if not given.
base::Optional<uint32_t> initial_framerate; base::Optional<uint32_t> initial_framerate;
// Codec level of encoded output stream for H264 only. This value should // Codec level of encoded output stream for H264 only. This value should
// be aligned to the H264 standard definition of SPS.level_idc. The only // be aligned to the H264 standard definition of SPS.level_idc. The only
// exception is in Main and Baseline profile we still use // exception is in Main and Baseline profile we still use
// |h264_output_level|=9 for Level 1b, which should set level_idc to 11 and // |h264_output_level|=9 for Level 1b, which should set level_idc to 11 and
// constraint_set3_flag to 1. (Spec A.3.1 and A.3.2) // constraint_set3_flag to 1 (Spec A.3.1 and A.3.2). This is optional and
// use |kDefaultH264Level| if not given.
base::Optional<uint8_t> h264_output_level; base::Optional<uint8_t> h264_output_level;
// Indicates captured video (from a camera) or generated (screen grabber). // Indicates captured video (from a camera) or generated (screen grabber).
......
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