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