vaapi_video_encode_accelerator.cc revision 34680572440d7894ef8dafce81d8039ed80726a2
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.
4
5#include "content/common/gpu/media/vaapi_video_encode_accelerator.h"
6
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"
17
18#define DVLOGF(level) DVLOG(level) << __FUNCTION__ << "(): "
19
20#define NOTIFY_ERROR(error, msg)                         \
21  do {                                                   \
22    SetState(kError);                                    \
23    DVLOGF(1) << msg;                                    \
24    DVLOGF(1) << "Calling NotifyError(" << error << ")"; \
25    NotifyError(error);                                  \
26  } while (0)
27
28namespace content {
29
30namespace {
31// Need 2 surfaces for each frame: one for input data and one for
32// reconstructed picture, which is later used for reference.
33const size_t kMinSurfacesToEncode = 2;
34
35// Subjectively chosen.
36const size_t kNumInputBuffers = 4;
37const size_t kMaxNumReferenceFrames = 4;
38
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.
47const size_t kNumSurfaces = kMaxNumReferenceFrames + kMinSurfacesToEncode +
48                            kMinSurfacesToEncode * (kNumInputBuffers - 1);
49
50// An IDR every 128 frames, an I frame every 30 and no B frames.
51const int kIDRPeriod = 128;
52const int kIPeriod = 30;
53const int kIPPeriod = 1;
54
55const int kDefaultFramerate = 30;
56
57// HRD parameters (ch. E.2.2 in spec).
58const int kBitRateScale = 0;  // bit_rate_scale for SPS HRD parameters.
59const int kCPBSizeScale = 0;  // cpb_size_scale for SPS HRD parameters.
60
61const int kDefaultQP = 26;
62// All Intel codecs can do at least 4.1.
63const int kDefaultLevelIDC = 41;
64const int kChromaFormatIDC = 1;  // 4:2:0
65
66// Arbitrarily chosen bitrate window size for rate control, in ms.
67const int kCPBWindowSizeMs = 1500;
68
69// UMA errors that the VaapiVideoEncodeAccelerator class reports.
70enum VAVEAEncoderFailure {
71  VAAPI_ERROR = 0,
72  VAVEA_ENCODER_FAILURES_MAX,
73};
74
75}
76
77// Round |value| up to |alignment|, which must be a power of 2.
78static inline size_t RoundUpToPowerOf2(size_t value, size_t alignment) {
79  // Check that |alignment| is a power of 2.
80  DCHECK((alignment + (alignment - 1)) == (alignment | (alignment - 1)));
81  return ((value + (alignment - 1)) & ~(alignment - 1));
82}
83
84static void ReportToUMA(VAVEAEncoderFailure failure) {
85  UMA_HISTOGRAM_ENUMERATION(
86      "Media.VAVEA.EncoderFailure",
87      failure,
88      VAVEA_ENCODER_FAILURES_MAX);
89}
90
91struct VaapiVideoEncodeAccelerator::InputFrameRef {
92  InputFrameRef(const scoped_refptr<media::VideoFrame>& frame,
93                bool force_keyframe)
94      : frame(frame), force_keyframe(force_keyframe) {}
95  const scoped_refptr<media::VideoFrame> frame;
96  const bool force_keyframe;
97};
98
99struct VaapiVideoEncodeAccelerator::BitstreamBufferRef {
100  BitstreamBufferRef(int32 id, scoped_ptr<base::SharedMemory> shm, size_t size)
101      : id(id), shm(shm.Pass()), size(size) {}
102  const int32 id;
103  const scoped_ptr<base::SharedMemory> shm;
104  const size_t size;
105};
106
107std::vector<media::VideoEncodeAccelerator::SupportedProfile>
108VaapiVideoEncodeAccelerator::GetSupportedProfiles() {
109  std::vector<SupportedProfile> profiles;
110
111  const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
112  if (cmd_line->HasSwitch(switches::kDisableVaapiAcceleratedVideoEncode))
113    return profiles;
114
115  std::vector<media::VideoCodecProfile> hw_profiles =
116      VaapiWrapper::GetSupportedEncodeProfiles(
117          x_display_, base::Bind(&base::DoNothing));
118
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);
126  }
127  return profiles;
128}
129
130static unsigned int Log2OfPowerOf2(unsigned int x) {
131  CHECK_GT(x, 0u);
132  DCHECK_EQ(x & (x - 1), 0u);
133
134  int log = 0;
135  while (x) {
136    x >>= 1;
137    ++log;
138  }
139  return log;
140}
141
142VaapiVideoEncodeAccelerator::VaapiVideoEncodeAccelerator(Display* x_display)
143    : profile_(media::VIDEO_CODEC_PROFILE_UNKNOWN),
144      mb_width_(0),
145      mb_height_(0),
146      output_buffer_byte_size_(0),
147      x_display_(x_display),
148      state_(kUninitialized),
149      frame_num_(0),
150      last_idr_frame_num_(0),
151      bitrate_(0),
152      framerate_(0),
153      cpb_size_(0),
154      encoding_parameters_changed_(false),
155      encoder_thread_("VAVEAEncoderThread"),
156      child_message_loop_proxy_(base::MessageLoopProxy::current()),
157      weak_this_ptr_factory_(this) {
158  DVLOGF(4);
159  weak_this_ = weak_this_ptr_factory_.GetWeakPtr();
160
161  max_ref_idx_l0_size_ = kMaxNumReferenceFrames;
162  qp_ = kDefaultQP;
163  idr_period_ = kIDRPeriod;
164  i_period_ = kIPeriod;
165  ip_period_ = kIPPeriod;
166}
167
168VaapiVideoEncodeAccelerator::~VaapiVideoEncodeAccelerator() {
169  DVLOGF(4);
170  DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
171  DCHECK(!encoder_thread_.IsRunning());
172}
173
174bool VaapiVideoEncodeAccelerator::Initialize(
175    media::VideoFrame::Format format,
176    const gfx::Size& input_visible_size,
177    media::VideoCodecProfile output_profile,
178    uint32 initial_bitrate,
179    Client* client) {
180  DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
181  DCHECK(!encoder_thread_.IsRunning());
182  DCHECK_EQ(state_, kUninitialized);
183
184  DVLOGF(1) << "Initializing VAVEA, input_format: "
185            << media::VideoFrame::FormatToString(format)
186            << ", input_visible_size: " << input_visible_size.ToString()
187            << ", output_profile: " << output_profile
188            << ", initial_bitrate: " << initial_bitrate;
189
190  client_ptr_factory_.reset(new base::WeakPtrFactory<Client>(client));
191  client_ = client_ptr_factory_->GetWeakPtr();
192
193  if (output_profile < media::H264PROFILE_BASELINE ||
194      output_profile > media::H264PROFILE_MAIN) {
195    DVLOGF(1) << "Unsupported output profile: " << output_profile;
196    return false;
197  }
198
199  if (format != media::VideoFrame::I420) {
200    DVLOGF(1) << "Unsupported input format: "
201              << media::VideoFrame::FormatToString(format);
202    return false;
203  }
204
205  profile_ = output_profile;
206  visible_size_ = input_visible_size;
207  // 4:2:0 format has to be 2-aligned.
208  DCHECK_EQ(visible_size_.width() % 2, 0);
209  DCHECK_EQ(visible_size_.height() % 2, 0);
210  coded_size_ = gfx::Size(RoundUpToPowerOf2(visible_size_.width(), 16),
211                          RoundUpToPowerOf2(visible_size_.height(), 16));
212  mb_width_ = coded_size_.width() / 16;
213  mb_height_ = coded_size_.height() / 16;
214  output_buffer_byte_size_ = coded_size_.GetArea();
215
216  UpdateRates(initial_bitrate, kDefaultFramerate);
217
218  vaapi_wrapper_ = VaapiWrapper::Create(VaapiWrapper::kEncode,
219                                        output_profile,
220                                        x_display_,
221                                        base::Bind(&ReportToUMA, VAAPI_ERROR));
222  if (!vaapi_wrapper_) {
223    DVLOGF(1) << "Failed initializing VAAPI";
224    return false;
225  }
226
227  if (!encoder_thread_.Start()) {
228    DVLOGF(1) << "Failed to start encoder thread";
229    return false;
230  }
231  encoder_thread_proxy_ = encoder_thread_.message_loop_proxy();
232
233  // Finish the remaining initialization on the encoder thread.
234  encoder_thread_proxy_->PostTask(
235      FROM_HERE,
236      base::Bind(&VaapiVideoEncodeAccelerator::InitializeTask,
237                 base::Unretained(this)));
238
239  return true;
240}
241
242void VaapiVideoEncodeAccelerator::InitializeTask() {
243  DCHECK(encoder_thread_proxy_->BelongsToCurrentThread());
244  DCHECK_EQ(state_, kUninitialized);
245  DVLOGF(4);
246
247  va_surface_release_cb_ = media::BindToCurrentLoop(
248      base::Bind(&VaapiVideoEncodeAccelerator::RecycleVASurfaceID,
249                 base::Unretained(this)));
250
251  if (!vaapi_wrapper_->CreateSurfaces(
252          coded_size_, kNumSurfaces, &available_va_surface_ids_)) {
253    NOTIFY_ERROR(kPlatformFailureError, "Failed creating VASurfaces");
254    return;
255  }
256
257  UpdateSPS();
258  GeneratePackedSPS();
259
260  UpdatePPS();
261  GeneratePackedPPS();
262
263  child_message_loop_proxy_->PostTask(
264      FROM_HERE,
265      base::Bind(&Client::RequireBitstreamBuffers,
266                 client_,
267                 kNumInputBuffers,
268                 coded_size_,
269                 output_buffer_byte_size_));
270
271  SetState(kEncoding);
272}
273
274void VaapiVideoEncodeAccelerator::RecycleVASurfaceID(
275    VASurfaceID va_surface_id) {
276  DVLOGF(4) << "va_surface_id: " << va_surface_id;
277  DCHECK(encoder_thread_proxy_->BelongsToCurrentThread());
278
279  available_va_surface_ids_.push_back(va_surface_id);
280  EncodeFrameTask();
281}
282
283void VaapiVideoEncodeAccelerator::BeginFrame(bool force_keyframe) {
284  memset(&current_pic_, 0, sizeof(current_pic_));
285
286  current_pic_.frame_num = frame_num_++;
287  frame_num_ %= idr_period_;
288
289  if (current_pic_.frame_num % i_period_ == 0 || force_keyframe)
290    current_pic_.type = media::H264SliceHeader::kISlice;
291  else
292    current_pic_.type = media::H264SliceHeader::kPSlice;
293
294  if (current_pic_.frame_num % idr_period_ == 0) {
295    current_pic_.idr = true;
296    last_idr_frame_num_ = current_pic_.frame_num;
297    ref_pic_list0_.clear();
298  }
299
300  if (current_pic_.type != media::H264SliceHeader::kBSlice)
301    current_pic_.ref = true;
302
303  current_pic_.pic_order_cnt = current_pic_.frame_num * 2;
304  current_pic_.top_field_order_cnt = current_pic_.pic_order_cnt;
305  current_pic_.pic_order_cnt_lsb = current_pic_.pic_order_cnt;
306
307  current_encode_job_->keyframe =
308      (current_pic_.type == media::H264SliceHeader::kISlice);
309
310  DVLOGF(4) << "Starting a new frame, type: " << current_pic_.type
311            << (force_keyframe ? " (forced keyframe)" : "")
312            << " frame_num: " << current_pic_.frame_num
313            << " POC: " << current_pic_.pic_order_cnt;
314}
315
316void VaapiVideoEncodeAccelerator::EndFrame() {
317  // Store the picture on the list of reference pictures and keep the list
318  // below maximum size, dropping oldest references.
319  if (current_pic_.ref)
320    ref_pic_list0_.push_front(current_encode_job_->recon_surface);
321  size_t max_num_ref_frames =
322      base::checked_cast<size_t>(current_sps_.max_num_ref_frames);
323  while (ref_pic_list0_.size() > max_num_ref_frames)
324    ref_pic_list0_.pop_back();
325
326  submitted_encode_jobs_.push(make_linked_ptr(current_encode_job_.release()));
327}
328
329static void InitVAPicture(VAPictureH264* va_pic) {
330  memset(va_pic, 0, sizeof(*va_pic));
331  va_pic->picture_id = VA_INVALID_ID;
332  va_pic->flags = VA_PICTURE_H264_INVALID;
333}
334
335bool VaapiVideoEncodeAccelerator::SubmitFrameParameters() {
336  VAEncSequenceParameterBufferH264 seq_param;
337  memset(&seq_param, 0, sizeof(seq_param));
338
339#define SPS_TO_SP(a) seq_param.a = current_sps_.a;
340  SPS_TO_SP(seq_parameter_set_id);
341  SPS_TO_SP(level_idc);
342
343  seq_param.intra_period = i_period_;
344  seq_param.intra_idr_period = idr_period_;
345  seq_param.ip_period = ip_period_;
346  seq_param.bits_per_second = bitrate_;
347
348  SPS_TO_SP(max_num_ref_frames);
349  seq_param.picture_width_in_mbs = mb_width_;
350  seq_param.picture_height_in_mbs = mb_height_;
351
352#define SPS_TO_SP_FS(a) seq_param.seq_fields.bits.a = current_sps_.a;
353  SPS_TO_SP_FS(chroma_format_idc);
354  SPS_TO_SP_FS(frame_mbs_only_flag);
355  SPS_TO_SP_FS(log2_max_frame_num_minus4);
356  SPS_TO_SP_FS(pic_order_cnt_type);
357  SPS_TO_SP_FS(log2_max_pic_order_cnt_lsb_minus4);
358#undef SPS_TO_SP_FS
359
360  SPS_TO_SP(bit_depth_luma_minus8);
361  SPS_TO_SP(bit_depth_chroma_minus8);
362
363  SPS_TO_SP(frame_cropping_flag);
364  if (current_sps_.frame_cropping_flag) {
365    SPS_TO_SP(frame_crop_left_offset);
366    SPS_TO_SP(frame_crop_right_offset);
367    SPS_TO_SP(frame_crop_top_offset);
368    SPS_TO_SP(frame_crop_bottom_offset);
369  }
370
371  SPS_TO_SP(vui_parameters_present_flag);
372#define SPS_TO_SP_VF(a) seq_param.vui_fields.bits.a = current_sps_.a;
373  SPS_TO_SP_VF(timing_info_present_flag);
374#undef SPS_TO_SP_VF
375  SPS_TO_SP(num_units_in_tick);
376  SPS_TO_SP(time_scale);
377#undef SPS_TO_SP
378
379  if (!vaapi_wrapper_->SubmitBuffer(VAEncSequenceParameterBufferType,
380                                    sizeof(seq_param),
381                                    &seq_param))
382    return false;
383
384  VAEncPictureParameterBufferH264 pic_param;
385  memset(&pic_param, 0, sizeof(pic_param));
386
387  pic_param.CurrPic.picture_id = current_encode_job_->recon_surface->id();
388  pic_param.CurrPic.TopFieldOrderCnt = current_pic_.top_field_order_cnt;
389  pic_param.CurrPic.BottomFieldOrderCnt = current_pic_.bottom_field_order_cnt;
390  pic_param.CurrPic.flags = 0;
391
392  for (size_t i = 0; i < arraysize(pic_param.ReferenceFrames); ++i)
393    InitVAPicture(&pic_param.ReferenceFrames[i]);
394
395  DCHECK_LE(ref_pic_list0_.size(), arraysize(pic_param.ReferenceFrames));
396  RefPicList::const_iterator iter = ref_pic_list0_.begin();
397  for (size_t i = 0;
398       i < arraysize(pic_param.ReferenceFrames) && iter != ref_pic_list0_.end();
399       ++iter, ++i) {
400    pic_param.ReferenceFrames[i].picture_id = (*iter)->id();
401    pic_param.ReferenceFrames[i].flags = 0;
402  }
403
404  pic_param.coded_buf = current_encode_job_->coded_buffer;
405  pic_param.pic_parameter_set_id = current_pps_.pic_parameter_set_id;
406  pic_param.seq_parameter_set_id = current_pps_.seq_parameter_set_id;
407  pic_param.frame_num = current_pic_.frame_num;
408  pic_param.pic_init_qp = qp_;
409  pic_param.num_ref_idx_l0_active_minus1 = max_ref_idx_l0_size_ - 1;
410  pic_param.pic_fields.bits.idr_pic_flag = current_pic_.idr;
411  pic_param.pic_fields.bits.reference_pic_flag = current_pic_.ref;
412#define PPS_TO_PP_PF(a) pic_param.pic_fields.bits.a = current_pps_.a;
413  PPS_TO_PP_PF(entropy_coding_mode_flag);
414  PPS_TO_PP_PF(transform_8x8_mode_flag);
415  PPS_TO_PP_PF(deblocking_filter_control_present_flag);
416#undef PPS_TO_PP_PF
417
418  if (!vaapi_wrapper_->SubmitBuffer(VAEncPictureParameterBufferType,
419                                    sizeof(pic_param),
420                                    &pic_param))
421    return false;
422
423  VAEncSliceParameterBufferH264 slice_param;
424  memset(&slice_param, 0, sizeof(slice_param));
425
426  slice_param.num_macroblocks = mb_width_ * mb_height_;
427  slice_param.macroblock_info = VA_INVALID_ID;
428  slice_param.slice_type = current_pic_.type;
429  slice_param.pic_parameter_set_id = current_pps_.pic_parameter_set_id;
430  slice_param.idr_pic_id = last_idr_frame_num_;
431  slice_param.pic_order_cnt_lsb = current_pic_.pic_order_cnt_lsb;
432  slice_param.num_ref_idx_active_override_flag = true;
433
434  for (size_t i = 0; i < arraysize(slice_param.RefPicList0); ++i)
435    InitVAPicture(&slice_param.RefPicList0[i]);
436
437  for (size_t i = 0; i < arraysize(slice_param.RefPicList1); ++i)
438    InitVAPicture(&slice_param.RefPicList1[i]);
439
440  DCHECK_LE(ref_pic_list0_.size(), arraysize(slice_param.RefPicList0));
441  iter = ref_pic_list0_.begin();
442  for (size_t i = 0;
443       i < arraysize(slice_param.RefPicList0) && iter != ref_pic_list0_.end();
444       ++iter, ++i) {
445    InitVAPicture(&slice_param.RefPicList0[i]);
446    slice_param.RefPicList0[i].picture_id = (*iter)->id();
447    slice_param.RefPicList0[i].flags = 0;
448  }
449
450  if (!vaapi_wrapper_->SubmitBuffer(VAEncSliceParameterBufferType,
451                                    sizeof(slice_param),
452                                    &slice_param))
453    return false;
454
455  VAEncMiscParameterRateControl rate_control_param;
456  memset(&rate_control_param, 0, sizeof(rate_control_param));
457  rate_control_param.bits_per_second = bitrate_;
458  rate_control_param.target_percentage = 90;
459  rate_control_param.window_size = kCPBWindowSizeMs;
460  rate_control_param.initial_qp = qp_;
461  rate_control_param.rc_flags.bits.disable_frame_skip = true;
462
463  if (!vaapi_wrapper_->SubmitVAEncMiscParamBuffer(
464          VAEncMiscParameterTypeRateControl,
465          sizeof(rate_control_param),
466          &rate_control_param))
467    return false;
468
469  VAEncMiscParameterFrameRate framerate_param;
470  memset(&framerate_param, 0, sizeof(framerate_param));
471  framerate_param.framerate = framerate_;
472  if (!vaapi_wrapper_->SubmitVAEncMiscParamBuffer(
473          VAEncMiscParameterTypeFrameRate,
474          sizeof(framerate_param),
475          &framerate_param))
476    return false;
477
478  VAEncMiscParameterHRD hrd_param;
479  memset(&hrd_param, 0, sizeof(hrd_param));
480  hrd_param.buffer_size = cpb_size_;
481  hrd_param.initial_buffer_fullness = cpb_size_ / 2;
482  if (!vaapi_wrapper_->SubmitVAEncMiscParamBuffer(VAEncMiscParameterTypeHRD,
483                                                  sizeof(hrd_param),
484                                                  &hrd_param))
485    return false;
486
487  return true;
488}
489
490bool VaapiVideoEncodeAccelerator::SubmitHeadersIfNeeded() {
491  if (current_pic_.type != media::H264SliceHeader::kISlice)
492    return true;
493
494  // Submit PPS.
495  VAEncPackedHeaderParameterBuffer par_buffer;
496  memset(&par_buffer, 0, sizeof(par_buffer));
497  par_buffer.type = VAEncPackedHeaderSequence;
498  par_buffer.bit_length = packed_sps_.BytesInBuffer() * 8;
499
500  if (!vaapi_wrapper_->SubmitBuffer(VAEncPackedHeaderParameterBufferType,
501                                    sizeof(par_buffer),
502                                    &par_buffer))
503    return false;
504
505  if (!vaapi_wrapper_->SubmitBuffer(VAEncPackedHeaderDataBufferType,
506                                    packed_sps_.BytesInBuffer(),
507                                    packed_sps_.data()))
508    return false;
509
510  // Submit PPS.
511  memset(&par_buffer, 0, sizeof(par_buffer));
512  par_buffer.type = VAEncPackedHeaderPicture;
513  par_buffer.bit_length = packed_pps_.BytesInBuffer() * 8;
514
515  if (!vaapi_wrapper_->SubmitBuffer(VAEncPackedHeaderParameterBufferType,
516                                    sizeof(par_buffer),
517                                    &par_buffer))
518    return false;
519
520  if (!vaapi_wrapper_->SubmitBuffer(VAEncPackedHeaderDataBufferType,
521                                    packed_pps_.BytesInBuffer(),
522                                    packed_pps_.data()))
523    return false;
524
525  return true;
526}
527
528bool VaapiVideoEncodeAccelerator::ExecuteEncode() {
529  DVLOGF(3) << "Encoding frame_num: " << current_pic_.frame_num;
530  return vaapi_wrapper_->ExecuteAndDestroyPendingBuffers(
531      current_encode_job_->input_surface->id());
532}
533
534bool VaapiVideoEncodeAccelerator::UploadFrame(
535    const scoped_refptr<media::VideoFrame>& frame) {
536  return vaapi_wrapper_->UploadVideoFrameToSurface(
537      frame, current_encode_job_->input_surface->id());
538}
539
540void VaapiVideoEncodeAccelerator::TryToReturnBitstreamBuffer() {
541  DCHECK(encoder_thread_proxy_->BelongsToCurrentThread());
542
543  if (state_ != kEncoding)
544    return;
545
546  if (submitted_encode_jobs_.empty() || available_bitstream_buffers_.empty())
547    return;
548
549  linked_ptr<BitstreamBufferRef> buffer = available_bitstream_buffers_.front();
550  available_bitstream_buffers_.pop();
551
552  uint8* target_data = reinterpret_cast<uint8*>(buffer->shm->memory());
553
554  linked_ptr<EncodeJob> encode_job = submitted_encode_jobs_.front();
555  submitted_encode_jobs_.pop();
556
557  size_t data_size = 0;
558  if (!vaapi_wrapper_->DownloadAndDestroyCodedBuffer(
559          encode_job->coded_buffer,
560          encode_job->input_surface->id(),
561          target_data,
562          buffer->size,
563          &data_size)) {
564    NOTIFY_ERROR(kPlatformFailureError, "Failed downloading coded buffer");
565    return;
566  }
567
568  DVLOGF(3) << "Returning bitstream buffer "
569            << (encode_job->keyframe ? "(keyframe)" : "")
570            << " id: " << buffer->id << " size: " << data_size;
571
572  child_message_loop_proxy_->PostTask(FROM_HERE,
573                                      base::Bind(&Client::BitstreamBufferReady,
574                                                 client_,
575                                                 buffer->id,
576                                                 data_size,
577                                                 encode_job->keyframe));
578}
579
580void VaapiVideoEncodeAccelerator::Encode(
581    const scoped_refptr<media::VideoFrame>& frame,
582    bool force_keyframe) {
583  DVLOGF(3) << "Frame timestamp: " << frame->timestamp().InMilliseconds()
584            << " force_keyframe: " << force_keyframe;
585  DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
586
587  encoder_thread_proxy_->PostTask(
588      FROM_HERE,
589      base::Bind(&VaapiVideoEncodeAccelerator::EncodeTask,
590                 base::Unretained(this),
591                 frame,
592                 force_keyframe));
593}
594
595bool VaapiVideoEncodeAccelerator::PrepareNextJob() {
596  if (available_va_surface_ids_.size() < kMinSurfacesToEncode)
597    return false;
598
599  DCHECK(!current_encode_job_);
600  current_encode_job_.reset(new EncodeJob());
601
602  if (!vaapi_wrapper_->CreateCodedBuffer(output_buffer_byte_size_,
603                                         &current_encode_job_->coded_buffer)) {
604    NOTIFY_ERROR(kPlatformFailureError, "Failed creating coded buffer");
605    return false;
606  }
607
608  current_encode_job_->input_surface =
609      new VASurface(available_va_surface_ids_.back(), va_surface_release_cb_);
610  available_va_surface_ids_.pop_back();
611
612  current_encode_job_->recon_surface =
613      new VASurface(available_va_surface_ids_.back(), va_surface_release_cb_);
614  available_va_surface_ids_.pop_back();
615
616  // Reference surfaces are needed until the job is done, but they get
617  // removed from ref_pic_list0_ when it's full at the end of job submission.
618  // Keep refs to them along with the job and only release after sync.
619  current_encode_job_->reference_surfaces = ref_pic_list0_;
620
621  return true;
622}
623
624void VaapiVideoEncodeAccelerator::EncodeTask(
625    const scoped_refptr<media::VideoFrame>& frame,
626    bool force_keyframe) {
627  DCHECK(encoder_thread_proxy_->BelongsToCurrentThread());
628  DCHECK_NE(state_, kUninitialized);
629
630  encoder_input_queue_.push(
631      make_linked_ptr(new InputFrameRef(frame, force_keyframe)));
632  EncodeFrameTask();
633}
634
635void VaapiVideoEncodeAccelerator::EncodeFrameTask() {
636  DCHECK(encoder_thread_proxy_->BelongsToCurrentThread());
637
638  if (state_ != kEncoding || encoder_input_queue_.empty())
639    return;
640
641  if (!PrepareNextJob()) {
642    DVLOGF(4) << "Not ready for next frame yet";
643    return;
644  }
645
646  linked_ptr<InputFrameRef> frame_ref = encoder_input_queue_.front();
647  encoder_input_queue_.pop();
648
649  if (!UploadFrame(frame_ref->frame)) {
650    NOTIFY_ERROR(kPlatformFailureError, "Failed uploading source frame to HW.");
651    return;
652  }
653
654  BeginFrame(frame_ref->force_keyframe || encoding_parameters_changed_);
655  encoding_parameters_changed_ = false;
656
657  if (!SubmitFrameParameters()) {
658    NOTIFY_ERROR(kPlatformFailureError, "Failed submitting frame parameters.");
659    return;
660  }
661
662  if (!SubmitHeadersIfNeeded()) {
663    NOTIFY_ERROR(kPlatformFailureError, "Failed submitting frame headers.");
664    return;
665  }
666
667  if (!ExecuteEncode()) {
668    NOTIFY_ERROR(kPlatformFailureError, "Failed submitting encode job to HW.");
669    return;
670  }
671
672  EndFrame();
673  TryToReturnBitstreamBuffer();
674}
675
676void VaapiVideoEncodeAccelerator::UseOutputBitstreamBuffer(
677    const media::BitstreamBuffer& buffer) {
678  DVLOGF(4) << "id: " << buffer.id();
679  DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
680
681  if (buffer.size() < output_buffer_byte_size_) {
682    NOTIFY_ERROR(kInvalidArgumentError, "Provided bitstream buffer too small");
683    return;
684  }
685
686  scoped_ptr<base::SharedMemory> shm(
687      new base::SharedMemory(buffer.handle(), false));
688  if (!shm->Map(buffer.size())) {
689    NOTIFY_ERROR(kPlatformFailureError, "Failed mapping shared memory.");
690    return;
691  }
692
693  scoped_ptr<BitstreamBufferRef> buffer_ref(
694      new BitstreamBufferRef(buffer.id(), shm.Pass(), buffer.size()));
695
696  encoder_thread_proxy_->PostTask(
697      FROM_HERE,
698      base::Bind(&VaapiVideoEncodeAccelerator::UseOutputBitstreamBufferTask,
699                 base::Unretained(this),
700                 base::Passed(&buffer_ref)));
701}
702
703void VaapiVideoEncodeAccelerator::UseOutputBitstreamBufferTask(
704    scoped_ptr<BitstreamBufferRef> buffer_ref) {
705  DCHECK(encoder_thread_proxy_->BelongsToCurrentThread());
706  DCHECK_NE(state_, kUninitialized);
707
708  available_bitstream_buffers_.push(make_linked_ptr(buffer_ref.release()));
709  TryToReturnBitstreamBuffer();
710}
711
712void VaapiVideoEncodeAccelerator::RequestEncodingParametersChange(
713    uint32 bitrate,
714    uint32 framerate) {
715  DVLOGF(2) << "bitrate: " << bitrate << " framerate: " << framerate;
716  DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
717
718  encoder_thread_proxy_->PostTask(
719      FROM_HERE,
720      base::Bind(
721          &VaapiVideoEncodeAccelerator::RequestEncodingParametersChangeTask,
722          base::Unretained(this),
723          bitrate,
724          framerate));
725}
726
727void VaapiVideoEncodeAccelerator::UpdateRates(uint32 bitrate,
728                                              uint32 framerate) {
729  if (encoder_thread_.IsRunning())
730    DCHECK(encoder_thread_proxy_->BelongsToCurrentThread());
731  DCHECK_NE(bitrate, 0u);
732  DCHECK_NE(framerate, 0u);
733  bitrate_ = bitrate;
734  framerate_ = framerate;
735  cpb_size_ = bitrate_ * kCPBWindowSizeMs / 1000;
736}
737
738void VaapiVideoEncodeAccelerator::RequestEncodingParametersChangeTask(
739    uint32 bitrate,
740    uint32 framerate) {
741  DVLOGF(2) << "bitrate: " << bitrate << " framerate: " << framerate;
742  DCHECK(encoder_thread_proxy_->BelongsToCurrentThread());
743  DCHECK_NE(state_, kUninitialized);
744
745  // This is a workaround to zero being temporarily, as part of the initial
746  // setup, provided by the webrtc video encode and a zero bitrate and
747  // framerate not being accepted by VAAPI
748  // TODO: This code is common with v4l2_video_encode_accelerator.cc, perhaps
749  // it could be pulled up to RTCVideoEncoder
750  if (bitrate < 1)
751    bitrate = 1;
752  if (framerate < 1)
753    framerate = 1;
754
755  UpdateRates(bitrate, framerate);
756
757  UpdateSPS();
758  GeneratePackedSPS();
759
760  // Submit new parameters along with next frame that will be processed.
761  encoding_parameters_changed_ = true;
762}
763
764void VaapiVideoEncodeAccelerator::Destroy() {
765  DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
766
767  // Can't call client anymore after Destroy() returns.
768  client_ptr_factory_.reset();
769  weak_this_ptr_factory_.InvalidateWeakPtrs();
770
771  // Early-exit encoder tasks if they are running and join the thread.
772  if (encoder_thread_.IsRunning()) {
773    encoder_thread_.message_loop()->PostTask(
774        FROM_HERE,
775        base::Bind(&VaapiVideoEncodeAccelerator::DestroyTask,
776                   base::Unretained(this)));
777    encoder_thread_.Stop();
778  }
779
780  delete this;
781}
782
783void VaapiVideoEncodeAccelerator::DestroyTask() {
784  DVLOGF(2);
785  DCHECK(encoder_thread_proxy_->BelongsToCurrentThread());
786  SetState(kError);
787}
788
789void VaapiVideoEncodeAccelerator::UpdateSPS() {
790  memset(&current_sps_, 0, sizeof(media::H264SPS));
791
792  // Spec A.2 and A.3.
793  switch (profile_) {
794    case media::H264PROFILE_BASELINE:
795      // Due to crbug.com/345569, we don't distinguish between constrained
796      // and non-constrained baseline profiles. Since many codecs can't do
797      // non-constrained, and constrained is usually what we mean (and it's a
798      // subset of non-constrained), default to it.
799      current_sps_.profile_idc = media::H264SPS::kProfileIDCBaseline;
800      current_sps_.constraint_set0_flag = true;
801      break;
802    case media::H264PROFILE_MAIN:
803      current_sps_.profile_idc = media::H264SPS::kProfileIDCMain;
804      current_sps_.constraint_set1_flag = true;
805      break;
806    case media::H264PROFILE_HIGH:
807      current_sps_.profile_idc = media::H264SPS::kProfileIDCHigh;
808      break;
809    default:
810      NOTIMPLEMENTED();
811      return;
812  }
813
814  current_sps_.level_idc = kDefaultLevelIDC;
815  current_sps_.seq_parameter_set_id = 0;
816  current_sps_.chroma_format_idc = kChromaFormatIDC;
817
818  DCHECK_GE(idr_period_, 1u << 4);
819  current_sps_.log2_max_frame_num_minus4 = Log2OfPowerOf2(idr_period_) - 4;
820  current_sps_.pic_order_cnt_type = 0;
821  current_sps_.log2_max_pic_order_cnt_lsb_minus4 =
822      Log2OfPowerOf2(idr_period_ * 2) - 4;
823  current_sps_.max_num_ref_frames = max_ref_idx_l0_size_;
824
825  current_sps_.frame_mbs_only_flag = true;
826
827  DCHECK_GT(mb_width_, 0u);
828  DCHECK_GT(mb_height_, 0u);
829  current_sps_.pic_width_in_mbs_minus1 = mb_width_ - 1;
830  DCHECK(current_sps_.frame_mbs_only_flag);
831  current_sps_.pic_height_in_map_units_minus1 = mb_height_ - 1;
832
833  if (visible_size_ != coded_size_) {
834    // Visible size differs from coded size, fill crop information.
835    current_sps_.frame_cropping_flag = true;
836    DCHECK(!current_sps_.separate_colour_plane_flag);
837    // Spec table 6-1. Only 4:2:0 for now.
838    DCHECK_EQ(current_sps_.chroma_format_idc, 1);
839    // Spec 7.4.2.1.1. Crop is in crop units, which is 2 pixels for 4:2:0.
840    const unsigned int crop_unit_x = 2;
841    const unsigned int crop_unit_y = 2 * (2 - current_sps_.frame_mbs_only_flag);
842    current_sps_.frame_crop_left_offset = 0;
843    current_sps_.frame_crop_right_offset =
844        (coded_size_.width() - visible_size_.width()) / crop_unit_x;
845    current_sps_.frame_crop_top_offset = 0;
846    current_sps_.frame_crop_bottom_offset =
847        (coded_size_.height() - visible_size_.height()) / crop_unit_y;
848  }
849
850  current_sps_.vui_parameters_present_flag = true;
851  current_sps_.timing_info_present_flag = true;
852  current_sps_.num_units_in_tick = 1;
853  current_sps_.time_scale = framerate_ * 2;  // See equation D-2 in spec.
854  current_sps_.fixed_frame_rate_flag = true;
855
856  current_sps_.nal_hrd_parameters_present_flag = true;
857  // H.264 spec ch. E.2.2.
858  current_sps_.cpb_cnt_minus1 = 0;
859  current_sps_.bit_rate_scale = kBitRateScale;
860  current_sps_.cpb_size_scale = kCPBSizeScale;
861  current_sps_.bit_rate_value_minus1[0] =
862      (bitrate_ >>
863          (kBitRateScale + media::H264SPS::kBitRateScaleConstantTerm)) - 1;
864  current_sps_.cpb_size_value_minus1[0] =
865      (cpb_size_ >>
866          (kCPBSizeScale + media::H264SPS::kCPBSizeScaleConstantTerm)) - 1;
867  current_sps_.cbr_flag[0] = true;
868  current_sps_.initial_cpb_removal_delay_length_minus_1 =
869      media::H264SPS::kDefaultInitialCPBRemovalDelayLength - 1;
870  current_sps_.cpb_removal_delay_length_minus1 =
871      media::H264SPS::kDefaultInitialCPBRemovalDelayLength - 1;
872  current_sps_.dpb_output_delay_length_minus1 =
873      media::H264SPS::kDefaultDPBOutputDelayLength - 1;
874  current_sps_.time_offset_length = media::H264SPS::kDefaultTimeOffsetLength;
875  current_sps_.low_delay_hrd_flag = false;
876}
877
878void VaapiVideoEncodeAccelerator::GeneratePackedSPS() {
879  packed_sps_.Reset();
880
881  packed_sps_.BeginNALU(media::H264NALU::kSPS, 3);
882
883  packed_sps_.AppendBits(8, current_sps_.profile_idc);
884  packed_sps_.AppendBool(current_sps_.constraint_set0_flag);
885  packed_sps_.AppendBool(current_sps_.constraint_set1_flag);
886  packed_sps_.AppendBool(current_sps_.constraint_set2_flag);
887  packed_sps_.AppendBool(current_sps_.constraint_set3_flag);
888  packed_sps_.AppendBool(current_sps_.constraint_set4_flag);
889  packed_sps_.AppendBool(current_sps_.constraint_set5_flag);
890  packed_sps_.AppendBits(2, 0);  // reserved_zero_2bits
891  packed_sps_.AppendBits(8, current_sps_.level_idc);
892  packed_sps_.AppendUE(current_sps_.seq_parameter_set_id);
893
894  if (current_sps_.profile_idc == media::H264SPS::kProfileIDCHigh) {
895    packed_sps_.AppendUE(current_sps_.chroma_format_idc);
896    if (current_sps_.chroma_format_idc == 3)
897      packed_sps_.AppendBool(current_sps_.separate_colour_plane_flag);
898    packed_sps_.AppendUE(current_sps_.bit_depth_luma_minus8);
899    packed_sps_.AppendUE(current_sps_.bit_depth_chroma_minus8);
900    packed_sps_.AppendBool(current_sps_.qpprime_y_zero_transform_bypass_flag);
901    packed_sps_.AppendBool(current_sps_.seq_scaling_matrix_present_flag);
902    CHECK(!current_sps_.seq_scaling_matrix_present_flag);
903  }
904
905  packed_sps_.AppendUE(current_sps_.log2_max_frame_num_minus4);
906  packed_sps_.AppendUE(current_sps_.pic_order_cnt_type);
907  if (current_sps_.pic_order_cnt_type == 0)
908    packed_sps_.AppendUE(current_sps_.log2_max_pic_order_cnt_lsb_minus4);
909  else if (current_sps_.pic_order_cnt_type == 1) {
910    CHECK(1);
911  }
912
913  packed_sps_.AppendUE(current_sps_.max_num_ref_frames);
914  packed_sps_.AppendBool(current_sps_.gaps_in_frame_num_value_allowed_flag);
915  packed_sps_.AppendUE(current_sps_.pic_width_in_mbs_minus1);
916  packed_sps_.AppendUE(current_sps_.pic_height_in_map_units_minus1);
917
918  packed_sps_.AppendBool(current_sps_.frame_mbs_only_flag);
919  if (!current_sps_.frame_mbs_only_flag)
920    packed_sps_.AppendBool(current_sps_.mb_adaptive_frame_field_flag);
921
922  packed_sps_.AppendBool(current_sps_.direct_8x8_inference_flag);
923
924  packed_sps_.AppendBool(current_sps_.frame_cropping_flag);
925  if (current_sps_.frame_cropping_flag) {
926    packed_sps_.AppendUE(current_sps_.frame_crop_left_offset);
927    packed_sps_.AppendUE(current_sps_.frame_crop_right_offset);
928    packed_sps_.AppendUE(current_sps_.frame_crop_top_offset);
929    packed_sps_.AppendUE(current_sps_.frame_crop_bottom_offset);
930  }
931
932  packed_sps_.AppendBool(current_sps_.vui_parameters_present_flag);
933  if (current_sps_.vui_parameters_present_flag) {
934    packed_sps_.AppendBool(false);  // aspect_ratio_info_present_flag
935    packed_sps_.AppendBool(false);  // overscan_info_present_flag
936    packed_sps_.AppendBool(false);  // video_signal_type_present_flag
937    packed_sps_.AppendBool(false);  // chroma_loc_info_present_flag
938
939    packed_sps_.AppendBool(current_sps_.timing_info_present_flag);
940    if (current_sps_.timing_info_present_flag) {
941      packed_sps_.AppendBits(32, current_sps_.num_units_in_tick);
942      packed_sps_.AppendBits(32, current_sps_.time_scale);
943      packed_sps_.AppendBool(current_sps_.fixed_frame_rate_flag);
944    }
945
946    packed_sps_.AppendBool(current_sps_.nal_hrd_parameters_present_flag);
947    if (current_sps_.nal_hrd_parameters_present_flag) {
948      packed_sps_.AppendUE(current_sps_.cpb_cnt_minus1);
949      packed_sps_.AppendBits(4, current_sps_.bit_rate_scale);
950      packed_sps_.AppendBits(4, current_sps_.cpb_size_scale);
951      CHECK_LT(base::checked_cast<size_t>(current_sps_.cpb_cnt_minus1),
952               arraysize(current_sps_.bit_rate_value_minus1));
953      for (int i = 0; i <= current_sps_.cpb_cnt_minus1; ++i) {
954        packed_sps_.AppendUE(current_sps_.bit_rate_value_minus1[i]);
955        packed_sps_.AppendUE(current_sps_.cpb_size_value_minus1[i]);
956        packed_sps_.AppendBool(current_sps_.cbr_flag[i]);
957      }
958      packed_sps_.AppendBits(
959          5, current_sps_.initial_cpb_removal_delay_length_minus_1);
960      packed_sps_.AppendBits(5, current_sps_.cpb_removal_delay_length_minus1);
961      packed_sps_.AppendBits(5, current_sps_.dpb_output_delay_length_minus1);
962      packed_sps_.AppendBits(5, current_sps_.time_offset_length);
963    }
964
965    packed_sps_.AppendBool(false);  // vcl_hrd_parameters_flag
966    if (current_sps_.nal_hrd_parameters_present_flag)
967      packed_sps_.AppendBool(current_sps_.low_delay_hrd_flag);
968
969    packed_sps_.AppendBool(false);  // pic_struct_present_flag
970    packed_sps_.AppendBool(false);  // bitstream_restriction_flag
971  }
972
973  packed_sps_.FinishNALU();
974}
975
976void VaapiVideoEncodeAccelerator::UpdatePPS() {
977  memset(&current_pps_, 0, sizeof(media::H264PPS));
978
979  current_pps_.seq_parameter_set_id = current_sps_.seq_parameter_set_id;
980  current_pps_.pic_parameter_set_id = 0;
981
982  current_pps_.entropy_coding_mode_flag =
983      current_sps_.profile_idc >= media::H264SPS::kProfileIDCMain;
984
985  CHECK_GT(max_ref_idx_l0_size_, 0u);
986  current_pps_.num_ref_idx_l0_default_active_minus1 = max_ref_idx_l0_size_ - 1;
987  current_pps_.num_ref_idx_l1_default_active_minus1 = 0;
988  DCHECK_LE(qp_, 51u);
989  current_pps_.pic_init_qp_minus26 = qp_ - 26;
990  current_pps_.deblocking_filter_control_present_flag = true;
991  current_pps_.transform_8x8_mode_flag =
992      (current_sps_.profile_idc == media::H264SPS::kProfileIDCHigh);
993}
994
995void VaapiVideoEncodeAccelerator::GeneratePackedPPS() {
996  packed_pps_.Reset();
997
998  packed_pps_.BeginNALU(media::H264NALU::kPPS, 3);
999
1000  packed_pps_.AppendUE(current_pps_.pic_parameter_set_id);
1001  packed_pps_.AppendUE(current_pps_.seq_parameter_set_id);
1002  packed_pps_.AppendBool(current_pps_.entropy_coding_mode_flag);
1003  packed_pps_.AppendBool(
1004      current_pps_.bottom_field_pic_order_in_frame_present_flag);
1005  CHECK_EQ(current_pps_.num_slice_groups_minus1, 0);
1006  packed_pps_.AppendUE(current_pps_.num_slice_groups_minus1);
1007
1008  packed_pps_.AppendUE(current_pps_.num_ref_idx_l0_default_active_minus1);
1009  packed_pps_.AppendUE(current_pps_.num_ref_idx_l1_default_active_minus1);
1010
1011  packed_pps_.AppendBool(current_pps_.weighted_pred_flag);
1012  packed_pps_.AppendBits(2, current_pps_.weighted_bipred_idc);
1013
1014  packed_pps_.AppendSE(current_pps_.pic_init_qp_minus26);
1015  packed_pps_.AppendSE(current_pps_.pic_init_qs_minus26);
1016  packed_pps_.AppendSE(current_pps_.chroma_qp_index_offset);
1017
1018  packed_pps_.AppendBool(current_pps_.deblocking_filter_control_present_flag);
1019  packed_pps_.AppendBool(current_pps_.constrained_intra_pred_flag);
1020  packed_pps_.AppendBool(current_pps_.redundant_pic_cnt_present_flag);
1021
1022  packed_pps_.AppendBool(current_pps_.transform_8x8_mode_flag);
1023  packed_pps_.AppendBool(current_pps_.pic_scaling_matrix_present_flag);
1024  DCHECK(!current_pps_.pic_scaling_matrix_present_flag);
1025  packed_pps_.AppendSE(current_pps_.second_chroma_qp_index_offset);
1026
1027  packed_pps_.FinishNALU();
1028}
1029
1030void VaapiVideoEncodeAccelerator::SetState(State state) {
1031  // Only touch state on encoder thread, unless it's not running.
1032  if (encoder_thread_.IsRunning() &&
1033      !encoder_thread_proxy_->BelongsToCurrentThread()) {
1034    encoder_thread_proxy_->PostTask(
1035        FROM_HERE,
1036        base::Bind(&VaapiVideoEncodeAccelerator::SetState,
1037                   base::Unretained(this),
1038                   state));
1039    return;
1040  }
1041
1042  DVLOGF(1) << "setting state to: " << state;
1043  state_ = state;
1044}
1045
1046void VaapiVideoEncodeAccelerator::NotifyError(Error error) {
1047  if (!child_message_loop_proxy_->BelongsToCurrentThread()) {
1048    child_message_loop_proxy_->PostTask(
1049        FROM_HERE,
1050        base::Bind(
1051            &VaapiVideoEncodeAccelerator::NotifyError, weak_this_, error));
1052    return;
1053  }
1054
1055  if (client_) {
1056    client_->NotifyError(error);
1057    client_ptr_factory_.reset();
1058  }
1059}
1060
1061VaapiVideoEncodeAccelerator::EncodeJob::EncodeJob()
1062    : coded_buffer(VA_INVALID_ID), keyframe(false) {
1063}
1064
1065VaapiVideoEncodeAccelerator::EncodeJob::~EncodeJob() {
1066}
1067
1068}  // namespace content
1069