Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(253)

Side by Side Diff: content/common/gpu/media/vaapi_video_encode_accelerator.cc

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

Powered by Google App Engine
This is Rietveld 408576698