| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "content/browser/renderer_host/media/video_capture_manager.h" | 5 #include "content/browser/renderer_host/media/video_capture_manager.h" |
| 6 | 6 |
| 7 #include <set> | 7 #include <set> |
| 8 | 8 |
| 9 #include "base/bind.h" | 9 #include "base/bind.h" |
| 10 #include "base/command_line.h" | 10 #include "base/command_line.h" |
| (...skipping 19 matching lines...) Expand all Loading... |
| 30 namespace content { | 30 namespace content { |
| 31 | 31 |
| 32 // Starting id for the first capture session. | 32 // Starting id for the first capture session. |
| 33 // VideoCaptureManager::kStartOpenSessionId is used as default id without | 33 // VideoCaptureManager::kStartOpenSessionId is used as default id without |
| 34 // explicitly calling open device. | 34 // explicitly calling open device. |
| 35 enum { kFirstSessionId = VideoCaptureManager::kStartOpenSessionId + 1 }; | 35 enum { kFirstSessionId = VideoCaptureManager::kStartOpenSessionId + 1 }; |
| 36 | 36 |
| 37 VideoCaptureManager::DeviceEntry::DeviceEntry( | 37 VideoCaptureManager::DeviceEntry::DeviceEntry( |
| 38 MediaStreamType stream_type, | 38 MediaStreamType stream_type, |
| 39 const std::string& id, | 39 const std::string& id, |
| 40 scoped_refptr<VideoCaptureController> controller) | 40 scoped_ptr<VideoCaptureController> controller) |
| 41 : stream_type(stream_type), | 41 : stream_type(stream_type), |
| 42 id(id), | 42 id(id), |
| 43 video_capture_controller(controller) {} | 43 video_capture_controller(controller.Pass()) {} |
| 44 | 44 |
| 45 VideoCaptureManager::DeviceEntry::~DeviceEntry() {} | 45 VideoCaptureManager::DeviceEntry::~DeviceEntry() {} |
| 46 | 46 |
| 47 VideoCaptureManager::VideoCaptureManager() | 47 VideoCaptureManager::VideoCaptureManager() |
| 48 : listener_(NULL), | 48 : listener_(NULL), |
| 49 new_capture_session_id_(kFirstSessionId), | 49 new_capture_session_id_(kFirstSessionId), |
| 50 use_fake_device_(false) { | 50 use_fake_device_(false) { |
| 51 } | 51 } |
| 52 | 52 |
| 53 VideoCaptureManager::~VideoCaptureManager() { | 53 VideoCaptureManager::~VideoCaptureManager() { |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 132 sessions_.erase(session_it); | 132 sessions_.erase(session_it); |
| 133 } | 133 } |
| 134 | 134 |
| 135 void VideoCaptureManager::UseFakeDevice() { | 135 void VideoCaptureManager::UseFakeDevice() { |
| 136 use_fake_device_ = true; | 136 use_fake_device_ = true; |
| 137 } | 137 } |
| 138 | 138 |
| 139 void VideoCaptureManager::DoStartDeviceOnDeviceThread( | 139 void VideoCaptureManager::DoStartDeviceOnDeviceThread( |
| 140 DeviceEntry* entry, | 140 DeviceEntry* entry, |
| 141 const media::VideoCaptureCapability& capture_params, | 141 const media::VideoCaptureCapability& capture_params, |
| 142 media::VideoCaptureDevice::EventHandler* controller_as_handler) { | 142 scoped_ptr<media::VideoCaptureDevice::EventHandler> device_client) { |
| 143 SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.StartDeviceTime"); | 143 SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.StartDeviceTime"); |
| 144 DCHECK(IsOnDeviceThread()); | 144 DCHECK(IsOnDeviceThread()); |
| 145 | 145 |
| 146 scoped_ptr<media::VideoCaptureDevice> video_capture_device; | 146 scoped_ptr<media::VideoCaptureDevice> video_capture_device; |
| 147 switch (entry->stream_type) { | 147 switch (entry->stream_type) { |
| 148 case MEDIA_DEVICE_VIDEO_CAPTURE: { | 148 case MEDIA_DEVICE_VIDEO_CAPTURE: { |
| 149 // We look up the device id from the renderer in our local enumeration | 149 // We look up the device id from the renderer in our local enumeration |
| 150 // since the renderer does not have all the information that might be | 150 // since the renderer does not have all the information that might be |
| 151 // held in the browser-side VideoCaptureDevice::Name structure. | 151 // held in the browser-side VideoCaptureDevice::Name structure. |
| 152 media::VideoCaptureDevice::Name* found = | 152 media::VideoCaptureDevice::Name* found = |
| (...skipping 19 matching lines...) Expand all Loading... |
| 172 #endif // defined(ENABLE_SCREEN_CAPTURE) | 172 #endif // defined(ENABLE_SCREEN_CAPTURE) |
| 173 break; | 173 break; |
| 174 } | 174 } |
| 175 default: { | 175 default: { |
| 176 NOTIMPLEMENTED(); | 176 NOTIMPLEMENTED(); |
| 177 break; | 177 break; |
| 178 } | 178 } |
| 179 } | 179 } |
| 180 | 180 |
| 181 if (!video_capture_device) { | 181 if (!video_capture_device) { |
| 182 controller_as_handler->OnError(); | 182 device_client->OnError(); |
| 183 return; | 183 return; |
| 184 } | 184 } |
| 185 | 185 |
| 186 // TODO(nick): Merge Allocate() and Start(). http://crbug.com/285562 | 186 video_capture_device->AllocateAndStart(capture_params, device_client.Pass()); |
| 187 video_capture_device->Allocate(capture_params, controller_as_handler); | |
| 188 video_capture_device->Start(); | |
| 189 entry->video_capture_device = video_capture_device.Pass(); | 187 entry->video_capture_device = video_capture_device.Pass(); |
| 190 } | 188 } |
| 191 | 189 |
| 192 void VideoCaptureManager::StartCaptureForClient( | 190 void VideoCaptureManager::StartCaptureForClient( |
| 193 const media::VideoCaptureParams& capture_params, | 191 const media::VideoCaptureParams& capture_params, |
| 194 base::ProcessHandle client_render_process, | 192 base::ProcessHandle client_render_process, |
| 195 VideoCaptureControllerID client_id, | 193 VideoCaptureControllerID client_id, |
| 196 VideoCaptureControllerEventHandler* client_handler, | 194 VideoCaptureControllerEventHandler* client_handler, |
| 197 base::Callback<void(VideoCaptureController*)> done_cb) { | 195 DoneCB done_cb) { |
| 198 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 196 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 199 DVLOG(1) << "VideoCaptureManager::StartCaptureForClient, (" | 197 DVLOG(1) << "VideoCaptureManager::StartCaptureForClient, (" |
| 200 << capture_params.width | 198 << capture_params.width |
| 201 << ", " << capture_params.height | 199 << ", " << capture_params.height |
| 202 << ", " << capture_params.frame_rate | 200 << ", " << capture_params.frame_rate |
| 203 << ", #" << capture_params.session_id | 201 << ", #" << capture_params.session_id |
| 204 << ")"; | 202 << ")"; |
| 205 | 203 |
| 206 if (capture_params.session_id == kStartOpenSessionId) { | 204 if (capture_params.session_id == kStartOpenSessionId) { |
| 207 // Solution for not using MediaStreamManager. Enumerate the devices and | 205 // Solution for not using MediaStreamManager. Enumerate the devices and |
| 208 // open the first one, and then start it. | 206 // open the first one, and then start it. |
| 209 base::PostTaskAndReplyWithResult(device_loop_, FROM_HERE, | 207 base::PostTaskAndReplyWithResult(device_loop_, FROM_HERE, |
| 210 base::Bind(&VideoCaptureManager::GetAvailableDevicesOnDeviceThread, | 208 base::Bind(&VideoCaptureManager::GetAvailableDevicesOnDeviceThread, |
| 211 this, MEDIA_DEVICE_VIDEO_CAPTURE), | 209 this, MEDIA_DEVICE_VIDEO_CAPTURE), |
| 212 base::Bind(&VideoCaptureManager::OpenAndStartDefaultSession, this, | 210 base::Bind(&VideoCaptureManager::OpenAndStartDefaultSession, this, |
| 213 capture_params, client_render_process, client_id, | 211 capture_params, client_render_process, client_id, |
| 214 client_handler, done_cb)); | 212 client_handler, done_cb)); |
| 215 return; | 213 return; |
| 216 } else { | 214 } else { |
| 217 DoStartCaptureForClient(capture_params, client_render_process, client_id, | 215 DoStartCaptureForClient(capture_params, client_render_process, client_id, |
| 218 client_handler, done_cb); | 216 client_handler, done_cb); |
| 219 } | 217 } |
| 220 } | 218 } |
| 221 | 219 |
| 222 void VideoCaptureManager::DoStartCaptureForClient( | 220 void VideoCaptureManager::DoStartCaptureForClient( |
| 223 const media::VideoCaptureParams& capture_params, | 221 const media::VideoCaptureParams& capture_params, |
| 224 base::ProcessHandle client_render_process, | 222 base::ProcessHandle client_render_process, |
| 225 VideoCaptureControllerID client_id, | 223 VideoCaptureControllerID client_id, |
| 226 VideoCaptureControllerEventHandler* client_handler, | 224 VideoCaptureControllerEventHandler* client_handler, |
| 227 base::Callback<void(VideoCaptureController*)> done_cb) { | 225 DoneCB done_cb) { |
| 228 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 226 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 229 | 227 |
| 230 DeviceEntry* entry = GetOrCreateDeviceEntry(capture_params.session_id); | 228 DeviceEntry* entry = GetOrCreateDeviceEntry(capture_params.session_id); |
| 231 if (!entry) { | 229 if (!entry) { |
| 232 done_cb.Run(NULL); | 230 done_cb.Run(base::WeakPtr<VideoCaptureController>()); |
| 233 return; | 231 return; |
| 234 } | 232 } |
| 235 | 233 |
| 236 DCHECK(entry->video_capture_controller); | 234 DCHECK(entry->video_capture_controller); |
| 237 | 235 |
| 238 // First client starts the device. | 236 // First client starts the device. |
| 239 if (entry->video_capture_controller->GetClientCount() == 0) { | 237 if (entry->video_capture_controller->GetClientCount() == 0) { |
| 240 DVLOG(1) << "VideoCaptureManager starting device (type = " | 238 DVLOG(1) << "VideoCaptureManager starting device (type = " |
| 241 << entry->stream_type << ", id = " << entry->id << ")"; | 239 << entry->stream_type << ", id = " << entry->id << ")"; |
| 242 | 240 |
| 243 media::VideoCaptureCapability params_as_capability; | 241 media::VideoCaptureCapability params_as_capability; |
| 244 params_as_capability.width = capture_params.width; | 242 params_as_capability.width = capture_params.width; |
| 245 params_as_capability.height = capture_params.height; | 243 params_as_capability.height = capture_params.height; |
| 246 params_as_capability.frame_rate = capture_params.frame_rate; | 244 params_as_capability.frame_rate = capture_params.frame_rate; |
| 247 params_as_capability.session_id = capture_params.session_id; | 245 params_as_capability.session_id = capture_params.session_id; |
| 248 params_as_capability.frame_size_type = capture_params.frame_size_type; | 246 params_as_capability.frame_size_type = capture_params.frame_size_type; |
| 249 | 247 |
| 250 device_loop_->PostTask(FROM_HERE, base::Bind( | 248 device_loop_->PostTask(FROM_HERE, base::Bind( |
| 251 &VideoCaptureManager::DoStartDeviceOnDeviceThread, this, | 249 &VideoCaptureManager::DoStartDeviceOnDeviceThread, this, |
| 252 entry, params_as_capability, entry->video_capture_controller)); | 250 entry, params_as_capability, |
| 251 base::Passed(entry->video_capture_controller->NewDeviceClient()))); |
| 253 } | 252 } |
| 254 // Run the callback first, as AddClient() may trigger OnFrameInfo(). | 253 // Run the callback first, as AddClient() may trigger OnFrameInfo(). |
| 255 done_cb.Run(entry->video_capture_controller); | 254 done_cb.Run(entry->video_capture_controller->GetWeakPtr()); |
| 256 entry->video_capture_controller->AddClient(client_id, | 255 entry->video_capture_controller->AddClient(client_id, |
| 257 client_handler, | 256 client_handler, |
| 258 client_render_process, | 257 client_render_process, |
| 259 capture_params); | 258 capture_params); |
| 260 } | 259 } |
| 261 | 260 |
| 262 void VideoCaptureManager::StopCaptureForClient( | 261 void VideoCaptureManager::StopCaptureForClient( |
| 263 VideoCaptureController* controller, | 262 VideoCaptureController* controller, |
| 264 VideoCaptureControllerID client_id, | 263 VideoCaptureControllerID client_id, |
| 265 VideoCaptureControllerEventHandler* client_handler) { | 264 VideoCaptureControllerEventHandler* client_handler) { |
| (...skipping 21 matching lines...) Expand all Loading... |
| 287 base::MessageLoop::current()->PostTask(FROM_HERE, | 286 base::MessageLoop::current()->PostTask(FROM_HERE, |
| 288 base::Bind(&VideoCaptureManager::OnClosed, this, | 287 base::Bind(&VideoCaptureManager::OnClosed, this, |
| 289 MEDIA_DEVICE_VIDEO_CAPTURE, kStartOpenSessionId)); | 288 MEDIA_DEVICE_VIDEO_CAPTURE, kStartOpenSessionId)); |
| 290 } | 289 } |
| 291 } | 290 } |
| 292 | 291 |
| 293 void VideoCaptureManager::DoStopDeviceOnDeviceThread(DeviceEntry* entry) { | 292 void VideoCaptureManager::DoStopDeviceOnDeviceThread(DeviceEntry* entry) { |
| 294 SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.StopDeviceTime"); | 293 SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.StopDeviceTime"); |
| 295 DCHECK(IsOnDeviceThread()); | 294 DCHECK(IsOnDeviceThread()); |
| 296 if (entry->video_capture_device) { | 295 if (entry->video_capture_device) { |
| 297 // TODO(nick): Merge Stop() and DeAllocate(). http://crbug.com/285562 | 296 entry->video_capture_device->StopAndDeAllocate(); |
| 298 entry->video_capture_device->Stop(); | |
| 299 entry->video_capture_device->DeAllocate(); | |
| 300 entry->video_capture_device.reset(); | |
| 301 } | 297 } |
| 302 } | 298 entry->video_capture_device.reset(); |
| 303 | |
| 304 void VideoCaptureManager::FreeDeviceEntryOnIOThread( | |
| 305 scoped_ptr<DeviceEntry> entry) { | |
| 306 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 307 entry->video_capture_controller = NULL; | |
| 308 entry.reset(); | |
| 309 } | 299 } |
| 310 | 300 |
| 311 void VideoCaptureManager::OnOpened(MediaStreamType stream_type, | 301 void VideoCaptureManager::OnOpened(MediaStreamType stream_type, |
| 312 int capture_session_id) { | 302 int capture_session_id) { |
| 313 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 303 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 314 if (!listener_) { | 304 if (!listener_) { |
| 315 // Listener has been removed. | 305 // Listener has been removed. |
| 316 return; | 306 return; |
| 317 } | 307 } |
| 318 listener_->Opened(stream_type, capture_session_id); | 308 listener_->Opened(stream_type, capture_session_id); |
| (...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 416 } | 406 } |
| 417 } | 407 } |
| 418 return NULL; | 408 return NULL; |
| 419 } | 409 } |
| 420 | 410 |
| 421 void VideoCaptureManager::OpenAndStartDefaultSession( | 411 void VideoCaptureManager::OpenAndStartDefaultSession( |
| 422 const media::VideoCaptureParams& capture_params, | 412 const media::VideoCaptureParams& capture_params, |
| 423 base::ProcessHandle client_render_process, | 413 base::ProcessHandle client_render_process, |
| 424 VideoCaptureControllerID client_id, | 414 VideoCaptureControllerID client_id, |
| 425 VideoCaptureControllerEventHandler* client_handler, | 415 VideoCaptureControllerEventHandler* client_handler, |
| 426 base::Callback<void(VideoCaptureController*)> done_cb, | 416 DoneCB done_cb, |
| 427 const media::VideoCaptureDevice::Names& device_names) { | 417 const media::VideoCaptureDevice::Names& device_names) { |
| 428 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 418 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 429 | 419 |
| 430 // |device_names| is a value returned by GetAvailableDevicesOnDeviceThread(). | 420 // |device_names| is a value returned by GetAvailableDevicesOnDeviceThread(). |
| 431 // We'll mimic an Open() operation on the first element in that list. | 421 // We'll mimic an Open() operation on the first element in that list. |
| 432 DCHECK(capture_params.session_id == kStartOpenSessionId); | 422 DCHECK(capture_params.session_id == kStartOpenSessionId); |
| 433 if (device_names.empty() || | 423 if (device_names.empty() || |
| 434 sessions_.count(capture_params.session_id) != 0) { | 424 sessions_.count(capture_params.session_id) != 0) { |
| 435 done_cb.Run(NULL); | 425 done_cb.Run(base::WeakPtr<VideoCaptureController>()); |
| 436 return; | 426 return; |
| 437 } | 427 } |
| 438 | 428 |
| 439 // Open the device by creating a |sessions_| entry. | 429 // Open the device by creating a |sessions_| entry. |
| 440 sessions_[capture_params.session_id] = | 430 sessions_[capture_params.session_id] = |
| 441 MediaStreamDevice(MEDIA_DEVICE_VIDEO_CAPTURE, | 431 MediaStreamDevice(MEDIA_DEVICE_VIDEO_CAPTURE, |
| 442 device_names.front().id(), | 432 device_names.front().id(), |
| 443 device_names.front().GetNameAndModel()); | 433 device_names.front().GetNameAndModel()); |
| 444 | 434 |
| 445 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, | 435 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, |
| 446 base::Bind(&VideoCaptureManager::OnOpened, this, | 436 base::Bind(&VideoCaptureManager::OnOpened, this, |
| 447 MEDIA_DEVICE_VIDEO_CAPTURE, kStartOpenSessionId)); | 437 MEDIA_DEVICE_VIDEO_CAPTURE, kStartOpenSessionId)); |
| 448 | 438 |
| 449 DoStartCaptureForClient(capture_params, client_render_process, client_id, | 439 DoStartCaptureForClient(capture_params, client_render_process, client_id, |
| 450 client_handler, done_cb); | 440 client_handler, done_cb); |
| 451 } | 441 } |
| 452 | 442 |
| 453 void VideoCaptureManager::DestroyDeviceEntryIfNoClients(DeviceEntry* entry) { | 443 void VideoCaptureManager::DestroyDeviceEntryIfNoClients(DeviceEntry* entry) { |
| 454 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 444 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 455 // Removal of the last client stops the device. | 445 // Removal of the last client stops the device. |
| 456 if (entry->video_capture_controller->GetClientCount() == 0) { | 446 if (entry->video_capture_controller->GetClientCount() == 0) { |
| 457 DVLOG(1) << "VideoCaptureManager stopping device (type = " | 447 DVLOG(1) << "VideoCaptureManager stopping device (type = " |
| 458 << entry->stream_type << ", id = " << entry->id << ")"; | 448 << entry->stream_type << ", id = " << entry->id << ")"; |
| 459 | 449 |
| 460 // The DeviceEntry is removed from |devices_| immediately, but will be torn | 450 // The DeviceEntry is removed from |devices_| immediately. The controller is |
| 461 // down asynchronously. After this point, subsequent request to open this | 451 // deleted immediately, and the device is freed asynchronously. After this |
| 462 // same device ID will create a new DeviceEntry, VideoCaptureController, | 452 // point, subsequent requests to open this same device ID will create a new |
| 463 // and VideoCaptureDevice. | 453 // DeviceEntry, VideoCaptureController, and VideoCaptureDevice. |
| 464 devices_.erase(entry); | 454 devices_.erase(entry); |
| 465 device_loop_->PostTaskAndReply( | 455 entry->video_capture_controller.reset(); |
| 456 device_loop_->PostTask( |
| 466 FROM_HERE, | 457 FROM_HERE, |
| 467 base::Bind(&VideoCaptureManager::DoStopDeviceOnDeviceThread, this, | 458 base::Bind(&VideoCaptureManager::DoStopDeviceOnDeviceThread, this, |
| 468 base::Unretained(entry)), | 459 base::Owned(entry))); |
| 469 base::Bind(&VideoCaptureManager::FreeDeviceEntryOnIOThread, this, | |
| 470 base::Passed(make_scoped_ptr(entry).Pass()))); | |
| 471 } | 460 } |
| 472 } | 461 } |
| 473 | 462 |
| 474 VideoCaptureManager::DeviceEntry* VideoCaptureManager::GetOrCreateDeviceEntry( | 463 VideoCaptureManager::DeviceEntry* VideoCaptureManager::GetOrCreateDeviceEntry( |
| 475 int capture_session_id) { | 464 int capture_session_id) { |
| 476 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 465 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 477 | 466 |
| 478 std::map<int, MediaStreamDevice>::iterator session_it = | 467 std::map<int, MediaStreamDevice>::iterator session_it = |
| 479 sessions_.find(capture_session_id); | 468 sessions_.find(capture_session_id); |
| 480 if (session_it == sessions_.end()) { | 469 if (session_it == sessions_.end()) { |
| 481 return NULL; | 470 return NULL; |
| 482 } | 471 } |
| 483 const MediaStreamDevice& device_info = session_it->second; | 472 const MediaStreamDevice& device_info = session_it->second; |
| 484 | 473 |
| 485 // Check if another session has already opened this device. If so, just | 474 // Check if another session has already opened this device. If so, just |
| 486 // use that opened device. | 475 // use that opened device. |
| 487 DeviceEntry* const existing_device = | 476 DeviceEntry* const existing_device = |
| 488 GetDeviceEntryForMediaStreamDevice(device_info); | 477 GetDeviceEntryForMediaStreamDevice(device_info); |
| 489 if (existing_device) { | 478 if (existing_device) { |
| 490 DCHECK_EQ(device_info.type, existing_device->stream_type); | 479 DCHECK_EQ(device_info.type, existing_device->stream_type); |
| 491 return existing_device; | 480 return existing_device; |
| 492 } | 481 } |
| 493 | 482 |
| 494 scoped_refptr<VideoCaptureController> video_capture_controller = | 483 scoped_ptr<VideoCaptureController> video_capture_controller( |
| 495 new VideoCaptureController(); | 484 new VideoCaptureController()); |
| 496 DeviceEntry* new_device = new DeviceEntry(device_info.type, | 485 DeviceEntry* new_device = new DeviceEntry(device_info.type, |
| 497 device_info.id, | 486 device_info.id, |
| 498 video_capture_controller); | 487 video_capture_controller.Pass()); |
| 499 devices_.insert(new_device); | 488 devices_.insert(new_device); |
| 500 return new_device; | 489 return new_device; |
| 501 } | 490 } |
| 502 | 491 |
| 503 } // namespace content | 492 } // namespace content |
| OLD | NEW |