|
OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2011 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/video_capture_impl.h" | |
6 | |
7 #include "content/common/child_process.h" | |
8 #include "content/common/video_capture_messages.h" | |
9 #include "content/common/view_messages.h" | |
jam
2011/05/24 19:15:41
nit: why add this?
wjia(left Chromium)
2011/05/24 20:47:15
removed.
| |
10 | |
11 namespace { | |
12 | |
13 // Timeout setting for StartCapture. | |
14 enum { | |
15 kTryTimes = 1000, | |
jam
2011/05/24 19:15:41
seems odd to put these two different constants in
wjia(left Chromium)
2011/05/24 20:47:15
split.
| |
16 kDelayInMillisecond = 1 | |
17 }; | |
18 | |
19 } // namespace | |
20 | |
21 VideoCaptureImpl::DIBBuffer::DIBBuffer( | |
22 TransportDIB* d, media::VideoCapture::VideoFrameBuffer* ptr) | |
23 : dib(d), | |
24 mapped_memory(ptr) {} | |
25 | |
26 VideoCaptureImpl::DIBBuffer::~DIBBuffer() { | |
27 delete dib; | |
28 } | |
29 | |
30 bool VideoCaptureImpl::CaptureStarted() { | |
31 return state_ == kStarted; | |
32 } | |
33 | |
34 int VideoCaptureImpl::CaptureWidth() { | |
35 return width_; | |
36 } | |
37 | |
38 int VideoCaptureImpl::CaptureHeight() { | |
39 return height_; | |
40 } | |
41 | |
42 int VideoCaptureImpl::CaptureFrameRate() { | |
43 return frame_rate_; | |
44 } | |
45 | |
46 VideoCaptureImpl::VideoCaptureImpl( | |
47 const media::VideoCaptureSessionId id, | |
48 scoped_refptr<base::MessageLoopProxy> ml_proxy, | |
49 VideoCaptureMessageFilter* filter) | |
50 : VideoCapture(), | |
51 message_filter_(filter), | |
52 session_id_(id), | |
53 ml_proxy_(ml_proxy), | |
54 device_id_(0), | |
55 width_(0), | |
56 height_(0), | |
57 frame_rate_(0), | |
58 video_type_(media::VideoFrame::I420), | |
59 new_width_(0), | |
60 new_height_(0), | |
61 state_(kStopped) { | |
62 DCHECK(filter); | |
63 } | |
64 | |
65 VideoCaptureImpl::~VideoCaptureImpl() {} | |
66 | |
67 void VideoCaptureImpl::Init() { | |
68 DCHECK(ChildProcess::current()) << "Must be in the renderer"; | |
jam
2011/05/24 19:15:41
this will return true if you're in any childproces
wjia(left Chromium)
2011/05/24 20:47:15
removed.
| |
69 base::MessageLoopProxy* message_loop_proxy = | |
70 ChildProcess::current()->io_message_loop_proxy(); | |
71 DCHECK(message_loop_proxy); | |
jam
2011/05/24 19:15:41
nit: this dcheck is unnecessary. in release build
wjia(left Chromium)
2011/05/24 20:47:15
changed to condition checking.
| |
72 | |
73 if (!message_loop_proxy->BelongsToCurrentThread()) { | |
74 message_loop_proxy->PostTask(FROM_HERE, | |
75 NewRunnableMethod(this, &VideoCaptureImpl::AddDelegateOnIOThread)); | |
76 return; | |
77 } | |
78 | |
79 AddDelegateOnIOThread(); | |
80 } | |
81 | |
82 void VideoCaptureImpl::DeInit(Task* task) { | |
83 DCHECK(ChildProcess::current()) << "Must be in the renderer"; | |
84 base::MessageLoopProxy* message_loop_proxy = | |
85 ChildProcess::current()->io_message_loop_proxy(); | |
86 DCHECK(message_loop_proxy); | |
87 | |
88 if (state_ == kStarted) | |
89 message_filter_->Send(new VideoCaptureHostMsg_Stop(0, device_id_)); | |
90 | |
91 if (!message_loop_proxy->BelongsToCurrentThread()) { | |
92 message_loop_proxy->PostTask(FROM_HERE, | |
93 NewRunnableMethod(this, &VideoCaptureImpl::RemoveDelegateOnIOThread, | |
94 task)); | |
95 return; | |
96 } | |
97 | |
98 RemoveDelegateOnIOThread(task); | |
99 } | |
100 | |
101 void VideoCaptureImpl::StartCapture( | |
102 media::VideoCapture::EventHandler* handler, | |
103 const VideoCaptureCapability& capability) { | |
104 DCHECK_EQ(capability.raw_type, media::VideoFrame::I420); | |
105 | |
106 if (!ml_proxy_->BelongsToCurrentThread()) { | |
107 ml_proxy_->PostTask(FROM_HERE, | |
108 NewRunnableMethod(this, &VideoCaptureImpl::StartCapture, handler, | |
109 capability)); | |
110 return; | |
111 } | |
112 | |
113 PendingClient::iterator it = pending_clients_.find(handler); | |
114 if (!device_id_ || !message_filter_->ReadyToSend()) { | |
115 if (it != pending_clients_.end() && it->second <= 0) { | |
116 if (it->second == 0) { | |
117 handler->OnError(this, 2); | |
118 } | |
119 pending_clients_.erase(it); | |
120 } else { | |
121 if (it == pending_clients_.end()) | |
122 pending_clients_[handler] = kTryTimes; | |
123 else | |
124 it->second--; | |
125 | |
126 ml_proxy_->PostDelayedTask(FROM_HERE, | |
127 NewRunnableMethod(this, &VideoCaptureImpl::StartCapture, handler, | |
128 capability), kDelayInMillisecond); | |
129 } | |
130 return; | |
131 } | |
132 | |
133 if (it != pending_clients_.end()) | |
134 pending_clients_.erase(it); | |
135 | |
136 if (capability.resolution_fixed && master_clients_.size() && | |
137 (capability.width != width_ || capability.height != height_)) { | |
138 // Can't have 2 master clients with different resolutions. | |
139 handler->OnError(this, 1); | |
140 return; | |
141 } | |
142 | |
143 clients_[handler] = capability; | |
144 if (capability.resolution_fixed) { | |
145 master_clients_.push_back(handler); | |
146 if (master_clients_.size() > 1) | |
147 return; | |
148 } | |
149 | |
150 if (state_ == kStarted) { | |
151 // Take the resolution of master client. | |
152 if (capability.resolution_fixed && | |
153 (capability.width != width_ || capability.height != height_)) { | |
154 new_width_ = capability.width; | |
155 new_height_ = capability.height; | |
156 DLOG(INFO) << "StartCapture: Got master client with new resolution " | |
157 "during started, try to restart."; | |
158 StopDevice(); | |
159 } | |
160 handler->OnStarted(this); | |
161 return; | |
162 } | |
163 | |
164 if (state_ == kStopping) { | |
165 if (capability.resolution_fixed || !pending_start()) { | |
166 new_width_ = capability.width; | |
167 new_height_ = capability.height; | |
168 DLOG(INFO) << "StartCapture: Got new resolution, already in stopping."; | |
169 } | |
170 handler->OnStarted(this); | |
171 return; | |
172 } | |
173 | |
174 DCHECK_EQ(clients_.size(), 1ul); | |
175 video_type_ = capability.raw_type; | |
176 new_width_ = 0; | |
177 new_height_ = 0; | |
178 width_ = capability.width; | |
179 height_ = capability.height; | |
180 | |
181 StartCaptureInternal(); | |
182 } | |
183 | |
184 void VideoCaptureImpl::StopCapture(media::VideoCapture::EventHandler* handler) { | |
185 if (!ml_proxy_->BelongsToCurrentThread()) { | |
186 ml_proxy_->PostTask(FROM_HERE, | |
187 NewRunnableMethod(this, &VideoCaptureImpl::StopCapture, handler)); | |
188 return; | |
189 } | |
190 | |
191 PendingClient::iterator it = pending_clients_.find(handler); | |
192 if (it != pending_clients_.end()) { | |
193 handler->OnStopped(this); | |
194 it->second = -1; | |
195 return; | |
196 } | |
197 | |
198 if (clients_.find(handler) == clients_.end()) | |
199 return; | |
200 | |
201 handler->OnStopped(this); | |
202 clients_.erase(handler); | |
203 master_clients_.remove(handler); | |
204 | |
205 // Still have at least one master client. | |
206 if (master_clients_.size() > 0) | |
207 return; | |
208 | |
209 // TODO(wjia): Is it really needed to handle resolution change for non-master | |
210 // clients, except no client case? | |
211 if (clients_.size() > 0) { | |
212 DLOG(INFO) << "StopCapture: No master client."; | |
213 int maxw = 0; | |
214 int maxh = 0; | |
215 for (ClientInfo::iterator it = clients_.begin(); | |
216 it != clients_.end(); it++) { | |
217 if (it->second.width > maxw && it->second.height > maxh) { | |
218 maxw = it->second.width; | |
219 maxh = it->second.height; | |
220 } | |
221 } | |
222 | |
223 if (state_ == kStarted) { | |
224 // Only handle resolution reduction. | |
225 if (maxw < width_ && maxh < height_) { | |
226 new_width_ = maxw; | |
227 new_height_ = maxh; | |
228 DLOG(INFO) << "StopCapture: New smaller resolution, stopping ..."; | |
229 StopDevice(); | |
230 } | |
231 return; | |
232 } | |
233 | |
234 if (state_ == kStopping) { | |
235 new_width_ = maxw; | |
236 new_height_ = maxh; | |
237 DLOG(INFO) << "StopCapture: New resolution, during stopping."; | |
238 return; | |
239 } | |
240 } else { | |
241 new_width_ = width_ = 0; | |
242 new_height_ = height_ = 0; | |
243 DLOG(INFO) << "StopCapture: No more client, stopping ..."; | |
244 StopDevice(); | |
245 } | |
246 } | |
247 | |
248 void VideoCaptureImpl::OnBufferReceived(TransportDIB::Handle handle, | |
249 base::Time timestamp) { | |
250 if (!ml_proxy_->BelongsToCurrentThread()) { | |
251 ml_proxy_->PostTask(FROM_HERE, | |
252 NewRunnableMethod(this, &VideoCaptureImpl::OnBufferReceived, | |
253 handle, timestamp)); | |
254 return; | |
255 } | |
256 | |
257 if (state_ != kStarted) { | |
258 message_filter_->Send( | |
259 new VideoCaptureHostMsg_BufferReady(0, device_id_, handle)); | |
260 return; | |
261 } | |
262 | |
263 media::VideoCapture::VideoFrameBuffer* buffer; | |
264 CachedDIB::iterator it; | |
265 for (it = cached_dibs_.begin(); it != cached_dibs_.end(); it++) { | |
266 if ((*it)->dib->handle() == handle) | |
267 break; | |
268 } | |
269 if (it == cached_dibs_.end()) { | |
270 TransportDIB* dib = TransportDIB::Map(handle); | |
271 buffer = new VideoFrameBuffer(); | |
272 buffer->memory_pointer = dib->memory(); | |
273 buffer->buffer_size = dib->size(); | |
274 buffer->width = width_; | |
275 buffer->height = height_; | |
276 | |
277 DIBBuffer* dib_buffer = new DIBBuffer(dib, buffer); | |
278 cached_dibs_.push_back(dib_buffer); | |
279 } else { | |
280 buffer = (*it)->mapped_memory; | |
281 } | |
282 | |
283 // TODO(wjia): handle buffer sharing with downstream modules. | |
284 for (ClientInfo::iterator it = clients_.begin(); it != clients_.end(); it++) { | |
285 it->first->OnBufferReady(this, buffer); | |
286 } | |
287 | |
288 message_filter_->Send( | |
289 new VideoCaptureHostMsg_BufferReady(0, device_id_, handle)); | |
290 } | |
291 | |
292 void VideoCaptureImpl::OnStateChanged( | |
293 const media::VideoCapture::State& state) { | |
294 if (!ml_proxy_->BelongsToCurrentThread()) { | |
295 ml_proxy_->PostTask(FROM_HERE, | |
296 NewRunnableMethod(this, &VideoCaptureImpl::OnStateChanged, state)); | |
297 return; | |
298 } | |
299 | |
300 switch (state) { | |
301 case media::VideoCapture::kStarted: | |
302 for (ClientInfo::iterator it = clients_.begin(); | |
303 it != clients_.end(); it++) { | |
304 it->first->OnStarted(this); | |
305 } | |
306 break; | |
307 case media::VideoCapture::kStopped: | |
308 state_ = kStopped; | |
309 DLOG(INFO) << "OnStateChanged: stopped!, device_id = " << device_id_; | |
310 if (pending_start()) | |
311 RestartCapture(); | |
312 break; | |
313 case media::VideoCapture::kPaused: | |
314 for (ClientInfo::iterator it = clients_.begin(); | |
315 it != clients_.end(); it++) { | |
316 it->first->OnPaused(this); | |
317 } | |
318 break; | |
319 case media::VideoCapture::kError: | |
320 for (ClientInfo::iterator it = clients_.begin(); | |
321 it != clients_.end(); it++) { | |
322 // TODO(wjia): browser process would send error code. | |
323 it->first->OnError(this, 1); | |
324 } | |
325 break; | |
326 default: | |
327 break; | |
328 } | |
329 } | |
330 | |
331 void VideoCaptureImpl::OnDeviceInfoReceived( | |
332 const media::VideoCaptureParams& device_info) { | |
333 if (!ml_proxy_->BelongsToCurrentThread()) { | |
334 ml_proxy_->PostTask(FROM_HERE, | |
335 NewRunnableMethod(this, &VideoCaptureImpl::OnDeviceInfoReceived, | |
336 device_info)); | |
337 return; | |
338 } | |
339 | |
340 for (ClientInfo::iterator it = clients_.begin(); it != clients_.end(); it++) { | |
341 it->first->OnDeviceInfoReceived(this, device_info); | |
342 } | |
343 } | |
344 | |
345 void VideoCaptureImpl::StopDevice() { | |
346 if (!ml_proxy_->BelongsToCurrentThread()) { | |
347 ml_proxy_->PostTask(FROM_HERE, | |
348 NewRunnableMethod(this, &VideoCaptureImpl::StopDevice)); | |
349 return; | |
350 } | |
351 | |
352 if (state_ == kStarted) { | |
353 state_ = kStopping; | |
354 message_filter_->Send(new VideoCaptureHostMsg_Stop(0, device_id_)); | |
355 width_ = height_ = 0; | |
356 } | |
357 } | |
358 | |
359 void VideoCaptureImpl::RestartCapture() { | |
360 DCHECK(ml_proxy_->BelongsToCurrentThread()); | |
361 DCHECK_EQ(state_, kStopped); | |
362 | |
363 width_ = new_width_; | |
364 height_ = new_height_; | |
365 new_width_ = 0; | |
366 new_height_ = 0; | |
367 | |
368 DLOG(INFO) << "RestartCapture, " << width_ << ", " << height_; | |
369 StartCaptureInternal(); | |
370 } | |
371 | |
372 void VideoCaptureImpl::StartCaptureInternal() { | |
373 DCHECK(ml_proxy_->BelongsToCurrentThread()); | |
374 DCHECK(device_id_); | |
375 DCHECK(message_filter_->ReadyToSend()); | |
376 | |
377 media::VideoCaptureParams params; | |
378 params.width = width_; | |
379 params.height = height_; | |
380 params.session_id = session_id_; | |
381 | |
382 message_filter_->Send(new VideoCaptureHostMsg_Start(0, device_id_, params)); | |
383 state_ = kStarted; | |
384 for (ClientInfo::iterator it = clients_.begin(); it != clients_.end(); it++) { | |
385 it->first->OnStarted(this); | |
386 } | |
387 } | |
388 | |
389 void VideoCaptureImpl::AddDelegateOnIOThread() { | |
390 // |device_id_| is modified only on io thread. | |
391 device_id_ = message_filter_->AddDelegate(this); | |
392 } | |
393 | |
394 void VideoCaptureImpl::RemoveDelegateOnIOThread(Task* task) { | |
395 message_filter_->RemoveDelegate(this); | |
396 media::AutoTaskRunner auto_runner(task); | |
397 } | |
OLD | NEW |