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"
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 // Default speed for the encoder (same as WebRTC). Increases the CPU
34 // usage as the value is more negative (VP8 valid range: -16..16).
35 const int32_t kDefaultCpuUsed
= -6;
37 // Default quantizer min/max values.
38 const int32_t kDefaultMinQuantizer
= 2;
39 const int32_t kDefaultMaxQuantizer
= 52;
41 // Bitstream buffer size.
42 const uint32_t kBitstreamBufferSize
= 2 * 1024 * 1024;
44 // Number of frames needs at any given time.
45 const uint32_t kInputFrameCount
= 1;
47 class VideoEncoderShim::EncoderImpl
{
49 explicit EncoderImpl(const base::WeakPtr
<VideoEncoderShim
>& shim
);
52 void Initialize(media::VideoPixelFormat input_format
,
53 const gfx::Size
& input_visible_size
,
54 media::VideoCodecProfile output_profile
,
55 uint32 initial_bitrate
);
56 void Encode(const scoped_refptr
<media::VideoFrame
>& frame
,
58 void UseOutputBitstreamBuffer(const media::BitstreamBuffer
& buffer
,
60 void RequestEncodingParametersChange(uint32 bitrate
, uint32 framerate
);
64 struct PendingEncode
{
65 PendingEncode(const scoped_refptr
<media::VideoFrame
>& frame
,
67 : frame(frame
), force_keyframe(force_keyframe
) {}
70 scoped_refptr
<media::VideoFrame
> frame
;
74 struct BitstreamBuffer
{
75 BitstreamBuffer(const media::BitstreamBuffer buffer
, uint8_t* mem
)
76 : buffer(buffer
), mem(mem
) {}
79 media::BitstreamBuffer buffer
;
84 void NotifyError(media::VideoEncodeAccelerator::Error error
);
86 base::WeakPtr
<VideoEncoderShim
> shim_
;
87 scoped_refptr
<base::SingleThreadTaskRunner
> renderer_task_runner_
;
91 // Libvpx internal objects. Only valid if |initialized_| is true.
92 vpx_codec_enc_cfg_t config_
;
93 vpx_codec_ctx_t encoder_
;
97 std::deque
<PendingEncode
> frames_
;
98 std::deque
<BitstreamBuffer
> buffers_
;
101 VideoEncoderShim::EncoderImpl::EncoderImpl(
102 const base::WeakPtr
<VideoEncoderShim
>& shim
)
104 renderer_task_runner_(base::ThreadTaskRunnerHandle::Get()),
105 initialized_(false) {
108 VideoEncoderShim::EncoderImpl::~EncoderImpl() {
110 vpx_codec_destroy(&encoder_
);
113 void VideoEncoderShim::EncoderImpl::Initialize(
114 media::VideoPixelFormat input_format
,
115 const gfx::Size
& input_visible_size
,
116 media::VideoCodecProfile output_profile
,
117 uint32 initial_bitrate
) {
118 gfx::Size coded_size
=
119 media::VideoFrame::PlaneSize(input_format
, 0, input_visible_size
);
121 // Populate encoder configuration with default values.
122 if (vpx_codec_enc_config_default(vpx_codec_vp8_cx(), &config_
, 0) !=
124 NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError
);
128 config_
.g_threads
= 1;
129 config_
.g_w
= input_visible_size
.width();
130 config_
.g_h
= input_visible_size
.height();
132 framerate_
= config_
.g_timebase
.den
;
134 config_
.g_lag_in_frames
= 0;
135 config_
.g_timebase
.num
= 1;
136 config_
.g_timebase
.den
= base::Time::kMicrosecondsPerSecond
;
137 config_
.rc_target_bitrate
= initial_bitrate
/ 1000;
138 config_
.rc_min_quantizer
= kDefaultMinQuantizer
;
139 config_
.rc_max_quantizer
= kDefaultMaxQuantizer
;
141 vpx_codec_flags_t flags
= 0;
142 if (vpx_codec_enc_init(&encoder_
, vpx_codec_vp8_cx(), &config_
, flags
) !=
144 NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError
);
149 if (vpx_codec_enc_config_set(&encoder_
, &config_
) != VPX_CODEC_OK
) {
150 NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError
);
154 if (vpx_codec_control(&encoder_
, VP8E_SET_CPUUSED
, kDefaultCpuUsed
) !=
156 NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError
);
160 renderer_task_runner_
->PostTask(
162 base::Bind(&VideoEncoderShim::OnRequireBitstreamBuffers
, shim_
,
163 kInputFrameCount
, coded_size
, kBitstreamBufferSize
));
166 void VideoEncoderShim::EncoderImpl::Encode(
167 const scoped_refptr
<media::VideoFrame
>& frame
,
168 bool force_keyframe
) {
169 frames_
.push_back(PendingEncode(frame
, force_keyframe
));
173 void VideoEncoderShim::EncoderImpl::UseOutputBitstreamBuffer(
174 const media::BitstreamBuffer
& buffer
,
176 buffers_
.push_back(BitstreamBuffer(buffer
, mem
));
180 void VideoEncoderShim::EncoderImpl::RequestEncodingParametersChange(
183 framerate_
= framerate
;
185 uint32 bitrate_kbit
= bitrate
/ 1000;
186 if (config_
.rc_target_bitrate
== bitrate_kbit
)
189 config_
.rc_target_bitrate
= bitrate_kbit
;
190 if (vpx_codec_enc_config_set(&encoder_
, &config_
) != VPX_CODEC_OK
)
191 NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError
);
194 void VideoEncoderShim::EncoderImpl::Stop() {
195 // Release frames on the renderer thread.
196 while (!frames_
.empty()) {
197 PendingEncode frame
= frames_
.front();
200 frame
.frame
->AddRef();
201 media::VideoFrame
* raw_frame
= frame
.frame
.get();
202 frame
.frame
= nullptr;
203 renderer_task_runner_
->ReleaseSoon(FROM_HERE
, raw_frame
);
208 void VideoEncoderShim::EncoderImpl::DoEncode() {
209 while (!frames_
.empty() && !buffers_
.empty()) {
210 PendingEncode frame
= frames_
.front();
213 // Wrapper for vpx_codec_encode() to access the YUV data in the
214 // |video_frame|. Only the VISIBLE rectangle within |video_frame|
215 // is exposed to the codec.
216 vpx_image_t vpx_image
;
217 vpx_image_t
* const result
= vpx_img_wrap(
218 &vpx_image
, VPX_IMG_FMT_I420
, frame
.frame
->visible_rect().width(),
219 frame
.frame
->visible_rect().height(), 1,
220 frame
.frame
->data(media::VideoFrame::kYPlane
));
221 DCHECK_EQ(result
, &vpx_image
);
222 vpx_image
.planes
[VPX_PLANE_Y
] =
223 frame
.frame
->visible_data(media::VideoFrame::kYPlane
);
224 vpx_image
.planes
[VPX_PLANE_U
] =
225 frame
.frame
->visible_data(media::VideoFrame::kUPlane
);
226 vpx_image
.planes
[VPX_PLANE_V
] =
227 frame
.frame
->visible_data(media::VideoFrame::kVPlane
);
228 vpx_image
.stride
[VPX_PLANE_Y
] =
229 frame
.frame
->stride(media::VideoFrame::kYPlane
);
230 vpx_image
.stride
[VPX_PLANE_U
] =
231 frame
.frame
->stride(media::VideoFrame::kUPlane
);
232 vpx_image
.stride
[VPX_PLANE_V
] =
233 frame
.frame
->stride(media::VideoFrame::kVPlane
);
235 vpx_codec_flags_t flags
= 0;
236 if (frame
.force_keyframe
)
237 flags
= VPX_EFLAG_FORCE_KF
;
239 const base::TimeDelta frame_duration
=
240 base::TimeDelta::FromSecondsD(1.0 / framerate_
);
241 if (vpx_codec_encode(&encoder_
, &vpx_image
, 0,
242 frame_duration
.InMicroseconds(), flags
,
243 VPX_DL_REALTIME
) != VPX_CODEC_OK
) {
244 NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError
);
248 const vpx_codec_cx_pkt_t
* packet
= nullptr;
249 vpx_codec_iter_t iter
= nullptr;
250 while ((packet
= vpx_codec_get_cx_data(&encoder_
, &iter
)) != nullptr) {
251 if (packet
->kind
!= VPX_CODEC_CX_FRAME_PKT
)
254 BitstreamBuffer buffer
= buffers_
.front();
255 buffers_
.pop_front();
257 CHECK(buffer
.buffer
.size() >= packet
->data
.frame
.sz
);
258 memcpy(buffer
.mem
, packet
->data
.frame
.buf
, packet
->data
.frame
.sz
);
260 // Pass the media::VideoFrame back to the renderer thread so it's
261 // freed on the right thread.
262 renderer_task_runner_
->PostTask(
264 base::Bind(&VideoEncoderShim::OnBitstreamBufferReady
, shim_
,
265 frame
.frame
, buffer
.buffer
.id(),
266 base::checked_cast
<size_t>(packet
->data
.frame
.sz
),
267 (packet
->data
.frame
.flags
& VPX_FRAME_IS_KEY
) != 0));
268 break; // Done, since all data is provided in one CX_FRAME_PKT packet.
273 void VideoEncoderShim::EncoderImpl::NotifyError(
274 media::VideoEncodeAccelerator::Error error
) {
275 renderer_task_runner_
->PostTask(
276 FROM_HERE
, base::Bind(&VideoEncoderShim::OnNotifyError
, shim_
, error
));
280 VideoEncoderShim::VideoEncoderShim(PepperVideoEncoderHost
* host
)
283 RenderThreadImpl::current()->GetMediaThreadTaskRunner()),
284 weak_ptr_factory_(this) {
285 encoder_impl_
.reset(new EncoderImpl(weak_ptr_factory_
.GetWeakPtr()));
288 VideoEncoderShim::~VideoEncoderShim() {
289 DCHECK(RenderThreadImpl::current());
291 media_task_runner_
->PostTask(
292 FROM_HERE
, base::Bind(&VideoEncoderShim::EncoderImpl::Stop
,
293 base::Owned(encoder_impl_
.release())));
296 media::VideoEncodeAccelerator::SupportedProfiles
297 VideoEncoderShim::GetSupportedProfiles() {
298 media::VideoEncodeAccelerator::SupportedProfiles profiles
;
300 // Get the default VP8 config from Libvpx.
301 vpx_codec_enc_cfg_t config
;
302 vpx_codec_err_t ret
=
303 vpx_codec_enc_config_default(vpx_codec_vp8_cx(), &config
, 0);
304 if (ret
== VPX_CODEC_OK
) {
305 media::VideoEncodeAccelerator::SupportedProfile profile
;
306 profile
.profile
= media::VP8PROFILE_ANY
;
307 profile
.max_resolution
= gfx::Size(kMaxWidth
, kMaxHeight
);
308 // Libvpx and media::VideoEncodeAccelerator are using opposite
309 // notions of denominator/numerator.
310 profile
.max_framerate_numerator
= config
.g_timebase
.den
;
311 profile
.max_framerate_denominator
= config
.g_timebase
.num
;
312 profiles
.push_back(profile
);
318 bool VideoEncoderShim::Initialize(
319 media::VideoPixelFormat input_format
,
320 const gfx::Size
& input_visible_size
,
321 media::VideoCodecProfile output_profile
,
322 uint32 initial_bitrate
,
323 media::VideoEncodeAccelerator::Client
* client
) {
324 DCHECK(RenderThreadImpl::current());
325 DCHECK_EQ(client
, host_
);
327 if (input_format
!= media::PIXEL_FORMAT_I420
)
330 media_task_runner_
->PostTask(
332 base::Bind(&VideoEncoderShim::EncoderImpl::Initialize
,
333 base::Unretained(encoder_impl_
.get()), input_format
,
334 input_visible_size
, output_profile
, initial_bitrate
));
339 void VideoEncoderShim::Encode(const scoped_refptr
<media::VideoFrame
>& frame
,
340 bool force_keyframe
) {
341 DCHECK(RenderThreadImpl::current());
343 media_task_runner_
->PostTask(
345 base::Bind(&VideoEncoderShim::EncoderImpl::Encode
,
346 base::Unretained(encoder_impl_
.get()), frame
, force_keyframe
));
349 void VideoEncoderShim::UseOutputBitstreamBuffer(
350 const media::BitstreamBuffer
& buffer
) {
351 DCHECK(RenderThreadImpl::current());
353 media_task_runner_
->PostTask(
355 base::Bind(&VideoEncoderShim::EncoderImpl::UseOutputBitstreamBuffer
,
356 base::Unretained(encoder_impl_
.get()), buffer
,
357 host_
->ShmHandleToAddress(buffer
.id())));
360 void VideoEncoderShim::RequestEncodingParametersChange(uint32 bitrate
,
362 DCHECK(RenderThreadImpl::current());
364 media_task_runner_
->PostTask(
367 &VideoEncoderShim::EncoderImpl::RequestEncodingParametersChange
,
368 base::Unretained(encoder_impl_
.get()), bitrate
, framerate
));
371 void VideoEncoderShim::Destroy() {
372 DCHECK(RenderThreadImpl::current());
377 void VideoEncoderShim::OnRequireBitstreamBuffers(
378 unsigned int input_count
,
379 const gfx::Size
& input_coded_size
,
380 size_t output_buffer_size
) {
381 DCHECK(RenderThreadImpl::current());
383 host_
->RequireBitstreamBuffers(input_count
, input_coded_size
,
387 void VideoEncoderShim::OnBitstreamBufferReady(
388 scoped_refptr
<media::VideoFrame
> frame
,
389 int32 bitstream_buffer_id
,
392 DCHECK(RenderThreadImpl::current());
394 host_
->BitstreamBufferReady(bitstream_buffer_id
, payload_size
, key_frame
);
397 void VideoEncoderShim::OnNotifyError(
398 media::VideoEncodeAccelerator::Error error
) {
399 DCHECK(RenderThreadImpl::current());
401 host_
->NotifyError(error
);
404 } // namespace content