summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/media/audio_stream_indicator.cc16
-rw-r--r--chrome/browser/media/audio_stream_indicator.h2
-rw-r--r--chrome/browser/media/media_capture_devices_dispatcher.cc5
-rw-r--r--chrome/browser/media/media_capture_devices_dispatcher.h2
-rw-r--r--content/browser/renderer_host/media/audio_renderer_host.cc364
-rw-r--r--content/browser/renderer_host/media/audio_renderer_host.h38
-rw-r--r--content/browser/renderer_host/media/audio_renderer_host_unittest.cc10
-rw-r--r--content/public/browser/media_observer.h5
-rw-r--r--media/audio/audio_output_controller.cc38
-rw-r--r--media/audio/audio_output_controller.h25
-rw-r--r--media/audio/audio_output_controller_unittest.cc35
-rw-r--r--media/audio/audio_silence_detector.cc134
-rw-r--r--media/audio/audio_silence_detector.h112
-rw-r--r--media/audio/audio_silence_detector_unittest.cc227
-rw-r--r--media/media.gyp3
15 files changed, 291 insertions, 725 deletions
diff --git a/chrome/browser/media/audio_stream_indicator.cc b/chrome/browser/media/audio_stream_indicator.cc
index 6cd6232..91396ce 100644
--- a/chrome/browser/media/audio_stream_indicator.cc
+++ b/chrome/browser/media/audio_stream_indicator.cc
@@ -18,16 +18,16 @@ using content::WebContents;
AudioStreamIndicator::AudioStreamIndicator() {}
AudioStreamIndicator::~AudioStreamIndicator() {}
-void AudioStreamIndicator::UpdateWebContentsStatus(
- int render_process_id, int render_view_id, int stream_id,
- bool is_playing_and_audible) {
+void AudioStreamIndicator::UpdateWebContentsStatus(int render_process_id,
+ int render_view_id,
+ int stream_id,
+ bool playing) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
- base::Bind(&AudioStreamIndicator::UpdateWebContentsStatusOnUIThread, this,
- render_process_id, render_view_id, stream_id,
- is_playing_and_audible));
+ base::Bind(&AudioStreamIndicator::UpdateWebContentsStatusOnUIThread,
+ this, render_process_id, render_view_id, stream_id, playing));
}
bool AudioStreamIndicator::IsPlayingAudio(WebContents* contents) {
@@ -56,10 +56,10 @@ void AudioStreamIndicator::UpdateWebContentsStatusOnUIThread(
int render_process_id,
int render_view_id,
int stream_id,
- bool is_playing_and_audible) {
+ bool playing) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
RenderViewId id(render_process_id, render_view_id);
- if (is_playing_and_audible) {
+ if (playing) {
audio_streams_[id].insert(stream_id);
} else {
std::map<RenderViewId, std::set<int> >::iterator it =
diff --git a/chrome/browser/media/audio_stream_indicator.h b/chrome/browser/media/audio_stream_indicator.h
index 2ce504e..a996830 100644
--- a/chrome/browser/media/audio_stream_indicator.h
+++ b/chrome/browser/media/audio_stream_indicator.h
@@ -23,7 +23,7 @@ class AudioStreamIndicator
void UpdateWebContentsStatus(int render_process_id,
int render_view_id,
int stream_id,
- bool is_playing_and_audible);
+ bool playing);
// This method should be called on the UI thread.
bool IsPlayingAudio(content::WebContents* contents);
diff --git a/chrome/browser/media/media_capture_devices_dispatcher.cc b/chrome/browser/media/media_capture_devices_dispatcher.cc
index 1997d69..062012c 100644
--- a/chrome/browser/media/media_capture_devices_dispatcher.cc
+++ b/chrome/browser/media/media_capture_devices_dispatcher.cc
@@ -204,12 +204,11 @@ void MediaCaptureDevicesDispatcher::OnMediaRequestStateChanged(
}
void MediaCaptureDevicesDispatcher::OnAudioStreamPlayingChanged(
- int render_process_id, int render_view_id, int stream_id,
- bool is_playing_and_audible) {
+ int render_process_id, int render_view_id, int stream_id, bool playing) {
audio_stream_indicator_->UpdateWebContentsStatus(render_process_id,
render_view_id,
stream_id,
- is_playing_and_audible);
+ playing);
}
void MediaCaptureDevicesDispatcher::UpdateAudioDevicesOnUIThread(
diff --git a/chrome/browser/media/media_capture_devices_dispatcher.h b/chrome/browser/media/media_capture_devices_dispatcher.h
index 5186946..f156ffb 100644
--- a/chrome/browser/media/media_capture_devices_dispatcher.h
+++ b/chrome/browser/media/media_capture_devices_dispatcher.h
@@ -97,7 +97,7 @@ class MediaCaptureDevicesDispatcher : public content::MediaObserver {
int render_process_id,
int render_view_id,
int stream_id,
- bool is_playing_and_audible) OVERRIDE;
+ bool playing) OVERRIDE;
scoped_refptr<MediaStreamCaptureIndicator> GetMediaStreamCaptureIndicator();
diff --git a/content/browser/renderer_host/media/audio_renderer_host.cc b/content/browser/renderer_host/media/audio_renderer_host.cc
index 0853b80..f57d182 100644
--- a/content/browser/renderer_host/media/audio_renderer_host.cc
+++ b/content/browser/renderer_host/media/audio_renderer_host.cc
@@ -5,7 +5,6 @@
#include "content/browser/renderer_host/media/audio_renderer_host.h"
#include "base/bind.h"
-#include "base/bind_helpers.h"
#include "base/metrics/histogram.h"
#include "base/process.h"
#include "base/shared_memory.h"
@@ -24,82 +23,34 @@ using media::AudioBus;
namespace content {
-class AudioRendererHost::AudioEntry
- : public media::AudioOutputController::EventHandler {
- public:
- AudioEntry(AudioRendererHost* host,
- int stream_id,
- int render_view_id,
- const media::AudioParameters& params,
- scoped_ptr<base::SharedMemory> shared_memory,
- scoped_ptr<media::AudioOutputController::SyncReader> reader);
- virtual ~AudioEntry();
-
- int stream_id() const {
- return stream_id_;
- }
-
- int render_view_id() const {
- return render_view_id_;
- }
- // TODO(miu): Remove this setter once my IPC clean-up change (in code review)
- // lands!
- void set_render_view_id(int render_view_id) {
- render_view_id_ = render_view_id;
- }
-
- media::AudioOutputController* controller() const {
- return controller_;
- }
-
- base::SharedMemory* shared_memory() {
- return shared_memory_.get();
- }
-
- media::AudioOutputController::SyncReader* reader() const {
- return reader_.get();
- }
+struct AudioRendererHost::AudioEntry {
+ AudioEntry();
+ ~AudioEntry();
- private:
- // media::AudioOutputController::EventHandler implementation.
- virtual void OnCreated() OVERRIDE;
- virtual void OnPlaying() OVERRIDE;
- virtual void OnAudible(bool is_audible) OVERRIDE;
- virtual void OnPaused() OVERRIDE;
- virtual void OnError(int error_code) OVERRIDE;
- virtual void OnDeviceChange(int new_buffer_size, int new_sample_rate)
- OVERRIDE;
+ // The AudioOutputController that manages the audio stream.
+ scoped_refptr<media::AudioOutputController> controller;
- AudioRendererHost* const host_;
- const int stream_id_;
+ // The audio stream ID.
+ int stream_id;
// The routing ID of the source render view.
- int render_view_id_;
-
- // The AudioOutputController that manages the audio stream.
- const scoped_refptr<media::AudioOutputController> controller_;
+ int render_view_id;
// Shared memory for transmission of the audio data.
- const scoped_ptr<base::SharedMemory> shared_memory_;
+ base::SharedMemory shared_memory;
+
+ // The synchronous reader to be used by the controller. We have the
+ // ownership of the reader.
+ scoped_ptr<media::AudioOutputController::SyncReader> reader;
- // The synchronous reader to be used by the controller.
- const scoped_ptr<media::AudioOutputController::SyncReader> reader_;
+ // Set to true after we called Close() for the controller.
+ bool pending_close;
};
-AudioRendererHost::AudioEntry::AudioEntry(
- AudioRendererHost* host, int stream_id, int render_view_id,
- const media::AudioParameters& params,
- scoped_ptr<base::SharedMemory> shared_memory,
- scoped_ptr<media::AudioOutputController::SyncReader> reader)
- : host_(host),
- stream_id_(stream_id),
- render_view_id_(render_view_id),
- ALLOW_THIS_IN_INITIALIZER_LIST(
- controller_(media::AudioOutputController::Create(
- host->audio_manager_, this, params, reader.get()))),
- shared_memory_(shared_memory.Pass()),
- reader_(reader.Pass()) {
- DCHECK(controller_);
+AudioRendererHost::AudioEntry::AudioEntry()
+ : stream_id(0),
+ render_view_id(MSG_ROUTING_NONE),
+ pending_close(false) {
}
AudioRendererHost::AudioEntry::~AudioEntry() {}
@@ -123,77 +74,76 @@ AudioRendererHost::~AudioRendererHost() {
}
void AudioRendererHost::OnChannelClosing() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
-
BrowserMessageFilter::OnChannelClosing();
// Since the IPC channel is gone, close all requested audio streams.
- AudioEntryMap zombies;
- zombies.swap(audio_entries_);
- for (AudioEntryMap::iterator i = zombies.begin(); i != zombies.end(); ++i) {
- CloseAndDeleteStream(i->second);
- }
+ DeleteEntries();
}
void AudioRendererHost::OnDestruct() const {
BrowserThread::DeleteOnIOThread::Destruct(this);
}
-void AudioRendererHost::AudioEntry::OnCreated() {
- BrowserThread::PostTask(
- BrowserThread::IO,
- FROM_HERE,
- base::Bind(&AudioRendererHost::DoCompleteCreation, host_, this));
-}
-
-void AudioRendererHost::AudioEntry::OnPlaying() {
+///////////////////////////////////////////////////////////////////////////////
+// media::AudioOutputController::EventHandler implementations.
+void AudioRendererHost::OnCreated(media::AudioOutputController* controller) {
BrowserThread::PostTask(
BrowserThread::IO,
FROM_HERE,
base::Bind(
- base::IgnoreResult(&AudioRendererHost::Send), host_,
- new AudioMsg_NotifyStreamStateChanged(
- stream_id_, media::AudioOutputIPCDelegate::kPlaying)));
+ &AudioRendererHost::DoCompleteCreation,
+ this,
+ make_scoped_refptr(controller)));
}
-void AudioRendererHost::AudioEntry::OnAudible(bool is_audible) {
+void AudioRendererHost::OnPlaying(media::AudioOutputController* controller) {
BrowserThread::PostTask(
BrowserThread::IO,
FROM_HERE,
- base::Bind(&AudioRendererHost::DoNotifyAudibleState, host_,
- this, is_audible));
+ base::Bind(
+ &AudioRendererHost::DoSendPlayingMessage,
+ this,
+ make_scoped_refptr(controller)));
}
-void AudioRendererHost::AudioEntry::OnPaused() {
+void AudioRendererHost::OnPaused(media::AudioOutputController* controller) {
BrowserThread::PostTask(
BrowserThread::IO,
FROM_HERE,
base::Bind(
- base::IgnoreResult(&AudioRendererHost::Send), host_,
- new AudioMsg_NotifyStreamStateChanged(
- stream_id_, media::AudioOutputIPCDelegate::kPaused)));
+ &AudioRendererHost::DoSendPausedMessage,
+ this,
+ make_scoped_refptr(controller)));
}
-void AudioRendererHost::AudioEntry::OnError(int error_code) {
+void AudioRendererHost::OnError(media::AudioOutputController* controller,
+ int error_code) {
BrowserThread::PostTask(
BrowserThread::IO,
FROM_HERE,
- base::Bind(&AudioRendererHost::DeleteEntryOnError, host_, this));
+ base::Bind(&AudioRendererHost::DoHandleError,
+ this, make_scoped_refptr(controller), error_code));
}
-void AudioRendererHost::AudioEntry::OnDeviceChange(int new_buffer_size,
- int new_sample_rate) {
+void AudioRendererHost::OnDeviceChange(media::AudioOutputController* controller,
+ int new_buffer_size,
+ int new_sample_rate) {
BrowserThread::PostTask(
BrowserThread::IO,
FROM_HERE,
- base::Bind(base::IgnoreResult(&AudioRendererHost::Send), host_,
- new AudioMsg_NotifyDeviceChanged(
- stream_id_, new_buffer_size, new_sample_rate)));
+ base::Bind(&AudioRendererHost::DoSendDeviceChangeMessage,
+ this, make_scoped_refptr(controller), new_buffer_size,
+ new_sample_rate));
}
-void AudioRendererHost::DoCompleteCreation(AudioEntry* entry) {
+void AudioRendererHost::DoCompleteCreation(
+ media::AudioOutputController* controller) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ AudioEntry* entry = LookupByController(controller);
+ if (!entry)
+ return;
+
if (!peer_handle()) {
NOTREACHED() << "Renderer process handle is invalid.";
DeleteEntryOnError(entry);
@@ -203,15 +153,16 @@ void AudioRendererHost::DoCompleteCreation(AudioEntry* entry) {
// Once the audio stream is created then complete the creation process by
// mapping shared memory and sharing with the renderer process.
base::SharedMemoryHandle foreign_memory_handle;
- if (!entry->shared_memory()->ShareToProcess(peer_handle(),
- &foreign_memory_handle)) {
+ if (!entry->shared_memory.ShareToProcess(peer_handle(),
+ &foreign_memory_handle)) {
// If we failed to map and share the shared memory then close the audio
// stream and send an error message.
DeleteEntryOnError(entry);
return;
}
- AudioSyncReader* reader = static_cast<AudioSyncReader*>(entry->reader());
+ AudioSyncReader* reader =
+ static_cast<AudioSyncReader*>(entry->reader.get());
#if defined(OS_WIN)
base::SyncSocket::Handle foreign_socket_handle;
@@ -228,29 +179,59 @@ void AudioRendererHost::DoCompleteCreation(AudioEntry* entry) {
}
Send(new AudioMsg_NotifyStreamCreated(
- entry->stream_id(),
+ entry->stream_id,
foreign_memory_handle,
foreign_socket_handle,
- media::PacketSizeInBytes(entry->shared_memory()->created_size())));
+ media::PacketSizeInBytes(entry->shared_memory.created_size())));
}
-void AudioRendererHost::DoNotifyAudibleState(AudioEntry* entry,
- bool is_audible) {
+void AudioRendererHost::DoSendPlayingMessage(
+ media::AudioOutputController* controller) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- MediaObserver* const media_observer =
- GetContentClient()->browser()->GetMediaObserver();
- if (media_observer) {
- DVLOG(1) << "AudioRendererHost@" << this
- << "::DoNotifyAudibleState(is_audible=" << is_audible
- << ") for stream_id=" << entry->stream_id();
+ AudioEntry* entry = LookupByController(controller);
+ if (!entry)
+ return;
- media_observer->OnAudioStreamPlayingChanged(
- render_process_id_, entry->render_view_id(), entry->stream_id(),
- is_audible);
- }
-};
+ Send(new AudioMsg_NotifyStreamStateChanged(
+ entry->stream_id, media::AudioOutputIPCDelegate::kPlaying));
+}
+void AudioRendererHost::DoSendPausedMessage(
+ media::AudioOutputController* controller) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ AudioEntry* entry = LookupByController(controller);
+ if (!entry)
+ return;
+
+ Send(new AudioMsg_NotifyStreamStateChanged(
+ entry->stream_id, media::AudioOutputIPCDelegate::kPaused));
+}
+
+void AudioRendererHost::DoSendDeviceChangeMessage(
+ media::AudioOutputController* controller, int new_buffer_size,
+ int new_sample_rate) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ AudioEntry* entry = LookupByController(controller);
+ if (!entry)
+ return;
+
+ Send(new AudioMsg_NotifyDeviceChanged(
+ entry->stream_id, new_buffer_size, new_sample_rate));
+}
+
+void AudioRendererHost::DoHandleError(media::AudioOutputController* controller,
+ int error_code) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ AudioEntry* entry = LookupByController(controller);
+ if (!entry)
+ return;
+
+ DeleteEntryOnError(entry);
+}
///////////////////////////////////////////////////////////////////////////////
// IPC Messages handler
@@ -284,35 +265,54 @@ void AudioRendererHost::OnCreateStream(
return;
}
+ media::AudioParameters audio_params(params);
+
// Calculate output and input memory size.
- int output_memory_size = AudioBus::CalculateMemorySize(params);
- int frames = params.frames_per_buffer();
+ int output_memory_size = AudioBus::CalculateMemorySize(audio_params);
+
+ int frames = audio_params.frames_per_buffer();
int input_memory_size =
AudioBus::CalculateMemorySize(input_channels, frames);
+ scoped_ptr<AudioEntry> entry(new AudioEntry());
+
// Create the shared memory and share with the renderer process.
// For synchronized I/O (if input_channels > 0) then we allocate
// extra memory after the output data for the input data.
uint32 io_buffer_size = output_memory_size + input_memory_size;
+
uint32 shared_memory_size =
media::TotalSharedMemorySizeInBytes(io_buffer_size);
- scoped_ptr<base::SharedMemory> shared_memory(new base::SharedMemory());
- if (!shared_memory->CreateAndMapAnonymous(shared_memory_size)) {
+ if (!entry->shared_memory.CreateAndMapAnonymous(shared_memory_size)) {
+ // If creation of shared memory failed then send an error message.
SendErrorMessage(stream_id);
return;
}
+ // Create sync reader and try to initialize it.
scoped_ptr<AudioSyncReader> reader(
- new AudioSyncReader(shared_memory.get(), params, input_channels));
+ new AudioSyncReader(&entry->shared_memory, params, input_channels));
+
if (!reader->Init()) {
SendErrorMessage(stream_id);
return;
}
- audio_entries_.insert(std::make_pair(stream_id, new AudioEntry(
- this, stream_id, MSG_ROUTING_NONE, params, shared_memory.Pass(),
- reader.PassAs<media::AudioOutputController::SyncReader>())));
+ // If we have successfully created the SyncReader then assign it to the
+ // entry and construct an AudioOutputController.
+ entry->reader.reset(reader.release());
+ entry->controller = media::AudioOutputController::Create(
+ audio_manager_, this, audio_params, entry->reader.get());
+ if (!entry->controller) {
+ SendErrorMessage(stream_id);
+ return;
+ }
+
+ // If we have created the controller successfully, create an entry and add it
+ // to the map.
+ entry->stream_id = stream_id;
+ audio_entries_.insert(std::make_pair(stream_id, entry.release()));
if (media_internals_)
media_internals_->OnSetAudioStreamStatus(this, stream_id, "created");
}
@@ -331,7 +331,7 @@ void AudioRendererHost::OnAssociateStreamWithProducer(int stream_id,
return;
}
- if (entry->render_view_id() == render_view_id)
+ if (entry->render_view_id == render_view_id)
return;
// TODO(miu): Merge "AssociateWithProducer" message into "CreateStream"
@@ -339,12 +339,12 @@ void AudioRendererHost::OnAssociateStreamWithProducer(int stream_id,
// once" scheme. http://crbug.com/166779
if (mirroring_manager_) {
mirroring_manager_->RemoveDiverter(
- render_process_id_, entry->render_view_id(), entry->controller());
+ render_process_id_, entry->render_view_id, entry->controller);
}
- entry->set_render_view_id(render_view_id);
+ entry->render_view_id = render_view_id;
if (mirroring_manager_) {
mirroring_manager_->AddDiverter(
- render_process_id_, entry->render_view_id(), entry->controller());
+ render_process_id_, entry->render_view_id, entry->controller);
}
}
@@ -357,9 +357,16 @@ void AudioRendererHost::OnPlayStream(int stream_id) {
return;
}
- entry->controller()->Play();
+ entry->controller->Play();
if (media_internals_)
media_internals_->OnSetAudioStreamPlaying(this, stream_id, true);
+
+ MediaObserver* media_observer =
+ GetContentClient()->browser()->GetMediaObserver();
+ if (media_observer) {
+ media_observer->OnAudioStreamPlayingChanged(
+ render_process_id_, entry->render_view_id, stream_id, true);
+ }
}
void AudioRendererHost::OnPauseStream(int stream_id) {
@@ -371,9 +378,16 @@ void AudioRendererHost::OnPauseStream(int stream_id) {
return;
}
- entry->controller()->Pause();
+ entry->controller->Pause();
if (media_internals_)
media_internals_->OnSetAudioStreamPlaying(this, stream_id, false);
+
+ MediaObserver* media_observer =
+ GetContentClient()->browser()->GetMediaObserver();
+ if (media_observer) {
+ media_observer->OnAudioStreamPlayingChanged(
+ render_process_id_, entry->render_view_id, stream_id, false);
+ }
}
void AudioRendererHost::OnFlushStream(int stream_id) {
@@ -385,7 +399,7 @@ void AudioRendererHost::OnFlushStream(int stream_id) {
return;
}
- entry->controller()->Flush();
+ entry->controller->Flush();
if (media_internals_)
media_internals_->OnSetAudioStreamStatus(this, stream_id, "flushed");
}
@@ -416,9 +430,17 @@ void AudioRendererHost::OnSetVolume(int stream_id, double volume) {
// Make sure the volume is valid.
if (volume < 0 || volume > 1.0)
return;
- entry->controller()->SetVolume(volume);
+ entry->controller->SetVolume(volume);
if (media_internals_)
media_internals_->OnSetAudioStreamVolume(this, stream_id, volume);
+
+ MediaObserver* media_observer =
+ GetContentClient()->browser()->GetMediaObserver();
+ if (media_observer) {
+ bool playing = volume > 0;
+ media_observer->OnAudioStreamPlayingChanged(
+ render_process_id_, entry->render_view_id, stream_id, playing);
+ }
}
void AudioRendererHost::SendErrorMessage(int32 stream_id) {
@@ -426,18 +448,33 @@ void AudioRendererHost::SendErrorMessage(int32 stream_id) {
stream_id, media::AudioOutputIPCDelegate::kError));
}
-void AudioRendererHost::CloseAndDeleteStream(AudioEntry* entry) {
+void AudioRendererHost::DeleteEntries() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- if (mirroring_manager_) {
- mirroring_manager_->RemoveDiverter(
- render_process_id_, entry->render_view_id(), entry->controller());
+ for (AudioEntryMap::iterator i = audio_entries_.begin();
+ i != audio_entries_.end(); ++i) {
+ CloseAndDeleteStream(i->second);
}
- entry->controller()->Close(
- base::Bind(&AudioRendererHost::DeleteEntry, this, entry));
+}
- // Erase the entry identified by |stream_id| from the map.
- audio_entries_.erase(entry->stream_id());
+void AudioRendererHost::CloseAndDeleteStream(AudioEntry* entry) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ MediaObserver* media_observer =
+ GetContentClient()->browser()->GetMediaObserver();
+ if (media_observer) {
+ media_observer->OnAudioStreamPlayingChanged(
+ render_process_id_, entry->render_view_id, entry->stream_id, false);
+ }
+ if (!entry->pending_close) {
+ if (mirroring_manager_) {
+ mirroring_manager_->RemoveDiverter(
+ render_process_id_, entry->render_view_id, entry->controller);
+ }
+ entry->controller->Close(
+ base::Bind(&AudioRendererHost::DeleteEntry, this, entry));
+ entry->pending_close = true;
+ }
}
void AudioRendererHost::DeleteEntry(AudioEntry* entry) {
@@ -446,17 +483,12 @@ void AudioRendererHost::DeleteEntry(AudioEntry* entry) {
// Delete the entry when this method goes out of scope.
scoped_ptr<AudioEntry> entry_deleter(entry);
- // At this point, make the final "say" in audio playback state.
- MediaObserver* const media_observer =
- GetContentClient()->browser()->GetMediaObserver();
- if (media_observer) {
- media_observer->OnAudioStreamPlayingChanged(
- render_process_id_, entry->render_view_id(), entry->stream_id(), false);
- }
+ // Erase the entry identified by |stream_id| from the map.
+ audio_entries_.erase(entry->stream_id);
// Notify the media observer.
if (media_internals_)
- media_internals_->OnDeleteAudioStream(this, entry->stream_id());
+ media_internals_->OnDeleteAudioStream(this, entry->stream_id);
}
void AudioRendererHost::DeleteEntryOnError(AudioEntry* entry) {
@@ -464,18 +496,40 @@ void AudioRendererHost::DeleteEntryOnError(AudioEntry* entry) {
// Sends the error message first before we close the stream because
// |entry| is destroyed in DeleteEntry().
- SendErrorMessage(entry->stream_id());
+ SendErrorMessage(entry->stream_id);
if (media_internals_)
- media_internals_->OnSetAudioStreamStatus(this, entry->stream_id(), "error");
+ media_internals_->OnSetAudioStreamStatus(this, entry->stream_id, "error");
CloseAndDeleteStream(entry);
}
AudioRendererHost::AudioEntry* AudioRendererHost::LookupById(int stream_id) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- AudioEntryMap::const_iterator i = audio_entries_.find(stream_id);
- return i != audio_entries_.end() ? i->second : NULL;
+ AudioEntryMap::iterator i = audio_entries_.find(stream_id);
+ if (i != audio_entries_.end() && !i->second->pending_close)
+ return i->second;
+ return NULL;
+}
+
+AudioRendererHost::AudioEntry* AudioRendererHost::LookupByController(
+ media::AudioOutputController* controller) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ // Iterate the map of entries.
+ // TODO(hclam): Implement a faster look up method.
+ for (AudioEntryMap::iterator i = audio_entries_.begin();
+ i != audio_entries_.end(); ++i) {
+ if (!i->second->pending_close && controller == i->second->controller.get())
+ return i->second;
+ }
+ return NULL;
+}
+
+media::AudioOutputController* AudioRendererHost::LookupControllerByIdForTesting(
+ int stream_id) {
+ AudioEntry* const entry = LookupById(stream_id);
+ return entry ? entry->controller : NULL;
}
} // namespace content
diff --git a/content/browser/renderer_host/media/audio_renderer_host.h b/content/browser/renderer_host/media/audio_renderer_host.h
index b34eee7..f4733b2 100644
--- a/content/browser/renderer_host/media/audio_renderer_host.h
+++ b/content/browser/renderer_host/media/audio_renderer_host.h
@@ -63,7 +63,9 @@ class AudioMirroringManager;
class MediaInternals;
class ResourceContext;
-class CONTENT_EXPORT AudioRendererHost : public BrowserMessageFilter {
+class CONTENT_EXPORT AudioRendererHost
+ : public BrowserMessageFilter,
+ public media::AudioOutputController::EventHandler {
public:
// Called from UI thread from the owner of this object.
AudioRendererHost(int render_process_id,
@@ -77,6 +79,16 @@ class CONTENT_EXPORT AudioRendererHost : public BrowserMessageFilter {
virtual bool OnMessageReceived(const IPC::Message& message,
bool* message_was_ok) OVERRIDE;
+ // AudioOutputController::EventHandler implementations.
+ virtual void OnCreated(media::AudioOutputController* controller) OVERRIDE;
+ virtual void OnPlaying(media::AudioOutputController* controller) OVERRIDE;
+ virtual void OnPaused(media::AudioOutputController* controller) OVERRIDE;
+ virtual void OnError(media::AudioOutputController* controller,
+ int error_code) OVERRIDE;
+ virtual void OnDeviceChange(media::AudioOutputController* controller,
+ int new_buffer_size,
+ int new_sample_rate) OVERRIDE;
+
private:
friend class AudioRendererHostTest;
friend class BrowserThread;
@@ -85,7 +97,7 @@ class CONTENT_EXPORT AudioRendererHost : public BrowserMessageFilter {
FRIEND_TEST_ALL_PREFIXES(AudioRendererHostTest, CreateMockStream);
FRIEND_TEST_ALL_PREFIXES(AudioRendererHostTest, MockStreamDataConversation);
- class AudioEntry;
+ struct AudioEntry;
typedef std::map<int, AudioEntry*> AudioEntryMap;
virtual ~AudioRendererHost();
@@ -120,14 +132,23 @@ class CONTENT_EXPORT AudioRendererHost : public BrowserMessageFilter {
// Complete the process of creating an audio stream. This will set up the
// shared memory or shared socket in low latency mode.
- void DoCompleteCreation(AudioEntry* entry);
+ void DoCompleteCreation(media::AudioOutputController* controller);
+
+ // Send a state change message to the renderer.
+ void DoSendPlayingMessage(media::AudioOutputController* controller);
+ void DoSendPausedMessage(media::AudioOutputController* controller);
+ void DoSendDeviceChangeMessage(media::AudioOutputController* controller,
+ int new_buffer_size, int new_sample_rate);
- // Propagate audible signal to MediaObserver.
- void DoNotifyAudibleState(AudioEntry* entry, bool is_audible);
+ // Handle error coming from audio stream.
+ void DoHandleError(media::AudioOutputController* controller, int error_code);
// Send an error message to the renderer.
void SendErrorMessage(int stream_id);
+ // Delete all audio entry and all audio streams
+ void DeleteEntries();
+
// Closes the stream. The stream is then deleted in DeleteEntry() after it
// is closed.
void CloseAndDeleteStream(AudioEntry* entry);
@@ -143,6 +164,13 @@ class CONTENT_EXPORT AudioRendererHost : public BrowserMessageFilter {
// Returns NULL if not found.
AudioEntry* LookupById(int stream_id);
+ // Search for a AudioEntry having the reference to |controller|.
+ // This method is used to look up an AudioEntry after a controller
+ // event is received.
+ AudioEntry* LookupByController(media::AudioOutputController* controller);
+
+ media::AudioOutputController* LookupControllerByIdForTesting(int stream_id);
+
// ID of the RenderProcessHost that owns this instance.
const int render_process_id_;
diff --git a/content/browser/renderer_host/media/audio_renderer_host_unittest.cc b/content/browser/renderer_host/media/audio_renderer_host_unittest.cc
index 4310e35..eaaadcc 100644
--- a/content/browser/renderer_host/media/audio_renderer_host_unittest.cc
+++ b/content/browser/renderer_host/media/audio_renderer_host_unittest.cc
@@ -275,16 +275,18 @@ class AudioRendererHostTest : public testing::Test {
void SimulateError() {
EXPECT_CALL(*observer_,
OnSetAudioStreamStatus(_, kStreamId, "error"));
- EXPECT_EQ(1u, host_->audio_entries_.size())
+ // Find the first AudioOutputController in the AudioRendererHost.
+ CHECK(host_->audio_entries_.size())
<< "Calls Create() before calling this method";
+ media::AudioOutputController* controller =
+ host_->LookupControllerByIdForTesting(kStreamId);
+ CHECK(controller) << "AudioOutputController not found";
// Expect an error signal sent through IPC.
EXPECT_CALL(*host_, OnStreamError(kStreamId));
// Simulate an error sent from the audio device.
- AudioRendererHost::AudioEntry* const entry = host_->LookupById(kStreamId);
- ASSERT_TRUE(entry);
- host_->DeleteEntryOnError(entry);
+ host_->OnError(controller, 0);
SyncWithAudioThread();
// Expect the audio stream record is removed.
diff --git a/content/public/browser/media_observer.h b/content/public/browser/media_observer.h
index b708d7f..6b61528 100644
--- a/content/public/browser/media_observer.h
+++ b/content/public/browser/media_observer.h
@@ -43,13 +43,12 @@ class MediaObserver {
const MediaStreamDevice& device,
MediaRequestState state) = 0;
- // Called when an audio stream transitions into playing audible or silent
- // sound, and when the stream is stopped.
+ // Called when an audio stream is played or paused.
virtual void OnAudioStreamPlayingChanged(
int render_process_id,
int render_view_id,
int stream_id,
- bool is_playing_and_audible) = 0;
+ bool playing) = 0;
protected:
virtual ~MediaObserver() {}
diff --git a/media/audio/audio_output_controller.cc b/media/audio/audio_output_controller.cc
index d162cc2..9120706 100644
--- a/media/audio/audio_output_controller.cc
+++ b/media/audio/audio_output_controller.cc
@@ -10,7 +10,6 @@
#include "base/threading/platform_thread.h"
#include "base/time.h"
#include "build/build_config.h"
-#include "media/audio/audio_silence_detector.h"
#include "media/audio/audio_util.h"
#include "media/audio/shared_memory_util.h"
@@ -19,17 +18,6 @@ using base::TimeDelta;
namespace media {
-// Amount of contiguous time where all audio is silent before considering the
-// stream to have transitioned and EventHandler::OnAudible() should be called.
-static const int kQuestionableSilencePeriodMillis = 50;
-
-// Sample value range below which audio is considered indistinguishably silent.
-//
-// TODO(miu): This value should be specified in dbFS units rather than full
-// scale. See TODO in audio_silence_detector.h.
-static const float kIndistinguishableSilenceThreshold =
- 1.0f / 4096.0f; // Note: This is approximately -72 dbFS.
-
// Polling-related constants.
const int AudioOutputController::kPollNumAttempts = 3;
const int AudioOutputController::kPollPauseInMilliseconds = 3;
@@ -121,7 +109,7 @@ void AudioOutputController::DoCreate(bool is_for_device_change) {
state_ = kError;
// TODO(hclam): Define error types.
- handler_->OnError(0);
+ handler_->OnError(this, 0);
return;
}
@@ -130,7 +118,7 @@ void AudioOutputController::DoCreate(bool is_for_device_change) {
state_ = kError;
// TODO(hclam): Define error types.
- handler_->OnError(0);
+ handler_->OnError(this, 0);
return;
}
@@ -147,7 +135,7 @@ void AudioOutputController::DoCreate(bool is_for_device_change) {
// And then report we have been created if we haven't done so already.
if (!is_for_device_change)
- handler_->OnCreated();
+ handler_->OnCreated(this);
}
void AudioOutputController::DoPlay() {
@@ -197,20 +185,12 @@ void AudioOutputController::StartStream() {
DCHECK(message_loop_->BelongsToCurrentThread());
state_ = kPlaying;
- silence_detector_.reset(new AudioSilenceDetector(
- params_.sample_rate(),
- TimeDelta::FromMilliseconds(kQuestionableSilencePeriodMillis),
- kIndistinguishableSilenceThreshold));
-
// We start the AudioOutputStream lazily.
AllowEntryToOnMoreIOData();
stream_->Start(this);
- // Tell the event handler that we are now playing, and also start the silence
- // detection notifications.
- handler_->OnPlaying();
- silence_detector_->Start(
- base::Bind(&EventHandler::OnAudible, base::Unretained(handler_)));
+ // Tell the event handler that we are now playing.
+ handler_->OnPlaying(this);
}
void AudioOutputController::StopStream() {
@@ -223,8 +203,6 @@ void AudioOutputController::StopStream() {
} else if (state_ == kPlaying) {
stream_->Stop();
DisallowEntryToOnMoreIOData();
- silence_detector_->Stop(true);
- silence_detector_.reset();
state_ = kPaused;
}
}
@@ -240,7 +218,7 @@ void AudioOutputController::DoPause() {
// Send a special pause mark to the low-latency audio thread.
sync_reader_->UpdatePendingBytes(kPauseMark);
- handler_->OnPaused();
+ handler_->OnPaused(this);
}
void AudioOutputController::DoFlush() {
@@ -281,7 +259,7 @@ void AudioOutputController::DoSetVolume(double volume) {
void AudioOutputController::DoReportError(int code) {
DCHECK(message_loop_->BelongsToCurrentThread());
if (state_ != kClosed)
- handler_->OnError(code);
+ handler_->OnError(this, code);
}
int AudioOutputController::OnMoreData(AudioBus* dest,
@@ -310,8 +288,6 @@ int AudioOutputController::OnMoreIOData(AudioBus* source,
sync_reader_->UpdatePendingBytes(
buffers_state.total_bytes() + frames * params_.GetBytesPerFrame());
- silence_detector_->Scan(dest, frames);
-
AllowEntryToOnMoreIOData();
return frames;
}
diff --git a/media/audio/audio_output_controller.h b/media/audio/audio_output_controller.h
index cfb685f..e8fae22 100644
--- a/media/audio/audio_output_controller.h
+++ b/media/audio/audio_output_controller.h
@@ -8,9 +8,7 @@
#include "base/atomic_ref_count.h"
#include "base/callback.h"
#include "base/memory/ref_counted.h"
-#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
-#include "base/timer.h"
#include "media/audio/audio_buffers_state.h"
#include "media/audio/audio_io.h"
#include "media/audio/audio_manager.h"
@@ -59,8 +57,6 @@ class MessageLoop;
namespace media {
-class AudioSilenceDetector;
-
class MEDIA_EXPORT AudioOutputController
: public base::RefCountedThreadSafe<AudioOutputController>,
public AudioOutputStream::AudioSourceCallback,
@@ -71,12 +67,12 @@ class MEDIA_EXPORT AudioOutputController
// following methods are called on the audio manager thread.
class MEDIA_EXPORT EventHandler {
public:
- virtual void OnCreated() = 0;
- virtual void OnPlaying() = 0;
- virtual void OnAudible(bool is_audible) = 0;
- virtual void OnPaused() = 0;
- virtual void OnError(int error_code) = 0;
- virtual void OnDeviceChange(int new_buffer_size, int new_sample_rate) = 0;
+ virtual void OnCreated(AudioOutputController* controller) = 0;
+ virtual void OnPlaying(AudioOutputController* controller) = 0;
+ virtual void OnPaused(AudioOutputController* controller) = 0;
+ virtual void OnError(AudioOutputController* controller, int error_code) = 0;
+ virtual void OnDeviceChange(AudioOutputController* controller,
+ int new_buffer_size, int new_sample_rate) = 0;
protected:
virtual ~EventHandler() {}
@@ -197,10 +193,6 @@ class MEDIA_EXPORT AudioOutputController
void DoStartDiverting(AudioOutputStream* to_stream);
void DoStopDiverting();
- // Called at regular intervals during playback to check for a change in
- // silence and call EventHandler::OnAudible() when state changes occur.
- void MaybeInvokeAudibleCallback();
-
// Helper methods that start/stop physical stream.
void StartStream();
void StopStream();
@@ -253,11 +245,6 @@ class MEDIA_EXPORT AudioOutputController
// (when starting-up a stream).
base::WeakPtrFactory<AudioOutputController> weak_this_;
- // Scans audio samples from OnMoreIOData() as input and causes
- // EventHandler::OnAudbile() to be called whenever a transition to a period of
- // silence or non-silence is detected.
- scoped_ptr<AudioSilenceDetector> silence_detector_;
-
DISALLOW_COPY_AND_ASSIGN(AudioOutputController);
};
diff --git a/media/audio/audio_output_controller_unittest.cc b/media/audio/audio_output_controller_unittest.cc
index 699810f..944d661 100644
--- a/media/audio/audio_output_controller_unittest.cc
+++ b/media/audio/audio_output_controller_unittest.cc
@@ -38,13 +38,13 @@ class MockAudioOutputControllerEventHandler
public:
MockAudioOutputControllerEventHandler() {}
- MOCK_METHOD0(OnCreated, void());
- MOCK_METHOD0(OnPlaying, void());
- MOCK_METHOD1(OnAudible, void(bool is_audible));
- MOCK_METHOD0(OnPaused, void());
- MOCK_METHOD1(OnError, void(int error_code));
- MOCK_METHOD2(OnDeviceChange, void(int new_buffer_size, int new_sample_rate));
-
+ MOCK_METHOD1(OnCreated, void(AudioOutputController* controller));
+ MOCK_METHOD1(OnPlaying, void(AudioOutputController* controller));
+ MOCK_METHOD1(OnPaused, void(AudioOutputController* controller));
+ MOCK_METHOD2(OnError, void(AudioOutputController* controller,
+ int error_code));
+ MOCK_METHOD3(OnDeviceChange, void(AudioOutputController* controller,
+ int new_buffer_size, int new_sample_rate));
private:
DISALLOW_COPY_AND_ASSIGN(MockAudioOutputControllerEventHandler);
};
@@ -117,7 +117,7 @@ class AudioOutputControllerTest : public testing::Test {
kSampleRate, kBitsPerSample, samples_per_packet);
if (params_.IsValid()) {
- EXPECT_CALL(mock_event_handler_, OnCreated())
+ EXPECT_CALL(mock_event_handler_, OnCreated(NotNull()))
.WillOnce(SignalEvent(&create_event_));
}
@@ -131,17 +131,14 @@ class AudioOutputControllerTest : public testing::Test {
}
void Play() {
- // Expect the event handler to receive one OnPlaying() call and one or more
- // OnAudible() calls.
- EXPECT_CALL(mock_event_handler_, OnPlaying())
+ // Expect the event handler to receive one OnPlaying() call.
+ EXPECT_CALL(mock_event_handler_, OnPlaying(NotNull()))
.WillOnce(SignalEvent(&play_event_));
- EXPECT_CALL(mock_event_handler_, OnAudible(_))
- .Times(AtLeast(1));
// During playback, the mock pretends to provide audio data rendered and
// sent from the render process.
EXPECT_CALL(mock_sync_reader_, UpdatePendingBytes(_))
- .Times(AtLeast(1));
+ .Times(AtLeast(2));
EXPECT_CALL(mock_sync_reader_, Read(_, _))
.WillRepeatedly(DoAll(PopulateBuffer(),
SignalEvent(&read_event_),
@@ -154,7 +151,7 @@ class AudioOutputControllerTest : public testing::Test {
void Pause() {
// Expect the event handler to receive one OnPaused() call.
- EXPECT_CALL(mock_event_handler_, OnPaused())
+ EXPECT_CALL(mock_event_handler_, OnPaused(NotNull()))
.WillOnce(SignalEvent(&pause_event_));
controller_->Pause();
@@ -163,9 +160,9 @@ class AudioOutputControllerTest : public testing::Test {
void ChangeDevice() {
// Expect the event handler to receive one OnPaying() call and no OnPaused()
// call.
- EXPECT_CALL(mock_event_handler_, OnPlaying())
+ EXPECT_CALL(mock_event_handler_, OnPlaying(NotNull()))
.WillOnce(SignalEvent(&play_event_));
- EXPECT_CALL(mock_event_handler_, OnPaused())
+ EXPECT_CALL(mock_event_handler_, OnPaused(NotNull()))
.Times(0);
// Simulate a device change event to AudioOutputController from the
@@ -179,7 +176,7 @@ class AudioOutputControllerTest : public testing::Test {
if (was_playing) {
// Expect the handler to receive one OnPlaying() call as a result of the
// stream switching.
- EXPECT_CALL(mock_event_handler_, OnPlaying())
+ EXPECT_CALL(mock_event_handler_, OnPlaying(NotNull()))
.WillOnce(SignalEvent(&play_event_));
}
@@ -211,7 +208,7 @@ class AudioOutputControllerTest : public testing::Test {
if (was_playing) {
// Expect the handler to receive one OnPlaying() call as a result of the
// stream switching back.
- EXPECT_CALL(mock_event_handler_, OnPlaying())
+ EXPECT_CALL(mock_event_handler_, OnPlaying(NotNull()))
.WillOnce(SignalEvent(&play_event_));
}
diff --git a/media/audio/audio_silence_detector.cc b/media/audio/audio_silence_detector.cc
deleted file mode 100644
index 4f4bc06..0000000
--- a/media/audio/audio_silence_detector.cc
+++ /dev/null
@@ -1,134 +0,0 @@
-// Copyright (c) 2013 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 "media/audio/audio_silence_detector.h"
-
-#include "base/float_util.h"
-#include "base/time.h"
-#include "media/base/audio_bus.h"
-
-using base::AtomicRefCountDec;
-using base::AtomicRefCountInc;
-using base::AtomicRefCountIsOne;
-using base::AtomicRefCountIsZero;
-
-namespace media {
-
-AudioSilenceDetector::AudioSilenceDetector(
- int sample_rate,
- const base::TimeDelta& questionable_silence_period,
- float indistinguishable_silence_threshold)
- : polling_period_(questionable_silence_period),
- frames_before_observing_silence_(
- sample_rate * questionable_silence_period.InSecondsF()),
- silence_threshold_(indistinguishable_silence_threshold),
- frames_silent_so_far_(frames_before_observing_silence_),
- observing_silence_(1),
- was_audible_(false) {
-}
-
-AudioSilenceDetector::~AudioSilenceDetector() {
- DCHECK(thread_checker_.CalledOnValidThread());
- // Note: If active, ~RepeatingTimer() will StopAndAbandon().
-}
-
-void AudioSilenceDetector::Start(const AudibleCallback& callback) {
- DCHECK(thread_checker_.CalledOnValidThread());
- DCHECK(notify_is_audible_.is_null());
- DCHECK(!callback.is_null());
-
- notify_is_audible_ = callback;
- was_audible_ = AtomicRefCountIsZero(&observing_silence_);
- notify_is_audible_.Run(was_audible_);
- poll_timer_.Start(
- FROM_HERE, polling_period_,
- this, &AudioSilenceDetector::MaybeInvokeAudibleCallback);
-}
-
-void AudioSilenceDetector::Stop(bool notify_ending_in_silence) {
- DCHECK(thread_checker_.CalledOnValidThread());
- DCHECK(!notify_is_audible_.is_null());
-
- poll_timer_.Stop();
- if (notify_ending_in_silence)
- notify_is_audible_.Run(false);
- notify_is_audible_.Reset();
-}
-
-void AudioSilenceDetector::Scan(const AudioBus* buffer, int frames) {
- // Determine whether the frames just read are probably silence. If enough
- // frames of silence have been observed, flip the |observing_silence_|
- // boolean, which will be read by another thread.
- if (ProbablyContainsSilence(buffer, frames)) {
- // Note: Prevent indefinite incrementing of |frames_silent_so_far_|, to
- // avoid eventual integer overflow.
- if (frames_silent_so_far_ < frames_before_observing_silence_) {
- frames_silent_so_far_ += frames;
- if (frames_silent_so_far_ >= frames_before_observing_silence_) {
- DCHECK(AtomicRefCountIsZero(&observing_silence_));
- AtomicRefCountInc(&observing_silence_);
- }
- }
- } else {
- if (frames_silent_so_far_ >= frames_before_observing_silence_) {
- DCHECK(AtomicRefCountIsOne(&observing_silence_));
- AtomicRefCountDec(&observing_silence_);
- }
- frames_silent_so_far_ = 0;
- }
-}
-
-void AudioSilenceDetector::MaybeInvokeAudibleCallback() {
- DCHECK(thread_checker_.CalledOnValidThread());
-
- const bool is_now_audible = AtomicRefCountIsZero(&observing_silence_);
- if (was_audible_ && !is_now_audible)
- notify_is_audible_.Run(was_audible_ = false);
- else if (!was_audible_ && is_now_audible)
- notify_is_audible_.Run(was_audible_ = true);
-}
-
-bool AudioSilenceDetector::ProbablyContainsSilence(const AudioBus* buffer,
- int num_frames) {
- if (!buffer)
- return true;
- DCHECK_LE(num_frames, buffer->frames());
- if (buffer->frames() <= 0)
- return true;
-
- // Scan the data in each channel. If any one channel contains sound whose
- // range of values exceeds |silence_threshold_|, return false immediately.
- for (int i = 0; i < buffer->channels(); ++i) {
- // Examine the 1st, 2nd (+1), 4th (+2), 7th (+3), 11th (+4), etc. samples,
- // checking whether |silence_threshold_| has been breached each time. For
- // typical AudioBus sizes, this algorithm will in the worst case examine
- // fewer than 10% of the samples.
- //
- // Note that there *is* a heavy bias in sampling at the beginning of the
- // channels, but that doesn't matter. The buffer sizes are simply too
- // small. For example, it is commonplace to use 128-sample buffers, which
- // represents ~3 ms of audio at 44.1 kHz; and this means that frequencies
- // below ~350 Hz will span more than one buffer to make a full cycle. In
- // all, the algorithm here is meant to be dirt-simple math that isn't
- // susceptible to periodic bias within a single buffer.
- const float* p = buffer->channel(i);
- const float* const end_of_samples = p + num_frames;
- int skip = 1;
- float min_value = *p;
- float max_value = *p;
- for (p += skip; p < end_of_samples; ++skip, p += skip) {
- DCHECK(base::IsFinite(*p));
- if (*p < min_value)
- min_value = *p;
- else if (max_value < *p)
- max_value = *p;
- if ((max_value - min_value) > silence_threshold_)
- return false;
- }
- }
-
- return true;
-}
-
-} // namespace media
diff --git a/media/audio/audio_silence_detector.h b/media/audio/audio_silence_detector.h
deleted file mode 100644
index b6c52a7..0000000
--- a/media/audio/audio_silence_detector.h
+++ /dev/null
@@ -1,112 +0,0 @@
-// Copyright (c) 2013 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.
-
-#ifndef MEDIA_AUDIO_AUDIO_SILENCE_DETECTOR_H_
-#define MEDIA_AUDIO_AUDIO_SILENCE_DETECTOR_H_
-
-#include "base/atomic_ref_count.h"
-#include "base/callback.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/threading/thread_checker.h"
-#include "base/timer.h"
-#include "media/base/media_export.h"
-
-// An audio silence detector. It is periodically provided an AudioBus by the
-// native audio thread, where simple logic determines whether the audio samples
-// in each channel in the buffer represent silence. If a long-enough period of
-// contiguous silence is observed in all channels, a notification callback is
-// run on the thread that constructed AudioSilenceDetector.
-//
-// Note that extreme care has been taken to make the
-// AudioSilenceDetector::Scan() method safe to be called on the native audio
-// thread. The code acquires no locks, nor engages in any operation that could
-// result in an undetermined/unbounded amount of run-time. Comments in
-// audio_silence_detector.cc elaborate further on the silence detection
-// algorithm.
-
-namespace base {
-class TimeDelta;
-}
-
-namespace media {
-
-class AudioBus;
-
-class MEDIA_EXPORT AudioSilenceDetector {
- public:
- typedef base::Callback<void(bool)> AudibleCallback;
-
- // Tunable parameters: |questionable_silence_period| is the amount of time
- // where audio must remain silent before triggerring a callback.
- // |indistinguishable_silence_threshold| is the value range below which audio
- // is considered silent, in full-scale units.
- //
- // TODO(miu): |indistinguishable_silence_threshold| should be specified in
- // dbFS units rather than full-scale. We need a dbFS data type for
- // media/audio first.
- AudioSilenceDetector(int sample_rate,
- const base::TimeDelta& questionable_silence_period,
- float indistinguishable_silence_threshold);
-
- ~AudioSilenceDetector();
-
- // Start detecting silence, notifying via the given callback.
- void Start(const AudibleCallback& notify_is_audible);
-
- // Stop detecting silence. If |notify_ending_in_silence| is true, a final
- // notify_is_audible(false) call will be made here.
- void Stop(bool notify_ending_in_silence);
-
- // Scan more |frames| of audio data from |buffer|. This is usually called
- // within the native audio thread's "more data" callback.
- void Scan(const AudioBus* buffer, int frames);
-
- private:
- // Called by |poll_timer_| at regular intervals to determine whether to invoke
- // the callback due to a silence state change.
- void MaybeInvokeAudibleCallback();
-
- // Returns true if the first |num_frames| frames in all channels in |buffer|
- // probably contain silence. A simple heuristic is used to quickly examine a
- // subset of the samples in each channel, hence the name of this method.
- // "Silence" means that the range of the sample values examined does not
- // exceed |silence_threshold_|.
- bool ProbablyContainsSilence(const AudioBus* buffer, int num_frames);
-
- // Time between polls for changes in state.
- const base::TimeDelta polling_period_;
-
- // Number of frames of contiguous silence to be observed before setting
- // |observing_silence_| to false.
- const int frames_before_observing_silence_;
-
- // Threshold below which audio should be considered indistinguishably silent.
- const float silence_threshold_;
-
- // Number of frames of contiguous silence observed thus far on the native
- // audio thread.
- int frames_silent_so_far_;
-
- // Boolean state (0 or 1) set by the native audio thread. This is polled
- // regularly by the thread that invokes the callback.
- base::AtomicRefCount observing_silence_;
-
- // Callback for notifying of a detected transition to silence or non-silence.
- AudibleCallback notify_is_audible_;
-
- // Last reported audible state, used for de-duping callback invocations.
- bool was_audible_;
-
- // Fires regularly, calling MaybeInvokeAudibleCallback().
- base::RepeatingTimer<AudioSilenceDetector> poll_timer_;
-
- // Constructor, destructor and most methods must be called on the same thread.
- base::ThreadChecker thread_checker_;
-
- DISALLOW_COPY_AND_ASSIGN(AudioSilenceDetector);
-};
-
-} // namespace media
-
-#endif // MEDIA_AUDIO_AUDIO_SILENCE_DETECTOR_H_
diff --git a/media/audio/audio_silence_detector_unittest.cc b/media/audio/audio_silence_detector_unittest.cc
deleted file mode 100644
index 52e6958..0000000
--- a/media/audio/audio_silence_detector_unittest.cc
+++ /dev/null
@@ -1,227 +0,0 @@
-// Copyright (c) 2013 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 "media/audio/audio_silence_detector.h"
-
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/message_loop.h"
-#include "base/synchronization/waitable_event.h"
-#include "base/threading/thread.h"
-#include "base/time.h"
-#include "media/base/audio_bus.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using ::testing::_;
-using ::testing::InvokeWithoutArgs;
-using ::testing::TestWithParam;
-using ::testing::Values;
-
-namespace media {
-
-static const int kSampleRate = 48000;
-static const int kFramesPerBuffer = 128;
-
-namespace {
-
-class MockObserver {
- public:
- MOCK_METHOD1(OnAudible, void(bool));
-};
-
-struct TestScenario {
- const float* data;
- int num_channels;
- int num_frames;
- float value_range;
-
- TestScenario(const float* d, int c, int f, float v)
- : data(d), num_channels(c), num_frames(f), value_range(v) {}
-};
-
-} // namespace
-
-class AudioSilenceDetectorTest : public TestWithParam<TestScenario> {
- public:
- AudioSilenceDetectorTest()
- : audio_manager_thread_(new base::Thread("AudioThread")),
- notification_received_(false, false) {
- audio_manager_thread_->Start();
- audio_message_loop_ = audio_manager_thread_->message_loop_proxy();
- }
-
- virtual ~AudioSilenceDetectorTest() {
- SyncWithAudioThread();
- }
-
- AudioSilenceDetector* silence_detector() const {
- return silence_detector_.get();
- }
-
- void StartSilenceDetector(float threshold, MockObserver* observer) {
- audio_message_loop_->PostTask(
- FROM_HERE,
- base::Bind(&AudioSilenceDetectorTest::StartDetectorOnAudioThread,
- base::Unretained(this), threshold, observer));
- SyncWithAudioThread();
- }
-
- void StopSilenceDetector() {
- audio_message_loop_->PostTask(
- FROM_HERE,
- base::Bind(&AudioSilenceDetectorTest::StopDetectorOnAudioThread,
- base::Unretained(this)));
- SyncWithAudioThread();
- }
-
- // Creates an AudioBus, sized and populated with kFramesPerBuffer frames of
- // data. The given test |data| is repeated to fill the buffer.
- scoped_ptr<AudioBus> CreatePopulatedBuffer(
- const float* data, int num_channels, int num_frames) {
- scoped_ptr<AudioBus> bus = AudioBus::Create(num_channels, kFramesPerBuffer);
- for (int ch = 0; ch < num_channels; ++ch) {
- for (int frames = 0; frames < kFramesPerBuffer; frames += num_frames) {
- const int num_to_copy = std::min(num_frames, kFramesPerBuffer - frames);
- memcpy(bus->channel(ch) + frames, data + num_frames * ch,
- sizeof(float) * num_to_copy);
- }
- }
- return bus.Pass();
- }
-
- void SignalNotificationReceived() {
- notification_received_.Signal();
- }
-
- void WaitForNotificationReceived() {
- notification_received_.Wait();
- }
-
- // Post a task on the audio thread and block until it is executed. This
- // provides a barrier: All previous tasks pending on the audio thread have
- // completed before this method returns.
- void SyncWithAudioThread() {
- base::WaitableEvent done(false, false);
- audio_message_loop_->PostTask(
- FROM_HERE,
- base::Bind(&base::WaitableEvent::Signal, base::Unretained(&done)));
- done.Wait();
- }
-
- private:
- void StartDetectorOnAudioThread(float threshold, MockObserver* observer) {
- const AudioSilenceDetector::AudibleCallback notify_is_audible =
- base::Bind(&MockObserver::OnAudible, base::Unretained(observer));
- silence_detector_.reset(new AudioSilenceDetector(
- kSampleRate, base::TimeDelta::FromMilliseconds(1), threshold));
- silence_detector_->Start(notify_is_audible);
- }
-
- void StopDetectorOnAudioThread() {
- silence_detector_->Stop(true);
- silence_detector_.reset();
- }
-
- scoped_ptr<base::Thread> audio_manager_thread_;
- scoped_refptr<base::MessageLoopProxy> audio_message_loop_;
-
- base::WaitableEvent notification_received_;
-
- scoped_ptr<AudioSilenceDetector> silence_detector_;
-
- DISALLOW_COPY_AND_ASSIGN(AudioSilenceDetectorTest);
-};
-
-TEST_P(AudioSilenceDetectorTest, TriggersAtVariousThresholds) {
- static const float kThresholdsToTry[] =
- { 0.0f, 0.125f, 0.25f, 0.5f, 0.75f, 1.0f };
-
- const TestScenario& scenario = GetParam();
-
- for (size_t i = 0; i < arraysize(kThresholdsToTry); ++i) {
- MockObserver observer;
-
- // Start the detector. This should cause a single callback to indicate
- // we're starting out in silence.
- EXPECT_CALL(observer, OnAudible(false))
- .WillOnce(InvokeWithoutArgs(
- this, &AudioSilenceDetectorTest::SignalNotificationReceived))
- .RetiresOnSaturation();
- StartSilenceDetector(kThresholdsToTry[i], &observer);
- WaitForNotificationReceived();
-
- // Send more data to the silence detector. For some test scenarios, the
- // sound data will trigger a callback.
- const bool expect_a_callback = (kThresholdsToTry[i] < scenario.value_range);
- if (expect_a_callback) {
- EXPECT_CALL(observer, OnAudible(true))
- .WillOnce(InvokeWithoutArgs(
- this, &AudioSilenceDetectorTest::SignalNotificationReceived))
- .RetiresOnSaturation();
- }
- scoped_ptr<AudioBus> bus = CreatePopulatedBuffer(
- scenario.data, scenario.num_channels, scenario.num_frames);
- silence_detector()->Scan(bus.get(), bus->frames());
- if (expect_a_callback)
- WaitForNotificationReceived();
-
- // Stop the detector. This should cause another callback to indicate we're
- // ending in silence.
- EXPECT_CALL(observer, OnAudible(false))
- .WillOnce(InvokeWithoutArgs(
- this, &AudioSilenceDetectorTest::SignalNotificationReceived))
- .RetiresOnSaturation();
- StopSilenceDetector();
- WaitForNotificationReceived();
-
- SyncWithAudioThread();
- }
-}
-
-static const float kAllZeros[] = {
- // left channel
- 0.0f,
- // right channel
- 0.0f
-};
-
-static const float kAllOnes[] = {
- // left channel
- 1.0f,
- // right channel
- 1.0f
-};
-
-static const float kSilentLeftChannel[] = {
- // left channel
- 0.5f, 0.5f, 0.5f, 0.5f,
- // right channel
- 0.0f, 0.25f, 0.0f, 0.0f
-};
-
-static const float kSilentRightChannel[] = {
- // left channel
- 1.0f, 1.0f, 0.75f, 0.5f, 1.0f,
- // right channel
- 0.0f, 0.0f, 0.0f, 0.0f, 0.0f
-};
-
-static const float kAtDifferentVolumesAndBias[] = {
- // left channel
- 1.0f, 0.9f, 0.8f, 0.7f, 0.6f, 0.5f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f, 0.9f,
- // right channel
- 0.0f, 0.2f, 0.4f, 0.6f, 0.4f, 0.2f, 0.0f, 0.2f, 0.4f, 0.6f, 0.4f, 0.2f
-};
-
-INSTANTIATE_TEST_CASE_P(
- Scenarios, AudioSilenceDetectorTest,
- Values(
- TestScenario(kAllZeros, 2, 1, 0.0f),
- TestScenario(kAllOnes, 2, 1, 0.0f),
- TestScenario(kSilentLeftChannel, 2, 4, 0.25f),
- TestScenario(kSilentRightChannel, 2, 5, 0.5f),
- TestScenario(kAtDifferentVolumesAndBias, 2, 12, 0.6f)));
-
-} // namespace media
diff --git a/media/media.gyp b/media/media.gyp
index c6977e4..ee117a0 100644
--- a/media/media.gyp
+++ b/media/media.gyp
@@ -97,8 +97,6 @@
'audio/audio_output_proxy.h',
'audio/audio_output_resampler.cc',
'audio/audio_output_resampler.h',
- 'audio/audio_silence_detector.cc',
- 'audio/audio_silence_detector.h',
'audio/audio_source_diverter.h',
'audio/audio_util.cc',
'audio/audio_util.h',
@@ -859,7 +857,6 @@
'audio/audio_output_device_unittest.cc',
'audio/audio_output_proxy_unittest.cc',
'audio/audio_parameters_unittest.cc',
- 'audio/audio_silence_detector_unittest.cc',
'audio/audio_util_unittest.cc',
'audio/cross_process_notification_unittest.cc',
'audio/fake_audio_consumer_unittest.cc',