| Index: content/renderer/media/image_capture_frame_grabber.cc
|
| diff --git a/content/renderer/media/image_capture_frame_grabber.cc b/content/renderer/media/image_capture_frame_grabber.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..af3a5f19efe7e007484bec65f77904137b046e8d
|
| --- /dev/null
|
| +++ b/content/renderer/media/image_capture_frame_grabber.cc
|
| @@ -0,0 +1,121 @@
|
| +// Copyright 2016 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/image_capture_frame_grabber.h"
|
| +
|
| +#include "media/base/bind_to_current_loop.h"
|
| +#include "media/base/video_frame.h"
|
| +#include "media/base/video_util.h"
|
| +#include "skia/ext/platform_canvas.h"
|
| +#include "third_party/WebKit/public/platform/WebCallbacks.h"
|
| +#include "third_party/WebKit/public/platform/WebMediaStreamSource.h"
|
| +#include "third_party/WebKit/public/platform/WebMediaStreamTrack.h"
|
| +#include "third_party/libyuv/include/libyuv.h"
|
| +#include "third_party/skia/include/core/SkImage.h"
|
| +#include "third_party/skia/include/core/SkSurface.h"
|
| +
|
| +namespace content {
|
| +
|
| +using blink::WebImageCaptureGrabFrameCallbacks;
|
| +
|
| +namespace {
|
| +
|
| +void OnError(std::unique_ptr<WebImageCaptureGrabFrameCallbacks> callbacks) {
|
| + callbacks->onError();
|
| +}
|
| +
|
| +// This internal method receives a |frame| and converts its pixels into a
|
| +// SkImage via an internal SkSurface and SkPixmap. Alpha channel, if any, is
|
| +// copied.
|
| +void OnVideoFrame(const ImageCaptureFrameGrabber::SkImageDeliverCB& callback,
|
| + const scoped_refptr<media::VideoFrame>& frame,
|
| + base::TimeTicks /* current_time */) {
|
| + DCHECK(frame->format() == media::PIXEL_FORMAT_YV12 ||
|
| + frame->format() == media::PIXEL_FORMAT_I420 ||
|
| + frame->format() == media::PIXEL_FORMAT_YV12A);
|
| +
|
| + const SkAlphaType alpha = media::IsOpaque(frame->format())
|
| + ? kOpaque_SkAlphaType
|
| + : kPremul_SkAlphaType;
|
| + const SkImageInfo info = SkImageInfo::MakeN32(
|
| + frame->visible_rect().width(), frame->visible_rect().height(), alpha);
|
| +
|
| + sk_sp<SkSurface> surface = SkSurface::MakeRaster(info);
|
| + DCHECK(surface);
|
| +
|
| + SkPixmap pixmap;
|
| + if (!skia::GetWritablePixels(surface->getCanvas(), &pixmap)) {
|
| + DLOG(ERROR) << "Error trying to map SkSurface's pixels";
|
| + callback.Run(sk_sp<SkImage>());
|
| + return;
|
| + }
|
| +
|
| + libyuv::I420ToARGB(frame->visible_data(media::VideoFrame::kYPlane),
|
| + frame->stride(media::VideoFrame::kYPlane),
|
| + frame->visible_data(media::VideoFrame::kUPlane),
|
| + frame->stride(media::VideoFrame::kUPlane),
|
| + frame->visible_data(media::VideoFrame::kVPlane),
|
| + frame->stride(media::VideoFrame::kVPlane),
|
| + static_cast<uint8*>(pixmap.writable_addr()),
|
| + pixmap.width() * 4, pixmap.width(), pixmap.height());
|
| +
|
| + if (frame->format() == media::PIXEL_FORMAT_YV12A) {
|
| + DCHECK(!info.isOpaque());
|
| + // This function copies any plane into the alpha channel of an ARGB image.
|
| + libyuv::ARGBCopyYToAlpha(frame->visible_data(media::VideoFrame::kAPlane),
|
| + frame->stride(media::VideoFrame::kAPlane),
|
| + static_cast<uint8*>(pixmap.writable_addr()),
|
| + pixmap.width() * 4, pixmap.width(),
|
| + pixmap.height());
|
| + }
|
| +
|
| + callback.Run(surface->makeImageSnapshot());
|
| +}
|
| +
|
| +} // anonymous namespace
|
| +
|
| +ImageCaptureFrameGrabber::ImageCaptureFrameGrabber() : weak_factory_(this) {}
|
| +
|
| +ImageCaptureFrameGrabber::~ImageCaptureFrameGrabber() {
|
| + DCHECK(thread_checker_.CalledOnValidThread());
|
| +}
|
| +
|
| +void ImageCaptureFrameGrabber::grabFrame(
|
| + blink::WebMediaStreamTrack* track,
|
| + WebImageCaptureGrabFrameCallbacks* callbacks) {
|
| + DVLOG(1) << __FUNCTION__;
|
| + DCHECK(thread_checker_.CalledOnValidThread());
|
| + DCHECK(!!callbacks);
|
| +
|
| + DCHECK(track && !track->isNull() && track->getExtraData());
|
| + DCHECK_EQ(blink::WebMediaStreamSource::TypeVideo, track->source().getType());
|
| +
|
| + ScopedWebCallbacks<WebImageCaptureGrabFrameCallbacks> scoped_callbacks =
|
| + make_scoped_web_callbacks(callbacks, base::Bind(&OnError));
|
| +
|
| + // ConnectToTrack() must happen on render's Main Thread, whereas VideoFrames
|
| + // are delivered on a background thread though, so we Bind the callback to our
|
| + // current thread.
|
| + MediaStreamVideoSink::ConnectToTrack(
|
| + *track,
|
| + base::Bind(&OnVideoFrame, media::BindToCurrentLoop(base::Bind(
|
| + &ImageCaptureFrameGrabber::OnSkImage,
|
| + weak_factory_.GetWeakPtr(),
|
| + base::Passed(&scoped_callbacks)))));
|
| +}
|
| +
|
| +void ImageCaptureFrameGrabber::OnSkImage(
|
| + ScopedWebCallbacks<WebImageCaptureGrabFrameCallbacks> callbacks,
|
| + sk_sp<SkImage> image) {
|
| + DVLOG(1) << __FUNCTION__;
|
| + DCHECK(thread_checker_.CalledOnValidThread());
|
| +
|
| + MediaStreamVideoSink::DisconnectFromTrack();
|
| + if (image)
|
| + callbacks.PassCallbacks()->onSuccess(image);
|
| + else
|
| + callbacks.PassCallbacks()->onError();
|
| +}
|
| +
|
| +} // namespace content
|
|
|