Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / content / common / gpu / media / vaapi_video_encode_accelerator.cc
blob1f9388e33abb400d88fbd28fbfdc27f228d1bb8a
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/common/gpu/media/vaapi_video_encode_accelerator.h"
7 #include "base/bind.h"
8 #include "base/callback.h"
9 #include "base/metrics/histogram.h"
10 #include "base/numerics/safe_conversions.h"
11 #include "content/common/gpu/media/h264_dpb.h"
12 #include "media/base/bind_to_current_loop.h"
13 #include "third_party/libva/va/va_enc_h264.h"
15 #define DVLOGF(level) DVLOG(level) << __FUNCTION__ << "(): "
17 #define NOTIFY_ERROR(error, msg) \
18 do { \
19 SetState(kError); \
20 LOG(ERROR) << msg; \
21 LOG(ERROR) << "Calling NotifyError(" << error << ")";\
22 NotifyError(error); \
23 } while (0)
25 namespace content {
27 namespace {
28 // Need 2 surfaces for each frame: one for input data and one for
29 // reconstructed picture, which is later used for reference.
30 const size_t kMinSurfacesToEncode = 2;
32 // Subjectively chosen.
33 const size_t kNumInputBuffers = 4;
34 const size_t kMaxNumReferenceFrames = 4;
36 // We need up to kMaxNumReferenceFrames surfaces for reference, plus one
37 // for input and one for encode (which will be added to the set of reference
38 // frames for subsequent frames). Actual execution of HW encode is done
39 // in parallel, and we want to process more frames in the meantime.
40 // To have kNumInputBuffers in flight, we need a full set of reference +
41 // encode surfaces (i.e. kMaxNumReferenceFrames + kMinSurfacesToEncode), and
42 // (kNumInputBuffers - 1) of kMinSurfacesToEncode for the remaining frames
43 // in flight.
44 const size_t kNumSurfaces = kMaxNumReferenceFrames + kMinSurfacesToEncode +
45 kMinSurfacesToEncode * (kNumInputBuffers - 1);
47 // An IDR every 2048 frames, an I frame every 256 and no B frames.
48 // We choose IDR period to equal MaxFrameNum so it must be a power of 2.
49 const int kIDRPeriod = 2048;
50 const int kIPeriod = 256;
51 const int kIPPeriod = 1;
53 const int kDefaultFramerate = 30;
55 // HRD parameters (ch. E.2.2 in spec).
56 const int kBitRateScale = 0; // bit_rate_scale for SPS HRD parameters.
57 const int kCPBSizeScale = 0; // cpb_size_scale for SPS HRD parameters.
59 const int kDefaultQP = 26;
60 // All Intel codecs can do at least 4.1.
61 const int kDefaultLevelIDC = 41;
62 const int kChromaFormatIDC = 1; // 4:2:0
64 // Arbitrarily chosen bitrate window size for rate control, in ms.
65 const int kCPBWindowSizeMs = 1500;
67 // UMA errors that the VaapiVideoEncodeAccelerator class reports.
68 enum VAVEAEncoderFailure {
69 VAAPI_ERROR = 0,
70 VAVEA_ENCODER_FAILURES_MAX,
75 // Round |value| up to |alignment|, which must be a power of 2.
76 static inline size_t RoundUpToPowerOf2(size_t value, size_t alignment) {
77 // Check that |alignment| is a power of 2.
78 DCHECK((alignment + (alignment - 1)) == (alignment | (alignment - 1)));
79 return ((value + (alignment - 1)) & ~(alignment - 1));
82 static void ReportToUMA(VAVEAEncoderFailure failure) {
83 UMA_HISTOGRAM_ENUMERATION(
84 "Media.VAVEA.EncoderFailure",
85 failure,
86 VAVEA_ENCODER_FAILURES_MAX);
89 struct VaapiVideoEncodeAccelerator::InputFrameRef {
90 InputFrameRef(const scoped_refptr<media::VideoFrame>& frame,
91 bool force_keyframe)
92 : frame(frame), force_keyframe(force_keyframe) {}
93 const scoped_refptr<media::VideoFrame> frame;
94 const bool force_keyframe;
97 struct VaapiVideoEncodeAccelerator::BitstreamBufferRef {
98 BitstreamBufferRef(int32 id, scoped_ptr<base::SharedMemory> shm, size_t size)
99 : id(id), shm(shm.Pass()), size(size) {}
100 const int32 id;
101 const scoped_ptr<base::SharedMemory> shm;
102 const size_t size;
105 media::VideoEncodeAccelerator::SupportedProfiles
106 VaapiVideoEncodeAccelerator::GetSupportedProfiles() {
107 return VaapiWrapper::GetSupportedEncodeProfiles();
110 static unsigned int Log2OfPowerOf2(unsigned int x) {
111 CHECK_GT(x, 0u);
112 DCHECK_EQ(x & (x - 1), 0u);
114 int log = 0;
115 while (x > 1) {
116 x >>= 1;
117 ++log;
119 return log;
122 VaapiVideoEncodeAccelerator::VaapiVideoEncodeAccelerator()
123 : profile_(media::VIDEO_CODEC_PROFILE_UNKNOWN),
124 mb_width_(0),
125 mb_height_(0),
126 output_buffer_byte_size_(0),
127 state_(kUninitialized),
128 frame_num_(0),
129 idr_pic_id_(0),
130 bitrate_(0),
131 framerate_(0),
132 cpb_size_(0),
133 encoding_parameters_changed_(false),
134 encoder_thread_("VAVEAEncoderThread"),
135 child_task_runner_(base::ThreadTaskRunnerHandle::Get()),
136 weak_this_ptr_factory_(this) {
137 DVLOGF(4);
138 weak_this_ = weak_this_ptr_factory_.GetWeakPtr();
140 max_ref_idx_l0_size_ = kMaxNumReferenceFrames;
141 qp_ = kDefaultQP;
142 idr_period_ = kIDRPeriod;
143 i_period_ = kIPeriod;
144 ip_period_ = kIPPeriod;
147 VaapiVideoEncodeAccelerator::~VaapiVideoEncodeAccelerator() {
148 DVLOGF(4);
149 DCHECK(child_task_runner_->BelongsToCurrentThread());
150 DCHECK(!encoder_thread_.IsRunning());
153 bool VaapiVideoEncodeAccelerator::Initialize(
154 media::VideoPixelFormat format,
155 const gfx::Size& input_visible_size,
156 media::VideoCodecProfile output_profile,
157 uint32 initial_bitrate,
158 Client* client) {
159 DCHECK(child_task_runner_->BelongsToCurrentThread());
160 DCHECK(!encoder_thread_.IsRunning());
161 DCHECK_EQ(state_, kUninitialized);
163 DVLOGF(1) << "Initializing VAVEA, input_format: "
164 << media::VideoPixelFormatToString(format)
165 << ", input_visible_size: " << input_visible_size.ToString()
166 << ", output_profile: " << output_profile
167 << ", initial_bitrate: " << initial_bitrate;
169 client_ptr_factory_.reset(new base::WeakPtrFactory<Client>(client));
170 client_ = client_ptr_factory_->GetWeakPtr();
172 if (output_profile < media::H264PROFILE_BASELINE ||
173 output_profile > media::H264PROFILE_MAIN) {
174 DVLOGF(1) << "Unsupported output profile: " << output_profile;
175 return false;
178 if (format != media::PIXEL_FORMAT_I420) {
179 DVLOGF(1) << "Unsupported input format: "
180 << media::VideoPixelFormatToString(format);
181 return false;
184 profile_ = output_profile;
185 visible_size_ = input_visible_size;
186 // 4:2:0 format has to be 2-aligned.
187 DCHECK_EQ(visible_size_.width() % 2, 0);
188 DCHECK_EQ(visible_size_.height() % 2, 0);
189 coded_size_ = gfx::Size(RoundUpToPowerOf2(visible_size_.width(), 16),
190 RoundUpToPowerOf2(visible_size_.height(), 16));
191 mb_width_ = coded_size_.width() / 16;
192 mb_height_ = coded_size_.height() / 16;
193 output_buffer_byte_size_ = coded_size_.GetArea();
195 UpdateRates(initial_bitrate, kDefaultFramerate);
197 vaapi_wrapper_ =
198 VaapiWrapper::CreateForVideoCodec(VaapiWrapper::kEncode, output_profile,
199 base::Bind(&ReportToUMA, VAAPI_ERROR));
200 if (!vaapi_wrapper_.get()) {
201 DVLOGF(1) << "Failed initializing VAAPI for profile " << output_profile;
202 return false;
205 if (!encoder_thread_.Start()) {
206 LOG(ERROR) << "Failed to start encoder thread";
207 return false;
209 encoder_thread_task_runner_ = encoder_thread_.task_runner();
211 // Finish the remaining initialization on the encoder thread.
212 encoder_thread_task_runner_->PostTask(
213 FROM_HERE, base::Bind(&VaapiVideoEncodeAccelerator::InitializeTask,
214 base::Unretained(this)));
216 return true;
219 void VaapiVideoEncodeAccelerator::InitializeTask() {
220 DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread());
221 DCHECK_EQ(state_, kUninitialized);
222 DVLOGF(4);
224 va_surface_release_cb_ = media::BindToCurrentLoop(
225 base::Bind(&VaapiVideoEncodeAccelerator::RecycleVASurfaceID,
226 base::Unretained(this)));
228 if (!vaapi_wrapper_->CreateSurfaces(VA_RT_FORMAT_YUV420, coded_size_,
229 kNumSurfaces,
230 &available_va_surface_ids_)) {
231 NOTIFY_ERROR(kPlatformFailureError, "Failed creating VASurfaces");
232 return;
235 UpdateSPS();
236 GeneratePackedSPS();
238 UpdatePPS();
239 GeneratePackedPPS();
241 child_task_runner_->PostTask(
242 FROM_HERE,
243 base::Bind(&Client::RequireBitstreamBuffers, client_, kNumInputBuffers,
244 coded_size_, output_buffer_byte_size_));
246 SetState(kEncoding);
249 void VaapiVideoEncodeAccelerator::RecycleVASurfaceID(
250 VASurfaceID va_surface_id) {
251 DVLOGF(4) << "va_surface_id: " << va_surface_id;
252 DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread());
254 available_va_surface_ids_.push_back(va_surface_id);
255 EncodeFrameTask();
258 void VaapiVideoEncodeAccelerator::BeginFrame(bool force_keyframe) {
259 current_pic_ = new H264Picture();
261 // If the current picture is an IDR picture, frame_num shall be equal to 0.
262 if (force_keyframe)
263 frame_num_ = 0;
265 current_pic_->frame_num = frame_num_++;
266 frame_num_ %= idr_period_;
268 if (current_pic_->frame_num == 0) {
269 current_pic_->idr = true;
270 // H264 spec mandates idr_pic_id to differ between two consecutive IDRs.
271 idr_pic_id_ ^= 1;
272 ref_pic_list0_.clear();
275 if (current_pic_->frame_num % i_period_ == 0)
276 current_pic_->type = media::H264SliceHeader::kISlice;
277 else
278 current_pic_->type = media::H264SliceHeader::kPSlice;
280 if (current_pic_->type != media::H264SliceHeader::kBSlice)
281 current_pic_->ref = true;
283 current_pic_->pic_order_cnt = current_pic_->frame_num * 2;
284 current_pic_->top_field_order_cnt = current_pic_->pic_order_cnt;
285 current_pic_->pic_order_cnt_lsb = current_pic_->pic_order_cnt;
287 current_encode_job_->keyframe = current_pic_->idr;
289 DVLOGF(4) << "Starting a new frame, type: " << current_pic_->type
290 << (force_keyframe ? " (forced keyframe)" : "")
291 << " frame_num: " << current_pic_->frame_num
292 << " POC: " << current_pic_->pic_order_cnt;
295 void VaapiVideoEncodeAccelerator::EndFrame() {
296 DCHECK(current_pic_);
297 // Store the picture on the list of reference pictures and keep the list
298 // below maximum size, dropping oldest references.
299 if (current_pic_->ref)
300 ref_pic_list0_.push_front(current_encode_job_->recon_surface);
301 size_t max_num_ref_frames =
302 base::checked_cast<size_t>(current_sps_.max_num_ref_frames);
303 while (ref_pic_list0_.size() > max_num_ref_frames)
304 ref_pic_list0_.pop_back();
306 submitted_encode_jobs_.push(make_linked_ptr(current_encode_job_.release()));
309 static void InitVAPicture(VAPictureH264* va_pic) {
310 memset(va_pic, 0, sizeof(*va_pic));
311 va_pic->picture_id = VA_INVALID_ID;
312 va_pic->flags = VA_PICTURE_H264_INVALID;
315 bool VaapiVideoEncodeAccelerator::SubmitFrameParameters() {
316 DCHECK(current_pic_);
317 VAEncSequenceParameterBufferH264 seq_param;
318 memset(&seq_param, 0, sizeof(seq_param));
320 #define SPS_TO_SP(a) seq_param.a = current_sps_.a;
321 SPS_TO_SP(seq_parameter_set_id);
322 SPS_TO_SP(level_idc);
324 seq_param.intra_period = i_period_;
325 seq_param.intra_idr_period = idr_period_;
326 seq_param.ip_period = ip_period_;
327 seq_param.bits_per_second = bitrate_;
329 SPS_TO_SP(max_num_ref_frames);
330 seq_param.picture_width_in_mbs = mb_width_;
331 seq_param.picture_height_in_mbs = mb_height_;
333 #define SPS_TO_SP_FS(a) seq_param.seq_fields.bits.a = current_sps_.a;
334 SPS_TO_SP_FS(chroma_format_idc);
335 SPS_TO_SP_FS(frame_mbs_only_flag);
336 SPS_TO_SP_FS(log2_max_frame_num_minus4);
337 SPS_TO_SP_FS(pic_order_cnt_type);
338 SPS_TO_SP_FS(log2_max_pic_order_cnt_lsb_minus4);
339 #undef SPS_TO_SP_FS
341 SPS_TO_SP(bit_depth_luma_minus8);
342 SPS_TO_SP(bit_depth_chroma_minus8);
344 SPS_TO_SP(frame_cropping_flag);
345 if (current_sps_.frame_cropping_flag) {
346 SPS_TO_SP(frame_crop_left_offset);
347 SPS_TO_SP(frame_crop_right_offset);
348 SPS_TO_SP(frame_crop_top_offset);
349 SPS_TO_SP(frame_crop_bottom_offset);
352 SPS_TO_SP(vui_parameters_present_flag);
353 #define SPS_TO_SP_VF(a) seq_param.vui_fields.bits.a = current_sps_.a;
354 SPS_TO_SP_VF(timing_info_present_flag);
355 #undef SPS_TO_SP_VF
356 SPS_TO_SP(num_units_in_tick);
357 SPS_TO_SP(time_scale);
358 #undef SPS_TO_SP
360 if (!vaapi_wrapper_->SubmitBuffer(VAEncSequenceParameterBufferType,
361 sizeof(seq_param),
362 &seq_param))
363 return false;
365 VAEncPictureParameterBufferH264 pic_param;
366 memset(&pic_param, 0, sizeof(pic_param));
368 pic_param.CurrPic.picture_id = current_encode_job_->recon_surface->id();
369 pic_param.CurrPic.TopFieldOrderCnt = current_pic_->top_field_order_cnt;
370 pic_param.CurrPic.BottomFieldOrderCnt = current_pic_->bottom_field_order_cnt;
371 pic_param.CurrPic.flags = 0;
373 for (size_t i = 0; i < arraysize(pic_param.ReferenceFrames); ++i)
374 InitVAPicture(&pic_param.ReferenceFrames[i]);
376 DCHECK_LE(ref_pic_list0_.size(), arraysize(pic_param.ReferenceFrames));
377 RefPicList::const_iterator iter = ref_pic_list0_.begin();
378 for (size_t i = 0;
379 i < arraysize(pic_param.ReferenceFrames) && iter != ref_pic_list0_.end();
380 ++iter, ++i) {
381 pic_param.ReferenceFrames[i].picture_id = (*iter)->id();
382 pic_param.ReferenceFrames[i].flags = 0;
385 pic_param.coded_buf = current_encode_job_->coded_buffer;
386 pic_param.pic_parameter_set_id = current_pps_.pic_parameter_set_id;
387 pic_param.seq_parameter_set_id = current_pps_.seq_parameter_set_id;
388 pic_param.frame_num = current_pic_->frame_num;
389 pic_param.pic_init_qp = qp_;
390 pic_param.num_ref_idx_l0_active_minus1 = max_ref_idx_l0_size_ - 1;
391 pic_param.pic_fields.bits.idr_pic_flag = current_pic_->idr;
392 pic_param.pic_fields.bits.reference_pic_flag = current_pic_->ref;
393 #define PPS_TO_PP_PF(a) pic_param.pic_fields.bits.a = current_pps_.a;
394 PPS_TO_PP_PF(entropy_coding_mode_flag);
395 PPS_TO_PP_PF(transform_8x8_mode_flag);
396 PPS_TO_PP_PF(deblocking_filter_control_present_flag);
397 #undef PPS_TO_PP_PF
399 if (!vaapi_wrapper_->SubmitBuffer(VAEncPictureParameterBufferType,
400 sizeof(pic_param),
401 &pic_param))
402 return false;
404 VAEncSliceParameterBufferH264 slice_param;
405 memset(&slice_param, 0, sizeof(slice_param));
407 slice_param.num_macroblocks = mb_width_ * mb_height_;
408 slice_param.macroblock_info = VA_INVALID_ID;
409 slice_param.slice_type = current_pic_->type;
410 slice_param.pic_parameter_set_id = current_pps_.pic_parameter_set_id;
411 slice_param.idr_pic_id = idr_pic_id_;
412 slice_param.pic_order_cnt_lsb = current_pic_->pic_order_cnt_lsb;
413 slice_param.num_ref_idx_active_override_flag = true;
415 for (size_t i = 0; i < arraysize(slice_param.RefPicList0); ++i)
416 InitVAPicture(&slice_param.RefPicList0[i]);
418 for (size_t i = 0; i < arraysize(slice_param.RefPicList1); ++i)
419 InitVAPicture(&slice_param.RefPicList1[i]);
421 DCHECK_LE(ref_pic_list0_.size(), arraysize(slice_param.RefPicList0));
422 iter = ref_pic_list0_.begin();
423 for (size_t i = 0;
424 i < arraysize(slice_param.RefPicList0) && iter != ref_pic_list0_.end();
425 ++iter, ++i) {
426 InitVAPicture(&slice_param.RefPicList0[i]);
427 slice_param.RefPicList0[i].picture_id = (*iter)->id();
428 slice_param.RefPicList0[i].flags = 0;
431 if (!vaapi_wrapper_->SubmitBuffer(VAEncSliceParameterBufferType,
432 sizeof(slice_param),
433 &slice_param))
434 return false;
436 VAEncMiscParameterRateControl rate_control_param;
437 memset(&rate_control_param, 0, sizeof(rate_control_param));
438 rate_control_param.bits_per_second = bitrate_;
439 rate_control_param.target_percentage = 90;
440 rate_control_param.window_size = kCPBWindowSizeMs;
441 rate_control_param.initial_qp = qp_;
442 rate_control_param.rc_flags.bits.disable_frame_skip = true;
444 if (!vaapi_wrapper_->SubmitVAEncMiscParamBuffer(
445 VAEncMiscParameterTypeRateControl,
446 sizeof(rate_control_param),
447 &rate_control_param))
448 return false;
450 VAEncMiscParameterFrameRate framerate_param;
451 memset(&framerate_param, 0, sizeof(framerate_param));
452 framerate_param.framerate = framerate_;
453 if (!vaapi_wrapper_->SubmitVAEncMiscParamBuffer(
454 VAEncMiscParameterTypeFrameRate,
455 sizeof(framerate_param),
456 &framerate_param))
457 return false;
459 VAEncMiscParameterHRD hrd_param;
460 memset(&hrd_param, 0, sizeof(hrd_param));
461 hrd_param.buffer_size = cpb_size_;
462 hrd_param.initial_buffer_fullness = cpb_size_ / 2;
463 if (!vaapi_wrapper_->SubmitVAEncMiscParamBuffer(VAEncMiscParameterTypeHRD,
464 sizeof(hrd_param),
465 &hrd_param))
466 return false;
468 return true;
471 bool VaapiVideoEncodeAccelerator::SubmitHeadersIfNeeded() {
472 DCHECK(current_pic_);
473 if (current_pic_->type != media::H264SliceHeader::kISlice)
474 return true;
476 // Submit PPS.
477 VAEncPackedHeaderParameterBuffer par_buffer;
478 memset(&par_buffer, 0, sizeof(par_buffer));
479 par_buffer.type = VAEncPackedHeaderSequence;
480 par_buffer.bit_length = packed_sps_.BytesInBuffer() * 8;
482 if (!vaapi_wrapper_->SubmitBuffer(VAEncPackedHeaderParameterBufferType,
483 sizeof(par_buffer),
484 &par_buffer))
485 return false;
487 if (!vaapi_wrapper_->SubmitBuffer(VAEncPackedHeaderDataBufferType,
488 packed_sps_.BytesInBuffer(),
489 packed_sps_.data()))
490 return false;
492 // Submit PPS.
493 memset(&par_buffer, 0, sizeof(par_buffer));
494 par_buffer.type = VAEncPackedHeaderPicture;
495 par_buffer.bit_length = packed_pps_.BytesInBuffer() * 8;
497 if (!vaapi_wrapper_->SubmitBuffer(VAEncPackedHeaderParameterBufferType,
498 sizeof(par_buffer),
499 &par_buffer))
500 return false;
502 if (!vaapi_wrapper_->SubmitBuffer(VAEncPackedHeaderDataBufferType,
503 packed_pps_.BytesInBuffer(),
504 packed_pps_.data()))
505 return false;
507 return true;
510 bool VaapiVideoEncodeAccelerator::ExecuteEncode() {
511 DCHECK(current_pic_);
512 DVLOGF(3) << "Encoding frame_num: " << current_pic_->frame_num;
513 return vaapi_wrapper_->ExecuteAndDestroyPendingBuffers(
514 current_encode_job_->input_surface->id());
517 bool VaapiVideoEncodeAccelerator::UploadFrame(
518 const scoped_refptr<media::VideoFrame>& frame) {
519 return vaapi_wrapper_->UploadVideoFrameToSurface(
520 frame, current_encode_job_->input_surface->id());
523 void VaapiVideoEncodeAccelerator::TryToReturnBitstreamBuffer() {
524 DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread());
526 if (state_ != kEncoding)
527 return;
529 if (submitted_encode_jobs_.empty() || available_bitstream_buffers_.empty())
530 return;
532 linked_ptr<BitstreamBufferRef> buffer = available_bitstream_buffers_.front();
533 available_bitstream_buffers_.pop();
535 uint8* target_data = reinterpret_cast<uint8*>(buffer->shm->memory());
537 linked_ptr<EncodeJob> encode_job = submitted_encode_jobs_.front();
538 submitted_encode_jobs_.pop();
540 size_t data_size = 0;
541 if (!vaapi_wrapper_->DownloadAndDestroyCodedBuffer(
542 encode_job->coded_buffer,
543 encode_job->input_surface->id(),
544 target_data,
545 buffer->size,
546 &data_size)) {
547 NOTIFY_ERROR(kPlatformFailureError, "Failed downloading coded buffer");
548 return;
551 DVLOGF(3) << "Returning bitstream buffer "
552 << (encode_job->keyframe ? "(keyframe)" : "")
553 << " id: " << buffer->id << " size: " << data_size;
555 child_task_runner_->PostTask(
556 FROM_HERE, base::Bind(&Client::BitstreamBufferReady, client_, buffer->id,
557 data_size, encode_job->keyframe));
560 void VaapiVideoEncodeAccelerator::Encode(
561 const scoped_refptr<media::VideoFrame>& frame,
562 bool force_keyframe) {
563 DVLOGF(3) << "Frame timestamp: " << frame->timestamp().InMilliseconds()
564 << " force_keyframe: " << force_keyframe;
565 DCHECK(child_task_runner_->BelongsToCurrentThread());
567 encoder_thread_task_runner_->PostTask(
568 FROM_HERE, base::Bind(&VaapiVideoEncodeAccelerator::EncodeTask,
569 base::Unretained(this), frame, force_keyframe));
572 bool VaapiVideoEncodeAccelerator::PrepareNextJob() {
573 if (available_va_surface_ids_.size() < kMinSurfacesToEncode)
574 return false;
576 DCHECK(!current_encode_job_);
577 current_encode_job_.reset(new EncodeJob());
579 if (!vaapi_wrapper_->CreateCodedBuffer(output_buffer_byte_size_,
580 &current_encode_job_->coded_buffer)) {
581 NOTIFY_ERROR(kPlatformFailureError, "Failed creating coded buffer");
582 return false;
585 current_encode_job_->input_surface = new VASurface(
586 available_va_surface_ids_.back(), coded_size_, va_surface_release_cb_);
587 available_va_surface_ids_.pop_back();
589 current_encode_job_->recon_surface = new VASurface(
590 available_va_surface_ids_.back(), coded_size_, va_surface_release_cb_);
591 available_va_surface_ids_.pop_back();
593 // Reference surfaces are needed until the job is done, but they get
594 // removed from ref_pic_list0_ when it's full at the end of job submission.
595 // Keep refs to them along with the job and only release after sync.
596 current_encode_job_->reference_surfaces = ref_pic_list0_;
598 return true;
601 void VaapiVideoEncodeAccelerator::EncodeTask(
602 const scoped_refptr<media::VideoFrame>& frame,
603 bool force_keyframe) {
604 DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread());
605 DCHECK_NE(state_, kUninitialized);
607 encoder_input_queue_.push(
608 make_linked_ptr(new InputFrameRef(frame, force_keyframe)));
609 EncodeFrameTask();
612 void VaapiVideoEncodeAccelerator::EncodeFrameTask() {
613 DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread());
615 if (state_ != kEncoding || encoder_input_queue_.empty())
616 return;
618 if (!PrepareNextJob()) {
619 DVLOGF(4) << "Not ready for next frame yet";
620 return;
623 linked_ptr<InputFrameRef> frame_ref = encoder_input_queue_.front();
624 encoder_input_queue_.pop();
626 if (!UploadFrame(frame_ref->frame)) {
627 NOTIFY_ERROR(kPlatformFailureError, "Failed uploading source frame to HW.");
628 return;
631 BeginFrame(frame_ref->force_keyframe || encoding_parameters_changed_);
632 encoding_parameters_changed_ = false;
634 if (!SubmitFrameParameters()) {
635 NOTIFY_ERROR(kPlatformFailureError, "Failed submitting frame parameters.");
636 return;
639 if (!SubmitHeadersIfNeeded()) {
640 NOTIFY_ERROR(kPlatformFailureError, "Failed submitting frame headers.");
641 return;
644 if (!ExecuteEncode()) {
645 NOTIFY_ERROR(kPlatformFailureError, "Failed submitting encode job to HW.");
646 return;
649 EndFrame();
650 TryToReturnBitstreamBuffer();
653 void VaapiVideoEncodeAccelerator::UseOutputBitstreamBuffer(
654 const media::BitstreamBuffer& buffer) {
655 DVLOGF(4) << "id: " << buffer.id();
656 DCHECK(child_task_runner_->BelongsToCurrentThread());
658 if (buffer.size() < output_buffer_byte_size_) {
659 NOTIFY_ERROR(kInvalidArgumentError, "Provided bitstream buffer too small");
660 return;
663 scoped_ptr<base::SharedMemory> shm(
664 new base::SharedMemory(buffer.handle(), false));
665 if (!shm->Map(buffer.size())) {
666 NOTIFY_ERROR(kPlatformFailureError, "Failed mapping shared memory.");
667 return;
670 scoped_ptr<BitstreamBufferRef> buffer_ref(
671 new BitstreamBufferRef(buffer.id(), shm.Pass(), buffer.size()));
673 encoder_thread_task_runner_->PostTask(
674 FROM_HERE,
675 base::Bind(&VaapiVideoEncodeAccelerator::UseOutputBitstreamBufferTask,
676 base::Unretained(this), base::Passed(&buffer_ref)));
679 void VaapiVideoEncodeAccelerator::UseOutputBitstreamBufferTask(
680 scoped_ptr<BitstreamBufferRef> buffer_ref) {
681 DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread());
682 DCHECK_NE(state_, kUninitialized);
684 available_bitstream_buffers_.push(make_linked_ptr(buffer_ref.release()));
685 TryToReturnBitstreamBuffer();
688 void VaapiVideoEncodeAccelerator::RequestEncodingParametersChange(
689 uint32 bitrate,
690 uint32 framerate) {
691 DVLOGF(2) << "bitrate: " << bitrate << " framerate: " << framerate;
692 DCHECK(child_task_runner_->BelongsToCurrentThread());
694 encoder_thread_task_runner_->PostTask(
695 FROM_HERE,
696 base::Bind(
697 &VaapiVideoEncodeAccelerator::RequestEncodingParametersChangeTask,
698 base::Unretained(this), bitrate, framerate));
701 void VaapiVideoEncodeAccelerator::UpdateRates(uint32 bitrate,
702 uint32 framerate) {
703 if (encoder_thread_.IsRunning())
704 DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread());
705 DCHECK_NE(bitrate, 0u);
706 DCHECK_NE(framerate, 0u);
707 bitrate_ = bitrate;
708 framerate_ = framerate;
709 cpb_size_ = bitrate_ * kCPBWindowSizeMs / 1000;
712 void VaapiVideoEncodeAccelerator::RequestEncodingParametersChangeTask(
713 uint32 bitrate,
714 uint32 framerate) {
715 DVLOGF(2) << "bitrate: " << bitrate << " framerate: " << framerate;
716 DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread());
717 DCHECK_NE(state_, kUninitialized);
719 // This is a workaround to zero being temporarily, as part of the initial
720 // setup, provided by the webrtc video encode and a zero bitrate and
721 // framerate not being accepted by VAAPI
722 // TODO: This code is common with v4l2_video_encode_accelerator.cc, perhaps
723 // it could be pulled up to RTCVideoEncoder
724 if (bitrate < 1)
725 bitrate = 1;
726 if (framerate < 1)
727 framerate = 1;
729 if (bitrate_ == bitrate && framerate_ == framerate)
730 return;
732 UpdateRates(bitrate, framerate);
734 UpdateSPS();
735 GeneratePackedSPS();
737 // Submit new parameters along with next frame that will be processed.
738 encoding_parameters_changed_ = true;
741 void VaapiVideoEncodeAccelerator::Destroy() {
742 DCHECK(child_task_runner_->BelongsToCurrentThread());
744 // Can't call client anymore after Destroy() returns.
745 client_ptr_factory_.reset();
746 weak_this_ptr_factory_.InvalidateWeakPtrs();
748 // Early-exit encoder tasks if they are running and join the thread.
749 if (encoder_thread_.IsRunning()) {
750 encoder_thread_.message_loop()->PostTask(
751 FROM_HERE,
752 base::Bind(&VaapiVideoEncodeAccelerator::DestroyTask,
753 base::Unretained(this)));
754 encoder_thread_.Stop();
757 delete this;
760 void VaapiVideoEncodeAccelerator::DestroyTask() {
761 DVLOGF(2);
762 DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread());
763 SetState(kError);
766 void VaapiVideoEncodeAccelerator::UpdateSPS() {
767 memset(&current_sps_, 0, sizeof(media::H264SPS));
769 // Spec A.2 and A.3.
770 switch (profile_) {
771 case media::H264PROFILE_BASELINE:
772 // Due to crbug.com/345569, we don't distinguish between constrained
773 // and non-constrained baseline profiles. Since many codecs can't do
774 // non-constrained, and constrained is usually what we mean (and it's a
775 // subset of non-constrained), default to it.
776 current_sps_.profile_idc = media::H264SPS::kProfileIDCBaseline;
777 current_sps_.constraint_set0_flag = true;
778 break;
779 case media::H264PROFILE_MAIN:
780 current_sps_.profile_idc = media::H264SPS::kProfileIDCMain;
781 current_sps_.constraint_set1_flag = true;
782 break;
783 case media::H264PROFILE_HIGH:
784 current_sps_.profile_idc = media::H264SPS::kProfileIDCHigh;
785 break;
786 default:
787 NOTIMPLEMENTED();
788 return;
791 current_sps_.level_idc = kDefaultLevelIDC;
792 current_sps_.seq_parameter_set_id = 0;
793 current_sps_.chroma_format_idc = kChromaFormatIDC;
795 DCHECK_GE(idr_period_, 1u << 4);
796 current_sps_.log2_max_frame_num_minus4 = Log2OfPowerOf2(idr_period_) - 4;
797 current_sps_.pic_order_cnt_type = 0;
798 current_sps_.log2_max_pic_order_cnt_lsb_minus4 =
799 Log2OfPowerOf2(idr_period_ * 2) - 4;
800 current_sps_.max_num_ref_frames = max_ref_idx_l0_size_;
802 current_sps_.frame_mbs_only_flag = true;
804 DCHECK_GT(mb_width_, 0u);
805 DCHECK_GT(mb_height_, 0u);
806 current_sps_.pic_width_in_mbs_minus1 = mb_width_ - 1;
807 DCHECK(current_sps_.frame_mbs_only_flag);
808 current_sps_.pic_height_in_map_units_minus1 = mb_height_ - 1;
810 if (visible_size_ != coded_size_) {
811 // Visible size differs from coded size, fill crop information.
812 current_sps_.frame_cropping_flag = true;
813 DCHECK(!current_sps_.separate_colour_plane_flag);
814 // Spec table 6-1. Only 4:2:0 for now.
815 DCHECK_EQ(current_sps_.chroma_format_idc, 1);
816 // Spec 7.4.2.1.1. Crop is in crop units, which is 2 pixels for 4:2:0.
817 const unsigned int crop_unit_x = 2;
818 const unsigned int crop_unit_y = 2 * (2 - current_sps_.frame_mbs_only_flag);
819 current_sps_.frame_crop_left_offset = 0;
820 current_sps_.frame_crop_right_offset =
821 (coded_size_.width() - visible_size_.width()) / crop_unit_x;
822 current_sps_.frame_crop_top_offset = 0;
823 current_sps_.frame_crop_bottom_offset =
824 (coded_size_.height() - visible_size_.height()) / crop_unit_y;
827 current_sps_.vui_parameters_present_flag = true;
828 current_sps_.timing_info_present_flag = true;
829 current_sps_.num_units_in_tick = 1;
830 current_sps_.time_scale = framerate_ * 2; // See equation D-2 in spec.
831 current_sps_.fixed_frame_rate_flag = true;
833 current_sps_.nal_hrd_parameters_present_flag = true;
834 // H.264 spec ch. E.2.2.
835 current_sps_.cpb_cnt_minus1 = 0;
836 current_sps_.bit_rate_scale = kBitRateScale;
837 current_sps_.cpb_size_scale = kCPBSizeScale;
838 current_sps_.bit_rate_value_minus1[0] =
839 (bitrate_ >>
840 (kBitRateScale + media::H264SPS::kBitRateScaleConstantTerm)) - 1;
841 current_sps_.cpb_size_value_minus1[0] =
842 (cpb_size_ >>
843 (kCPBSizeScale + media::H264SPS::kCPBSizeScaleConstantTerm)) - 1;
844 current_sps_.cbr_flag[0] = true;
845 current_sps_.initial_cpb_removal_delay_length_minus_1 =
846 media::H264SPS::kDefaultInitialCPBRemovalDelayLength - 1;
847 current_sps_.cpb_removal_delay_length_minus1 =
848 media::H264SPS::kDefaultInitialCPBRemovalDelayLength - 1;
849 current_sps_.dpb_output_delay_length_minus1 =
850 media::H264SPS::kDefaultDPBOutputDelayLength - 1;
851 current_sps_.time_offset_length = media::H264SPS::kDefaultTimeOffsetLength;
852 current_sps_.low_delay_hrd_flag = false;
855 void VaapiVideoEncodeAccelerator::GeneratePackedSPS() {
856 packed_sps_.Reset();
858 packed_sps_.BeginNALU(media::H264NALU::kSPS, 3);
860 packed_sps_.AppendBits(8, current_sps_.profile_idc);
861 packed_sps_.AppendBool(current_sps_.constraint_set0_flag);
862 packed_sps_.AppendBool(current_sps_.constraint_set1_flag);
863 packed_sps_.AppendBool(current_sps_.constraint_set2_flag);
864 packed_sps_.AppendBool(current_sps_.constraint_set3_flag);
865 packed_sps_.AppendBool(current_sps_.constraint_set4_flag);
866 packed_sps_.AppendBool(current_sps_.constraint_set5_flag);
867 packed_sps_.AppendBits(2, 0); // reserved_zero_2bits
868 packed_sps_.AppendBits(8, current_sps_.level_idc);
869 packed_sps_.AppendUE(current_sps_.seq_parameter_set_id);
871 if (current_sps_.profile_idc == media::H264SPS::kProfileIDCHigh) {
872 packed_sps_.AppendUE(current_sps_.chroma_format_idc);
873 if (current_sps_.chroma_format_idc == 3)
874 packed_sps_.AppendBool(current_sps_.separate_colour_plane_flag);
875 packed_sps_.AppendUE(current_sps_.bit_depth_luma_minus8);
876 packed_sps_.AppendUE(current_sps_.bit_depth_chroma_minus8);
877 packed_sps_.AppendBool(current_sps_.qpprime_y_zero_transform_bypass_flag);
878 packed_sps_.AppendBool(current_sps_.seq_scaling_matrix_present_flag);
879 CHECK(!current_sps_.seq_scaling_matrix_present_flag);
882 packed_sps_.AppendUE(current_sps_.log2_max_frame_num_minus4);
883 packed_sps_.AppendUE(current_sps_.pic_order_cnt_type);
884 if (current_sps_.pic_order_cnt_type == 0)
885 packed_sps_.AppendUE(current_sps_.log2_max_pic_order_cnt_lsb_minus4);
886 else if (current_sps_.pic_order_cnt_type == 1) {
887 CHECK(1);
890 packed_sps_.AppendUE(current_sps_.max_num_ref_frames);
891 packed_sps_.AppendBool(current_sps_.gaps_in_frame_num_value_allowed_flag);
892 packed_sps_.AppendUE(current_sps_.pic_width_in_mbs_minus1);
893 packed_sps_.AppendUE(current_sps_.pic_height_in_map_units_minus1);
895 packed_sps_.AppendBool(current_sps_.frame_mbs_only_flag);
896 if (!current_sps_.frame_mbs_only_flag)
897 packed_sps_.AppendBool(current_sps_.mb_adaptive_frame_field_flag);
899 packed_sps_.AppendBool(current_sps_.direct_8x8_inference_flag);
901 packed_sps_.AppendBool(current_sps_.frame_cropping_flag);
902 if (current_sps_.frame_cropping_flag) {
903 packed_sps_.AppendUE(current_sps_.frame_crop_left_offset);
904 packed_sps_.AppendUE(current_sps_.frame_crop_right_offset);
905 packed_sps_.AppendUE(current_sps_.frame_crop_top_offset);
906 packed_sps_.AppendUE(current_sps_.frame_crop_bottom_offset);
909 packed_sps_.AppendBool(current_sps_.vui_parameters_present_flag);
910 if (current_sps_.vui_parameters_present_flag) {
911 packed_sps_.AppendBool(false); // aspect_ratio_info_present_flag
912 packed_sps_.AppendBool(false); // overscan_info_present_flag
913 packed_sps_.AppendBool(false); // video_signal_type_present_flag
914 packed_sps_.AppendBool(false); // chroma_loc_info_present_flag
916 packed_sps_.AppendBool(current_sps_.timing_info_present_flag);
917 if (current_sps_.timing_info_present_flag) {
918 packed_sps_.AppendBits(32, current_sps_.num_units_in_tick);
919 packed_sps_.AppendBits(32, current_sps_.time_scale);
920 packed_sps_.AppendBool(current_sps_.fixed_frame_rate_flag);
923 packed_sps_.AppendBool(current_sps_.nal_hrd_parameters_present_flag);
924 if (current_sps_.nal_hrd_parameters_present_flag) {
925 packed_sps_.AppendUE(current_sps_.cpb_cnt_minus1);
926 packed_sps_.AppendBits(4, current_sps_.bit_rate_scale);
927 packed_sps_.AppendBits(4, current_sps_.cpb_size_scale);
928 CHECK_LT(base::checked_cast<size_t>(current_sps_.cpb_cnt_minus1),
929 arraysize(current_sps_.bit_rate_value_minus1));
930 for (int i = 0; i <= current_sps_.cpb_cnt_minus1; ++i) {
931 packed_sps_.AppendUE(current_sps_.bit_rate_value_minus1[i]);
932 packed_sps_.AppendUE(current_sps_.cpb_size_value_minus1[i]);
933 packed_sps_.AppendBool(current_sps_.cbr_flag[i]);
935 packed_sps_.AppendBits(
936 5, current_sps_.initial_cpb_removal_delay_length_minus_1);
937 packed_sps_.AppendBits(5, current_sps_.cpb_removal_delay_length_minus1);
938 packed_sps_.AppendBits(5, current_sps_.dpb_output_delay_length_minus1);
939 packed_sps_.AppendBits(5, current_sps_.time_offset_length);
942 packed_sps_.AppendBool(false); // vcl_hrd_parameters_flag
943 if (current_sps_.nal_hrd_parameters_present_flag)
944 packed_sps_.AppendBool(current_sps_.low_delay_hrd_flag);
946 packed_sps_.AppendBool(false); // pic_struct_present_flag
947 packed_sps_.AppendBool(true); // bitstream_restriction_flag
949 packed_sps_.AppendBool(false); // motion_vectors_over_pic_boundaries_flag
950 packed_sps_.AppendUE(2); // max_bytes_per_pic_denom
951 packed_sps_.AppendUE(1); // max_bits_per_mb_denom
952 packed_sps_.AppendUE(16); // log2_max_mv_length_horizontal
953 packed_sps_.AppendUE(16); // log2_max_mv_length_vertical
955 // Explicitly set max_num_reorder_frames to 0 to allow the decoder to
956 // output pictures early.
957 packed_sps_.AppendUE(0); // max_num_reorder_frames
959 // The value of max_dec_frame_buffering shall be greater than or equal to
960 // max_num_ref_frames.
961 const unsigned int max_dec_frame_buffering =
962 current_sps_.max_num_ref_frames;
963 packed_sps_.AppendUE(max_dec_frame_buffering);
966 packed_sps_.FinishNALU();
969 void VaapiVideoEncodeAccelerator::UpdatePPS() {
970 memset(&current_pps_, 0, sizeof(media::H264PPS));
972 current_pps_.seq_parameter_set_id = current_sps_.seq_parameter_set_id;
973 current_pps_.pic_parameter_set_id = 0;
975 current_pps_.entropy_coding_mode_flag =
976 current_sps_.profile_idc >= media::H264SPS::kProfileIDCMain;
978 CHECK_GT(max_ref_idx_l0_size_, 0u);
979 current_pps_.num_ref_idx_l0_default_active_minus1 = max_ref_idx_l0_size_ - 1;
980 current_pps_.num_ref_idx_l1_default_active_minus1 = 0;
981 DCHECK_LE(qp_, 51u);
982 current_pps_.pic_init_qp_minus26 = qp_ - 26;
983 current_pps_.deblocking_filter_control_present_flag = true;
984 current_pps_.transform_8x8_mode_flag =
985 (current_sps_.profile_idc == media::H264SPS::kProfileIDCHigh);
988 void VaapiVideoEncodeAccelerator::GeneratePackedPPS() {
989 packed_pps_.Reset();
991 packed_pps_.BeginNALU(media::H264NALU::kPPS, 3);
993 packed_pps_.AppendUE(current_pps_.pic_parameter_set_id);
994 packed_pps_.AppendUE(current_pps_.seq_parameter_set_id);
995 packed_pps_.AppendBool(current_pps_.entropy_coding_mode_flag);
996 packed_pps_.AppendBool(
997 current_pps_.bottom_field_pic_order_in_frame_present_flag);
998 CHECK_EQ(current_pps_.num_slice_groups_minus1, 0);
999 packed_pps_.AppendUE(current_pps_.num_slice_groups_minus1);
1001 packed_pps_.AppendUE(current_pps_.num_ref_idx_l0_default_active_minus1);
1002 packed_pps_.AppendUE(current_pps_.num_ref_idx_l1_default_active_minus1);
1004 packed_pps_.AppendBool(current_pps_.weighted_pred_flag);
1005 packed_pps_.AppendBits(2, current_pps_.weighted_bipred_idc);
1007 packed_pps_.AppendSE(current_pps_.pic_init_qp_minus26);
1008 packed_pps_.AppendSE(current_pps_.pic_init_qs_minus26);
1009 packed_pps_.AppendSE(current_pps_.chroma_qp_index_offset);
1011 packed_pps_.AppendBool(current_pps_.deblocking_filter_control_present_flag);
1012 packed_pps_.AppendBool(current_pps_.constrained_intra_pred_flag);
1013 packed_pps_.AppendBool(current_pps_.redundant_pic_cnt_present_flag);
1015 packed_pps_.AppendBool(current_pps_.transform_8x8_mode_flag);
1016 packed_pps_.AppendBool(current_pps_.pic_scaling_matrix_present_flag);
1017 DCHECK(!current_pps_.pic_scaling_matrix_present_flag);
1018 packed_pps_.AppendSE(current_pps_.second_chroma_qp_index_offset);
1020 packed_pps_.FinishNALU();
1023 void VaapiVideoEncodeAccelerator::SetState(State state) {
1024 // Only touch state on encoder thread, unless it's not running.
1025 if (encoder_thread_.IsRunning() &&
1026 !encoder_thread_task_runner_->BelongsToCurrentThread()) {
1027 encoder_thread_task_runner_->PostTask(
1028 FROM_HERE, base::Bind(&VaapiVideoEncodeAccelerator::SetState,
1029 base::Unretained(this), state));
1030 return;
1033 DVLOGF(1) << "setting state to: " << state;
1034 state_ = state;
1037 void VaapiVideoEncodeAccelerator::NotifyError(Error error) {
1038 if (!child_task_runner_->BelongsToCurrentThread()) {
1039 child_task_runner_->PostTask(
1040 FROM_HERE, base::Bind(&VaapiVideoEncodeAccelerator::NotifyError,
1041 weak_this_, error));
1042 return;
1045 if (client_) {
1046 client_->NotifyError(error);
1047 client_ptr_factory_.reset();
1051 VaapiVideoEncodeAccelerator::EncodeJob::EncodeJob()
1052 : coded_buffer(VA_INVALID_ID), keyframe(false) {
1055 VaapiVideoEncodeAccelerator::EncodeJob::~EncodeJob() {
1058 } // namespace content