| // 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/audio_input_message_filter.h" |
| |
| #include <string> |
| |
| #include "base/bind.h" |
| #include "base/single_thread_task_runner.h" |
| #include "base/strings/stringprintf.h" |
| #include "content/common/media/audio_messages.h" |
| #include "content/renderer/media/webrtc_logging.h" |
| #include "ipc/ipc_logging.h" |
| #include "ipc/ipc_sender.h" |
| |
| namespace { |
| |
| const int kStreamIDNotSet = -1; |
| |
| void LogMessage(int stream_id, const std::string& msg) { |
| std::ostringstream oss; |
| oss << "[stream_id=" << stream_id << "] AIMF::" << msg; |
| content::WebRtcLogMessage(oss.str()); |
| DVLOG(1) << oss.str(); |
| } |
| |
| } // namespace |
| |
| namespace content { |
| |
| class AudioInputMessageFilter::AudioInputIPCImpl : public media::AudioInputIPC { |
| public: |
| AudioInputIPCImpl(const scoped_refptr<AudioInputMessageFilter>& filter, |
| int render_frame_id); |
| ~AudioInputIPCImpl() override; |
| |
| // media::AudioInputIPC implementation. |
| void CreateStream(media::AudioInputIPCDelegate* delegate, |
| int session_id, |
| const media::AudioParameters& params, |
| bool automatic_gain_control, |
| uint32_t total_segments) override; |
| void RecordStream() override; |
| void SetVolume(double volume) override; |
| void CloseStream() override; |
| |
| private: |
| const scoped_refptr<AudioInputMessageFilter> filter_; |
| const int render_frame_id_; |
| int stream_id_; |
| }; |
| |
| AudioInputMessageFilter* AudioInputMessageFilter::g_filter = nullptr; |
| |
| AudioInputMessageFilter::AudioInputMessageFilter( |
| scoped_refptr<base::SingleThreadTaskRunner> io_task_runner) |
| : sender_(nullptr), io_task_runner_(io_task_runner) { |
| DCHECK(!g_filter); |
| g_filter = this; |
| } |
| |
| AudioInputMessageFilter::~AudioInputMessageFilter() { |
| DCHECK_EQ(g_filter, this); |
| g_filter = nullptr; |
| } |
| |
| // static |
| AudioInputMessageFilter* AudioInputMessageFilter::Get() { |
| return g_filter; |
| } |
| |
| void AudioInputMessageFilter::Send(IPC::Message* message) { |
| DCHECK(io_task_runner_->BelongsToCurrentThread()); |
| if (!sender_) { |
| delete message; |
| } else { |
| sender_->Send(message); |
| } |
| } |
| |
| bool AudioInputMessageFilter::OnMessageReceived(const IPC::Message& message) { |
| DCHECK(io_task_runner_->BelongsToCurrentThread()); |
| bool handled = true; |
| IPC_BEGIN_MESSAGE_MAP(AudioInputMessageFilter, message) |
| IPC_MESSAGE_HANDLER(AudioInputMsg_NotifyStreamCreated, |
| OnStreamCreated) |
| IPC_MESSAGE_HANDLER(AudioInputMsg_NotifyStreamError, OnStreamError) |
| IPC_MESSAGE_HANDLER(AudioInputMsg_NotifyStreamMuted, OnStreamMuted) |
| IPC_MESSAGE_UNHANDLED(handled = false) |
| IPC_END_MESSAGE_MAP() |
| return handled; |
| } |
| |
| void AudioInputMessageFilter::OnFilterAdded(IPC::Channel* channel) { |
| DCHECK(io_task_runner_->BelongsToCurrentThread()); |
| |
| // Captures the sender for IPC. |
| sender_ = channel; |
| } |
| |
| void AudioInputMessageFilter::OnFilterRemoved() { |
| DCHECK(io_task_runner_->BelongsToCurrentThread()); |
| |
| // Once removed, a filter will not be used again. At this time all |
| // delegates must be notified so they release their reference. |
| OnChannelClosing(); |
| } |
| |
| void AudioInputMessageFilter::OnChannelClosing() { |
| DCHECK(io_task_runner_->BelongsToCurrentThread()); |
| sender_ = nullptr; |
| |
| DLOG_IF(WARNING, !delegates_.IsEmpty()) |
| << "Not all audio devices have been closed."; |
| |
| base::IDMap<media::AudioInputIPCDelegate*>::iterator it(&delegates_); |
| while (!it.IsAtEnd()) { |
| it.GetCurrentValue()->OnIPCClosed(); |
| delegates_.Remove(it.GetCurrentKey()); |
| it.Advance(); |
| } |
| } |
| |
| void AudioInputMessageFilter::OnStreamCreated( |
| int stream_id, |
| base::SharedMemoryHandle handle, |
| base::SyncSocket::TransitDescriptor socket_descriptor, |
| bool initially_muted) { |
| DCHECK(io_task_runner_->BelongsToCurrentThread()); |
| LogMessage(stream_id, "OnStreamCreated"); |
| |
| base::SyncSocket::Handle socket_handle = |
| base::SyncSocket::UnwrapHandle(socket_descriptor); |
| media::AudioInputIPCDelegate* delegate = delegates_.Lookup(stream_id); |
| if (!delegate) { |
| DLOG(WARNING) << "Got audio stream event for a non-existent or removed" |
| << " audio capturer (stream_id=" << stream_id << ")."; |
| base::SharedMemory::CloseHandle(handle); |
| base::SyncSocket socket(socket_handle); |
| return; |
| } |
| // Forward message to the stream delegate. |
| delegate->OnStreamCreated(handle, socket_handle, initially_muted); |
| } |
| |
| void AudioInputMessageFilter::OnStreamError(int stream_id) { |
| DCHECK(io_task_runner_->BelongsToCurrentThread()); |
| media::AudioInputIPCDelegate* delegate = delegates_.Lookup(stream_id); |
| if (!delegate) { |
| DLOG(WARNING) << "Got audio stream event for a non-existent or removed" |
| << " audio renderer."; |
| return; |
| } |
| delegate->OnError(); |
| } |
| |
| void AudioInputMessageFilter::OnStreamMuted(int stream_id, bool is_muted) { |
| DCHECK(io_task_runner_->BelongsToCurrentThread()); |
| media::AudioInputIPCDelegate* delegate = delegates_.Lookup(stream_id); |
| if (!delegate) { |
| DLOG(WARNING) << "Got audio stream muted event for a non-existent or " |
| "removed audio renderer."; |
| return; |
| } |
| delegate->OnMuted(is_muted); |
| } |
| |
| AudioInputMessageFilter::AudioInputIPCImpl::AudioInputIPCImpl( |
| const scoped_refptr<AudioInputMessageFilter>& filter, |
| int render_frame_id) |
| : filter_(filter), |
| render_frame_id_(render_frame_id), |
| stream_id_(kStreamIDNotSet) { |
| } |
| |
| AudioInputMessageFilter::AudioInputIPCImpl::~AudioInputIPCImpl() {} |
| |
| std::unique_ptr<media::AudioInputIPC> |
| AudioInputMessageFilter::CreateAudioInputIPC(int render_frame_id) { |
| DCHECK_GT(render_frame_id, 0); |
| return std::unique_ptr<media::AudioInputIPC>( |
| new AudioInputIPCImpl(this, render_frame_id)); |
| } |
| |
| void AudioInputMessageFilter::AudioInputIPCImpl::CreateStream( |
| media::AudioInputIPCDelegate* delegate, |
| int session_id, |
| const media::AudioParameters& params, |
| bool automatic_gain_control, |
| uint32_t total_segments) { |
| DCHECK(filter_->io_task_runner_->BelongsToCurrentThread()); |
| DCHECK(delegate); |
| |
| stream_id_ = filter_->delegates_.Add(delegate); |
| // TODO(henrika): remove all LogMessage calls when we have sorted out the |
| // existing "no input audio" issues. |
| LogMessage(stream_id_, "CreateStream"); |
| |
| AudioInputHostMsg_CreateStream_Config config; |
| config.params = params; |
| config.automatic_gain_control = automatic_gain_control; |
| config.shared_memory_count = total_segments; |
| filter_->Send(new AudioInputHostMsg_CreateStream(stream_id_, render_frame_id_, |
| session_id, config)); |
| } |
| |
| void AudioInputMessageFilter::AudioInputIPCImpl::RecordStream() { |
| DCHECK_NE(stream_id_, kStreamIDNotSet); |
| LogMessage(stream_id_, "RecordStream"); |
| filter_->Send(new AudioInputHostMsg_RecordStream(stream_id_)); |
| } |
| |
| void AudioInputMessageFilter::AudioInputIPCImpl::SetVolume(double volume) { |
| DCHECK_NE(stream_id_, kStreamIDNotSet); |
| DCHECK_GE(volume, 0); |
| DCHECK_LE(volume, 1); |
| filter_->Send(new AudioInputHostMsg_SetVolume(stream_id_, volume)); |
| } |
| |
| void AudioInputMessageFilter::AudioInputIPCImpl::CloseStream() { |
| DCHECK(filter_->io_task_runner_->BelongsToCurrentThread()); |
| DCHECK_NE(stream_id_, kStreamIDNotSet); |
| LogMessage(stream_id_, "CloseStream"); |
| filter_->Send(new AudioInputHostMsg_CloseStream(stream_id_)); |
| filter_->delegates_.Remove(stream_id_); |
| stream_id_ = kStreamIDNotSet; |
| } |
| |
| } // namespace content |