Roll src/third_party/WebKit 9d2dfea:3aea697 (svn 201972:201973)
[chromium-blink-merge.git] / content / renderer / pepper / video_encoder_shim.cc
blob1942a210b9e32c081106d73d28a5dc7e4cbc2367
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"
7 #include <inttypes.h>
9 #include <deque>
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"
23 namespace content {
25 namespace {
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
30 // software encoding.
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
56 // conferencing)).
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,
63 int32_t* cpu_used) {
64 switch (codec) {
65 case media::VP8PROFILE_ANY:
66 *vpx_codec = vpx_codec_vp8_cx();
67 *min_quantizer = kVp8DefaultMinQuantizer;
68 *max_quantizer = kVp8DefaultMaxQuantizer;
69 *cpu_used = kVp8DefaultCpuUsed;
70 break;
71 case media::VP9PROFILE_ANY:
72 *vpx_codec = vpx_codec_vp9_cx();
73 *min_quantizer = kVp9DefaultMinQuantizer;
74 *max_quantizer = kVp9DefaultMaxQuantizer;
75 *cpu_used = kVp9DefaultCpuUsed;
76 break;
77 default:
78 *vpx_codec = nullptr;
79 *min_quantizer = 0;
80 *max_quantizer = 0;
81 *cpu_used = 0;
82 NOTREACHED();
86 } // namespace
88 class VideoEncoderShim::EncoderImpl {
89 public:
90 explicit EncoderImpl(const base::WeakPtr<VideoEncoderShim>& shim);
91 ~EncoderImpl();
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,
98 bool force_keyframe);
99 void UseOutputBitstreamBuffer(const media::BitstreamBuffer& buffer,
100 uint8_t* mem);
101 void RequestEncodingParametersChange(uint32 bitrate, uint32 framerate);
102 void Stop();
104 private:
105 struct PendingEncode {
106 PendingEncode(const scoped_refptr<media::VideoFrame>& frame,
107 bool force_keyframe)
108 : frame(frame), force_keyframe(force_keyframe) {}
109 ~PendingEncode() {}
111 scoped_refptr<media::VideoFrame> frame;
112 bool force_keyframe;
115 struct BitstreamBuffer {
116 BitstreamBuffer(const media::BitstreamBuffer buffer, uint8_t* mem)
117 : buffer(buffer), mem(mem) {}
118 ~BitstreamBuffer() {}
120 media::BitstreamBuffer buffer;
121 uint8_t* mem;
124 void DoEncode();
125 void NotifyError(media::VideoEncodeAccelerator::Error error);
127 base::WeakPtr<VideoEncoderShim> shim_;
128 scoped_refptr<base::SingleThreadTaskRunner> renderer_task_runner_;
130 bool initialized_;
132 // Libvpx internal objects. Only valid if |initialized_| is true.
133 vpx_codec_enc_cfg_t config_;
134 vpx_codec_ctx_t encoder_;
136 uint32 framerate_;
138 std::deque<PendingEncode> frames_;
139 std::deque<BitstreamBuffer> buffers_;
142 VideoEncoderShim::EncoderImpl::EncoderImpl(
143 const base::WeakPtr<VideoEncoderShim>& shim)
144 : shim_(shim),
145 renderer_task_runner_(base::ThreadTaskRunnerHandle::Get()),
146 initialized_(false) {
149 VideoEncoderShim::EncoderImpl::~EncoderImpl() {
150 if (initialized_)
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);
170 return;
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) !=
187 VPX_CODEC_OK) {
188 NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError);
189 return;
191 initialized_ = true;
193 if (vpx_codec_enc_config_set(&encoder_, &config_) != VPX_CODEC_OK) {
194 NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError);
195 return;
198 if (vpx_codec_control(&encoder_, VP8E_SET_CPUUSED, cpu_used) !=
199 VPX_CODEC_OK) {
200 NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError);
201 return;
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);
208 return;
212 renderer_task_runner_->PostTask(
213 FROM_HERE,
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));
222 DoEncode();
225 void VideoEncoderShim::EncoderImpl::UseOutputBitstreamBuffer(
226 const media::BitstreamBuffer& buffer,
227 uint8_t* mem) {
228 buffers_.push_back(BitstreamBuffer(buffer, mem));
229 DoEncode();
232 void VideoEncoderShim::EncoderImpl::RequestEncodingParametersChange(
233 uint32 bitrate,
234 uint32 framerate) {
235 framerate_ = framerate;
237 uint32 bitrate_kbit = bitrate / 1000;
238 if (config_.rc_target_bitrate == bitrate_kbit)
239 return;
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();
250 frames_.pop_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);
257 buffers_.clear();
260 void VideoEncoderShim::EncoderImpl::DoEncode() {
261 while (!frames_.empty() && !buffers_.empty()) {
262 PendingEncode frame = frames_.front();
263 frames_.pop_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);
297 return;
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)
304 continue;
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(
315 FROM_HERE,
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));
329 Stop();
332 VideoEncoderShim::VideoEncoderShim(PepperVideoEncoderHost* host)
333 : host_(host),
334 media_task_runner_(
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);
377 return profiles;
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)
390 return false;
392 if (output_profile != media::VP8PROFILE_ANY &&
393 output_profile != media::VP9PROFILE_ANY)
394 return false;
396 media_task_runner_->PostTask(
397 FROM_HERE,
398 base::Bind(&VideoEncoderShim::EncoderImpl::Initialize,
399 base::Unretained(encoder_impl_.get()), input_format,
400 input_visible_size, output_profile, initial_bitrate));
402 return true;
405 void VideoEncoderShim::Encode(const scoped_refptr<media::VideoFrame>& frame,
406 bool force_keyframe) {
407 DCHECK(RenderThreadImpl::current());
409 media_task_runner_->PostTask(
410 FROM_HERE,
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(
420 FROM_HERE,
421 base::Bind(&VideoEncoderShim::EncoderImpl::UseOutputBitstreamBuffer,
422 base::Unretained(encoder_impl_.get()), buffer,
423 host_->ShmHandleToAddress(buffer.id())));
426 void VideoEncoderShim::RequestEncodingParametersChange(uint32 bitrate,
427 uint32 framerate) {
428 DCHECK(RenderThreadImpl::current());
430 media_task_runner_->PostTask(
431 FROM_HERE,
432 base::Bind(
433 &VideoEncoderShim::EncoderImpl::RequestEncodingParametersChange,
434 base::Unretained(encoder_impl_.get()), bitrate, framerate));
437 void VideoEncoderShim::Destroy() {
438 DCHECK(RenderThreadImpl::current());
440 delete this;
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,
450 output_buffer_size);
453 void VideoEncoderShim::OnBitstreamBufferReady(
454 scoped_refptr<media::VideoFrame> frame,
455 int32 bitstream_buffer_id,
456 size_t payload_size,
457 bool key_frame) {
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