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