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/audio_input_message_filter.h"
6
7#include "base/bind.h"
8#include "base/message_loop/message_loop_proxy.h"
9#include "base/strings/stringprintf.h"
10#include "content/common/media/audio_messages.h"
11#include "content/renderer/media/webrtc_logging.h"
12#include "ipc/ipc_logging.h"
13#include "ipc/ipc_sender.h"
14
15namespace {
16
17const int kStreamIDNotSet = -1;
18
19void LogMessage(int stream_id, const std::string& msg) {
20  std::ostringstream oss;
21  oss << "[stream_id=" << stream_id << "] AIMF::" << msg;
22  content::WebRtcLogMessage(oss.str());
23  DVLOG(1) << oss.str();
24}
25
26}  // namespace
27
28namespace content {
29
30class AudioInputMessageFilter::AudioInputIPCImpl
31    : public NON_EXPORTED_BASE(media::AudioInputIPC) {
32 public:
33  AudioInputIPCImpl(const scoped_refptr<AudioInputMessageFilter>& filter,
34                    int render_view_id);
35  virtual ~AudioInputIPCImpl();
36
37  // media::AudioInputIPC implementation.
38  virtual void CreateStream(media::AudioInputIPCDelegate* delegate,
39                            int session_id,
40                            const media::AudioParameters& params,
41                            bool automatic_gain_control,
42                            uint32 total_segments) OVERRIDE;
43  virtual void RecordStream() OVERRIDE;
44  virtual void SetVolume(double volume) OVERRIDE;
45  virtual void CloseStream() OVERRIDE;
46
47 private:
48  const scoped_refptr<AudioInputMessageFilter> filter_;
49  const int render_view_id_;
50  int stream_id_;
51};
52
53AudioInputMessageFilter* AudioInputMessageFilter::g_filter = NULL;
54
55AudioInputMessageFilter::AudioInputMessageFilter(
56    const scoped_refptr<base::MessageLoopProxy>& io_message_loop)
57    : sender_(NULL),
58      io_message_loop_(io_message_loop) {
59  DCHECK(!g_filter);
60  g_filter = this;
61}
62
63AudioInputMessageFilter::~AudioInputMessageFilter() {
64  DCHECK_EQ(g_filter, this);
65  g_filter = NULL;
66}
67
68// static
69AudioInputMessageFilter* AudioInputMessageFilter::Get() {
70  return g_filter;
71}
72
73void AudioInputMessageFilter::Send(IPC::Message* message) {
74  DCHECK(io_message_loop_->BelongsToCurrentThread());
75  if (!sender_) {
76    delete message;
77  } else {
78    sender_->Send(message);
79  }
80}
81
82bool AudioInputMessageFilter::OnMessageReceived(const IPC::Message& message) {
83  DCHECK(io_message_loop_->BelongsToCurrentThread());
84  bool handled = true;
85  IPC_BEGIN_MESSAGE_MAP(AudioInputMessageFilter, message)
86    IPC_MESSAGE_HANDLER(AudioInputMsg_NotifyStreamCreated,
87                        OnStreamCreated)
88    IPC_MESSAGE_HANDLER(AudioInputMsg_NotifyStreamVolume, OnStreamVolume)
89    IPC_MESSAGE_HANDLER(AudioInputMsg_NotifyStreamStateChanged,
90                        OnStreamStateChanged)
91    IPC_MESSAGE_UNHANDLED(handled = false)
92  IPC_END_MESSAGE_MAP()
93  return handled;
94}
95
96void AudioInputMessageFilter::OnFilterAdded(IPC::Sender* sender) {
97  DCHECK(io_message_loop_->BelongsToCurrentThread());
98
99  // Captures the sender for IPC.
100  sender_ = sender;
101}
102
103void AudioInputMessageFilter::OnFilterRemoved() {
104  DCHECK(io_message_loop_->BelongsToCurrentThread());
105
106  // Once removed, a filter will not be used again.  At this time all
107  // delegates must be notified so they release their reference.
108  OnChannelClosing();
109}
110
111void AudioInputMessageFilter::OnChannelClosing() {
112  DCHECK(io_message_loop_->BelongsToCurrentThread());
113  sender_ = NULL;
114
115  DLOG_IF(WARNING, !delegates_.IsEmpty())
116      << "Not all audio devices have been closed.";
117
118  IDMap<media::AudioInputIPCDelegate>::iterator it(&delegates_);
119  while (!it.IsAtEnd()) {
120    it.GetCurrentValue()->OnIPCClosed();
121    delegates_.Remove(it.GetCurrentKey());
122    it.Advance();
123  }
124}
125
126void AudioInputMessageFilter::OnStreamCreated(
127    int stream_id,
128    base::SharedMemoryHandle handle,
129    base::SyncSocket::TransitDescriptor socket_descriptor,
130    uint32 length,
131    uint32 total_segments) {
132  DCHECK(io_message_loop_->BelongsToCurrentThread());
133  LogMessage(stream_id, "OnStreamCreated");
134
135  base::SyncSocket::Handle socket_handle =
136      base::SyncSocket::UnwrapHandle(socket_descriptor);
137  media::AudioInputIPCDelegate* delegate = delegates_.Lookup(stream_id);
138  if (!delegate) {
139    DLOG(WARNING) << "Got audio stream event for a non-existent or removed"
140                  << " audio capturer (stream_id=" << stream_id << ").";
141    base::SharedMemory::CloseHandle(handle);
142    base::SyncSocket socket(socket_handle);
143    return;
144  }
145  // Forward message to the stream delegate.
146  delegate->OnStreamCreated(handle, socket_handle, length, total_segments);
147}
148
149void AudioInputMessageFilter::OnStreamVolume(int stream_id, double volume) {
150  DCHECK(io_message_loop_->BelongsToCurrentThread());
151  media::AudioInputIPCDelegate* delegate = delegates_.Lookup(stream_id);
152  if (!delegate) {
153    DLOG(WARNING) << "Got audio stream event for a non-existent or removed"
154                  << " audio capturer.";
155    return;
156  }
157  delegate->OnVolume(volume);
158}
159
160void AudioInputMessageFilter::OnStreamStateChanged(
161    int stream_id, media::AudioInputIPCDelegate::State state) {
162  DCHECK(io_message_loop_->BelongsToCurrentThread());
163  media::AudioInputIPCDelegate* delegate = delegates_.Lookup(stream_id);
164  if (!delegate) {
165    DLOG(WARNING) << "Got audio stream event for a non-existent or removed"
166                  << " audio renderer.";
167    return;
168  }
169  delegate->OnStateChanged(state);
170}
171
172AudioInputMessageFilter::AudioInputIPCImpl::AudioInputIPCImpl(
173    const scoped_refptr<AudioInputMessageFilter>& filter, int render_view_id)
174    : filter_(filter),
175      render_view_id_(render_view_id),
176      stream_id_(kStreamIDNotSet) {}
177
178AudioInputMessageFilter::AudioInputIPCImpl::~AudioInputIPCImpl() {}
179
180scoped_ptr<media::AudioInputIPC> AudioInputMessageFilter::CreateAudioInputIPC(
181    int render_view_id) {
182  DCHECK_GT(render_view_id, 0);
183  return scoped_ptr<media::AudioInputIPC>(
184      new AudioInputIPCImpl(this, render_view_id));
185}
186
187void AudioInputMessageFilter::AudioInputIPCImpl::CreateStream(
188    media::AudioInputIPCDelegate* delegate,
189    int session_id,
190    const media::AudioParameters& params,
191    bool automatic_gain_control,
192    uint32 total_segments) {
193  DCHECK(filter_->io_message_loop_->BelongsToCurrentThread());
194  DCHECK(delegate);
195
196  stream_id_ = filter_->delegates_.Add(delegate);
197  // TODO(henrika): remove all LogMessage calls when we have sorted out the
198  // existing "no input audio" issues.
199  LogMessage(stream_id_, "CreateStream");
200
201  AudioInputHostMsg_CreateStream_Config config;
202  config.params = params;
203  config.automatic_gain_control = automatic_gain_control;
204  config.shared_memory_count = total_segments;
205  filter_->Send(new AudioInputHostMsg_CreateStream(
206      stream_id_, render_view_id_, session_id, config));
207}
208
209void AudioInputMessageFilter::AudioInputIPCImpl::RecordStream() {
210  DCHECK_NE(stream_id_, kStreamIDNotSet);
211  LogMessage(stream_id_, "RecordStream");
212  filter_->Send(new AudioInputHostMsg_RecordStream(stream_id_));
213}
214
215void AudioInputMessageFilter::AudioInputIPCImpl::SetVolume(double volume) {
216  DCHECK_NE(stream_id_, kStreamIDNotSet);
217  filter_->Send(new AudioInputHostMsg_SetVolume(stream_id_, volume));
218}
219
220void AudioInputMessageFilter::AudioInputIPCImpl::CloseStream() {
221  DCHECK(filter_->io_message_loop_->BelongsToCurrentThread());
222  DCHECK_NE(stream_id_, kStreamIDNotSet);
223  LogMessage(stream_id_, "CloseStream");
224  filter_->Send(new AudioInputHostMsg_CloseStream(stream_id_));
225  filter_->delegates_.Remove(stream_id_);
226  stream_id_ = kStreamIDNotSet;
227}
228
229}  // namespace content
230