| Index: content/renderer/media/webrtc/webrtc_video_capturer_adapter.cc
|
| diff --git a/content/renderer/media/webrtc/webrtc_video_capturer_adapter.cc b/content/renderer/media/webrtc/webrtc_video_capturer_adapter.cc
|
| index 96b6fe32af607570b192f469204a6f3298979e64..c0d5c7c7b39556231179d31b4b0f24bba16e9591 100644
|
| --- a/content/renderer/media/webrtc/webrtc_video_capturer_adapter.cc
|
| +++ b/content/renderer/media/webrtc/webrtc_video_capturer_adapter.cc
|
| @@ -6,17 +6,26 @@
|
|
|
| #include "base/bind.h"
|
| #include "base/memory/aligned_memory.h"
|
| +#include "base/memory/ref_counted.h"
|
| +#include "base/synchronization/waitable_event.h"
|
| #include "base/trace_event/trace_event.h"
|
| +#include "content/common/gpu/client/context_provider_command_buffer.h"
|
| #include "content/renderer/media/webrtc/webrtc_video_frame_adapter.h"
|
| +#include "content/renderer/render_thread_impl.h"
|
| #include "media/base/timestamp_constants.h"
|
| #include "media/base/video_util.h"
|
| +#include "media/renderers/skcanvas_video_renderer.h"
|
| +#include "skia/ext/platform_canvas.h"
|
| +#include "third_party/libyuv/include/libyuv/convert.h"
|
| #include "third_party/libyuv/include/libyuv/convert_from.h"
|
| #include "third_party/libyuv/include/libyuv/scale.h"
|
| +#include "third_party/skia/include/core/SkSurface.h"
|
| #include "third_party/webrtc/common_video/include/video_frame_buffer.h"
|
| #include "third_party/webrtc/common_video/rotation.h"
|
| #include "third_party/webrtc/media/engine/webrtcvideoframe.h"
|
|
|
| namespace content {
|
| +
|
| namespace {
|
|
|
| // Empty method used for keeping a reference to the original media::VideoFrame.
|
| @@ -24,72 +33,127 @@ namespace {
|
| void ReleaseOriginalFrame(const scoped_refptr<media::VideoFrame>& frame) {
|
| }
|
|
|
| +// Helper class that signals a WaitableEvent when it goes out of scope.
|
| +class ScopedWaitableEvent {
|
| + public:
|
| + explicit ScopedWaitableEvent(base::WaitableEvent* event) : event_(event) {}
|
| + ~ScopedWaitableEvent() {
|
| + if (event_)
|
| + event_->Signal();
|
| + }
|
| +
|
| + private:
|
| + base::WaitableEvent* const event_;
|
| +};
|
| +
|
| } // anonymous namespace
|
|
|
| -WebRtcVideoCapturerAdapter::WebRtcVideoCapturerAdapter(bool is_screencast)
|
| - : is_screencast_(is_screencast),
|
| - running_(false) {
|
| - thread_checker_.DetachFromThread();
|
| -}
|
| +// Initializes the GL context environment and provides a method for copying
|
| +// texture backed frames into CPU mappable memory.
|
| +// The class is created and destroyed on the main render thread.
|
| +class WebRtcVideoCapturerAdapter::TextureFrameCopier
|
| + : public base::RefCounted<WebRtcVideoCapturerAdapter::TextureFrameCopier> {
|
| + public:
|
| + TextureFrameCopier()
|
| + : main_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()),
|
| + canvas_video_renderer_(new media::SkCanvasVideoRenderer) {
|
| + RenderThreadImpl* const main_thread = RenderThreadImpl::current();
|
| + if (main_thread)
|
| + provider_ = main_thread->SharedMainThreadContextProvider();
|
| + }
|
|
|
| -WebRtcVideoCapturerAdapter::~WebRtcVideoCapturerAdapter() {
|
| - DVLOG(3) << " WebRtcVideoCapturerAdapter::dtor";
|
| -}
|
| + // Synchronous call to copy a texture backed |frame| into a CPU mappable
|
| + // |new_frame|. If it is not called on the main render thread, this call posts
|
| + // a task on main thread by calling CopyTextureFrameOnMainThread() and blocks
|
| + // until it is completed.
|
| + void CopyTextureFrame(const scoped_refptr<media::VideoFrame>& frame,
|
| + scoped_refptr<media::VideoFrame>* new_frame) {
|
| + if (main_thread_task_runner_->BelongsToCurrentThread()) {
|
| + CopyTextureFrameOnMainThread(frame, new_frame, nullptr);
|
| + return;
|
| + }
|
|
|
| -cricket::CaptureState WebRtcVideoCapturerAdapter::Start(
|
| - const cricket::VideoFormat& capture_format) {
|
| - DCHECK(thread_checker_.CalledOnValidThread());
|
| - DCHECK(!running_);
|
| - DVLOG(3) << " WebRtcVideoCapturerAdapter::Start w = " << capture_format.width
|
| - << " h = " << capture_format.height;
|
| + base::WaitableEvent waiter(base::WaitableEvent::ResetPolicy::MANUAL,
|
| + base::WaitableEvent::InitialState::NOT_SIGNALED);
|
| + main_thread_task_runner_->PostTask(
|
| + FROM_HERE, base::Bind(&TextureFrameCopier::CopyTextureFrameOnMainThread,
|
| + this, frame, new_frame, &waiter));
|
| + waiter.Wait();
|
| + }
|
|
|
| - running_ = true;
|
| - return cricket::CS_RUNNING;
|
| -}
|
| + private:
|
| + friend class base::RefCounted<TextureFrameCopier>;
|
| + ~TextureFrameCopier() {
|
| + // |canvas_video_renderer_| should be deleted on the thread it was created.
|
| + if (!main_thread_task_runner_->BelongsToCurrentThread()) {
|
| + main_thread_task_runner_->DeleteSoon(FROM_HERE,
|
| + canvas_video_renderer_.release());
|
| + }
|
| + }
|
|
|
| -void WebRtcVideoCapturerAdapter::Stop() {
|
| - DCHECK(thread_checker_.CalledOnValidThread());
|
| - DVLOG(3) << " WebRtcVideoCapturerAdapter::Stop ";
|
| - DCHECK(running_);
|
| - running_ = false;
|
| - SetCaptureFormat(NULL);
|
| - SignalStateChange(this, cricket::CS_STOPPED);
|
| -}
|
| + void CopyTextureFrameOnMainThread(
|
| + const scoped_refptr<media::VideoFrame>& frame,
|
| + scoped_refptr<media::VideoFrame>* new_frame,
|
| + base::WaitableEvent* waiter) {
|
| + DCHECK(main_thread_task_runner_->BelongsToCurrentThread());
|
| + DCHECK(frame->format() == media::PIXEL_FORMAT_ARGB ||
|
| + frame->format() == media::PIXEL_FORMAT_XRGB ||
|
| + frame->format() == media::PIXEL_FORMAT_I420 ||
|
| + frame->format() == media::PIXEL_FORMAT_UYVY ||
|
| + frame->format() == media::PIXEL_FORMAT_NV12);
|
| + ScopedWaitableEvent event(waiter);
|
| + sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(
|
| + frame->visible_rect().width(), frame->visible_rect().height());
|
|
|
| -bool WebRtcVideoCapturerAdapter::IsRunning() {
|
| - DCHECK(thread_checker_.CalledOnValidThread());
|
| - return running_;
|
| -}
|
| + if (!surface || !provider_) {
|
| + // Return a black frame (yuv = {0, 0x80, 0x80}).
|
| + *new_frame = media::VideoFrame::CreateColorFrame(
|
| + frame->visible_rect().size(), 0u, 0x80, 0x80, frame->timestamp());
|
| + return;
|
| + }
|
|
|
| -bool WebRtcVideoCapturerAdapter::GetPreferredFourccs(
|
| - std::vector<uint32_t>* fourccs) {
|
| - DCHECK(thread_checker_.CalledOnValidThread());
|
| - DCHECK(!fourccs || fourccs->empty());
|
| - if (fourccs)
|
| - fourccs->push_back(cricket::FOURCC_I420);
|
| - return fourccs != NULL;
|
| -}
|
| + *new_frame = media::VideoFrame::CreateFrame(
|
| + media::PIXEL_FORMAT_I420, frame->coded_size(), frame->visible_rect(),
|
| + frame->natural_size(), frame->timestamp());
|
| + DCHECK(provider_->ContextGL());
|
| + canvas_video_renderer_->Copy(
|
| + frame.get(), surface->getCanvas(),
|
| + media::Context3D(provider_->ContextGL(), provider_->GrContext()));
|
|
|
| -bool WebRtcVideoCapturerAdapter::IsScreencast() const {
|
| - return is_screencast_;
|
| -}
|
| + SkPixmap pixmap;
|
| + const bool result = surface->getCanvas()->peekPixels(&pixmap);
|
| + DCHECK(result) << "Error trying to access SkSurface's pixels";
|
| + const uint32 source_pixel_format =
|
| + (kN32_SkColorType == kRGBA_8888_SkColorType) ? cricket::FOURCC_ABGR
|
| + : cricket::FOURCC_ARGB;
|
| + libyuv::ConvertToI420(
|
| + static_cast<const uint8*>(pixmap.addr(0, 0)), pixmap.getSafeSize64(),
|
| + (*new_frame)->visible_data(media::VideoFrame::kYPlane),
|
| + (*new_frame)->stride(media::VideoFrame::kYPlane),
|
| + (*new_frame)->visible_data(media::VideoFrame::kUPlane),
|
| + (*new_frame)->stride(media::VideoFrame::kUPlane),
|
| + (*new_frame)->visible_data(media::VideoFrame::kVPlane),
|
| + (*new_frame)->stride(media::VideoFrame::kVPlane), 0 /* crop_x */,
|
| + 0 /* crop_y */, pixmap.width(), pixmap.height(),
|
| + (*new_frame)->visible_rect().width(),
|
| + (*new_frame)->visible_rect().height(), libyuv::kRotate0,
|
| + source_pixel_format);
|
| + }
|
|
|
| -bool WebRtcVideoCapturerAdapter::GetBestCaptureFormat(
|
| - const cricket::VideoFormat& desired,
|
| - cricket::VideoFormat* best_format) {
|
| - DCHECK(thread_checker_.CalledOnValidThread());
|
| - DVLOG(3) << " GetBestCaptureFormat:: "
|
| - << " w = " << desired.width
|
| - << " h = " << desired.height;
|
| + const scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_;
|
| + scoped_refptr<ContextProviderCommandBuffer> provider_;
|
| + std::unique_ptr<media::SkCanvasVideoRenderer> canvas_video_renderer_;
|
| +};
|
|
|
| - // Capability enumeration is done in MediaStreamVideoSource. The adapter can
|
| - // just use what is provided.
|
| - // Use the desired format as the best format.
|
| - best_format->width = desired.width;
|
| - best_format->height = desired.height;
|
| - best_format->fourcc = cricket::FOURCC_I420;
|
| - best_format->interval = desired.interval;
|
| - return true;
|
| +WebRtcVideoCapturerAdapter::WebRtcVideoCapturerAdapter(bool is_screencast)
|
| + : texture_copier_(new WebRtcVideoCapturerAdapter::TextureFrameCopier()),
|
| + is_screencast_(is_screencast),
|
| + running_(false) {
|
| + thread_checker_.DetachFromThread();
|
| +}
|
| +
|
| +WebRtcVideoCapturerAdapter::~WebRtcVideoCapturerAdapter() {
|
| + DVLOG(3) << __func__;
|
| }
|
|
|
| void WebRtcVideoCapturerAdapter::OnFrameCaptured(
|
| @@ -99,9 +163,12 @@ void WebRtcVideoCapturerAdapter::OnFrameCaptured(
|
| if (!(input_frame->IsMappable() &&
|
| (input_frame->format() == media::PIXEL_FORMAT_I420 ||
|
| input_frame->format() == media::PIXEL_FORMAT_YV12 ||
|
| - input_frame->format() == media::PIXEL_FORMAT_YV12A))) {
|
| + input_frame->format() == media::PIXEL_FORMAT_YV12A)) &&
|
| + !input_frame->HasTextures()) {
|
| // Since connecting sources and sinks do not check the format, we need to
|
| // just ignore formats that we can not handle.
|
| + LOG(ERROR) << "We cannot send frame with storage type: "
|
| + << input_frame->AsHumanReadableString();
|
| NOTREACHED();
|
| return;
|
| }
|
| @@ -133,11 +200,11 @@ void WebRtcVideoCapturerAdapter::OnFrameCaptured(
|
|
|
| // Return |frame| directly if it is texture backed, because there is no
|
| // cropping support for texture yet. See http://crbug/503653.
|
| - // Return |frame| directly if it is GpuMemoryBuffer backed, as we want to
|
| - // keep the frame on native buffers.
|
| if (frame->HasTextures()) {
|
| OnFrame(cricket::WebRtcVideoFrame(
|
| - new rtc::RefCountedObject<WebRtcVideoFrameAdapter>(frame),
|
| + new rtc::RefCountedObject<WebRtcVideoFrameAdapter>(
|
| + frame, base::Bind(&TextureFrameCopier::CopyTextureFrame,
|
| + texture_copier_)),
|
| webrtc::kVideoRotation_0, translated_camera_time_us),
|
| orig_width, orig_height);
|
| return;
|
| @@ -164,7 +231,9 @@ void WebRtcVideoCapturerAdapter::OnFrameCaptured(
|
| // If no scaling is needed, return a wrapped version of |frame| directly.
|
| if (video_frame->natural_size() == video_frame->visible_rect().size()) {
|
| OnFrame(cricket::WebRtcVideoFrame(
|
| - new rtc::RefCountedObject<WebRtcVideoFrameAdapter>(video_frame),
|
| + new rtc::RefCountedObject<WebRtcVideoFrameAdapter>(
|
| + video_frame,
|
| + WebRtcVideoFrameAdapter::CopyTextureFrameCallback()),
|
| webrtc::kVideoRotation_0, translated_camera_time_us),
|
| orig_width, orig_height);
|
| return;
|
| @@ -192,9 +261,65 @@ void WebRtcVideoCapturerAdapter::OnFrameCaptured(
|
| adapted_width, adapted_height, libyuv::kFilterBilinear);
|
|
|
| OnFrame(cricket::WebRtcVideoFrame(
|
| - new rtc::RefCountedObject<WebRtcVideoFrameAdapter>(scaled_frame),
|
| + new rtc::RefCountedObject<WebRtcVideoFrameAdapter>(
|
| + scaled_frame,
|
| + WebRtcVideoFrameAdapter::CopyTextureFrameCallback()),
|
| webrtc::kVideoRotation_0, translated_camera_time_us),
|
| orig_width, orig_height);
|
| }
|
|
|
| +cricket::CaptureState WebRtcVideoCapturerAdapter::Start(
|
| + const cricket::VideoFormat& capture_format) {
|
| + DCHECK(thread_checker_.CalledOnValidThread());
|
| + DCHECK(!running_);
|
| + DVLOG(3) << __func__ << " capture format: " << capture_format.ToString();
|
| +
|
| + running_ = true;
|
| + return cricket::CS_RUNNING;
|
| +}
|
| +
|
| +void WebRtcVideoCapturerAdapter::Stop() {
|
| + DCHECK(thread_checker_.CalledOnValidThread());
|
| + DVLOG(3) << __func__;
|
| + DCHECK(running_);
|
| + running_ = false;
|
| + SetCaptureFormat(NULL);
|
| + SignalStateChange(this, cricket::CS_STOPPED);
|
| +}
|
| +
|
| +bool WebRtcVideoCapturerAdapter::IsRunning() {
|
| + DCHECK(thread_checker_.CalledOnValidThread());
|
| + return running_;
|
| +}
|
| +
|
| +bool WebRtcVideoCapturerAdapter::GetPreferredFourccs(
|
| + std::vector<uint32_t>* fourccs) {
|
| + DCHECK(thread_checker_.CalledOnValidThread());
|
| + if (!fourccs)
|
| + return false;
|
| + DCHECK(fourccs->empty());
|
| + fourccs->push_back(cricket::FOURCC_I420);
|
| + return true;
|
| +}
|
| +
|
| +bool WebRtcVideoCapturerAdapter::IsScreencast() const {
|
| + return is_screencast_;
|
| +}
|
| +
|
| +bool WebRtcVideoCapturerAdapter::GetBestCaptureFormat(
|
| + const cricket::VideoFormat& desired,
|
| + cricket::VideoFormat* best_format) {
|
| + DCHECK(thread_checker_.CalledOnValidThread());
|
| + DVLOG(3) << __func__ << " desired: " << desired.ToString();
|
| +
|
| + // Capability enumeration is done in MediaStreamVideoSource. The adapter can
|
| + // just use what is provided.
|
| + // Use the desired format as the best format.
|
| + best_format->width = desired.width;
|
| + best_format->height = desired.height;
|
| + best_format->fourcc = cricket::FOURCC_I420;
|
| + best_format->interval = desired.interval;
|
| + return true;
|
| +}
|
| +
|
| } // namespace content
|
|
|