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