OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2012 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/browser/renderer_host/media/screen_capturer.h" | |
6 | |
7 #include "remoting/capturer/capture_data.h" | |
8 #include "remoting/capturer/mouse_cursor_shape.h" | |
9 #include "third_party/skia/include/core/SkCanvas.h" | |
10 #include "third_party/skia/include/core/SkDevice.h" | |
11 | |
12 namespace content { | |
13 | |
14 namespace { | |
15 const int kBytesPerPixel = 4; | |
16 } // namespace | |
17 | |
18 ScreenCapturer::ScreenCapturer() | |
19 : capture_thread_("Screen Capture Thread"), | |
20 event_handler_(NULL), | |
21 capture_pending_(false), | |
22 waiting_frame_size_(false), | |
23 started_(false) { | |
24 name_.device_name = "Screen"; | |
25 } | |
26 | |
27 ScreenCapturer::~ScreenCapturer() { | |
Wez
2013/01/10 00:58:14
If the caller hasn't called DeAllocate then we sti
Sergey Ulanov
2013/01/11 23:46:20
Added DeAllocate.
| |
28 } | |
29 | |
30 void ScreenCapturer::Allocate(int width, int height, | |
31 int frame_rate, | |
32 EventHandler* event_handler) { | |
Wez
2013/01/10 00:58:14
nit: Consider [D]CHECKing the parameters. e.g. fra
Sergey Ulanov
2013/01/11 23:46:20
Done.
| |
33 capture_thread_.Start(); | |
Wez
2013/01/10 00:58:14
Is there no other thread or thread pool we can all
Sergey Ulanov
2013/01/11 23:46:20
Changed this code to use blocking IO pool.
| |
34 | |
35 // base::Unretained is safe because we control lifetime of the thread. | |
36 capture_thread_.message_loop()->PostTask( | |
37 FROM_HERE, base::Bind(&ScreenCapturer::DoAllocate, base::Unretained(this), | |
38 frame_rate, event_handler)); | |
39 } | |
40 | |
41 void ScreenCapturer::Start() { | |
wjia(left Chromium)
2013/01/07 22:15:26
check capture_thread_.IsRunning() here too?
Sergey Ulanov
2013/01/09 20:35:30
Done.
| |
42 capture_thread_.message_loop()->PostTask( | |
43 FROM_HERE, base::Bind(&ScreenCapturer::DoStart, base::Unretained(this))); | |
44 } | |
45 | |
46 void ScreenCapturer::Stop() { | |
47 capture_thread_.message_loop()->PostTask( | |
48 FROM_HERE, base::Bind(&ScreenCapturer::DoStop, base::Unretained(this))); | |
49 } | |
50 | |
51 void ScreenCapturer::DeAllocate() { | |
52 if (capture_thread_.IsRunning()) { | |
53 capture_thread_.message_loop()->PostTask( | |
54 FROM_HERE, base::Bind(&ScreenCapturer::DoDeAllocate, | |
55 base::Unretained(this))); | |
56 capture_thread_.Stop(); | |
Wez
2013/01/10 00:58:14
Does it matter that this might block e.g. for half
Sergey Ulanov
2013/01/11 23:46:20
Removed capture_thread_. thread pool is used inste
| |
57 } | |
58 } | |
59 | |
60 const media::VideoCaptureDevice::Name& ScreenCapturer::device_name() { | |
61 return name_; | |
62 } | |
63 | |
64 void ScreenCapturer::OnCaptureCompleted( | |
65 scoped_refptr<remoting::CaptureData> capture_data) { | |
66 DCHECK(capture_thread_.message_loop_proxy()->BelongsToCurrentThread()); | |
67 DCHECK(capture_pending_); | |
68 capture_pending_ = false; | |
69 | |
70 if (waiting_frame_size_) { | |
71 frame_size_ = capture_data->size(); | |
72 waiting_frame_size_ = false; | |
73 | |
74 // Invoke OnFrameInfo(). | |
Wez
2013/01/10 00:58:14
nit: Please make this more descriptive, e.g. "Info
Sergey Ulanov
2013/01/11 23:46:20
Done.
| |
75 media::VideoCaptureCapability caps; | |
76 caps.width = frame_size_.width(); | |
77 caps.height = frame_size_.height(); | |
78 caps.frame_rate = frame_rate_; | |
79 caps.color = media::VideoCaptureCapability::kARGB; | |
80 caps.expected_capture_delay = | |
81 base::Time::kMillisecondsPerSecond / frame_rate_; | |
Wez
2013/01/10 00:58:14
Does it matter that the host platform might not be
Sergey Ulanov
2013/01/11 23:46:20
I don't think so. In either case we have no way to
| |
82 caps.interlaced = false; | |
83 event_handler_->OnFrameInfo(caps); | |
Wez
2013/01/10 00:58:14
This calls the EventHandler on the capture thread
Sergey Ulanov
2013/01/11 23:46:20
That's the same behavior that we have in device vi
| |
84 } | |
85 | |
86 if (!started_) | |
Wez
2013/01/10 00:58:14
This is happening on the same thread that DoStop()
Sergey Ulanov
2013/01/11 23:46:20
OnFrameInfo() needs to be called in response to Al
| |
87 return; | |
Wez
2013/01/10 00:58:14
Check this before the |waiting_frame_size_| check
Sergey Ulanov
2013/01/11 23:46:20
See my previous comment. OnFrameInfo() doesn't dep
| |
88 size_t buffer_size = | |
89 frame_size_.width() * frame_size_.height() * | |
90 remoting::CaptureData::kBytesPerPixel; | |
91 | |
92 if (capture_data->size() == frame_size_) { | |
93 // Invalidate resized frame buffer if we previously allocated it. | |
Wez
2013/01/10 00:58:14
Reword this comment and place it before the if, e.
Sergey Ulanov
2013/01/11 23:46:20
Done.
| |
94 resized_bitmap_.reset(); | |
95 event_handler_->OnIncomingCapturedFrame( | |
96 capture_data->data(), buffer_size, base::Time::Now()); | |
97 return; | |
98 } | |
99 | |
100 // In case screen size has changed we need to resize the image. Resized image | |
Wez
2013/01/10 00:58:14
Do we really have no way to inform the caller of t
Sergey Ulanov
2013/01/11 23:46:20
No. The interface was designed for webcams and the
| |
101 // is stored to |resized_bitmap_|. Only regions of the screen that are | |
102 // changing are copied. | |
103 | |
104 bool redraw_all = false; | |
Wez
2013/01/10 00:58:14
Why not re-set the CaptureData's rects to contain
Sergey Ulanov
2013/01/11 23:46:20
Done.
| |
105 | |
106 if (resized_bitmap_.width() != frame_size_.width() || | |
107 resized_bitmap_.height() != frame_size_.height()) { | |
108 resized_bitmap_.setConfig(SkBitmap::kARGB_8888_Config, | |
109 frame_size_.width(), frame_size_.height()); | |
110 resized_bitmap_.setIsOpaque(true); | |
111 resized_bitmap_.allocPixels(); | |
112 redraw_all = true; | |
113 } | |
114 | |
115 float scale_x = static_cast<float>(frame_size_.width()) / | |
116 capture_data->size().width(); | |
Wez
2013/01/10 00:58:14
Our capturers won't produce 0x0 frames, but if the
Sergey Ulanov
2013/01/11 23:46:20
Added DCHECK on top of this method.
| |
117 float scale_y = static_cast<float>(frame_size_.height()) / | |
118 capture_data->size().height(); | |
119 float scale; | |
120 float x, y; | |
121 // Center the image in case aspect ratio is different. | |
122 if (scale_x > scale_y) { | |
123 scale = scale_y; | |
124 x = (scale_x - scale_y) / scale * frame_size_.width() / 2.0; | |
125 y = 0; | |
Wez
2013/01/10 00:58:14
nit: 0.0f
Sergey Ulanov
2013/01/11 23:46:20
Done.
| |
126 } else { | |
127 scale = scale_x; | |
128 x = 0; | |
129 y = (scale_y - scale_x) / scale * frame_size_.height() / 2.0; | |
130 } | |
131 | |
132 SkDevice device(resized_bitmap_); | |
133 SkCanvas canvas(&device); | |
134 canvas.scale(scale, scale); | |
Wez
2013/01/10 00:58:14
Add a comment before this block to summarize the p
Sergey Ulanov
2013/01/11 23:46:20
Done.
| |
135 | |
136 if (redraw_all) { | |
137 SkBitmap source_bitmap; | |
138 source_bitmap.setConfig(SkBitmap::kARGB_8888_Config, | |
139 capture_data->size().width(), | |
140 capture_data->size().height()); | |
141 source_bitmap.setIsOpaque(true); | |
142 source_bitmap.setPixels(capture_data->data()); | |
143 canvas.drawBitmap(source_bitmap, x / scale, y / scale, NULL); | |
144 } else { | |
145 int source_stride = capture_data->stride(); | |
146 for (SkRegion::Iterator i(capture_data->dirty_region()); | |
147 !i.done(); i.next()) { | |
148 SkBitmap source_bitmap; | |
149 source_bitmap.setConfig(SkBitmap::kARGB_8888_Config, | |
150 i.rect().width(), i.rect().height(), | |
151 source_stride); | |
152 source_bitmap.setIsOpaque(true); | |
153 source_bitmap.setPixels( | |
154 capture_data->data() + i.rect().top() * source_stride + | |
155 i.rect().left() * remoting::CaptureData::kBytesPerPixel); | |
156 canvas.drawBitmap(source_bitmap, i.rect().left() + x / scale, | |
157 i.rect().top() + y / scale, NULL); | |
Wez
2013/01/10 00:58:14
Does this do the right thing wrt sub-pixel destina
Sergey Ulanov
2013/01/11 23:46:20
Yes, as far as I know, but it uses simple nearest
| |
158 } | |
159 } | |
160 | |
161 event_handler_->OnIncomingCapturedFrame( | |
162 reinterpret_cast<uint8*>(resized_bitmap_.getPixels()), buffer_size, | |
163 base::Time::Now()); | |
164 } | |
165 | |
166 void ScreenCapturer::OnCursorShapeChanged( | |
167 scoped_ptr<remoting::MouseCursorShape> cursor_shape) { | |
168 DCHECK(capture_thread_.message_loop_proxy()->BelongsToCurrentThread()); | |
169 } | |
170 | |
171 void ScreenCapturer::DoAllocate(int frame_rate, | |
172 EventHandler* event_handler) { | |
173 DCHECK(capture_thread_.message_loop_proxy()->BelongsToCurrentThread()); | |
174 frame_rate_ = frame_rate; | |
175 event_handler_ = event_handler; | |
176 if (!video_frame_capturer_) | |
177 video_frame_capturer_ = remoting::VideoFrameCapturer::Create(); | |
178 if (video_frame_capturer_) | |
179 video_frame_capturer_->Start(this); | |
180 waiting_frame_size_ = true; | |
Wez
2013/01/10 00:58:14
nit: If you need this, set it before Start()ing th
Wez
2013/01/10 00:58:14
nit: Add blank lines around frame_rate and event_h
Sergey Ulanov
2013/01/11 23:46:20
Start() doesn't really start anything. DoCapture()
Sergey Ulanov
2013/01/11 23:46:20
Done.
| |
181 DoCapture(); | |
182 } | |
183 | |
184 void ScreenCapturer::DoStart() { | |
185 DCHECK(capture_thread_.message_loop_proxy()->BelongsToCurrentThread()); | |
186 started_ = true; | |
187 if (video_frame_capturer_) { | |
188 timer_.reset(new base::RepeatingTimer<ScreenCapturer>()); | |
189 timer_->Start(FROM_HERE, base::TimeDelta::FromSeconds(1) / frame_rate_, | |
190 base::Bind(&ScreenCapturer::DoCapture, | |
191 base::Unretained(this))); | |
192 } | |
193 } | |
194 | |
195 void ScreenCapturer::DoStop() { | |
196 DCHECK(capture_thread_.message_loop_proxy()->BelongsToCurrentThread()); | |
197 started_ = false; | |
198 if (video_frame_capturer_) { | |
199 timer_.reset(); | |
200 video_frame_capturer_->Stop(); | |
wjia(left Chromium)
2013/01/07 22:15:26
Is this an synchronous API? ScreenCapturer wouldn'
Sergey Ulanov
2013/01/09 20:35:30
Yes.
| |
201 } | |
202 } | |
203 | |
204 void ScreenCapturer::DoDeAllocate() { | |
Wez
2013/01/10 00:58:14
ALso deallocate the resize buffer here.
Sergey Ulanov
2013/01/11 23:46:20
Added it in DoStop.
| |
205 DCHECK(capture_thread_.message_loop_proxy()->BelongsToCurrentThread()); | |
206 video_frame_capturer_.reset(); | |
207 event_handler_ = NULL; | |
208 waiting_frame_size_ = false; | |
209 started_ = false; | |
Wez
2013/01/10 00:58:14
We should already be stopped via Stop?
Sergey Ulanov
2013/01/11 23:46:20
DeAllocate can be called without calling Stop().
| |
210 capture_pending_ = true; | |
Wez
2013/01/10 00:58:14
Why should capture_pending be true when deallocate
Sergey Ulanov
2013/01/11 23:46:20
Fixed now.
| |
211 } | |
212 | |
213 void ScreenCapturer::DoCapture() { | |
214 if (capture_pending_) | |
215 return; | |
216 capture_pending_ = true; | |
217 video_frame_capturer_->CaptureFrame(); | |
218 } | |
219 | |
220 } // namespace content | |
OLD | NEW |