Chromium Code Reviews
|
| 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 |