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 #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 |