summaryrefslogtreecommitdiff
path: root/chromium/media/gpu/vaapi/vaapi_video_encode_accelerator.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/media/gpu/vaapi/vaapi_video_encode_accelerator.cc')
-rw-r--r--chromium/media/gpu/vaapi/vaapi_video_encode_accelerator.cc1457
1 files changed, 714 insertions, 743 deletions
diff --git a/chromium/media/gpu/vaapi/vaapi_video_encode_accelerator.cc b/chromium/media/gpu/vaapi/vaapi_video_encode_accelerator.cc
index 57bb2fcfdff..a1ebebe4012 100644
--- a/chromium/media/gpu/vaapi/vaapi_video_encode_accelerator.cc
+++ b/chromium/media/gpu/vaapi/vaapi_video_encode_accelerator.cc
@@ -6,22 +6,31 @@
#include <string.h>
+#include <algorithm>
#include <memory>
#include <utility>
#include <va/va.h>
+#include <va/va_enc_h264.h>
+#include <va/va_enc_vp8.h>
#include "base/bind.h"
#include "base/callback.h"
#include "base/macros.h"
+#include "base/memory/ptr_util.h"
#include "base/metrics/histogram_macros.h"
#include "base/numerics/safe_conversions.h"
#include "base/single_thread_task_runner.h"
+#include "base/stl_util.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/trace_event/trace_event.h"
#include "media/base/bind_to_current_loop.h"
#include "media/gpu/h264_dpb.h"
#include "media/gpu/shared_memory_region.h"
+#include "media/gpu/vaapi/h264_encoder.h"
+#include "media/gpu/vaapi/vaapi_common.h"
+#include "media/gpu/vaapi/vp8_encoder.h"
+#include "media/gpu/vp8_reference_frame_vector.h"
#define VLOGF(level) VLOG(level) << __func__ << "(): "
#define DVLOGF(level) DVLOG(level) << __func__ << "(): "
@@ -37,69 +46,77 @@
namespace media {
namespace {
+// Minimum number of frames in flight for pipeline depth, adjust to this number
+// if encoder requests less.
+constexpr size_t kMinNumFramesInFlight = 4;
+
// Need 2 surfaces for each frame: one for input data and one for
// reconstructed picture, which is later used for reference.
-const size_t kMinSurfacesToEncode = 2;
+constexpr size_t kNumSurfacesPerFrame = 2;
+// TODO(owenlin): Adjust the value after b/71367113 is fixed
+constexpr size_t kExtraOutputBufferSizeInBytes = 32768;
-// Subjectively chosen.
-const size_t kNumInputBuffers = 4;
-const size_t kMaxNumReferenceFrames = 4;
+constexpr int kDefaultFramerate = 30;
-// TODO(owenlin): Adjust the value after b/71367113 is fixed
-const size_t kExtraOutputBufferSize = 32768; // bytes
-
-// We need up to kMaxNumReferenceFrames surfaces for reference, plus one
-// for input and one for encode (which will be added to the set of reference
-// frames for subsequent frames). Actual execution of HW encode is done
-// in parallel, and we want to process more frames in the meantime.
-// To have kNumInputBuffers in flight, we need a full set of reference +
-// encode surfaces (i.e. kMaxNumReferenceFrames + kMinSurfacesToEncode), and
-// (kNumInputBuffers - 1) of kMinSurfacesToEncode for the remaining frames
-// in flight.
-const size_t kNumSurfaces = kMaxNumReferenceFrames + kMinSurfacesToEncode +
- kMinSurfacesToEncode * (kNumInputBuffers - 1);
-
-// An IDR every 2048 frames, an I frame every 256 and no B frames.
-// We choose IDR period to equal MaxFrameNum so it must be a power of 2.
-const int kIDRPeriod = 2048;
-const int kIPeriod = 256;
-const int kIPPeriod = 1;
-
-const int kDefaultFramerate = 30;
-
-// HRD parameters (ch. E.2.2 in spec).
-const int kBitRateScale = 0; // bit_rate_scale for SPS HRD parameters.
-const int kCPBSizeScale = 0; // cpb_size_scale for SPS HRD parameters.
-
-const int kDefaultQP = 26;
-// All Intel codecs can do at least 4.1.
-const int kDefaultLevelIDC = 41;
-const int kChromaFormatIDC = 1; // 4:2:0
-
-// Arbitrarily chosen bitrate window size for rate control, in ms.
-const int kCPBWindowSizeMs = 1500;
+// Percentage of bitrate set to be targeted by the HW encoder.
+constexpr unsigned int kTargetBitratePercentage = 90;
// UMA errors that the VaapiVideoEncodeAccelerator class reports.
enum VAVEAEncoderFailure {
VAAPI_ERROR = 0,
VAVEA_ENCODER_FAILURES_MAX,
};
-}
-
-// Round |value| up to |alignment|, which must be a power of 2.
-static inline size_t RoundUpToPowerOf2(size_t value, size_t alignment) {
- // Check that |alignment| is a power of 2.
- DCHECK((alignment + (alignment - 1)) == (alignment | (alignment - 1)));
- return ((value + (alignment - 1)) & ~(alignment - 1));
-}
static void ReportToUMA(VAVEAEncoderFailure failure) {
UMA_HISTOGRAM_ENUMERATION("Media.VAVEA.EncoderFailure", failure,
VAVEA_ENCODER_FAILURES_MAX + 1);
}
+} // namespace
+
+// Encode job for one frame. Created when an input frame is awaiting and
+// enough resources are available to proceed. Once the job is prepared and
+// submitted to the hardware, it awaits on the |submitted_encode_jobs_| queue
+// for an output bitstream buffer to become available. Once one is ready,
+// the encoded bytes are downloaded to it, job resources are released
+// and become available for reuse.
+class VaapiEncodeJob : public AcceleratedVideoEncoder::EncodeJob {
+ public:
+ VaapiEncodeJob(scoped_refptr<VideoFrame> input_frame,
+ bool keyframe,
+ base::OnceClosure execute_cb,
+ scoped_refptr<VASurface> input_surface,
+ scoped_refptr<VASurface> reconstructed_surface,
+ VABufferID coded_buffer_id);
+
+ VaapiEncodeJob* AsVaapiEncodeJob() override { return this; }
+
+ VABufferID coded_buffer_id() const { return coded_buffer_id_; }
+ const scoped_refptr<VASurface> input_surface() const {
+ return input_surface_;
+ }
+ const scoped_refptr<VASurface> reconstructed_surface() const {
+ return reconstructed_surface_;
+ }
+
+ private:
+ ~VaapiEncodeJob() override = default;
+
+ // Input surface for video frame data.
+ const scoped_refptr<VASurface> input_surface_;
+
+ // Surface for the reconstructed picture, used for reference
+ // for subsequent frames.
+ const scoped_refptr<VASurface> reconstructed_surface_;
+
+ // Buffer that will contain the output bitstream data for this frame.
+ VABufferID coded_buffer_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(VaapiEncodeJob);
+};
+
struct VaapiVideoEncodeAccelerator::InputFrameRef {
- InputFrameRef(const scoped_refptr<VideoFrame>& frame, bool force_keyframe)
+ InputFrameRef(scoped_refptr<VideoFrame> frame, bool force_keyframe)
: frame(frame), force_keyframe(force_keyframe) {}
const scoped_refptr<VideoFrame> frame;
const bool force_keyframe;
@@ -117,40 +134,64 @@ VaapiVideoEncodeAccelerator::GetSupportedProfiles() {
return VaapiWrapper::GetSupportedEncodeProfiles();
}
-static unsigned int Log2OfPowerOf2(unsigned int x) {
- CHECK_GT(x, 0u);
- DCHECK_EQ(x & (x - 1), 0u);
+class VaapiVideoEncodeAccelerator::H264Accelerator
+ : public H264Encoder::Accelerator {
+ public:
+ explicit H264Accelerator(VaapiVideoEncodeAccelerator* vea) : vea_(vea) {}
+
+ ~H264Accelerator() override = default;
+
+ // H264Encoder::Accelerator implementation.
+ scoped_refptr<H264Picture> GetPicture(
+ AcceleratedVideoEncoder::EncodeJob* job) override;
+
+ bool SubmitPackedHeaders(
+ AcceleratedVideoEncoder::EncodeJob* job,
+ scoped_refptr<H264BitstreamBuffer> packed_sps,
+ scoped_refptr<H264BitstreamBuffer> packed_pps) override;
+
+ bool SubmitFrameParameters(
+ AcceleratedVideoEncoder::EncodeJob* job,
+ const H264Encoder::EncodeParams& encode_params,
+ const media::H264SPS& sps,
+ const media::H264PPS& pps,
+ scoped_refptr<H264Picture> pic,
+ const std::list<scoped_refptr<H264Picture>>& ref_pic_list0,
+ const std::list<scoped_refptr<H264Picture>>& ref_pic_list1) override;
+
+ private:
+ VaapiVideoEncodeAccelerator* const vea_;
+};
- int log = 0;
- while (x > 1) {
- x >>= 1;
- ++log;
- }
- return log;
-}
+class VaapiVideoEncodeAccelerator::VP8Accelerator
+ : public VP8Encoder::Accelerator {
+ public:
+ explicit VP8Accelerator(VaapiVideoEncodeAccelerator* vea) : vea_(vea) {}
+
+ ~VP8Accelerator() override = default;
+
+ // VP8Encoder::Accelerator implementation.
+ scoped_refptr<VP8Picture> GetPicture(
+ AcceleratedVideoEncoder::EncodeJob* job) override;
+
+ bool SubmitFrameParameters(
+ AcceleratedVideoEncoder::EncodeJob* job,
+ const media::VP8Encoder::EncodeParams& encode_params,
+ scoped_refptr<VP8Picture> pic,
+ const Vp8ReferenceFrameVector& ref_frames) override;
+
+ private:
+ VaapiVideoEncodeAccelerator* const vea_;
+};
VaapiVideoEncodeAccelerator::VaapiVideoEncodeAccelerator()
- : profile_(VIDEO_CODEC_PROFILE_UNKNOWN),
- mb_width_(0),
- mb_height_(0),
+ : codec_(kUnknownVideoCodec),
output_buffer_byte_size_(0),
state_(kUninitialized),
- frame_num_(0),
- idr_pic_id_(0),
- bitrate_(0),
- framerate_(0),
- cpb_size_(0),
- encoding_parameters_changed_(false),
encoder_thread_("VAVEAEncoderThread"),
child_task_runner_(base::ThreadTaskRunnerHandle::Get()),
weak_this_ptr_factory_(this) {
VLOGF(2);
- weak_this_ = weak_this_ptr_factory_.GetWeakPtr();
- max_ref_idx_l0_size_ = kMaxNumReferenceFrames;
- qp_ = kDefaultQP;
- idr_period_ = kIDRPeriod;
- i_period_ = kIPeriod;
- ip_period_ = kIPPeriod;
}
VaapiVideoEncodeAccelerator::~VaapiVideoEncodeAccelerator() {
@@ -178,6 +219,18 @@ bool VaapiVideoEncodeAccelerator::Initialize(
client_ptr_factory_.reset(new base::WeakPtrFactory<Client>(client));
client_ = client_ptr_factory_->GetWeakPtr();
+ codec_ = VideoCodecProfileToVideoCodec(output_profile);
+ if (codec_ != kCodecH264 && codec_ != kCodecVP8) {
+ DVLOGF(1) << "Unsupported profile: " << GetProfileName(output_profile);
+ return false;
+ }
+
+ if (format != PIXEL_FORMAT_I420) {
+ DVLOGF(1) << "Unsupported input format: "
+ << VideoPixelFormatToString(format);
+ return false;
+ }
+
const SupportedProfiles& profiles = GetSupportedProfiles();
auto profile = find_if(profiles.begin(), profiles.end(),
[output_profile](const SupportedProfile& profile) {
@@ -187,6 +240,7 @@ bool VaapiVideoEncodeAccelerator::Initialize(
VLOGF(1) << "Unsupported output profile " << GetProfileName(output_profile);
return false;
}
+
if (input_visible_size.width() > profile->max_resolution.width() ||
input_visible_size.height() > profile->max_resolution.height()) {
VLOGF(1) << "Input size too big: " << input_visible_size.ToString()
@@ -194,29 +248,10 @@ bool VaapiVideoEncodeAccelerator::Initialize(
return false;
}
- if (format != PIXEL_FORMAT_I420) {
- VLOGF(1) << "Unsupported input format: "
- << VideoPixelFormatToString(format);
- return false;
- }
-
- profile_ = output_profile;
- visible_size_ = input_visible_size;
- // 4:2:0 format has to be 2-aligned.
- DCHECK_EQ(visible_size_.width() % 2, 0);
- DCHECK_EQ(visible_size_.height() % 2, 0);
- coded_size_ = gfx::Size(RoundUpToPowerOf2(visible_size_.width(), 16),
- RoundUpToPowerOf2(visible_size_.height(), 16));
- mb_width_ = coded_size_.width() / 16;
- mb_height_ = coded_size_.height() / 16;
- output_buffer_byte_size_ = coded_size_.GetArea() + kExtraOutputBufferSize;
-
- UpdateRates(initial_bitrate, kDefaultFramerate);
-
vaapi_wrapper_ =
VaapiWrapper::CreateForVideoCodec(VaapiWrapper::kEncode, output_profile,
base::Bind(&ReportToUMA, VAAPI_ERROR));
- if (!vaapi_wrapper_.get()) {
+ if (!vaapi_wrapper_) {
VLOGF(1) << "Failed initializing VAAPI for profile "
<< GetProfileName(output_profile);
return false;
@@ -226,42 +261,74 @@ bool VaapiVideoEncodeAccelerator::Initialize(
VLOGF(1) << "Failed to start encoder thread";
return false;
}
+
encoder_thread_task_runner_ = encoder_thread_.task_runner();
- // Finish the remaining initialization on the encoder thread.
+ // Finish remaining initialization on the encoder thread.
encoder_thread_task_runner_->PostTask(
FROM_HERE, base::Bind(&VaapiVideoEncodeAccelerator::InitializeTask,
- base::Unretained(this)));
-
+ base::Unretained(this), input_visible_size,
+ output_profile, initial_bitrate));
return true;
}
-void VaapiVideoEncodeAccelerator::InitializeTask() {
+void VaapiVideoEncodeAccelerator::InitializeTask(const gfx::Size& visible_size,
+ VideoCodecProfile profile,
+ uint32_t bitrate) {
DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread());
DCHECK_EQ(state_, kUninitialized);
VLOGF(2);
+ weak_this_ = weak_this_ptr_factory_.GetWeakPtr();
+
+ switch (codec_) {
+ case kCodecH264:
+ encoder_ = std::make_unique<H264Encoder>(
+ std::make_unique<H264Accelerator>(this));
+ break;
+
+ case kCodecVP8:
+ encoder_ =
+ std::make_unique<VP8Encoder>(std::make_unique<VP8Accelerator>(this));
+ break;
+
+ default:
+ NOTREACHED() << "Unsupported codec type " << GetCodecName(codec_);
+ return;
+ }
+
+ if (!encoder_->Initialize(visible_size, profile, bitrate,
+ kDefaultFramerate)) {
+ NOTIFY_ERROR(kInvalidArgumentError, "Failed initializing encoder");
+ return;
+ }
+
+ coded_size_ = encoder_->GetCodedSize();
+ output_buffer_byte_size_ =
+ encoder_->GetBitstreamBufferSize() + kExtraOutputBufferSizeInBytes;
+ const size_t max_ref_frames = encoder_->GetMaxNumOfRefFrames();
+ // Use at least kMinNumFramesInFlight if encoder requested less for
+ // pipeline depth.
+ const size_t num_frames_in_flight =
+ std::max(kMinNumFramesInFlight, max_ref_frames);
+ const size_t num_surfaces = (num_frames_in_flight + 1) * kNumSurfacesPerFrame;
+ DVLOGF(1) << "Frames in flight: " << num_frames_in_flight;
+
va_surface_release_cb_ = BindToCurrentLoop(
base::Bind(&VaapiVideoEncodeAccelerator::RecycleVASurfaceID,
base::Unretained(this)));
if (!vaapi_wrapper_->CreateSurfaces(VA_RT_FORMAT_YUV420, coded_size_,
- kNumSurfaces,
+ num_surfaces,
&available_va_surface_ids_)) {
NOTIFY_ERROR(kPlatformFailureError, "Failed creating VASurfaces");
return;
}
- UpdateSPS();
- GeneratePackedSPS();
-
- UpdatePPS();
- GeneratePackedPPS();
-
child_task_runner_->PostTask(
- FROM_HERE,
- base::Bind(&Client::RequireBitstreamBuffers, client_, kNumInputBuffers,
- coded_size_, output_buffer_byte_size_));
+ FROM_HERE, base::BindOnce(&Client::RequireBitstreamBuffers, client_,
+ num_frames_in_flight, coded_size_,
+ output_buffer_byte_size_));
SetState(kEncoding);
}
@@ -272,264 +339,50 @@ void VaapiVideoEncodeAccelerator::RecycleVASurfaceID(
DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread());
available_va_surface_ids_.push_back(va_surface_id);
- EncodeFrameTask();
+ EncodePendingInputs();
}
-void VaapiVideoEncodeAccelerator::BeginFrame(bool force_keyframe) {
- current_pic_ = new H264Picture();
-
- // If the current picture is an IDR picture, frame_num shall be equal to 0.
- if (force_keyframe)
- frame_num_ = 0;
-
- current_pic_->frame_num = frame_num_++;
- frame_num_ %= idr_period_;
-
- if (current_pic_->frame_num == 0) {
- current_pic_->idr = true;
- // H264 spec mandates idr_pic_id to differ between two consecutive IDRs.
- idr_pic_id_ ^= 1;
- ref_pic_list0_.clear();
- }
-
- if (current_pic_->frame_num % i_period_ == 0)
- current_pic_->type = H264SliceHeader::kISlice;
- else
- current_pic_->type = H264SliceHeader::kPSlice;
-
- if (current_pic_->type != H264SliceHeader::kBSlice)
- current_pic_->ref = true;
-
- current_pic_->pic_order_cnt = current_pic_->frame_num * 2;
- current_pic_->top_field_order_cnt = current_pic_->pic_order_cnt;
- current_pic_->pic_order_cnt_lsb = current_pic_->pic_order_cnt;
-
- current_encode_job_->keyframe = current_pic_->idr;
-
- DVLOGF(4) << "Starting a new frame, type: " << current_pic_->type
- << (force_keyframe ? " (forced keyframe)" : "")
- << " frame_num: " << current_pic_->frame_num
- << " POC: " << current_pic_->pic_order_cnt;
+void VaapiVideoEncodeAccelerator::ExecuteEncode(VASurfaceID va_surface_id) {
+ DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread());
+ if (!vaapi_wrapper_->ExecuteAndDestroyPendingBuffers(va_surface_id))
+ NOTIFY_ERROR(kPlatformFailureError, "Failed to execute encode");
}
-void VaapiVideoEncodeAccelerator::EndFrame() {
- DCHECK(current_pic_);
- // Store the picture on the list of reference pictures and keep the list
- // below maximum size, dropping oldest references.
- if (current_pic_->ref)
- ref_pic_list0_.push_front(current_encode_job_->recon_surface);
- size_t max_num_ref_frames =
- base::checked_cast<size_t>(current_sps_.max_num_ref_frames);
- while (ref_pic_list0_.size() > max_num_ref_frames)
- ref_pic_list0_.pop_back();
-
- submitted_encode_jobs_.push(std::move(current_encode_job_));
+void VaapiVideoEncodeAccelerator::UploadFrame(scoped_refptr<VideoFrame> frame,
+ VASurfaceID va_surface_id) {
+ DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread());
+ if (!vaapi_wrapper_->UploadVideoFrameToSurface(frame, va_surface_id))
+ NOTIFY_ERROR(kPlatformFailureError, "Failed to upload frame");
}
-static void InitVAPicture(VAPictureH264* va_pic) {
- memset(va_pic, 0, sizeof(*va_pic));
- va_pic->picture_id = VA_INVALID_ID;
- va_pic->flags = VA_PICTURE_H264_INVALID;
+void VaapiVideoEncodeAccelerator::SubmitBuffer(
+ VABufferType type,
+ scoped_refptr<base::RefCountedBytes> buffer) {
+ DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread());
+ if (!vaapi_wrapper_->SubmitBuffer(type, buffer->size(), buffer->front()))
+ NOTIFY_ERROR(kPlatformFailureError, "Failed submitting a buffer");
}
-bool VaapiVideoEncodeAccelerator::SubmitFrameParameters() {
- DCHECK(current_pic_);
- VAEncSequenceParameterBufferH264 seq_param;
- memset(&seq_param, 0, sizeof(seq_param));
-
-#define SPS_TO_SP(a) seq_param.a = current_sps_.a;
- SPS_TO_SP(seq_parameter_set_id);
- SPS_TO_SP(level_idc);
-
- seq_param.intra_period = i_period_;
- seq_param.intra_idr_period = idr_period_;
- seq_param.ip_period = ip_period_;
- seq_param.bits_per_second = bitrate_;
-
- SPS_TO_SP(max_num_ref_frames);
- seq_param.picture_width_in_mbs = mb_width_;
- seq_param.picture_height_in_mbs = mb_height_;
-
-#define SPS_TO_SP_FS(a) seq_param.seq_fields.bits.a = current_sps_.a;
- SPS_TO_SP_FS(chroma_format_idc);
- SPS_TO_SP_FS(frame_mbs_only_flag);
- SPS_TO_SP_FS(log2_max_frame_num_minus4);
- SPS_TO_SP_FS(pic_order_cnt_type);
- SPS_TO_SP_FS(log2_max_pic_order_cnt_lsb_minus4);
-#undef SPS_TO_SP_FS
-
- SPS_TO_SP(bit_depth_luma_minus8);
- SPS_TO_SP(bit_depth_chroma_minus8);
-
- SPS_TO_SP(frame_cropping_flag);
- if (current_sps_.frame_cropping_flag) {
- SPS_TO_SP(frame_crop_left_offset);
- SPS_TO_SP(frame_crop_right_offset);
- SPS_TO_SP(frame_crop_top_offset);
- SPS_TO_SP(frame_crop_bottom_offset);
- }
-
- SPS_TO_SP(vui_parameters_present_flag);
-#define SPS_TO_SP_VF(a) seq_param.vui_fields.bits.a = current_sps_.a;
- SPS_TO_SP_VF(timing_info_present_flag);
-#undef SPS_TO_SP_VF
- SPS_TO_SP(num_units_in_tick);
- SPS_TO_SP(time_scale);
-#undef SPS_TO_SP
-
- if (!vaapi_wrapper_->SubmitBuffer(VAEncSequenceParameterBufferType,
- sizeof(seq_param), &seq_param))
- return false;
-
- VAEncPictureParameterBufferH264 pic_param;
- memset(&pic_param, 0, sizeof(pic_param));
-
- pic_param.CurrPic.picture_id = current_encode_job_->recon_surface->id();
- pic_param.CurrPic.TopFieldOrderCnt = current_pic_->top_field_order_cnt;
- pic_param.CurrPic.BottomFieldOrderCnt = current_pic_->bottom_field_order_cnt;
- pic_param.CurrPic.flags = 0;
-
- for (size_t i = 0; i < arraysize(pic_param.ReferenceFrames); ++i)
- InitVAPicture(&pic_param.ReferenceFrames[i]);
-
- DCHECK_LE(ref_pic_list0_.size(), arraysize(pic_param.ReferenceFrames));
- RefPicList::const_iterator iter = ref_pic_list0_.begin();
- for (size_t i = 0;
- i < arraysize(pic_param.ReferenceFrames) && iter != ref_pic_list0_.end();
- ++iter, ++i) {
- pic_param.ReferenceFrames[i].picture_id = (*iter)->id();
- pic_param.ReferenceFrames[i].flags = 0;
- }
-
- pic_param.coded_buf = current_encode_job_->coded_buffer;
- pic_param.pic_parameter_set_id = current_pps_.pic_parameter_set_id;
- pic_param.seq_parameter_set_id = current_pps_.seq_parameter_set_id;
- pic_param.frame_num = current_pic_->frame_num;
- pic_param.pic_init_qp = qp_;
- pic_param.num_ref_idx_l0_active_minus1 = max_ref_idx_l0_size_ - 1;
- pic_param.pic_fields.bits.idr_pic_flag = current_pic_->idr;
- pic_param.pic_fields.bits.reference_pic_flag = current_pic_->ref;
-#define PPS_TO_PP_PF(a) pic_param.pic_fields.bits.a = current_pps_.a;
- PPS_TO_PP_PF(entropy_coding_mode_flag);
- PPS_TO_PP_PF(transform_8x8_mode_flag);
- PPS_TO_PP_PF(deblocking_filter_control_present_flag);
-#undef PPS_TO_PP_PF
-
- if (!vaapi_wrapper_->SubmitBuffer(VAEncPictureParameterBufferType,
- sizeof(pic_param), &pic_param))
- return false;
-
- VAEncSliceParameterBufferH264 slice_param;
- memset(&slice_param, 0, sizeof(slice_param));
-
- slice_param.num_macroblocks = mb_width_ * mb_height_;
- slice_param.macroblock_info = VA_INVALID_ID;
- slice_param.slice_type = current_pic_->type;
- slice_param.pic_parameter_set_id = current_pps_.pic_parameter_set_id;
- slice_param.idr_pic_id = idr_pic_id_;
- slice_param.pic_order_cnt_lsb = current_pic_->pic_order_cnt_lsb;
- slice_param.num_ref_idx_active_override_flag = true;
-
- for (size_t i = 0; i < arraysize(slice_param.RefPicList0); ++i)
- InitVAPicture(&slice_param.RefPicList0[i]);
-
- for (size_t i = 0; i < arraysize(slice_param.RefPicList1); ++i)
- InitVAPicture(&slice_param.RefPicList1[i]);
-
- DCHECK_LE(ref_pic_list0_.size(), arraysize(slice_param.RefPicList0));
- iter = ref_pic_list0_.begin();
- for (size_t i = 0;
- i < arraysize(slice_param.RefPicList0) && iter != ref_pic_list0_.end();
- ++iter, ++i) {
- InitVAPicture(&slice_param.RefPicList0[i]);
- slice_param.RefPicList0[i].picture_id = (*iter)->id();
- slice_param.RefPicList0[i].flags = 0;
+void VaapiVideoEncodeAccelerator::SubmitVAEncMiscParamBuffer(
+ VAEncMiscParameterType type,
+ scoped_refptr<base::RefCountedBytes> buffer) {
+ DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread());
+ if (!vaapi_wrapper_->SubmitVAEncMiscParamBuffer(type, buffer->size(),
+ buffer->front())) {
+ NOTIFY_ERROR(kPlatformFailureError, "Failed submitting a parameter buffer");
}
-
- if (!vaapi_wrapper_->SubmitBuffer(VAEncSliceParameterBufferType,
- sizeof(slice_param), &slice_param))
- return false;
-
- VAEncMiscParameterRateControl rate_control_param;
- memset(&rate_control_param, 0, sizeof(rate_control_param));
- rate_control_param.bits_per_second = bitrate_;
- rate_control_param.target_percentage = 90;
- rate_control_param.window_size = kCPBWindowSizeMs;
- rate_control_param.initial_qp = qp_;
- rate_control_param.rc_flags.bits.disable_frame_skip = true;
-
- if (!vaapi_wrapper_->SubmitVAEncMiscParamBuffer(
- VAEncMiscParameterTypeRateControl, sizeof(rate_control_param),
- &rate_control_param))
- return false;
-
- VAEncMiscParameterFrameRate framerate_param;
- memset(&framerate_param, 0, sizeof(framerate_param));
- framerate_param.framerate = framerate_;
- if (!vaapi_wrapper_->SubmitVAEncMiscParamBuffer(
- VAEncMiscParameterTypeFrameRate, sizeof(framerate_param),
- &framerate_param))
- return false;
-
- VAEncMiscParameterHRD hrd_param;
- memset(&hrd_param, 0, sizeof(hrd_param));
- hrd_param.buffer_size = cpb_size_;
- hrd_param.initial_buffer_fullness = cpb_size_ / 2;
- if (!vaapi_wrapper_->SubmitVAEncMiscParamBuffer(
- VAEncMiscParameterTypeHRD, sizeof(hrd_param), &hrd_param))
- return false;
-
- return true;
}
-bool VaapiVideoEncodeAccelerator::SubmitHeadersIfNeeded() {
- DCHECK(current_pic_);
- if (current_pic_->type != H264SliceHeader::kISlice)
- return true;
-
- // Submit SPS.
- VAEncPackedHeaderParameterBuffer par_buffer;
- memset(&par_buffer, 0, sizeof(par_buffer));
- par_buffer.type = VAEncPackedHeaderSequence;
- par_buffer.bit_length = packed_sps_.BytesInBuffer() * 8;
-
- if (!vaapi_wrapper_->SubmitBuffer(VAEncPackedHeaderParameterBufferType,
- sizeof(par_buffer), &par_buffer))
- return false;
-
- if (!vaapi_wrapper_->SubmitBuffer(VAEncPackedHeaderDataBufferType,
- packed_sps_.BytesInBuffer(),
- packed_sps_.data()))
- return false;
-
- // Submit PPS.
- memset(&par_buffer, 0, sizeof(par_buffer));
- par_buffer.type = VAEncPackedHeaderPicture;
- par_buffer.bit_length = packed_pps_.BytesInBuffer() * 8;
-
- if (!vaapi_wrapper_->SubmitBuffer(VAEncPackedHeaderParameterBufferType,
- sizeof(par_buffer), &par_buffer))
- return false;
-
+void VaapiVideoEncodeAccelerator::SubmitH264BitstreamBuffer(
+ scoped_refptr<H264BitstreamBuffer> buffer) {
+ DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread());
+ // TODO(crbug.com/844303): use vaMapBuffer in VaapiWrapper::SubmitBuffer()
+ // instead to avoid this.
+ void* non_const_ptr = const_cast<uint8_t*>(buffer->data());
if (!vaapi_wrapper_->SubmitBuffer(VAEncPackedHeaderDataBufferType,
- packed_pps_.BytesInBuffer(),
- packed_pps_.data()))
- return false;
-
- return true;
-}
-
-bool VaapiVideoEncodeAccelerator::ExecuteEncode() {
- DCHECK(current_pic_);
- DVLOGF(4) << "Encoding frame_num: " << current_pic_->frame_num;
- return vaapi_wrapper_->ExecuteAndDestroyPendingBuffers(
- current_encode_job_->input_surface->id());
-}
-
-bool VaapiVideoEncodeAccelerator::UploadFrame(
- const scoped_refptr<VideoFrame>& frame) {
- return vaapi_wrapper_->UploadVideoFrameToSurface(
- frame, current_encode_job_->input_surface->id());
+ buffer->BytesInBuffer(), non_const_ptr)) {
+ NOTIFY_ERROR(kPlatformFailureError, "Failed submitting a bitstream buffer");
+ }
}
void VaapiVideoEncodeAccelerator::TryToReturnBitstreamBuffer() {
@@ -538,45 +391,51 @@ void VaapiVideoEncodeAccelerator::TryToReturnBitstreamBuffer() {
if (state_ != kEncoding)
return;
- while (!submitted_encode_jobs_.empty()) {
+ while (!submitted_encode_jobs_.empty() &&
+ submitted_encode_jobs_.front() == nullptr) {
// A null job indicates a flush command.
- if (submitted_encode_jobs_.front() == nullptr) {
- submitted_encode_jobs_.pop();
- DVLOGF(2) << "FlushDone";
- DCHECK(flush_callback_);
- child_task_runner_->PostTask(
- FROM_HERE, base::BindOnce(std::move(flush_callback_), true));
- continue;
- }
+ submitted_encode_jobs_.pop();
+ DVLOGF(2) << "FlushDone";
+ DCHECK(flush_callback_);
+ child_task_runner_->PostTask(
+ FROM_HERE, base::BindOnce(std::move(flush_callback_), true));
+ }
- if (available_bitstream_buffers_.empty())
- break;
+ if (submitted_encode_jobs_.empty() || available_bitstream_buffers_.empty())
+ return;
- const auto encode_job = std::move(submitted_encode_jobs_.front());
- submitted_encode_jobs_.pop();
- const auto buffer = std::move(available_bitstream_buffers_.front());
- available_bitstream_buffers_.pop();
+ auto buffer = std::move(available_bitstream_buffers_.front());
+ available_bitstream_buffers_.pop();
+ auto encode_job = submitted_encode_jobs_.front();
+ submitted_encode_jobs_.pop();
- uint8_t* target_data = reinterpret_cast<uint8_t*>(buffer->shm->memory());
+ ReturnBitstreamBuffer(encode_job, std::move(buffer));
+}
- size_t data_size = 0;
- if (!vaapi_wrapper_->DownloadAndDestroyCodedBuffer(
- encode_job->coded_buffer, encode_job->input_surface->id(),
- target_data, buffer->shm->size(), &data_size)) {
- NOTIFY_ERROR(kPlatformFailureError, "Failed downloading coded buffer");
- return;
- }
+void VaapiVideoEncodeAccelerator::ReturnBitstreamBuffer(
+ scoped_refptr<VaapiEncodeJob> encode_job,
+ std::unique_ptr<BitstreamBufferRef> buffer) {
+ DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread());
- DVLOGF(4) << "Returning bitstream buffer "
- << (encode_job->keyframe ? "(keyframe)" : "")
- << " id: " << buffer->id << " size: " << data_size;
+ uint8_t* target_data = reinterpret_cast<uint8_t*>(buffer->shm->memory());
+ size_t data_size = 0;
- child_task_runner_->PostTask(
- FROM_HERE,
- base::Bind(&Client::BitstreamBufferReady, client_, buffer->id,
- data_size, encode_job->keyframe, encode_job->timestamp));
- break;
+ if (!vaapi_wrapper_->DownloadAndDestroyCodedBuffer(
+ encode_job->coded_buffer_id(), encode_job->input_surface()->id(),
+ target_data, buffer->shm->size(), &data_size)) {
+ NOTIFY_ERROR(kPlatformFailureError, "Failed downloading coded buffer");
+ return;
}
+
+ DVLOGF(4) << "Returning bitstream buffer "
+ << (encode_job->IsKeyframeRequested() ? "(keyframe)" : "")
+ << " id: " << buffer->id << " size: " << data_size;
+
+ child_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&Client::BitstreamBufferReady, client_, buffer->id,
+ data_size, encode_job->IsKeyframeRequested(),
+ encode_job->timestamp()));
}
void VaapiVideoEncodeAccelerator::Encode(const scoped_refptr<VideoFrame>& frame,
@@ -590,99 +449,87 @@ void VaapiVideoEncodeAccelerator::Encode(const scoped_refptr<VideoFrame>& frame,
base::Unretained(this), frame, force_keyframe));
}
-bool VaapiVideoEncodeAccelerator::PrepareNextJob(base::TimeDelta timestamp) {
- if (available_va_surface_ids_.size() < kMinSurfacesToEncode)
- return false;
+void VaapiVideoEncodeAccelerator::EncodeTask(scoped_refptr<VideoFrame> frame,
+ bool force_keyframe) {
+ DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread());
+ DCHECK_NE(state_, kUninitialized);
- DCHECK(!current_encode_job_);
- current_encode_job_.reset(new EncodeJob());
+ input_queue_.push(std::make_unique<InputFrameRef>(frame, force_keyframe));
+ EncodePendingInputs();
+}
+
+scoped_refptr<VaapiEncodeJob> VaapiVideoEncodeAccelerator::CreateEncodeJob(
+ scoped_refptr<VideoFrame> frame,
+ bool force_keyframe) {
+ DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread());
+
+ if (available_va_surface_ids_.size() < kNumSurfacesPerFrame) {
+ DVLOGF(4) << "Not enough surfaces available";
+ return nullptr;
+ }
+ VABufferID coded_buffer_id;
if (!vaapi_wrapper_->CreateCodedBuffer(output_buffer_byte_size_,
- &current_encode_job_->coded_buffer)) {
+ &coded_buffer_id)) {
NOTIFY_ERROR(kPlatformFailureError, "Failed creating coded buffer");
- return false;
+ return nullptr;
}
- current_encode_job_->timestamp = timestamp;
-
- current_encode_job_->input_surface = new VASurface(
+ static_assert(kNumSurfacesPerFrame == 2, "kNumSurfacesPerFrame must be 2");
+ scoped_refptr<VASurface> input_surface = new VASurface(
available_va_surface_ids_.back(), coded_size_,
vaapi_wrapper_->va_surface_format(), va_surface_release_cb_);
available_va_surface_ids_.pop_back();
- current_encode_job_->recon_surface = new VASurface(
+ scoped_refptr<VASurface> reconstructed_surface = new VASurface(
available_va_surface_ids_.back(), coded_size_,
vaapi_wrapper_->va_surface_format(), va_surface_release_cb_);
available_va_surface_ids_.pop_back();
- // Reference surfaces are needed until the job is done, but they get
- // removed from ref_pic_list0_ when it's full at the end of job submission.
- // Keep refs to them along with the job and only release after sync.
- current_encode_job_->reference_surfaces = ref_pic_list0_;
+ auto job = base::MakeRefCounted<VaapiEncodeJob>(
+ frame, force_keyframe,
+ base::BindOnce(&VaapiVideoEncodeAccelerator::ExecuteEncode,
+ base::Unretained(this), input_surface->id()),
+ input_surface, reconstructed_surface, coded_buffer_id);
- return true;
-}
+ job->AddSetupCallback(
+ base::BindOnce(&VaapiVideoEncodeAccelerator::UploadFrame,
+ base::Unretained(this), frame, input_surface->id()));
-void VaapiVideoEncodeAccelerator::EncodeTask(
- const scoped_refptr<VideoFrame>& frame,
- bool force_keyframe) {
- DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread());
- DCHECK_NE(state_, kUninitialized);
-
- encoder_input_queue_.push(
- std::make_unique<InputFrameRef>(frame, force_keyframe));
- EncodeFrameTask();
+ return job;
}
-void VaapiVideoEncodeAccelerator::EncodeFrameTask() {
+void VaapiVideoEncodeAccelerator::EncodePendingInputs() {
DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread());
+ DVLOGF(4);
+
+ while (state_ == kEncoding && !input_queue_.empty()) {
+ const std::unique_ptr<InputFrameRef>& input_frame = input_queue_.front();
+
+ // If this is a flush (null) frame, don't create/submit a new encode job for
+ // it, but forward a null job to the submitted_encode_jobs_ queue.
+ scoped_refptr<VaapiEncodeJob> job;
+ if (input_frame) {
+ job = CreateEncodeJob(input_frame->frame, input_frame->force_keyframe);
+ if (!job)
+ return;
+ }
- if (state_ != kEncoding || encoder_input_queue_.empty())
- return;
-
- // Pass the nullptr to the next queue |submitted_encode_jobs_|.
- if (encoder_input_queue_.front() == nullptr) {
- encoder_input_queue_.pop();
- submitted_encode_jobs_.push(nullptr);
- TryToReturnBitstreamBuffer();
- return;
- }
-
- if (!PrepareNextJob(encoder_input_queue_.front()->frame->timestamp())) {
- DVLOGF(4) << "Not ready for next frame yet";
- return;
- }
-
- const auto frame_ref = std::move(encoder_input_queue_.front());
- encoder_input_queue_.pop();
-
- TRACE_EVENT0("media,gpu", "VAVEA::EncodeFrameTask");
-
- if (!UploadFrame(frame_ref->frame)) {
- NOTIFY_ERROR(kPlatformFailureError, "Failed uploading source frame to HW.");
- return;
- }
-
- BeginFrame(frame_ref->force_keyframe || encoding_parameters_changed_);
- encoding_parameters_changed_ = false;
+ input_queue_.pop();
- if (!SubmitFrameParameters()) {
- NOTIFY_ERROR(kPlatformFailureError, "Failed submitting frame parameters.");
- return;
- }
+ if (job) {
+ if (!encoder_->PrepareEncodeJob(job.get())) {
+ NOTIFY_ERROR(kPlatformFailureError, "Failed preparing an encode job.");
+ return;
+ }
- if (!SubmitHeadersIfNeeded()) {
- NOTIFY_ERROR(kPlatformFailureError, "Failed submitting frame headers.");
- return;
- }
+ TRACE_EVENT0("media,gpu", "VAVEA: Execute");
+ job->Execute();
+ }
- if (!ExecuteEncode()) {
- NOTIFY_ERROR(kPlatformFailureError, "Failed submitting encode job to HW.");
- return;
+ submitted_encode_jobs_.push(job);
+ TryToReturnBitstreamBuffer();
}
-
- EndFrame();
- TryToReturnBitstreamBuffer();
}
void VaapiVideoEncodeAccelerator::UseOutputBitstreamBuffer(
@@ -695,15 +542,8 @@ void VaapiVideoEncodeAccelerator::UseOutputBitstreamBuffer(
return;
}
- std::unique_ptr<SharedMemoryRegion> shm(
- new SharedMemoryRegion(buffer, false));
- if (!shm->Map()) {
- NOTIFY_ERROR(kPlatformFailureError, "Failed mapping shared memory.");
- return;
- }
-
- std::unique_ptr<BitstreamBufferRef> buffer_ref(
- new BitstreamBufferRef(buffer.id(), std::move(shm)));
+ auto buffer_ref = std::make_unique<BitstreamBufferRef>(
+ buffer.id(), std::make_unique<SharedMemoryRegion>(buffer, false));
encoder_thread_task_runner_->PostTask(
FROM_HERE,
@@ -716,6 +556,11 @@ void VaapiVideoEncodeAccelerator::UseOutputBitstreamBufferTask(
DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread());
DCHECK_NE(state_, kUninitialized);
+ if (!buffer_ref->shm->Map()) {
+ NOTIFY_ERROR(kPlatformFailureError, "Failed mapping shared memory.");
+ return;
+ }
+
available_bitstream_buffers_.push(std::move(buffer_ref));
TryToReturnBitstreamBuffer();
}
@@ -733,17 +578,6 @@ void VaapiVideoEncodeAccelerator::RequestEncodingParametersChange(
base::Unretained(this), bitrate, framerate));
}
-void VaapiVideoEncodeAccelerator::UpdateRates(uint32_t bitrate,
- uint32_t framerate) {
- if (encoder_thread_.IsRunning())
- DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread());
- DCHECK_NE(bitrate, 0u);
- DCHECK_NE(framerate, 0u);
- bitrate_ = bitrate;
- framerate_ = framerate;
- cpb_size_ = bitrate_ * kCPBWindowSizeMs / 1000;
-}
-
void VaapiVideoEncodeAccelerator::RequestEncodingParametersChangeTask(
uint32_t bitrate,
uint32_t framerate) {
@@ -751,26 +585,8 @@ void VaapiVideoEncodeAccelerator::RequestEncodingParametersChangeTask(
DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread());
DCHECK_NE(state_, kUninitialized);
- // This is a workaround to zero being temporarily, as part of the initial
- // setup, provided by the webrtc video encode and a zero bitrate and
- // framerate not being accepted by VAAPI
- // TODO: This code is common with v4l2_video_encode_accelerator.cc, perhaps
- // it could be pulled up to RTCVideoEncoder
- if (bitrate < 1)
- bitrate = 1;
- if (framerate < 1)
- framerate = 1;
-
- if (bitrate_ == bitrate && framerate_ == framerate)
- return;
-
- UpdateRates(bitrate, framerate);
-
- UpdateSPS();
- GeneratePackedSPS();
-
- // Submit new parameters along with next frame that will be processed.
- encoding_parameters_changed_ = true;
+ if (!encoder_->UpdateRates(bitrate, framerate))
+ VLOGF(1) << "Failed to update rates to " << bitrate << " " << framerate;
}
void VaapiVideoEncodeAccelerator::Flush(FlushCallback flush_callback) {
@@ -791,18 +607,14 @@ void VaapiVideoEncodeAccelerator::FlushTask() {
DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread());
// Insert an null job to indicate a flush command.
- encoder_input_queue_.push(std::unique_ptr<InputFrameRef>(nullptr));
- EncodeFrameTask();
+ input_queue_.push(std::unique_ptr<InputFrameRef>(nullptr));
+ EncodePendingInputs();
}
void VaapiVideoEncodeAccelerator::Destroy() {
+ DVLOGF(2);
DCHECK(child_task_runner_->BelongsToCurrentThread());
- // Can't call client anymore after Destroy() returns.
- client_ptr_factory_.reset();
- weak_this_ptr_factory_.InvalidateWeakPtrs();
-
- // Early-exit encoder tasks if they are running and join the thread.
if (encoder_thread_.IsRunning()) {
encoder_thread_.task_runner()->PostTask(
FROM_HERE, base::Bind(&VaapiVideoEncodeAccelerator::DestroyTask,
@@ -813,301 +625,460 @@ void VaapiVideoEncodeAccelerator::Destroy() {
if (flush_callback_)
std::move(flush_callback_).Run(false);
+ weak_this_ptr_factory_.InvalidateWeakPtrs();
delete this;
}
void VaapiVideoEncodeAccelerator::DestroyTask() {
VLOGF(2);
DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread());
- SetState(kError);
+
+ // Clean up members that are to be accessed on the encoder thread only.
+ available_va_surface_ids_.clear();
+ available_va_buffer_ids_.clear();
+
+ while (!available_bitstream_buffers_.empty())
+ available_bitstream_buffers_.pop();
+
+ while (!input_queue_.empty())
+ input_queue_.pop();
+
+ while (!submitted_encode_jobs_.empty())
+ submitted_encode_jobs_.pop();
+
+ encoder_ = nullptr;
}
-void VaapiVideoEncodeAccelerator::UpdateSPS() {
- memset(&current_sps_, 0, sizeof(H264SPS));
-
- // Spec A.2 and A.3.
- switch (profile_) {
- case H264PROFILE_BASELINE:
- // Due to https://crbug.com/345569, we don't distinguish between
- // constrained and non-constrained baseline profiles. Since many codecs
- // can't do non-constrained, and constrained is usually what we mean (and
- // it's a subset of non-constrained), default to it.
- current_sps_.profile_idc = H264SPS::kProfileIDCBaseline;
- current_sps_.constraint_set0_flag = true;
- break;
- case H264PROFILE_MAIN:
- current_sps_.profile_idc = H264SPS::kProfileIDCMain;
- current_sps_.constraint_set1_flag = true;
- break;
- case H264PROFILE_HIGH:
- current_sps_.profile_idc = H264SPS::kProfileIDCHigh;
- break;
- default:
- NOTIMPLEMENTED();
- return;
+void VaapiVideoEncodeAccelerator::SetState(State state) {
+ // Only touch state on encoder thread, unless it's not running.
+ if (encoder_thread_.IsRunning() &&
+ !encoder_thread_task_runner_->BelongsToCurrentThread()) {
+ encoder_thread_task_runner_->PostTask(
+ FROM_HERE, base::BindOnce(&VaapiVideoEncodeAccelerator::SetState,
+ base::Unretained(this), state));
+ return;
+ }
+
+ VLOGF(2) << "setting state to: " << state;
+ state_ = state;
+}
+
+void VaapiVideoEncodeAccelerator::NotifyError(Error error) {
+ if (!child_task_runner_->BelongsToCurrentThread()) {
+ child_task_runner_->PostTask(
+ FROM_HERE, base::BindOnce(&VaapiVideoEncodeAccelerator::NotifyError,
+ weak_this_, error));
+ return;
}
- current_sps_.level_idc = kDefaultLevelIDC;
- current_sps_.seq_parameter_set_id = 0;
- current_sps_.chroma_format_idc = kChromaFormatIDC;
-
- DCHECK_GE(idr_period_, 1u << 4);
- current_sps_.log2_max_frame_num_minus4 = Log2OfPowerOf2(idr_period_) - 4;
- current_sps_.pic_order_cnt_type = 0;
- current_sps_.log2_max_pic_order_cnt_lsb_minus4 =
- Log2OfPowerOf2(idr_period_ * 2) - 4;
- current_sps_.max_num_ref_frames = max_ref_idx_l0_size_;
-
- current_sps_.frame_mbs_only_flag = true;
-
- DCHECK_GT(mb_width_, 0u);
- DCHECK_GT(mb_height_, 0u);
- current_sps_.pic_width_in_mbs_minus1 = mb_width_ - 1;
- DCHECK(current_sps_.frame_mbs_only_flag);
- current_sps_.pic_height_in_map_units_minus1 = mb_height_ - 1;
-
- if (visible_size_ != coded_size_) {
- // Visible size differs from coded size, fill crop information.
- current_sps_.frame_cropping_flag = true;
- DCHECK(!current_sps_.separate_colour_plane_flag);
- // Spec table 6-1. Only 4:2:0 for now.
- DCHECK_EQ(current_sps_.chroma_format_idc, 1);
- // Spec 7.4.2.1.1. Crop is in crop units, which is 2 pixels for 4:2:0.
- const unsigned int crop_unit_x = 2;
- const unsigned int crop_unit_y = 2 * (2 - current_sps_.frame_mbs_only_flag);
- current_sps_.frame_crop_left_offset = 0;
- current_sps_.frame_crop_right_offset =
- (coded_size_.width() - visible_size_.width()) / crop_unit_x;
- current_sps_.frame_crop_top_offset = 0;
- current_sps_.frame_crop_bottom_offset =
- (coded_size_.height() - visible_size_.height()) / crop_unit_y;
+ if (client_) {
+ client_->NotifyError(error);
+ client_ptr_factory_.reset();
}
+}
+
+VaapiEncodeJob::VaapiEncodeJob(scoped_refptr<VideoFrame> input_frame,
+ bool keyframe,
+ base::OnceClosure execute_cb,
+ scoped_refptr<VASurface> input_surface,
+ scoped_refptr<VASurface> reconstructed_surface,
+ VABufferID coded_buffer_id)
+ : EncodeJob(input_frame, keyframe, std::move(execute_cb)),
+ input_surface_(input_surface),
+ reconstructed_surface_(reconstructed_surface),
+ coded_buffer_id_(coded_buffer_id) {
+ DCHECK(input_surface_);
+ DCHECK(reconstructed_surface_);
+ DCHECK_NE(coded_buffer_id_, VA_INVALID_ID);
+}
+
+static void InitVAPictureH264(VAPictureH264* va_pic) {
+ *va_pic = {};
+ va_pic->picture_id = VA_INVALID_ID;
+ va_pic->flags = VA_PICTURE_H264_INVALID;
+}
- current_sps_.vui_parameters_present_flag = true;
- current_sps_.timing_info_present_flag = true;
- current_sps_.num_units_in_tick = 1;
- current_sps_.time_scale = framerate_ * 2; // See equation D-2 in spec.
- current_sps_.fixed_frame_rate_flag = true;
-
- current_sps_.nal_hrd_parameters_present_flag = true;
- // H.264 spec ch. E.2.2.
- current_sps_.cpb_cnt_minus1 = 0;
- current_sps_.bit_rate_scale = kBitRateScale;
- current_sps_.cpb_size_scale = kCPBSizeScale;
- current_sps_.bit_rate_value_minus1[0] =
- (bitrate_ >> (kBitRateScale + H264SPS::kBitRateScaleConstantTerm)) - 1;
- current_sps_.cpb_size_value_minus1[0] =
- (cpb_size_ >> (kCPBSizeScale + H264SPS::kCPBSizeScaleConstantTerm)) - 1;
- current_sps_.cbr_flag[0] = true;
- current_sps_.initial_cpb_removal_delay_length_minus_1 =
- H264SPS::kDefaultInitialCPBRemovalDelayLength - 1;
- current_sps_.cpb_removal_delay_length_minus1 =
- H264SPS::kDefaultInitialCPBRemovalDelayLength - 1;
- current_sps_.dpb_output_delay_length_minus1 =
- H264SPS::kDefaultDPBOutputDelayLength - 1;
- current_sps_.time_offset_length = H264SPS::kDefaultTimeOffsetLength;
- current_sps_.low_delay_hrd_flag = false;
+static scoped_refptr<base::RefCountedBytes> MakeRefCountedBytes(void* ptr,
+ size_t size) {
+ return base::MakeRefCounted<base::RefCountedBytes>(
+ reinterpret_cast<uint8_t*>(ptr), size);
}
-void VaapiVideoEncodeAccelerator::GeneratePackedSPS() {
- packed_sps_.Reset();
-
- packed_sps_.BeginNALU(H264NALU::kSPS, 3);
-
- packed_sps_.AppendBits(8, current_sps_.profile_idc);
- packed_sps_.AppendBool(current_sps_.constraint_set0_flag);
- packed_sps_.AppendBool(current_sps_.constraint_set1_flag);
- packed_sps_.AppendBool(current_sps_.constraint_set2_flag);
- packed_sps_.AppendBool(current_sps_.constraint_set3_flag);
- packed_sps_.AppendBool(current_sps_.constraint_set4_flag);
- packed_sps_.AppendBool(current_sps_.constraint_set5_flag);
- packed_sps_.AppendBits(2, 0); // reserved_zero_2bits
- packed_sps_.AppendBits(8, current_sps_.level_idc);
- packed_sps_.AppendUE(current_sps_.seq_parameter_set_id);
-
- if (current_sps_.profile_idc == H264SPS::kProfileIDCHigh) {
- packed_sps_.AppendUE(current_sps_.chroma_format_idc);
- if (current_sps_.chroma_format_idc == 3)
- packed_sps_.AppendBool(current_sps_.separate_colour_plane_flag);
- packed_sps_.AppendUE(current_sps_.bit_depth_luma_minus8);
- packed_sps_.AppendUE(current_sps_.bit_depth_chroma_minus8);
- packed_sps_.AppendBool(current_sps_.qpprime_y_zero_transform_bypass_flag);
- packed_sps_.AppendBool(current_sps_.seq_scaling_matrix_present_flag);
- CHECK(!current_sps_.seq_scaling_matrix_present_flag);
+bool VaapiVideoEncodeAccelerator::H264Accelerator::SubmitFrameParameters(
+ AcceleratedVideoEncoder::EncodeJob* job,
+ const media::H264Encoder::EncodeParams& encode_params,
+ const media::H264SPS& sps,
+ const media::H264PPS& pps,
+ scoped_refptr<H264Picture> pic,
+ const std::list<scoped_refptr<H264Picture>>& ref_pic_list0,
+ const std::list<scoped_refptr<H264Picture>>& ref_pic_list1) {
+ VAEncSequenceParameterBufferH264 seq_param = {};
+
+#define SPS_TO_SP(a) seq_param.a = sps.a;
+ SPS_TO_SP(seq_parameter_set_id);
+ SPS_TO_SP(level_idc);
+
+ seq_param.intra_period = encode_params.i_period_frames;
+ seq_param.intra_idr_period = encode_params.idr_period_frames;
+ seq_param.ip_period = encode_params.ip_period_frames;
+ seq_param.bits_per_second = encode_params.bitrate_bps;
+
+ SPS_TO_SP(max_num_ref_frames);
+ base::Optional<gfx::Size> coded_size = sps.GetCodedSize();
+ if (!coded_size) {
+ DVLOGF(1) << "Invalid coded size";
+ return false;
}
+ constexpr int kH264MacroblockSizeInPixels = 16;
+ seq_param.picture_width_in_mbs =
+ coded_size->width() / kH264MacroblockSizeInPixels;
+ seq_param.picture_height_in_mbs =
+ coded_size->height() / kH264MacroblockSizeInPixels;
+
+#define SPS_TO_SP_FS(a) seq_param.seq_fields.bits.a = sps.a;
+ SPS_TO_SP_FS(chroma_format_idc);
+ SPS_TO_SP_FS(frame_mbs_only_flag);
+ SPS_TO_SP_FS(log2_max_frame_num_minus4);
+ SPS_TO_SP_FS(pic_order_cnt_type);
+ SPS_TO_SP_FS(log2_max_pic_order_cnt_lsb_minus4);
+#undef SPS_TO_SP_FS
+
+ SPS_TO_SP(bit_depth_luma_minus8);
+ SPS_TO_SP(bit_depth_chroma_minus8);
- packed_sps_.AppendUE(current_sps_.log2_max_frame_num_minus4);
- packed_sps_.AppendUE(current_sps_.pic_order_cnt_type);
- if (current_sps_.pic_order_cnt_type == 0)
- packed_sps_.AppendUE(current_sps_.log2_max_pic_order_cnt_lsb_minus4);
- else if (current_sps_.pic_order_cnt_type == 1) {
- CHECK(1);
+ SPS_TO_SP(frame_cropping_flag);
+ if (sps.frame_cropping_flag) {
+ SPS_TO_SP(frame_crop_left_offset);
+ SPS_TO_SP(frame_crop_right_offset);
+ SPS_TO_SP(frame_crop_top_offset);
+ SPS_TO_SP(frame_crop_bottom_offset);
}
- packed_sps_.AppendUE(current_sps_.max_num_ref_frames);
- packed_sps_.AppendBool(current_sps_.gaps_in_frame_num_value_allowed_flag);
- packed_sps_.AppendUE(current_sps_.pic_width_in_mbs_minus1);
- packed_sps_.AppendUE(current_sps_.pic_height_in_map_units_minus1);
+ SPS_TO_SP(vui_parameters_present_flag);
+#define SPS_TO_SP_VF(a) seq_param.vui_fields.bits.a = sps.a;
+ SPS_TO_SP_VF(timing_info_present_flag);
+#undef SPS_TO_SP_VF
+ SPS_TO_SP(num_units_in_tick);
+ SPS_TO_SP(time_scale);
+#undef SPS_TO_SP
- packed_sps_.AppendBool(current_sps_.frame_mbs_only_flag);
- if (!current_sps_.frame_mbs_only_flag)
- packed_sps_.AppendBool(current_sps_.mb_adaptive_frame_field_flag);
+ job->AddSetupCallback(
+ base::BindOnce(&VaapiVideoEncodeAccelerator::SubmitBuffer,
+ base::Unretained(vea_), VAEncSequenceParameterBufferType,
+ MakeRefCountedBytes(&seq_param, sizeof(seq_param))));
- packed_sps_.AppendBool(current_sps_.direct_8x8_inference_flag);
+ VAEncPictureParameterBufferH264 pic_param = {};
- packed_sps_.AppendBool(current_sps_.frame_cropping_flag);
- if (current_sps_.frame_cropping_flag) {
- packed_sps_.AppendUE(current_sps_.frame_crop_left_offset);
- packed_sps_.AppendUE(current_sps_.frame_crop_right_offset);
- packed_sps_.AppendUE(current_sps_.frame_crop_top_offset);
- packed_sps_.AppendUE(current_sps_.frame_crop_bottom_offset);
- }
+ auto va_surface_id = pic->AsVaapiH264Picture()->GetVASurfaceID();
+ pic_param.CurrPic.picture_id = va_surface_id;
+ pic_param.CurrPic.TopFieldOrderCnt = pic->top_field_order_cnt;
+ pic_param.CurrPic.BottomFieldOrderCnt = pic->bottom_field_order_cnt;
+ pic_param.CurrPic.flags = 0;
- packed_sps_.AppendBool(current_sps_.vui_parameters_present_flag);
- if (current_sps_.vui_parameters_present_flag) {
- packed_sps_.AppendBool(false); // aspect_ratio_info_present_flag
- packed_sps_.AppendBool(false); // overscan_info_present_flag
- packed_sps_.AppendBool(false); // video_signal_type_present_flag
- packed_sps_.AppendBool(false); // chroma_loc_info_present_flag
-
- packed_sps_.AppendBool(current_sps_.timing_info_present_flag);
- if (current_sps_.timing_info_present_flag) {
- packed_sps_.AppendBits(32, current_sps_.num_units_in_tick);
- packed_sps_.AppendBits(32, current_sps_.time_scale);
- packed_sps_.AppendBool(current_sps_.fixed_frame_rate_flag);
- }
+ pic_param.coded_buf = job->AsVaapiEncodeJob()->coded_buffer_id();
+ pic_param.pic_parameter_set_id = pps.pic_parameter_set_id;
+ pic_param.seq_parameter_set_id = pps.seq_parameter_set_id;
+ pic_param.frame_num = pic->frame_num;
+ pic_param.pic_init_qp = pps.pic_init_qp_minus26 + 26;
+ pic_param.num_ref_idx_l0_active_minus1 =
+ pps.num_ref_idx_l0_default_active_minus1;
+
+ pic_param.pic_fields.bits.idr_pic_flag = pic->idr;
+ pic_param.pic_fields.bits.reference_pic_flag = pic->ref;
+#define PPS_TO_PP_PF(a) pic_param.pic_fields.bits.a = pps.a;
+ PPS_TO_PP_PF(entropy_coding_mode_flag);
+ PPS_TO_PP_PF(transform_8x8_mode_flag);
+ PPS_TO_PP_PF(deblocking_filter_control_present_flag);
+#undef PPS_TO_PP_PF
- packed_sps_.AppendBool(current_sps_.nal_hrd_parameters_present_flag);
- if (current_sps_.nal_hrd_parameters_present_flag) {
- packed_sps_.AppendUE(current_sps_.cpb_cnt_minus1);
- packed_sps_.AppendBits(4, current_sps_.bit_rate_scale);
- packed_sps_.AppendBits(4, current_sps_.cpb_size_scale);
- CHECK_LT(base::checked_cast<size_t>(current_sps_.cpb_cnt_minus1),
- arraysize(current_sps_.bit_rate_value_minus1));
- for (int i = 0; i <= current_sps_.cpb_cnt_minus1; ++i) {
- packed_sps_.AppendUE(current_sps_.bit_rate_value_minus1[i]);
- packed_sps_.AppendUE(current_sps_.cpb_size_value_minus1[i]);
- packed_sps_.AppendBool(current_sps_.cbr_flag[i]);
- }
- packed_sps_.AppendBits(
- 5, current_sps_.initial_cpb_removal_delay_length_minus_1);
- packed_sps_.AppendBits(5, current_sps_.cpb_removal_delay_length_minus1);
- packed_sps_.AppendBits(5, current_sps_.dpb_output_delay_length_minus1);
- packed_sps_.AppendBits(5, current_sps_.time_offset_length);
- }
+ VAEncSliceParameterBufferH264 slice_param = {};
- packed_sps_.AppendBool(false); // vcl_hrd_parameters_flag
- if (current_sps_.nal_hrd_parameters_present_flag)
- packed_sps_.AppendBool(current_sps_.low_delay_hrd_flag);
+ slice_param.num_macroblocks =
+ seq_param.picture_width_in_mbs * seq_param.picture_height_in_mbs;
+ slice_param.macroblock_info = VA_INVALID_ID;
+ slice_param.slice_type = pic->type;
+ slice_param.pic_parameter_set_id = pps.pic_parameter_set_id;
+ slice_param.idr_pic_id = pic->idr_pic_id;
+ slice_param.pic_order_cnt_lsb = pic->pic_order_cnt_lsb;
+ slice_param.num_ref_idx_active_override_flag = true;
- packed_sps_.AppendBool(false); // pic_struct_present_flag
- packed_sps_.AppendBool(true); // bitstream_restriction_flag
+ for (size_t i = 0; i < base::size(pic_param.ReferenceFrames); ++i)
+ InitVAPictureH264(&pic_param.ReferenceFrames[i]);
+
+ for (size_t i = 0; i < base::size(slice_param.RefPicList0); ++i)
+ InitVAPictureH264(&slice_param.RefPicList0[i]);
+
+ for (size_t i = 0; i < base::size(slice_param.RefPicList1); ++i)
+ InitVAPictureH264(&slice_param.RefPicList1[i]);
+
+ VAPictureH264* ref_frames_entry = pic_param.ReferenceFrames;
+ VAPictureH264* ref_list_entry = slice_param.RefPicList0;
+ // Initialize the current entry on slice and picture reference lists to
+ // |ref_pic| and advance list pointers.
+ auto fill_ref_frame = [&ref_frames_entry,
+ &ref_list_entry](scoped_refptr<H264Picture> ref_pic) {
+ VAPictureH264 va_pic_h264;
+ InitVAPictureH264(&va_pic_h264);
+ va_pic_h264.picture_id = ref_pic->AsVaapiH264Picture()->GetVASurfaceID();
+ va_pic_h264.flags = 0;
+
+ *ref_frames_entry = va_pic_h264;
+ *ref_list_entry = va_pic_h264;
+ ++ref_frames_entry;
+ ++ref_list_entry;
+ };
+
+ // Fill slice_param.RefPicList{0,1} with pictures from ref_pic_list{0,1},
+ // respectively, and pic_param.ReferenceFrames with entries from both.
+ std::for_each(ref_pic_list0.begin(), ref_pic_list0.end(), fill_ref_frame);
+ ref_list_entry = slice_param.RefPicList1;
+ std::for_each(ref_pic_list1.begin(), ref_pic_list1.end(), fill_ref_frame);
+
+ VAEncMiscParameterRateControl rate_control_param = {};
+ rate_control_param.bits_per_second = encode_params.bitrate_bps;
+ rate_control_param.target_percentage = kTargetBitratePercentage;
+ rate_control_param.window_size = encode_params.cpb_window_size_ms;
+ rate_control_param.initial_qp = pic_param.pic_init_qp;
+ rate_control_param.rc_flags.bits.disable_frame_skip = true;
- packed_sps_.AppendBool(false); // motion_vectors_over_pic_boundaries_flag
- packed_sps_.AppendUE(2); // max_bytes_per_pic_denom
- packed_sps_.AppendUE(1); // max_bits_per_mb_denom
- packed_sps_.AppendUE(16); // log2_max_mv_length_horizontal
- packed_sps_.AppendUE(16); // log2_max_mv_length_vertical
+ VAEncMiscParameterFrameRate framerate_param = {};
+ framerate_param.framerate = encode_params.framerate;
- // Explicitly set max_num_reorder_frames to 0 to allow the decoder to
- // output pictures early.
- packed_sps_.AppendUE(0); // max_num_reorder_frames
+ VAEncMiscParameterHRD hrd_param = {};
+ hrd_param.buffer_size = encode_params.cpb_size_bits;
+ hrd_param.initial_buffer_fullness = hrd_param.buffer_size / 2;
- // The value of max_dec_frame_buffering shall be greater than or equal to
- // max_num_ref_frames.
- const unsigned int max_dec_frame_buffering =
- current_sps_.max_num_ref_frames;
- packed_sps_.AppendUE(max_dec_frame_buffering);
- }
+ job->AddSetupCallback(
+ base::BindOnce(&VaapiVideoEncodeAccelerator::SubmitBuffer,
+ base::Unretained(vea_), VAEncPictureParameterBufferType,
+ MakeRefCountedBytes(&pic_param, sizeof(pic_param))));
- packed_sps_.FinishNALU();
-}
+ job->AddSetupCallback(
+ base::BindOnce(&VaapiVideoEncodeAccelerator::SubmitBuffer,
+ base::Unretained(vea_), VAEncSliceParameterBufferType,
+ MakeRefCountedBytes(&slice_param, sizeof(slice_param))));
-void VaapiVideoEncodeAccelerator::UpdatePPS() {
- memset(&current_pps_, 0, sizeof(H264PPS));
+ job->AddSetupCallback(base::BindOnce(
+ &VaapiVideoEncodeAccelerator::SubmitVAEncMiscParamBuffer,
+ base::Unretained(vea_), VAEncMiscParameterTypeRateControl,
+ MakeRefCountedBytes(&rate_control_param, sizeof(rate_control_param))));
- current_pps_.seq_parameter_set_id = current_sps_.seq_parameter_set_id;
- current_pps_.pic_parameter_set_id = 0;
+ job->AddSetupCallback(base::BindOnce(
+ &VaapiVideoEncodeAccelerator::SubmitVAEncMiscParamBuffer,
+ base::Unretained(vea_), VAEncMiscParameterTypeFrameRate,
+ MakeRefCountedBytes(&framerate_param, sizeof(framerate_param))));
- current_pps_.entropy_coding_mode_flag =
- current_sps_.profile_idc >= H264SPS::kProfileIDCMain;
+ job->AddSetupCallback(
+ base::BindOnce(&VaapiVideoEncodeAccelerator::SubmitVAEncMiscParamBuffer,
+ base::Unretained(vea_), VAEncMiscParameterTypeHRD,
+ MakeRefCountedBytes(&hrd_param, sizeof(hrd_param))));
- CHECK_GT(max_ref_idx_l0_size_, 0u);
- current_pps_.num_ref_idx_l0_default_active_minus1 = max_ref_idx_l0_size_ - 1;
- current_pps_.num_ref_idx_l1_default_active_minus1 = 0;
- DCHECK_LE(qp_, 51u);
- current_pps_.pic_init_qp_minus26 = qp_ - 26;
- current_pps_.deblocking_filter_control_present_flag = true;
- current_pps_.transform_8x8_mode_flag =
- (current_sps_.profile_idc == H264SPS::kProfileIDCHigh);
+ return true;
}
-void VaapiVideoEncodeAccelerator::GeneratePackedPPS() {
- packed_pps_.Reset();
+scoped_refptr<H264Picture>
+VaapiVideoEncodeAccelerator::H264Accelerator::GetPicture(
+ AcceleratedVideoEncoder::EncodeJob* job) {
+ return base::MakeRefCounted<VaapiH264Picture>(
+ job->AsVaapiEncodeJob()->reconstructed_surface());
+}
- packed_pps_.BeginNALU(H264NALU::kPPS, 3);
+bool VaapiVideoEncodeAccelerator::H264Accelerator::SubmitPackedHeaders(
+ AcceleratedVideoEncoder::EncodeJob* job,
+ scoped_refptr<H264BitstreamBuffer> packed_sps,
+ scoped_refptr<H264BitstreamBuffer> packed_pps) {
+ // Submit SPS.
+ VAEncPackedHeaderParameterBuffer par_buffer = {};
+ par_buffer.type = VAEncPackedHeaderSequence;
+ par_buffer.bit_length = packed_sps->BytesInBuffer() * 8;
- packed_pps_.AppendUE(current_pps_.pic_parameter_set_id);
- packed_pps_.AppendUE(current_pps_.seq_parameter_set_id);
- packed_pps_.AppendBool(current_pps_.entropy_coding_mode_flag);
- packed_pps_.AppendBool(
- current_pps_.bottom_field_pic_order_in_frame_present_flag);
- CHECK_EQ(current_pps_.num_slice_groups_minus1, 0);
- packed_pps_.AppendUE(current_pps_.num_slice_groups_minus1);
+ job->AddSetupCallback(base::BindOnce(
+ &VaapiVideoEncodeAccelerator::SubmitBuffer, base::Unretained(vea_),
+ VAEncPackedHeaderParameterBufferType,
+ MakeRefCountedBytes(&par_buffer, sizeof(par_buffer))));
- packed_pps_.AppendUE(current_pps_.num_ref_idx_l0_default_active_minus1);
- packed_pps_.AppendUE(current_pps_.num_ref_idx_l1_default_active_minus1);
+ job->AddSetupCallback(
+ base::BindOnce(&VaapiVideoEncodeAccelerator::SubmitH264BitstreamBuffer,
+ base::Unretained(vea_), packed_sps));
- packed_pps_.AppendBool(current_pps_.weighted_pred_flag);
- packed_pps_.AppendBits(2, current_pps_.weighted_bipred_idc);
+ // Submit PPS.
+ par_buffer = {};
+ par_buffer.type = VAEncPackedHeaderPicture;
+ par_buffer.bit_length = packed_pps->BytesInBuffer() * 8;
- packed_pps_.AppendSE(current_pps_.pic_init_qp_minus26);
- packed_pps_.AppendSE(current_pps_.pic_init_qs_minus26);
- packed_pps_.AppendSE(current_pps_.chroma_qp_index_offset);
+ job->AddSetupCallback(base::BindOnce(
+ &VaapiVideoEncodeAccelerator::SubmitBuffer, base::Unretained(vea_),
+ VAEncPackedHeaderParameterBufferType,
+ MakeRefCountedBytes(&par_buffer, sizeof(par_buffer))));
- packed_pps_.AppendBool(current_pps_.deblocking_filter_control_present_flag);
- packed_pps_.AppendBool(current_pps_.constrained_intra_pred_flag);
- packed_pps_.AppendBool(current_pps_.redundant_pic_cnt_present_flag);
+ job->AddSetupCallback(
+ base::BindOnce(&VaapiVideoEncodeAccelerator::SubmitH264BitstreamBuffer,
+ base::Unretained(vea_), packed_pps));
- packed_pps_.AppendBool(current_pps_.transform_8x8_mode_flag);
- packed_pps_.AppendBool(current_pps_.pic_scaling_matrix_present_flag);
- DCHECK(!current_pps_.pic_scaling_matrix_present_flag);
- packed_pps_.AppendSE(current_pps_.second_chroma_qp_index_offset);
+ return true;
+}
- packed_pps_.FinishNALU();
+scoped_refptr<VP8Picture>
+VaapiVideoEncodeAccelerator::VP8Accelerator::GetPicture(
+ AcceleratedVideoEncoder::EncodeJob* job) {
+ return base::MakeRefCounted<VaapiVP8Picture>(
+ job->AsVaapiEncodeJob()->reconstructed_surface());
}
-void VaapiVideoEncodeAccelerator::SetState(State state) {
- // Only touch state on encoder thread, unless it's not running.
- if (encoder_thread_.IsRunning() &&
- !encoder_thread_task_runner_->BelongsToCurrentThread()) {
- encoder_thread_task_runner_->PostTask(
- FROM_HERE, base::Bind(&VaapiVideoEncodeAccelerator::SetState,
- base::Unretained(this), state));
- return;
+bool VaapiVideoEncodeAccelerator::VP8Accelerator::SubmitFrameParameters(
+ AcceleratedVideoEncoder::EncodeJob* job,
+ const media::VP8Encoder::EncodeParams& encode_params,
+ scoped_refptr<VP8Picture> pic,
+ const Vp8ReferenceFrameVector& ref_frames) {
+ VAEncSequenceParameterBufferVP8 seq_param = {};
+
+ const auto& frame_header = pic->frame_hdr;
+ seq_param.frame_width = frame_header->width;
+ seq_param.frame_height = frame_header->height;
+ seq_param.frame_width_scale = frame_header->horizontal_scale;
+ seq_param.frame_height_scale = frame_header->vertical_scale;
+ seq_param.error_resilient = 1;
+ seq_param.bits_per_second = encode_params.bitrate_bps;
+ seq_param.intra_period = encode_params.kf_period_frames;
+
+ VAEncPictureParameterBufferVP8 pic_param = {};
+
+ pic_param.reconstructed_frame = pic->AsVaapiVP8Picture()->GetVASurfaceID();
+ DCHECK_NE(pic_param.reconstructed_frame, VA_INVALID_ID);
+
+ auto last_frame = ref_frames.GetFrame(Vp8RefType::VP8_FRAME_LAST);
+ pic_param.ref_last_frame =
+ last_frame ? last_frame->AsVaapiVP8Picture()->GetVASurfaceID()
+ : VA_INVALID_ID;
+ auto golden_frame = ref_frames.GetFrame(Vp8RefType::VP8_FRAME_GOLDEN);
+ pic_param.ref_gf_frame =
+ golden_frame ? golden_frame->AsVaapiVP8Picture()->GetVASurfaceID()
+ : VA_INVALID_ID;
+ auto alt_frame = ref_frames.GetFrame(Vp8RefType::VP8_FRAME_ALTREF);
+ pic_param.ref_arf_frame =
+ alt_frame ? alt_frame->AsVaapiVP8Picture()->GetVASurfaceID()
+ : VA_INVALID_ID;
+ pic_param.coded_buf = job->AsVaapiEncodeJob()->coded_buffer_id();
+ DCHECK_NE(pic_param.coded_buf, VA_INVALID_ID);
+
+ if (frame_header->IsKeyframe())
+ pic_param.ref_flags.bits.force_kf = true;
+
+ pic_param.pic_flags.bits.frame_type = frame_header->frame_type;
+ pic_param.pic_flags.bits.version = frame_header->version;
+ pic_param.pic_flags.bits.show_frame = frame_header->show_frame;
+ pic_param.pic_flags.bits.loop_filter_type = frame_header->loopfilter_hdr.type;
+ pic_param.pic_flags.bits.num_token_partitions =
+ frame_header->num_of_dct_partitions;
+ pic_param.pic_flags.bits.segmentation_enabled =
+ frame_header->segmentation_hdr.segmentation_enabled;
+ pic_param.pic_flags.bits.update_mb_segmentation_map =
+ frame_header->segmentation_hdr.update_mb_segmentation_map;
+ pic_param.pic_flags.bits.update_segment_feature_data =
+ frame_header->segmentation_hdr.update_segment_feature_data;
+
+ pic_param.pic_flags.bits.loop_filter_adj_enable =
+ frame_header->loopfilter_hdr.loop_filter_adj_enable;
+
+ pic_param.pic_flags.bits.refresh_entropy_probs =
+ frame_header->refresh_entropy_probs;
+ pic_param.pic_flags.bits.refresh_golden_frame =
+ frame_header->refresh_golden_frame;
+ pic_param.pic_flags.bits.refresh_alternate_frame =
+ frame_header->refresh_alternate_frame;
+ pic_param.pic_flags.bits.refresh_last = frame_header->refresh_last;
+ pic_param.pic_flags.bits.copy_buffer_to_golden =
+ frame_header->copy_buffer_to_golden;
+ pic_param.pic_flags.bits.copy_buffer_to_alternate =
+ frame_header->copy_buffer_to_alternate;
+ pic_param.pic_flags.bits.sign_bias_golden = frame_header->sign_bias_golden;
+ pic_param.pic_flags.bits.sign_bias_alternate =
+ frame_header->sign_bias_alternate;
+ pic_param.pic_flags.bits.mb_no_coeff_skip = frame_header->mb_no_skip_coeff;
+ if (frame_header->IsKeyframe())
+ pic_param.pic_flags.bits.forced_lf_adjustment = true;
+
+ static_assert(
+ arraysize(pic_param.loop_filter_level) ==
+ arraysize(pic_param.ref_lf_delta) &&
+ arraysize(pic_param.ref_lf_delta) ==
+ arraysize(pic_param.mode_lf_delta) &&
+ arraysize(pic_param.ref_lf_delta) ==
+ arraysize(frame_header->loopfilter_hdr.ref_frame_delta) &&
+ arraysize(pic_param.mode_lf_delta) ==
+ arraysize(frame_header->loopfilter_hdr.mb_mode_delta),
+ "Invalid loop filter array sizes");
+
+ for (size_t i = 0; i < base::size(pic_param.loop_filter_level); ++i) {
+ pic_param.loop_filter_level[i] = frame_header->loopfilter_hdr.level;
+ pic_param.ref_lf_delta[i] = frame_header->loopfilter_hdr.ref_frame_delta[i];
+ pic_param.mode_lf_delta[i] = frame_header->loopfilter_hdr.mb_mode_delta[i];
}
- VLOGF(2) << "setting state to: " << state;
- state_ = state;
-}
+ pic_param.sharpness_level = frame_header->loopfilter_hdr.sharpness_level;
+ pic_param.clamp_qindex_high = encode_params.max_qp;
+ pic_param.clamp_qindex_low = encode_params.min_qp;
+
+ VAQMatrixBufferVP8 qmatrix_buf = {};
+ for (size_t i = 0; i < base::size(qmatrix_buf.quantization_index); ++i)
+ qmatrix_buf.quantization_index[i] = frame_header->quantization_hdr.y_ac_qi;
+
+ qmatrix_buf.quantization_index_delta[0] =
+ frame_header->quantization_hdr.y_dc_delta;
+ qmatrix_buf.quantization_index_delta[1] =
+ frame_header->quantization_hdr.y2_dc_delta;
+ qmatrix_buf.quantization_index_delta[2] =
+ frame_header->quantization_hdr.y2_ac_delta;
+ qmatrix_buf.quantization_index_delta[3] =
+ frame_header->quantization_hdr.uv_dc_delta;
+ qmatrix_buf.quantization_index_delta[4] =
+ frame_header->quantization_hdr.uv_ac_delta;
+
+ VAEncMiscParameterRateControl rate_control_param = {};
+ rate_control_param.bits_per_second = encode_params.bitrate_bps;
+ rate_control_param.target_percentage = kTargetBitratePercentage;
+ rate_control_param.window_size = encode_params.cpb_window_size_ms;
+ rate_control_param.initial_qp = encode_params.initial_qp;
+ rate_control_param.rc_flags.bits.disable_frame_skip = true;
-void VaapiVideoEncodeAccelerator::NotifyError(Error error) {
- if (!child_task_runner_->BelongsToCurrentThread()) {
- child_task_runner_->PostTask(
- FROM_HERE, base::Bind(&VaapiVideoEncodeAccelerator::NotifyError,
- weak_this_, error));
- return;
- }
+ VAEncMiscParameterFrameRate framerate_param = {};
+ framerate_param.framerate = encode_params.framerate;
- if (client_) {
- client_->NotifyError(error);
- client_ptr_factory_.reset();
- }
-}
+ VAEncMiscParameterHRD hrd_param = {};
+ hrd_param.buffer_size = encode_params.cpb_size_bits;
+ hrd_param.initial_buffer_fullness = hrd_param.buffer_size / 2;
+
+ job->AddSetupCallback(
+ base::BindOnce(&VaapiVideoEncodeAccelerator::SubmitBuffer,
+ base::Unretained(vea_), VAEncSequenceParameterBufferType,
+ MakeRefCountedBytes(&seq_param, sizeof(seq_param))));
+
+ job->AddSetupCallback(
+ base::BindOnce(&VaapiVideoEncodeAccelerator::SubmitBuffer,
+ base::Unretained(vea_), VAEncPictureParameterBufferType,
+ MakeRefCountedBytes(&pic_param, sizeof(pic_param))));
-VaapiVideoEncodeAccelerator::EncodeJob::EncodeJob()
- : coded_buffer(VA_INVALID_ID), keyframe(false) {}
+ job->AddSetupCallback(
+ base::BindOnce(&VaapiVideoEncodeAccelerator::SubmitBuffer,
+ base::Unretained(vea_), VAQMatrixBufferType,
+ MakeRefCountedBytes(&qmatrix_buf, sizeof(qmatrix_buf))));
-VaapiVideoEncodeAccelerator::EncodeJob::~EncodeJob() {}
+ job->AddSetupCallback(base::BindOnce(
+ &VaapiVideoEncodeAccelerator::SubmitVAEncMiscParamBuffer,
+ base::Unretained(vea_), VAEncMiscParameterTypeRateControl,
+ MakeRefCountedBytes(&rate_control_param, sizeof(rate_control_param))));
+
+ job->AddSetupCallback(base::BindOnce(
+ &VaapiVideoEncodeAccelerator::SubmitVAEncMiscParamBuffer,
+ base::Unretained(vea_), VAEncMiscParameterTypeFrameRate,
+ MakeRefCountedBytes(&framerate_param, sizeof(framerate_param))));
+
+ job->AddSetupCallback(
+ base::BindOnce(&VaapiVideoEncodeAccelerator::SubmitVAEncMiscParamBuffer,
+ base::Unretained(vea_), VAEncMiscParameterTypeHRD,
+ MakeRefCountedBytes(&hrd_param, sizeof(hrd_param))));
+
+ return true;
+}
} // namespace media