Process Alt-Svc headers.
[chromium-blink-merge.git] / content / renderer / pepper / video_encoder_shim.cc
blobd28744fd76fdced6433c43cea3315bb3aacaaf20
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 // 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
29 // software encoding.
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 {
48 public:
49 explicit EncoderImpl(const base::WeakPtr<VideoEncoderShim>& shim);
50 ~EncoderImpl();
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,
57 bool force_keyframe);
58 void UseOutputBitstreamBuffer(const media::BitstreamBuffer& buffer,
59 uint8_t* mem);
60 void RequestEncodingParametersChange(uint32 bitrate, uint32 framerate);
61 void Stop();
63 private:
64 struct PendingEncode {
65 PendingEncode(const scoped_refptr<media::VideoFrame>& frame,
66 bool force_keyframe)
67 : frame(frame), force_keyframe(force_keyframe) {}
68 ~PendingEncode() {}
70 scoped_refptr<media::VideoFrame> frame;
71 bool force_keyframe;
74 struct BitstreamBuffer {
75 BitstreamBuffer(const media::BitstreamBuffer buffer, uint8_t* mem)
76 : buffer(buffer), mem(mem) {}
77 ~BitstreamBuffer() {}
79 media::BitstreamBuffer buffer;
80 uint8_t* mem;
83 void DoEncode();
84 void NotifyError(media::VideoEncodeAccelerator::Error error);
86 base::WeakPtr<VideoEncoderShim> shim_;
87 scoped_refptr<base::SingleThreadTaskRunner> renderer_task_runner_;
89 bool initialized_;
91 // Libvpx internal objects. Only valid if |initialized_| is true.
92 vpx_codec_enc_cfg_t config_;
93 vpx_codec_ctx_t encoder_;
95 uint32 framerate_;
97 std::deque<PendingEncode> frames_;
98 std::deque<BitstreamBuffer> buffers_;
101 VideoEncoderShim::EncoderImpl::EncoderImpl(
102 const base::WeakPtr<VideoEncoderShim>& shim)
103 : shim_(shim),
104 renderer_task_runner_(base::ThreadTaskRunnerHandle::Get()),
105 initialized_(false) {
108 VideoEncoderShim::EncoderImpl::~EncoderImpl() {
109 if (initialized_)
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) !=
123 VPX_CODEC_OK) {
124 NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError);
125 return;
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) !=
143 VPX_CODEC_OK) {
144 NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError);
145 return;
147 initialized_ = true;
149 if (vpx_codec_enc_config_set(&encoder_, &config_) != VPX_CODEC_OK) {
150 NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError);
151 return;
154 if (vpx_codec_control(&encoder_, VP8E_SET_CPUUSED, kDefaultCpuUsed) !=
155 VPX_CODEC_OK) {
156 NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError);
157 return;
160 renderer_task_runner_->PostTask(
161 FROM_HERE,
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));
170 DoEncode();
173 void VideoEncoderShim::EncoderImpl::UseOutputBitstreamBuffer(
174 const media::BitstreamBuffer& buffer,
175 uint8_t* mem) {
176 buffers_.push_back(BitstreamBuffer(buffer, mem));
177 DoEncode();
180 void VideoEncoderShim::EncoderImpl::RequestEncodingParametersChange(
181 uint32 bitrate,
182 uint32 framerate) {
183 framerate_ = framerate;
185 uint32 bitrate_kbit = bitrate / 1000;
186 if (config_.rc_target_bitrate == bitrate_kbit)
187 return;
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();
198 frames_.pop_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);
205 buffers_.clear();
208 void VideoEncoderShim::EncoderImpl::DoEncode() {
209 while (!frames_.empty() && !buffers_.empty()) {
210 PendingEncode frame = frames_.front();
211 frames_.pop_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);
245 return;
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)
252 continue;
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(
263 FROM_HERE,
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));
277 Stop();
280 VideoEncoderShim::VideoEncoderShim(PepperVideoEncoderHost* host)
281 : host_(host),
282 media_task_runner_(
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);
315 return profiles;
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)
328 return false;
330 media_task_runner_->PostTask(
331 FROM_HERE,
332 base::Bind(&VideoEncoderShim::EncoderImpl::Initialize,
333 base::Unretained(encoder_impl_.get()), input_format,
334 input_visible_size, output_profile, initial_bitrate));
336 return true;
339 void VideoEncoderShim::Encode(const scoped_refptr<media::VideoFrame>& frame,
340 bool force_keyframe) {
341 DCHECK(RenderThreadImpl::current());
343 media_task_runner_->PostTask(
344 FROM_HERE,
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(
354 FROM_HERE,
355 base::Bind(&VideoEncoderShim::EncoderImpl::UseOutputBitstreamBuffer,
356 base::Unretained(encoder_impl_.get()), buffer,
357 host_->ShmHandleToAddress(buffer.id())));
360 void VideoEncoderShim::RequestEncodingParametersChange(uint32 bitrate,
361 uint32 framerate) {
362 DCHECK(RenderThreadImpl::current());
364 media_task_runner_->PostTask(
365 FROM_HERE,
366 base::Bind(
367 &VideoEncoderShim::EncoderImpl::RequestEncodingParametersChange,
368 base::Unretained(encoder_impl_.get()), bitrate, framerate));
371 void VideoEncoderShim::Destroy() {
372 DCHECK(RenderThreadImpl::current());
374 delete this;
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,
384 output_buffer_size);
387 void VideoEncoderShim::OnBitstreamBufferReady(
388 scoped_refptr<media::VideoFrame> frame,
389 int32 bitstream_buffer_id,
390 size_t payload_size,
391 bool key_frame) {
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