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/scoped_vector.h"
15 #include "base/memory/shared_memory.h"
16 #include "base/single_thread_task_runner.h"
17 #include "base/thread_task_runner_handle.h"
18 #include "content/renderer/pepper/pepper_video_encoder_host.h"
19 #include "content/renderer/render_thread_impl.h"
20 #include "third_party/libvpx/source/libvpx/vpx/vp8cx.h"
21 #include "third_party/libvpx/source/libvpx/vpx/vpx_encoder.h"
22 #include "ui/gfx/geometry/size.h"
28 // TODO(llandwerlin): Libvpx doesn't seem to have a maximum frame size
29 // limitation. We currently limit the size of the frames to encode at
30 // 1080p (%64 pixels blocks), this seems like a reasonable limit for
32 const int32_t kMaxWidth
= 1920;
33 const int32_t kMaxHeight
= 1088;
35 // Bitstream buffer size.
36 const uint32_t kBitstreamBufferSize
= 2 * 1024 * 1024;
38 // Number of frames needs at any given time.
39 const uint32_t kInputFrameCount
= 1;
41 // Default speed for the encoder. Increases the CPU usage as the value
42 // is more negative (VP8 valid range: -16..16, VP9 valid range:
43 // -8..8), using the same value as WebRTC.
44 const int32_t kVp8DefaultCpuUsed
= -6;
46 // Default quantizer min/max values (same values as WebRTC).
47 const int32_t kVp8DefaultMinQuantizer
= 2;
48 const int32_t kVp8DefaultMaxQuantizer
= 52;
50 // For VP9, the following 3 values are the same values as remoting.
51 const int32_t kVp9DefaultCpuUsed
= 6;
53 const int32_t kVp9DefaultMinQuantizer
= 20;
54 const int32_t kVp9DefaultMaxQuantizer
= 30;
56 // VP9 adaptive quantization strategy (same as remoting (live video
58 const int kVp9AqModeCyclicRefresh
= 3;
60 void GetVpxCodecParameters(media::VideoCodecProfile codec
,
61 vpx_codec_iface_t
** vpx_codec
,
62 int32_t* min_quantizer
,
63 int32_t* max_quantizer
,
66 case media::VP8PROFILE_ANY
:
67 *vpx_codec
= vpx_codec_vp8_cx();
68 *min_quantizer
= kVp8DefaultMinQuantizer
;
69 *max_quantizer
= kVp8DefaultMaxQuantizer
;
70 *cpu_used
= kVp8DefaultCpuUsed
;
72 case media::VP9PROFILE_ANY
:
73 *vpx_codec
= vpx_codec_vp9_cx();
74 *min_quantizer
= kVp9DefaultMinQuantizer
;
75 *max_quantizer
= kVp9DefaultMaxQuantizer
;
76 *cpu_used
= kVp9DefaultCpuUsed
;
89 class VideoEncoderShim::EncoderImpl
{
91 explicit EncoderImpl(const base::WeakPtr
<VideoEncoderShim
>& shim
);
94 void Initialize(media::VideoPixelFormat input_format
,
95 const gfx::Size
& input_visible_size
,
96 media::VideoCodecProfile output_profile
,
97 uint32 initial_bitrate
);
98 void Encode(const scoped_refptr
<media::VideoFrame
>& frame
,
100 void UseOutputBitstreamBuffer(const media::BitstreamBuffer
& buffer
,
102 void RequestEncodingParametersChange(uint32 bitrate
, uint32 framerate
);
106 struct PendingEncode
{
107 PendingEncode(const scoped_refptr
<media::VideoFrame
>& frame
,
109 : frame(frame
), force_keyframe(force_keyframe
) {}
112 scoped_refptr
<media::VideoFrame
> frame
;
116 struct BitstreamBuffer
{
117 BitstreamBuffer(const media::BitstreamBuffer buffer
, uint8_t* mem
)
118 : buffer(buffer
), mem(mem
) {}
119 ~BitstreamBuffer() {}
121 media::BitstreamBuffer buffer
;
126 void NotifyError(media::VideoEncodeAccelerator::Error error
);
128 base::WeakPtr
<VideoEncoderShim
> shim_
;
129 scoped_refptr
<base::SingleThreadTaskRunner
> renderer_task_runner_
;
133 // Libvpx internal objects. Only valid if |initialized_| is true.
134 vpx_codec_enc_cfg_t config_
;
135 vpx_codec_ctx_t encoder_
;
139 std::deque
<PendingEncode
> frames_
;
140 std::deque
<BitstreamBuffer
> buffers_
;
143 VideoEncoderShim::EncoderImpl::EncoderImpl(
144 const base::WeakPtr
<VideoEncoderShim
>& shim
)
146 renderer_task_runner_(base::ThreadTaskRunnerHandle::Get()),
147 initialized_(false) {
150 VideoEncoderShim::EncoderImpl::~EncoderImpl() {
152 vpx_codec_destroy(&encoder_
);
155 void VideoEncoderShim::EncoderImpl::Initialize(
156 media::VideoPixelFormat input_format
,
157 const gfx::Size
& input_visible_size
,
158 media::VideoCodecProfile output_profile
,
159 uint32 initial_bitrate
) {
160 gfx::Size coded_size
=
161 media::VideoFrame::PlaneSize(input_format
, 0, input_visible_size
);
163 vpx_codec_iface_t
* vpx_codec
;
164 int32_t min_quantizer
, max_quantizer
, cpu_used
;
165 GetVpxCodecParameters(output_profile
, &vpx_codec
, &min_quantizer
,
166 &max_quantizer
, &cpu_used
);
168 // Populate encoder configuration with default values.
169 if (vpx_codec_enc_config_default(vpx_codec
, &config_
, 0) != VPX_CODEC_OK
) {
170 NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError
);
174 config_
.g_w
= input_visible_size
.width();
175 config_
.g_h
= input_visible_size
.height();
177 framerate_
= config_
.g_timebase
.den
;
179 config_
.g_lag_in_frames
= 0;
180 config_
.g_timebase
.num
= 1;
181 config_
.g_timebase
.den
= base::Time::kMicrosecondsPerSecond
;
182 config_
.rc_target_bitrate
= initial_bitrate
/ 1000;
183 config_
.rc_min_quantizer
= min_quantizer
;
184 config_
.rc_max_quantizer
= max_quantizer
;
186 vpx_codec_flags_t flags
= 0;
187 if (vpx_codec_enc_init(&encoder_
, vpx_codec
, &config_
, flags
) !=
189 NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError
);
194 if (vpx_codec_enc_config_set(&encoder_
, &config_
) != VPX_CODEC_OK
) {
195 NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError
);
199 if (vpx_codec_control(&encoder_
, VP8E_SET_CPUUSED
, cpu_used
) !=
201 NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError
);
205 if (output_profile
== media::VP9PROFILE_ANY
) {
206 if (vpx_codec_control(&encoder_
, VP9E_SET_AQ_MODE
,
207 kVp9AqModeCyclicRefresh
) != VPX_CODEC_OK
) {
208 NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError
);
213 renderer_task_runner_
->PostTask(
215 base::Bind(&VideoEncoderShim::OnRequireBitstreamBuffers
, shim_
,
216 kInputFrameCount
, coded_size
, kBitstreamBufferSize
));
219 void VideoEncoderShim::EncoderImpl::Encode(
220 const scoped_refptr
<media::VideoFrame
>& frame
,
221 bool force_keyframe
) {
222 frames_
.push_back(PendingEncode(frame
, force_keyframe
));
226 void VideoEncoderShim::EncoderImpl::UseOutputBitstreamBuffer(
227 const media::BitstreamBuffer
& buffer
,
229 buffers_
.push_back(BitstreamBuffer(buffer
, mem
));
233 void VideoEncoderShim::EncoderImpl::RequestEncodingParametersChange(
236 framerate_
= framerate
;
238 uint32 bitrate_kbit
= bitrate
/ 1000;
239 if (config_
.rc_target_bitrate
== bitrate_kbit
)
242 config_
.rc_target_bitrate
= bitrate_kbit
;
243 if (vpx_codec_enc_config_set(&encoder_
, &config_
) != VPX_CODEC_OK
)
244 NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError
);
247 void VideoEncoderShim::EncoderImpl::Stop() {
248 // Release frames on the renderer thread.
249 while (!frames_
.empty()) {
250 PendingEncode frame
= frames_
.front();
253 frame
.frame
->AddRef();
254 media::VideoFrame
* raw_frame
= frame
.frame
.get();
255 frame
.frame
= nullptr;
256 renderer_task_runner_
->ReleaseSoon(FROM_HERE
, raw_frame
);
261 void VideoEncoderShim::EncoderImpl::DoEncode() {
262 while (!frames_
.empty() && !buffers_
.empty()) {
263 PendingEncode frame
= frames_
.front();
266 // Wrapper for vpx_codec_encode() to access the YUV data in the
267 // |video_frame|. Only the VISIBLE rectangle within |video_frame|
268 // is exposed to the codec.
269 vpx_image_t vpx_image
;
270 vpx_image_t
* const result
= vpx_img_wrap(
271 &vpx_image
, VPX_IMG_FMT_I420
, frame
.frame
->visible_rect().width(),
272 frame
.frame
->visible_rect().height(), 1,
273 frame
.frame
->data(media::VideoFrame::kYPlane
));
274 DCHECK_EQ(result
, &vpx_image
);
275 vpx_image
.planes
[VPX_PLANE_Y
] =
276 frame
.frame
->visible_data(media::VideoFrame::kYPlane
);
277 vpx_image
.planes
[VPX_PLANE_U
] =
278 frame
.frame
->visible_data(media::VideoFrame::kUPlane
);
279 vpx_image
.planes
[VPX_PLANE_V
] =
280 frame
.frame
->visible_data(media::VideoFrame::kVPlane
);
281 vpx_image
.stride
[VPX_PLANE_Y
] =
282 frame
.frame
->stride(media::VideoFrame::kYPlane
);
283 vpx_image
.stride
[VPX_PLANE_U
] =
284 frame
.frame
->stride(media::VideoFrame::kUPlane
);
285 vpx_image
.stride
[VPX_PLANE_V
] =
286 frame
.frame
->stride(media::VideoFrame::kVPlane
);
288 vpx_codec_flags_t flags
= 0;
289 if (frame
.force_keyframe
)
290 flags
= VPX_EFLAG_FORCE_KF
;
292 const base::TimeDelta frame_duration
=
293 base::TimeDelta::FromSecondsD(1.0 / framerate_
);
294 if (vpx_codec_encode(&encoder_
, &vpx_image
, 0,
295 frame_duration
.InMicroseconds(), flags
,
296 VPX_DL_REALTIME
) != VPX_CODEC_OK
) {
297 NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError
);
301 const vpx_codec_cx_pkt_t
* packet
= nullptr;
302 vpx_codec_iter_t iter
= nullptr;
303 while ((packet
= vpx_codec_get_cx_data(&encoder_
, &iter
)) != nullptr) {
304 if (packet
->kind
!= VPX_CODEC_CX_FRAME_PKT
)
307 BitstreamBuffer buffer
= buffers_
.front();
308 buffers_
.pop_front();
310 CHECK(buffer
.buffer
.size() >= packet
->data
.frame
.sz
);
311 memcpy(buffer
.mem
, packet
->data
.frame
.buf
, packet
->data
.frame
.sz
);
313 // Pass the media::VideoFrame back to the renderer thread so it's
314 // freed on the right thread.
315 renderer_task_runner_
->PostTask(
317 base::Bind(&VideoEncoderShim::OnBitstreamBufferReady
, shim_
,
318 frame
.frame
, buffer
.buffer
.id(),
319 base::checked_cast
<size_t>(packet
->data
.frame
.sz
),
320 (packet
->data
.frame
.flags
& VPX_FRAME_IS_KEY
) != 0));
321 break; // Done, since all data is provided in one CX_FRAME_PKT packet.
326 void VideoEncoderShim::EncoderImpl::NotifyError(
327 media::VideoEncodeAccelerator::Error error
) {
328 renderer_task_runner_
->PostTask(
329 FROM_HERE
, base::Bind(&VideoEncoderShim::OnNotifyError
, shim_
, error
));
333 VideoEncoderShim::VideoEncoderShim(PepperVideoEncoderHost
* host
)
336 RenderThreadImpl::current()->GetMediaThreadTaskRunner()),
337 weak_ptr_factory_(this) {
338 encoder_impl_
.reset(new EncoderImpl(weak_ptr_factory_
.GetWeakPtr()));
341 VideoEncoderShim::~VideoEncoderShim() {
342 DCHECK(RenderThreadImpl::current());
344 media_task_runner_
->PostTask(
345 FROM_HERE
, base::Bind(&VideoEncoderShim::EncoderImpl::Stop
,
346 base::Owned(encoder_impl_
.release())));
349 media::VideoEncodeAccelerator::SupportedProfiles
350 VideoEncoderShim::GetSupportedProfiles() {
351 media::VideoEncodeAccelerator::SupportedProfiles profiles
;
353 // Get the default VP8 config from Libvpx.
354 vpx_codec_enc_cfg_t config
;
355 vpx_codec_err_t ret
=
356 vpx_codec_enc_config_default(vpx_codec_vp8_cx(), &config
, 0);
357 if (ret
== VPX_CODEC_OK
) {
358 media::VideoEncodeAccelerator::SupportedProfile profile
;
359 profile
.profile
= media::VP8PROFILE_ANY
;
360 profile
.max_resolution
= gfx::Size(kMaxWidth
, kMaxHeight
);
361 // Libvpx and media::VideoEncodeAccelerator are using opposite
362 // notions of denominator/numerator.
363 profile
.max_framerate_numerator
= config
.g_timebase
.den
;
364 profile
.max_framerate_denominator
= config
.g_timebase
.num
;
365 profiles
.push_back(profile
);
368 ret
= vpx_codec_enc_config_default(vpx_codec_vp9_cx(), &config
, 0);
369 if (ret
== VPX_CODEC_OK
) {
370 media::VideoEncodeAccelerator::SupportedProfile profile
;
371 profile
.profile
= media::VP9PROFILE_ANY
;
372 profile
.max_resolution
= gfx::Size(kMaxWidth
, kMaxHeight
);
373 profile
.max_framerate_numerator
= config
.g_timebase
.den
;
374 profile
.max_framerate_denominator
= config
.g_timebase
.num
;
375 profiles
.push_back(profile
);
381 bool VideoEncoderShim::Initialize(
382 media::VideoPixelFormat input_format
,
383 const gfx::Size
& input_visible_size
,
384 media::VideoCodecProfile output_profile
,
385 uint32 initial_bitrate
,
386 media::VideoEncodeAccelerator::Client
* client
) {
387 DCHECK(RenderThreadImpl::current());
388 DCHECK_EQ(client
, host_
);
390 if (input_format
!= media::PIXEL_FORMAT_I420
)
393 if (output_profile
!= media::VP8PROFILE_ANY
&&
394 output_profile
!= media::VP9PROFILE_ANY
)
397 media_task_runner_
->PostTask(
399 base::Bind(&VideoEncoderShim::EncoderImpl::Initialize
,
400 base::Unretained(encoder_impl_
.get()), input_format
,
401 input_visible_size
, output_profile
, initial_bitrate
));
406 void VideoEncoderShim::Encode(const scoped_refptr
<media::VideoFrame
>& frame
,
407 bool force_keyframe
) {
408 DCHECK(RenderThreadImpl::current());
410 media_task_runner_
->PostTask(
412 base::Bind(&VideoEncoderShim::EncoderImpl::Encode
,
413 base::Unretained(encoder_impl_
.get()), frame
, force_keyframe
));
416 void VideoEncoderShim::UseOutputBitstreamBuffer(
417 const media::BitstreamBuffer
& buffer
) {
418 DCHECK(RenderThreadImpl::current());
420 media_task_runner_
->PostTask(
422 base::Bind(&VideoEncoderShim::EncoderImpl::UseOutputBitstreamBuffer
,
423 base::Unretained(encoder_impl_
.get()), buffer
,
424 host_
->ShmHandleToAddress(buffer
.id())));
427 void VideoEncoderShim::RequestEncodingParametersChange(uint32 bitrate
,
429 DCHECK(RenderThreadImpl::current());
431 media_task_runner_
->PostTask(
434 &VideoEncoderShim::EncoderImpl::RequestEncodingParametersChange
,
435 base::Unretained(encoder_impl_
.get()), bitrate
, framerate
));
438 void VideoEncoderShim::Destroy() {
439 DCHECK(RenderThreadImpl::current());
444 void VideoEncoderShim::OnRequireBitstreamBuffers(
445 unsigned int input_count
,
446 const gfx::Size
& input_coded_size
,
447 size_t output_buffer_size
) {
448 DCHECK(RenderThreadImpl::current());
450 host_
->RequireBitstreamBuffers(input_count
, input_coded_size
,
454 void VideoEncoderShim::OnBitstreamBufferReady(
455 scoped_refptr
<media::VideoFrame
> frame
,
456 int32 bitstream_buffer_id
,
459 DCHECK(RenderThreadImpl::current());
461 host_
->BitstreamBufferReady(bitstream_buffer_id
, payload_size
, key_frame
);
464 void VideoEncoderShim::OnNotifyError(
465 media::VideoEncodeAccelerator::Error error
) {
466 DCHECK(RenderThreadImpl::current());
468 host_
->NotifyError(error
);
471 } // namespace content