This source file includes following definitions.
- OnRenderError
- OnData
- OnSetFormat
- sink_started_
- Start
- Stop
- Play
- Pause
- SetVolume
- GetCurrentRenderTime
- IsLocalRenderer
- MaybeStartSink
- ReconfigureSink
#include "content/renderer/media/webrtc_local_audio_renderer.h"
#include "base/debug/trace_event.h"
#include "base/logging.h"
#include "base/message_loop/message_loop_proxy.h"
#include "base/metrics/histogram.h"
#include "base/synchronization/lock.h"
#include "content/renderer/media/audio_device_factory.h"
#include "content/renderer/media/webrtc_audio_capturer.h"
#include "media/audio/audio_output_device.h"
#include "media/base/audio_bus.h"
#include "media/base/audio_fifo.h"
namespace content {
namespace {
enum LocalRendererSinkStates {
kSinkStarted = 0,
kSinkNeverStarted,
kSinkStatesMax
};
}
int WebRtcLocalAudioRenderer::Render(
media::AudioBus* audio_bus, int audio_delay_milliseconds) {
TRACE_EVENT0("audio", "WebRtcLocalAudioRenderer::Render");
base::AutoLock auto_lock(thread_lock_);
if (!playing_ || !volume_ || !loopback_fifo_) {
audio_bus->Zero();
return 0;
}
if (loopback_fifo_->frames() >= audio_bus->frames()) {
loopback_fifo_->Consume(audio_bus, 0, audio_bus->frames());
} else {
audio_bus->Zero();
DVLOG(2) << "loopback FIFO is empty";
}
return audio_bus->frames();
}
void WebRtcLocalAudioRenderer::OnRenderError() {
NOTIMPLEMENTED();
}
void WebRtcLocalAudioRenderer::OnData(const int16* audio_data,
int sample_rate,
int number_of_channels,
int number_of_frames) {
DCHECK(capture_thread_checker_.CalledOnValidThread());
TRACE_EVENT0("audio", "WebRtcLocalAudioRenderer::CaptureData");
base::AutoLock auto_lock(thread_lock_);
if (!playing_ || !volume_ || !loopback_fifo_)
return;
if (loopback_fifo_->frames() + number_of_frames <=
loopback_fifo_->max_frames()) {
scoped_ptr<media::AudioBus> audio_source = media::AudioBus::Create(
number_of_channels, number_of_frames);
audio_source->FromInterleaved(audio_data,
audio_source->frames(),
sizeof(audio_data[0]));
loopback_fifo_->Push(audio_source.get());
const base::TimeTicks now = base::TimeTicks::Now();
total_render_time_ += now - last_render_time_;
last_render_time_ = now;
} else {
DVLOG(1) << "FIFO is full";
}
}
void WebRtcLocalAudioRenderer::OnSetFormat(
const media::AudioParameters& params) {
DVLOG(1) << "WebRtcLocalAudioRenderer::OnSetFormat()";
capture_thread_checker_.DetachFromThread();
DCHECK(capture_thread_checker_.CalledOnValidThread());
{
base::AutoLock auto_lock(thread_lock_);
if (source_params_ == params)
return;
source_params_ = params;
sink_params_ = media::AudioParameters(source_params_.format(),
source_params_.channel_layout(), source_params_.channels(),
source_params_.input_channels(), source_params_.sample_rate(),
source_params_.bits_per_sample(),
#if defined(OS_ANDROID)
frames_per_buffer_,
#else
2 * source_params_.frames_per_buffer(),
#endif
source_params_.effects());
loopback_fifo_.reset(new media::AudioFifo(
params.channels(), 10 * params.frames_per_buffer()));
}
message_loop_->PostTask(
FROM_HERE,
base::Bind(&WebRtcLocalAudioRenderer::ReconfigureSink, this,
params));
}
WebRtcLocalAudioRenderer::WebRtcLocalAudioRenderer(
const blink::WebMediaStreamTrack& audio_track,
int source_render_view_id,
int source_render_frame_id,
int session_id,
int frames_per_buffer)
: audio_track_(audio_track),
source_render_view_id_(source_render_view_id),
source_render_frame_id_(source_render_frame_id),
session_id_(session_id),
message_loop_(base::MessageLoopProxy::current()),
playing_(false),
frames_per_buffer_(frames_per_buffer),
volume_(0.0),
sink_started_(false) {
DVLOG(1) << "WebRtcLocalAudioRenderer::WebRtcLocalAudioRenderer()";
}
WebRtcLocalAudioRenderer::~WebRtcLocalAudioRenderer() {
DCHECK(message_loop_->BelongsToCurrentThread());
DCHECK(!sink_.get());
DVLOG(1) << "WebRtcLocalAudioRenderer::~WebRtcLocalAudioRenderer()";
}
void WebRtcLocalAudioRenderer::Start() {
DVLOG(1) << "WebRtcLocalAudioRenderer::Start()";
DCHECK(message_loop_->BelongsToCurrentThread());
MediaStreamAudioSink::AddToAudioTrack(this, audio_track_);
DCHECK(!sink_.get());
sink_ = AudioDeviceFactory::NewOutputDevice(source_render_view_id_,
source_render_frame_id_);
base::AutoLock auto_lock(thread_lock_);
last_render_time_ = base::TimeTicks::Now();
playing_ = false;
}
void WebRtcLocalAudioRenderer::Stop() {
DVLOG(1) << "WebRtcLocalAudioRenderer::Stop()";
DCHECK(message_loop_->BelongsToCurrentThread());
{
base::AutoLock auto_lock(thread_lock_);
playing_ = false;
loopback_fifo_.reset();
}
if (sink_) {
sink_->Stop();
sink_ = NULL;
}
if (!sink_started_) {
UMA_HISTOGRAM_ENUMERATION("Media.LocalRendererSinkStates",
kSinkNeverStarted, kSinkStatesMax);
}
sink_started_ = false;
MediaStreamAudioSink::RemoveFromAudioTrack(this, audio_track_);
}
void WebRtcLocalAudioRenderer::Play() {
DVLOG(1) << "WebRtcLocalAudioRenderer::Play()";
DCHECK(message_loop_->BelongsToCurrentThread());
if (!sink_.get())
return;
{
base::AutoLock auto_lock(thread_lock_);
playing_ = true;
last_render_time_ = base::TimeTicks::Now();
}
MaybeStartSink();
}
void WebRtcLocalAudioRenderer::Pause() {
DVLOG(1) << "WebRtcLocalAudioRenderer::Pause()";
DCHECK(message_loop_->BelongsToCurrentThread());
if (!sink_.get())
return;
base::AutoLock auto_lock(thread_lock_);
playing_ = false;
}
void WebRtcLocalAudioRenderer::SetVolume(float volume) {
DVLOG(1) << "WebRtcLocalAudioRenderer::SetVolume(" << volume << ")";
DCHECK(message_loop_->BelongsToCurrentThread());
{
base::AutoLock auto_lock(thread_lock_);
volume_ = volume;
}
MaybeStartSink();
if (sink_.get())
sink_->SetVolume(volume);
}
base::TimeDelta WebRtcLocalAudioRenderer::GetCurrentRenderTime() const {
DCHECK(message_loop_->BelongsToCurrentThread());
base::AutoLock auto_lock(thread_lock_);
if (!sink_.get())
return base::TimeDelta();
return total_render_time();
}
bool WebRtcLocalAudioRenderer::IsLocalRenderer() const {
return true;
}
void WebRtcLocalAudioRenderer::MaybeStartSink() {
DCHECK(message_loop_->BelongsToCurrentThread());
DVLOG(1) << "WebRtcLocalAudioRenderer::MaybeStartSink()";
if (!sink_.get() || !source_params_.IsValid())
return;
base::AutoLock auto_lock(thread_lock_);
loopback_fifo_->Clear();
if (!sink_params_.IsValid() || !playing_ || !volume_ || sink_started_)
return;
DVLOG(1) << "WebRtcLocalAudioRenderer::MaybeStartSink() -- Starting sink_.";
sink_->InitializeUnifiedStream(sink_params_, this, session_id_);
sink_->Start();
sink_started_ = true;
UMA_HISTOGRAM_ENUMERATION("Media.LocalRendererSinkStates",
kSinkStarted, kSinkStatesMax);
}
void WebRtcLocalAudioRenderer::ReconfigureSink(
const media::AudioParameters& params) {
DCHECK(message_loop_->BelongsToCurrentThread());
DVLOG(1) << "WebRtcLocalAudioRenderer::ReconfigureSink()";
if (!sink_)
return;
if (sink_started_) {
sink_->Stop();
sink_started_ = false;
}
sink_ = AudioDeviceFactory::NewOutputDevice(source_render_view_id_,
source_render_frame_id_);
MaybeStartSink();
}
}