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 "base/lazy_instance.h" | |
8 #include "base/logging.h" | |
9 #include "base/rand_util.h" | |
10 #include "content/browser/browser_thread.h" | |
11 #include "content/browser/renderer_host/media/media_stream_device_settings.h" | |
12 #include "content/browser/renderer_host/media/media_stream_requester.h" | |
13 #include "content/browser/renderer_host/media/video_capture_manager.h" | |
14 #include "content/common/media/media_stream_options.h" | |
15 | |
16 namespace media_stream { | |
17 | |
18 // TODO(mflodman) Find out who should own MediaStreamManager. | |
19 base::LazyInstance<MediaStreamManager> g_media_stream_manager( | |
20 base::LINKER_INITIALIZED); | |
21 | |
22 // Creates a random label used to identify requests. | |
23 static std::string RandomLabel() { | |
24 static const char alphabet[] = "0123456789" | |
Leandro Graciá Gil
2011/07/01 11:31:35
I think this comes from the old demo code (and hen
mflodman1
2011/07/01 14:07:23
Length updated. The label is used in Chrome intern
mflodman1
2011/07/04 21:43:14
Updated according to WhatWG spec.
| |
25 "abcdefghijklmnopqrstuvwxyz" | |
26 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; | |
27 | |
28 std::string label(63, ' '); | |
29 for (size_t i = 0; i < label.size(); ++i) { | |
30 int random_char = base::RandGenerator(sizeof(alphabet) - 1); | |
31 label[i] = alphabet[random_char]; | |
32 } | |
33 return label; | |
34 } | |
35 | |
36 // Helper to verify if a media stream type is part of options or not. | |
37 static bool Requested(const StreamOptions& options, | |
38 MediaStreamType stream_type) { | |
39 if (stream_type == kVideoCapture | |
40 && (options.video_option != StreamOptions::kNoCamera)) { | |
Leandro Graciá Gil
2011/07/01 11:31:35
What if stream_type asks for video but options don
mflodman1
2011/07/01 14:07:23
This is just a helper to see if the stream type is
| |
41 return true; | |
42 } else if (stream_type == kAudioCapture && options.audio == true) { | |
43 return true; | |
44 } | |
45 return false; | |
46 } | |
47 | |
48 MediaStreamManager* MediaStreamManager::Get() { | |
49 return g_media_stream_manager.Pointer(); | |
50 } | |
51 | |
52 MediaStreamManager::~MediaStreamManager() { | |
53 delete device_settings_; | |
54 delete video_capture_manager_; | |
55 } | |
56 | |
57 VideoCaptureManager* MediaStreamManager::GetVideoCaptureManager() { | |
58 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
59 return video_capture_manager_; | |
60 } | |
61 | |
62 void MediaStreamManager::GenerateStream(MediaStreamRequester* requester, | |
63 int render_process_id, | |
64 int render_view_id, | |
65 const StreamOptions& options, | |
66 const std::string& security_origin, | |
67 std::string* label) { | |
68 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
69 | |
70 // TODO(mflodman) Remove next line when audio is supported. | |
71 (const_cast<StreamOptions&>(options)).audio = false; | |
72 | |
73 // Create a new request based on options. | |
74 DeviceRequest new_request = DeviceRequest(requester, options); | |
75 if (Requested(new_request.options, kAudioCapture)) { | |
76 new_request.state[kAudioCapture] = DeviceRequest::kRequested; | |
77 } | |
78 if (Requested(new_request.options, kVideoCapture)) { | |
79 new_request.state[kVideoCapture] = DeviceRequest::kRequested; | |
80 } | |
81 | |
82 // Create a label for this request and verify it is unique. | |
83 new_request.label = RandomLabel(); | |
84 for (DeviceRequestList::iterator it = requests_.begin(); | |
85 it != requests_.end(); ++it) { | |
86 if (new_request.label == it->label) { | |
Leandro Graciá Gil
2011/07/01 11:31:35
Does the request list contain the labels of existi
mflodman1
2011/07/01 14:07:23
A request is stored until the stream is either clo
mflodman1
2011/07/04 21:43:14
Done.
| |
87 new_request.label = RandomLabel(); | |
88 it = requests_.begin(); | |
89 } | |
90 } | |
91 requests_.push_back(new_request); | |
92 | |
93 // Get user confirmation to use capture devices. | |
94 device_settings_->RequestCaptureDeviceUsage(new_request.label, | |
95 render_process_id, render_view_id, | |
96 options, security_origin); | |
97 (*label) = new_request.label; | |
98 } | |
99 | |
100 void MediaStreamManager::CancelRequests(MediaStreamRequester* requester) { | |
101 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
102 | |
103 DeviceRequestList::iterator it = requests_.begin(); | |
104 while (it != requests_.end()) { | |
105 if (it->requester == requester && !RequestDone(*it)) { | |
106 it = requests_.erase(it); | |
107 it = requests_.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 for (DeviceRequestList::iterator it = requests_.begin(); | |
118 it != requests_.end(); ++it) { | |
119 if (it->label == label) { | |
120 for (StreamDeviceInfoArray::iterator audio_it = it->audio_devices.begin(); | |
121 audio_it != it->audio_devices.end(); ++audio_it) { | |
122 // TODO(mflodman) Add code when audio input manager exists. | |
123 DCHECK(false); | |
124 } | |
125 for (StreamDeviceInfoArray::iterator video_it = it->video_devices.begin(); | |
126 video_it != it->video_devices.end(); ++video_it) { | |
127 video_capture_manager_->Close(video_it->session_id); | |
128 } | |
129 requests_.erase(it); | |
130 return; | |
131 } | |
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 for (DeviceRequestList::iterator request_it = requests_.begin(); | |
143 request_it != requests_.end() && request == NULL; ++request_it) { | |
144 StreamDeviceInfoArray* devices = NULL; | |
145 if (stream_type == kAudioCapture) { | |
146 devices = &(request_it->audio_devices); | |
147 } else if (stream_type == kVideoCapture) { | |
148 devices = &(request_it->video_devices); | |
149 } else { | |
150 DCHECK(false); | |
151 } | |
152 | |
153 for (StreamDeviceInfoArray::iterator device_it = devices->begin(); | |
154 device_it != devices->end(); ++device_it) { | |
155 if (device_it->session_id == capture_session_id) { | |
156 // We've found the request. | |
157 | |
158 request = &(*request_it); | |
159 device = &(*device_it); | |
160 break; | |
161 } | |
162 } | |
163 } | |
164 if (request == NULL) { | |
165 // The request doesn't exist. | |
Leandro Graciá Gil
2011/07/01 11:31:35
Can this happen in legitimate situations without b
mflodman1
2011/07/01 14:07:23
Yes, the request can be canceled during the time t
| |
166 return; | |
167 } | |
168 | |
169 device->in_use = true; | |
Leandro Graciá Gil
2011/07/01 11:31:35
Is there a possibility that the device is already
mflodman1
2011/07/01 14:07:23
A device is marked as in_use by the VideoCaptureMa
| |
170 if (!RequestDone(*request)) { | |
171 // Wait for more devices to be opened before we're done. | |
172 return; | |
173 } | |
174 | |
175 if (request->state[kAudioCapture] == DeviceRequest::kOpening) { | |
Leandro Graciá Gil
2011/07/01 11:31:35
Which other states are valid here? Maybe it's wort
mflodman1
2011/07/01 14:07:23
It should only be kRequested that is invalid, mean
| |
176 request->state[kAudioCapture] = DeviceRequest::kDone; | |
177 } | |
178 if (request->state[kVideoCapture] == DeviceRequest::kOpening) { | |
179 request->state[kVideoCapture] = DeviceRequest::kDone; | |
180 } | |
181 | |
182 request->requester->StreamGenerated(request->label, request->audio_devices, | |
183 request->video_devices); | |
184 } | |
185 | |
186 void MediaStreamManager::Closed(MediaStreamType stream_type, | |
187 int capture_session_id) { | |
188 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
189 } | |
190 | |
191 void MediaStreamManager::DevicesEnumerated( | |
192 MediaStreamType stream_type, const StreamDeviceInfoArray& devices) { | |
193 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
194 | |
195 // Publish the result for all requests waiting for device list(s). | |
196 // Find the requests waiting for this device list, store their labels and | |
197 // release the iterator before calling device settings. We might get a call | |
198 // back from device_settings that will need to iterate through devices. | |
199 std::list<std::string> label_list; | |
200 for (DeviceRequestList::iterator it = requests_.begin(); | |
201 it != requests_.end(); ++it) { | |
202 if (it->state[stream_type] == DeviceRequest::kRequested | |
203 && Requested(it->options, stream_type)) { | |
204 label_list.push_back(it->label); | |
205 } | |
206 } | |
207 while (label_list.size()) { | |
208 device_settings_->AvailableDevices(label_list.front(), stream_type, | |
209 devices); | |
210 label_list.pop_front(); | |
Leandro Graciá Gil
2011/07/01 11:31:35
Since label_list is local and you always pop the f
mflodman1
2011/07/01 14:07:23
I changed to iterate the list and then do clear wh
| |
211 } | |
212 enumeration_in_progress_[stream_type] = false; | |
213 } | |
214 | |
215 void MediaStreamManager::Error(MediaStreamType stream_type, | |
216 int capture_session_id, | |
217 MediaStreamProviderError error) { | |
218 // Find the device for the error call. | |
219 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
220 for (DeviceRequestList::iterator it = requests_.begin(); | |
221 it != requests_.end(); ++it) { | |
222 StreamDeviceInfoArray* devices = NULL; | |
223 if (stream_type == kAudioCapture) { | |
224 devices = &(it->audio_devices); | |
225 } else if (stream_type == kVideoCapture) { | |
226 devices = &(it->video_devices); | |
227 } else { | |
228 DCHECK(false); | |
Leandro Graciá Gil
2011/07/01 11:31:35
Use NOTREACHED().
mflodman1
2011/07/01 14:07:23
Done.
| |
229 } | |
230 | |
231 int device_idx = 0; | |
232 for (StreamDeviceInfoArray::iterator device_it = devices->begin(); | |
233 device_it != devices->end(); ++device_it, ++device_idx) { | |
234 if (device_it->session_id == capture_session_id) { | |
235 // We've found the failing device. Find the error case: | |
236 if (it->state[stream_type] == DeviceRequest::kDone) { | |
237 // 1. Already opened -> signal device failure and close device. | |
238 // Use device_idx to signal which of the devices encountered an | |
239 // error. | |
240 if (stream_type == kAudioCapture) { | |
241 it->requester->AudioDeviceFailed(it->label, device_idx); | |
242 } else if (stream_type == kVideoCapture) { | |
243 it->requester->VideoDeviceFailed(it->label, device_idx); | |
244 } | |
245 GetDeviceManager(stream_type)->Close(capture_session_id); | |
246 devices->erase(device_it); | |
Leandro Graciá Gil
2011/07/01 11:31:35
This will move all the elements after it in the de
mflodman1
2011/07/01 14:07:23
It won't happen often and it's likely the vector c
| |
247 } else if (it->audio_devices.size() + it->video_devices.size() <= 1) { | |
248 // 2. Device not opened and no other devices for this request -> | |
249 // signal stream error and remove the request. | |
250 it->requester->StreamGenerationFailed(it->label); | |
251 requests_.erase(it); | |
252 } else { | |
253 // 3. Not opened but other devices exists for this request -> remove | |
254 // device from list, but don't signal an error. | |
255 devices->erase(device_it); | |
256 } | |
257 return; | |
258 } | |
259 } | |
260 } | |
261 } | |
262 | |
263 void MediaStreamManager::GetDevices(const std::string& label, | |
264 MediaStreamType stream_type) { | |
265 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
266 if (!enumeration_in_progress_[stream_type]) { | |
267 enumeration_in_progress_[stream_type] = true; | |
268 GetDeviceManager(stream_type)->EnumerateDevices(); | |
269 } | |
270 } | |
271 | |
272 void MediaStreamManager::DevicesAccepted(const std::string& label, | |
273 const StreamDeviceInfoArray& devices) { | |
274 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
275 for (DeviceRequestList::iterator request_it = requests_.begin(); | |
276 request_it != requests_.end(); ++request_it) { | |
277 if (request_it->label == label) { | |
278 // We've found our request. | |
279 if (devices.empty()) { | |
280 // No available devices or user didn't accept device usage. | |
281 request_it->requester->StreamGenerationFailed(request_it->label); | |
282 requests_.erase(request_it); | |
283 return; | |
284 } | |
285 | |
286 // Loop through all device types for this request. | |
287 for (StreamDeviceInfoArray::const_iterator device_it = devices.begin(); | |
288 device_it != devices.end(); ++device_it) { | |
289 StreamDeviceInfo device_info = *device_it; | |
290 | |
291 // Set in_use to false to be able to track if this device has been | |
292 // opened. in_use might be true if the device type can be used in more | |
293 // than one session. | |
294 device_info.in_use = false; | |
295 device_info.session_id = | |
296 GetDeviceManager(device_info.stream_type)->Open(device_info); | |
297 request_it->state[device_it->stream_type] = DeviceRequest::kOpening; | |
298 if (device_info.stream_type == kAudioCapture) { | |
299 request_it->audio_devices.push_back(device_info); | |
300 } else if (device_info.stream_type == kVideoCapture) { | |
301 request_it->video_devices.push_back(device_info); | |
302 } else { | |
303 DCHECK(false); | |
Leandro Graciá Gil
2011/07/01 11:31:35
NOTREACHED()
mflodman1
2011/07/01 14:07:23
Done.
| |
304 } | |
305 } | |
306 return; | |
307 } | |
308 } | |
309 } | |
310 | |
311 void MediaStreamManager::SettingsError(const std::string& label) { | |
312 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
313 // Erase this request and report an error. | |
314 for (DeviceRequestList::iterator it = requests_.begin(); | |
315 it != requests_.end(); ++it) { | |
316 if (it->label == label) { | |
317 // We've found our request. erase it and report error. | |
318 it->requester->StreamGenerationFailed(label); | |
319 requests_.erase(it); | |
320 return; | |
321 } | |
322 } | |
323 } | |
324 | |
325 void MediaStreamManager::UseFakeDevice() { | |
326 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
327 video_capture_manager_->UseFakeDevice(); | |
328 // TODO(mflodman) Add audio manager when available. | |
329 } | |
330 | |
331 bool MediaStreamManager::RequestDone(const DeviceRequest& request) const { | |
332 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
333 // Check if all devices are opened. | |
334 if (Requested(request.options, kAudioCapture)) { | |
335 for (StreamDeviceInfoArray::const_iterator it = | |
336 request.audio_devices.begin(); it != request.audio_devices.end(); | |
337 ++it) { | |
338 if (it->in_use == false) { | |
339 return false; | |
340 } | |
341 } | |
342 } | |
343 if (Requested(request.options, kVideoCapture)) { | |
344 for (StreamDeviceInfoArray::const_iterator it = | |
345 request.video_devices.begin(); it != request.video_devices.end(); | |
346 ++it) { | |
347 if (it->in_use == false) { | |
348 return false; | |
349 } | |
350 } | |
351 } | |
352 return true; | |
353 } | |
354 | |
355 // Called to get media capture device manager of specified type. | |
356 MediaStreamProvider* MediaStreamManager::GetDeviceManager( | |
357 MediaStreamType stream_type) const { | |
358 if (stream_type == kVideoCapture) { | |
359 return video_capture_manager_; | |
360 } else if (stream_type == kAudioCapture) { | |
361 // TODO(mflodman) Add support when audio input manager is available. | |
362 DCHECK(false); | |
363 return NULL; | |
364 } | |
365 DCHECK(false); | |
366 return NULL; | |
367 } | |
368 | |
369 MediaStreamManager::MediaStreamManager() | |
370 : video_capture_manager_(new VideoCaptureManager()), | |
371 enumeration_in_progress_(kNumMediaStreamTypes, false), | |
372 requests_(), | |
373 device_settings_(NULL) { | |
374 device_settings_ = new MediaStreamDeviceSettings(this); | |
375 video_capture_manager_->Register(this); | |
376 // TODO(mflodman) Add when audio input manager is available. | |
377 } | |
378 | |
379 } // namespace media_stream | |
OLD | NEW |