Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(212)

Unified Diff: content/renderer/media/rtc_video_encoder.cc

Issue 20632002: Add media::VideoEncodeAccelerator with WebRTC integration (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@git-svn
Patch Set: 7fd9dbd5 More debugging statements, some fixes Created 7 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: content/renderer/media/rtc_video_encoder.cc
diff --git a/content/renderer/media/rtc_video_encoder.cc b/content/renderer/media/rtc_video_encoder.cc
new file mode 100644
index 0000000000000000000000000000000000000000..9dc56cc89616accaec82f9b59c2f0d3b65b63931
--- /dev/null
+++ b/content/renderer/media/rtc_video_encoder.cc
@@ -0,0 +1,457 @@
+// Copyright 2013 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/media/rtc_video_encoder.h"
+
+#include <vector>
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/synchronization/waitable_event.h"
+#include "media/base/bitstream_buffer.h"
+#include "media/filters/gpu_video_accelerator_factories.h"
+#include "media/video/video_encode_accelerator.h"
+
+namespace content {
+
+// This private class of RTCVideoEncoder does the actual work of communicating
+// with a media::VideoEncodeAccelerator for handling video encoding. It can
+// be created on any thread, but should subsequently be posted to (and
+// destroyed) on the thread that calls CreateAndInitializeVEA(). Notifications
+// returned by the encoder are posted to the thread on which the instance was
+// constructed.
+//
+// This class is separated from the RTCVideoEncoder class to allow
+// RTCVideoEncoder to be deleted directly by WebRTC, while RTCVideoEncoder::Impl
+// stays long enough to properly shut down the VEA.
+class RTCVideoEncoder::Impl : public media::VideoEncodeAccelerator::Client {
+ public:
+ Impl(const base::WeakPtr<RTCVideoEncoder>& weak_encoder);
Ami GONE FROM CHROMIUM 2013/07/31 23:01:12 single-arg ctors need to be marked explicit.
sheu 2013/08/02 01:27:49 Done.
+ virtual ~Impl();
+
+ // Create the VEA and call Initialize() on it. This instance of Impl is bound
+ // to whichever thread makes this call.
+ void CreateAndInitializeVEA(
+ const webrtc::VideoCodec& codecSettings,
+ media::VideoCodecProfile profile,
+ base::WaitableEvent* initialization_waiter,
+ int32_t* initialization_retval,
+ const scoped_refptr<media::GpuVideoAcceleratorFactories>& gpu_factories);
+ void Encode(const media::BitstreamBuffer& buffer, bool force_keyframe);
+ void UseOutputBitstreamBuffer(const media::BitstreamBuffer& buffer);
+ void RequestEncodingParameterChange(int32 bitrate);
+ static void Destroy(scoped_ptr<Impl> self);
+
+ // media::VideoEncodeAccelerator::Client implementation.
+ virtual void NotifyInitializeDone() OVERRIDE;
+ virtual void RequireBitstreamBuffers(int input_count,
+ const gfx::Size& input_dimensions,
+ size_t output_size) OVERRIDE;
+ virtual void NotifyInputDone(int32 bitstream_buffer_id) OVERRIDE;
+ virtual void BitstreamBufferReady(int32 bitstream_buffer_id,
+ size_t payload_size,
+ bool key_frame) OVERRIDE;
+ virtual void NotifyError(media::VideoEncodeAccelerator::Error error) OVERRIDE;
+
+ private:
+ base::ThreadChecker thread_checker_;
+
+ // Weak pointer to the parent RTCVideoEncoder, for posting back VEA::Client
+ // notifications.
+ const base::WeakPtr<RTCVideoEncoder> weak_encoder_;
+
+ // The message loop on which to post notifications.
+ const scoped_refptr<base::MessageLoopProxy> encoder_message_loop_proxy_;
+
+ // webrtc::VideoEncoder expects initialization to be synchronous. Do this by
+ // waiting on the |initialization_waiter_| in InitEncode(). The event is
+ // signalled when initialization completes, or an error ooccurs, and the
+ // return value is retuned in |initialization_retval_|.
+ base::WaitableEvent* initialization_waiter_;
+ int32_t* initialization_retval_;
+
+ // The underling VEA to perform encoding on.
+ scoped_ptr<media::VideoEncodeAccelerator> video_encoder_;
+
+ DISALLOW_COPY_AND_ASSIGN(Impl);
+};
+
+RTCVideoEncoder::Impl::Impl(
+ const base::WeakPtr<RTCVideoEncoder>& weak_encoder)
+ : weak_encoder_(weak_encoder),
+ encoder_message_loop_proxy_(base::MessageLoopProxy::current()),
+ initialization_waiter_(NULL),
+ initialization_retval_(NULL) {
+ thread_checker_.DetachFromThread();
+}
+
+RTCVideoEncoder::Impl::~Impl() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!video_encoder_);
+}
+
+void RTCVideoEncoder::Impl::CreateAndInitializeVEA(
+ const webrtc::VideoCodec& codecSettings,
+ media::VideoCodecProfile profile,
+ base::WaitableEvent* initialization_waiter,
+ int32_t* initialization_retval,
+ const scoped_refptr<media::GpuVideoAcceleratorFactories>& gpu_factories) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ initialization_waiter_ = initialization_waiter;
+ initialization_retval_ = initialization_retval;
+ video_encoder_ = gpu_factories->CreateVideoEncodeAccelerator(this).Pass();
+ if (!video_encoder_) {
+ NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError);
+ return;
+ }
+ gfx::Size dimensions(codecSettings.width, codecSettings.height);
+ video_encoder_->Initialize(
+ media::VideoFrame::I420,
+ dimensions,
+ profile,
+ codecSettings.startBitrate * 1000); // startBitrate is in kbits/sec.
+}
+
+void RTCVideoEncoder::Impl::Encode(const media::BitstreamBuffer& buffer,
+ bool force_keyframe) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (video_encoder_)
+ video_encoder_->Encode(buffer, force_keyframe);
+}
+
+void RTCVideoEncoder::Impl::UseOutputBitstreamBuffer(
+ const media::BitstreamBuffer& buffer) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (video_encoder_)
+ video_encoder_->UseOutputBitstreamBuffer(buffer);
+}
+
+void RTCVideoEncoder::Impl::RequestEncodingParameterChange(int32 bitrate) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (video_encoder_)
+ video_encoder_->RequestEncodingParameterChange(bitrate);
+}
+
+// static
+void RTCVideoEncoder::Impl::Destroy(scoped_ptr<Impl> self) {
+ DCHECK(self->thread_checker_.CalledOnValidThread());
+ if (self->video_encoder_)
+ self->video_encoder_.release()->Destroy();
+}
+
+void RTCVideoEncoder::Impl::NotifyInitializeDone() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ *initialization_retval_ = WEBRTC_VIDEO_CODEC_OK;
+ initialization_waiter_->Signal();
+ initialization_retval_ = NULL;
+ initialization_waiter_ = NULL;
+}
+
+void RTCVideoEncoder::Impl::RequireBitstreamBuffers(
+ int input_count,
+ const gfx::Size& input_dimensions,
+ size_t output_size) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ encoder_message_loop_proxy_->PostTask(
+ FROM_HERE,
+ base::Bind(&RTCVideoEncoder::RequireBitstreamBuffers,
+ weak_encoder_,
+ input_count,
+ input_dimensions,
+ output_size));
+}
+
+void RTCVideoEncoder::Impl::NotifyInputDone(int32 bitstream_buffer_id) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ encoder_message_loop_proxy_->PostTask(
+ FROM_HERE,
+ base::Bind(&RTCVideoEncoder::NotifyInputDone,
+ weak_encoder_,
+ bitstream_buffer_id));
+}
+
+void RTCVideoEncoder::Impl::BitstreamBufferReady(int32 bitstream_buffer_id,
+ size_t payload_size,
+ bool key_frame) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ encoder_message_loop_proxy_->PostTask(
+ FROM_HERE,
+ base::Bind(&RTCVideoEncoder::BitstreamBufferReady,
+ weak_encoder_,
+ bitstream_buffer_id,
+ payload_size,
+ key_frame));
+}
+
+void RTCVideoEncoder::Impl::NotifyError(
+ media::VideoEncodeAccelerator::Error error) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ int32_t retval;
+ switch (error) {
+ default:
+ retval = WEBRTC_VIDEO_CODEC_ERROR;
+ }
+
+ if (video_encoder_)
+ video_encoder_.release()->Destroy();
+
+ if (initialization_waiter_) {
+ *initialization_retval_ = retval;
+ initialization_waiter_->Signal();
+ initialization_retval_ = NULL;
+ initialization_waiter_ = NULL;
+ } else {
+ encoder_message_loop_proxy_->PostTask(
+ FROM_HERE,
+ base::Bind(&RTCVideoEncoder::NotifyError, weak_encoder_, retval));
+ }
+}
+
+RTCVideoEncoder::RTCVideoEncoder(
+ media::VideoCodecProfile profile,
+ const scoped_refptr<media::GpuVideoAcceleratorFactories>& gpu_factories)
+ : video_codec_profile_(profile),
+ gpu_factories_(gpu_factories),
+ impl_message_loop_proxy_(gpu_factories_->GetMessageLoop()),
+ weak_this_factory_(this),
+ weak_this_(weak_this_factory_.GetWeakPtr()),
+ encoded_image_callback_(NULL),
+ impl_status_(WEBRTC_VIDEO_CODEC_OK) {
+ DVLOG(1) << "RTCVideoEncoder::RTCVideoEncoder(): profile=" << profile;
+}
+
+RTCVideoEncoder::~RTCVideoEncoder() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ Release();
+ DCHECK(!impl_);
+}
+
+int32_t RTCVideoEncoder::InitEncode(const webrtc::VideoCodec* codec_settings,
+ int32_t number_of_cores,
+ uint32_t max_payload_size) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!impl_);
+ DVLOG(1) << "RTCVideoEncoder::InitEncode(): "
+ "codecType=" << codec_settings->codecType
+ << ", width=" << codec_settings->width
+ << ", height=" << codec_settings->height
+ << ", startBitrate=" << codec_settings->startBitrate;
+
+ impl_.reset(new Impl(weak_this_));
+ base::WaitableEvent initialization_waiter(true, false);
+ int32_t initialization_retval = WEBRTC_VIDEO_CODEC_UNINITIALIZED;
+ impl_message_loop_proxy_->PostTask(
+ FROM_HERE,
+ base::Bind(&RTCVideoEncoder::Impl::CreateAndInitializeVEA,
+ base::Unretained(impl_.get()),
+ *codec_settings,
+ video_codec_profile_,
+ &initialization_waiter,
+ &initialization_retval,
+ gpu_factories_));
+
+ // webrtc::VideoEncoder expects this call to be synchronous.
+ initialization_waiter.Wait();
+ return initialization_retval;
+}
+
+int32_t RTCVideoEncoder::Encode(
+ const webrtc::I420VideoFrame& input_image,
+ const webrtc::CodecSpecificInfo* codec_specific_info,
+ const std::vector<webrtc::VideoFrameType>* frame_types) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (!impl_)
+ return impl_status_;
+
+ if (input_buffers_free_.empty())
+ return WEBRTC_VIDEO_CODEC_OK;
+ int index = input_buffers_free_.back();
+ input_buffers_free_.pop_back();
+ base::SharedMemory* input_buffer = input_buffers_[index];
+
+ // Do a strided copy of the input frame to match the input requirements for
+ // the encoder.
+ const uint8_t* src = input_image.buffer(webrtc::kYPlane);
+ uint8* dst = reinterpret_cast<uint8*>(input_buffer->memory());
+ int width = input_frame_dimensions_.width();
+ int stride = input_image.stride(webrtc::kYPlane);
+ for (int i = 0; i < input_image.height(); ++i) {
+ memcpy(dst, src, width);
+ dst += stride;
+ src += width;
+ }
+ src = input_image.buffer(webrtc::kUPlane);
+ width = input_frame_dimensions_.width() / 2;
+ stride = input_image.stride(webrtc::kUPlane);
+ for (int i = 0; i < input_image.height() / 2; ++i) {
+ memcpy(dst, src, width);
+ dst += stride;
+ src += width;
+ }
+ src = input_image.buffer(webrtc::kVPlane);
+ width = input_frame_dimensions_.width() / 2;
+ stride = input_image.stride(webrtc::kVPlane);
+ for (int i = 0; i < input_image.height() / 2; ++i) {
+ memcpy(dst, src, width);
+ dst += stride;
+ src += width;
+ }
+
+ const webrtc::VideoFrameType type = frame_types->front();
+ DVLOG(3) << "RTCVideoEncoder::Encode() buffer.id()=" << index;
+ impl_message_loop_proxy_->PostTask(
+ FROM_HERE,
+ base::Bind(
+ &RTCVideoEncoder::Impl::Encode,
+ base::Unretained(impl_.get()),
+ media::BitstreamBuffer(index,
+ input_buffer->handle(),
+ input_frame_dimensions_.GetArea() * 3 / 2),
+ (type == webrtc::kKeyFrame)));
+ return WEBRTC_VIDEO_CODEC_OK;
+}
+
+int32_t RTCVideoEncoder::RegisterEncodeCompleteCallback(
+ webrtc::EncodedImageCallback* callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (!impl_)
+ return impl_status_;
+
+ encoded_image_callback_ = callback;
+ return WEBRTC_VIDEO_CODEC_OK;
+}
+
+int32_t RTCVideoEncoder::Release() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (!impl_)
+ return impl_status_;
+
+ impl_message_loop_proxy_->PostTask(
+ FROM_HERE,
+ base::Bind(&RTCVideoEncoder::Impl::Destroy, base::Passed(&impl_)));
+ return WEBRTC_VIDEO_CODEC_OK;
+}
+
+int32_t RTCVideoEncoder::SetChannelParameters(uint32_t packet_loss, int rtt) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ // Ignored.
+ return WEBRTC_VIDEO_CODEC_OK;
+}
+
+int32_t RTCVideoEncoder::SetRates(uint32_t new_bit_rate, uint32_t frame_rate) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (!impl_)
+ return impl_status_;
+
+ impl_message_loop_proxy_->PostTask(
+ FROM_HERE,
+ base::Bind(&RTCVideoEncoder::Impl::RequestEncodingParameterChange,
+ base::Unretained(impl_.get()),
+ new_bit_rate));
+ return WEBRTC_VIDEO_CODEC_OK;
+}
+
+void RTCVideoEncoder::RequireBitstreamBuffers(int input_count,
+ const gfx::Size& input_dimensions,
+ size_t output_size) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_EQ(input_buffers_.size(), 0U);
+ DCHECK_EQ(output_buffers_.size(), 0U);
+ input_frame_dimensions_ = input_dimensions;
+ output_buffer_size_ = output_size;
+
+ for (int i = 0; i < input_count + kInputBufferExtraCount; ++i) {
+ scoped_ptr<base::SharedMemory> shm(gpu_factories_->CreateSharedMemory(
+ input_frame_dimensions_.GetArea() * 3 / 2));
+ if (shm)
+ input_buffers_.push_back(shm.release());
+ }
+ for (size_t i = 0; i < input_buffers_.size(); ++i)
+ input_buffers_free_.push_back(i);
+
+ for (int i = 0; i < kOutputBufferCount; ++i) {
+ scoped_ptr<base::SharedMemory> shm(
+ gpu_factories_->CreateSharedMemory(output_buffer_size_));
+ if (shm)
+ output_buffers_.push_back(shm.release());
+ }
+
+ // Immediately provide all output buffers to the VEA.
+ for (size_t i = 0; i < output_buffers_.size(); ++i) {
+ impl_message_loop_proxy_->PostTask(
+ FROM_HERE,
+ base::Bind(&RTCVideoEncoder::Impl::UseOutputBitstreamBuffer,
+ base::Unretained(impl_.get()),
+ media::BitstreamBuffer(i,
+ output_buffers_[i]->handle(),
+ output_buffers_[i]->mapped_size())));
+ }
+}
+
+void RTCVideoEncoder::NotifyInputDone(int32 bitstream_buffer_id) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ if (!impl_)
+ return;
+ if (bitstream_buffer_id < 0 ||
+ bitstream_buffer_id >= (int32)input_buffers_.size())
+ return;
+ input_buffers_free_.push_back(bitstream_buffer_id);
+}
+
+void RTCVideoEncoder::BitstreamBufferReady(int32 bitstream_buffer_id,
+ size_t payload_size,
+ bool key_frame) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ if (!impl_)
+ return;
+ if (!encoded_image_callback_)
+ return;
+ if (bitstream_buffer_id < 0 ||
+ bitstream_buffer_id >= (int32)output_buffers_.size())
+ return;
+ base::SharedMemory* output_buffer = output_buffers_[bitstream_buffer_id];
+ if (payload_size > output_buffer->mapped_size())
+ return;
+
+ webrtc::EncodedImage image(
+ reinterpret_cast<uint8_t*>(output_buffer->memory()),
+ payload_size,
+ output_buffer->mapped_size());
+ image._encodedWidth = output_frame_dimensions_.width();
+ image._encodedHeight = output_frame_dimensions_.height();
+ image._frameType = (key_frame ? webrtc::kKeyFrame : webrtc::kDeltaFrame);
+ image._completeFrame = true;
+ DVLOG(3) << "RTCVideoEncoder::BitstreamBufferReady() buffer.id()="
+ << bitstream_buffer_id;
+ int32_t retval = encoded_image_callback_->Encoded(image, NULL, NULL);
+ if (retval < 0) {
+ NotifyError(retval);
+ return;
+ }
+
+ // The call through webrtc::EncodedImageCallback is synchronous, so we can
+ // immediately recycle the output buffer back to the VEA.
+ impl_message_loop_proxy_->PostTask(
+ FROM_HERE,
+ base::Bind(&RTCVideoEncoder::Impl::UseOutputBitstreamBuffer,
+ base::Unretained(impl_.get()),
+ media::BitstreamBuffer(bitstream_buffer_id,
+ output_buffer->handle(),
+ output_buffer->mapped_size())));
+}
+
+void RTCVideoEncoder::NotifyError(int32_t error) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ impl_status_ = error;
+ impl_message_loop_proxy_->PostTask(
+ FROM_HERE,
+ base::Bind(&RTCVideoEncoder::Impl::Destroy, base::Passed(&impl_)));
+}
+
+} // namespace content

Powered by Google App Engine
This is Rietveld 408576698