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 |