Chromium Code Reviews| Index: content/renderer/pepper/video_encoder_shim.cc |
| diff --git a/content/renderer/pepper/video_encoder_shim.cc b/content/renderer/pepper/video_encoder_shim.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..eeaa88d1928041d59820a19ea1b1a6eac717417b |
| --- /dev/null |
| +++ b/content/renderer/pepper/video_encoder_shim.cc |
| @@ -0,0 +1,277 @@ |
| +// Copyright 2015 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "content/renderer/pepper/video_encoder_shim.h" |
| + |
| +#include <deque> |
| + |
| +#include "base/bind.h" |
| +#include "base/bind_helpers.h" |
| +#include "base/memory/scoped_vector.h" |
| +#include "base/memory/shared_memory.h" |
| +#include "base/message_loop/message_loop.h" |
| +#include "content/renderer/pepper/pepper_video_encoder_host.h" |
| +#include "content/renderer/render_thread_impl.h" |
| +#include "media/cast/cast_config.h" |
| +#include "media/cast/sender/vp8_encoder.h" |
| +#include "ui/gfx/geometry/size.h" |
| + |
| +namespace content { |
| + |
| +// TODO(llandwerlin): Libvpx doesn't seem to have a maximum frame size |
| +// limitation. We currently limit the size of the frames to encode at |
| +// 1080p (%64 pixels blocks), this seems like a reasonable limit for |
| +// software encoding. |
| +const int32_t kMaxWidth = 1920; |
| +const int32_t kMaxHeight = 1088; |
| + |
| +// Bitstream buffer size : 4Mb. |
| +const uint32_t kBitstreamBufferSize = 4000000; |
|
bbudge
2015/03/16 18:27:48
nit: not really megabits (Mbs) or even megabytes (
llandwerlin-old
2015/03/16 19:08:09
Thanks, I think that's a bit too much for the aver
|
| + |
| +// Number of frames needs at any given time. |
| +const uint32_t kInputFrameCount = 1; |
| + |
| +class VideoEncoderShim::EncoderImpl { |
| + public: |
| + explicit EncoderImpl(const base::WeakPtr<VideoEncoderShim>& shim); |
| + ~EncoderImpl(); |
| + |
| + void Initialize(media::VideoFrame::Format input_format, |
| + const gfx::Size& input_visible_size, |
| + media::VideoCodecProfile output_profile, |
| + uint32 initial_bitrate); |
| + void Encode(const scoped_refptr<media::VideoFrame>& frame, |
| + bool force_keyframe); |
| + void UseOutputBitstreamBuffer(const media::BitstreamBuffer& buffer, |
| + uint8_t* mem); |
| + void RequestEncodingParametersChange(uint32 bitrate, uint32 framerate); |
| + void Stop(); |
| + |
| + private: |
| + struct PendingEncode { |
| + PendingEncode(const scoped_refptr<media::VideoFrame>& frame, |
| + bool force_keyframe) |
| + : frame(frame), force_keyframe(force_keyframe) {} |
| + ~PendingEncode() {} |
| + |
| + scoped_refptr<media::VideoFrame> frame; |
| + bool force_keyframe; |
| + }; |
| + |
| + struct BitstreamBuffer { |
| + BitstreamBuffer(const media::BitstreamBuffer buffer, uint8_t* mem) |
| + : buffer(buffer), mem(mem) {} |
| + ~BitstreamBuffer() {} |
| + |
| + media::BitstreamBuffer buffer; |
| + uint8_t* mem; |
| + }; |
| + |
| + void DoEncode(); |
| + |
| + base::WeakPtr<VideoEncoderShim> shim_; |
| + scoped_refptr<base::SingleThreadTaskRunner> media_task_runner_; |
| + |
| + scoped_ptr<media::cast::SoftwareVideoEncoder> encoder_; |
| + std::deque<PendingEncode> frames_; |
| + std::deque<BitstreamBuffer> buffers_; |
| +}; |
| + |
| +VideoEncoderShim::EncoderImpl::EncoderImpl( |
| + const base::WeakPtr<VideoEncoderShim>& shim) |
| + : shim_(shim), media_task_runner_(base::MessageLoopProxy::current()) { |
| +} |
| + |
| +VideoEncoderShim::EncoderImpl::~EncoderImpl() { |
| +} |
| + |
| +void VideoEncoderShim::EncoderImpl::Initialize( |
| + media::VideoFrame::Format input_format, |
| + const gfx::Size& input_visible_size, |
| + media::VideoCodecProfile output_profile, |
| + uint32 initial_bitrate) { |
| + media::cast::VideoSenderConfig config; |
| + |
| + config.max_number_of_video_buffers_used = kInputFrameCount; |
| + config.number_of_encode_threads = 1; |
| + encoder_.reset(new media::cast::Vp8Encoder(config)); |
| + |
| + encoder_->UpdateRates(initial_bitrate); |
| + |
| + media_task_runner_->PostTask( |
| + FROM_HERE, base::Bind(&VideoEncoderShim::OnRequireBitstreamBuffers, shim_, |
| + kInputFrameCount, input_visible_size, |
| + kBitstreamBufferSize)); |
| +} |
| + |
| +void VideoEncoderShim::EncoderImpl::Encode( |
| + const scoped_refptr<media::VideoFrame>& frame, |
| + bool force_keyframe) { |
| + frames_.push_back(PendingEncode(frame, force_keyframe)); |
| + DoEncode(); |
| +} |
| + |
| +void VideoEncoderShim::EncoderImpl::UseOutputBitstreamBuffer( |
| + const media::BitstreamBuffer& buffer, |
| + uint8_t* mem) { |
| + buffers_.push_back(BitstreamBuffer(buffer, mem)); |
| + DoEncode(); |
| +} |
| + |
| +void VideoEncoderShim::EncoderImpl::RequestEncodingParametersChange( |
| + uint32 bitrate, |
| + uint32 framerate) { |
| + encoder_->UpdateRates(bitrate); |
| +} |
| + |
| +void VideoEncoderShim::EncoderImpl::Stop() { |
| + frames_.clear(); |
| + buffers_.clear(); |
| + encoder_.reset(); |
| +} |
| + |
| +void VideoEncoderShim::EncoderImpl::DoEncode() { |
| + while (!frames_.empty() && !buffers_.empty()) { |
| + PendingEncode frame = frames_.front(); |
| + frames_.pop_front(); |
| + |
| + if (frame.force_keyframe) |
| + encoder_->GenerateKeyFrame(); |
| + |
| + scoped_ptr<media::cast::EncodedFrame> encoded_frame( |
| + new media::cast::EncodedFrame()); |
| + encoder_->Encode(frame.frame, base::TimeTicks::Now(), encoded_frame.get()); |
| + |
| + BitstreamBuffer buffer = buffers_.front(); |
| + buffers_.pop_front(); |
| + |
| + CHECK(buffer.buffer.size() >= encoded_frame->data.size()); |
| + memcpy(buffer.mem, encoded_frame->bytes(), encoded_frame->data.size()); |
| + |
| + // Pass the media::VideoFrame back to the renderer thread so it's |
| + // freed on the right thread. |
| + media_task_runner_->PostTask( |
| + FROM_HERE, |
| + base::Bind( |
| + &VideoEncoderShim::OnBitstreamBufferReady, shim_, |
| + frame.frame, buffer.buffer.id(), encoded_frame->data.size(), |
| + encoded_frame->dependency == media::cast::EncodedFrame::KEY)); |
| + } |
| +} |
| + |
| +VideoEncoderShim::VideoEncoderShim(PepperVideoEncoderHost* host) |
| + : host_(host), |
| + media_task_runner_( |
| + RenderThreadImpl::current()->GetMediaThreadTaskRunner()), |
| + weak_ptr_factory_(this) { |
| + encoder_impl_.reset(new EncoderImpl(weak_ptr_factory_.GetWeakPtr())); |
| +} |
| + |
| +VideoEncoderShim::~VideoEncoderShim() { |
| + DCHECK(RenderThreadImpl::current()); |
| + |
| + media_task_runner_->PostTask( |
| + FROM_HERE, base::Bind(&VideoEncoderShim::EncoderImpl::Stop, |
| + base::Owned(encoder_impl_.release()))); |
| +} |
| + |
| +std::vector<media::VideoEncodeAccelerator::SupportedProfile> |
| +VideoEncoderShim::GetSupportedProfiles() { |
| + media::VideoEncodeAccelerator::SupportedProfile profile = { |
| + media::VP8PROFILE_ANY, |
| + gfx::Size(kMaxWidth, kMaxHeight), |
| + media::cast::kDefaultMaxFrameRate, |
| + 1}; |
| + std::vector<media::VideoEncodeAccelerator::SupportedProfile> profiles; |
| + profiles.push_back(profile); |
| + return profiles; |
| +} |
| + |
| +bool VideoEncoderShim::Initialize( |
| + media::VideoFrame::Format input_format, |
| + const gfx::Size& input_visible_size, |
| + media::VideoCodecProfile output_profile, |
| + uint32 initial_bitrate, |
| + media::VideoEncodeAccelerator::Client* client) { |
| + DCHECK(RenderThreadImpl::current()); |
| + DCHECK_EQ(client, host_); |
| + |
| + if (input_format != media::VideoFrame::I420) |
| + return false; |
| + |
| + media_task_runner_->PostTask( |
| + FROM_HERE, |
| + base::Bind(&VideoEncoderShim::EncoderImpl::Initialize, |
| + base::Unretained(encoder_impl_.get()), input_format, |
| + input_visible_size, output_profile, initial_bitrate)); |
| + |
| + return true; |
| +} |
| + |
| +void VideoEncoderShim::Encode(const scoped_refptr<media::VideoFrame>& frame, |
| + bool force_keyframe) { |
| + DCHECK(RenderThreadImpl::current()); |
| + |
| + media_task_runner_->PostTask( |
| + FROM_HERE, |
| + base::Bind(&VideoEncoderShim::EncoderImpl::Encode, |
| + base::Unretained(encoder_impl_.get()), frame, force_keyframe)); |
| +} |
| + |
| +void VideoEncoderShim::UseOutputBitstreamBuffer( |
| + const media::BitstreamBuffer& buffer) { |
| + DCHECK(RenderThreadImpl::current()); |
| + |
| + media_task_runner_->PostTask( |
| + FROM_HERE, |
| + base::Bind(&VideoEncoderShim::EncoderImpl::UseOutputBitstreamBuffer, |
| + base::Unretained(encoder_impl_.get()), buffer, |
| + host_->ShmHandleToAddress(buffer.id()))); |
| +} |
| + |
| +void VideoEncoderShim::RequestEncodingParametersChange(uint32 bitrate, |
| + uint32 framerate) { |
| + DCHECK(RenderThreadImpl::current()); |
| + |
| + media_task_runner_->PostTask( |
| + FROM_HERE, |
| + base::Bind( |
| + &VideoEncoderShim::EncoderImpl::RequestEncodingParametersChange, |
| + base::Unretained(encoder_impl_.get()), bitrate, framerate)); |
| +} |
| + |
| +void VideoEncoderShim::Destroy() { |
| + DCHECK(RenderThreadImpl::current()); |
| + |
| + delete this; |
| +} |
| + |
| +void VideoEncoderShim::OnRequireBitstreamBuffers( |
| + unsigned int input_count, |
| + const gfx::Size& input_coded_size, |
| + size_t output_buffer_size) { |
| + DCHECK(RenderThreadImpl::current()); |
| + |
| + host_->RequireBitstreamBuffers(input_count, input_coded_size, |
| + output_buffer_size); |
| +} |
| + |
| +void VideoEncoderShim::OnBitstreamBufferReady( |
| + scoped_refptr<media::VideoFrame> frame, |
| + int32 bitstream_buffer_id, |
| + size_t payload_size, |
| + bool key_frame) { |
| + DCHECK(RenderThreadImpl::current()); |
| + |
| + host_->BitstreamBufferReady(bitstream_buffer_id, payload_size, key_frame); |
| +} |
| + |
| +void VideoEncoderShim::OnNotifyError( |
| + media::VideoEncodeAccelerator::Error error) { |
| + DCHECK(RenderThreadImpl::current()); |
| + |
| + host_->NotifyError(error); |
| +} |
| + |
| +} // namespace content |