OLD | NEW |
(Empty) | |
| 1 // Copyright 2016 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. |
| 4 |
| 5 #include "content/renderer/media/image_capture_frame_grabber.h" |
| 6 |
| 7 #include "media/base/bind_to_current_loop.h" |
| 8 #include "media/base/video_frame.h" |
| 9 #include "media/base/video_util.h" |
| 10 #include "skia/ext/platform_canvas.h" |
| 11 #include "third_party/WebKit/public/platform/WebCallbacks.h" |
| 12 #include "third_party/WebKit/public/platform/WebMediaStreamSource.h" |
| 13 #include "third_party/WebKit/public/platform/WebMediaStreamTrack.h" |
| 14 #include "third_party/libyuv/include/libyuv.h" |
| 15 #include "third_party/skia/include/core/SkImage.h" |
| 16 #include "third_party/skia/include/core/SkSurface.h" |
| 17 |
| 18 namespace content { |
| 19 |
| 20 using blink::WebImageCaptureGrabFrameCallbacks; |
| 21 |
| 22 namespace { |
| 23 |
| 24 void OnError(std::unique_ptr<WebImageCaptureGrabFrameCallbacks> callbacks) { |
| 25 callbacks->onError(); |
| 26 } |
| 27 |
| 28 // This internal method receives a |frame| and converts its pixels into a |
| 29 // SkImage via an internal SkSurface and SkPixmap. Alpha channel, if any, is |
| 30 // copied. |
| 31 void OnVideoFrame(const ImageCaptureFrameGrabber::SkImageDeliverCB& callback, |
| 32 const scoped_refptr<media::VideoFrame>& frame, |
| 33 base::TimeTicks /* current_time */) { |
| 34 DCHECK(frame->format() == media::PIXEL_FORMAT_YV12 || |
| 35 frame->format() == media::PIXEL_FORMAT_I420 || |
| 36 frame->format() == media::PIXEL_FORMAT_YV12A); |
| 37 |
| 38 const SkAlphaType alpha = media::IsOpaque(frame->format()) |
| 39 ? kOpaque_SkAlphaType |
| 40 : kPremul_SkAlphaType; |
| 41 const SkImageInfo info = SkImageInfo::MakeN32( |
| 42 frame->visible_rect().width(), frame->visible_rect().height(), alpha); |
| 43 |
| 44 sk_sp<SkSurface> surface = SkSurface::MakeRaster(info); |
| 45 DCHECK(surface); |
| 46 |
| 47 SkPixmap pixmap; |
| 48 if (!skia::GetWritablePixels(surface->getCanvas(), &pixmap)) { |
| 49 DLOG(ERROR) << "Error trying to map SkSurface's pixels"; |
| 50 callback.Run(sk_sp<SkImage>()); |
| 51 return; |
| 52 } |
| 53 |
| 54 libyuv::I420ToARGB(frame->visible_data(media::VideoFrame::kYPlane), |
| 55 frame->stride(media::VideoFrame::kYPlane), |
| 56 frame->visible_data(media::VideoFrame::kUPlane), |
| 57 frame->stride(media::VideoFrame::kUPlane), |
| 58 frame->visible_data(media::VideoFrame::kVPlane), |
| 59 frame->stride(media::VideoFrame::kVPlane), |
| 60 static_cast<uint8*>(pixmap.writable_addr()), |
| 61 pixmap.width() * 4, pixmap.width(), pixmap.height()); |
| 62 |
| 63 if (frame->format() == media::PIXEL_FORMAT_YV12A) { |
| 64 DCHECK(!info.isOpaque()); |
| 65 // This function copies any plane into the alpha channel of an ARGB image. |
| 66 libyuv::ARGBCopyYToAlpha(frame->visible_data(media::VideoFrame::kAPlane), |
| 67 frame->stride(media::VideoFrame::kAPlane), |
| 68 static_cast<uint8*>(pixmap.writable_addr()), |
| 69 pixmap.width() * 4, pixmap.width(), |
| 70 pixmap.height()); |
| 71 } |
| 72 |
| 73 callback.Run(surface->makeImageSnapshot()); |
| 74 } |
| 75 |
| 76 } // anonymous namespace |
| 77 |
| 78 ImageCaptureFrameGrabber::ImageCaptureFrameGrabber() : weak_factory_(this) {} |
| 79 |
| 80 ImageCaptureFrameGrabber::~ImageCaptureFrameGrabber() { |
| 81 DCHECK(thread_checker_.CalledOnValidThread()); |
| 82 } |
| 83 |
| 84 void ImageCaptureFrameGrabber::grabFrame( |
| 85 blink::WebMediaStreamTrack* track, |
| 86 WebImageCaptureGrabFrameCallbacks* callbacks) { |
| 87 DVLOG(1) << __FUNCTION__; |
| 88 DCHECK(thread_checker_.CalledOnValidThread()); |
| 89 DCHECK(!!callbacks); |
| 90 |
| 91 DCHECK(track && !track->isNull() && track->getExtraData()); |
| 92 DCHECK_EQ(blink::WebMediaStreamSource::TypeVideo, track->source().getType()); |
| 93 |
| 94 ScopedWebCallbacks<WebImageCaptureGrabFrameCallbacks> scoped_callbacks = |
| 95 make_scoped_web_callbacks(callbacks, base::Bind(&OnError)); |
| 96 |
| 97 // ConnectToTrack() must happen on render's Main Thread, whereas VideoFrames |
| 98 // are delivered on a background thread though, so we Bind the callback to our |
| 99 // current thread. |
| 100 MediaStreamVideoSink::ConnectToTrack( |
| 101 *track, |
| 102 base::Bind(&OnVideoFrame, media::BindToCurrentLoop(base::Bind( |
| 103 &ImageCaptureFrameGrabber::OnSkImage, |
| 104 weak_factory_.GetWeakPtr(), |
| 105 base::Passed(&scoped_callbacks))))); |
| 106 } |
| 107 |
| 108 void ImageCaptureFrameGrabber::OnSkImage( |
| 109 ScopedWebCallbacks<WebImageCaptureGrabFrameCallbacks> callbacks, |
| 110 sk_sp<SkImage> image) { |
| 111 DVLOG(1) << __FUNCTION__; |
| 112 DCHECK(thread_checker_.CalledOnValidThread()); |
| 113 |
| 114 MediaStreamVideoSink::DisconnectFromTrack(); |
| 115 if (image) |
| 116 callbacks.PassCallbacks()->onSuccess(image); |
| 117 else |
| 118 callbacks.PassCallbacks()->onError(); |
| 119 } |
| 120 |
| 121 } // namespace content |
OLD | NEW |