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 // A request is identified by pair (request_id, handler), or ipc_request. 18 // There could be multiple clients making requests and each has its own 19 // request_id sequence. 20 // The ipc_request is garanteed to be unique when it's created in 21 // MediaStreamDispatcher. 22 struct MediaStreamDispatcher::Request { 23 Request(const base::WeakPtr<MediaStreamDispatcherEventHandler>& handler, 24 int request_id, 25 int ipc_request) 26 : handler(handler), 27 request_id(request_id), 28 ipc_request(ipc_request) { 29 } 30 bool IsThisRequest( 31 int request_id1, 32 const base::WeakPtr<MediaStreamDispatcherEventHandler>& handler1) { 33 return (request_id1 == request_id && handler1.get() == handler.get()); 34 } 35 base::WeakPtr<MediaStreamDispatcherEventHandler> handler; 36 int request_id; 37 int ipc_request; 38 }; 39 40 struct MediaStreamDispatcher::Stream { 41 Stream() {} 42 ~Stream() {} 43 base::WeakPtr<MediaStreamDispatcherEventHandler> handler; 44 StreamDeviceInfoArray audio_array; 45 StreamDeviceInfoArray video_array; 46 }; 47 48 MediaStreamDispatcher::EnumerationRequest::EnumerationRequest( 49 const base::WeakPtr<MediaStreamDispatcherEventHandler>& handler, 50 int request_id) 51 : handler(handler), 52 request_id(request_id) { 53 } 54 55 MediaStreamDispatcher::EnumerationRequest::~EnumerationRequest() {} 56 57 bool MediaStreamDispatcher::EnumerationRequest::IsThisRequest( 58 int request_id1, 59 const base::WeakPtr<MediaStreamDispatcherEventHandler>& handler1) { 60 return (request_id1 == request_id && handler1.get() == handler.get()); 61 } 62 63 MediaStreamDispatcher::EnumerationState::EnumerationState() 64 : ipc_id(-1) { 65 } 66 67 MediaStreamDispatcher::EnumerationState::~EnumerationState() {} 68 69 struct MediaStreamDispatcher::EnumerationState::CachedDevices { 70 CachedDevices(const std::string& label, 71 const StreamDeviceInfoArray& device_array) 72 : label(label), 73 devices(device_array) { 74 } 75 ~CachedDevices() {} 76 77 std::string label; 78 StreamDeviceInfoArray devices; 79 }; 80 81 MediaStreamDispatcher::MediaStreamDispatcher(RenderViewImpl* render_view) 82 : RenderViewObserver(render_view), 83 main_loop_(base::MessageLoopProxy::current()), 84 next_ipc_id_(0) { 85 } 86 87 MediaStreamDispatcher::~MediaStreamDispatcher() {} 88 89 void MediaStreamDispatcher::GenerateStream( 90 int request_id, 91 const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler, 92 const StreamOptions& components, 93 const GURL& security_origin) { 94 DCHECK(main_loop_->BelongsToCurrentThread()); 95 DVLOG(1) << "MediaStreamDispatcher::GenerateStream(" << request_id << ")"; 96 97 requests_.push_back(Request(event_handler, request_id, next_ipc_id_)); 98 Send(new MediaStreamHostMsg_GenerateStream(routing_id(), 99 next_ipc_id_++, 100 components, 101 security_origin)); 102 } 103 104 void MediaStreamDispatcher::CancelGenerateStream( 105 int request_id, 106 const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler) { 107 DCHECK(main_loop_->BelongsToCurrentThread()); 108 DVLOG(1) << "MediaStreamDispatcher::CancelGenerateStream" 109 << ", {request_id = " << request_id << "}"; 110 111 RequestList::iterator it = requests_.begin(); 112 for (; it != requests_.end(); ++it) { 113 if (it->IsThisRequest(request_id, event_handler)) { 114 int ipc_request = it->ipc_request; 115 requests_.erase(it); 116 Send(new MediaStreamHostMsg_CancelGenerateStream(routing_id(), 117 ipc_request)); 118 break; 119 } 120 } 121 } 122 123 void MediaStreamDispatcher::StopStream(const std::string& label) { 124 DCHECK(main_loop_->BelongsToCurrentThread()); 125 DVLOG(1) << "MediaStreamDispatcher::StopStream" 126 << ", {label = " << label << "}"; 127 128 LabelStreamMap::iterator it = label_stream_map_.find(label); 129 if (it == label_stream_map_.end()) 130 return; 131 132 Send(new MediaStreamHostMsg_StopGeneratedStream(routing_id(), label)); 133 label_stream_map_.erase(it); 134 } 135 136 void MediaStreamDispatcher::EnumerateDevices( 137 int request_id, 138 const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler, 139 MediaStreamType type, 140 const GURL& security_origin) { 141 DCHECK(main_loop_->BelongsToCurrentThread()); 142 DCHECK(type == MEDIA_DEVICE_AUDIO_CAPTURE || 143 type == MEDIA_DEVICE_VIDEO_CAPTURE); 144 DVLOG(1) << "MediaStreamDispatcher::EnumerateDevices(" 145 << request_id << ")"; 146 147 EnumerationState* state = 148 (type == MEDIA_DEVICE_AUDIO_CAPTURE ? 149 &audio_enumeration_state_ : &video_enumeration_state_); 150 state->requests.push_back(EnumerationRequest(event_handler, request_id)); 151 152 if (state->cached_devices) { 153 event_handler->OnDevicesEnumerated( 154 request_id, state->cached_devices->devices); 155 } else if (state->ipc_id < 0) { 156 Send(new MediaStreamHostMsg_EnumerateDevices(routing_id(), 157 next_ipc_id_, 158 type, 159 security_origin)); 160 state->ipc_id = next_ipc_id_++; 161 } 162 } 163 164 void MediaStreamDispatcher::StopEnumerateDevices( 165 int request_id, 166 const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler) { 167 DCHECK(main_loop_->BelongsToCurrentThread()); 168 DVLOG(1) << "MediaStreamDispatcher::StopEnumerateDevices(" 169 << request_id << ")"; 170 171 // Remove the request. 172 RemoveEnumerationRequest( 173 request_id, event_handler, &audio_enumeration_state_); 174 RemoveEnumerationRequest( 175 request_id, event_handler, &video_enumeration_state_); 176 } 177 178 void MediaStreamDispatcher::RemoveEnumerationRequest( 179 int request_id, 180 const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler, 181 EnumerationState* state) { 182 EnumerationRequestList* requests = &state->requests; 183 for (EnumerationRequestList::iterator it = requests->begin(); 184 it != requests->end(); ++it) { 185 if (it->IsThisRequest(request_id, event_handler)) { 186 requests->erase(it); 187 if (requests->empty() && state->cached_devices) { 188 // No more request and has a label, try to stop the label 189 // and invalidate the state. 190 Send(new MediaStreamHostMsg_StopGeneratedStream( 191 routing_id(), state->cached_devices->label)); 192 state->ipc_id = -1; 193 state->cached_devices.reset(); 194 } 195 return; 196 } 197 } 198 } 199 200 void MediaStreamDispatcher::OpenDevice( 201 int request_id, 202 const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler, 203 const std::string& device_id, 204 MediaStreamType type, 205 const GURL& security_origin) { 206 DCHECK(main_loop_->BelongsToCurrentThread()); 207 DVLOG(1) << "MediaStreamDispatcher::OpenDevice(" << request_id << ")"; 208 209 requests_.push_back(Request(event_handler, request_id, next_ipc_id_)); 210 Send(new MediaStreamHostMsg_OpenDevice(routing_id(), 211 next_ipc_id_++, 212 device_id, 213 type, 214 security_origin)); 215 } 216 217 void MediaStreamDispatcher::CancelOpenDevice( 218 int request_id, 219 const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler) { 220 CancelGenerateStream(request_id, event_handler); 221 } 222 223 void MediaStreamDispatcher::CloseDevice(const std::string& label) { 224 DCHECK(main_loop_->BelongsToCurrentThread()); 225 DVLOG(1) << "MediaStreamDispatcher::CloseDevice" 226 << ", {label = " << label << "}"; 227 228 StopStream(label); 229 } 230 231 bool MediaStreamDispatcher::Send(IPC::Message* message) { 232 if (!RenderThread::Get()) { 233 delete message; 234 return false; 235 } 236 237 return RenderThread::Get()->Send(message); 238 } 239 240 bool MediaStreamDispatcher::OnMessageReceived(const IPC::Message& message) { 241 bool handled = true; 242 IPC_BEGIN_MESSAGE_MAP(MediaStreamDispatcher, message) 243 IPC_MESSAGE_HANDLER(MediaStreamMsg_StreamGenerated, 244 OnStreamGenerated) 245 IPC_MESSAGE_HANDLER(MediaStreamMsg_StreamGenerationFailed, 246 OnStreamGenerationFailed) 247 IPC_MESSAGE_HANDLER(MediaStreamMsg_StopGeneratedStream, 248 OnStopGeneratedStream) 249 IPC_MESSAGE_HANDLER(MediaStreamMsg_DevicesEnumerated, 250 OnDevicesEnumerated) 251 IPC_MESSAGE_HANDLER(MediaStreamMsg_DevicesEnumerationFailed, 252 OnDevicesEnumerationFailed) 253 IPC_MESSAGE_HANDLER(MediaStreamMsg_DeviceOpened, 254 OnDeviceOpened) 255 IPC_MESSAGE_HANDLER(MediaStreamMsg_DeviceOpenFailed, 256 OnDeviceOpenFailed) 257 IPC_MESSAGE_UNHANDLED(handled = false) 258 IPC_END_MESSAGE_MAP() 259 return handled; 260 } 261 262 void MediaStreamDispatcher::OnStreamGenerated( 263 int request_id, 264 const std::string& label, 265 const StreamDeviceInfoArray& audio_array, 266 const StreamDeviceInfoArray& video_array) { 267 DCHECK(main_loop_->BelongsToCurrentThread()); 268 269 for (RequestList::iterator it = requests_.begin(); 270 it != requests_.end(); ++it) { 271 Request& request = *it; 272 if (request.ipc_request == request_id) { 273 Stream new_stream; 274 new_stream.handler = request.handler; 275 new_stream.audio_array = audio_array; 276 new_stream.video_array = video_array; 277 label_stream_map_[label] = new_stream; 278 if (request.handler.get()) { 279 request.handler->OnStreamGenerated( 280 request.request_id, label, audio_array, video_array); 281 DVLOG(1) << "MediaStreamDispatcher::OnStreamGenerated(" 282 << request.request_id << ", " << label << ")"; 283 } 284 requests_.erase(it); 285 break; 286 } 287 } 288 } 289 290 void MediaStreamDispatcher::OnStreamGenerationFailed(int request_id) { 291 DCHECK(main_loop_->BelongsToCurrentThread()); 292 for (RequestList::iterator it = requests_.begin(); 293 it != requests_.end(); ++it) { 294 Request& request = *it; 295 if (request.ipc_request == request_id) { 296 if (request.handler.get()) { 297 request.handler->OnStreamGenerationFailed(request.request_id); 298 DVLOG(1) << "MediaStreamDispatcher::OnStreamGenerationFailed(" 299 << request.request_id << ")\n"; 300 } 301 requests_.erase(it); 302 break; 303 } 304 } 305 } 306 307 void MediaStreamDispatcher::OnStopGeneratedStream(const std::string& label) { 308 DCHECK(main_loop_->BelongsToCurrentThread()); 309 LabelStreamMap::iterator it = label_stream_map_.find(label); 310 if (it == label_stream_map_.end()) 311 return; 312 313 if (it->second.handler.get()) { 314 it->second.handler->OnStopGeneratedStream(label); 315 DVLOG(1) << "MediaStreamDispatcher::OnStopGeneratedStream(" 316 << label << ")\n"; 317 } 318 label_stream_map_.erase(it); 319 } 320 321 void MediaStreamDispatcher::OnDevicesEnumerated( 322 int request_id, 323 const std::string& label, 324 const StreamDeviceInfoArray& device_array) { 325 DCHECK(main_loop_->BelongsToCurrentThread()); 326 DCHECK_GE(request_id, 0); 327 328 EnumerationState* state; 329 if (request_id == audio_enumeration_state_.ipc_id) { 330 state = &audio_enumeration_state_; 331 } else if (request_id == video_enumeration_state_.ipc_id) { 332 state = &video_enumeration_state_; 333 } else { 334 // This could happen when requester has stopped enumeration while some 335 // enumerated response is on the way. Have to stop the |label| because 336 // this might be the first enumerated device list is received. This also 337 // lead to same label being stopped multiple times. 338 Send(new MediaStreamHostMsg_StopGeneratedStream(routing_id(), label)); 339 return; 340 } 341 342 DCHECK(!label.empty()); 343 state->cached_devices.reset(new EnumerationState::CachedDevices( 344 label, device_array)); 345 346 for (EnumerationRequestList::iterator it = state->requests.begin(); 347 it != state->requests.end(); ++it) { 348 if (it->handler.get()) { 349 it->handler->OnDevicesEnumerated(it->request_id, device_array); 350 DVLOG(1) << "MediaStreamDispatcher::OnDevicesEnumerated(" 351 << it->request_id << ")"; 352 } 353 } 354 } 355 356 void MediaStreamDispatcher::OnDevicesEnumerationFailed(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->OnDevicesEnumerationFailed(request.request_id); 364 DVLOG(1) << "MediaStreamDispatcher::OnDevicesEnumerationFailed(" 365 << request.request_id << ")\n"; 366 } 367 requests_.erase(it); 368 break; 369 } 370 } 371 } 372 373 void MediaStreamDispatcher::OnDeviceOpened( 374 int request_id, 375 const std::string& label, 376 const StreamDeviceInfo& device_info) { 377 DCHECK(main_loop_->BelongsToCurrentThread()); 378 for (RequestList::iterator it = requests_.begin(); 379 it != requests_.end(); ++it) { 380 Request& request = *it; 381 if (request.ipc_request == request_id) { 382 Stream new_stream; 383 new_stream.handler = request.handler; 384 if (IsAudioMediaType(device_info.device.type)) { 385 new_stream.audio_array.push_back(device_info); 386 } else if (IsVideoMediaType(device_info.device.type)) { 387 new_stream.video_array.push_back(device_info); 388 } else { 389 NOTREACHED(); 390 } 391 label_stream_map_[label] = new_stream; 392 if (request.handler.get()) { 393 request.handler->OnDeviceOpened(request.request_id, label, device_info); 394 DVLOG(1) << "MediaStreamDispatcher::OnDeviceOpened(" 395 << request.request_id << ", " << label << ")"; 396 } 397 requests_.erase(it); 398 break; 399 } 400 } 401 } 402 403 void MediaStreamDispatcher::OnDeviceOpenFailed(int request_id) { 404 DCHECK(main_loop_->BelongsToCurrentThread()); 405 for (RequestList::iterator it = requests_.begin(); 406 it != requests_.end(); ++it) { 407 Request& request = *it; 408 if (request.ipc_request == request_id) { 409 if (request.handler.get()) { 410 request.handler->OnDeviceOpenFailed(request.request_id); 411 DVLOG(1) << "MediaStreamDispatcher::OnDeviceOpenFailed(" 412 << request.request_id << ")\n"; 413 } 414 requests_.erase(it); 415 break; 416 } 417 } 418 } 419 420 int MediaStreamDispatcher::audio_session_id(const std::string& label, 421 int index) { 422 LabelStreamMap::iterator it = label_stream_map_.find(label); 423 if (it == label_stream_map_.end()) 424 return StreamDeviceInfo::kNoId; 425 426 DCHECK_GT(it->second.audio_array.size(), static_cast<size_t>(index)); 427 return it->second.audio_array[index].session_id; 428 } 429 430 bool MediaStreamDispatcher::IsStream(const std::string& label) { 431 return label_stream_map_.find(label) != label_stream_map_.end(); 432 } 433 434 int MediaStreamDispatcher::video_session_id(const std::string& label, 435 int index) { 436 LabelStreamMap::iterator it = label_stream_map_.find(label); 437 if (it == label_stream_map_.end()) 438 return StreamDeviceInfo::kNoId; 439 440 DCHECK_GT(it->second.video_array.size(), static_cast<size_t>(index)); 441 return it->second.video_array[index].session_id; 442 } 443 444 } // namespace content 445