Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(508)

Side by Side Diff: content/browser/renderer_host/media/video_capture_controller.cc

Issue 8304017: enable video capture to support sharing across multiple renderer processes (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 9 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 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 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_controller.h" 5 #include "content/browser/renderer_host/media/video_capture_controller.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/stl_util.h" 8 #include "base/stl_util.h"
9 #include "content/browser/browser_thread.h" 9 #include "content/browser/browser_thread.h"
10 #include "content/browser/renderer_host/media/media_stream_manager.h" 10 #include "content/browser/renderer_host/media/media_stream_manager.h"
11 #include "content/browser/renderer_host/media/video_capture_manager.h" 11 #include "content/browser/renderer_host/media/video_capture_manager.h"
12 #include "media/base/yuv_convert.h" 12 #include "media/base/yuv_convert.h"
13 13
14 // The number of TransportDIBs VideoCaptureController allocate. 14 // The number of DIBs VideoCaptureController allocate.
15 static const size_t kNoOfDIBS = 3; 15 static const size_t kNoOfDIBS = 3;
16 16
17 struct VideoCaptureController::ControllerClient {
18 ControllerClient(
19 const VideoCaptureControllerID& id,
20 VideoCaptureControllerEventHandler* handler,
21 base::ProcessHandle render_process,
22 const media::VideoCaptureParams& params)
23 : controller_id(id),
24 event_handler(handler),
25 render_process_handle(render_process),
26 parameters(params),
27 report_ready_to_delete(false) {
28 }
29
30 ~ControllerClient() {}
31
32 // ID used for identifying this object.
33 VideoCaptureControllerID controller_id;
34 VideoCaptureControllerEventHandler* event_handler;
35 // Handle to the render process that will receive the DIBs.
36 base::ProcessHandle render_process_handle;
37 media::VideoCaptureParams parameters;
38 // Buffers used by this client.
39 std::list<int> buffers;
40 bool report_ready_to_delete;
41 };
42
17 VideoCaptureController::VideoCaptureController( 43 VideoCaptureController::VideoCaptureController(
18 const VideoCaptureControllerID& id,
19 base::ProcessHandle render_process,
20 VideoCaptureControllerEventHandler* event_handler,
21 media_stream::VideoCaptureManager* video_capture_manager) 44 media_stream::VideoCaptureManager* video_capture_manager)
22 : render_handle_(render_process), 45 : frame_info_available_(false),
23 report_ready_to_delete_(false), 46 video_capture_manager_(video_capture_manager),
24 event_handler_(event_handler), 47 device_in_use_(false),
25 id_(id), 48 state_(media::VideoCapture::kStopped) {
26 video_capture_manager_(video_capture_manager) { 49 memset(&current_params_, 0, sizeof(current_params_));
27 memset(&params_, 0, sizeof(params_));
28 } 50 }
29 51
30 VideoCaptureController::~VideoCaptureController() { 52 VideoCaptureController::~VideoCaptureController() {
31 // Delete all TransportDIBs. 53 // Delete all DIBs.
32 STLDeleteContainerPairSecondPointers(owned_dibs_.begin(), 54 STLDeleteContainerPairSecondPointers(owned_dibs_.begin(),
33 owned_dibs_.end()); 55 owned_dibs_.end());
56 STLDeleteContainerPointers(controller_clients_.begin(),
57 controller_clients_.end());
58 STLDeleteContainerPointers(pending_clients_.begin(),
59 pending_clients_.end());
34 } 60 }
35 61
36 void VideoCaptureController::StartCapture( 62 void VideoCaptureController::StartCapture(
63 const VideoCaptureControllerID& id,
64 VideoCaptureControllerEventHandler* event_handler,
65 base::ProcessHandle render_process,
37 const media::VideoCaptureParams& params) { 66 const media::VideoCaptureParams& params) {
38 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 67 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
39 68 // Signal error in case device is already in error state.
40 params_ = params; 69 if (state_ == media::VideoCapture::kError) {
70 event_handler->OnError(id);
71 return;
72 }
73
74 // Do nothing if this client has called StartCapture before.
75 if (FindClient(id, event_handler, controller_clients_) !=
76 controller_clients_.end() ||
77 FindClient(id, event_handler, pending_clients_) !=
78 pending_clients_.end())
79 return;
80
81 ControllerClient* client = new ControllerClient(id, event_handler,
82 render_process, params);
83 // In case capture has been started, need to check different condtions.
84 if (state_ == media::VideoCapture::kStarted) {
85 // This client has higher resolution than what is currently requested.
86 // Need restart capturing.
87 if (params.width > current_params_.width ||
88 params.height > current_params_.height) {
89 video_capture_manager_->Stop(current_params_.session_id,
90 base::Bind(&VideoCaptureController::OnDeviceStopped, this));
91 frame_info_available_ = false;
92 state_ = media::VideoCapture::kStopping;
93 pending_clients_.push_back(client);
94 return;
95 }
96
97 // This client's resolution is no larger than what's currently requested.
98 // When frame_info has been returned by device, send them to client.
99 if (frame_info_available_) {
100 SendFrameInfoAndBuffers(client);
101 }
102 controller_clients_.push_back(client);
103 return;
104 }
105
106 // In case the device is in the middle of stopping, put the client in
107 // pending queue.
108 if (state_ == media::VideoCapture::kStopping) {
109 pending_clients_.push_back(client);
110 return;
111 }
112
113 // Fresh start.
114 controller_clients_.push_back(client);
115 current_params_ = params;
41 // Order the manager to start the actual capture. 116 // Order the manager to start the actual capture.
42 video_capture_manager_->Start(params, this); 117 video_capture_manager_->Start(params, this);
43 } 118 state_ = media::VideoCapture::kStarted;
44 119 }
45 void VideoCaptureController::StopCapture(base::Closure stopped_cb) { 120
121 void VideoCaptureController::StopCapture(
122 const VideoCaptureControllerID& id,
123 VideoCaptureControllerEventHandler* event_handler,
124 bool force_buffer_return) {
46 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 125 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
47 126
48 video_capture_manager_->Stop(params_.session_id, 127 ControllerClients::iterator cit;
49 base::Bind(&VideoCaptureController::OnDeviceStopped, this, stopped_cb)); 128 cit = FindClient(id, event_handler, pending_clients_);
50 } 129 // If the client is still in pending queue, just remove it.
51 130 if (cit != pending_clients_.end()) {
52 void VideoCaptureController::ReturnBuffer(int buffer_id) { 131 (*cit)->event_handler->OnReadyToDelete((*cit)->controller_id);
132 pending_clients_.erase(cit);
133 return;
134 }
135
136 cit = FindClient(id, event_handler, controller_clients_);
137 // The client should have called StartCapture.
138 DCHECK(cit != controller_clients_.end());
139
140 if (force_buffer_return) {
141 // The client requests to return buffers which means it can't return
142 // buffers normally. After buffers are returned, client is free to
143 // delete itself. No need to call OnReadyToDelete.
144
145 // Return all buffers held by the clients.
146 for (std::list<int>::iterator bit = (*cit)->buffers.begin();
147 bit != (*cit)->buffers.end(); ++bit) {
148 int buffer_id = *bit;
149 ClientSideDIBCount::iterator dit = client_side_dib_count_.find(buffer_id);
150 if (dit == client_side_dib_count_.end())
151 continue;
152
153 if (--dit->second > 0)
154 continue;
155
156 // Now this |buffer_id| is not used by any client.
157 client_side_dib_count_.erase(buffer_id);
158 {
159 base::AutoLock lock(lock_);
160 free_dibs_.push_back(buffer_id);
161 }
162 }
163 (*cit)->buffers.clear();
164 } else {
165 // Normal way to stop capture.
166 if ((*cit)->buffers.size() > 0) {
167 // There are still some buffers held by the client.
168 (*cit)->report_ready_to_delete = true;
169 return;
170 }
171 // No buffer is held by the client. Ready to delete.
172 (*cit)->event_handler->OnReadyToDelete((*cit)->controller_id);
173 }
174
175 delete (*cit);
176 controller_clients_.erase(cit);
177
178 // No more clients. Stop device.
179 if (controller_clients_.size() == 0) {
180 video_capture_manager_->Stop(current_params_.session_id,
181 base::Bind(&VideoCaptureController::OnDeviceStopped, this));
182 frame_info_available_ = false;
183 state_ = media::VideoCapture::kStopping;
184 }
185 }
186
187 void VideoCaptureController::ReturnBuffer(
188 const VideoCaptureControllerID& id,
189 VideoCaptureControllerEventHandler* event_handler,
190 int buffer_id) {
53 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 191 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
54 192
55 bool ready_to_delete; 193 ControllerClients::iterator cit = FindClient(id, event_handler,
194 controller_clients_);
195 ClientSideDIBCount::iterator dit = client_side_dib_count_.find(buffer_id);
196 // If this buffer is not held by this client, or this client doesn't exist
197 // in controller, do nothing.
198 if (cit == controller_clients_.end() || dit == client_side_dib_count_.end())
199 return;
200
201 (*cit)->buffers.remove(buffer_id);
202 // If this client has called StopCapture and doesn't hold any buffer after
203 // after this return, ready to delete this client.
204 if ((*cit)->report_ready_to_delete && (*cit)->buffers.size() == 0) {
205 (*cit)->event_handler->OnReadyToDelete((*cit)->controller_id);
206 delete (*cit);
207 controller_clients_.erase(cit);
208 }
209 if (--dit->second > 0)
210 return;
211
212 // Now this |buffer_id| is not used by any client.
213 client_side_dib_count_.erase(buffer_id);
56 { 214 {
57 base::AutoLock lock(lock_); 215 base::AutoLock lock(lock_);
58 free_dibs_.push_back(buffer_id); 216 free_dibs_.push_back(buffer_id);
59 ready_to_delete = (free_dibs_.size() == owned_dibs_.size()) && 217 }
60 report_ready_to_delete_; 218
61 } 219 // When all buffers have been returned by clients and device has been
62 if (ready_to_delete) { 220 // called to stop, check if restart is needed. This could happen when
63 event_handler_->OnReadyToDelete(id_); 221 // some clients call StopCapture before returning all buffers.
64 } 222 if (!ClientHasDIB() && state_ == media::VideoCapture::kStopping) {
65 } 223 PostStopping();
66 224 }
225 }
226
67 /////////////////////////////////////////////////////////////////////////////// 227 ///////////////////////////////////////////////////////////////////////////////
68 // Implements VideoCaptureDevice::EventHandler. 228 // Implements VideoCaptureDevice::EventHandler.
69 // OnIncomingCapturedFrame is called the thread running the capture device. 229 // OnIncomingCapturedFrame is called the thread running the capture device.
70 // I.e.- DirectShow thread on windows and v4l2_thread on Linux. 230 // I.e.- DirectShow thread on windows and v4l2_thread on Linux.
71 void VideoCaptureController::OnIncomingCapturedFrame(const uint8* data, 231 void VideoCaptureController::OnIncomingCapturedFrame(const uint8* data,
72 int length, 232 int length,
73 base::Time timestamp) { 233 base::Time timestamp) {
74 int buffer_id = 0; 234 int buffer_id = 0;
75 base::SharedMemory* dib = NULL; 235 base::SharedMemory* dib = NULL;
76 // Check if there is a TransportDIB to fill. 236 // Check if there is a DIB to fill.
77 bool buffer_exist = false; 237 bool buffer_exist = false;
78 { 238 {
79 base::AutoLock lock(lock_); 239 base::AutoLock lock(lock_);
80 if (!report_ready_to_delete_ && free_dibs_.size() > 0) { 240 if (free_dibs_.size() > 0) {
81 buffer_id = free_dibs_.front(); 241 buffer_id = free_dibs_.front();
82 free_dibs_.pop_front(); 242 free_dibs_.pop_front();
83 DIBMap::iterator it = owned_dibs_.find(buffer_id); 243 DIBMap::iterator it = owned_dibs_.find(buffer_id);
84 if (it != owned_dibs_.end()) { 244 if (it != owned_dibs_.end()) {
85 dib = it->second; 245 dib = it->second;
86 buffer_exist = true; 246 buffer_exist = true;
87 } 247 }
88 } 248 }
89 } 249 }
90 250
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after
143 uint8* vplane = uplane + (frame_info_.width * frame_info_.height) / 4; 303 uint8* vplane = uplane + (frame_info_.width * frame_info_.height) / 4;
144 media::ConvertRGB32ToYUV(data, yplane, uplane, vplane, frame_info_.width, 304 media::ConvertRGB32ToYUV(data, yplane, uplane, vplane, frame_info_.width,
145 frame_info_.height, frame_info_.width * 4, 305 frame_info_.height, frame_info_.width * 4,
146 frame_info_.width, frame_info_.width / 2); 306 frame_info_.width, frame_info_.width / 2);
147 break; 307 break;
148 } 308 }
149 default: 309 default:
150 NOTREACHED(); 310 NOTREACHED();
151 } 311 }
152 312
153 event_handler_->OnBufferReady(id_, buffer_id, timestamp); 313 BrowserThread::PostTask(BrowserThread::IO,
314 FROM_HERE,
315 base::Bind(&VideoCaptureController::DoIncomingCapturedFrameOnIOThread,
316 this, buffer_id, timestamp));
154 } 317 }
155 318
156 void VideoCaptureController::OnError() { 319 void VideoCaptureController::OnError() {
157 event_handler_->OnError(id_); 320 video_capture_manager_->Error(current_params_.session_id);
158 video_capture_manager_->Error(params_.session_id); 321 BrowserThread::PostTask(BrowserThread::IO,
322 FROM_HERE,
323 base::Bind(&VideoCaptureController::DoErrorOnIOThread, this));
159 } 324 }
160 325
161 void VideoCaptureController::OnFrameInfo( 326 void VideoCaptureController::OnFrameInfo(
162 const media::VideoCaptureDevice::Capability& info) { 327 const media::VideoCaptureDevice::Capability& info) {
328 BrowserThread::PostTask(BrowserThread::IO,
329 FROM_HERE,
330 base::Bind(&VideoCaptureController::DoFrameInfoOnIOThread,
331 this, info));
332 }
333
334 void VideoCaptureController::DoIncomingCapturedFrameOnIOThread(
335 int buffer_id, base::Time timestamp) {
336 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
337
338 if (state_ != media::VideoCapture::kStarted) {
339 base::AutoLock lock(lock_);
340 free_dibs_.push_back(buffer_id);
341 return;
342 }
343
344 int count = 0;
345 for (ControllerClients::iterator cit = controller_clients_.begin();
346 cit != controller_clients_.end(); cit++) {
347 if ((*cit)->report_ready_to_delete)
348 continue;
349
350 (*cit)->event_handler->OnBufferReady((*cit)->controller_id,
351 buffer_id, timestamp);
352 (*cit)->buffers.push_back(buffer_id);
353 count++;
354 }
355 if (count > 0) {
356 client_side_dib_count_[buffer_id] = count;
357 } else {
358 base::AutoLock lock(lock_);
359 free_dibs_.push_back(buffer_id);
360 }
361 }
362
363 void VideoCaptureController::DoErrorOnIOThread() {
364 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
365 state_ = media::VideoCapture::kError;
366 ControllerClients::iterator cit;
367 for (cit = controller_clients_.begin();
368 cit != controller_clients_.end(); cit++) {
369 (*cit)->event_handler->OnError((*cit)->controller_id);
370 }
371 for (cit = pending_clients_.begin();
372 cit != pending_clients_.end(); cit++) {
373 (*cit)->event_handler->OnError((*cit)->controller_id);
374 }
375 }
376
377 void VideoCaptureController::DoFrameInfoOnIOThread(
378 const media::VideoCaptureDevice::Capability info) {
379 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
163 DCHECK(owned_dibs_.empty()); 380 DCHECK(owned_dibs_.empty());
381
164 bool frames_created = true; 382 bool frames_created = true;
165 const size_t needed_size = (info.width * info.height * 3) / 2; 383 const size_t needed_size = (info.width * info.height * 3) / 2;
384 base::AutoLock lock(lock_);
166 for (size_t i = 1; i <= kNoOfDIBS; ++i) { 385 for (size_t i = 1; i <= kNoOfDIBS; ++i) {
167 base::SharedMemory* shared_memory = new base::SharedMemory(); 386 base::SharedMemory* shared_memory = new base::SharedMemory();
168 if (!shared_memory->CreateAndMapAnonymous(needed_size)) { 387 if (!shared_memory->CreateAndMapAnonymous(needed_size)) {
169 frames_created = false; 388 frames_created = false;
170 break; 389 break;
171 } 390 }
172 base::SharedMemoryHandle remote_handle;
173 shared_memory->ShareToProcess(render_handle_, &remote_handle);
174
175 base::AutoLock lock(lock_);
176 owned_dibs_.insert(std::make_pair(i, shared_memory)); 391 owned_dibs_.insert(std::make_pair(i, shared_memory));
177 free_dibs_.push_back(i); 392 free_dibs_.push_back(i);
178 event_handler_->OnBufferCreated(id_, remote_handle, 393 }
179 static_cast<int>(needed_size), 394 // Check whether all DIBs were created successfully.
180 static_cast<int>(i)); 395 if (!frames_created) {
396 state_ = media::VideoCapture::kError;
397 for (ControllerClients::iterator cit = controller_clients_.begin();
398 cit != controller_clients_.end(); cit++) {
399 (*cit)->event_handler->OnError((*cit)->controller_id);
400 }
401 return;
181 } 402 }
182 frame_info_= info; 403 frame_info_= info;
183 404 frame_info_available_ = true;
184 // Check that all DIBs where created successfully. 405
185 if (!frames_created) { 406 for (ControllerClients::iterator cit = controller_clients_.begin();
186 event_handler_->OnError(id_); 407 cit != controller_clients_.end(); cit++) {
187 } 408 SendFrameInfoAndBuffersWithLock((*cit), static_cast<int>(needed_size));
188 event_handler_->OnFrameInfo(id_, info.width, info.height, info.frame_rate); 409 }
410 }
411
412 void VideoCaptureController::SendFrameInfoAndBuffers(ControllerClient* client) {
413 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
414 DCHECK(frame_info_available_);
415 const size_t needed_size = (frame_info_.width * frame_info_.height * 3) / 2;
416 base::AutoLock lock(lock_);
417 SendFrameInfoAndBuffersWithLock(client, static_cast<int>(needed_size));
418 }
419
420 void VideoCaptureController::SendFrameInfoAndBuffersWithLock(
421 ControllerClient* client, int needed_size) {
422 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
423 DCHECK(frame_info_available_);
424 client->event_handler->OnFrameInfo(client->controller_id,
425 frame_info_.width, frame_info_.height,
426 frame_info_.frame_rate);
427 for (DIBMap::iterator dit = owned_dibs_.begin();
428 dit != owned_dibs_.end(); dit++) {
429 base::SharedMemory* shared_memory = dit->second;
430 int index = dit->first;
431 base::SharedMemoryHandle remote_handle;
432 shared_memory->ShareToProcess(client->render_process_handle,
433 &remote_handle);
434 client->event_handler->OnBufferCreated(client->controller_id,
435 remote_handle,
436 needed_size,
437 index);
438 }
439 }
440
441 // This function is called when all buffers have been returned to controller,
442 // or when device is stopped. It decides whether the device needs to be
443 // restarted.
444 void VideoCaptureController::PostStopping() {
445 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
446 DCHECK_EQ(state_, media::VideoCapture::kStopping);
447
448 // When clients still have some buffers, or device has not been stopped yet,
449 // do nothing.
450 if (ClientHasDIB() || device_in_use_)
451 return;
452
453 // It's safe to free all DIB's on IO thread since device won't send
454 // buffer over.
455 free_dibs_.clear();
456 STLDeleteValues(&owned_dibs_);
457
458 // No more client. Therefore the controller is stopped.
459 if (controller_clients_.size() + pending_clients_.size() == 0) {
460 state_ = media::VideoCapture::kStopped;
461 return;
462 }
463
464 // Restart the device.
465 current_params_.width = 0;
466 current_params_.height = 0;
467 ControllerClients::iterator cit;
468 for (cit = controller_clients_.begin();
469 cit != controller_clients_.end(); cit++) {
470 if (current_params_.width < (*cit)->parameters.width)
471 current_params_.width = (*cit)->parameters.width;
472 if (current_params_.height < (*cit)->parameters.height)
473 current_params_.height = (*cit)->parameters.height;
474 }
475 for (cit = pending_clients_.begin();
476 cit != pending_clients_.end(); ) {
477 if (current_params_.width < (*cit)->parameters.width)
478 current_params_.width = (*cit)->parameters.width;
479 if (current_params_.height < (*cit)->parameters.height)
480 current_params_.height = (*cit)->parameters.height;
481 controller_clients_.push_back((*cit));
482 pending_clients_.erase(cit++);
483 }
484 // Request the manager to start the actual capture.
485 video_capture_manager_->Start(current_params_, this);
486 state_ = media::VideoCapture::kStarted;
487 }
488
489 bool VideoCaptureController::ClientHasDIB() {
490 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
491 for (ClientSideDIBCount::iterator dit = client_side_dib_count_.begin();
492 dit != client_side_dib_count_.end(); dit++) {
493 if (dit->second > 0)
494 return true;
495 }
496 return false;
497 }
498
499 VideoCaptureController::ControllerClients::iterator
500 VideoCaptureController::FindClient(
501 const VideoCaptureControllerID& id,
502 VideoCaptureControllerEventHandler* handler,
503 ControllerClients& clients) {
504 for (ControllerClients::iterator cit = clients.begin();
505 cit != clients.end(); cit++) {
506 if ((*cit)->controller_id == id && (*cit)->event_handler == handler) {
507 return cit;
508 }
509 }
510 return clients.end();
189 } 511 }
190 512
191 /////////////////////////////////////////////////////////////////////////////// 513 ///////////////////////////////////////////////////////////////////////////////
192 // Called by VideoCaptureManager when a device have been stopped. 514 // Called by VideoCaptureManager when a device have been stopped.
193 // This will report to the event handler that this object is ready to be deleted 515 void VideoCaptureController::OnDeviceStopped() {
194 // if all DIBS have been returned. 516 BrowserThread::PostTask(BrowserThread::IO,
195 void VideoCaptureController::OnDeviceStopped(base::Closure stopped_cb) { 517 FROM_HERE,
196 bool ready_to_delete_now; 518 base::Bind(&VideoCaptureController::DoDeviceStoppedOnIOThread, this));
197 519 }
198 { 520
199 base::AutoLock lock(lock_); 521 void VideoCaptureController::DoDeviceStoppedOnIOThread() {
200 // Set flag to indicate we need to report when all DIBs have been returned. 522 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
201 report_ready_to_delete_ = true; 523 device_in_use_ = false;
202 ready_to_delete_now = (free_dibs_.size() == owned_dibs_.size()); 524 if (state_ == media::VideoCapture::kStopping) {
203 } 525 PostStopping();
204 526 }
205 if (ready_to_delete_now) { 527 }
206 event_handler_->OnReadyToDelete(id_); 528
207 }
208
209 if (!stopped_cb.is_null())
210 stopped_cb.Run();
211 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698