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

Side by Side Diff: content/renderer/media/video_capture_impl_manager.cc

Issue 2365223002: Video Capture: Allow suspension of individual devices. (Closed)
Patch Set: REBASE, and clean-ups+tests suggested by chfremer@. Created 4 years, 2 months 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
OLDNEW
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 // Implementation notes about interactions with VideoCaptureImpl. 5 // Implementation notes about interactions with VideoCaptureImpl.
6 // 6 //
7 // How is VideoCaptureImpl used: 7 // How is VideoCaptureImpl used:
8 // 8 //
9 // VideoCaptureImpl is an IO thread object while VideoCaptureImplManager 9 // VideoCaptureImpl is an IO thread object while VideoCaptureImplManager
10 // lives only on the render thread. It is only possible to access an 10 // lives only on the render thread. It is only possible to access an
11 // object of VideoCaptureImpl via a task on the IO thread. 11 // object of VideoCaptureImpl via a task on the IO thread.
12 // 12 //
13 // How is VideoCaptureImpl deleted: 13 // How is VideoCaptureImpl deleted:
14 // 14 //
15 // A task is posted to the IO thread to delete a VideoCaptureImpl. 15 // A task is posted to the IO thread to delete a VideoCaptureImpl.
16 // Immediately after that the pointer to it is dropped. This means no 16 // Immediately after that the pointer to it is dropped. This means no
17 // access to this VideoCaptureImpl object is possible on the render 17 // access to this VideoCaptureImpl object is possible on the render
18 // thread. Also note that VideoCaptureImpl does not post task to itself. 18 // thread. Also note that VideoCaptureImpl does not post task to itself.
19 // 19 //
20 // The use of Unretained: 20 // The use of Unretained:
21 // 21 //
22 // We make sure deletion is the last task on the IO thread for a 22 // We make sure deletion is the last task on the IO thread for a
23 // VideoCaptureImpl object. This allows the use of Unretained() binding. 23 // VideoCaptureImpl object. This allows the use of Unretained() binding.
24 24
25 #include "content/renderer/media/video_capture_impl_manager.h" 25 #include "content/renderer/media/video_capture_impl_manager.h"
26 26
27 #include <algorithm>
28
27 #include "base/bind.h" 29 #include "base/bind.h"
28 #include "base/bind_helpers.h" 30 #include "base/bind_helpers.h"
29 #include "base/location.h" 31 #include "base/location.h"
30 #include "base/threading/thread_task_runner_handle.h" 32 #include "base/threading/thread_task_runner_handle.h"
31 #include "content/child/child_process.h" 33 #include "content/child/child_process.h"
32 #include "content/renderer/media/video_capture_impl.h" 34 #include "content/renderer/media/video_capture_impl.h"
33 #include "content/renderer/media/video_capture_message_filter.h" 35 #include "content/renderer/media/video_capture_message_filter.h"
34 #include "media/base/bind_to_current_loop.h" 36 #include "media/base/bind_to_current_loop.h"
35 37
36 namespace content { 38 namespace content {
37 39
40 VideoCaptureImplManager::DeviceEntry::DeviceEntry()
41 : session_id(0), client_count(0), is_individually_suspended(false) {}
42
43 VideoCaptureImplManager::DeviceEntry::DeviceEntry(DeviceEntry&& other) =
44 default;
45
46 VideoCaptureImplManager::DeviceEntry&
47 VideoCaptureImplManager::DeviceEntry::operator=(DeviceEntry&& other) = default;
48
49 VideoCaptureImplManager::DeviceEntry::~DeviceEntry() {}
50
38 VideoCaptureImplManager::VideoCaptureImplManager() 51 VideoCaptureImplManager::VideoCaptureImplManager()
39 : next_client_id_(0), 52 : next_client_id_(0),
40 filter_(new VideoCaptureMessageFilter()), 53 filter_(new VideoCaptureMessageFilter()),
41 render_main_task_runner_(base::ThreadTaskRunnerHandle::Get()), 54 render_main_task_runner_(base::ThreadTaskRunnerHandle::Get()),
55 is_suspending_all_(false),
42 weak_factory_(this) {} 56 weak_factory_(this) {}
43 57
44 VideoCaptureImplManager::~VideoCaptureImplManager() { 58 VideoCaptureImplManager::~VideoCaptureImplManager() {
45 DCHECK(render_main_task_runner_->BelongsToCurrentThread()); 59 DCHECK(render_main_task_runner_->BelongsToCurrentThread());
46 if (devices_.empty()) 60 if (devices_.empty())
47 return; 61 return;
48 // Forcibly release all video capture resources. 62 // Forcibly release all video capture resources.
49 for (const auto& device : devices_) { 63 for (auto& entry : devices_) {
50 VideoCaptureImpl* const impl = device.second.second; 64 if (entry.impl) {
mcasas 2016/09/28 21:34:44 This should never happen, right? The only way |ent
miu 2016/09/28 22:35:16 Done, and removed all null checks elsewhere since
51 ChildProcess::current()->io_task_runner()->PostTask( 65 ChildProcess::current()->io_task_runner()->PostTask(
52 FROM_HERE, 66 FROM_HERE, base::Bind(&VideoCaptureImpl::DeInit,
53 base::Bind(&VideoCaptureImpl::DeInit, base::Unretained(impl))); 67 base::Unretained(entry.impl.get())));
mcasas 2016/09/28 21:34:44 Surprised to see this specific DeInit(): I would s
miu 2016/09/28 22:35:16 Yes, this is a bit weird. Though, it could be solv
54 ChildProcess::current()->io_task_runner()->DeleteSoon(FROM_HERE, impl); 68 ChildProcess::current()->io_task_runner()->DeleteSoon(
69 FROM_HERE, entry.impl.release());
70 }
55 } 71 }
56 devices_.clear(); 72 devices_.clear();
57 } 73 }
58 74
59 base::Closure VideoCaptureImplManager::UseDevice( 75 base::Closure VideoCaptureImplManager::UseDevice(
60 media::VideoCaptureSessionId id) { 76 media::VideoCaptureSessionId id) {
61 DCHECK(render_main_task_runner_->BelongsToCurrentThread()); 77 DCHECK(render_main_task_runner_->BelongsToCurrentThread());
62 VideoCaptureImpl* impl = NULL; 78 auto it = std::find_if(
63 const VideoCaptureDeviceMap::iterator it = devices_.find(id); 79 devices_.begin(), devices_.end(),
80 [id] (const DeviceEntry& entry) { return entry.session_id == id; });
64 if (it == devices_.end()) { 81 if (it == devices_.end()) {
65 impl = CreateVideoCaptureImplForTesting(id, filter_.get()); 82 devices_.push_back(DeviceEntry());
66 if (!impl) 83 it = devices_.end() - 1;
67 impl = new VideoCaptureImpl(id, filter_.get()); 84 it->session_id = id;
68 devices_[id] = std::make_pair(1, impl); 85 }
86 if (!it->impl) {
87 it->impl = CreateVideoCaptureImplForTesting(id, filter_.get());
88 if (!it->impl)
89 it->impl.reset(new VideoCaptureImpl(id, filter_.get()));
69 ChildProcess::current()->io_task_runner()->PostTask( 90 ChildProcess::current()->io_task_runner()->PostTask(
70 FROM_HERE, base::Bind(&VideoCaptureImpl::Init, base::Unretained(impl))); 91 FROM_HERE, base::Bind(&VideoCaptureImpl::Init,
71 } else { 92 base::Unretained(it->impl.get())));
72 ++it->second.first;
73 } 93 }
94 ++it->client_count;
95
96 // Design limit: When there are multiple clients, VideoCaptureImplManager
97 // would have to individually track which ones requested suspending/resuming,
98 // in order to determine whether the whole device should be suspended.
99 // Instead, handle the non-common use case of multiple clients by just
100 // resuming the suspended device, and disable suspend functionality while
101 // there are multiple clients.
102 if (it->is_individually_suspended)
103 Resume(id);
104
74 return base::Bind(&VideoCaptureImplManager::UnrefDevice, 105 return base::Bind(&VideoCaptureImplManager::UnrefDevice,
75 weak_factory_.GetWeakPtr(), id); 106 weak_factory_.GetWeakPtr(), id);
76 } 107 }
77 108
78 base::Closure VideoCaptureImplManager::StartCapture( 109 base::Closure VideoCaptureImplManager::StartCapture(
79 media::VideoCaptureSessionId id, 110 media::VideoCaptureSessionId id,
80 const media::VideoCaptureParams& params, 111 const media::VideoCaptureParams& params,
81 const VideoCaptureStateUpdateCB& state_update_cb, 112 const VideoCaptureStateUpdateCB& state_update_cb,
82 const VideoCaptureDeliverFrameCB& deliver_frame_cb) { 113 const VideoCaptureDeliverFrameCB& deliver_frame_cb) {
83 DCHECK(render_main_task_runner_->BelongsToCurrentThread()); 114 DCHECK(render_main_task_runner_->BelongsToCurrentThread());
84 const VideoCaptureDeviceMap::const_iterator it = devices_.find(id); 115 const auto it = std::find_if(
116 devices_.begin(), devices_.end(),
117 [id] (const DeviceEntry& entry) { return entry.session_id == id; });
85 DCHECK(it != devices_.end()); 118 DCHECK(it != devices_.end());
86 VideoCaptureImpl* const impl = it->second.second;
87 119
88 // This ID is used to identify a client of VideoCaptureImpl. 120 // This ID is used to identify a client of VideoCaptureImpl.
89 const int client_id = ++next_client_id_; 121 const int client_id = ++next_client_id_;
90 122
91 ChildProcess::current()->io_task_runner()->PostTask( 123 ChildProcess::current()->io_task_runner()->PostTask(
92 FROM_HERE, 124 FROM_HERE,
93 base::Bind(&VideoCaptureImpl::StartCapture, base::Unretained(impl), 125 base::Bind(&VideoCaptureImpl::StartCapture,
94 client_id, params, state_update_cb, deliver_frame_cb)); 126 base::Unretained(it->impl.get()), client_id, params,
127 state_update_cb, deliver_frame_cb));
95 return base::Bind(&VideoCaptureImplManager::StopCapture, 128 return base::Bind(&VideoCaptureImplManager::StopCapture,
96 weak_factory_.GetWeakPtr(), client_id, id); 129 weak_factory_.GetWeakPtr(), client_id, id);
97 } 130 }
98 131
99 void VideoCaptureImplManager::RequestRefreshFrame( 132 void VideoCaptureImplManager::RequestRefreshFrame(
100 media::VideoCaptureSessionId id) { 133 media::VideoCaptureSessionId id) {
101 DCHECK(render_main_task_runner_->BelongsToCurrentThread()); 134 DCHECK(render_main_task_runner_->BelongsToCurrentThread());
102 const VideoCaptureDeviceMap::const_iterator it = devices_.find(id); 135 const auto it = std::find_if(
136 devices_.begin(), devices_.end(),
137 [id] (const DeviceEntry& entry) { return entry.session_id == id; });
103 DCHECK(it != devices_.end()); 138 DCHECK(it != devices_.end());
104 VideoCaptureImpl* const impl = it->second.second;
105 ChildProcess::current()->io_task_runner()->PostTask( 139 ChildProcess::current()->io_task_runner()->PostTask(
106 FROM_HERE, 140 FROM_HERE,
107 base::Bind(&VideoCaptureImpl::RequestRefreshFrame, 141 base::Bind(&VideoCaptureImpl::RequestRefreshFrame,
108 base::Unretained(impl))); 142 base::Unretained(it->impl.get())));
143 }
144
145 void VideoCaptureImplManager::Suspend(media::VideoCaptureSessionId id) {
146 DCHECK(render_main_task_runner_->BelongsToCurrentThread());
147 const auto it = std::find_if(
148 devices_.begin(), devices_.end(),
149 [id] (const DeviceEntry& entry) { return entry.session_id == id; });
150 DCHECK(it != devices_.end());
151 if (it->is_individually_suspended)
152 return; // Device has already been individually suspended.
153 if (it->client_count > 1)
154 return; // Punt when there is >1 client (see comments in UseDevice()).
155 it->is_individually_suspended = true;
156 if (is_suspending_all_)
157 return; // Device should already be suspended.
158 ChildProcess::current()->io_task_runner()->PostTask(
159 FROM_HERE, base::Bind(&VideoCaptureImpl::SuspendCapture,
160 base::Unretained(it->impl.get()), true));
161 }
162
163 void VideoCaptureImplManager::Resume(media::VideoCaptureSessionId id) {
164 DCHECK(render_main_task_runner_->BelongsToCurrentThread());
165 const auto it = std::find_if(
166 devices_.begin(), devices_.end(),
167 [id] (const DeviceEntry& entry) { return entry.session_id == id; });
168 DCHECK(it != devices_.end());
169 if (!it->is_individually_suspended)
170 return; // Device was not individually suspended.
171 it->is_individually_suspended = false;
172 if (is_suspending_all_)
173 return; // Device must remain suspended until all are resumed.
174 ChildProcess::current()->io_task_runner()->PostTask(
175 FROM_HERE, base::Bind(&VideoCaptureImpl::SuspendCapture,
176 base::Unretained(it->impl.get()), false));
109 } 177 }
110 178
111 void VideoCaptureImplManager::GetDeviceSupportedFormats( 179 void VideoCaptureImplManager::GetDeviceSupportedFormats(
112 media::VideoCaptureSessionId id, 180 media::VideoCaptureSessionId id,
113 const VideoCaptureDeviceFormatsCB& callback) { 181 const VideoCaptureDeviceFormatsCB& callback) {
114 DCHECK(render_main_task_runner_->BelongsToCurrentThread()); 182 DCHECK(render_main_task_runner_->BelongsToCurrentThread());
115 const VideoCaptureDeviceMap::const_iterator it = devices_.find(id); 183 const auto it = std::find_if(
184 devices_.begin(), devices_.end(),
185 [id] (const DeviceEntry& entry) { return entry.session_id == id; });
116 DCHECK(it != devices_.end()); 186 DCHECK(it != devices_.end());
117 VideoCaptureImpl* const impl = it->second.second;
118 ChildProcess::current()->io_task_runner()->PostTask( 187 ChildProcess::current()->io_task_runner()->PostTask(
119 FROM_HERE, base::Bind(&VideoCaptureImpl::GetDeviceSupportedFormats, 188 FROM_HERE, base::Bind(&VideoCaptureImpl::GetDeviceSupportedFormats,
120 base::Unretained(impl), callback)); 189 base::Unretained(it->impl.get()), callback));
121 } 190 }
122 191
123 void VideoCaptureImplManager::GetDeviceFormatsInUse( 192 void VideoCaptureImplManager::GetDeviceFormatsInUse(
124 media::VideoCaptureSessionId id, 193 media::VideoCaptureSessionId id,
125 const VideoCaptureDeviceFormatsCB& callback) { 194 const VideoCaptureDeviceFormatsCB& callback) {
126 DCHECK(render_main_task_runner_->BelongsToCurrentThread()); 195 DCHECK(render_main_task_runner_->BelongsToCurrentThread());
127 const VideoCaptureDeviceMap::const_iterator it = devices_.find(id); 196 const auto it = std::find_if(
197 devices_.begin(), devices_.end(),
198 [id] (const DeviceEntry& entry) { return entry.session_id == id; });
128 DCHECK(it != devices_.end()); 199 DCHECK(it != devices_.end());
129 VideoCaptureImpl* const impl = it->second.second;
130 ChildProcess::current()->io_task_runner()->PostTask( 200 ChildProcess::current()->io_task_runner()->PostTask(
131 FROM_HERE, base::Bind(&VideoCaptureImpl::GetDeviceFormatsInUse, 201 FROM_HERE, base::Bind(&VideoCaptureImpl::GetDeviceFormatsInUse,
132 base::Unretained(impl), callback)); 202 base::Unretained(it->impl.get()), callback));
133 } 203 }
134 204
135 VideoCaptureImpl* 205 std::unique_ptr<VideoCaptureImpl>
136 VideoCaptureImplManager::CreateVideoCaptureImplForTesting( 206 VideoCaptureImplManager::CreateVideoCaptureImplForTesting(
137 media::VideoCaptureSessionId id, 207 media::VideoCaptureSessionId id,
138 VideoCaptureMessageFilter* filter) const { 208 VideoCaptureMessageFilter* filter) const {
139 return NULL; 209 return std::unique_ptr<VideoCaptureImpl>();
140 } 210 }
141 211
142 void VideoCaptureImplManager::StopCapture(int client_id, 212 void VideoCaptureImplManager::StopCapture(int client_id,
143 media::VideoCaptureSessionId id) { 213 media::VideoCaptureSessionId id) {
144 DCHECK(render_main_task_runner_->BelongsToCurrentThread()); 214 DCHECK(render_main_task_runner_->BelongsToCurrentThread());
145 const VideoCaptureDeviceMap::const_iterator it = devices_.find(id); 215 const auto it = std::find_if(
216 devices_.begin(), devices_.end(),
217 [id] (const DeviceEntry& entry) { return entry.session_id == id; });
146 DCHECK(it != devices_.end()); 218 DCHECK(it != devices_.end());
147 VideoCaptureImpl* const impl = it->second.second;
148 ChildProcess::current()->io_task_runner()->PostTask( 219 ChildProcess::current()->io_task_runner()->PostTask(
149 FROM_HERE, base::Bind(&VideoCaptureImpl::StopCapture, 220 FROM_HERE, base::Bind(&VideoCaptureImpl::StopCapture,
150 base::Unretained(impl), client_id)); 221 base::Unretained(it->impl.get()), client_id));
151 } 222 }
152 223
153 void VideoCaptureImplManager::UnrefDevice( 224 void VideoCaptureImplManager::UnrefDevice(
154 media::VideoCaptureSessionId id) { 225 media::VideoCaptureSessionId id) {
155 DCHECK(render_main_task_runner_->BelongsToCurrentThread()); 226 DCHECK(render_main_task_runner_->BelongsToCurrentThread());
156 const VideoCaptureDeviceMap::iterator it = devices_.find(id); 227 const auto it = std::find_if(
228 devices_.begin(), devices_.end(),
229 [id] (const DeviceEntry& entry) { return entry.session_id == id; });
157 DCHECK(it != devices_.end()); 230 DCHECK(it != devices_.end());
158 VideoCaptureImpl* const impl = it->second.second; 231 DCHECK_GT(it->client_count, 0);
159 232 --it->client_count;
160 // Unref and destroy on the IO thread if there's no more client. 233 if (it->client_count == 0) {
161 DCHECK(it->second.first); 234 if (it->impl) {
mcasas 2016/09/28 21:34:44 Early return?
miu 2016/09/28 22:35:16 Done.
162 --it->second.first; 235 ChildProcess::current()->io_task_runner()->PostTask(
163 if (!it->second.first) { 236 FROM_HERE, base::Bind(&VideoCaptureImpl::DeInit,
164 devices_.erase(id); 237 base::Unretained(it->impl.get())));
165 ChildProcess::current()->io_task_runner()->PostTask( 238 ChildProcess::current()->io_task_runner()->DeleteSoon(
166 FROM_HERE, 239 FROM_HERE, it->impl.release());
167 base::Bind(&VideoCaptureImpl::DeInit, base::Unretained(impl))); 240 }
168 ChildProcess::current()->io_task_runner()->DeleteSoon(FROM_HERE, impl); 241 devices_.erase(it);
169 } 242 }
170 } 243 }
171 244
172 void VideoCaptureImplManager::SuspendDevices(bool suspend) { 245 void VideoCaptureImplManager::SuspendDevices(bool suspend) {
173 DCHECK(render_main_task_runner_->BelongsToCurrentThread()); 246 DCHECK(render_main_task_runner_->BelongsToCurrentThread());
174 for (const auto& device : devices_) { 247 if (is_suspending_all_ == suspend)
175 VideoCaptureImpl* const impl = device.second.second; 248 return;
249 is_suspending_all_ = suspend;
250 for (auto& entry : devices_) {
251 if (entry.is_individually_suspended)
252 continue; // Either: 1) Already suspended; or 2) Should not be resumed.
176 ChildProcess::current()->io_task_runner()->PostTask( 253 ChildProcess::current()->io_task_runner()->PostTask(
177 FROM_HERE, base::Bind(&VideoCaptureImpl::SuspendCapture, 254 FROM_HERE, base::Bind(&VideoCaptureImpl::SuspendCapture,
178 base::Unretained(impl), suspend)); 255 base::Unretained(entry.impl.get()), suspend));
179 } 256 }
180 } 257 }
181 258
182 } // namespace content 259 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698