Index: content/renderer/media/renderer_webaudiodevice_impl.cc |
diff --git a/content/renderer/media/renderer_webaudiodevice_impl.cc b/content/renderer/media/renderer_webaudiodevice_impl.cc |
index 81817f79818c0bb0b68cdd31197f4d9a03f731ae..5b697db3057213faf9faf31b197132660c4ae178 100644 |
--- a/content/renderer/media/renderer_webaudiodevice_impl.cc |
+++ b/content/renderer/media/renderer_webaudiodevice_impl.cc |
@@ -6,9 +6,13 @@ |
#include "base/command_line.h" |
#include "base/logging.h" |
+#include "base/single_thread_task_runner.h" |
+#include "base/thread_task_runner_handle.h" |
+#include "base/time/time.h" |
#include "content/renderer/media/audio_device_factory.h" |
#include "content/renderer/render_frame_impl.h" |
#include "media/audio/audio_output_device.h" |
+#include "media/audio/null_audio_sink.h" |
#include "media/base/media_switches.h" |
#include "third_party/WebKit/public/web/WebLocalFrame.h" |
#include "third_party/WebKit/public/web/WebView.h" |
@@ -20,24 +24,34 @@ using blink::WebView; |
namespace content { |
+#if defined(OS_ANDROID) |
+static const int kSilenceInSecondsToEnterIdleMode = 30; |
+#endif |
+ |
RendererWebAudioDeviceImpl::RendererWebAudioDeviceImpl( |
const media::AudioParameters& params, |
WebAudioDevice::RenderCallback* callback, |
int session_id) |
: params_(params), |
client_callback_(callback), |
- session_id_(session_id) { |
+ session_id_(session_id), |
+ task_runner_(base::ThreadTaskRunnerHandle::Get()), |
+ null_audio_sink_(new media::NullAudioSink(task_runner_)), |
+ is_using_null_audio_sink_(false), |
+ first_buffer_after_silence_(media::AudioBus::Create(params_)), |
+ is_first_buffer_after_silence_(false) { |
DCHECK(client_callback_); |
+ null_audio_sink_->Initialize(params_, this); |
} |
RendererWebAudioDeviceImpl::~RendererWebAudioDeviceImpl() { |
- DCHECK(!output_device_.get()); |
+ DCHECK(!output_device_); |
} |
void RendererWebAudioDeviceImpl::start() { |
DCHECK(thread_checker_.CalledOnValidThread()); |
- if (output_device_.get()) |
+ if (output_device_) |
return; // Already started. |
// Assumption: This method is being invoked within a V8 call stack. CHECKs |
@@ -54,16 +68,22 @@ void RendererWebAudioDeviceImpl::start() { |
render_frame ? render_frame->GetRoutingID(): MSG_ROUTING_NONE); |
output_device_->InitializeWithSessionId(params_, this, session_id_); |
output_device_->Start(); |
+ start_null_audio_sink_callback_.Reset( |
+ base::Bind(&media::NullAudioSink::Play, null_audio_sink_)); |
// Note: Default behavior is to auto-play on start. |
} |
void RendererWebAudioDeviceImpl::stop() { |
DCHECK(thread_checker_.CalledOnValidThread()); |
- if (output_device_.get()) { |
+ if (output_device_) { |
output_device_->Stop(); |
output_device_ = NULL; |
} |
+ null_audio_sink_->Stop(); |
+ is_using_null_audio_sink_ = false; |
+ is_first_buffer_after_silence_ = false; |
+ start_null_audio_sink_callback_.Cancel(); |
} |
double RendererWebAudioDeviceImpl::sampleRate() { |
@@ -72,21 +92,59 @@ double RendererWebAudioDeviceImpl::sampleRate() { |
int RendererWebAudioDeviceImpl::Render(media::AudioBus* dest, |
int audio_delay_milliseconds) { |
- if (client_callback_) { |
- // Wrap the output pointers using WebVector. |
- WebVector<float*> web_audio_dest_data( |
- static_cast<size_t>(dest->channels())); |
- for (int i = 0; i < dest->channels(); ++i) |
- web_audio_dest_data[i] = dest->channel(i); |
- |
- // TODO(xians): Remove the following |web_audio_source_data| after |
- // changing the blink interface. |
- WebVector<float*> web_audio_source_data(static_cast<size_t>(0)); |
- client_callback_->render(web_audio_source_data, |
- web_audio_dest_data, |
- dest->frames()); |
+#if defined(OS_ANDROID) |
+ if (is_first_buffer_after_silence_) { |
+ DCHECK(!is_using_null_audio_sink_); |
+ first_buffer_after_silence_->CopyTo(dest); |
+ is_first_buffer_after_silence_ = false; |
+ return dest->frames(); |
} |
- |
+#endif |
+ // Wrap the output pointers using WebVector. |
+ WebVector<float*> web_audio_dest_data( |
+ static_cast<size_t>(dest->channels())); |
+ for (int i = 0; i < dest->channels(); ++i) |
+ web_audio_dest_data[i] = dest->channel(i); |
+ |
+ // TODO(xians): Remove the following |web_audio_source_data| after |
+ // changing the blink interface. |
+ WebVector<float*> web_audio_source_data(static_cast<size_t>(0)); |
+ client_callback_->render(web_audio_source_data, |
+ web_audio_dest_data, |
+ dest->frames()); |
+ |
+#if defined(OS_ANDROID) |
+ const bool is_zero = dest->AreFramesZero(); |
+ if (!is_zero) { |
+ first_silence_time_ = base::TimeTicks(); |
+ if (is_using_null_audio_sink_) { |
+ // This is called on the main render thread when audio is detected. |
+ output_device_->Play(); |
+ is_using_null_audio_sink_ = false; |
+ is_first_buffer_after_silence_ = true; |
+ dest->CopyTo(first_buffer_after_silence_.get()); |
+ task_runner_->PostTask( |
+ FROM_HERE, |
+ base::Bind(&media::NullAudioSink::Stop, null_audio_sink_)); |
+ } |
+ } else if (!is_using_null_audio_sink_) { |
+ // Called on the audio device thread. |
+ const base::TimeTicks now = base::TimeTicks::Now(); |
+ if (first_silence_time_.is_null()) |
+ first_silence_time_ = now; |
+ if (now - first_silence_time_ |
+ > base::TimeDelta::FromSeconds(kSilenceInSecondsToEnterIdleMode)) { |
+ output_device_->Pause(); |
+ is_using_null_audio_sink_ = true; |
+ // If Stop() is called right after the task is posted, need to cancel |
+ // this task. |
+ task_runner_->PostDelayedTask( |
+ FROM_HERE, |
+ start_null_audio_sink_callback_.callback(), |
+ params_.GetBufferDuration()); |
+ } |
+ } |
+#endif |
return dest->frames(); |
} |