blob: b1f2106e781dba042a90db153fb9506e045fa6dd [file] [log] [blame]
// 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_message_filter.h"
#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 "content/renderer/render_thread_impl.h"
#include "ipc/ipc_logging.h"
namespace content {
namespace {
const int kStreamIDNotSet = -1;
}
class AudioMessageFilter::AudioOutputIPCImpl : public media::AudioOutputIPC {
public:
AudioOutputIPCImpl(const scoped_refptr<AudioMessageFilter>& filter,
int render_frame_id);
~AudioOutputIPCImpl() override;
// media::AudioOutputIPC implementation.
void RequestDeviceAuthorization(media::AudioOutputIPCDelegate* delegate,
int session_id,
const std::string& device_id,
const url::Origin& security_origin) override;
void CreateStream(media::AudioOutputIPCDelegate* delegate,
const media::AudioParameters& params) override;
void PlayStream() override;
void PauseStream() override;
void CloseStream() override;
void SetVolume(double volume) override;
private:
const scoped_refptr<AudioMessageFilter> filter_;
const int render_frame_id_;
int stream_id_;
bool stream_created_;
};
AudioMessageFilter* AudioMessageFilter::g_filter = nullptr;
AudioMessageFilter::AudioMessageFilter(
scoped_refptr<base::SingleThreadTaskRunner> io_task_runner)
: sender_(nullptr), io_task_runner_(io_task_runner) {
DCHECK(!g_filter);
g_filter = this;
}
AudioMessageFilter::~AudioMessageFilter() {
DCHECK_EQ(g_filter, this);
g_filter = nullptr;
}
// static
AudioMessageFilter* AudioMessageFilter::Get() {
return g_filter;
}
AudioMessageFilter::AudioOutputIPCImpl::AudioOutputIPCImpl(
const scoped_refptr<AudioMessageFilter>& filter,
int render_frame_id)
: filter_(filter),
render_frame_id_(render_frame_id),
stream_id_(kStreamIDNotSet),
stream_created_(false) {}
AudioMessageFilter::AudioOutputIPCImpl::~AudioOutputIPCImpl() {}
std::unique_ptr<media::AudioOutputIPC> AudioMessageFilter::CreateAudioOutputIPC(
int render_frame_id) {
DCHECK_GT(render_frame_id, 0);
return std::unique_ptr<media::AudioOutputIPC>(
new AudioOutputIPCImpl(this, render_frame_id));
}
void AudioMessageFilter::AudioOutputIPCImpl::RequestDeviceAuthorization(
media::AudioOutputIPCDelegate* delegate,
int session_id,
const std::string& device_id,
const url::Origin& security_origin) {
DCHECK(filter_->io_task_runner_->BelongsToCurrentThread());
DCHECK(delegate);
DCHECK_EQ(stream_id_, kStreamIDNotSet);
DCHECK(!stream_created_);
stream_id_ = filter_->delegates_.Add(delegate);
filter_->Send(new AudioHostMsg_RequestDeviceAuthorization(
stream_id_, render_frame_id_, session_id, device_id,
url::Origin(security_origin)));
}
void AudioMessageFilter::AudioOutputIPCImpl::CreateStream(
media::AudioOutputIPCDelegate* delegate,
const media::AudioParameters& params) {
DCHECK(filter_->io_task_runner_->BelongsToCurrentThread());
DCHECK(!stream_created_);
if (stream_id_ == kStreamIDNotSet)
stream_id_ = filter_->delegates_.Add(delegate);
filter_->Send(
new AudioHostMsg_CreateStream(stream_id_, render_frame_id_, params));
stream_created_ = true;
}
void AudioMessageFilter::AudioOutputIPCImpl::PlayStream() {
DCHECK(stream_created_);
filter_->Send(new AudioHostMsg_PlayStream(stream_id_));
}
void AudioMessageFilter::AudioOutputIPCImpl::PauseStream() {
DCHECK(stream_created_);
filter_->Send(new AudioHostMsg_PauseStream(stream_id_));
}
void AudioMessageFilter::AudioOutputIPCImpl::CloseStream() {
DCHECK(filter_->io_task_runner_->BelongsToCurrentThread());
DCHECK_NE(stream_id_, kStreamIDNotSet);
filter_->Send(new AudioHostMsg_CloseStream(stream_id_));
filter_->delegates_.Remove(stream_id_);
stream_id_ = kStreamIDNotSet;
stream_created_ = false;
}
void AudioMessageFilter::AudioOutputIPCImpl::SetVolume(double volume) {
DCHECK(stream_created_);
filter_->Send(new AudioHostMsg_SetVolume(stream_id_, volume));
}
void AudioMessageFilter::Send(IPC::Message* message) {
DCHECK(io_task_runner_->BelongsToCurrentThread());
if (!sender_) {
delete message;
} else {
sender_->Send(message);
}
}
bool AudioMessageFilter::OnMessageReceived(const IPC::Message& message) {
DCHECK(io_task_runner_->BelongsToCurrentThread());
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(AudioMessageFilter, message)
IPC_MESSAGE_HANDLER(AudioMsg_NotifyDeviceAuthorized, OnDeviceAuthorized)
IPC_MESSAGE_HANDLER(AudioMsg_NotifyStreamCreated, OnStreamCreated)
IPC_MESSAGE_HANDLER(AudioMsg_NotifyStreamError, OnStreamError)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
}
void AudioMessageFilter::OnFilterAdded(IPC::Channel* channel) {
DCHECK(io_task_runner_->BelongsToCurrentThread());
sender_ = channel;
}
void AudioMessageFilter::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 AudioMessageFilter::OnChannelClosing() {
DCHECK(io_task_runner_->BelongsToCurrentThread());
sender_ = nullptr;
DLOG_IF(WARNING, !delegates_.IsEmpty())
<< "Not all audio devices have been closed.";
base::IDMap<media::AudioOutputIPCDelegate*>::iterator it(&delegates_);
while (!it.IsAtEnd()) {
it.GetCurrentValue()->OnIPCClosed();
delegates_.Remove(it.GetCurrentKey());
it.Advance();
}
}
void AudioMessageFilter::OnDeviceAuthorized(
int stream_id,
media::OutputDeviceStatus device_status,
const media::AudioParameters& output_params,
const std::string& matched_device_id) {
DCHECK(io_task_runner_->BelongsToCurrentThread());
media::AudioOutputIPCDelegate* delegate = delegates_.Lookup(stream_id);
if (!delegate)
return;
delegate->OnDeviceAuthorized(device_status, output_params, matched_device_id);
}
void AudioMessageFilter::OnStreamCreated(
int stream_id,
base::SharedMemoryHandle handle,
base::SyncSocket::TransitDescriptor socket_descriptor) {
DCHECK(io_task_runner_->BelongsToCurrentThread());
WebRtcLogMessage(base::StringPrintf(
"AMF::OnStreamCreated. stream_id=%d",
stream_id));
base::SyncSocket::Handle socket_handle =
base::SyncSocket::UnwrapHandle(socket_descriptor);
media::AudioOutputIPCDelegate* delegate = delegates_.Lookup(stream_id);
if (!delegate) {
DLOG(WARNING) << "Got OnStreamCreated() event for a non-existent or removed"
<< " audio renderer. (stream_id=" << stream_id << ").";
base::SharedMemory::CloseHandle(handle);
base::SyncSocket socket(socket_handle);
return;
}
delegate->OnStreamCreated(handle, socket_handle);
}
void AudioMessageFilter::OnStreamError(int stream_id) {
DCHECK(io_task_runner_->BelongsToCurrentThread());
media::AudioOutputIPCDelegate* delegate = delegates_.Lookup(stream_id);
if (!delegate) {
DLOG(WARNING) << "Got OnStreamError() event for a non-existent or"
<< " removed audio renderer.";
return;
}
delegate->OnError();
}
} // namespace content