ozone: evdev: Sync caps lock LED state to evdev
[chromium-blink-merge.git] / content / common / gpu / media / vaapi_video_encode_accelerator.cc
blob6bf0e3b68e57fcc279c79dd04f3429ca798b7a06
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/command_line.h"
10 #include "base/message_loop/message_loop_proxy.h"
11 #include "base/metrics/histogram.h"
12 #include "base/numerics/safe_conversions.h"
13 #include "content/common/gpu/media/h264_dpb.h"
14 #include "content/public/common/content_switches.h"
15 #include "media/base/bind_to_current_loop.h"
16 #include "third_party/libva/va/va_enc_h264.h"
18 #define DVLOGF(level) DVLOG(level) << __FUNCTION__ << "(): "
20 #define NOTIFY_ERROR(error, msg) \
21 do { \
22 SetState(kError); \
23 LOG(ERROR) << msg; \
24 LOG(ERROR) << "Calling NotifyError(" << error << ")";\
25 NotifyError(error); \
26 } while (0)
28 namespace content {
30 namespace {
31 // Need 2 surfaces for each frame: one for input data and one for
32 // reconstructed picture, which is later used for reference.
33 const size_t kMinSurfacesToEncode = 2;
35 // Subjectively chosen.
36 const size_t kNumInputBuffers = 4;
37 const size_t kMaxNumReferenceFrames = 4;
39 // We need up to kMaxNumReferenceFrames surfaces for reference, plus one
40 // for input and one for encode (which will be added to the set of reference
41 // frames for subsequent frames). Actual execution of HW encode is done
42 // in parallel, and we want to process more frames in the meantime.
43 // To have kNumInputBuffers in flight, we need a full set of reference +
44 // encode surfaces (i.e. kMaxNumReferenceFrames + kMinSurfacesToEncode), and
45 // (kNumInputBuffers - 1) of kMinSurfacesToEncode for the remaining frames
46 // in flight.
47 const size_t kNumSurfaces = kMaxNumReferenceFrames + kMinSurfacesToEncode +
48 kMinSurfacesToEncode * (kNumInputBuffers - 1);
50 // An IDR every 2048 frames, an I frame every 256 and no B frames.
51 // We choose IDR period to equal MaxFrameNum so it must be a power of 2.
52 const int kIDRPeriod = 2048;
53 const int kIPeriod = 256;
54 const int kIPPeriod = 1;
56 const int kDefaultFramerate = 30;
58 // HRD parameters (ch. E.2.2 in spec).
59 const int kBitRateScale = 0; // bit_rate_scale for SPS HRD parameters.
60 const int kCPBSizeScale = 0; // cpb_size_scale for SPS HRD parameters.
62 const int kDefaultQP = 26;
63 // All Intel codecs can do at least 4.1.
64 const int kDefaultLevelIDC = 41;
65 const int kChromaFormatIDC = 1; // 4:2:0
67 // Arbitrarily chosen bitrate window size for rate control, in ms.
68 const int kCPBWindowSizeMs = 1500;
70 // UMA errors that the VaapiVideoEncodeAccelerator class reports.
71 enum VAVEAEncoderFailure {
72 VAAPI_ERROR = 0,
73 VAVEA_ENCODER_FAILURES_MAX,
78 // Round |value| up to |alignment|, which must be a power of 2.
79 static inline size_t RoundUpToPowerOf2(size_t value, size_t alignment) {
80 // Check that |alignment| is a power of 2.
81 DCHECK((alignment + (alignment - 1)) == (alignment | (alignment - 1)));
82 return ((value + (alignment - 1)) & ~(alignment - 1));
85 static void ReportToUMA(VAVEAEncoderFailure failure) {
86 UMA_HISTOGRAM_ENUMERATION(
87 "Media.VAVEA.EncoderFailure",
88 failure,
89 VAVEA_ENCODER_FAILURES_MAX);
92 struct VaapiVideoEncodeAccelerator::InputFrameRef {
93 InputFrameRef(const scoped_refptr<media::VideoFrame>& frame,
94 bool force_keyframe)
95 : frame(frame), force_keyframe(force_keyframe) {}
96 const scoped_refptr<media::VideoFrame> frame;
97 const bool force_keyframe;
100 struct VaapiVideoEncodeAccelerator::BitstreamBufferRef {
101 BitstreamBufferRef(int32 id, scoped_ptr<base::SharedMemory> shm, size_t size)
102 : id(id), shm(shm.Pass()), size(size) {}
103 const int32 id;
104 const scoped_ptr<base::SharedMemory> shm;
105 const size_t size;
108 std::vector<media::VideoEncodeAccelerator::SupportedProfile>
109 VaapiVideoEncodeAccelerator::GetSupportedProfiles() {
110 std::vector<SupportedProfile> profiles;
112 const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
113 if (cmd_line->HasSwitch(switches::kDisableVaapiAcceleratedVideoEncode))
114 return profiles;
116 std::vector<media::VideoCodecProfile> hw_profiles =
117 VaapiWrapper::GetSupportedEncodeProfiles(base::Bind(&base::DoNothing));
119 media::VideoEncodeAccelerator::SupportedProfile profile;
120 profile.max_resolution.SetSize(1920, 1088);
121 profile.max_framerate_numerator = kDefaultFramerate;
122 profile.max_framerate_denominator = 1;
123 for (size_t i = 0; i < hw_profiles.size(); i++) {
124 profile.profile = hw_profiles[i];
125 profiles.push_back(profile);
127 return profiles;
130 static unsigned int Log2OfPowerOf2(unsigned int x) {
131 CHECK_GT(x, 0u);
132 DCHECK_EQ(x & (x - 1), 0u);
134 int log = 0;
135 while (x > 1) {
136 x >>= 1;
137 ++log;
139 return log;
142 VaapiVideoEncodeAccelerator::VaapiVideoEncodeAccelerator()
143 : profile_(media::VIDEO_CODEC_PROFILE_UNKNOWN),
144 mb_width_(0),
145 mb_height_(0),
146 output_buffer_byte_size_(0),
147 state_(kUninitialized),
148 frame_num_(0),
149 idr_pic_id_(0),
150 bitrate_(0),
151 framerate_(0),
152 cpb_size_(0),
153 encoding_parameters_changed_(false),
154 encoder_thread_("VAVEAEncoderThread"),
155 child_message_loop_proxy_(base::MessageLoopProxy::current()),
156 weak_this_ptr_factory_(this) {
157 DVLOGF(4);
158 weak_this_ = weak_this_ptr_factory_.GetWeakPtr();
160 max_ref_idx_l0_size_ = kMaxNumReferenceFrames;
161 qp_ = kDefaultQP;
162 idr_period_ = kIDRPeriod;
163 i_period_ = kIPeriod;
164 ip_period_ = kIPPeriod;
167 VaapiVideoEncodeAccelerator::~VaapiVideoEncodeAccelerator() {
168 DVLOGF(4);
169 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
170 DCHECK(!encoder_thread_.IsRunning());
173 bool VaapiVideoEncodeAccelerator::Initialize(
174 media::VideoFrame::Format format,
175 const gfx::Size& input_visible_size,
176 media::VideoCodecProfile output_profile,
177 uint32 initial_bitrate,
178 Client* client) {
179 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
180 DCHECK(!encoder_thread_.IsRunning());
181 DCHECK_EQ(state_, kUninitialized);
183 DVLOGF(1) << "Initializing VAVEA, input_format: "
184 << media::VideoFrame::FormatToString(format)
185 << ", input_visible_size: " << input_visible_size.ToString()
186 << ", output_profile: " << output_profile
187 << ", initial_bitrate: " << initial_bitrate;
189 client_ptr_factory_.reset(new base::WeakPtrFactory<Client>(client));
190 client_ = client_ptr_factory_->GetWeakPtr();
192 if (output_profile < media::H264PROFILE_BASELINE ||
193 output_profile > media::H264PROFILE_MAIN) {
194 DVLOGF(1) << "Unsupported output profile: " << output_profile;
195 return false;
198 if (format != media::VideoFrame::I420) {
199 DVLOGF(1) << "Unsupported input format: "
200 << media::VideoFrame::FormatToString(format);
201 return false;
204 profile_ = output_profile;
205 visible_size_ = input_visible_size;
206 // 4:2:0 format has to be 2-aligned.
207 DCHECK_EQ(visible_size_.width() % 2, 0);
208 DCHECK_EQ(visible_size_.height() % 2, 0);
209 coded_size_ = gfx::Size(RoundUpToPowerOf2(visible_size_.width(), 16),
210 RoundUpToPowerOf2(visible_size_.height(), 16));
211 mb_width_ = coded_size_.width() / 16;
212 mb_height_ = coded_size_.height() / 16;
213 output_buffer_byte_size_ = coded_size_.GetArea();
215 UpdateRates(initial_bitrate, kDefaultFramerate);
217 vaapi_wrapper_ =
218 VaapiWrapper::CreateForVideoCodec(VaapiWrapper::kEncode, output_profile,
219 base::Bind(&ReportToUMA, VAAPI_ERROR));
220 if (!vaapi_wrapper_.get()) {
221 LOG(ERROR) << "Failed initializing VAAPI";
222 return false;
225 if (!encoder_thread_.Start()) {
226 LOG(ERROR) << "Failed to start encoder thread";
227 return false;
229 encoder_thread_proxy_ = encoder_thread_.message_loop_proxy();
231 // Finish the remaining initialization on the encoder thread.
232 encoder_thread_proxy_->PostTask(
233 FROM_HERE,
234 base::Bind(&VaapiVideoEncodeAccelerator::InitializeTask,
235 base::Unretained(this)));
237 return true;
240 void VaapiVideoEncodeAccelerator::InitializeTask() {
241 DCHECK(encoder_thread_proxy_->BelongsToCurrentThread());
242 DCHECK_EQ(state_, kUninitialized);
243 DVLOGF(4);
245 va_surface_release_cb_ = media::BindToCurrentLoop(
246 base::Bind(&VaapiVideoEncodeAccelerator::RecycleVASurfaceID,
247 base::Unretained(this)));
249 if (!vaapi_wrapper_->CreateSurfaces(
250 coded_size_, kNumSurfaces, &available_va_surface_ids_)) {
251 NOTIFY_ERROR(kPlatformFailureError, "Failed creating VASurfaces");
252 return;
255 UpdateSPS();
256 GeneratePackedSPS();
258 UpdatePPS();
259 GeneratePackedPPS();
261 child_message_loop_proxy_->PostTask(
262 FROM_HERE,
263 base::Bind(&Client::RequireBitstreamBuffers,
264 client_,
265 kNumInputBuffers,
266 coded_size_,
267 output_buffer_byte_size_));
269 SetState(kEncoding);
272 void VaapiVideoEncodeAccelerator::RecycleVASurfaceID(
273 VASurfaceID va_surface_id) {
274 DVLOGF(4) << "va_surface_id: " << va_surface_id;
275 DCHECK(encoder_thread_proxy_->BelongsToCurrentThread());
277 available_va_surface_ids_.push_back(va_surface_id);
278 EncodeFrameTask();
281 void VaapiVideoEncodeAccelerator::BeginFrame(bool force_keyframe) {
282 memset(&current_pic_, 0, sizeof(current_pic_));
284 // If the current picture is an IDR picture, frame_num shall be equal to 0.
285 if (force_keyframe)
286 frame_num_ = 0;
288 current_pic_.frame_num = frame_num_++;
289 frame_num_ %= idr_period_;
291 if (current_pic_.frame_num == 0) {
292 current_pic_.idr = true;
293 // H264 spec mandates idr_pic_id to differ between two consecutive IDRs.
294 idr_pic_id_ ^= 1;
295 ref_pic_list0_.clear();
298 if (current_pic_.frame_num % i_period_ == 0)
299 current_pic_.type = media::H264SliceHeader::kISlice;
300 else
301 current_pic_.type = media::H264SliceHeader::kPSlice;
303 if (current_pic_.type != media::H264SliceHeader::kBSlice)
304 current_pic_.ref = true;
306 current_pic_.pic_order_cnt = current_pic_.frame_num * 2;
307 current_pic_.top_field_order_cnt = current_pic_.pic_order_cnt;
308 current_pic_.pic_order_cnt_lsb = current_pic_.pic_order_cnt;
310 current_encode_job_->keyframe = current_pic_.idr;
312 DVLOGF(4) << "Starting a new frame, type: " << current_pic_.type
313 << (force_keyframe ? " (forced keyframe)" : "")
314 << " frame_num: " << current_pic_.frame_num
315 << " POC: " << current_pic_.pic_order_cnt;
318 void VaapiVideoEncodeAccelerator::EndFrame() {
319 // Store the picture on the list of reference pictures and keep the list
320 // below maximum size, dropping oldest references.
321 if (current_pic_.ref)
322 ref_pic_list0_.push_front(current_encode_job_->recon_surface);
323 size_t max_num_ref_frames =
324 base::checked_cast<size_t>(current_sps_.max_num_ref_frames);
325 while (ref_pic_list0_.size() > max_num_ref_frames)
326 ref_pic_list0_.pop_back();
328 submitted_encode_jobs_.push(make_linked_ptr(current_encode_job_.release()));
331 static void InitVAPicture(VAPictureH264* va_pic) {
332 memset(va_pic, 0, sizeof(*va_pic));
333 va_pic->picture_id = VA_INVALID_ID;
334 va_pic->flags = VA_PICTURE_H264_INVALID;
337 bool VaapiVideoEncodeAccelerator::SubmitFrameParameters() {
338 VAEncSequenceParameterBufferH264 seq_param;
339 memset(&seq_param, 0, sizeof(seq_param));
341 #define SPS_TO_SP(a) seq_param.a = current_sps_.a;
342 SPS_TO_SP(seq_parameter_set_id);
343 SPS_TO_SP(level_idc);
345 seq_param.intra_period = i_period_;
346 seq_param.intra_idr_period = idr_period_;
347 seq_param.ip_period = ip_period_;
348 seq_param.bits_per_second = bitrate_;
350 SPS_TO_SP(max_num_ref_frames);
351 seq_param.picture_width_in_mbs = mb_width_;
352 seq_param.picture_height_in_mbs = mb_height_;
354 #define SPS_TO_SP_FS(a) seq_param.seq_fields.bits.a = current_sps_.a;
355 SPS_TO_SP_FS(chroma_format_idc);
356 SPS_TO_SP_FS(frame_mbs_only_flag);
357 SPS_TO_SP_FS(log2_max_frame_num_minus4);
358 SPS_TO_SP_FS(pic_order_cnt_type);
359 SPS_TO_SP_FS(log2_max_pic_order_cnt_lsb_minus4);
360 #undef SPS_TO_SP_FS
362 SPS_TO_SP(bit_depth_luma_minus8);
363 SPS_TO_SP(bit_depth_chroma_minus8);
365 SPS_TO_SP(frame_cropping_flag);
366 if (current_sps_.frame_cropping_flag) {
367 SPS_TO_SP(frame_crop_left_offset);
368 SPS_TO_SP(frame_crop_right_offset);
369 SPS_TO_SP(frame_crop_top_offset);
370 SPS_TO_SP(frame_crop_bottom_offset);
373 SPS_TO_SP(vui_parameters_present_flag);
374 #define SPS_TO_SP_VF(a) seq_param.vui_fields.bits.a = current_sps_.a;
375 SPS_TO_SP_VF(timing_info_present_flag);
376 #undef SPS_TO_SP_VF
377 SPS_TO_SP(num_units_in_tick);
378 SPS_TO_SP(time_scale);
379 #undef SPS_TO_SP
381 if (!vaapi_wrapper_->SubmitBuffer(VAEncSequenceParameterBufferType,
382 sizeof(seq_param),
383 &seq_param))
384 return false;
386 VAEncPictureParameterBufferH264 pic_param;
387 memset(&pic_param, 0, sizeof(pic_param));
389 pic_param.CurrPic.picture_id = current_encode_job_->recon_surface->id();
390 pic_param.CurrPic.TopFieldOrderCnt = current_pic_.top_field_order_cnt;
391 pic_param.CurrPic.BottomFieldOrderCnt = current_pic_.bottom_field_order_cnt;
392 pic_param.CurrPic.flags = 0;
394 for (size_t i = 0; i < arraysize(pic_param.ReferenceFrames); ++i)
395 InitVAPicture(&pic_param.ReferenceFrames[i]);
397 DCHECK_LE(ref_pic_list0_.size(), arraysize(pic_param.ReferenceFrames));
398 RefPicList::const_iterator iter = ref_pic_list0_.begin();
399 for (size_t i = 0;
400 i < arraysize(pic_param.ReferenceFrames) && iter != ref_pic_list0_.end();
401 ++iter, ++i) {
402 pic_param.ReferenceFrames[i].picture_id = (*iter)->id();
403 pic_param.ReferenceFrames[i].flags = 0;
406 pic_param.coded_buf = current_encode_job_->coded_buffer;
407 pic_param.pic_parameter_set_id = current_pps_.pic_parameter_set_id;
408 pic_param.seq_parameter_set_id = current_pps_.seq_parameter_set_id;
409 pic_param.frame_num = current_pic_.frame_num;
410 pic_param.pic_init_qp = qp_;
411 pic_param.num_ref_idx_l0_active_minus1 = max_ref_idx_l0_size_ - 1;
412 pic_param.pic_fields.bits.idr_pic_flag = current_pic_.idr;
413 pic_param.pic_fields.bits.reference_pic_flag = current_pic_.ref;
414 #define PPS_TO_PP_PF(a) pic_param.pic_fields.bits.a = current_pps_.a;
415 PPS_TO_PP_PF(entropy_coding_mode_flag);
416 PPS_TO_PP_PF(transform_8x8_mode_flag);
417 PPS_TO_PP_PF(deblocking_filter_control_present_flag);
418 #undef PPS_TO_PP_PF
420 if (!vaapi_wrapper_->SubmitBuffer(VAEncPictureParameterBufferType,
421 sizeof(pic_param),
422 &pic_param))
423 return false;
425 VAEncSliceParameterBufferH264 slice_param;
426 memset(&slice_param, 0, sizeof(slice_param));
428 slice_param.num_macroblocks = mb_width_ * mb_height_;
429 slice_param.macroblock_info = VA_INVALID_ID;
430 slice_param.slice_type = current_pic_.type;
431 slice_param.pic_parameter_set_id = current_pps_.pic_parameter_set_id;
432 slice_param.idr_pic_id = idr_pic_id_;
433 slice_param.pic_order_cnt_lsb = current_pic_.pic_order_cnt_lsb;
434 slice_param.num_ref_idx_active_override_flag = true;
436 for (size_t i = 0; i < arraysize(slice_param.RefPicList0); ++i)
437 InitVAPicture(&slice_param.RefPicList0[i]);
439 for (size_t i = 0; i < arraysize(slice_param.RefPicList1); ++i)
440 InitVAPicture(&slice_param.RefPicList1[i]);
442 DCHECK_LE(ref_pic_list0_.size(), arraysize(slice_param.RefPicList0));
443 iter = ref_pic_list0_.begin();
444 for (size_t i = 0;
445 i < arraysize(slice_param.RefPicList0) && iter != ref_pic_list0_.end();
446 ++iter, ++i) {
447 InitVAPicture(&slice_param.RefPicList0[i]);
448 slice_param.RefPicList0[i].picture_id = (*iter)->id();
449 slice_param.RefPicList0[i].flags = 0;
452 if (!vaapi_wrapper_->SubmitBuffer(VAEncSliceParameterBufferType,
453 sizeof(slice_param),
454 &slice_param))
455 return false;
457 VAEncMiscParameterRateControl rate_control_param;
458 memset(&rate_control_param, 0, sizeof(rate_control_param));
459 rate_control_param.bits_per_second = bitrate_;
460 rate_control_param.target_percentage = 90;
461 rate_control_param.window_size = kCPBWindowSizeMs;
462 rate_control_param.initial_qp = qp_;
463 rate_control_param.rc_flags.bits.disable_frame_skip = true;
465 if (!vaapi_wrapper_->SubmitVAEncMiscParamBuffer(
466 VAEncMiscParameterTypeRateControl,
467 sizeof(rate_control_param),
468 &rate_control_param))
469 return false;
471 VAEncMiscParameterFrameRate framerate_param;
472 memset(&framerate_param, 0, sizeof(framerate_param));
473 framerate_param.framerate = framerate_;
474 if (!vaapi_wrapper_->SubmitVAEncMiscParamBuffer(
475 VAEncMiscParameterTypeFrameRate,
476 sizeof(framerate_param),
477 &framerate_param))
478 return false;
480 VAEncMiscParameterHRD hrd_param;
481 memset(&hrd_param, 0, sizeof(hrd_param));
482 hrd_param.buffer_size = cpb_size_;
483 hrd_param.initial_buffer_fullness = cpb_size_ / 2;
484 if (!vaapi_wrapper_->SubmitVAEncMiscParamBuffer(VAEncMiscParameterTypeHRD,
485 sizeof(hrd_param),
486 &hrd_param))
487 return false;
489 return true;
492 bool VaapiVideoEncodeAccelerator::SubmitHeadersIfNeeded() {
493 if (current_pic_.type != media::H264SliceHeader::kISlice)
494 return true;
496 // Submit PPS.
497 VAEncPackedHeaderParameterBuffer par_buffer;
498 memset(&par_buffer, 0, sizeof(par_buffer));
499 par_buffer.type = VAEncPackedHeaderSequence;
500 par_buffer.bit_length = packed_sps_.BytesInBuffer() * 8;
502 if (!vaapi_wrapper_->SubmitBuffer(VAEncPackedHeaderParameterBufferType,
503 sizeof(par_buffer),
504 &par_buffer))
505 return false;
507 if (!vaapi_wrapper_->SubmitBuffer(VAEncPackedHeaderDataBufferType,
508 packed_sps_.BytesInBuffer(),
509 packed_sps_.data()))
510 return false;
512 // Submit PPS.
513 memset(&par_buffer, 0, sizeof(par_buffer));
514 par_buffer.type = VAEncPackedHeaderPicture;
515 par_buffer.bit_length = packed_pps_.BytesInBuffer() * 8;
517 if (!vaapi_wrapper_->SubmitBuffer(VAEncPackedHeaderParameterBufferType,
518 sizeof(par_buffer),
519 &par_buffer))
520 return false;
522 if (!vaapi_wrapper_->SubmitBuffer(VAEncPackedHeaderDataBufferType,
523 packed_pps_.BytesInBuffer(),
524 packed_pps_.data()))
525 return false;
527 return true;
530 bool VaapiVideoEncodeAccelerator::ExecuteEncode() {
531 DVLOGF(3) << "Encoding frame_num: " << current_pic_.frame_num;
532 return vaapi_wrapper_->ExecuteAndDestroyPendingBuffers(
533 current_encode_job_->input_surface->id());
536 bool VaapiVideoEncodeAccelerator::UploadFrame(
537 const scoped_refptr<media::VideoFrame>& frame) {
538 return vaapi_wrapper_->UploadVideoFrameToSurface(
539 frame, current_encode_job_->input_surface->id());
542 void VaapiVideoEncodeAccelerator::TryToReturnBitstreamBuffer() {
543 DCHECK(encoder_thread_proxy_->BelongsToCurrentThread());
545 if (state_ != kEncoding)
546 return;
548 if (submitted_encode_jobs_.empty() || available_bitstream_buffers_.empty())
549 return;
551 linked_ptr<BitstreamBufferRef> buffer = available_bitstream_buffers_.front();
552 available_bitstream_buffers_.pop();
554 uint8* target_data = reinterpret_cast<uint8*>(buffer->shm->memory());
556 linked_ptr<EncodeJob> encode_job = submitted_encode_jobs_.front();
557 submitted_encode_jobs_.pop();
559 size_t data_size = 0;
560 if (!vaapi_wrapper_->DownloadAndDestroyCodedBuffer(
561 encode_job->coded_buffer,
562 encode_job->input_surface->id(),
563 target_data,
564 buffer->size,
565 &data_size)) {
566 NOTIFY_ERROR(kPlatformFailureError, "Failed downloading coded buffer");
567 return;
570 DVLOGF(3) << "Returning bitstream buffer "
571 << (encode_job->keyframe ? "(keyframe)" : "")
572 << " id: " << buffer->id << " size: " << data_size;
574 child_message_loop_proxy_->PostTask(FROM_HERE,
575 base::Bind(&Client::BitstreamBufferReady,
576 client_,
577 buffer->id,
578 data_size,
579 encode_job->keyframe));
582 void VaapiVideoEncodeAccelerator::Encode(
583 const scoped_refptr<media::VideoFrame>& frame,
584 bool force_keyframe) {
585 DVLOGF(3) << "Frame timestamp: " << frame->timestamp().InMilliseconds()
586 << " force_keyframe: " << force_keyframe;
587 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
589 encoder_thread_proxy_->PostTask(
590 FROM_HERE,
591 base::Bind(&VaapiVideoEncodeAccelerator::EncodeTask,
592 base::Unretained(this),
593 frame,
594 force_keyframe));
597 bool VaapiVideoEncodeAccelerator::PrepareNextJob() {
598 if (available_va_surface_ids_.size() < kMinSurfacesToEncode)
599 return false;
601 DCHECK(!current_encode_job_);
602 current_encode_job_.reset(new EncodeJob());
604 if (!vaapi_wrapper_->CreateCodedBuffer(output_buffer_byte_size_,
605 &current_encode_job_->coded_buffer)) {
606 NOTIFY_ERROR(kPlatformFailureError, "Failed creating coded buffer");
607 return false;
610 current_encode_job_->input_surface = new VASurface(
611 available_va_surface_ids_.back(), coded_size_, va_surface_release_cb_);
612 available_va_surface_ids_.pop_back();
614 current_encode_job_->recon_surface = new VASurface(
615 available_va_surface_ids_.back(), coded_size_, va_surface_release_cb_);
616 available_va_surface_ids_.pop_back();
618 // Reference surfaces are needed until the job is done, but they get
619 // removed from ref_pic_list0_ when it's full at the end of job submission.
620 // Keep refs to them along with the job and only release after sync.
621 current_encode_job_->reference_surfaces = ref_pic_list0_;
623 return true;
626 void VaapiVideoEncodeAccelerator::EncodeTask(
627 const scoped_refptr<media::VideoFrame>& frame,
628 bool force_keyframe) {
629 DCHECK(encoder_thread_proxy_->BelongsToCurrentThread());
630 DCHECK_NE(state_, kUninitialized);
632 encoder_input_queue_.push(
633 make_linked_ptr(new InputFrameRef(frame, force_keyframe)));
634 EncodeFrameTask();
637 void VaapiVideoEncodeAccelerator::EncodeFrameTask() {
638 DCHECK(encoder_thread_proxy_->BelongsToCurrentThread());
640 if (state_ != kEncoding || encoder_input_queue_.empty())
641 return;
643 if (!PrepareNextJob()) {
644 DVLOGF(4) << "Not ready for next frame yet";
645 return;
648 linked_ptr<InputFrameRef> frame_ref = encoder_input_queue_.front();
649 encoder_input_queue_.pop();
651 if (!UploadFrame(frame_ref->frame)) {
652 NOTIFY_ERROR(kPlatformFailureError, "Failed uploading source frame to HW.");
653 return;
656 BeginFrame(frame_ref->force_keyframe || encoding_parameters_changed_);
657 encoding_parameters_changed_ = false;
659 if (!SubmitFrameParameters()) {
660 NOTIFY_ERROR(kPlatformFailureError, "Failed submitting frame parameters.");
661 return;
664 if (!SubmitHeadersIfNeeded()) {
665 NOTIFY_ERROR(kPlatformFailureError, "Failed submitting frame headers.");
666 return;
669 if (!ExecuteEncode()) {
670 NOTIFY_ERROR(kPlatformFailureError, "Failed submitting encode job to HW.");
671 return;
674 EndFrame();
675 TryToReturnBitstreamBuffer();
678 void VaapiVideoEncodeAccelerator::UseOutputBitstreamBuffer(
679 const media::BitstreamBuffer& buffer) {
680 DVLOGF(4) << "id: " << buffer.id();
681 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
683 if (buffer.size() < output_buffer_byte_size_) {
684 NOTIFY_ERROR(kInvalidArgumentError, "Provided bitstream buffer too small");
685 return;
688 scoped_ptr<base::SharedMemory> shm(
689 new base::SharedMemory(buffer.handle(), false));
690 if (!shm->Map(buffer.size())) {
691 NOTIFY_ERROR(kPlatformFailureError, "Failed mapping shared memory.");
692 return;
695 scoped_ptr<BitstreamBufferRef> buffer_ref(
696 new BitstreamBufferRef(buffer.id(), shm.Pass(), buffer.size()));
698 encoder_thread_proxy_->PostTask(
699 FROM_HERE,
700 base::Bind(&VaapiVideoEncodeAccelerator::UseOutputBitstreamBufferTask,
701 base::Unretained(this),
702 base::Passed(&buffer_ref)));
705 void VaapiVideoEncodeAccelerator::UseOutputBitstreamBufferTask(
706 scoped_ptr<BitstreamBufferRef> buffer_ref) {
707 DCHECK(encoder_thread_proxy_->BelongsToCurrentThread());
708 DCHECK_NE(state_, kUninitialized);
710 available_bitstream_buffers_.push(make_linked_ptr(buffer_ref.release()));
711 TryToReturnBitstreamBuffer();
714 void VaapiVideoEncodeAccelerator::RequestEncodingParametersChange(
715 uint32 bitrate,
716 uint32 framerate) {
717 DVLOGF(2) << "bitrate: " << bitrate << " framerate: " << framerate;
718 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
720 encoder_thread_proxy_->PostTask(
721 FROM_HERE,
722 base::Bind(
723 &VaapiVideoEncodeAccelerator::RequestEncodingParametersChangeTask,
724 base::Unretained(this),
725 bitrate,
726 framerate));
729 void VaapiVideoEncodeAccelerator::UpdateRates(uint32 bitrate,
730 uint32 framerate) {
731 if (encoder_thread_.IsRunning())
732 DCHECK(encoder_thread_proxy_->BelongsToCurrentThread());
733 DCHECK_NE(bitrate, 0u);
734 DCHECK_NE(framerate, 0u);
735 bitrate_ = bitrate;
736 framerate_ = framerate;
737 cpb_size_ = bitrate_ * kCPBWindowSizeMs / 1000;
740 void VaapiVideoEncodeAccelerator::RequestEncodingParametersChangeTask(
741 uint32 bitrate,
742 uint32 framerate) {
743 DVLOGF(2) << "bitrate: " << bitrate << " framerate: " << framerate;
744 DCHECK(encoder_thread_proxy_->BelongsToCurrentThread());
745 DCHECK_NE(state_, kUninitialized);
747 // This is a workaround to zero being temporarily, as part of the initial
748 // setup, provided by the webrtc video encode and a zero bitrate and
749 // framerate not being accepted by VAAPI
750 // TODO: This code is common with v4l2_video_encode_accelerator.cc, perhaps
751 // it could be pulled up to RTCVideoEncoder
752 if (bitrate < 1)
753 bitrate = 1;
754 if (framerate < 1)
755 framerate = 1;
757 if (bitrate_ == bitrate && framerate_ == framerate)
758 return;
760 UpdateRates(bitrate, framerate);
762 UpdateSPS();
763 GeneratePackedSPS();
765 // Submit new parameters along with next frame that will be processed.
766 encoding_parameters_changed_ = true;
769 void VaapiVideoEncodeAccelerator::Destroy() {
770 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
772 // Can't call client anymore after Destroy() returns.
773 client_ptr_factory_.reset();
774 weak_this_ptr_factory_.InvalidateWeakPtrs();
776 // Early-exit encoder tasks if they are running and join the thread.
777 if (encoder_thread_.IsRunning()) {
778 encoder_thread_.message_loop()->PostTask(
779 FROM_HERE,
780 base::Bind(&VaapiVideoEncodeAccelerator::DestroyTask,
781 base::Unretained(this)));
782 encoder_thread_.Stop();
785 delete this;
788 void VaapiVideoEncodeAccelerator::DestroyTask() {
789 DVLOGF(2);
790 DCHECK(encoder_thread_proxy_->BelongsToCurrentThread());
791 SetState(kError);
794 void VaapiVideoEncodeAccelerator::UpdateSPS() {
795 memset(&current_sps_, 0, sizeof(media::H264SPS));
797 // Spec A.2 and A.3.
798 switch (profile_) {
799 case media::H264PROFILE_BASELINE:
800 // Due to crbug.com/345569, we don't distinguish between constrained
801 // and non-constrained baseline profiles. Since many codecs can't do
802 // non-constrained, and constrained is usually what we mean (and it's a
803 // subset of non-constrained), default to it.
804 current_sps_.profile_idc = media::H264SPS::kProfileIDCBaseline;
805 current_sps_.constraint_set0_flag = true;
806 break;
807 case media::H264PROFILE_MAIN:
808 current_sps_.profile_idc = media::H264SPS::kProfileIDCMain;
809 current_sps_.constraint_set1_flag = true;
810 break;
811 case media::H264PROFILE_HIGH:
812 current_sps_.profile_idc = media::H264SPS::kProfileIDCHigh;
813 break;
814 default:
815 NOTIMPLEMENTED();
816 return;
819 current_sps_.level_idc = kDefaultLevelIDC;
820 current_sps_.seq_parameter_set_id = 0;
821 current_sps_.chroma_format_idc = kChromaFormatIDC;
823 DCHECK_GE(idr_period_, 1u << 4);
824 current_sps_.log2_max_frame_num_minus4 = Log2OfPowerOf2(idr_period_) - 4;
825 current_sps_.pic_order_cnt_type = 0;
826 current_sps_.log2_max_pic_order_cnt_lsb_minus4 =
827 Log2OfPowerOf2(idr_period_ * 2) - 4;
828 current_sps_.max_num_ref_frames = max_ref_idx_l0_size_;
830 current_sps_.frame_mbs_only_flag = true;
832 DCHECK_GT(mb_width_, 0u);
833 DCHECK_GT(mb_height_, 0u);
834 current_sps_.pic_width_in_mbs_minus1 = mb_width_ - 1;
835 DCHECK(current_sps_.frame_mbs_only_flag);
836 current_sps_.pic_height_in_map_units_minus1 = mb_height_ - 1;
838 if (visible_size_ != coded_size_) {
839 // Visible size differs from coded size, fill crop information.
840 current_sps_.frame_cropping_flag = true;
841 DCHECK(!current_sps_.separate_colour_plane_flag);
842 // Spec table 6-1. Only 4:2:0 for now.
843 DCHECK_EQ(current_sps_.chroma_format_idc, 1);
844 // Spec 7.4.2.1.1. Crop is in crop units, which is 2 pixels for 4:2:0.
845 const unsigned int crop_unit_x = 2;
846 const unsigned int crop_unit_y = 2 * (2 - current_sps_.frame_mbs_only_flag);
847 current_sps_.frame_crop_left_offset = 0;
848 current_sps_.frame_crop_right_offset =
849 (coded_size_.width() - visible_size_.width()) / crop_unit_x;
850 current_sps_.frame_crop_top_offset = 0;
851 current_sps_.frame_crop_bottom_offset =
852 (coded_size_.height() - visible_size_.height()) / crop_unit_y;
855 current_sps_.vui_parameters_present_flag = true;
856 current_sps_.timing_info_present_flag = true;
857 current_sps_.num_units_in_tick = 1;
858 current_sps_.time_scale = framerate_ * 2; // See equation D-2 in spec.
859 current_sps_.fixed_frame_rate_flag = true;
861 current_sps_.nal_hrd_parameters_present_flag = true;
862 // H.264 spec ch. E.2.2.
863 current_sps_.cpb_cnt_minus1 = 0;
864 current_sps_.bit_rate_scale = kBitRateScale;
865 current_sps_.cpb_size_scale = kCPBSizeScale;
866 current_sps_.bit_rate_value_minus1[0] =
867 (bitrate_ >>
868 (kBitRateScale + media::H264SPS::kBitRateScaleConstantTerm)) - 1;
869 current_sps_.cpb_size_value_minus1[0] =
870 (cpb_size_ >>
871 (kCPBSizeScale + media::H264SPS::kCPBSizeScaleConstantTerm)) - 1;
872 current_sps_.cbr_flag[0] = true;
873 current_sps_.initial_cpb_removal_delay_length_minus_1 =
874 media::H264SPS::kDefaultInitialCPBRemovalDelayLength - 1;
875 current_sps_.cpb_removal_delay_length_minus1 =
876 media::H264SPS::kDefaultInitialCPBRemovalDelayLength - 1;
877 current_sps_.dpb_output_delay_length_minus1 =
878 media::H264SPS::kDefaultDPBOutputDelayLength - 1;
879 current_sps_.time_offset_length = media::H264SPS::kDefaultTimeOffsetLength;
880 current_sps_.low_delay_hrd_flag = false;
883 void VaapiVideoEncodeAccelerator::GeneratePackedSPS() {
884 packed_sps_.Reset();
886 packed_sps_.BeginNALU(media::H264NALU::kSPS, 3);
888 packed_sps_.AppendBits(8, current_sps_.profile_idc);
889 packed_sps_.AppendBool(current_sps_.constraint_set0_flag);
890 packed_sps_.AppendBool(current_sps_.constraint_set1_flag);
891 packed_sps_.AppendBool(current_sps_.constraint_set2_flag);
892 packed_sps_.AppendBool(current_sps_.constraint_set3_flag);
893 packed_sps_.AppendBool(current_sps_.constraint_set4_flag);
894 packed_sps_.AppendBool(current_sps_.constraint_set5_flag);
895 packed_sps_.AppendBits(2, 0); // reserved_zero_2bits
896 packed_sps_.AppendBits(8, current_sps_.level_idc);
897 packed_sps_.AppendUE(current_sps_.seq_parameter_set_id);
899 if (current_sps_.profile_idc == media::H264SPS::kProfileIDCHigh) {
900 packed_sps_.AppendUE(current_sps_.chroma_format_idc);
901 if (current_sps_.chroma_format_idc == 3)
902 packed_sps_.AppendBool(current_sps_.separate_colour_plane_flag);
903 packed_sps_.AppendUE(current_sps_.bit_depth_luma_minus8);
904 packed_sps_.AppendUE(current_sps_.bit_depth_chroma_minus8);
905 packed_sps_.AppendBool(current_sps_.qpprime_y_zero_transform_bypass_flag);
906 packed_sps_.AppendBool(current_sps_.seq_scaling_matrix_present_flag);
907 CHECK(!current_sps_.seq_scaling_matrix_present_flag);
910 packed_sps_.AppendUE(current_sps_.log2_max_frame_num_minus4);
911 packed_sps_.AppendUE(current_sps_.pic_order_cnt_type);
912 if (current_sps_.pic_order_cnt_type == 0)
913 packed_sps_.AppendUE(current_sps_.log2_max_pic_order_cnt_lsb_minus4);
914 else if (current_sps_.pic_order_cnt_type == 1) {
915 CHECK(1);
918 packed_sps_.AppendUE(current_sps_.max_num_ref_frames);
919 packed_sps_.AppendBool(current_sps_.gaps_in_frame_num_value_allowed_flag);
920 packed_sps_.AppendUE(current_sps_.pic_width_in_mbs_minus1);
921 packed_sps_.AppendUE(current_sps_.pic_height_in_map_units_minus1);
923 packed_sps_.AppendBool(current_sps_.frame_mbs_only_flag);
924 if (!current_sps_.frame_mbs_only_flag)
925 packed_sps_.AppendBool(current_sps_.mb_adaptive_frame_field_flag);
927 packed_sps_.AppendBool(current_sps_.direct_8x8_inference_flag);
929 packed_sps_.AppendBool(current_sps_.frame_cropping_flag);
930 if (current_sps_.frame_cropping_flag) {
931 packed_sps_.AppendUE(current_sps_.frame_crop_left_offset);
932 packed_sps_.AppendUE(current_sps_.frame_crop_right_offset);
933 packed_sps_.AppendUE(current_sps_.frame_crop_top_offset);
934 packed_sps_.AppendUE(current_sps_.frame_crop_bottom_offset);
937 packed_sps_.AppendBool(current_sps_.vui_parameters_present_flag);
938 if (current_sps_.vui_parameters_present_flag) {
939 packed_sps_.AppendBool(false); // aspect_ratio_info_present_flag
940 packed_sps_.AppendBool(false); // overscan_info_present_flag
941 packed_sps_.AppendBool(false); // video_signal_type_present_flag
942 packed_sps_.AppendBool(false); // chroma_loc_info_present_flag
944 packed_sps_.AppendBool(current_sps_.timing_info_present_flag);
945 if (current_sps_.timing_info_present_flag) {
946 packed_sps_.AppendBits(32, current_sps_.num_units_in_tick);
947 packed_sps_.AppendBits(32, current_sps_.time_scale);
948 packed_sps_.AppendBool(current_sps_.fixed_frame_rate_flag);
951 packed_sps_.AppendBool(current_sps_.nal_hrd_parameters_present_flag);
952 if (current_sps_.nal_hrd_parameters_present_flag) {
953 packed_sps_.AppendUE(current_sps_.cpb_cnt_minus1);
954 packed_sps_.AppendBits(4, current_sps_.bit_rate_scale);
955 packed_sps_.AppendBits(4, current_sps_.cpb_size_scale);
956 CHECK_LT(base::checked_cast<size_t>(current_sps_.cpb_cnt_minus1),
957 arraysize(current_sps_.bit_rate_value_minus1));
958 for (int i = 0; i <= current_sps_.cpb_cnt_minus1; ++i) {
959 packed_sps_.AppendUE(current_sps_.bit_rate_value_minus1[i]);
960 packed_sps_.AppendUE(current_sps_.cpb_size_value_minus1[i]);
961 packed_sps_.AppendBool(current_sps_.cbr_flag[i]);
963 packed_sps_.AppendBits(
964 5, current_sps_.initial_cpb_removal_delay_length_minus_1);
965 packed_sps_.AppendBits(5, current_sps_.cpb_removal_delay_length_minus1);
966 packed_sps_.AppendBits(5, current_sps_.dpb_output_delay_length_minus1);
967 packed_sps_.AppendBits(5, current_sps_.time_offset_length);
970 packed_sps_.AppendBool(false); // vcl_hrd_parameters_flag
971 if (current_sps_.nal_hrd_parameters_present_flag)
972 packed_sps_.AppendBool(current_sps_.low_delay_hrd_flag);
974 packed_sps_.AppendBool(false); // pic_struct_present_flag
975 packed_sps_.AppendBool(true); // bitstream_restriction_flag
977 packed_sps_.AppendBool(false); // motion_vectors_over_pic_boundaries_flag
978 packed_sps_.AppendUE(2); // max_bytes_per_pic_denom
979 packed_sps_.AppendUE(1); // max_bits_per_mb_denom
980 packed_sps_.AppendUE(16); // log2_max_mv_length_horizontal
981 packed_sps_.AppendUE(16); // log2_max_mv_length_vertical
983 // Explicitly set max_num_reorder_frames to 0 to allow the decoder to
984 // output pictures early.
985 packed_sps_.AppendUE(0); // max_num_reorder_frames
987 // The value of max_dec_frame_buffering shall be greater than or equal to
988 // max_num_ref_frames.
989 const unsigned int max_dec_frame_buffering =
990 current_sps_.max_num_ref_frames;
991 packed_sps_.AppendUE(max_dec_frame_buffering);
994 packed_sps_.FinishNALU();
997 void VaapiVideoEncodeAccelerator::UpdatePPS() {
998 memset(&current_pps_, 0, sizeof(media::H264PPS));
1000 current_pps_.seq_parameter_set_id = current_sps_.seq_parameter_set_id;
1001 current_pps_.pic_parameter_set_id = 0;
1003 current_pps_.entropy_coding_mode_flag =
1004 current_sps_.profile_idc >= media::H264SPS::kProfileIDCMain;
1006 CHECK_GT(max_ref_idx_l0_size_, 0u);
1007 current_pps_.num_ref_idx_l0_default_active_minus1 = max_ref_idx_l0_size_ - 1;
1008 current_pps_.num_ref_idx_l1_default_active_minus1 = 0;
1009 DCHECK_LE(qp_, 51u);
1010 current_pps_.pic_init_qp_minus26 = qp_ - 26;
1011 current_pps_.deblocking_filter_control_present_flag = true;
1012 current_pps_.transform_8x8_mode_flag =
1013 (current_sps_.profile_idc == media::H264SPS::kProfileIDCHigh);
1016 void VaapiVideoEncodeAccelerator::GeneratePackedPPS() {
1017 packed_pps_.Reset();
1019 packed_pps_.BeginNALU(media::H264NALU::kPPS, 3);
1021 packed_pps_.AppendUE(current_pps_.pic_parameter_set_id);
1022 packed_pps_.AppendUE(current_pps_.seq_parameter_set_id);
1023 packed_pps_.AppendBool(current_pps_.entropy_coding_mode_flag);
1024 packed_pps_.AppendBool(
1025 current_pps_.bottom_field_pic_order_in_frame_present_flag);
1026 CHECK_EQ(current_pps_.num_slice_groups_minus1, 0);
1027 packed_pps_.AppendUE(current_pps_.num_slice_groups_minus1);
1029 packed_pps_.AppendUE(current_pps_.num_ref_idx_l0_default_active_minus1);
1030 packed_pps_.AppendUE(current_pps_.num_ref_idx_l1_default_active_minus1);
1032 packed_pps_.AppendBool(current_pps_.weighted_pred_flag);
1033 packed_pps_.AppendBits(2, current_pps_.weighted_bipred_idc);
1035 packed_pps_.AppendSE(current_pps_.pic_init_qp_minus26);
1036 packed_pps_.AppendSE(current_pps_.pic_init_qs_minus26);
1037 packed_pps_.AppendSE(current_pps_.chroma_qp_index_offset);
1039 packed_pps_.AppendBool(current_pps_.deblocking_filter_control_present_flag);
1040 packed_pps_.AppendBool(current_pps_.constrained_intra_pred_flag);
1041 packed_pps_.AppendBool(current_pps_.redundant_pic_cnt_present_flag);
1043 packed_pps_.AppendBool(current_pps_.transform_8x8_mode_flag);
1044 packed_pps_.AppendBool(current_pps_.pic_scaling_matrix_present_flag);
1045 DCHECK(!current_pps_.pic_scaling_matrix_present_flag);
1046 packed_pps_.AppendSE(current_pps_.second_chroma_qp_index_offset);
1048 packed_pps_.FinishNALU();
1051 void VaapiVideoEncodeAccelerator::SetState(State state) {
1052 // Only touch state on encoder thread, unless it's not running.
1053 if (encoder_thread_.IsRunning() &&
1054 !encoder_thread_proxy_->BelongsToCurrentThread()) {
1055 encoder_thread_proxy_->PostTask(
1056 FROM_HERE,
1057 base::Bind(&VaapiVideoEncodeAccelerator::SetState,
1058 base::Unretained(this),
1059 state));
1060 return;
1063 DVLOGF(1) << "setting state to: " << state;
1064 state_ = state;
1067 void VaapiVideoEncodeAccelerator::NotifyError(Error error) {
1068 if (!child_message_loop_proxy_->BelongsToCurrentThread()) {
1069 child_message_loop_proxy_->PostTask(
1070 FROM_HERE,
1071 base::Bind(
1072 &VaapiVideoEncodeAccelerator::NotifyError, weak_this_, error));
1073 return;
1076 if (client_) {
1077 client_->NotifyError(error);
1078 client_ptr_factory_.reset();
1082 VaapiVideoEncodeAccelerator::EncodeJob::EncodeJob()
1083 : coded_buffer(VA_INVALID_ID), keyframe(false) {
1086 VaapiVideoEncodeAccelerator::EncodeJob::~EncodeJob() {
1089 } // namespace content