| 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 "cc/paint/paint_canvas.h" | |
| 8 #include "cc/paint/paint_surface.h" | |
| 9 #include "media/base/bind_to_current_loop.h" | |
| 10 #include "media/base/video_frame.h" | |
| 11 #include "media/base/video_util.h" | |
| 12 #include "skia/ext/platform_canvas.h" | |
| 13 #include "third_party/WebKit/public/platform/WebCallbacks.h" | |
| 14 #include "third_party/WebKit/public/platform/WebMediaStreamSource.h" | |
| 15 #include "third_party/WebKit/public/platform/WebMediaStreamTrack.h" | |
| 16 #include "third_party/libyuv/include/libyuv.h" | |
| 17 #include "third_party/skia/include/core/SkImage.h" | |
| 18 | |
| 19 namespace content { | |
| 20 | |
| 21 using blink::WebImageCaptureGrabFrameCallbacks; | |
| 22 | |
| 23 namespace { | |
| 24 | |
| 25 void OnError(std::unique_ptr<WebImageCaptureGrabFrameCallbacks> callbacks) { | |
| 26 callbacks->onError(); | |
| 27 } | |
| 28 | |
| 29 } // anonymous namespace | |
| 30 | |
| 31 // Ref-counted class to receive a single VideoFrame on IO thread, convert it and | |
| 32 // send it to |main_task_runner_|, where this class is created and destroyed. | |
| 33 class ImageCaptureFrameGrabber::SingleShotFrameHandler | |
| 34 : public base::RefCountedThreadSafe<SingleShotFrameHandler> { | |
| 35 public: | |
| 36 SingleShotFrameHandler() : first_frame_received_(false) {} | |
| 37 | |
| 38 // Receives a |frame| and converts its pixels into a SkImage via an internal | |
| 39 // PaintSurface and SkPixmap. Alpha channel, if any, is copied. | |
| 40 void OnVideoFrameOnIOThread(SkImageDeliverCB callback, | |
| 41 const scoped_refptr<media::VideoFrame>& frame, | |
| 42 base::TimeTicks current_time); | |
| 43 | |
| 44 private: | |
| 45 friend class base::RefCountedThreadSafe<SingleShotFrameHandler>; | |
| 46 virtual ~SingleShotFrameHandler() {} | |
| 47 | |
| 48 // Flag to indicate that the first frames has been processed, and subsequent | |
| 49 // ones can be safely discarded. | |
| 50 bool first_frame_received_; | |
| 51 | |
| 52 DISALLOW_COPY_AND_ASSIGN(SingleShotFrameHandler); | |
| 53 }; | |
| 54 | |
| 55 void ImageCaptureFrameGrabber::SingleShotFrameHandler::OnVideoFrameOnIOThread( | |
| 56 SkImageDeliverCB callback, | |
| 57 const scoped_refptr<media::VideoFrame>& frame, | |
| 58 base::TimeTicks /* current_time */) { | |
| 59 DCHECK(frame->format() == media::PIXEL_FORMAT_YV12 || | |
| 60 frame->format() == media::PIXEL_FORMAT_I420 || | |
| 61 frame->format() == media::PIXEL_FORMAT_YV12A); | |
| 62 | |
| 63 if (first_frame_received_) | |
| 64 return; | |
| 65 first_frame_received_ = true; | |
| 66 | |
| 67 const SkAlphaType alpha = media::IsOpaque(frame->format()) | |
| 68 ? kOpaque_SkAlphaType | |
| 69 : kPremul_SkAlphaType; | |
| 70 const SkImageInfo info = SkImageInfo::MakeN32( | |
| 71 frame->visible_rect().width(), frame->visible_rect().height(), alpha); | |
| 72 | |
| 73 sk_sp<SkSurface> surface = SkSurface::MakeRaster(info); | |
| 74 DCHECK(surface); | |
| 75 | |
| 76 SkPixmap pixmap; | |
| 77 if (!skia::GetWritablePixels(surface->getCanvas(), &pixmap)) { | |
| 78 DLOG(ERROR) << "Error trying to map SkSurface's pixels"; | |
| 79 callback.Run(sk_sp<SkImage>()); | |
| 80 return; | |
| 81 } | |
| 82 | |
| 83 const uint32 destination_pixel_format = | |
| 84 (kN32_SkColorType == kRGBA_8888_SkColorType) ? libyuv::FOURCC_ABGR | |
| 85 : libyuv::FOURCC_ARGB; | |
| 86 | |
| 87 libyuv::ConvertFromI420(frame->visible_data(media::VideoFrame::kYPlane), | |
| 88 frame->stride(media::VideoFrame::kYPlane), | |
| 89 frame->visible_data(media::VideoFrame::kUPlane), | |
| 90 frame->stride(media::VideoFrame::kUPlane), | |
| 91 frame->visible_data(media::VideoFrame::kVPlane), | |
| 92 frame->stride(media::VideoFrame::kVPlane), | |
| 93 static_cast<uint8*>(pixmap.writable_addr()), | |
| 94 pixmap.width() * 4, pixmap.width(), pixmap.height(), | |
| 95 destination_pixel_format); | |
| 96 | |
| 97 if (frame->format() == media::PIXEL_FORMAT_YV12A) { | |
| 98 DCHECK(!info.isOpaque()); | |
| 99 // This function copies any plane into the alpha channel of an ARGB image. | |
| 100 libyuv::ARGBCopyYToAlpha(frame->visible_data(media::VideoFrame::kAPlane), | |
| 101 frame->stride(media::VideoFrame::kAPlane), | |
| 102 static_cast<uint8*>(pixmap.writable_addr()), | |
| 103 pixmap.width() * 4, pixmap.width(), | |
| 104 pixmap.height()); | |
| 105 } | |
| 106 | |
| 107 callback.Run(surface->makeImageSnapshot()); | |
| 108 } | |
| 109 | |
| 110 ImageCaptureFrameGrabber::ImageCaptureFrameGrabber() | |
| 111 : frame_grab_in_progress_(false), weak_factory_(this) {} | |
| 112 | |
| 113 ImageCaptureFrameGrabber::~ImageCaptureFrameGrabber() { | |
| 114 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 115 } | |
| 116 | |
| 117 void ImageCaptureFrameGrabber::grabFrame( | |
| 118 blink::WebMediaStreamTrack* track, | |
| 119 WebImageCaptureGrabFrameCallbacks* callbacks) { | |
| 120 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 121 DCHECK(!!callbacks); | |
| 122 | |
| 123 DCHECK(track && !track->isNull() && track->getTrackData()); | |
| 124 DCHECK_EQ(blink::WebMediaStreamSource::TypeVideo, track->source().getType()); | |
| 125 | |
| 126 if (frame_grab_in_progress_) { | |
| 127 // Reject grabFrame()s too close back to back. | |
| 128 callbacks->onError(); | |
| 129 return; | |
| 130 } | |
| 131 | |
| 132 ScopedWebCallbacks<WebImageCaptureGrabFrameCallbacks> scoped_callbacks = | |
| 133 make_scoped_web_callbacks(callbacks, base::Bind(&OnError)); | |
| 134 | |
| 135 // A SingleShotFrameHandler is bound and given to the Track to guarantee that | |
| 136 // only one VideoFrame is converted and delivered to OnSkImage(), otherwise | |
| 137 // SKImages might be sent to resolved |callbacks| while DisconnectFromTrack() | |
| 138 // is being processed, which might be further held up if UI is busy, see | |
| 139 // https://crbug.com/623042. | |
| 140 frame_grab_in_progress_ = true; | |
| 141 MediaStreamVideoSink::ConnectToTrack( | |
| 142 *track, base::Bind(&SingleShotFrameHandler::OnVideoFrameOnIOThread, | |
| 143 make_scoped_refptr(new SingleShotFrameHandler), | |
| 144 media::BindToCurrentLoop( | |
| 145 base::Bind(&ImageCaptureFrameGrabber::OnSkImage, | |
| 146 weak_factory_.GetWeakPtr(), | |
| 147 base::Passed(&scoped_callbacks)))), | |
| 148 false); | |
| 149 } | |
| 150 | |
| 151 void ImageCaptureFrameGrabber::OnSkImage( | |
| 152 ScopedWebCallbacks<blink::WebImageCaptureGrabFrameCallbacks> callbacks, | |
| 153 sk_sp<SkImage> image) { | |
| 154 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 155 | |
| 156 MediaStreamVideoSink::DisconnectFromTrack(); | |
| 157 frame_grab_in_progress_ = false; | |
| 158 if (image) | |
| 159 callbacks.PassCallbacks()->onSuccess(image); | |
| 160 else | |
| 161 callbacks.PassCallbacks()->onError(); | |
| 162 } | |
| 163 | |
| 164 } // namespace content | |
| OLD | NEW |