1 // Copyright 2015 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/renderer/pepper/video_encoder_shim.h"
11 #include "base/bind.h"
12 #include "base/bind_helpers.h"
13 #include "base/location.h"
14 #include "base/memory/shared_memory.h"
15 #include "base/single_thread_task_runner.h"
16 #include "base/thread_task_runner_handle.h"
17 #include "content/renderer/pepper/pepper_video_encoder_host.h"
18 #include "content/renderer/render_thread_impl.h"
19 #include "third_party/libvpx/source/libvpx/vpx/vp8cx.h"
20 #include "third_party/libvpx/source/libvpx/vpx/vpx_encoder.h"
21 #include "ui/gfx/geometry/size.h"
27 // TODO(llandwerlin): Libvpx doesn't seem to have a maximum frame size
28 // limitation. We currently limit the size of the frames to encode at
29 // 1080p (%64 pixels blocks), this seems like a reasonable limit for
31 const int32_t kMaxWidth
= 1920;
32 const int32_t kMaxHeight
= 1088;
34 // Bitstream buffer size.
35 const uint32_t kBitstreamBufferSize
= 2 * 1024 * 1024;
37 // Number of frames needs at any given time.
38 const uint32_t kInputFrameCount
= 1;
40 // Default speed for the encoder. Increases the CPU usage as the value
41 // is more negative (VP8 valid range: -16..16, VP9 valid range:
42 // -8..8), using the same value as WebRTC.
43 const int32_t kVp8DefaultCpuUsed
= -6;
45 // Default quantizer min/max values (same values as WebRTC).
46 const int32_t kVp8DefaultMinQuantizer
= 2;
47 const int32_t kVp8DefaultMaxQuantizer
= 52;
49 // For VP9, the following 3 values are the same values as remoting.
50 const int32_t kVp9DefaultCpuUsed
= 6;
52 const int32_t kVp9DefaultMinQuantizer
= 20;
53 const int32_t kVp9DefaultMaxQuantizer
= 30;
55 // VP9 adaptive quantization strategy (same as remoting (live video
57 const int kVp9AqModeCyclicRefresh
= 3;
59 void GetVpxCodecParameters(media::VideoCodecProfile codec
,
60 vpx_codec_iface_t
** vpx_codec
,
61 int32_t* min_quantizer
,
62 int32_t* max_quantizer
,
65 case media::VP8PROFILE_ANY
:
66 *vpx_codec
= vpx_codec_vp8_cx();
67 *min_quantizer
= kVp8DefaultMinQuantizer
;
68 *max_quantizer
= kVp8DefaultMaxQuantizer
;
69 *cpu_used
= kVp8DefaultCpuUsed
;
71 case media::VP9PROFILE_ANY
:
72 *vpx_codec
= vpx_codec_vp9_cx();
73 *min_quantizer
= kVp9DefaultMinQuantizer
;
74 *max_quantizer
= kVp9DefaultMaxQuantizer
;
75 *cpu_used
= kVp9DefaultCpuUsed
;
88 class VideoEncoderShim::EncoderImpl
{
90 explicit EncoderImpl(const base::WeakPtr
<VideoEncoderShim
>& shim
);
93 void Initialize(media::VideoPixelFormat input_format
,
94 const gfx::Size
& input_visible_size
,
95 media::VideoCodecProfile output_profile
,
96 uint32 initial_bitrate
);
97 void Encode(const scoped_refptr
<media::VideoFrame
>& frame
,
99 void UseOutputBitstreamBuffer(const media::BitstreamBuffer
& buffer
,
101 void RequestEncodingParametersChange(uint32 bitrate
, uint32 framerate
);
105 struct PendingEncode
{
106 PendingEncode(const scoped_refptr
<media::VideoFrame
>& frame
,
108 : frame(frame
), force_keyframe(force_keyframe
) {}
111 scoped_refptr
<media::VideoFrame
> frame
;
115 struct BitstreamBuffer
{
116 BitstreamBuffer(const media::BitstreamBuffer buffer
, uint8_t* mem
)
117 : buffer(buffer
), mem(mem
) {}
118 ~BitstreamBuffer() {}
120 media::BitstreamBuffer buffer
;
125 void NotifyError(media::VideoEncodeAccelerator::Error error
);
127 base::WeakPtr
<VideoEncoderShim
> shim_
;
128 scoped_refptr
<base::SingleThreadTaskRunner
> renderer_task_runner_
;
132 // Libvpx internal objects. Only valid if |initialized_| is true.
133 vpx_codec_enc_cfg_t config_
;
134 vpx_codec_ctx_t encoder_
;
138 std::deque
<PendingEncode
> frames_
;
139 std::deque
<BitstreamBuffer
> buffers_
;
142 VideoEncoderShim::EncoderImpl::EncoderImpl(
143 const base::WeakPtr
<VideoEncoderShim
>& shim
)
145 renderer_task_runner_(base::ThreadTaskRunnerHandle::Get()),
146 initialized_(false) {
149 VideoEncoderShim::EncoderImpl::~EncoderImpl() {
151 vpx_codec_destroy(&encoder_
);
154 void VideoEncoderShim::EncoderImpl::Initialize(
155 media::VideoPixelFormat input_format
,
156 const gfx::Size
& input_visible_size
,
157 media::VideoCodecProfile output_profile
,
158 uint32 initial_bitrate
) {
159 gfx::Size coded_size
=
160 media::VideoFrame::PlaneSize(input_format
, 0, input_visible_size
);
162 vpx_codec_iface_t
* vpx_codec
;
163 int32_t min_quantizer
, max_quantizer
, cpu_used
;
164 GetVpxCodecParameters(output_profile
, &vpx_codec
, &min_quantizer
,
165 &max_quantizer
, &cpu_used
);
167 // Populate encoder configuration with default values.
168 if (vpx_codec_enc_config_default(vpx_codec
, &config_
, 0) != VPX_CODEC_OK
) {
169 NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError
);
173 config_
.g_w
= input_visible_size
.width();
174 config_
.g_h
= input_visible_size
.height();
176 framerate_
= config_
.g_timebase
.den
;
178 config_
.g_lag_in_frames
= 0;
179 config_
.g_timebase
.num
= 1;
180 config_
.g_timebase
.den
= base::Time::kMicrosecondsPerSecond
;
181 config_
.rc_target_bitrate
= initial_bitrate
/ 1000;
182 config_
.rc_min_quantizer
= min_quantizer
;
183 config_
.rc_max_quantizer
= max_quantizer
;
185 vpx_codec_flags_t flags
= 0;
186 if (vpx_codec_enc_init(&encoder_
, vpx_codec
, &config_
, flags
) !=
188 NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError
);
193 if (vpx_codec_enc_config_set(&encoder_
, &config_
) != VPX_CODEC_OK
) {
194 NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError
);
198 if (vpx_codec_control(&encoder_
, VP8E_SET_CPUUSED
, cpu_used
) !=
200 NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError
);
204 if (output_profile
== media::VP9PROFILE_ANY
) {
205 if (vpx_codec_control(&encoder_
, VP9E_SET_AQ_MODE
,
206 kVp9AqModeCyclicRefresh
) != VPX_CODEC_OK
) {
207 NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError
);
212 renderer_task_runner_
->PostTask(
214 base::Bind(&VideoEncoderShim::OnRequireBitstreamBuffers
, shim_
,
215 kInputFrameCount
, coded_size
, kBitstreamBufferSize
));
218 void VideoEncoderShim::EncoderImpl::Encode(
219 const scoped_refptr
<media::VideoFrame
>& frame
,
220 bool force_keyframe
) {
221 frames_
.push_back(PendingEncode(frame
, force_keyframe
));
225 void VideoEncoderShim::EncoderImpl::UseOutputBitstreamBuffer(
226 const media::BitstreamBuffer
& buffer
,
228 buffers_
.push_back(BitstreamBuffer(buffer
, mem
));
232 void VideoEncoderShim::EncoderImpl::RequestEncodingParametersChange(
235 framerate_
= framerate
;
237 uint32 bitrate_kbit
= bitrate
/ 1000;
238 if (config_
.rc_target_bitrate
== bitrate_kbit
)
241 config_
.rc_target_bitrate
= bitrate_kbit
;
242 if (vpx_codec_enc_config_set(&encoder_
, &config_
) != VPX_CODEC_OK
)
243 NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError
);
246 void VideoEncoderShim::EncoderImpl::Stop() {
247 // Release frames on the renderer thread.
248 while (!frames_
.empty()) {
249 PendingEncode frame
= frames_
.front();
252 frame
.frame
->AddRef();
253 media::VideoFrame
* raw_frame
= frame
.frame
.get();
254 frame
.frame
= nullptr;
255 renderer_task_runner_
->ReleaseSoon(FROM_HERE
, raw_frame
);
260 void VideoEncoderShim::EncoderImpl::DoEncode() {
261 while (!frames_
.empty() && !buffers_
.empty()) {
262 PendingEncode frame
= frames_
.front();
265 // Wrapper for vpx_codec_encode() to access the YUV data in the
266 // |video_frame|. Only the VISIBLE rectangle within |video_frame|
267 // is exposed to the codec.
268 vpx_image_t vpx_image
;
269 vpx_image_t
* const result
= vpx_img_wrap(
270 &vpx_image
, VPX_IMG_FMT_I420
, frame
.frame
->visible_rect().width(),
271 frame
.frame
->visible_rect().height(), 1,
272 frame
.frame
->data(media::VideoFrame::kYPlane
));
273 DCHECK_EQ(result
, &vpx_image
);
274 vpx_image
.planes
[VPX_PLANE_Y
] =
275 frame
.frame
->visible_data(media::VideoFrame::kYPlane
);
276 vpx_image
.planes
[VPX_PLANE_U
] =
277 frame
.frame
->visible_data(media::VideoFrame::kUPlane
);
278 vpx_image
.planes
[VPX_PLANE_V
] =
279 frame
.frame
->visible_data(media::VideoFrame::kVPlane
);
280 vpx_image
.stride
[VPX_PLANE_Y
] =
281 frame
.frame
->stride(media::VideoFrame::kYPlane
);
282 vpx_image
.stride
[VPX_PLANE_U
] =
283 frame
.frame
->stride(media::VideoFrame::kUPlane
);
284 vpx_image
.stride
[VPX_PLANE_V
] =
285 frame
.frame
->stride(media::VideoFrame::kVPlane
);
287 vpx_codec_flags_t flags
= 0;
288 if (frame
.force_keyframe
)
289 flags
= VPX_EFLAG_FORCE_KF
;
291 const base::TimeDelta frame_duration
=
292 base::TimeDelta::FromSecondsD(1.0 / framerate_
);
293 if (vpx_codec_encode(&encoder_
, &vpx_image
, 0,
294 frame_duration
.InMicroseconds(), flags
,
295 VPX_DL_REALTIME
) != VPX_CODEC_OK
) {
296 NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError
);
300 const vpx_codec_cx_pkt_t
* packet
= nullptr;
301 vpx_codec_iter_t iter
= nullptr;
302 while ((packet
= vpx_codec_get_cx_data(&encoder_
, &iter
)) != nullptr) {
303 if (packet
->kind
!= VPX_CODEC_CX_FRAME_PKT
)
306 BitstreamBuffer buffer
= buffers_
.front();
307 buffers_
.pop_front();
309 CHECK(buffer
.buffer
.size() >= packet
->data
.frame
.sz
);
310 memcpy(buffer
.mem
, packet
->data
.frame
.buf
, packet
->data
.frame
.sz
);
312 // Pass the media::VideoFrame back to the renderer thread so it's
313 // freed on the right thread.
314 renderer_task_runner_
->PostTask(
316 base::Bind(&VideoEncoderShim::OnBitstreamBufferReady
, shim_
,
317 frame
.frame
, buffer
.buffer
.id(),
318 base::checked_cast
<size_t>(packet
->data
.frame
.sz
),
319 (packet
->data
.frame
.flags
& VPX_FRAME_IS_KEY
) != 0));
320 break; // Done, since all data is provided in one CX_FRAME_PKT packet.
325 void VideoEncoderShim::EncoderImpl::NotifyError(
326 media::VideoEncodeAccelerator::Error error
) {
327 renderer_task_runner_
->PostTask(
328 FROM_HERE
, base::Bind(&VideoEncoderShim::OnNotifyError
, shim_
, error
));
332 VideoEncoderShim::VideoEncoderShim(PepperVideoEncoderHost
* host
)
335 RenderThreadImpl::current()->GetMediaThreadTaskRunner()),
336 weak_ptr_factory_(this) {
337 encoder_impl_
.reset(new EncoderImpl(weak_ptr_factory_
.GetWeakPtr()));
340 VideoEncoderShim::~VideoEncoderShim() {
341 DCHECK(RenderThreadImpl::current());
343 media_task_runner_
->PostTask(
344 FROM_HERE
, base::Bind(&VideoEncoderShim::EncoderImpl::Stop
,
345 base::Owned(encoder_impl_
.release())));
348 media::VideoEncodeAccelerator::SupportedProfiles
349 VideoEncoderShim::GetSupportedProfiles() {
350 media::VideoEncodeAccelerator::SupportedProfiles profiles
;
352 // Get the default VP8 config from Libvpx.
353 vpx_codec_enc_cfg_t config
;
354 vpx_codec_err_t ret
=
355 vpx_codec_enc_config_default(vpx_codec_vp8_cx(), &config
, 0);
356 if (ret
== VPX_CODEC_OK
) {
357 media::VideoEncodeAccelerator::SupportedProfile profile
;
358 profile
.profile
= media::VP8PROFILE_ANY
;
359 profile
.max_resolution
= gfx::Size(kMaxWidth
, kMaxHeight
);
360 // Libvpx and media::VideoEncodeAccelerator are using opposite
361 // notions of denominator/numerator.
362 profile
.max_framerate_numerator
= config
.g_timebase
.den
;
363 profile
.max_framerate_denominator
= config
.g_timebase
.num
;
364 profiles
.push_back(profile
);
367 ret
= vpx_codec_enc_config_default(vpx_codec_vp9_cx(), &config
, 0);
368 if (ret
== VPX_CODEC_OK
) {
369 media::VideoEncodeAccelerator::SupportedProfile profile
;
370 profile
.profile
= media::VP9PROFILE_ANY
;
371 profile
.max_resolution
= gfx::Size(kMaxWidth
, kMaxHeight
);
372 profile
.max_framerate_numerator
= config
.g_timebase
.den
;
373 profile
.max_framerate_denominator
= config
.g_timebase
.num
;
374 profiles
.push_back(profile
);
380 bool VideoEncoderShim::Initialize(
381 media::VideoPixelFormat input_format
,
382 const gfx::Size
& input_visible_size
,
383 media::VideoCodecProfile output_profile
,
384 uint32 initial_bitrate
,
385 media::VideoEncodeAccelerator::Client
* client
) {
386 DCHECK(RenderThreadImpl::current());
387 DCHECK_EQ(client
, host_
);
389 if (input_format
!= media::PIXEL_FORMAT_I420
)
392 if (output_profile
!= media::VP8PROFILE_ANY
&&
393 output_profile
!= media::VP9PROFILE_ANY
)
396 media_task_runner_
->PostTask(
398 base::Bind(&VideoEncoderShim::EncoderImpl::Initialize
,
399 base::Unretained(encoder_impl_
.get()), input_format
,
400 input_visible_size
, output_profile
, initial_bitrate
));
405 void VideoEncoderShim::Encode(const scoped_refptr
<media::VideoFrame
>& frame
,
406 bool force_keyframe
) {
407 DCHECK(RenderThreadImpl::current());
409 media_task_runner_
->PostTask(
411 base::Bind(&VideoEncoderShim::EncoderImpl::Encode
,
412 base::Unretained(encoder_impl_
.get()), frame
, force_keyframe
));
415 void VideoEncoderShim::UseOutputBitstreamBuffer(
416 const media::BitstreamBuffer
& buffer
) {
417 DCHECK(RenderThreadImpl::current());
419 media_task_runner_
->PostTask(
421 base::Bind(&VideoEncoderShim::EncoderImpl::UseOutputBitstreamBuffer
,
422 base::Unretained(encoder_impl_
.get()), buffer
,
423 host_
->ShmHandleToAddress(buffer
.id())));
426 void VideoEncoderShim::RequestEncodingParametersChange(uint32 bitrate
,
428 DCHECK(RenderThreadImpl::current());
430 media_task_runner_
->PostTask(
433 &VideoEncoderShim::EncoderImpl::RequestEncodingParametersChange
,
434 base::Unretained(encoder_impl_
.get()), bitrate
, framerate
));
437 void VideoEncoderShim::Destroy() {
438 DCHECK(RenderThreadImpl::current());
443 void VideoEncoderShim::OnRequireBitstreamBuffers(
444 unsigned int input_count
,
445 const gfx::Size
& input_coded_size
,
446 size_t output_buffer_size
) {
447 DCHECK(RenderThreadImpl::current());
449 host_
->RequireBitstreamBuffers(input_count
, input_coded_size
,
453 void VideoEncoderShim::OnBitstreamBufferReady(
454 scoped_refptr
<media::VideoFrame
> frame
,
455 int32 bitstream_buffer_id
,
458 DCHECK(RenderThreadImpl::current());
460 host_
->BitstreamBufferReady(bitstream_buffer_id
, payload_size
, key_frame
);
463 void VideoEncoderShim::OnNotifyError(
464 media::VideoEncodeAccelerator::Error error
) {
465 DCHECK(RenderThreadImpl::current());
467 host_
->NotifyError(error
);
470 } // namespace content