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 "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(RenderViewImpl* render_view) 65 : RenderViewObserver(render_view), 66 main_loop_(base::MessageLoopProxy::current()), 67 next_ipc_id_(0) { 68 } 69 70 MediaStreamDispatcher::~MediaStreamDispatcher() {} 71 72 void MediaStreamDispatcher::GenerateStream( 73 int request_id, 74 const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler, 75 const StreamOptions& components, 76 const GURL& security_origin) { 77 DCHECK(main_loop_->BelongsToCurrentThread()); 78 DVLOG(1) << "MediaStreamDispatcher::GenerateStream(" << request_id << ")"; 79 80 requests_.push_back(Request(event_handler, request_id, next_ipc_id_)); 81 Send(new MediaStreamHostMsg_GenerateStream(routing_id(), 82 next_ipc_id_++, 83 components, 84 security_origin)); 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 DCHECK(main_loop_->BelongsToCurrentThread()); 139 DCHECK(type == MEDIA_DEVICE_AUDIO_CAPTURE || 140 type == MEDIA_DEVICE_VIDEO_CAPTURE); 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(main_loop_->BelongsToCurrentThread()); 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(main_loop_->BelongsToCurrentThread()); 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(main_loop_->BelongsToCurrentThread()); 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 bool MediaStreamDispatcher::Send(IPC::Message* message) { 211 if (!RenderThread::Get()) { 212 delete message; 213 return false; 214 } 215 216 return RenderThread::Get()->Send(message); 217 } 218 219 bool MediaStreamDispatcher::OnMessageReceived(const IPC::Message& message) { 220 bool handled = true; 221 IPC_BEGIN_MESSAGE_MAP(MediaStreamDispatcher, message) 222 IPC_MESSAGE_HANDLER(MediaStreamMsg_StreamGenerated, 223 OnStreamGenerated) 224 IPC_MESSAGE_HANDLER(MediaStreamMsg_StreamGenerationFailed, 225 OnStreamGenerationFailed) 226 IPC_MESSAGE_HANDLER(MediaStreamMsg_DeviceStopped, 227 OnDeviceStopped) 228 IPC_MESSAGE_HANDLER(MediaStreamMsg_DevicesEnumerated, 229 OnDevicesEnumerated) 230 IPC_MESSAGE_HANDLER(MediaStreamMsg_DeviceOpened, 231 OnDeviceOpened) 232 IPC_MESSAGE_HANDLER(MediaStreamMsg_DeviceOpenFailed, 233 OnDeviceOpenFailed) 234 IPC_MESSAGE_UNHANDLED(handled = false) 235 IPC_END_MESSAGE_MAP() 236 return handled; 237 } 238 239 void MediaStreamDispatcher::OnStreamGenerated( 240 int request_id, 241 const std::string& label, 242 const StreamDeviceInfoArray& audio_array, 243 const StreamDeviceInfoArray& video_array) { 244 DCHECK(main_loop_->BelongsToCurrentThread()); 245 246 for (RequestList::iterator it = requests_.begin(); 247 it != requests_.end(); ++it) { 248 Request& request = *it; 249 if (request.ipc_request == request_id) { 250 Stream new_stream; 251 new_stream.handler = request.handler; 252 new_stream.audio_array = audio_array; 253 new_stream.video_array = video_array; 254 label_stream_map_[label] = new_stream; 255 if (request.handler.get()) { 256 request.handler->OnStreamGenerated( 257 request.request_id, label, audio_array, video_array); 258 DVLOG(1) << "MediaStreamDispatcher::OnStreamGenerated(" 259 << request.request_id << ", " << label << ")"; 260 } 261 requests_.erase(it); 262 break; 263 } 264 } 265 } 266 267 void MediaStreamDispatcher::OnStreamGenerationFailed(int request_id) { 268 DCHECK(main_loop_->BelongsToCurrentThread()); 269 for (RequestList::iterator it = requests_.begin(); 270 it != requests_.end(); ++it) { 271 Request& request = *it; 272 if (request.ipc_request == request_id) { 273 if (request.handler.get()) { 274 request.handler->OnStreamGenerationFailed(request.request_id); 275 DVLOG(1) << "MediaStreamDispatcher::OnStreamGenerationFailed(" 276 << request.request_id << ")\n"; 277 } 278 requests_.erase(it); 279 break; 280 } 281 } 282 } 283 284 void MediaStreamDispatcher::OnDeviceStopped( 285 const std::string& label, 286 const StreamDeviceInfo& device_info) { 287 DCHECK(main_loop_->BelongsToCurrentThread()); 288 DVLOG(1) << "MediaStreamDispatcher::OnDeviceStopped(" 289 << "{label = " << label << "})" 290 << ", {device_id = " << device_info.device.id << "})"; 291 292 LabelStreamMap::iterator it = label_stream_map_.find(label); 293 if (it == label_stream_map_.end()) { 294 // This can happen if a user happen stop a the device from JS at the same 295 // time as the underlying media device is unplugged from the system. 296 return; 297 } 298 Stream* stream = &it->second; 299 if (IsAudioMediaType(device_info.device.type)) 300 RemoveStreamDeviceFromArray(device_info, &stream->audio_array); 301 else 302 RemoveStreamDeviceFromArray(device_info, &stream->video_array); 303 304 if (stream->handler.get()) 305 stream->handler->OnDeviceStopped(label, device_info); 306 307 if (stream->audio_array.empty() && stream->video_array.empty()) 308 label_stream_map_.erase(it); 309 } 310 311 void MediaStreamDispatcher::OnDevicesEnumerated( 312 int request_id, 313 const StreamDeviceInfoArray& device_array) { 314 DCHECK(main_loop_->BelongsToCurrentThread()); 315 DCHECK_GE(request_id, 0); 316 317 for (RequestList::iterator it = requests_.begin(); it != requests_.end(); 318 ++it) { 319 if (it->ipc_request == request_id && it->handler.get()) { 320 it->handler->OnDevicesEnumerated(it->request_id, device_array); 321 break; 322 } 323 } 324 } 325 326 void MediaStreamDispatcher::OnDeviceOpened( 327 int request_id, 328 const std::string& label, 329 const StreamDeviceInfo& device_info) { 330 DCHECK(main_loop_->BelongsToCurrentThread()); 331 for (RequestList::iterator it = requests_.begin(); 332 it != requests_.end(); ++it) { 333 Request& request = *it; 334 if (request.ipc_request == request_id) { 335 Stream new_stream; 336 new_stream.handler = request.handler; 337 if (IsAudioMediaType(device_info.device.type)) { 338 new_stream.audio_array.push_back(device_info); 339 } else if (IsVideoMediaType(device_info.device.type)) { 340 new_stream.video_array.push_back(device_info); 341 } else { 342 NOTREACHED(); 343 } 344 label_stream_map_[label] = new_stream; 345 if (request.handler.get()) { 346 request.handler->OnDeviceOpened(request.request_id, label, device_info); 347 DVLOG(1) << "MediaStreamDispatcher::OnDeviceOpened(" 348 << request.request_id << ", " << label << ")"; 349 } 350 requests_.erase(it); 351 break; 352 } 353 } 354 } 355 356 void MediaStreamDispatcher::OnDeviceOpenFailed(int request_id) { 357 DCHECK(main_loop_->BelongsToCurrentThread()); 358 for (RequestList::iterator it = requests_.begin(); 359 it != requests_.end(); ++it) { 360 Request& request = *it; 361 if (request.ipc_request == request_id) { 362 if (request.handler.get()) { 363 request.handler->OnDeviceOpenFailed(request.request_id); 364 DVLOG(1) << "MediaStreamDispatcher::OnDeviceOpenFailed(" 365 << request.request_id << ")\n"; 366 } 367 requests_.erase(it); 368 break; 369 } 370 } 371 } 372 373 int MediaStreamDispatcher::audio_session_id(const std::string& label, 374 int index) { 375 LabelStreamMap::iterator it = label_stream_map_.find(label); 376 if (it == label_stream_map_.end() || 377 it->second.audio_array.size() <= static_cast<size_t>(index)) { 378 return StreamDeviceInfo::kNoId; 379 } 380 return it->second.audio_array[index].session_id; 381 } 382 383 bool MediaStreamDispatcher::IsStream(const std::string& label) { 384 return label_stream_map_.find(label) != label_stream_map_.end(); 385 } 386 387 int MediaStreamDispatcher::video_session_id(const std::string& label, 388 int index) { 389 LabelStreamMap::iterator it = label_stream_map_.find(label); 390 if (it == label_stream_map_.end() || 391 it->second.video_array.size() <= static_cast<size_t>(index)) { 392 return StreamDeviceInfo::kNoId; 393 } 394 return it->second.video_array[index].session_id; 395 } 396 397 } // namespace content 398