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"
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) \
24 LOG(ERROR) << "Calling NotifyError(" << error << ")";\
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
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
{
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",
89 VAVEA_ENCODER_FAILURES_MAX
);
92 struct VaapiVideoEncodeAccelerator::InputFrameRef
{
93 InputFrameRef(const scoped_refptr
<media::VideoFrame
>& frame
,
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
) {}
104 const scoped_ptr
<base::SharedMemory
> shm
;
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
))
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
);
130 static unsigned int Log2OfPowerOf2(unsigned int x
) {
132 DCHECK_EQ(x
& (x
- 1), 0u);
142 VaapiVideoEncodeAccelerator::VaapiVideoEncodeAccelerator()
143 : profile_(media::VIDEO_CODEC_PROFILE_UNKNOWN
),
146 output_buffer_byte_size_(0),
147 state_(kUninitialized
),
153 encoding_parameters_changed_(false),
154 encoder_thread_("VAVEAEncoderThread"),
155 child_message_loop_proxy_(base::MessageLoopProxy::current()),
156 weak_this_ptr_factory_(this) {
158 weak_this_
= weak_this_ptr_factory_
.GetWeakPtr();
160 max_ref_idx_l0_size_
= kMaxNumReferenceFrames
;
162 idr_period_
= kIDRPeriod
;
163 i_period_
= kIPeriod
;
164 ip_period_
= kIPPeriod
;
167 VaapiVideoEncodeAccelerator::~VaapiVideoEncodeAccelerator() {
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
,
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
;
198 if (format
!= media::VideoFrame::I420
) {
199 DVLOGF(1) << "Unsupported input format: "
200 << media::VideoFrame::FormatToString(format
);
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
);
218 VaapiWrapper::CreateForVideoCodec(VaapiWrapper::kEncode
, output_profile
,
219 base::Bind(&ReportToUMA
, VAAPI_ERROR
));
220 if (!vaapi_wrapper_
.get()) {
221 LOG(ERROR
) << "Failed initializing VAAPI";
225 if (!encoder_thread_
.Start()) {
226 LOG(ERROR
) << "Failed to start encoder thread";
229 encoder_thread_proxy_
= encoder_thread_
.message_loop_proxy();
231 // Finish the remaining initialization on the encoder thread.
232 encoder_thread_proxy_
->PostTask(
234 base::Bind(&VaapiVideoEncodeAccelerator::InitializeTask
,
235 base::Unretained(this)));
240 void VaapiVideoEncodeAccelerator::InitializeTask() {
241 DCHECK(encoder_thread_proxy_
->BelongsToCurrentThread());
242 DCHECK_EQ(state_
, kUninitialized
);
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");
261 child_message_loop_proxy_
->PostTask(
263 base::Bind(&Client::RequireBitstreamBuffers
,
267 output_buffer_byte_size_
));
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
);
281 void VaapiVideoEncodeAccelerator::BeginFrame(bool force_keyframe
) {
282 memset(¤t_pic_
, 0, sizeof(current_pic_
));
284 // If the current picture is an IDR picture, frame_num shall be equal to 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.
295 ref_pic_list0_
.clear();
298 if (current_pic_
.frame_num
% i_period_
== 0)
299 current_pic_
.type
= media::H264SliceHeader::kISlice
;
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
);
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
);
377 SPS_TO_SP(num_units_in_tick
);
378 SPS_TO_SP(time_scale
);
381 if (!vaapi_wrapper_
->SubmitBuffer(VAEncSequenceParameterBufferType
,
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();
400 i
< arraysize(pic_param
.ReferenceFrames
) && iter
!= ref_pic_list0_
.end();
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
);
420 if (!vaapi_wrapper_
->SubmitBuffer(VAEncPictureParameterBufferType
,
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();
445 i
< arraysize(slice_param
.RefPicList0
) && iter
!= ref_pic_list0_
.end();
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
,
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
))
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
),
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
,
492 bool VaapiVideoEncodeAccelerator::SubmitHeadersIfNeeded() {
493 if (current_pic_
.type
!= media::H264SliceHeader::kISlice
)
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
,
507 if (!vaapi_wrapper_
->SubmitBuffer(VAEncPackedHeaderDataBufferType
,
508 packed_sps_
.BytesInBuffer(),
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
,
522 if (!vaapi_wrapper_
->SubmitBuffer(VAEncPackedHeaderDataBufferType
,
523 packed_pps_
.BytesInBuffer(),
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
)
548 if (submitted_encode_jobs_
.empty() || available_bitstream_buffers_
.empty())
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(),
566 NOTIFY_ERROR(kPlatformFailureError
, "Failed downloading coded buffer");
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
,
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(
591 base::Bind(&VaapiVideoEncodeAccelerator::EncodeTask
,
592 base::Unretained(this),
597 bool VaapiVideoEncodeAccelerator::PrepareNextJob() {
598 if (available_va_surface_ids_
.size() < kMinSurfacesToEncode
)
601 DCHECK(!current_encode_job_
);
602 current_encode_job_
.reset(new EncodeJob());
604 if (!vaapi_wrapper_
->CreateCodedBuffer(output_buffer_byte_size_
,
605 ¤t_encode_job_
->coded_buffer
)) {
606 NOTIFY_ERROR(kPlatformFailureError
, "Failed creating coded buffer");
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_
;
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
)));
637 void VaapiVideoEncodeAccelerator::EncodeFrameTask() {
638 DCHECK(encoder_thread_proxy_
->BelongsToCurrentThread());
640 if (state_
!= kEncoding
|| encoder_input_queue_
.empty())
643 if (!PrepareNextJob()) {
644 DVLOGF(4) << "Not ready for next frame yet";
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.");
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.");
664 if (!SubmitHeadersIfNeeded()) {
665 NOTIFY_ERROR(kPlatformFailureError
, "Failed submitting frame headers.");
669 if (!ExecuteEncode()) {
670 NOTIFY_ERROR(kPlatformFailureError
, "Failed submitting encode job to HW.");
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");
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.");
695 scoped_ptr
<BitstreamBufferRef
> buffer_ref(
696 new BitstreamBufferRef(buffer
.id(), shm
.Pass(), buffer
.size()));
698 encoder_thread_proxy_
->PostTask(
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(
717 DVLOGF(2) << "bitrate: " << bitrate
<< " framerate: " << framerate
;
718 DCHECK(child_message_loop_proxy_
->BelongsToCurrentThread());
720 encoder_thread_proxy_
->PostTask(
723 &VaapiVideoEncodeAccelerator::RequestEncodingParametersChangeTask
,
724 base::Unretained(this),
729 void VaapiVideoEncodeAccelerator::UpdateRates(uint32 bitrate
,
731 if (encoder_thread_
.IsRunning())
732 DCHECK(encoder_thread_proxy_
->BelongsToCurrentThread());
733 DCHECK_NE(bitrate
, 0u);
734 DCHECK_NE(framerate
, 0u);
736 framerate_
= framerate
;
737 cpb_size_
= bitrate_
* kCPBWindowSizeMs
/ 1000;
740 void VaapiVideoEncodeAccelerator::RequestEncodingParametersChangeTask(
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
757 if (bitrate_
== bitrate
&& framerate_
== framerate
)
760 UpdateRates(bitrate
, framerate
);
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(
780 base::Bind(&VaapiVideoEncodeAccelerator::DestroyTask
,
781 base::Unretained(this)));
782 encoder_thread_
.Stop();
788 void VaapiVideoEncodeAccelerator::DestroyTask() {
790 DCHECK(encoder_thread_proxy_
->BelongsToCurrentThread());
794 void VaapiVideoEncodeAccelerator::UpdateSPS() {
795 memset(¤t_sps_
, 0, sizeof(media::H264SPS
));
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;
807 case media::H264PROFILE_MAIN
:
808 current_sps_
.profile_idc
= media::H264SPS::kProfileIDCMain
;
809 current_sps_
.constraint_set1_flag
= true;
811 case media::H264PROFILE_HIGH
:
812 current_sps_
.profile_idc
= media::H264SPS::kProfileIDCHigh
;
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] =
868 (kBitRateScale
+ media::H264SPS::kBitRateScaleConstantTerm
)) - 1;
869 current_sps_
.cpb_size_value_minus1
[0] =
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() {
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) {
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(¤t_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(
1057 base::Bind(&VaapiVideoEncodeAccelerator::SetState
,
1058 base::Unretained(this),
1063 DVLOGF(1) << "setting state to: " << state
;
1067 void VaapiVideoEncodeAccelerator::NotifyError(Error error
) {
1068 if (!child_message_loop_proxy_
->BelongsToCurrentThread()) {
1069 child_message_loop_proxy_
->PostTask(
1072 &VaapiVideoEncodeAccelerator::NotifyError
, weak_this_
, error
));
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