[Android] Implement 3-way sensor fallback for Device Orientation.
[chromium-blink-merge.git] / content / renderer / pepper / video_encoder_shim.cc
blobac4e1b3c2d5ef2d7891f270d576bf8ad259e9257
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/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"
24 namespace content {
26 namespace {
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
31 // software encoding.
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
57 // conferencing)).
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,
64 int32_t* cpu_used) {
65 switch (codec) {
66 case media::VP8PROFILE_ANY:
67 *vpx_codec = vpx_codec_vp8_cx();
68 *min_quantizer = kVp8DefaultMinQuantizer;
69 *max_quantizer = kVp8DefaultMaxQuantizer;
70 *cpu_used = kVp8DefaultCpuUsed;
71 break;
72 case media::VP9PROFILE_ANY:
73 *vpx_codec = vpx_codec_vp9_cx();
74 *min_quantizer = kVp9DefaultMinQuantizer;
75 *max_quantizer = kVp9DefaultMaxQuantizer;
76 *cpu_used = kVp9DefaultCpuUsed;
77 break;
78 default:
79 *vpx_codec = nullptr;
80 *min_quantizer = 0;
81 *max_quantizer = 0;
82 *cpu_used = 0;
83 NOTREACHED();
87 } // namespace
89 class VideoEncoderShim::EncoderImpl {
90 public:
91 explicit EncoderImpl(const base::WeakPtr<VideoEncoderShim>& shim);
92 ~EncoderImpl();
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,
99 bool force_keyframe);
100 void UseOutputBitstreamBuffer(const media::BitstreamBuffer& buffer,
101 uint8_t* mem);
102 void RequestEncodingParametersChange(uint32 bitrate, uint32 framerate);
103 void Stop();
105 private:
106 struct PendingEncode {
107 PendingEncode(const scoped_refptr<media::VideoFrame>& frame,
108 bool force_keyframe)
109 : frame(frame), force_keyframe(force_keyframe) {}
110 ~PendingEncode() {}
112 scoped_refptr<media::VideoFrame> frame;
113 bool force_keyframe;
116 struct BitstreamBuffer {
117 BitstreamBuffer(const media::BitstreamBuffer buffer, uint8_t* mem)
118 : buffer(buffer), mem(mem) {}
119 ~BitstreamBuffer() {}
121 media::BitstreamBuffer buffer;
122 uint8_t* mem;
125 void DoEncode();
126 void NotifyError(media::VideoEncodeAccelerator::Error error);
128 base::WeakPtr<VideoEncoderShim> shim_;
129 scoped_refptr<base::SingleThreadTaskRunner> renderer_task_runner_;
131 bool initialized_;
133 // Libvpx internal objects. Only valid if |initialized_| is true.
134 vpx_codec_enc_cfg_t config_;
135 vpx_codec_ctx_t encoder_;
137 uint32 framerate_;
139 std::deque<PendingEncode> frames_;
140 std::deque<BitstreamBuffer> buffers_;
143 VideoEncoderShim::EncoderImpl::EncoderImpl(
144 const base::WeakPtr<VideoEncoderShim>& shim)
145 : shim_(shim),
146 renderer_task_runner_(base::ThreadTaskRunnerHandle::Get()),
147 initialized_(false) {
150 VideoEncoderShim::EncoderImpl::~EncoderImpl() {
151 if (initialized_)
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);
171 return;
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) !=
188 VPX_CODEC_OK) {
189 NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError);
190 return;
192 initialized_ = true;
194 if (vpx_codec_enc_config_set(&encoder_, &config_) != VPX_CODEC_OK) {
195 NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError);
196 return;
199 if (vpx_codec_control(&encoder_, VP8E_SET_CPUUSED, cpu_used) !=
200 VPX_CODEC_OK) {
201 NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError);
202 return;
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);
209 return;
213 renderer_task_runner_->PostTask(
214 FROM_HERE,
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));
223 DoEncode();
226 void VideoEncoderShim::EncoderImpl::UseOutputBitstreamBuffer(
227 const media::BitstreamBuffer& buffer,
228 uint8_t* mem) {
229 buffers_.push_back(BitstreamBuffer(buffer, mem));
230 DoEncode();
233 void VideoEncoderShim::EncoderImpl::RequestEncodingParametersChange(
234 uint32 bitrate,
235 uint32 framerate) {
236 framerate_ = framerate;
238 uint32 bitrate_kbit = bitrate / 1000;
239 if (config_.rc_target_bitrate == bitrate_kbit)
240 return;
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();
251 frames_.pop_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);
258 buffers_.clear();
261 void VideoEncoderShim::EncoderImpl::DoEncode() {
262 while (!frames_.empty() && !buffers_.empty()) {
263 PendingEncode frame = frames_.front();
264 frames_.pop_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);
298 return;
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)
305 continue;
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(
316 FROM_HERE,
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));
330 Stop();
333 VideoEncoderShim::VideoEncoderShim(PepperVideoEncoderHost* host)
334 : host_(host),
335 media_task_runner_(
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);
378 return profiles;
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)
391 return false;
393 if (output_profile != media::VP8PROFILE_ANY &&
394 output_profile != media::VP9PROFILE_ANY)
395 return false;
397 media_task_runner_->PostTask(
398 FROM_HERE,
399 base::Bind(&VideoEncoderShim::EncoderImpl::Initialize,
400 base::Unretained(encoder_impl_.get()), input_format,
401 input_visible_size, output_profile, initial_bitrate));
403 return true;
406 void VideoEncoderShim::Encode(const scoped_refptr<media::VideoFrame>& frame,
407 bool force_keyframe) {
408 DCHECK(RenderThreadImpl::current());
410 media_task_runner_->PostTask(
411 FROM_HERE,
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(
421 FROM_HERE,
422 base::Bind(&VideoEncoderShim::EncoderImpl::UseOutputBitstreamBuffer,
423 base::Unretained(encoder_impl_.get()), buffer,
424 host_->ShmHandleToAddress(buffer.id())));
427 void VideoEncoderShim::RequestEncodingParametersChange(uint32 bitrate,
428 uint32 framerate) {
429 DCHECK(RenderThreadImpl::current());
431 media_task_runner_->PostTask(
432 FROM_HERE,
433 base::Bind(
434 &VideoEncoderShim::EncoderImpl::RequestEncodingParametersChange,
435 base::Unretained(encoder_impl_.get()), bitrate, framerate));
438 void VideoEncoderShim::Destroy() {
439 DCHECK(RenderThreadImpl::current());
441 delete this;
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,
451 output_buffer_size);
454 void VideoEncoderShim::OnBitstreamBufferReady(
455 scoped_refptr<media::VideoFrame> frame,
456 int32 bitstream_buffer_id,
457 size_t payload_size,
458 bool key_frame) {
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