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

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

Issue 2365223002: Video Capture: Allow suspension of individual devices. (Closed)
Patch Set: Style tweaks, per mcasas's comments. 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 struct VideoCaptureImplManager::DeviceEntry {
41 media::VideoCaptureSessionId session_id;
42
43 // To be used and destroyed only on the IO thread.
44 std::unique_ptr<VideoCaptureImpl> impl;
45
46 // Number of clients using |impl|.
47 int client_count;
48
49 // This is set to true if this device is being suspended, via
50 // VideoCaptureImplManager::Suspend().
51 // See also: VideoCaptureImplManager::is_suspending_all_.
52 bool is_individually_suspended;
53
54 DeviceEntry()
55 : session_id(0), client_count(0), is_individually_suspended(false) {}
56 DeviceEntry(DeviceEntry&& other) = default;
57 DeviceEntry& operator=(DeviceEntry&& other) = default;
58 ~DeviceEntry() = default;
59 };
60
38 VideoCaptureImplManager::VideoCaptureImplManager() 61 VideoCaptureImplManager::VideoCaptureImplManager()
39 : next_client_id_(0), 62 : next_client_id_(0),
40 filter_(new VideoCaptureMessageFilter()), 63 filter_(new VideoCaptureMessageFilter()),
41 render_main_task_runner_(base::ThreadTaskRunnerHandle::Get()), 64 render_main_task_runner_(base::ThreadTaskRunnerHandle::Get()),
65 is_suspending_all_(false),
42 weak_factory_(this) {} 66 weak_factory_(this) {}
43 67
44 VideoCaptureImplManager::~VideoCaptureImplManager() { 68 VideoCaptureImplManager::~VideoCaptureImplManager() {
45 DCHECK(render_main_task_runner_->BelongsToCurrentThread()); 69 DCHECK(render_main_task_runner_->BelongsToCurrentThread());
46 if (devices_.empty()) 70 if (devices_.empty())
47 return; 71 return;
48 // Forcibly release all video capture resources. 72 // Forcibly release all video capture resources.
49 for (const auto& device : devices_) { 73 for (auto& entry : devices_) {
50 VideoCaptureImpl* const impl = device.second.second;
51 ChildProcess::current()->io_task_runner()->PostTask( 74 ChildProcess::current()->io_task_runner()->PostTask(
52 FROM_HERE, 75 FROM_HERE, base::Bind(&VideoCaptureImpl::DeInit,
53 base::Bind(&VideoCaptureImpl::DeInit, base::Unretained(impl))); 76 base::Unretained(entry.impl.get())));
54 ChildProcess::current()->io_task_runner()->DeleteSoon(FROM_HERE, impl); 77 ChildProcess::current()->io_task_runner()->DeleteSoon(
78 FROM_HERE, entry.impl.release());
55 } 79 }
56 devices_.clear(); 80 devices_.clear();
57 } 81 }
58 82
59 base::Closure VideoCaptureImplManager::UseDevice( 83 base::Closure VideoCaptureImplManager::UseDevice(
60 media::VideoCaptureSessionId id) { 84 media::VideoCaptureSessionId id) {
61 DCHECK(render_main_task_runner_->BelongsToCurrentThread()); 85 DCHECK(render_main_task_runner_->BelongsToCurrentThread());
62 VideoCaptureImpl* impl = NULL; 86 auto it = std::find_if(
63 const VideoCaptureDeviceMap::iterator it = devices_.find(id); 87 devices_.begin(), devices_.end(),
88 [id] (const DeviceEntry& entry) { return entry.session_id == id; });
64 if (it == devices_.end()) { 89 if (it == devices_.end()) {
65 impl = CreateVideoCaptureImplForTesting(id, filter_.get()); 90 devices_.push_back(DeviceEntry());
66 if (!impl) 91 it = devices_.end() - 1;
67 impl = new VideoCaptureImpl(id, filter_.get()); 92 it->session_id = id;
68 devices_[id] = std::make_pair(1, impl); 93 it->impl = CreateVideoCaptureImplForTesting(id, filter_.get());
94 if (!it->impl)
95 it->impl.reset(new VideoCaptureImpl(id, filter_.get()));
69 ChildProcess::current()->io_task_runner()->PostTask( 96 ChildProcess::current()->io_task_runner()->PostTask(
70 FROM_HERE, base::Bind(&VideoCaptureImpl::Init, base::Unretained(impl))); 97 FROM_HERE, base::Bind(&VideoCaptureImpl::Init,
71 } else { 98 base::Unretained(it->impl.get())));
72 ++it->second.first;
73 } 99 }
100 ++it->client_count;
101
102 // Design limit: When there are multiple clients, VideoCaptureImplManager
103 // would have to individually track which ones requested suspending/resuming,
104 // in order to determine whether the whole device should be suspended.
105 // Instead, handle the non-common use case of multiple clients by just
106 // resuming the suspended device, and disable suspend functionality while
107 // there are multiple clients.
108 if (it->is_individually_suspended)
109 Resume(id);
110
74 return base::Bind(&VideoCaptureImplManager::UnrefDevice, 111 return base::Bind(&VideoCaptureImplManager::UnrefDevice,
75 weak_factory_.GetWeakPtr(), id); 112 weak_factory_.GetWeakPtr(), id);
76 } 113 }
77 114
78 base::Closure VideoCaptureImplManager::StartCapture( 115 base::Closure VideoCaptureImplManager::StartCapture(
79 media::VideoCaptureSessionId id, 116 media::VideoCaptureSessionId id,
80 const media::VideoCaptureParams& params, 117 const media::VideoCaptureParams& params,
81 const VideoCaptureStateUpdateCB& state_update_cb, 118 const VideoCaptureStateUpdateCB& state_update_cb,
82 const VideoCaptureDeliverFrameCB& deliver_frame_cb) { 119 const VideoCaptureDeliverFrameCB& deliver_frame_cb) {
83 DCHECK(render_main_task_runner_->BelongsToCurrentThread()); 120 DCHECK(render_main_task_runner_->BelongsToCurrentThread());
84 const VideoCaptureDeviceMap::const_iterator it = devices_.find(id); 121 const auto it = std::find_if(
122 devices_.begin(), devices_.end(),
123 [id] (const DeviceEntry& entry) { return entry.session_id == id; });
85 DCHECK(it != devices_.end()); 124 DCHECK(it != devices_.end());
86 VideoCaptureImpl* const impl = it->second.second;
87 125
88 // This ID is used to identify a client of VideoCaptureImpl. 126 // This ID is used to identify a client of VideoCaptureImpl.
89 const int client_id = ++next_client_id_; 127 const int client_id = ++next_client_id_;
90 128
91 ChildProcess::current()->io_task_runner()->PostTask( 129 ChildProcess::current()->io_task_runner()->PostTask(
92 FROM_HERE, 130 FROM_HERE,
93 base::Bind(&VideoCaptureImpl::StartCapture, base::Unretained(impl), 131 base::Bind(&VideoCaptureImpl::StartCapture,
94 client_id, params, state_update_cb, deliver_frame_cb)); 132 base::Unretained(it->impl.get()), client_id, params,
133 state_update_cb, deliver_frame_cb));
95 return base::Bind(&VideoCaptureImplManager::StopCapture, 134 return base::Bind(&VideoCaptureImplManager::StopCapture,
96 weak_factory_.GetWeakPtr(), client_id, id); 135 weak_factory_.GetWeakPtr(), client_id, id);
97 } 136 }
98 137
99 void VideoCaptureImplManager::RequestRefreshFrame( 138 void VideoCaptureImplManager::RequestRefreshFrame(
100 media::VideoCaptureSessionId id) { 139 media::VideoCaptureSessionId id) {
101 DCHECK(render_main_task_runner_->BelongsToCurrentThread()); 140 DCHECK(render_main_task_runner_->BelongsToCurrentThread());
102 const VideoCaptureDeviceMap::const_iterator it = devices_.find(id); 141 const auto it = std::find_if(
142 devices_.begin(), devices_.end(),
143 [id] (const DeviceEntry& entry) { return entry.session_id == id; });
103 DCHECK(it != devices_.end()); 144 DCHECK(it != devices_.end());
104 VideoCaptureImpl* const impl = it->second.second;
105 ChildProcess::current()->io_task_runner()->PostTask( 145 ChildProcess::current()->io_task_runner()->PostTask(
106 FROM_HERE, 146 FROM_HERE,
107 base::Bind(&VideoCaptureImpl::RequestRefreshFrame, 147 base::Bind(&VideoCaptureImpl::RequestRefreshFrame,
108 base::Unretained(impl))); 148 base::Unretained(it->impl.get())));
149 }
150
151 void VideoCaptureImplManager::Suspend(media::VideoCaptureSessionId id) {
152 DCHECK(render_main_task_runner_->BelongsToCurrentThread());
153 const auto it = std::find_if(
154 devices_.begin(), devices_.end(),
155 [id] (const DeviceEntry& entry) { return entry.session_id == id; });
156 DCHECK(it != devices_.end());
157 if (it->is_individually_suspended)
158 return; // Device has already been individually suspended.
159 if (it->client_count > 1)
160 return; // Punt when there is >1 client (see comments in UseDevice()).
161 it->is_individually_suspended = true;
162 if (is_suspending_all_)
163 return; // Device should already be suspended.
164 ChildProcess::current()->io_task_runner()->PostTask(
165 FROM_HERE, base::Bind(&VideoCaptureImpl::SuspendCapture,
166 base::Unretained(it->impl.get()), true));
167 }
168
169 void VideoCaptureImplManager::Resume(media::VideoCaptureSessionId id) {
170 DCHECK(render_main_task_runner_->BelongsToCurrentThread());
171 const auto it = std::find_if(
172 devices_.begin(), devices_.end(),
173 [id] (const DeviceEntry& entry) { return entry.session_id == id; });
174 DCHECK(it != devices_.end());
175 if (!it->is_individually_suspended)
176 return; // Device was not individually suspended.
177 it->is_individually_suspended = false;
178 if (is_suspending_all_)
179 return; // Device must remain suspended until all are resumed.
180 ChildProcess::current()->io_task_runner()->PostTask(
181 FROM_HERE, base::Bind(&VideoCaptureImpl::SuspendCapture,
182 base::Unretained(it->impl.get()), false));
109 } 183 }
110 184
111 void VideoCaptureImplManager::GetDeviceSupportedFormats( 185 void VideoCaptureImplManager::GetDeviceSupportedFormats(
112 media::VideoCaptureSessionId id, 186 media::VideoCaptureSessionId id,
113 const VideoCaptureDeviceFormatsCB& callback) { 187 const VideoCaptureDeviceFormatsCB& callback) {
114 DCHECK(render_main_task_runner_->BelongsToCurrentThread()); 188 DCHECK(render_main_task_runner_->BelongsToCurrentThread());
115 const VideoCaptureDeviceMap::const_iterator it = devices_.find(id); 189 const auto it = std::find_if(
190 devices_.begin(), devices_.end(),
191 [id] (const DeviceEntry& entry) { return entry.session_id == id; });
116 DCHECK(it != devices_.end()); 192 DCHECK(it != devices_.end());
117 VideoCaptureImpl* const impl = it->second.second;
118 ChildProcess::current()->io_task_runner()->PostTask( 193 ChildProcess::current()->io_task_runner()->PostTask(
119 FROM_HERE, base::Bind(&VideoCaptureImpl::GetDeviceSupportedFormats, 194 FROM_HERE, base::Bind(&VideoCaptureImpl::GetDeviceSupportedFormats,
120 base::Unretained(impl), callback)); 195 base::Unretained(it->impl.get()), callback));
121 } 196 }
122 197
123 void VideoCaptureImplManager::GetDeviceFormatsInUse( 198 void VideoCaptureImplManager::GetDeviceFormatsInUse(
124 media::VideoCaptureSessionId id, 199 media::VideoCaptureSessionId id,
125 const VideoCaptureDeviceFormatsCB& callback) { 200 const VideoCaptureDeviceFormatsCB& callback) {
126 DCHECK(render_main_task_runner_->BelongsToCurrentThread()); 201 DCHECK(render_main_task_runner_->BelongsToCurrentThread());
127 const VideoCaptureDeviceMap::const_iterator it = devices_.find(id); 202 const auto it = std::find_if(
203 devices_.begin(), devices_.end(),
204 [id] (const DeviceEntry& entry) { return entry.session_id == id; });
128 DCHECK(it != devices_.end()); 205 DCHECK(it != devices_.end());
129 VideoCaptureImpl* const impl = it->second.second;
130 ChildProcess::current()->io_task_runner()->PostTask( 206 ChildProcess::current()->io_task_runner()->PostTask(
131 FROM_HERE, base::Bind(&VideoCaptureImpl::GetDeviceFormatsInUse, 207 FROM_HERE, base::Bind(&VideoCaptureImpl::GetDeviceFormatsInUse,
132 base::Unretained(impl), callback)); 208 base::Unretained(it->impl.get()), callback));
133 } 209 }
134 210
135 VideoCaptureImpl* 211 std::unique_ptr<VideoCaptureImpl>
136 VideoCaptureImplManager::CreateVideoCaptureImplForTesting( 212 VideoCaptureImplManager::CreateVideoCaptureImplForTesting(
137 media::VideoCaptureSessionId id, 213 media::VideoCaptureSessionId id,
138 VideoCaptureMessageFilter* filter) const { 214 VideoCaptureMessageFilter* filter) const {
139 return NULL; 215 return std::unique_ptr<VideoCaptureImpl>();
140 } 216 }
141 217
142 void VideoCaptureImplManager::StopCapture(int client_id, 218 void VideoCaptureImplManager::StopCapture(int client_id,
143 media::VideoCaptureSessionId id) { 219 media::VideoCaptureSessionId id) {
144 DCHECK(render_main_task_runner_->BelongsToCurrentThread()); 220 DCHECK(render_main_task_runner_->BelongsToCurrentThread());
145 const VideoCaptureDeviceMap::const_iterator it = devices_.find(id); 221 const auto it = std::find_if(
222 devices_.begin(), devices_.end(),
223 [id] (const DeviceEntry& entry) { return entry.session_id == id; });
146 DCHECK(it != devices_.end()); 224 DCHECK(it != devices_.end());
147 VideoCaptureImpl* const impl = it->second.second;
148 ChildProcess::current()->io_task_runner()->PostTask( 225 ChildProcess::current()->io_task_runner()->PostTask(
149 FROM_HERE, base::Bind(&VideoCaptureImpl::StopCapture, 226 FROM_HERE, base::Bind(&VideoCaptureImpl::StopCapture,
150 base::Unretained(impl), client_id)); 227 base::Unretained(it->impl.get()), client_id));
151 } 228 }
152 229
153 void VideoCaptureImplManager::UnrefDevice( 230 void VideoCaptureImplManager::UnrefDevice(
154 media::VideoCaptureSessionId id) { 231 media::VideoCaptureSessionId id) {
155 DCHECK(render_main_task_runner_->BelongsToCurrentThread()); 232 DCHECK(render_main_task_runner_->BelongsToCurrentThread());
156 const VideoCaptureDeviceMap::iterator it = devices_.find(id); 233 const auto it = std::find_if(
234 devices_.begin(), devices_.end(),
235 [id] (const DeviceEntry& entry) { return entry.session_id == id; });
157 DCHECK(it != devices_.end()); 236 DCHECK(it != devices_.end());
158 VideoCaptureImpl* const impl = it->second.second; 237 DCHECK_GT(it->client_count, 0);
159 238 --it->client_count;
160 // Unref and destroy on the IO thread if there's no more client. 239 if (it->client_count > 0)
161 DCHECK(it->second.first); 240 return;
162 --it->second.first; 241 ChildProcess::current()->io_task_runner()->PostTask(
163 if (!it->second.first) { 242 FROM_HERE, base::Bind(&VideoCaptureImpl::DeInit,
164 devices_.erase(id); 243 base::Unretained(it->impl.get())));
165 ChildProcess::current()->io_task_runner()->PostTask( 244 ChildProcess::current()->io_task_runner()->DeleteSoon(
166 FROM_HERE, 245 FROM_HERE, it->impl.release());
167 base::Bind(&VideoCaptureImpl::DeInit, base::Unretained(impl))); 246 devices_.erase(it);
168 ChildProcess::current()->io_task_runner()->DeleteSoon(FROM_HERE, impl);
169 }
170 } 247 }
171 248
172 void VideoCaptureImplManager::SuspendDevices(bool suspend) { 249 void VideoCaptureImplManager::SuspendDevices(bool suspend) {
173 DCHECK(render_main_task_runner_->BelongsToCurrentThread()); 250 DCHECK(render_main_task_runner_->BelongsToCurrentThread());
174 for (const auto& device : devices_) { 251 if (is_suspending_all_ == suspend)
175 VideoCaptureImpl* const impl = device.second.second; 252 return;
253 is_suspending_all_ = suspend;
254 for (auto& entry : devices_) {
255 if (entry.is_individually_suspended)
256 continue; // Either: 1) Already suspended; or 2) Should not be resumed.
176 ChildProcess::current()->io_task_runner()->PostTask( 257 ChildProcess::current()->io_task_runner()->PostTask(
177 FROM_HERE, base::Bind(&VideoCaptureImpl::SuspendCapture, 258 FROM_HERE, base::Bind(&VideoCaptureImpl::SuspendCapture,
178 base::Unretained(impl), suspend)); 259 base::Unretained(entry.impl.get()), suspend));
179 } 260 }
180 } 261 }
181 262
182 } // namespace content 263 } // namespace content
OLDNEW
« no previous file with comments | « content/renderer/media/video_capture_impl_manager.h ('k') | content/renderer/media/video_capture_impl_manager_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698