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 |