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