| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/renderer/media/stream/media_stream_device_observer.h" |
| |
| #include <stddef.h> |
| |
| #include <utility> |
| |
| #include "base/bind_helpers.h" |
| #include "base/logging.h" |
| #include "content/child/child_thread_impl.h" |
| #include "content/public/common/service_names.mojom.h" |
| #include "content/public/renderer/render_frame.h" |
| #include "content/renderer/media/stream/media_stream_dispatcher_eventhandler.h" |
| #include "url/origin.h" |
| |
| namespace content { |
| |
| namespace { |
| |
| bool RemoveStreamDeviceFromArray(const MediaStreamDevice& device, |
| MediaStreamDevices* devices) { |
| for (MediaStreamDevices::iterator device_it = devices->begin(); |
| device_it != devices->end(); ++device_it) { |
| if (device_it->IsSameDevice(device)) { |
| devices->erase(device_it); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| } // namespace |
| |
| struct MediaStreamDeviceObserver::Stream { |
| Stream() {} |
| ~Stream() {} |
| base::WeakPtr<MediaStreamDispatcherEventHandler> handler; |
| MediaStreamDevices audio_devices; |
| MediaStreamDevices video_devices; |
| }; |
| |
| MediaStreamDeviceObserver::MediaStreamDeviceObserver(RenderFrame* render_frame) |
| : RenderFrameObserver(render_frame), binding_(this) { |
| registry_.AddInterface(base::Bind( |
| &MediaStreamDeviceObserver::BindMediaStreamDeviceObserverRequest, |
| base::Unretained(this))); |
| } |
| |
| MediaStreamDeviceObserver::~MediaStreamDeviceObserver() {} |
| |
| MediaStreamDevices MediaStreamDeviceObserver::GetNonScreenCaptureDevices() { |
| MediaStreamDevices video_devices; |
| for (const auto& stream_it : label_stream_map_) { |
| for (const auto& video_device : stream_it.second.video_devices) { |
| if (!IsScreenCaptureMediaType(video_device.type)) |
| video_devices.push_back(video_device); |
| } |
| } |
| return video_devices; |
| } |
| |
| void MediaStreamDeviceObserver::OnInterfaceRequestForFrame( |
| const std::string& interface_name, |
| mojo::ScopedMessagePipeHandle* interface_pipe) { |
| registry_.TryBindInterface(interface_name, interface_pipe); |
| } |
| |
| void MediaStreamDeviceObserver::OnDestruct() { |
| // Do not self-destruct. UserMediaClientImpl owns |this|. |
| } |
| |
| void MediaStreamDeviceObserver::OnDeviceStopped( |
| const std::string& label, |
| const MediaStreamDevice& device) { |
| DVLOG(1) << __func__ << " label=" << label << " device_id=" << device.id; |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| |
| auto it = label_stream_map_.find(label); |
| if (it == label_stream_map_.end()) { |
| // This can happen if a user happen stop a the device from JS at the same |
| // time as the underlying media device is unplugged from the system. |
| return; |
| } |
| Stream* stream = &it->second; |
| if (IsAudioInputMediaType(device.type)) |
| RemoveStreamDeviceFromArray(device, &stream->audio_devices); |
| else |
| RemoveStreamDeviceFromArray(device, &stream->video_devices); |
| |
| if (stream->handler.get()) |
| stream->handler->OnDeviceStopped(device); |
| |
| // |it| could have already been invalidated in the function call above. So we |
| // need to check if |label| is still in |label_stream_map_| again. |
| // Note: this is a quick fix to the crash caused by erasing the invalidated |
| // iterator from |label_stream_map_| (crbug.com/616884). Future work needs to |
| // be done to resolve this re-entrancy issue. |
| it = label_stream_map_.find(label); |
| if (it == label_stream_map_.end()) |
| return; |
| stream = &it->second; |
| if (stream->audio_devices.empty() && stream->video_devices.empty()) |
| label_stream_map_.erase(it); |
| } |
| |
| void MediaStreamDeviceObserver::BindMediaStreamDeviceObserverRequest( |
| mojom::MediaStreamDeviceObserverRequest request) { |
| binding_.Bind(std::move(request)); |
| } |
| |
| void MediaStreamDeviceObserver::AddStream( |
| const std::string& label, |
| const MediaStreamDevices& audio_devices, |
| const MediaStreamDevices& video_devices, |
| const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| |
| Stream stream; |
| stream.handler = event_handler; |
| stream.audio_devices = audio_devices; |
| stream.video_devices = video_devices; |
| |
| label_stream_map_[label] = stream; |
| } |
| |
| void MediaStreamDeviceObserver::AddStream(const std::string& label, |
| const MediaStreamDevice& device) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| |
| Stream stream; |
| if (IsAudioInputMediaType(device.type)) |
| stream.audio_devices.push_back(device); |
| else if (IsVideoInputMediaType(device.type)) |
| stream.video_devices.push_back(device); |
| else |
| NOTREACHED(); |
| |
| label_stream_map_[label] = stream; |
| } |
| |
| bool MediaStreamDeviceObserver::RemoveStream(const std::string& label) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| |
| auto it = label_stream_map_.find(label); |
| if (it == label_stream_map_.end()) |
| return false; |
| |
| label_stream_map_.erase(it); |
| return true; |
| } |
| |
| void MediaStreamDeviceObserver::RemoveStreamDevice( |
| const MediaStreamDevice& device) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| |
| // Remove |device| from all streams in |label_stream_map_|. |
| bool device_found = false; |
| auto stream_it = label_stream_map_.begin(); |
| while (stream_it != label_stream_map_.end()) { |
| MediaStreamDevices& audio_devices = stream_it->second.audio_devices; |
| MediaStreamDevices& video_devices = stream_it->second.video_devices; |
| |
| if (RemoveStreamDeviceFromArray(device, &audio_devices) || |
| RemoveStreamDeviceFromArray(device, &video_devices)) { |
| device_found = true; |
| if (audio_devices.empty() && video_devices.empty()) { |
| label_stream_map_.erase(stream_it++); |
| continue; |
| } |
| } |
| ++stream_it; |
| } |
| DCHECK(device_found); |
| } |
| |
| int MediaStreamDeviceObserver::audio_session_id(const std::string& label) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| |
| auto it = label_stream_map_.find(label); |
| if (it == label_stream_map_.end() || it->second.audio_devices.empty()) |
| return MediaStreamDevice::kNoId; |
| |
| return it->second.audio_devices[0].session_id; |
| } |
| |
| int MediaStreamDeviceObserver::video_session_id(const std::string& label) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| |
| auto it = label_stream_map_.find(label); |
| if (it == label_stream_map_.end() || it->second.video_devices.empty()) |
| return MediaStreamDevice::kNoId; |
| |
| return it->second.video_devices[0].session_id; |
| } |
| |
| } // namespace content |