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/browser/renderer_host/media/media_stream_manager.h" | |
6 | |
7 #include <list> | |
8 | |
9 #include "base/lazy_instance.h" | |
10 #include "base/logging.h" | |
11 #include "base/rand_util.h" | |
12 #include "content/browser/browser_thread.h" | |
13 #include "content/browser/renderer_host/media/media_stream_device_settings.h" | |
14 #include "content/browser/renderer_host/media/media_stream_requester.h" | |
15 #include "content/browser/renderer_host/media/video_capture_manager.h" | |
16 #include "content/common/media/media_stream_options.h" | |
17 | |
18 namespace media_stream { | |
19 | |
20 // TODO(mflodman) Find out who should own MediaStreamManager. | |
21 base::LazyInstance<MediaStreamManager> g_media_stream_manager( | |
22 base::LINKER_INITIALIZED); | |
23 | |
24 // Creates a random label used to identify requests. | |
25 static std::string RandomLabel() { | |
26 // Alphbet according to WhatWG standard, i.e. containing 36 characters from | |
27 // range: U+0021, U+0023 to U+0027, U+002A to U+002B, U+002D to U+002E, | |
28 // U+0030 to U+0039, U+0041 to U+005A, U+005E to U+007E. | |
29 static const char alphabet[] = "!#$%&\'*+-.0123456789" | |
30 "abcdefghijklmnopqrstuvwxyz^_`ABCDEFGHIJKLMNOPQRSTUVWXYZ{|}~"; | |
31 | |
32 std::string label(36, ' '); | |
33 for (size_t i = 0; i < label.size(); ++i) { | |
34 int random_char = base::RandGenerator(sizeof(alphabet) - 1); | |
35 label[i] = alphabet[random_char]; | |
36 } | |
37 return label; | |
38 } | |
39 | |
40 // Helper to verify if a media stream type is part of options or not. | |
41 static bool Requested(const StreamOptions& options, | |
42 MediaStreamType stream_type) { | |
43 if (stream_type == kVideoCapture | |
44 && (options.video_option != StreamOptions::kNoCamera)) { | |
wjia(left Chromium)
2011/07/06 02:58:42
"&&" should be at the end of previous line.
mflodman1
2011/07/06 11:31:45
Done.
| |
45 return true; | |
46 } else if (stream_type == kAudioCapture && options.audio == true) { | |
47 return true; | |
48 } | |
49 return false; | |
50 } | |
51 | |
52 MediaStreamManager* MediaStreamManager::Get() { | |
53 return g_media_stream_manager.Pointer(); | |
54 } | |
55 | |
56 MediaStreamManager::~MediaStreamManager() { | |
57 delete device_settings_; | |
58 delete video_capture_manager_; | |
59 } | |
60 | |
61 VideoCaptureManager* MediaStreamManager::GetVideoCaptureManager() { | |
62 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
63 return video_capture_manager_; | |
64 } | |
65 | |
66 void MediaStreamManager::GenerateStream(MediaStreamRequester* requester, | |
67 int render_process_id, | |
68 int render_view_id, | |
69 const StreamOptions& options, | |
70 const std::string& security_origin, | |
71 std::string* label) { | |
72 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
73 | |
74 // TODO(mflodman) Remove next line when audio is supported. | |
75 (const_cast<StreamOptions&>(options)).audio = false; | |
76 | |
77 // Create a new request based on options. | |
78 DeviceRequest new_request = DeviceRequest(requester, options); | |
79 if (Requested(new_request.options, kAudioCapture)) { | |
80 new_request.state[kAudioCapture] = DeviceRequest::kRequested; | |
81 } | |
82 if (Requested(new_request.options, kVideoCapture)) { | |
83 new_request.state[kVideoCapture] = DeviceRequest::kRequested; | |
84 } | |
85 | |
86 // Create a label for this request and verify it is unique. | |
87 std::string request_label; | |
88 do { | |
89 request_label = RandomLabel(); | |
90 } while (requests_.find(request_label) != requests_.end()); | |
91 | |
92 requests_.insert(std::make_pair(request_label, new_request)); | |
93 | |
94 // Get user confirmation to use capture devices. | |
95 device_settings_->RequestCaptureDeviceUsage(request_label, render_process_id, | |
96 render_view_id, options, | |
97 security_origin); | |
98 (*label) = request_label; | |
99 } | |
100 | |
101 void MediaStreamManager::CancelRequests(MediaStreamRequester* requester) { | |
102 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
103 DeviceRequests::iterator it = requests_.begin(); | |
104 while (it != requests_.end()) { | |
105 if (it->second.requester == requester && !RequestDone(it->second)) { | |
wjia(left Chromium)
2011/07/06 02:58:42
looks like a request can be cancelled if any reque
mflodman1
2011/07/06 11:31:45
Very good point! I added code here to close open d
| |
106 requests_.erase(it); | |
107 it = requests_.begin(); | |
wjia(left Chromium)
2011/07/06 02:58:42
do you have to go from the beginning again? are yo
mflodman1
2011/07/06 11:31:45
You're right, there is no need to start from begin
| |
108 } else { | |
109 ++it; | |
110 } | |
111 } | |
112 } | |
113 | |
114 void MediaStreamManager::StopGeneratedStream(const std::string& label) { | |
115 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
116 // Find the request and close all open devices for the request. | |
117 DeviceRequests::iterator it = requests_.find(label); | |
118 if (it != requests_.end()) { | |
119 for (StreamDeviceInfoArray::iterator audio_it = | |
120 it->second.audio_devices.begin(); | |
121 audio_it != it->second.audio_devices.end(); ++audio_it) { | |
122 // TODO(mflodman) Add code when audio input manager exists. | |
123 NOTREACHED(); | |
124 } | |
125 for (StreamDeviceInfoArray::iterator video_it = | |
126 it->second.video_devices.begin(); | |
127 video_it != it->second.video_devices.end(); ++video_it) { | |
128 video_capture_manager_->Close(video_it->session_id); | |
129 } | |
130 requests_.erase(it); | |
131 return; | |
132 } | |
133 } | |
134 | |
135 void MediaStreamManager::Opened(MediaStreamType stream_type, | |
136 int capture_session_id) { | |
137 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
138 | |
139 // Find the request containing this device and mark it as used. | |
140 DeviceRequest* request = NULL; | |
141 StreamDeviceInfo* device = NULL; | |
142 std::string label; | |
143 for (DeviceRequests::iterator request_it = requests_.begin(); | |
144 request_it != requests_.end() && request == NULL; ++request_it) { | |
145 StreamDeviceInfoArray* devices = NULL; | |
146 if (stream_type == kAudioCapture) { | |
147 devices = &(request_it->second.audio_devices); | |
148 } else if (stream_type == kVideoCapture) { | |
149 devices = &(request_it->second.video_devices); | |
150 } else { | |
151 NOTREACHED(); | |
152 } | |
153 | |
154 for (StreamDeviceInfoArray::iterator device_it = devices->begin(); | |
155 device_it != devices->end(); ++device_it) { | |
156 if (device_it->session_id == capture_session_id) { | |
157 // We've found the request. | |
158 label = request_it->first; | |
159 request = &(request_it->second); | |
160 device = &(*device_it); | |
161 break; | |
162 } | |
163 } | |
164 } | |
165 if (request == NULL) { | |
166 // The request doesn't exist. | |
167 return; | |
168 } | |
169 | |
170 DCHECK_NE(request->state[stream_type], DeviceRequest::kRequested); | |
171 | |
172 device->in_use = true; | |
173 if (!RequestDone(*request)) { | |
174 // Wait for more devices to be opened before we're done. | |
175 return; | |
176 } | |
177 | |
178 if (request->state[kAudioCapture] == DeviceRequest::kOpening) { | |
179 request->state[kAudioCapture] = DeviceRequest::kDone; | |
180 } | |
181 if (request->state[kVideoCapture] == DeviceRequest::kOpening) { | |
182 request->state[kVideoCapture] = DeviceRequest::kDone; | |
183 } | |
184 | |
185 request->requester->StreamGenerated(label, request->audio_devices, | |
186 request->video_devices); | |
187 } | |
188 | |
189 void MediaStreamManager::Closed(MediaStreamType stream_type, | |
190 int capture_session_id) { | |
191 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
192 } | |
193 | |
194 void MediaStreamManager::DevicesEnumerated( | |
195 MediaStreamType stream_type, const StreamDeviceInfoArray& devices) { | |
196 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
197 | |
198 // Publish the result for all requests waiting for device list(s). | |
199 // Find the requests waiting for this device list, store their labels and | |
200 // release the iterator before calling device settings. We might get a call | |
201 // back from device_settings that will need to iterate through devices. | |
202 std::list<std::string> label_list; | |
203 for (DeviceRequests::iterator it = requests_.begin(); it != requests_.end(); | |
204 ++it) { | |
205 if (it->second.state[stream_type] == DeviceRequest::kRequested | |
206 && Requested(it->second.options, stream_type)) { | |
207 label_list.push_back(it->first); | |
208 } | |
209 } | |
210 for (std::list<std::string>::iterator it = label_list.begin(); | |
211 it != label_list.end(); ++it) { | |
212 device_settings_->AvailableDevices(*it, stream_type, devices); | |
wjia(left Chromium)
2011/07/06 02:58:42
the label_list and 2nd for loop are not needed if
mflodman1
2011/07/06 11:31:45
I had it like that originally, but the problem was
| |
213 } | |
214 label_list.clear(); | |
215 enumeration_in_progress_[stream_type] = false; | |
216 } | |
217 | |
218 void MediaStreamManager::Error(MediaStreamType stream_type, | |
219 int capture_session_id, | |
220 MediaStreamProviderError error) { | |
221 // Find the device for the error call. | |
222 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
223 for (DeviceRequests::iterator it = requests_.begin(); it != requests_.end(); | |
224 ++it) { | |
225 StreamDeviceInfoArray* devices = NULL; | |
226 if (stream_type == kAudioCapture) { | |
227 devices = &(it->second.audio_devices); | |
228 } else if (stream_type == kVideoCapture) { | |
229 devices = &(it->second.video_devices); | |
230 } else { | |
231 NOTREACHED(); | |
232 } | |
233 | |
234 int device_idx = 0; | |
235 for (StreamDeviceInfoArray::iterator device_it = devices->begin(); | |
236 device_it != devices->end(); ++device_it, ++device_idx) { | |
237 if (device_it->session_id == capture_session_id) { | |
238 // We've found the failing device. Find the error case: | |
239 if (it->second.state[stream_type] == DeviceRequest::kDone) { | |
240 // 1. Already opened -> signal device failure and close device. | |
241 // Use device_idx to signal which of the devices encountered an | |
242 // error. | |
243 if (stream_type == kAudioCapture) { | |
244 it->second.requester->AudioDeviceFailed(it->first, device_idx); | |
245 } else if (stream_type == kVideoCapture) { | |
246 it->second.requester->VideoDeviceFailed(it->first, device_idx); | |
247 } | |
248 GetDeviceManager(stream_type)->Close(capture_session_id); | |
249 devices->erase(device_it); | |
250 } else if (it->second.audio_devices.size() | |
251 + it->second.video_devices.size() <= 1) { | |
252 // 2. Device not opened and no other devices for this request -> | |
253 // signal stream error and remove the request. | |
254 it->second.requester->StreamGenerationFailed(it->first); | |
255 requests_.erase(it); | |
256 } else { | |
257 // 3. Not opened but other devices exists for this request -> remove | |
258 // device from list, but don't signal an error. | |
259 devices->erase(device_it); | |
260 } | |
261 return; | |
262 } | |
263 } | |
264 } | |
265 } | |
266 | |
267 void MediaStreamManager::GetDevices(const std::string& label, | |
268 MediaStreamType stream_type) { | |
269 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
270 if (!enumeration_in_progress_[stream_type]) { | |
271 enumeration_in_progress_[stream_type] = true; | |
272 GetDeviceManager(stream_type)->EnumerateDevices(); | |
273 } | |
274 } | |
275 | |
276 void MediaStreamManager::DevicesAccepted(const std::string& label, | |
277 const StreamDeviceInfoArray& devices) { | |
278 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
279 DeviceRequests::iterator request_it = requests_.find(label); | |
280 if (request_it != requests_.end()) { | |
wjia(left Chromium)
2011/07/06 02:58:42
is it possible only partial request is accepted? e
mflodman1
2011/07/06 11:31:45
Yes. No error is signalled to the requester if thi
| |
281 if (devices.empty()) { | |
282 // No available devices or user didn't accept device usage. | |
283 request_it->second.requester->StreamGenerationFailed(request_it->first); | |
284 requests_.erase(request_it); | |
285 return; | |
286 } | |
287 | |
288 // Loop through all device types for this request. | |
289 for (StreamDeviceInfoArray::const_iterator device_it = devices.begin(); | |
290 device_it != devices.end(); ++device_it) { | |
291 StreamDeviceInfo device_info = *device_it; | |
292 | |
293 // Set in_use to false to be able to track if this device has been | |
294 // opened. in_use might be true if the device type can be used in more | |
295 // than one session. | |
296 device_info.in_use = false; | |
297 device_info.session_id = | |
298 GetDeviceManager(device_info.stream_type)->Open(device_info); | |
299 request_it->second.state[device_it->stream_type] = | |
300 DeviceRequest::kOpening; | |
301 if (device_info.stream_type == kAudioCapture) { | |
302 request_it->second.audio_devices.push_back(device_info); | |
303 } else if (device_info.stream_type == kVideoCapture) { | |
304 request_it->second.video_devices.push_back(device_info); | |
305 } else { | |
306 NOTREACHED(); | |
307 } | |
308 } | |
309 return; | |
310 } | |
311 } | |
312 | |
313 void MediaStreamManager::SettingsError(const std::string& label) { | |
314 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
315 // Erase this request and report an error. | |
316 DeviceRequests::iterator it = requests_.find(label); | |
317 if (it != requests_.end()) { | |
318 it->second.requester->StreamGenerationFailed(label); | |
319 requests_.erase(it); | |
320 return; | |
321 } | |
322 } | |
323 | |
324 void MediaStreamManager::UseFakeDevice() { | |
325 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
326 video_capture_manager_->UseFakeDevice(); | |
327 // TODO(mflodman) Add audio manager when available. | |
328 } | |
329 | |
330 bool MediaStreamManager::RequestDone(const DeviceRequest& request) const { | |
331 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
332 // Check if all devices are opened. | |
333 if (Requested(request.options, kAudioCapture)) { | |
334 for (StreamDeviceInfoArray::const_iterator it = | |
335 request.audio_devices.begin(); it != request.audio_devices.end(); | |
336 ++it) { | |
337 if (it->in_use == false) { | |
338 return false; | |
339 } | |
340 } | |
341 } | |
342 if (Requested(request.options, kVideoCapture)) { | |
343 for (StreamDeviceInfoArray::const_iterator it = | |
wjia(left Chromium)
2011/07/06 02:58:42
indentation.
mflodman1
2011/07/06 11:31:45
Done.
| |
344 request.video_devices.begin(); it != request.video_devices.end(); | |
345 ++it) { | |
346 if (it->in_use == false) { | |
347 return false; | |
348 } | |
349 } | |
350 } | |
351 return true; | |
352 } | |
353 | |
354 // Called to get media capture device manager of specified type. | |
355 MediaStreamProvider* MediaStreamManager::GetDeviceManager( | |
356 MediaStreamType stream_type) const { | |
357 if (stream_type == kVideoCapture) { | |
358 return video_capture_manager_; | |
359 } else if (stream_type == kAudioCapture) { | |
360 // TODO(mflodman) Add support when audio input manager is available. | |
361 NOTREACHED(); | |
362 return NULL; | |
363 } | |
364 NOTREACHED(); | |
365 return NULL; | |
366 } | |
367 | |
368 MediaStreamManager::MediaStreamManager() | |
369 : video_capture_manager_(new VideoCaptureManager()), | |
370 enumeration_in_progress_(kNumMediaStreamTypes, false), | |
371 requests_(), | |
372 device_settings_(NULL) { | |
373 device_settings_ = new MediaStreamDeviceSettings(this); | |
374 video_capture_manager_->Register(this); | |
375 // TODO(mflodman) Add when audio input manager is available. | |
376 } | |
377 | |
378 MediaStreamManager::DeviceRequest::DeviceRequest() | |
379 : requester(NULL), | |
380 state(kNumMediaStreamTypes, kNotRequested) { | |
381 options.audio = false; | |
382 options.video_option = StreamOptions::kNoCamera; | |
383 } | |
384 | |
385 MediaStreamManager::DeviceRequest::DeviceRequest( | |
386 MediaStreamRequester* requester, const StreamOptions& request_options) | |
387 : requester(requester), | |
388 options(request_options), | |
389 state(kNumMediaStreamTypes, kNotRequested) { | |
390 DCHECK(requester); | |
391 } | |
392 | |
393 } // namespace media_stream | |
OLD | NEW |