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 3 // found in the LICENSE file. 4 5 #include "content/renderer/media/media_stream_dispatcher.h" 6 7 #include "base/logging.h" 8 #include "content/common/media/media_stream_messages.h" 9 #include "content/renderer/media/media_stream_dispatcher_eventhandler.h" 10 #include "content/renderer/render_thread_impl.h" 11 #include "media/audio/audio_parameters.h" 12 #include "third_party/WebKit/public/web/WebUserGestureIndicator.h" 13 #include "url/gurl.h" 14 15 namespace content { 16 17 namespace { 18 19 bool RemoveStreamDeviceFromArray(const StreamDeviceInfo device_info, 20 StreamDeviceInfoArray* array) { 21 for (StreamDeviceInfoArray::iterator device_it = array->begin(); 22 device_it != array->end(); ++device_it) { 23 if (StreamDeviceInfo::IsEqual(*device_it, device_info)) { 24 array->erase(device_it); 25 return true; 26 } 27 } 28 return false; 29 } 30 31 } // namespace 32 33 // A request is identified by pair (request_id, handler), or ipc_request. 34 // There could be multiple clients making requests and each has its own 35 // request_id sequence. 36 // The ipc_request is garanteed to be unique when it's created in 37 // MediaStreamDispatcher. 38 struct MediaStreamDispatcher::Request { 39 Request(const base::WeakPtr<MediaStreamDispatcherEventHandler>& handler, 40 int request_id, 41 int ipc_request) 42 : handler(handler), 43 request_id(request_id), 44 ipc_request(ipc_request) { 45 } 46 bool IsThisRequest( 47 int request_id1, 48 const base::WeakPtr<MediaStreamDispatcherEventHandler>& handler1) { 49 return (request_id1 == request_id && handler1.get() == handler.get()); 50 } 51 base::WeakPtr<MediaStreamDispatcherEventHandler> handler; 52 int request_id; 53 int ipc_request; 54 }; 55 56 struct MediaStreamDispatcher::Stream { 57 Stream() {} 58 ~Stream() {} 59 base::WeakPtr<MediaStreamDispatcherEventHandler> handler; 60 StreamDeviceInfoArray audio_array; 61 StreamDeviceInfoArray video_array; 62 }; 63 64 MediaStreamDispatcher::MediaStreamDispatcher(RenderFrame* render_frame) 65 : RenderFrameObserver(render_frame), 66 next_ipc_id_(0) { 67 } 68 69 MediaStreamDispatcher::~MediaStreamDispatcher() {} 70 71 void MediaStreamDispatcher::GenerateStream( 72 int request_id, 73 const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler, 74 const StreamOptions& components, 75 const GURL& security_origin) { 76 DCHECK(thread_checker_.CalledOnValidThread()); 77 DVLOG(1) << "MediaStreamDispatcher::GenerateStream(" << request_id << ")"; 78 79 requests_.push_back(Request(event_handler, request_id, next_ipc_id_)); 80 Send(new MediaStreamHostMsg_GenerateStream( 81 routing_id(), next_ipc_id_++, components, security_origin, 82 blink::WebUserGestureIndicator::isProcessingUserGesture())); 83 } 84 85 void MediaStreamDispatcher::CancelGenerateStream( 86 int request_id, 87 const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler) { 88 DCHECK(thread_checker_.CalledOnValidThread()); 89 DVLOG(1) << "MediaStreamDispatcher::CancelGenerateStream" 90 << ", {request_id = " << request_id << "}"; 91 92 RequestList::iterator it = requests_.begin(); 93 for (; it != requests_.end(); ++it) { 94 if (it->IsThisRequest(request_id, event_handler)) { 95 int ipc_request = it->ipc_request; 96 requests_.erase(it); 97 Send(new MediaStreamHostMsg_CancelGenerateStream(routing_id(), 98 ipc_request)); 99 break; 100 } 101 } 102 } 103 104 void MediaStreamDispatcher::StopStreamDevice( 105 const StreamDeviceInfo& device_info) { 106 DCHECK(thread_checker_.CalledOnValidThread()); 107 DVLOG(1) << "MediaStreamDispatcher::StopStreamDevice" 108 << ", {device_id = " << device_info.device.id << "}"; 109 // Remove |device_info| from all streams in |label_stream_map_|. 110 bool device_found = false; 111 LabelStreamMap::iterator stream_it = label_stream_map_.begin(); 112 while (stream_it != label_stream_map_.end()) { 113 StreamDeviceInfoArray& audio_array = stream_it->second.audio_array; 114 StreamDeviceInfoArray& video_array = stream_it->second.video_array; 115 116 if (RemoveStreamDeviceFromArray(device_info, &audio_array) || 117 RemoveStreamDeviceFromArray(device_info, &video_array)) { 118 device_found = true; 119 if (audio_array.empty() && video_array.empty()) { 120 label_stream_map_.erase(stream_it++); 121 continue; 122 } 123 } 124 ++stream_it; 125 } 126 DCHECK(device_found); 127 128 Send(new MediaStreamHostMsg_StopStreamDevice(routing_id(), 129 device_info.device.id)); 130 } 131 132 void MediaStreamDispatcher::EnumerateDevices( 133 int request_id, 134 const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler, 135 MediaStreamType type, 136 const GURL& security_origin) { 137 DCHECK(thread_checker_.CalledOnValidThread()); 138 DCHECK(type == MEDIA_DEVICE_AUDIO_CAPTURE || 139 type == MEDIA_DEVICE_VIDEO_CAPTURE || 140 type == MEDIA_DEVICE_AUDIO_OUTPUT); 141 DVLOG(1) << "MediaStreamDispatcher::EnumerateDevices(" 142 << request_id << ")"; 143 144 for (RequestList::iterator it = requests_.begin(); it != requests_.end(); 145 ++it) { 146 DCHECK(!it->IsThisRequest(request_id, event_handler)); 147 } 148 149 requests_.push_back(Request(event_handler, request_id, next_ipc_id_)); 150 Send(new MediaStreamHostMsg_EnumerateDevices(routing_id(), 151 next_ipc_id_++, 152 type, 153 security_origin)); 154 } 155 156 void MediaStreamDispatcher::StopEnumerateDevices( 157 int request_id, 158 const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler) { 159 DCHECK(thread_checker_.CalledOnValidThread()); 160 DVLOG(1) << "MediaStreamDispatcher::StopEnumerateDevices(" 161 << request_id << ")"; 162 for (RequestList::iterator it = requests_.begin(); it != requests_.end(); 163 ++it) { 164 if (it->IsThisRequest(request_id, event_handler)) { 165 Send(new MediaStreamHostMsg_CancelEnumerateDevices(routing_id(), 166 it->ipc_request)); 167 requests_.erase(it); 168 break; 169 } 170 } 171 } 172 173 void MediaStreamDispatcher::OpenDevice( 174 int request_id, 175 const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler, 176 const std::string& device_id, 177 MediaStreamType type, 178 const GURL& security_origin) { 179 DCHECK(thread_checker_.CalledOnValidThread()); 180 DVLOG(1) << "MediaStreamDispatcher::OpenDevice(" << request_id << ")"; 181 182 requests_.push_back(Request(event_handler, request_id, next_ipc_id_)); 183 Send(new MediaStreamHostMsg_OpenDevice(routing_id(), 184 next_ipc_id_++, 185 device_id, 186 type, 187 security_origin)); 188 } 189 190 void MediaStreamDispatcher::CancelOpenDevice( 191 int request_id, 192 const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler) { 193 CancelGenerateStream(request_id, event_handler); 194 } 195 196 void MediaStreamDispatcher::CloseDevice(const std::string& label) { 197 DCHECK(thread_checker_.CalledOnValidThread()); 198 DCHECK(!label.empty()); 199 DVLOG(1) << "MediaStreamDispatcher::CloseDevice" 200 << ", {label = " << label << "}"; 201 202 LabelStreamMap::iterator it = label_stream_map_.find(label); 203 if (it == label_stream_map_.end()) 204 return; 205 label_stream_map_.erase(it); 206 207 Send(new MediaStreamHostMsg_CloseDevice(routing_id(), label)); 208 } 209 210 void MediaStreamDispatcher::OnDestruct() { 211 // Do not self-destruct. UserMediaClientImpl owns |this|. 212 } 213 214 bool MediaStreamDispatcher::Send(IPC::Message* message) { 215 if (!RenderThread::Get()) { 216 delete message; 217 return false; 218 } 219 220 return RenderThread::Get()->Send(message); 221 } 222 223 bool MediaStreamDispatcher::OnMessageReceived(const IPC::Message& message) { 224 bool handled = true; 225 IPC_BEGIN_MESSAGE_MAP(MediaStreamDispatcher, message) 226 IPC_MESSAGE_HANDLER(MediaStreamMsg_StreamGenerated, 227 OnStreamGenerated) 228 IPC_MESSAGE_HANDLER(MediaStreamMsg_StreamGenerationFailed, 229 OnStreamGenerationFailed) 230 IPC_MESSAGE_HANDLER(MediaStreamMsg_DeviceStopped, 231 OnDeviceStopped) 232 IPC_MESSAGE_HANDLER(MediaStreamMsg_DevicesEnumerated, 233 OnDevicesEnumerated) 234 IPC_MESSAGE_HANDLER(MediaStreamMsg_DeviceOpened, 235 OnDeviceOpened) 236 IPC_MESSAGE_HANDLER(MediaStreamMsg_DeviceOpenFailed, 237 OnDeviceOpenFailed) 238 IPC_MESSAGE_UNHANDLED(handled = false) 239 IPC_END_MESSAGE_MAP() 240 return handled; 241 } 242 243 void MediaStreamDispatcher::OnStreamGenerated( 244 int request_id, 245 const std::string& label, 246 const StreamDeviceInfoArray& audio_array, 247 const StreamDeviceInfoArray& video_array) { 248 DCHECK(thread_checker_.CalledOnValidThread()); 249 250 for (RequestList::iterator it = requests_.begin(); 251 it != requests_.end(); ++it) { 252 Request& request = *it; 253 if (request.ipc_request == request_id) { 254 Stream new_stream; 255 new_stream.handler = request.handler; 256 new_stream.audio_array = audio_array; 257 new_stream.video_array = video_array; 258 label_stream_map_[label] = new_stream; 259 if (request.handler.get()) { 260 request.handler->OnStreamGenerated( 261 request.request_id, label, audio_array, video_array); 262 DVLOG(1) << "MediaStreamDispatcher::OnStreamGenerated(" 263 << request.request_id << ", " << label << ")"; 264 } 265 requests_.erase(it); 266 break; 267 } 268 } 269 } 270 271 void MediaStreamDispatcher::OnStreamGenerationFailed( 272 int request_id, 273 content::MediaStreamRequestResult result) { 274 DCHECK(thread_checker_.CalledOnValidThread()); 275 for (RequestList::iterator it = requests_.begin(); 276 it != requests_.end(); ++it) { 277 Request& request = *it; 278 if (request.ipc_request == request_id) { 279 if (request.handler.get()) { 280 request.handler->OnStreamGenerationFailed(request.request_id, result); 281 DVLOG(1) << "MediaStreamDispatcher::OnStreamGenerationFailed(" 282 << request.request_id << ")\n"; 283 } 284 requests_.erase(it); 285 break; 286 } 287 } 288 } 289 290 void MediaStreamDispatcher::OnDeviceStopped( 291 const std::string& label, 292 const StreamDeviceInfo& device_info) { 293 DCHECK(thread_checker_.CalledOnValidThread()); 294 DVLOG(1) << "MediaStreamDispatcher::OnDeviceStopped(" 295 << "{label = " << label << "})" 296 << ", {device_id = " << device_info.device.id << "})"; 297 298 LabelStreamMap::iterator it = label_stream_map_.find(label); 299 if (it == label_stream_map_.end()) { 300 // This can happen if a user happen stop a the device from JS at the same 301 // time as the underlying media device is unplugged from the system. 302 return; 303 } 304 Stream* stream = &it->second; 305 if (IsAudioInputMediaType(device_info.device.type)) 306 RemoveStreamDeviceFromArray(device_info, &stream->audio_array); 307 else 308 RemoveStreamDeviceFromArray(device_info, &stream->video_array); 309 310 if (stream->handler.get()) 311 stream->handler->OnDeviceStopped(label, device_info); 312 313 if (stream->audio_array.empty() && stream->video_array.empty()) 314 label_stream_map_.erase(it); 315 } 316 317 void MediaStreamDispatcher::OnDevicesEnumerated( 318 int request_id, 319 const StreamDeviceInfoArray& device_array) { 320 DCHECK(thread_checker_.CalledOnValidThread()); 321 DCHECK_GE(request_id, 0); 322 323 for (RequestList::iterator it = requests_.begin(); it != requests_.end(); 324 ++it) { 325 if (it->ipc_request == request_id && it->handler.get()) { 326 it->handler->OnDevicesEnumerated(it->request_id, device_array); 327 break; 328 } 329 } 330 } 331 332 void MediaStreamDispatcher::OnDeviceOpened( 333 int request_id, 334 const std::string& label, 335 const StreamDeviceInfo& device_info) { 336 DCHECK(thread_checker_.CalledOnValidThread()); 337 for (RequestList::iterator it = requests_.begin(); 338 it != requests_.end(); ++it) { 339 Request& request = *it; 340 if (request.ipc_request == request_id) { 341 Stream new_stream; 342 new_stream.handler = request.handler; 343 if (IsAudioInputMediaType(device_info.device.type)) { 344 new_stream.audio_array.push_back(device_info); 345 } else if (IsVideoMediaType(device_info.device.type)) { 346 new_stream.video_array.push_back(device_info); 347 } else { 348 NOTREACHED(); 349 } 350 label_stream_map_[label] = new_stream; 351 if (request.handler.get()) { 352 request.handler->OnDeviceOpened(request.request_id, label, device_info); 353 DVLOG(1) << "MediaStreamDispatcher::OnDeviceOpened(" 354 << request.request_id << ", " << label << ")"; 355 } 356 requests_.erase(it); 357 break; 358 } 359 } 360 } 361 362 void MediaStreamDispatcher::OnDeviceOpenFailed(int request_id) { 363 DCHECK(thread_checker_.CalledOnValidThread()); 364 for (RequestList::iterator it = requests_.begin(); 365 it != requests_.end(); ++it) { 366 Request& request = *it; 367 if (request.ipc_request == request_id) { 368 if (request.handler.get()) { 369 request.handler->OnDeviceOpenFailed(request.request_id); 370 DVLOG(1) << "MediaStreamDispatcher::OnDeviceOpenFailed(" 371 << request.request_id << ")\n"; 372 } 373 requests_.erase(it); 374 break; 375 } 376 } 377 } 378 379 int MediaStreamDispatcher::audio_session_id(const std::string& label, 380 int index) { 381 DCHECK(thread_checker_.CalledOnValidThread()); 382 LabelStreamMap::iterator it = label_stream_map_.find(label); 383 if (it == label_stream_map_.end() || 384 it->second.audio_array.size() <= static_cast<size_t>(index)) { 385 return StreamDeviceInfo::kNoId; 386 } 387 return it->second.audio_array[index].session_id; 388 } 389 390 bool MediaStreamDispatcher::IsStream(const std::string& label) { 391 DCHECK(thread_checker_.CalledOnValidThread()); 392 return label_stream_map_.find(label) != label_stream_map_.end(); 393 } 394 395 int MediaStreamDispatcher::video_session_id(const std::string& label, 396 int index) { 397 DCHECK(thread_checker_.CalledOnValidThread()); 398 LabelStreamMap::iterator it = label_stream_map_.find(label); 399 if (it == label_stream_map_.end() || 400 it->second.video_array.size() <= static_cast<size_t>(index)) { 401 return StreamDeviceInfo::kNoId; 402 } 403 return it->second.video_array[index].session_id; 404 } 405 406 bool MediaStreamDispatcher::IsAudioDuckingActive() const { 407 DCHECK(thread_checker_.CalledOnValidThread()); 408 LabelStreamMap::const_iterator stream_it = label_stream_map_.begin(); 409 while (stream_it != label_stream_map_.end()) { 410 const StreamDeviceInfoArray& audio_array = stream_it->second.audio_array; 411 for (StreamDeviceInfoArray::const_iterator device_it = audio_array.begin(); 412 device_it != audio_array.end(); ++device_it) { 413 if (device_it->device.input.effects & media::AudioParameters::DUCKING) 414 return true; 415 } 416 ++stream_it; 417 } 418 return false; 419 } 420 421 } // namespace content 422