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/single_thread_task_runner.h"
15 #include "base/thread_task_runner_handle.h"
16 #include "content/renderer/pepper/pepper_video_encoder_host.h"
17 #include "content/renderer/render_thread_impl.h"
18 #include "third_party/libvpx/source/libvpx/vpx/vp8cx.h"
19 #include "third_party/libvpx/source/libvpx/vpx/vpx_encoder.h"
20 #include "ui/gfx/geometry/size.h"
26 // TODO(llandwerlin): Libvpx doesn't seem to have a maximum frame size
27 // limitation. We currently limit the size of the frames to encode at
28 // 1080p (%64 pixels blocks), this seems like a reasonable limit for
30 const int32_t kMaxWidth
= 1920;
31 const int32_t kMaxHeight
= 1088;
33 // Bitstream buffer size.
34 const uint32_t kBitstreamBufferSize
= 2 * 1024 * 1024;
36 // Number of frames needs at any given time.
37 const uint32_t kInputFrameCount
= 1;
39 // Default speed for the encoder. Increases the CPU usage as the value
40 // is more negative (VP8 valid range: -16..16, VP9 valid range:
41 // -8..8), using the same value as WebRTC.
42 const int32_t kVp8DefaultCpuUsed
= -6;
44 // Default quantizer min/max values (same values as WebRTC).
45 const int32_t kVp8DefaultMinQuantizer
= 2;
46 const int32_t kVp8DefaultMaxQuantizer
= 52;
48 // For VP9, the following 3 values are the same values as remoting.
49 const int32_t kVp9DefaultCpuUsed
= 6;
51 const int32_t kVp9DefaultMinQuantizer
= 20;
52 const int32_t kVp9DefaultMaxQuantizer
= 30;
54 // VP9 adaptive quantization strategy (same as remoting (live video
56 const int kVp9AqModeCyclicRefresh
= 3;
58 void GetVpxCodecParameters(media::VideoCodecProfile codec
,
59 vpx_codec_iface_t
** vpx_codec
,
60 int32_t* min_quantizer
,
61 int32_t* max_quantizer
,
64 case media::VP8PROFILE_ANY
:
65 *vpx_codec
= vpx_codec_vp8_cx();
66 *min_quantizer
= kVp8DefaultMinQuantizer
;
67 *max_quantizer
= kVp8DefaultMaxQuantizer
;
68 *cpu_used
= kVp8DefaultCpuUsed
;
70 case media::VP9PROFILE_ANY
:
71 *vpx_codec
= vpx_codec_vp9_cx();
72 *min_quantizer
= kVp9DefaultMinQuantizer
;
73 *max_quantizer
= kVp9DefaultMaxQuantizer
;
74 *cpu_used
= kVp9DefaultCpuUsed
;
87 class VideoEncoderShim::EncoderImpl
{
89 explicit EncoderImpl(const base::WeakPtr
<VideoEncoderShim
>& shim
);
92 void Initialize(media::VideoPixelFormat input_format
,
93 const gfx::Size
& input_visible_size
,
94 media::VideoCodecProfile output_profile
,
95 uint32 initial_bitrate
);
96 void Encode(const scoped_refptr
<media::VideoFrame
>& frame
,
98 void UseOutputBitstreamBuffer(const media::BitstreamBuffer
& buffer
,
100 void RequestEncodingParametersChange(uint32 bitrate
, uint32 framerate
);
104 struct PendingEncode
{
105 PendingEncode(const scoped_refptr
<media::VideoFrame
>& frame
,
107 : frame(frame
), force_keyframe(force_keyframe
) {}
110 scoped_refptr
<media::VideoFrame
> frame
;
114 struct BitstreamBuffer
{
115 BitstreamBuffer(const media::BitstreamBuffer buffer
, uint8_t* mem
)
116 : buffer(buffer
), mem(mem
) {}
117 ~BitstreamBuffer() {}
119 media::BitstreamBuffer buffer
;
124 void NotifyError(media::VideoEncodeAccelerator::Error error
);
126 base::WeakPtr
<VideoEncoderShim
> shim_
;
127 scoped_refptr
<base::SingleThreadTaskRunner
> renderer_task_runner_
;
131 // Libvpx internal objects. Only valid if |initialized_| is true.
132 vpx_codec_enc_cfg_t config_
;
133 vpx_codec_ctx_t encoder_
;
137 std::deque
<PendingEncode
> frames_
;
138 std::deque
<BitstreamBuffer
> buffers_
;
141 VideoEncoderShim::EncoderImpl::EncoderImpl(
142 const base::WeakPtr
<VideoEncoderShim
>& shim
)
144 renderer_task_runner_(base::ThreadTaskRunnerHandle::Get()),
145 initialized_(false) {
148 VideoEncoderShim::EncoderImpl::~EncoderImpl() {
150 vpx_codec_destroy(&encoder_
);
153 void VideoEncoderShim::EncoderImpl::Initialize(
154 media::VideoPixelFormat input_format
,
155 const gfx::Size
& input_visible_size
,
156 media::VideoCodecProfile output_profile
,
157 uint32 initial_bitrate
) {
158 gfx::Size coded_size
=
159 media::VideoFrame::PlaneSize(input_format
, 0, input_visible_size
);
161 vpx_codec_iface_t
* vpx_codec
;
162 int32_t min_quantizer
, max_quantizer
, cpu_used
;
163 GetVpxCodecParameters(output_profile
, &vpx_codec
, &min_quantizer
,
164 &max_quantizer
, &cpu_used
);
166 // Populate encoder configuration with default values.
167 if (vpx_codec_enc_config_default(vpx_codec
, &config_
, 0) != VPX_CODEC_OK
) {
168 NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError
);
172 config_
.g_w
= input_visible_size
.width();
173 config_
.g_h
= input_visible_size
.height();
175 framerate_
= config_
.g_timebase
.den
;
177 config_
.g_lag_in_frames
= 0;
178 config_
.g_timebase
.num
= 1;
179 config_
.g_timebase
.den
= base::Time::kMicrosecondsPerSecond
;
180 config_
.rc_target_bitrate
= initial_bitrate
/ 1000;
181 config_
.rc_min_quantizer
= min_quantizer
;
182 config_
.rc_max_quantizer
= max_quantizer
;
184 vpx_codec_flags_t flags
= 0;
185 if (vpx_codec_enc_init(&encoder_
, vpx_codec
, &config_
, flags
) !=
187 NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError
);
192 if (vpx_codec_enc_config_set(&encoder_
, &config_
) != VPX_CODEC_OK
) {
193 NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError
);
197 if (vpx_codec_control(&encoder_
, VP8E_SET_CPUUSED
, cpu_used
) !=
199 NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError
);
203 if (output_profile
== media::VP9PROFILE_ANY
) {
204 if (vpx_codec_control(&encoder_
, VP9E_SET_AQ_MODE
,
205 kVp9AqModeCyclicRefresh
) != VPX_CODEC_OK
) {
206 NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError
);
211 renderer_task_runner_
->PostTask(
213 base::Bind(&VideoEncoderShim::OnRequireBitstreamBuffers
, shim_
,
214 kInputFrameCount
, coded_size
, kBitstreamBufferSize
));
217 void VideoEncoderShim::EncoderImpl::Encode(
218 const scoped_refptr
<media::VideoFrame
>& frame
,
219 bool force_keyframe
) {
220 frames_
.push_back(PendingEncode(frame
, force_keyframe
));
224 void VideoEncoderShim::EncoderImpl::UseOutputBitstreamBuffer(
225 const media::BitstreamBuffer
& buffer
,
227 buffers_
.push_back(BitstreamBuffer(buffer
, mem
));
231 void VideoEncoderShim::EncoderImpl::RequestEncodingParametersChange(
234 framerate_
= framerate
;
236 uint32 bitrate_kbit
= bitrate
/ 1000;
237 if (config_
.rc_target_bitrate
== bitrate_kbit
)
240 config_
.rc_target_bitrate
= bitrate_kbit
;
241 if (vpx_codec_enc_config_set(&encoder_
, &config_
) != VPX_CODEC_OK
)
242 NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError
);
245 void VideoEncoderShim::EncoderImpl::Stop() {
246 // Release frames on the renderer thread.
247 while (!frames_
.empty()) {
248 PendingEncode frame
= frames_
.front();
251 frame
.frame
->AddRef();
252 media::VideoFrame
* raw_frame
= frame
.frame
.get();
253 frame
.frame
= nullptr;
254 renderer_task_runner_
->ReleaseSoon(FROM_HERE
, raw_frame
);
259 void VideoEncoderShim::EncoderImpl::DoEncode() {
260 while (!frames_
.empty() && !buffers_
.empty()) {
261 PendingEncode frame
= frames_
.front();
264 // Wrapper for vpx_codec_encode() to access the YUV data in the
265 // |video_frame|. Only the VISIBLE rectangle within |video_frame|
266 // is exposed to the codec.
267 vpx_image_t vpx_image
;
268 vpx_image_t
* const result
= vpx_img_wrap(
269 &vpx_image
, VPX_IMG_FMT_I420
, frame
.frame
->visible_rect().width(),
270 frame
.frame
->visible_rect().height(), 1,
271 frame
.frame
->data(media::VideoFrame::kYPlane
));
272 DCHECK_EQ(result
, &vpx_image
);
273 vpx_image
.planes
[VPX_PLANE_Y
] =
274 frame
.frame
->visible_data(media::VideoFrame::kYPlane
);
275 vpx_image
.planes
[VPX_PLANE_U
] =
276 frame
.frame
->visible_data(media::VideoFrame::kUPlane
);
277 vpx_image
.planes
[VPX_PLANE_V
] =
278 frame
.frame
->visible_data(media::VideoFrame::kVPlane
);
279 vpx_image
.stride
[VPX_PLANE_Y
] =
280 frame
.frame
->stride(media::VideoFrame::kYPlane
);
281 vpx_image
.stride
[VPX_PLANE_U
] =
282 frame
.frame
->stride(media::VideoFrame::kUPlane
);
283 vpx_image
.stride
[VPX_PLANE_V
] =
284 frame
.frame
->stride(media::VideoFrame::kVPlane
);
286 vpx_codec_flags_t flags
= 0;
287 if (frame
.force_keyframe
)
288 flags
= VPX_EFLAG_FORCE_KF
;
290 const base::TimeDelta frame_duration
=
291 base::TimeDelta::FromSecondsD(1.0 / framerate_
);
292 if (vpx_codec_encode(&encoder_
, &vpx_image
, 0,
293 frame_duration
.InMicroseconds(), flags
,
294 VPX_DL_REALTIME
) != VPX_CODEC_OK
) {
295 NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError
);
299 const vpx_codec_cx_pkt_t
* packet
= nullptr;
300 vpx_codec_iter_t iter
= nullptr;
301 while ((packet
= vpx_codec_get_cx_data(&encoder_
, &iter
)) != nullptr) {
302 if (packet
->kind
!= VPX_CODEC_CX_FRAME_PKT
)
305 BitstreamBuffer buffer
= buffers_
.front();
306 buffers_
.pop_front();
308 CHECK(buffer
.buffer
.size() >= packet
->data
.frame
.sz
);
309 memcpy(buffer
.mem
, packet
->data
.frame
.buf
, packet
->data
.frame
.sz
);
311 // Pass the media::VideoFrame back to the renderer thread so it's
312 // freed on the right thread.
313 renderer_task_runner_
->PostTask(
315 base::Bind(&VideoEncoderShim::OnBitstreamBufferReady
, shim_
,
316 frame
.frame
, buffer
.buffer
.id(),
317 base::checked_cast
<size_t>(packet
->data
.frame
.sz
),
318 (packet
->data
.frame
.flags
& VPX_FRAME_IS_KEY
) != 0));
319 break; // Done, since all data is provided in one CX_FRAME_PKT packet.
324 void VideoEncoderShim::EncoderImpl::NotifyError(
325 media::VideoEncodeAccelerator::Error error
) {
326 renderer_task_runner_
->PostTask(
327 FROM_HERE
, base::Bind(&VideoEncoderShim::OnNotifyError
, shim_
, error
));
331 VideoEncoderShim::VideoEncoderShim(PepperVideoEncoderHost
* host
)
334 RenderThreadImpl::current()->GetMediaThreadTaskRunner()),
335 weak_ptr_factory_(this) {
336 encoder_impl_
.reset(new EncoderImpl(weak_ptr_factory_
.GetWeakPtr()));
339 VideoEncoderShim::~VideoEncoderShim() {
340 DCHECK(RenderThreadImpl::current());
342 media_task_runner_
->PostTask(
343 FROM_HERE
, base::Bind(&VideoEncoderShim::EncoderImpl::Stop
,
344 base::Owned(encoder_impl_
.release())));
347 media::VideoEncodeAccelerator::SupportedProfiles
348 VideoEncoderShim::GetSupportedProfiles() {
349 media::VideoEncodeAccelerator::SupportedProfiles profiles
;
351 // Get the default VP8 config from Libvpx.
352 vpx_codec_enc_cfg_t config
;
353 vpx_codec_err_t ret
=
354 vpx_codec_enc_config_default(vpx_codec_vp8_cx(), &config
, 0);
355 if (ret
== VPX_CODEC_OK
) {
356 media::VideoEncodeAccelerator::SupportedProfile profile
;
357 profile
.profile
= media::VP8PROFILE_ANY
;
358 profile
.max_resolution
= gfx::Size(kMaxWidth
, kMaxHeight
);
359 // Libvpx and media::VideoEncodeAccelerator are using opposite
360 // notions of denominator/numerator.
361 profile
.max_framerate_numerator
= config
.g_timebase
.den
;
362 profile
.max_framerate_denominator
= config
.g_timebase
.num
;
363 profiles
.push_back(profile
);
366 ret
= vpx_codec_enc_config_default(vpx_codec_vp9_cx(), &config
, 0);
367 if (ret
== VPX_CODEC_OK
) {
368 media::VideoEncodeAccelerator::SupportedProfile profile
;
369 profile
.profile
= media::VP9PROFILE_ANY
;
370 profile
.max_resolution
= gfx::Size(kMaxWidth
, kMaxHeight
);
371 profile
.max_framerate_numerator
= config
.g_timebase
.den
;
372 profile
.max_framerate_denominator
= config
.g_timebase
.num
;
373 profiles
.push_back(profile
);
379 bool VideoEncoderShim::Initialize(
380 media::VideoPixelFormat input_format
,
381 const gfx::Size
& input_visible_size
,
382 media::VideoCodecProfile output_profile
,
383 uint32 initial_bitrate
,
384 media::VideoEncodeAccelerator::Client
* client
) {
385 DCHECK(RenderThreadImpl::current());
386 DCHECK_EQ(client
, host_
);
388 if (input_format
!= media::PIXEL_FORMAT_I420
)
391 if (output_profile
!= media::VP8PROFILE_ANY
&&
392 output_profile
!= media::VP9PROFILE_ANY
)
395 media_task_runner_
->PostTask(
397 base::Bind(&VideoEncoderShim::EncoderImpl::Initialize
,
398 base::Unretained(encoder_impl_
.get()), input_format
,
399 input_visible_size
, output_profile
, initial_bitrate
));
404 void VideoEncoderShim::Encode(const scoped_refptr
<media::VideoFrame
>& frame
,
405 bool force_keyframe
) {
406 DCHECK(RenderThreadImpl::current());
408 media_task_runner_
->PostTask(
410 base::Bind(&VideoEncoderShim::EncoderImpl::Encode
,
411 base::Unretained(encoder_impl_
.get()), frame
, force_keyframe
));
414 void VideoEncoderShim::UseOutputBitstreamBuffer(
415 const media::BitstreamBuffer
& buffer
) {
416 DCHECK(RenderThreadImpl::current());
418 media_task_runner_
->PostTask(
420 base::Bind(&VideoEncoderShim::EncoderImpl::UseOutputBitstreamBuffer
,
421 base::Unretained(encoder_impl_
.get()), buffer
,
422 host_
->ShmHandleToAddress(buffer
.id())));
425 void VideoEncoderShim::RequestEncodingParametersChange(uint32 bitrate
,
427 DCHECK(RenderThreadImpl::current());
429 media_task_runner_
->PostTask(
432 &VideoEncoderShim::EncoderImpl::RequestEncodingParametersChange
,
433 base::Unretained(encoder_impl_
.get()), bitrate
, framerate
));
436 void VideoEncoderShim::Destroy() {
437 DCHECK(RenderThreadImpl::current());
442 void VideoEncoderShim::OnRequireBitstreamBuffers(
443 unsigned int input_count
,
444 const gfx::Size
& input_coded_size
,
445 size_t output_buffer_size
) {
446 DCHECK(RenderThreadImpl::current());
448 host_
->RequireBitstreamBuffers(input_count
, input_coded_size
,
452 void VideoEncoderShim::OnBitstreamBufferReady(
453 scoped_refptr
<media::VideoFrame
> frame
,
454 int32 bitstream_buffer_id
,
457 DCHECK(RenderThreadImpl::current());
459 host_
->BitstreamBufferReady(bitstream_buffer_id
, payload_size
, key_frame
);
462 void VideoEncoderShim::OnNotifyError(
463 media::VideoEncodeAccelerator::Error error
) {
464 DCHECK(RenderThreadImpl::current());
466 host_
->NotifyError(error
);
469 } // namespace content