summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--content/browser/browser_main_loop.cc9
-rw-r--r--content/browser/browser_main_loop.h6
-rw-r--r--content/browser/renderer_host/media/audio_input_renderer_host.cc30
-rw-r--r--content/browser/renderer_host/media/audio_input_renderer_host.h13
-rw-r--r--content/browser/renderer_host/media/audio_input_sync_writer.cc7
-rw-r--r--content/browser/renderer_host/media/audio_input_sync_writer.h5
-rw-r--r--content/browser/renderer_host/render_process_host_impl.cc9
-rw-r--r--content/browser/speech/speech_recognizer_impl.cc2
-rw-r--r--content/renderer/media/webaudio_capturer_source.cc2
-rw-r--r--content/renderer/media/webrtc_audio_capturer.cc22
-rw-r--r--content/renderer/media/webrtc_audio_capturer.h3
-rw-r--r--content/renderer/media/webrtc_audio_capturer_sink_owner.cc16
-rw-r--r--content/renderer/media/webrtc_audio_capturer_sink_owner.h3
-rw-r--r--content/renderer/media/webrtc_audio_device_impl.cc5
-rw-r--r--content/renderer/media/webrtc_audio_device_impl.h6
-rw-r--r--content/renderer/media/webrtc_audio_device_unittest.cc12
-rw-r--r--content/renderer/media/webrtc_local_audio_renderer.cc15
-rw-r--r--content/renderer/media/webrtc_local_audio_renderer.h3
-rw-r--r--content/renderer/media/webrtc_local_audio_track.cc16
-rw-r--r--content/renderer/media/webrtc_local_audio_track.h3
-rw-r--r--content/renderer/media/webrtc_local_audio_track_unittest.cc132
-rw-r--r--content/test/webrtc_audio_device_test.cc8
-rw-r--r--media/DEPS1
-rw-r--r--media/audio/audio_input_controller.cc53
-rw-r--r--media/audio/audio_input_controller.h46
-rw-r--r--media/audio/audio_input_controller_unittest.cc50
-rw-r--r--media/audio/audio_input_device.cc5
-rw-r--r--media/audio/audio_parameters.h1
-rw-r--r--media/audio/test_audio_input_controller_factory.cc12
-rw-r--r--media/audio/test_audio_input_controller_factory.h7
-rw-r--r--media/base/audio_capturer_source.h3
-rw-r--r--media/base/user_input_monitor.cc87
-rw-r--r--media/base/user_input_monitor.h99
-rw-r--r--media/base/user_input_monitor_linux.cc338
-rw-r--r--media/base/user_input_monitor_mac.mm16
-rw-r--r--media/base/user_input_monitor_win.cc16
-rw-r--r--media/media.gyp29
37 files changed, 931 insertions, 159 deletions
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc
index c51b5c5..4194aa8 100644
--- a/content/browser/browser_main_loop.cc
+++ b/content/browser/browser_main_loop.cc
@@ -52,6 +52,7 @@
#include "crypto/nss_util.h"
#include "media/audio/audio_manager.h"
#include "media/base/media.h"
+#include "media/base/user_input_monitor.h"
#include "media/midi/midi_manager.h"
#include "net/base/network_change_notifier.h"
#include "net/socket/client_socket_factory.h"
@@ -872,6 +873,14 @@ int BrowserMainLoop::BrowserThreadsStarted() {
audio_manager_.get(), media_stream_manager_.get()));
}
+ {
+ TRACE_EVENT0(
+ "startup",
+ "BrowserMainLoop::BrowserThreadsStarted::InitUserInputMonitor");
+ user_input_monitor_ = media::UserInputMonitor::Create(
+ io_thread_->message_loop_proxy(), main_thread_->message_loop_proxy());
+ }
+
// Alert the clipboard class to which threads are allowed to access the
// clipboard:
std::vector<base::PlatformThreadId> allowed_clipboard_threads;
diff --git a/content/browser/browser_main_loop.h b/content/browser/browser_main_loop.h
index 7b2879f..83dbafd 100644
--- a/content/browser/browser_main_loop.h
+++ b/content/browser/browser_main_loop.h
@@ -26,6 +26,7 @@ class TraceMemoryController;
namespace media {
class AudioManager;
class MIDIManager;
+class UserInputMonitor;
} // namespace media
namespace net {
@@ -86,6 +87,9 @@ class CONTENT_EXPORT BrowserMainLoop {
MediaStreamManager* media_stream_manager() const {
return media_stream_manager_.get();
}
+ media::UserInputMonitor* user_input_monitor() const {
+ return user_input_monitor_.get();
+ }
media::MIDIManager* midi_manager() const { return midi_manager_.get(); }
base::Thread* indexed_db_thread() const { return indexed_db_thread_.get(); }
@@ -122,6 +126,8 @@ class CONTENT_EXPORT BrowserMainLoop {
scoped_ptr<base::PowerMonitor> power_monitor_;
scoped_ptr<base::HighResolutionTimerManager> hi_res_timer_manager_;
scoped_ptr<net::NetworkChangeNotifier> network_change_notifier_;
+ // user_input_monitor_ has to outlive audio_manager_, so declared first.
+ scoped_ptr<media::UserInputMonitor> user_input_monitor_;
scoped_ptr<media::AudioManager> audio_manager_;
scoped_ptr<media::MIDIManager> midi_manager_;
scoped_ptr<AudioMirroringManager> audio_mirroring_manager_;
diff --git a/content/browser/renderer_host/media/audio_input_renderer_host.cc b/content/browser/renderer_host/media/audio_input_renderer_host.cc
index 4a2f731..1c02109 100644
--- a/content/browser/renderer_host/media/audio_input_renderer_host.cc
+++ b/content/browser/renderer_host/media/audio_input_renderer_host.cc
@@ -51,11 +51,12 @@ AudioInputRendererHost::AudioEntry::~AudioEntry() {}
AudioInputRendererHost::AudioInputRendererHost(
media::AudioManager* audio_manager,
MediaStreamManager* media_stream_manager,
- AudioMirroringManager* audio_mirroring_manager)
+ AudioMirroringManager* audio_mirroring_manager,
+ media::UserInputMonitor* user_input_monitor)
: audio_manager_(audio_manager),
media_stream_manager_(media_stream_manager),
- audio_mirroring_manager_(audio_mirroring_manager) {
-}
+ audio_mirroring_manager_(audio_mirroring_manager),
+ user_input_monitor_(user_input_monitor) {}
AudioInputRendererHost::~AudioInputRendererHost() {
DCHECK(audio_entries_.empty());
@@ -272,20 +273,23 @@ void AudioInputRendererHost::OnCreateStream(
entry->controller = media::AudioInputController::CreateForStream(
audio_manager_->GetMessageLoop(),
this,
- WebContentsAudioInputStream::Create(
- device_id, audio_params, audio_manager_->GetWorkerLoop(),
- audio_mirroring_manager_),
- entry->writer.get());
+ WebContentsAudioInputStream::Create(device_id,
+ audio_params,
+ audio_manager_->GetWorkerLoop(),
+ audio_mirroring_manager_),
+ entry->writer.get(),
+ user_input_monitor_);
} else {
// TODO(henrika): replace CreateLowLatency() with Create() as soon
// as satish has ensured that Speech Input also uses the default low-
// latency path. See crbug.com/112472 for details.
- entry->controller = media::AudioInputController::CreateLowLatency(
- audio_manager_,
- this,
- audio_params,
- device_id,
- entry->writer.get());
+ entry->controller =
+ media::AudioInputController::CreateLowLatency(audio_manager_,
+ this,
+ audio_params,
+ device_id,
+ entry->writer.get(),
+ user_input_monitor_);
}
if (!entry->controller.get()) {
diff --git a/content/browser/renderer_host/media/audio_input_renderer_host.h b/content/browser/renderer_host/media/audio_input_renderer_host.h
index d16ebfa..5df1fc6 100644
--- a/content/browser/renderer_host/media/audio_input_renderer_host.h
+++ b/content/browser/renderer_host/media/audio_input_renderer_host.h
@@ -44,6 +44,7 @@
namespace media {
class AudioManager;
class AudioParameters;
+class UserInputMonitor;
}
namespace content {
@@ -55,10 +56,11 @@ class CONTENT_EXPORT AudioInputRendererHost
public media::AudioInputController::EventHandler {
public:
// Called from UI thread from the owner of this object.
- AudioInputRendererHost(
- media::AudioManager* audio_manager,
- MediaStreamManager* media_stream_manager,
- AudioMirroringManager* audio_mirroring_manager);
+ // |user_input_monitor| is used for typing detection and can be NULL.
+ AudioInputRendererHost(media::AudioManager* audio_manager,
+ MediaStreamManager* media_stream_manager,
+ AudioMirroringManager* audio_mirroring_manager,
+ media::UserInputMonitor* user_input_monitor);
// BrowserMessageFilter implementation.
virtual void OnChannelClosing() OVERRIDE;
@@ -154,6 +156,9 @@ class CONTENT_EXPORT AudioInputRendererHost
// A map of stream IDs to audio sources.
AudioEntryMap audio_entries_;
+ // Raw pointer of the UserInputMonitor.
+ media::UserInputMonitor* user_input_monitor_;
+
DISALLOW_COPY_AND_ASSIGN(AudioInputRendererHost);
};
diff --git a/content/browser/renderer_host/media/audio_input_sync_writer.cc b/content/browser/renderer_host/media/audio_input_sync_writer.cc
index 572abf3..369e0a8 100644
--- a/content/browser/renderer_host/media/audio_input_sync_writer.cc
+++ b/content/browser/renderer_host/media/audio_input_sync_writer.cc
@@ -29,14 +29,17 @@ void AudioInputSyncWriter::UpdateRecordedBytes(uint32 bytes) {
socket_->Send(&bytes, sizeof(bytes));
}
-uint32 AudioInputSyncWriter::Write(
- const void* data, uint32 size, double volume) {
+uint32 AudioInputSyncWriter::Write(const void* data,
+ uint32 size,
+ double volume,
+ bool key_pressed) {
uint8* ptr = static_cast<uint8*>(shared_memory_->memory());
ptr += current_segment_id_ * shared_memory_segment_size_;
media::AudioInputBuffer* buffer =
reinterpret_cast<media::AudioInputBuffer*>(ptr);
buffer->params.volume = volume;
buffer->params.size = size;
+ buffer->params.key_pressed = key_pressed;
memcpy(buffer->audio, data, size);
if (++current_segment_id_ >= shared_memory_segment_count_)
diff --git a/content/browser/renderer_host/media/audio_input_sync_writer.h b/content/browser/renderer_host/media/audio_input_sync_writer.h
index 4cfe9e3..d16911f 100644
--- a/content/browser/renderer_host/media/audio_input_sync_writer.h
+++ b/content/browser/renderer_host/media/audio_input_sync_writer.h
@@ -28,7 +28,10 @@ class AudioInputSyncWriter : public media::AudioInputController::SyncWriter {
// media::AudioOutputController::SyncWriter implementation.
virtual void UpdateRecordedBytes(uint32 bytes) OVERRIDE;
- virtual uint32 Write(const void* data, uint32 size, double volume) OVERRIDE;
+ virtual uint32 Write(const void* data,
+ uint32 size,
+ double volume,
+ bool key_pressed) OVERRIDE;
virtual void Close() OVERRIDE;
bool Init();
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index e31aff2..3b22bc8 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -617,11 +617,14 @@ void RenderProcessHostImpl::CreateMessageFilters() {
channel_->AddFilter(new AudioInputRendererHost(
audio_manager,
media_stream_manager,
- BrowserMainLoop::GetInstance()->audio_mirroring_manager()));
+ BrowserMainLoop::GetInstance()->audio_mirroring_manager(),
+ BrowserMainLoop::GetInstance()->user_input_monitor()));
channel_->AddFilter(new AudioRendererHost(
- GetID(), audio_manager,
+ GetID(),
+ audio_manager,
BrowserMainLoop::GetInstance()->audio_mirroring_manager(),
- media_internals, media_stream_manager));
+ media_internals,
+ media_stream_manager));
channel_->AddFilter(
new MIDIHost(BrowserMainLoop::GetInstance()->midi_manager()));
channel_->AddFilter(new MIDIDispatcherHost(GetID(), browser_context));
diff --git a/content/browser/speech/speech_recognizer_impl.cc b/content/browser/speech/speech_recognizer_impl.cc
index 2081b2f..2dae430 100644
--- a/content/browser/speech/speech_recognizer_impl.cc
+++ b/content/browser/speech/speech_recognizer_impl.cc
@@ -564,7 +564,7 @@ SpeechRecognizerImpl::StartRecording(const FSMEventArgs&) {
new OnDataConverter(input_parameters, output_parameters));
audio_controller_ = AudioInputController::Create(
- audio_manager, this, input_parameters, device_id_);
+ audio_manager, this, input_parameters, device_id_, NULL);
if (!audio_controller_.get()) {
return Abort(SpeechRecognitionError(SPEECH_RECOGNITION_ERROR_AUDIO));
diff --git a/content/renderer/media/webaudio_capturer_source.cc b/content/renderer/media/webaudio_capturer_source.cc
index 0ab8dc3..35cd99c 100644
--- a/content/renderer/media/webaudio_capturer_source.cc
+++ b/content/renderer/media/webaudio_capturer_source.cc
@@ -96,7 +96,7 @@ void WebAudioCapturerSource::consumeAudio(
int capture_frames = params_.frames_per_buffer();
while (fifo_->frames() >= capture_frames) {
fifo_->Consume(capture_bus_.get(), 0, capture_frames);
- callback_->Capture(capture_bus_.get(), 0, 1.0);
+ callback_->Capture(capture_bus_.get(), 0, 1.0, false);
}
}
diff --git a/content/renderer/media/webrtc_audio_capturer.cc b/content/renderer/media/webrtc_audio_capturer.cc
index 238b2a2..26b0fd7 100644
--- a/content/renderer/media/webrtc_audio_capturer.cc
+++ b/content/renderer/media/webrtc_audio_capturer.cc
@@ -102,14 +102,16 @@ class WebRtcAudioCapturer::TrackOwner
int number_of_channels,
int number_of_frames,
int audio_delay_milliseconds,
- int volume) {
+ int volume,
+ bool key_pressed) {
base::AutoLock lock(lock_);
if (delegate_) {
delegate_->CaptureData(audio_data,
number_of_channels,
number_of_frames,
audio_delay_milliseconds,
- volume);
+ volume,
+ key_pressed);
}
}
@@ -424,10 +426,11 @@ void WebRtcAudioCapturer::SetAutomaticGainControl(bool enable) {
void WebRtcAudioCapturer::Capture(media::AudioBus* audio_source,
int audio_delay_milliseconds,
- double volume) {
- // This callback is driven by AudioInputDevice::AudioThreadCallback if
- // |source_| is AudioInputDevice, otherwise it is driven by client's
- // CaptureCallback.
+ double volume,
+ bool key_pressed) {
+// This callback is driven by AudioInputDevice::AudioThreadCallback if
+// |source_| is AudioInputDevice, otherwise it is driven by client's
+// CaptureCallback.
#if defined(OS_WIN) || defined(OS_MACOSX)
DCHECK_LE(volume, 1.0);
#elif defined(OS_LINUX) || defined(OS_OPENBSD)
@@ -471,8 +474,11 @@ void WebRtcAudioCapturer::Capture(media::AudioBus* audio_source,
it != tracks.end();
++it) {
(*it)->CaptureData(buffer_ref_while_calling->buffer(),
- audio_source->channels(), audio_source->frames(),
- audio_delay_milliseconds, volume_);
+ audio_source->channels(),
+ audio_source->frames(),
+ audio_delay_milliseconds,
+ volume,
+ key_pressed);
}
}
diff --git a/content/renderer/media/webrtc_audio_capturer.h b/content/renderer/media/webrtc_audio_capturer.h
index e85ea04..caa88d03 100644
--- a/content/renderer/media/webrtc_audio_capturer.h
+++ b/content/renderer/media/webrtc_audio_capturer.h
@@ -108,7 +108,8 @@ class CONTENT_EXPORT WebRtcAudioCapturer
// Called on the AudioInputDevice audio thread.
virtual void Capture(media::AudioBus* audio_source,
int audio_delay_milliseconds,
- double volume) OVERRIDE;
+ double volume,
+ bool key_pressed) OVERRIDE;
virtual void OnCaptureError() OVERRIDE;
// Reconfigures the capturer with a new buffer size and capture parameters.
diff --git a/content/renderer/media/webrtc_audio_capturer_sink_owner.cc b/content/renderer/media/webrtc_audio_capturer_sink_owner.cc
index cb6a813..59de510 100644
--- a/content/renderer/media/webrtc_audio_capturer_sink_owner.cc
+++ b/content/renderer/media/webrtc_audio_capturer_sink_owner.cc
@@ -18,13 +18,19 @@ int WebRtcAudioCapturerSinkOwner::CaptureData(const std::vector<int>& channels,
int number_of_frames,
int audio_delay_milliseconds,
int current_volume,
- bool need_audio_processing) {
+ bool need_audio_processing,
+ bool key_pressed) {
base::AutoLock lock(lock_);
if (delegate_) {
- return delegate_->CaptureData(channels, audio_data, sample_rate,
- number_of_channels, number_of_frames,
- audio_delay_milliseconds, current_volume,
- need_audio_processing);
+ delegate_->CaptureData(channels,
+ audio_data,
+ sample_rate,
+ number_of_channels,
+ number_of_frames,
+ audio_delay_milliseconds,
+ current_volume,
+ need_audio_processing,
+ key_pressed);
}
return 0;
diff --git a/content/renderer/media/webrtc_audio_capturer_sink_owner.h b/content/renderer/media/webrtc_audio_capturer_sink_owner.h
index a3641b8..f338209 100644
--- a/content/renderer/media/webrtc_audio_capturer_sink_owner.h
+++ b/content/renderer/media/webrtc_audio_capturer_sink_owner.h
@@ -31,7 +31,8 @@ class WebRtcAudioCapturerSinkOwner
int number_of_frames,
int audio_delay_milliseconds,
int current_volume,
- bool need_audio_processing) OVERRIDE;
+ bool need_audio_processing,
+ bool key_pressed) OVERRIDE;
virtual void SetCaptureFormat(const media::AudioParameters& params) OVERRIDE;
diff --git a/content/renderer/media/webrtc_audio_device_impl.cc b/content/renderer/media/webrtc_audio_device_impl.cc
index c40e0a2..93e024b 100644
--- a/content/renderer/media/webrtc_audio_device_impl.cc
+++ b/content/renderer/media/webrtc_audio_device_impl.cc
@@ -59,7 +59,8 @@ int WebRtcAudioDeviceImpl::CaptureData(const std::vector<int>& channels,
int number_of_frames,
int audio_delay_milliseconds,
int current_volume,
- bool need_audio_processing) {
+ bool need_audio_processing,
+ bool key_pressed) {
int total_delay_ms = 0;
{
base::AutoLock auto_lock(lock_);
@@ -75,11 +76,9 @@ int WebRtcAudioDeviceImpl::CaptureData(const std::vector<int>& channels,
// Write audio samples in blocks of 10 milliseconds to the registered
// webrtc::AudioTransport sink. Keep writing until our internal byte
// buffer is empty.
- // TODO(niklase): Wire up the key press detection.
const int16* audio_buffer = audio_data;
const int samples_per_10_msec = (sample_rate / 100);
int accumulated_audio_samples = 0;
- bool key_pressed = false;
uint32_t new_volume = 0;
while (accumulated_audio_samples < number_of_frames) {
// Deliver 10ms of recorded 16-bit linear PCM audio.
diff --git a/content/renderer/media/webrtc_audio_device_impl.h b/content/renderer/media/webrtc_audio_device_impl.h
index ef3cb7c..b644178 100644
--- a/content/renderer/media/webrtc_audio_device_impl.h
+++ b/content/renderer/media/webrtc_audio_device_impl.h
@@ -223,7 +223,8 @@ class WebRtcAudioCapturerSink {
int number_of_frames,
int audio_delay_milliseconds,
int current_volume,
- bool need_audio_processing) = 0;
+ bool need_audio_processing,
+ bool key_pressed) = 0;
// Set the format for the capture audio parameters.
virtual void SetCaptureFormat(const media::AudioParameters& params) = 0;
@@ -339,7 +340,8 @@ class CONTENT_EXPORT WebRtcAudioDeviceImpl
int number_of_frames,
int audio_delay_milliseconds,
int current_volume,
- bool need_audio_processing) OVERRIDE;
+ bool need_audio_processing,
+ bool key_pressed) OVERRIDE;
// Called on the main render thread.
virtual void SetCaptureFormat(const media::AudioParameters& params) OVERRIDE;
diff --git a/content/renderer/media/webrtc_audio_device_unittest.cc b/content/renderer/media/webrtc_audio_device_unittest.cc
index 6746ab6..a08ea93 100644
--- a/content/renderer/media/webrtc_audio_device_unittest.cc
+++ b/content/renderer/media/webrtc_audio_device_unittest.cc
@@ -221,7 +221,8 @@ class MockWebRtcAudioCapturerSink : public WebRtcAudioCapturerSink {
int number_of_frames,
int audio_delay_milliseconds,
int current_volume,
- bool need_audio_processing) OVERRIDE {
+ bool need_audio_processing,
+ bool key_pressed) OVERRIDE {
// Signal that a callback has been received.
event_->Signal();
return 0;
@@ -381,8 +382,13 @@ int RunWebRtcLoopbackTimeTest(media::AudioManager* manager,
capturer_sink->CaptureData(
voe_channels,
reinterpret_cast<int16*>(capture_data.get() + input_packet_size * j),
- params.sample_rate(), params.channels(), params.frames_per_buffer(),
- kHardwareLatencyInMs, 1.0, enable_apm);
+ params.sample_rate(),
+ params.channels(),
+ params.frames_per_buffer(),
+ kHardwareLatencyInMs,
+ 1.0,
+ enable_apm,
+ false);
// Receiving data from WebRtc.
renderer_source->RenderData(
diff --git a/content/renderer/media/webrtc_local_audio_renderer.cc b/content/renderer/media/webrtc_local_audio_renderer.cc
index 11c125f..af65d8d 100644
--- a/content/renderer/media/webrtc_local_audio_renderer.cc
+++ b/content/renderer/media/webrtc_local_audio_renderer.cc
@@ -52,13 +52,14 @@ void WebRtcLocalAudioRenderer::OnRenderError() {
// content::WebRtcAudioCapturerSink implementation
int WebRtcLocalAudioRenderer::CaptureData(const std::vector<int>& channels,
- const int16* audio_data,
- int sample_rate,
- int number_of_channels,
- int number_of_frames,
- int audio_delay_milliseconds,
- int current_volume,
- bool need_audio_processing) {
+ const int16* audio_data,
+ int sample_rate,
+ int number_of_channels,
+ int number_of_frames,
+ int audio_delay_milliseconds,
+ int current_volume,
+ bool need_audio_processing,
+ bool key_pressed) {
TRACE_EVENT0("audio", "WebRtcLocalAudioRenderer::CaptureData");
base::AutoLock auto_lock(thread_lock_);
diff --git a/content/renderer/media/webrtc_local_audio_renderer.h b/content/renderer/media/webrtc_local_audio_renderer.h
index 2930393..f58aee3 100644
--- a/content/renderer/media/webrtc_local_audio_renderer.h
+++ b/content/renderer/media/webrtc_local_audio_renderer.h
@@ -78,7 +78,8 @@ class CONTENT_EXPORT WebRtcLocalAudioRenderer
int number_of_frames,
int audio_delay_milliseconds,
int current_volume,
- bool need_audio_processing) OVERRIDE;
+ bool need_audio_processing,
+ bool key_pressed) OVERRIDE;
// Can be called on different user thread.
virtual void SetCaptureFormat(const media::AudioParameters& params) OVERRIDE;
diff --git a/content/renderer/media/webrtc_local_audio_track.cc b/content/renderer/media/webrtc_local_audio_track.cc
index de86a3c..cae6bfe 100644
--- a/content/renderer/media/webrtc_local_audio_track.cc
+++ b/content/renderer/media/webrtc_local_audio_track.cc
@@ -47,7 +47,8 @@ void WebRtcLocalAudioTrack::CaptureData(const int16* audio_data,
int number_of_channels,
int number_of_frames,
int audio_delay_milliseconds,
- int volume) {
+ int volume,
+ bool key_pressed) {
scoped_refptr<WebRtcAudioCapturer> capturer;
std::vector<int> voe_channels;
int sample_rate = 0;
@@ -68,10 +69,15 @@ void WebRtcLocalAudioTrack::CaptureData(const int16* audio_data,
// Feed the data to the sinks.
for (SinkList::const_iterator it = sinks.begin(); it != sinks.end(); ++it) {
- int new_volume = (*it)->CaptureData(voe_channels, audio_data, sample_rate,
- number_of_channels, number_of_frames,
- audio_delay_milliseconds, volume,
- need_audio_processing_);
+ int new_volume = (*it)->CaptureData(voe_channels,
+ audio_data,
+ sample_rate,
+ number_of_channels,
+ number_of_frames,
+ audio_delay_milliseconds,
+ volume,
+ need_audio_processing_,
+ key_pressed);
if (new_volume != 0 && capturer.get())
capturer->SetVolume(new_volume);
}
diff --git a/content/renderer/media/webrtc_local_audio_track.h b/content/renderer/media/webrtc_local_audio_track.h
index 7cb8c85..0f4e712 100644
--- a/content/renderer/media/webrtc_local_audio_track.h
+++ b/content/renderer/media/webrtc_local_audio_track.h
@@ -61,7 +61,8 @@ class CONTENT_EXPORT WebRtcLocalAudioTrack
int number_of_channels,
int number_of_frames,
int audio_delay_milliseconds,
- int volume);
+ int volume,
+ bool key_pressed);
// Method called by the capturer to set the audio parameters used by source
// of the capture data..
diff --git a/content/renderer/media/webrtc_local_audio_track_unittest.cc b/content/renderer/media/webrtc_local_audio_track_unittest.cc
index 4720e45..b75ca79 100644
--- a/content/renderer/media/webrtc_local_audio_track_unittest.cc
+++ b/content/renderer/media/webrtc_local_audio_track_unittest.cc
@@ -50,7 +50,7 @@ class FakeAudioThread : public base::PlatformThread::Delegate {
static_cast<media::AudioCapturerSource::CaptureCallback*>(
capturer_.get());
audio_bus_->Zero();
- callback->Capture(audio_bus_.get(), 0, 0);
+ callback->Capture(audio_bus_.get(), 0, 0, false);
// Sleep 1ms to yield the resource for the main thread.
base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(1));
@@ -103,20 +103,27 @@ class MockWebRtcAudioCapturerSink : public WebRtcAudioCapturerSink {
int number_of_frames,
int audio_delay_milliseconds,
int current_volume,
- bool need_audio_processing) OVERRIDE {
- CaptureData(channels.size(), sample_rate, number_of_channels,
- number_of_frames, audio_delay_milliseconds, current_volume,
- need_audio_processing);
+ bool need_audio_processing,
+ bool key_pressed) OVERRIDE {
+ CaptureData(channels.size(),
+ sample_rate,
+ number_of_channels,
+ number_of_frames,
+ audio_delay_milliseconds,
+ current_volume,
+ need_audio_processing,
+ key_pressed);
return 0;
}
- MOCK_METHOD7(CaptureData, void(int number_of_network_channels,
- int sample_rate,
- int number_of_channels,
- int number_of_frames,
- int audio_delay_milliseconds,
- int current_volume,
- bool need_audio_processing));
-
+ MOCK_METHOD8(CaptureData,
+ void(int number_of_network_channels,
+ int sample_rate,
+ int number_of_channels,
+ int number_of_frames,
+ int audio_delay_milliseconds,
+ int current_volume,
+ bool need_audio_processing,
+ bool key_pressed));
MOCK_METHOD1(SetCaptureFormat, void(const media::AudioParameters& params));
};
@@ -173,10 +180,16 @@ TEST_F(WebRtcLocalAudioTrackTest, ConnectAndDisconnectOneSink) {
const media::AudioParameters params = capturer_->audio_parameters();
base::WaitableEvent event(false, false);
EXPECT_CALL(*sink, SetCaptureFormat(_)).WillOnce(Return());
- EXPECT_CALL(*sink, CaptureData(
- kNumberOfNetworkChannels, params.sample_rate(), params.channels(),
- params.frames_per_buffer(), 0, 0, false))
- .Times(AtLeast(1)).WillRepeatedly(SignalEvent(&event));
+ EXPECT_CALL(*sink,
+ CaptureData(kNumberOfNetworkChannels,
+ params.sample_rate(),
+ params.channels(),
+ params.frames_per_buffer(),
+ 0,
+ 0,
+ false,
+ false)).Times(AtLeast(1))
+ .WillRepeatedly(SignalEvent(&event));
track->AddSink(sink.get());
EXPECT_TRUE(event.TimedWait(TestTimeouts::tiny_timeout()));
@@ -207,18 +220,29 @@ TEST_F(WebRtcLocalAudioTrackTest, DISABLED_DisableEnableAudioTrack) {
const media::AudioParameters params = capturer_->audio_parameters();
base::WaitableEvent event(false, false);
EXPECT_CALL(*sink, SetCaptureFormat(_)).WillOnce(Return());
- EXPECT_CALL(*sink, CaptureData(
- 1, params.sample_rate(), params.channels(),
- params.frames_per_buffer(), 0, 0, false))
- .Times(0);
+ EXPECT_CALL(*sink,
+ CaptureData(1,
+ params.sample_rate(),
+ params.channels(),
+ params.frames_per_buffer(),
+ 0,
+ 0,
+ false,
+ false)).Times(0);
track->AddSink(sink.get());
EXPECT_FALSE(event.TimedWait(TestTimeouts::tiny_timeout()));
event.Reset();
- EXPECT_CALL(*sink, CaptureData(
- 1, params.sample_rate(), params.channels(),
- params.frames_per_buffer(), 0, 0, false))
- .Times(AtLeast(1)).WillRepeatedly(SignalEvent(&event));
+ EXPECT_CALL(*sink,
+ CaptureData(1,
+ params.sample_rate(),
+ params.channels(),
+ params.frames_per_buffer(),
+ 0,
+ 0,
+ false,
+ false)).Times(AtLeast(1))
+ .WillRepeatedly(SignalEvent(&event));
EXPECT_TRUE(track->set_enabled(true));
EXPECT_TRUE(event.TimedWait(TestTimeouts::tiny_timeout()));
track->RemoveSink(sink.get());
@@ -243,10 +267,16 @@ TEST_F(WebRtcLocalAudioTrackTest, MultipleAudioTracks) {
const media::AudioParameters params = capturer_->audio_parameters();
base::WaitableEvent event_1(false, false);
EXPECT_CALL(*sink_1, SetCaptureFormat(_)).WillOnce(Return());
- EXPECT_CALL(*sink_1, CaptureData(
- 1, params.sample_rate(), params.channels(),
- params.frames_per_buffer(), 0, 0, false))
- .Times(AtLeast(1)).WillRepeatedly(SignalEvent(&event_1));
+ EXPECT_CALL(*sink_1,
+ CaptureData(1,
+ params.sample_rate(),
+ params.channels(),
+ params.frames_per_buffer(),
+ 0,
+ 0,
+ false,
+ false)).Times(AtLeast(1))
+ .WillRepeatedly(SignalEvent(&event_1));
track_1->AddSink(sink_1.get());
EXPECT_TRUE(event_1.TimedWait(TestTimeouts::tiny_timeout()));
@@ -264,14 +294,26 @@ TEST_F(WebRtcLocalAudioTrackTest, MultipleAudioTracks) {
scoped_ptr<MockWebRtcAudioCapturerSink> sink_2(
new MockWebRtcAudioCapturerSink());
EXPECT_CALL(*sink_2, SetCaptureFormat(_)).WillOnce(Return());
- EXPECT_CALL(*sink_1, CaptureData(
- 1, params.sample_rate(), params.channels(),
- params.frames_per_buffer(), 0, 0, false))
- .Times(AtLeast(1)).WillRepeatedly(SignalEvent(&event_1));
- EXPECT_CALL(*sink_2, CaptureData(
- 1, params.sample_rate(), params.channels(),
- params.frames_per_buffer(), 0, 0, false))
- .Times(AtLeast(1)).WillRepeatedly(SignalEvent(&event_2));
+ EXPECT_CALL(*sink_1,
+ CaptureData(1,
+ params.sample_rate(),
+ params.channels(),
+ params.frames_per_buffer(),
+ 0,
+ 0,
+ false,
+ false)).Times(AtLeast(1))
+ .WillRepeatedly(SignalEvent(&event_1));
+ EXPECT_CALL(*sink_2,
+ CaptureData(1,
+ params.sample_rate(),
+ params.channels(),
+ params.frames_per_buffer(),
+ 0,
+ 0,
+ false,
+ false)).Times(AtLeast(1))
+ .WillRepeatedly(SignalEvent(&event_2));
track_2->AddSink(sink_2.get());
EXPECT_TRUE(event_1.TimedWait(TestTimeouts::tiny_timeout()));
EXPECT_TRUE(event_2.TimedWait(TestTimeouts::tiny_timeout()));
@@ -319,7 +361,7 @@ TEST_F(WebRtcLocalAudioTrackTest, StartAndStopAudioTracks) {
scoped_ptr<MockWebRtcAudioCapturerSink> sink(
new MockWebRtcAudioCapturerSink());
event.Reset();
- EXPECT_CALL(*sink, CaptureData(_, _, _, _, 0, 0, false))
+ EXPECT_CALL(*sink, CaptureData(_, _, _, _, 0, 0, false, false))
.Times(AnyNumber()).WillRepeatedly(Return());
EXPECT_CALL(*sink, SetCaptureFormat(_)).Times(1);
track_1->AddSink(sink.get());
@@ -339,7 +381,7 @@ TEST_F(WebRtcLocalAudioTrackTest, StartAndStopAudioTracks) {
track_1->Stop();
track_1 = NULL;
- EXPECT_CALL(*sink, CaptureData(_, _, _, _, 0, 0, false))
+ EXPECT_CALL(*sink, CaptureData(_, _, _, _, 0, 0, false, false))
.Times(AnyNumber()).WillRepeatedly(Return());
EXPECT_CALL(*sink, SetCaptureFormat(_)).Times(1);
track_2->AddSink(sink.get());
@@ -396,8 +438,10 @@ TEST_F(WebRtcLocalAudioTrackTest, ConnectTracksToDifferentCapturers) {
// Verify the data flow by connecting the |sink_1| to |track_1|.
scoped_ptr<MockWebRtcAudioCapturerSink> sink_1(
new MockWebRtcAudioCapturerSink());
- EXPECT_CALL(*sink_1.get(), CaptureData(kNumberOfNetworkChannelsForTrack1,
- 48000, 2, _, 0, 0, false))
+ EXPECT_CALL(
+ *sink_1.get(),
+ CaptureData(
+ kNumberOfNetworkChannelsForTrack1, 48000, 2, _, 0, 0, false, false))
.Times(AnyNumber()).WillRepeatedly(Return());
EXPECT_CALL(*sink_1.get(), SetCaptureFormat(_)).Times(1);
track_1->AddSink(sink_1.get());
@@ -433,8 +477,10 @@ TEST_F(WebRtcLocalAudioTrackTest, ConnectTracksToDifferentCapturers) {
// Verify the data flow by connecting the |sink_2| to |track_2|.
scoped_ptr<MockWebRtcAudioCapturerSink> sink_2(
new MockWebRtcAudioCapturerSink());
- EXPECT_CALL(*sink_2, CaptureData(kNumberOfNetworkChannelsForTrack2,
- 44100, 1, _, 0, 0, false))
+ EXPECT_CALL(
+ *sink_2,
+ CaptureData(
+ kNumberOfNetworkChannelsForTrack2, 44100, 1, _, 0, 0, false, false))
.Times(AnyNumber()).WillRepeatedly(Return());
EXPECT_CALL(*sink_2, SetCaptureFormat(_)).Times(1);
track_2->AddSink(sink_2.get());
diff --git a/content/test/webrtc_audio_device_test.cc b/content/test/webrtc_audio_device_test.cc
index c258caf..eb2f1c7 100644
--- a/content/test/webrtc_audio_device_test.cc
+++ b/content/test/webrtc_audio_device_test.cc
@@ -252,9 +252,11 @@ void MAYBE_WebRTCAudioDeviceTest::CreateChannel(const char* name) {
media_internals_.get(), media_stream_manager_.get());
audio_render_host_->OnChannelConnected(base::GetCurrentProcId());
- audio_input_renderer_host_ = new AudioInputRendererHost(
- audio_manager_.get(), media_stream_manager_.get(),
- mirroring_manager_.get());
+ audio_input_renderer_host_ =
+ new AudioInputRendererHost(audio_manager_.get(),
+ media_stream_manager_.get(),
+ mirroring_manager_.get(),
+ NULL);
audio_input_renderer_host_->OnChannelConnected(base::GetCurrentProcId());
channel_.reset(new IPC::Channel(name, IPC::Channel::MODE_SERVER, this));
diff --git a/media/DEPS b/media/DEPS
index 9c27aba..495c804 100644
--- a/media/DEPS
+++ b/media/DEPS
@@ -5,6 +5,7 @@ include_rules = [
"+third_party/libvpx",
"+third_party/opus",
"+third_party/skia",
+ "+ui/base",
"+ui/gfx",
"+ui/gl",
]
diff --git a/media/audio/audio_input_controller.cc b/media/audio/audio_input_controller.cc
index 31e137e..f7747b9 100644
--- a/media/audio/audio_input_controller.cc
+++ b/media/audio/audio_input_controller.cc
@@ -36,14 +36,17 @@ namespace media {
AudioInputController::Factory* AudioInputController::factory_ = NULL;
AudioInputController::AudioInputController(EventHandler* handler,
- SyncWriter* sync_writer)
+ SyncWriter* sync_writer,
+ UserInputMonitor* user_input_monitor)
: creator_loop_(base::MessageLoopProxy::current()),
handler_(handler),
stream_(NULL),
data_is_active_(false),
state_(kEmpty),
sync_writer_(sync_writer),
- max_volume_(0.0) {
+ max_volume_(0.0),
+ user_input_monitor_(user_input_monitor),
+ key_pressed_(false) {
DCHECK(creator_loop_.get());
}
@@ -56,17 +59,19 @@ scoped_refptr<AudioInputController> AudioInputController::Create(
AudioManager* audio_manager,
EventHandler* event_handler,
const AudioParameters& params,
- const std::string& device_id) {
+ const std::string& device_id,
+ UserInputMonitor* user_input_monitor) {
DCHECK(audio_manager);
if (!params.IsValid() || (params.channels() > kMaxInputChannels))
return NULL;
- if (factory_)
- return factory_->Create(audio_manager, event_handler, params);
-
- scoped_refptr<AudioInputController> controller(new AudioInputController(
- event_handler, NULL));
+ if (factory_) {
+ return factory_->Create(
+ audio_manager, event_handler, params, user_input_monitor);
+ }
+ scoped_refptr<AudioInputController> controller(
+ new AudioInputController(event_handler, NULL, user_input_monitor));
controller->message_loop_ = audio_manager->GetMessageLoop();
@@ -87,7 +92,8 @@ scoped_refptr<AudioInputController> AudioInputController::CreateLowLatency(
EventHandler* event_handler,
const AudioParameters& params,
const std::string& device_id,
- SyncWriter* sync_writer) {
+ SyncWriter* sync_writer,
+ UserInputMonitor* user_input_monitor) {
DCHECK(audio_manager);
DCHECK(sync_writer);
@@ -96,8 +102,8 @@ scoped_refptr<AudioInputController> AudioInputController::CreateLowLatency(
// Create the AudioInputController object and ensure that it runs on
// the audio-manager thread.
- scoped_refptr<AudioInputController> controller(new AudioInputController(
- event_handler, sync_writer));
+ scoped_refptr<AudioInputController> controller(
+ new AudioInputController(event_handler, sync_writer, user_input_monitor));
controller->message_loop_ = audio_manager->GetMessageLoop();
// Create and open a new audio input stream from the existing
@@ -116,14 +122,15 @@ scoped_refptr<AudioInputController> AudioInputController::CreateForStream(
const scoped_refptr<base::MessageLoopProxy>& message_loop,
EventHandler* event_handler,
AudioInputStream* stream,
- SyncWriter* sync_writer) {
+ SyncWriter* sync_writer,
+ UserInputMonitor* user_input_monitor) {
DCHECK(sync_writer);
DCHECK(stream);
// Create the AudioInputController object and ensure that it runs on
// the audio-manager thread.
- scoped_refptr<AudioInputController> controller(new AudioInputController(
- event_handler, sync_writer));
+ scoped_refptr<AudioInputController> controller(
+ new AudioInputController(event_handler, sync_writer, user_input_monitor));
controller->message_loop_ = message_loop;
// TODO(miu): See TODO at top of file. Until that's resolved, we need to
@@ -233,6 +240,9 @@ void AudioInputController::DoRecord() {
stream_->Start(this);
handler_->OnRecording(this);
+
+ if (user_input_monitor_)
+ user_input_monitor_->AddKeyStrokeListener(this);
}
void AudioInputController::DoClose() {
@@ -251,6 +261,9 @@ void AudioInputController::DoClose() {
}
state_ = kClosed;
+
+ if (user_input_monitor_)
+ user_input_monitor_->RemoveKeyStrokeListener(this);
}
}
@@ -320,10 +333,13 @@ void AudioInputController::DoCheckForNoData() {
void AudioInputController::OnData(AudioInputStream* stream, const uint8* data,
uint32 size, uint32 hardware_delay_bytes,
double volume) {
+ bool key_pressed = false;
{
base::AutoLock auto_lock(lock_);
if (state_ != kRecording)
return;
+
+ std::swap(key_pressed, key_pressed_);
}
// Mark data as active to ensure that the periodic calls to
@@ -332,7 +348,7 @@ void AudioInputController::OnData(AudioInputStream* stream, const uint8* data,
// Use SyncSocket if we are in a low-latency mode.
if (LowLatencyMode()) {
- sync_writer_->Write(data, size, volume);
+ sync_writer_->Write(data, size, volume, key_pressed);
sync_writer_->UpdateRecordedBytes(hardware_delay_bytes);
return;
}
@@ -353,8 +369,13 @@ void AudioInputController::OnError(AudioInputStream* stream) {
&AudioInputController::DoReportError, this));
}
+void AudioInputController::OnKeyStroke() {
+ base::AutoLock auto_lock(lock_);
+ key_pressed_ = true;
+}
+
void AudioInputController::DoStopCloseAndClearStream(
- base::WaitableEvent *done) {
+ base::WaitableEvent* done) {
DCHECK(message_loop_->BelongsToCurrentThread());
// Allow calling unconditionally and bail if we don't have a stream to close.
diff --git a/media/audio/audio_input_controller.h b/media/audio/audio_input_controller.h
index 586d477..6be4821 100644
--- a/media/audio/audio_input_controller.h
+++ b/media/audio/audio_input_controller.h
@@ -16,6 +16,7 @@
#include "base/timer/timer.h"
#include "media/audio/audio_io.h"
#include "media/audio/audio_manager_base.h"
+#include "media/base/user_input_monitor.h"
// An AudioInputController controls an AudioInputStream and records data
// from this input stream. The two main methods are Record() and Close() and
@@ -74,7 +75,8 @@ namespace media {
class MEDIA_EXPORT AudioInputController
: public base::RefCountedThreadSafe<AudioInputController>,
- public AudioInputStream::AudioInputCallback {
+ public AudioInputStream::AudioInputCallback,
+ public UserInputMonitor::KeyStrokeListener {
public:
// An event handler that receives events from the AudioInputController. The
// following methods are all called on the audio thread.
@@ -102,7 +104,10 @@ class MEDIA_EXPORT AudioInputController
// Write certain amount of data from |data|. This method returns
// number of written bytes.
- virtual uint32 Write(const void* data, uint32 size, double volume) = 0;
+ virtual uint32 Write(const void* data,
+ uint32 size,
+ double volume,
+ bool key_pressed) = 0;
// Close this synchronous writer.
virtual void Close() = 0;
@@ -110,11 +115,15 @@ class MEDIA_EXPORT AudioInputController
// AudioInputController::Create() can use the currently registered Factory
// to create the AudioInputController. Factory is intended for testing only.
+ // |user_input_monitor| is used for typing detection and can be NULL.
class Factory {
public:
- virtual AudioInputController* Create(AudioManager* audio_manager,
- EventHandler* event_handler,
- AudioParameters params) = 0;
+ virtual AudioInputController* Create(
+ AudioManager* audio_manager,
+ EventHandler* event_handler,
+ AudioParameters params,
+ UserInputMonitor* user_input_monitor) = 0;
+
protected:
virtual ~Factory() {}
};
@@ -123,11 +132,13 @@ class MEDIA_EXPORT AudioInputController
// The audio device will be created on the audio thread, and when that is
// done, the event handler will receive an OnCreated() call from that same
// thread. |device_id| is the unique ID of the audio device to be opened.
+ // |user_input_monitor| is used for typing detection and can be NULL.
static scoped_refptr<AudioInputController> Create(
AudioManager* audio_manager,
EventHandler* event_handler,
const AudioParameters& params,
- const std::string& device_id);
+ const std::string& device_id,
+ UserInputMonitor* user_input_monitor);
// Sets the factory used by the static method Create(). AudioInputController
// does not take ownership of |factory|. A value of NULL results in an
@@ -138,25 +149,28 @@ class MEDIA_EXPORT AudioInputController
// Factory method for creating an AudioInputController for low-latency mode.
// The audio device will be created on the audio thread, and when that is
// done, the event handler will receive an OnCreated() call from that same
- // thread.
+ // thread. |user_input_monitor| is used for typing detection and can be NULL.
static scoped_refptr<AudioInputController> CreateLowLatency(
AudioManager* audio_manager,
EventHandler* event_handler,
const AudioParameters& params,
const std::string& device_id,
// External synchronous writer for audio controller.
- SyncWriter* sync_writer);
+ SyncWriter* sync_writer,
+ UserInputMonitor* user_input_monitor);
// Factory method for creating an AudioInputController for low-latency mode,
// taking ownership of |stream|. The stream will be opened on the audio
// thread, and when that is done, the event handler will receive an
- // OnCreated() call from that same thread.
+ // OnCreated() call from that same thread. |user_input_monitor| is used for
+ // typing detection and can be NULL.
static scoped_refptr<AudioInputController> CreateForStream(
const scoped_refptr<base::MessageLoopProxy>& message_loop,
EventHandler* event_handler,
AudioInputStream* stream,
// External synchronous writer for audio controller.
- SyncWriter* sync_writer);
+ SyncWriter* sync_writer,
+ UserInputMonitor* user_input_monitor);
// Starts recording using the created audio input stream.
// This method is called on the creator thread.
@@ -189,6 +203,9 @@ class MEDIA_EXPORT AudioInputController
bool LowLatencyMode() const { return sync_writer_ != NULL; }
+ // Impl of KeyStrokeListener.
+ virtual void OnKeyStroke() OVERRIDE;
+
protected:
friend class base::RefCountedThreadSafe<AudioInputController>;
@@ -201,7 +218,9 @@ class MEDIA_EXPORT AudioInputController
kError
};
- AudioInputController(EventHandler* handler, SyncWriter* sync_writer);
+ AudioInputController(EventHandler* handler,
+ SyncWriter* sync_writer,
+ UserInputMonitor* user_input_monitor);
virtual ~AudioInputController();
// Methods called on the audio thread (owned by the AudioManager).
@@ -266,6 +285,11 @@ class MEDIA_EXPORT AudioInputController
double max_volume_;
+ UserInputMonitor* user_input_monitor_;
+
+ // True if any key has been pressed after the last OnData call.
+ bool key_pressed_;
+
DISALLOW_COPY_AND_ASSIGN(AudioInputController);
};
diff --git a/media/audio/audio_input_controller_unittest.cc b/media/audio/audio_input_controller_unittest.cc
index b96ef3a..6388cbf 100644
--- a/media/audio/audio_input_controller_unittest.cc
+++ b/media/audio/audio_input_controller_unittest.cc
@@ -83,9 +83,13 @@ TEST_F(AudioInputControllerTest, CreateAndClose) {
scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
AudioParameters params(AudioParameters::AUDIO_FAKE, kChannelLayout,
kSampleRate, kBitsPerSample, kSamplesPerPacket);
+
scoped_refptr<AudioInputController> controller =
- AudioInputController::Create(audio_manager.get(), &event_handler, params,
- AudioManagerBase::kDefaultDeviceId);
+ AudioInputController::Create(audio_manager.get(),
+ &event_handler,
+ params,
+ AudioManagerBase::kDefaultDeviceId,
+ NULL);
ASSERT_TRUE(controller.get());
// Wait for OnCreated() to fire.
@@ -120,8 +124,11 @@ TEST_F(AudioInputControllerTest, RecordAndClose) {
// Creating the AudioInputController should render an OnCreated() call.
scoped_refptr<AudioInputController> controller =
- AudioInputController::Create(audio_manager.get(), &event_handler, params,
- AudioManagerBase::kDefaultDeviceId);
+ AudioInputController::Create(audio_manager.get(),
+ &event_handler,
+ params,
+ AudioManagerBase::kDefaultDeviceId,
+ NULL);
ASSERT_TRUE(controller.get());
// Start recording and trigger one OnRecording() call.
@@ -167,8 +174,11 @@ TEST_F(AudioInputControllerTest, RecordAndError) {
// Creating the AudioInputController should render an OnCreated() call.
scoped_refptr<AudioInputController> controller =
- AudioInputController::Create(audio_manager.get(), &event_handler, params,
- AudioManagerBase::kDefaultDeviceId);
+ AudioInputController::Create(audio_manager.get(),
+ &event_handler,
+ params,
+ AudioManagerBase::kDefaultDeviceId,
+ NULL);
ASSERT_TRUE(controller.get());
// Start recording and trigger one OnRecording() call.
@@ -196,11 +206,17 @@ TEST_F(AudioInputControllerTest, SamplesPerPacketTooLarge) {
.Times(Exactly(0));
scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
- AudioParameters params(AudioParameters::AUDIO_FAKE, kChannelLayout,
- kSampleRate, kBitsPerSample, kSamplesPerPacket * 1000);
+ AudioParameters params(AudioParameters::AUDIO_FAKE,
+ kChannelLayout,
+ kSampleRate,
+ kBitsPerSample,
+ kSamplesPerPacket * 1000);
scoped_refptr<AudioInputController> controller =
- AudioInputController::Create(audio_manager.get(), &event_handler, params,
- AudioManagerBase::kDefaultDeviceId);
+ AudioInputController::Create(audio_manager.get(),
+ &event_handler,
+ params,
+ AudioManagerBase::kDefaultDeviceId,
+ NULL);
ASSERT_FALSE(controller.get());
}
@@ -216,11 +232,17 @@ TEST_F(AudioInputControllerTest, CloseTwice) {
.Times(Exactly(1));
scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
- AudioParameters params(AudioParameters::AUDIO_FAKE, kChannelLayout,
- kSampleRate, kBitsPerSample, kSamplesPerPacket);
+ AudioParameters params(AudioParameters::AUDIO_FAKE,
+ kChannelLayout,
+ kSampleRate,
+ kBitsPerSample,
+ kSamplesPerPacket);
scoped_refptr<AudioInputController> controller =
- AudioInputController::Create(audio_manager.get(), &event_handler, params,
- AudioManagerBase::kDefaultDeviceId);
+ AudioInputController::Create(audio_manager.get(),
+ &event_handler,
+ params,
+ AudioManagerBase::kDefaultDeviceId,
+ NULL);
ASSERT_TRUE(controller.get());
controller->Record();
diff --git a/media/audio/audio_input_device.cc b/media/audio/audio_input_device.cc
index 87fd571..5477be6 100644
--- a/media/audio/audio_input_device.cc
+++ b/media/audio/audio_input_device.cc
@@ -294,6 +294,7 @@ void AudioInputDevice::AudioThreadCallback::Process(int pending_data) {
DCHECK_EQ(buffer->params.size,
segment_length_ - sizeof(AudioInputBufferParameters));
double volume = buffer->params.volume;
+ bool key_pressed = buffer->params.key_pressed;
int audio_delay_milliseconds = pending_data / bytes_per_ms_;
int16* memory = reinterpret_cast<int16*>(&buffer->audio[0]);
@@ -308,8 +309,8 @@ void AudioInputDevice::AudioThreadCallback::Process(int pending_data) {
// Deliver captured data to the client in floating point format
// and update the audio-delay measurement.
- capture_callback_->Capture(audio_bus_.get(),
- audio_delay_milliseconds, volume);
+ capture_callback_->Capture(
+ audio_bus_.get(), audio_delay_milliseconds, volume, key_pressed);
}
} // namespace media
diff --git a/media/audio/audio_parameters.h b/media/audio/audio_parameters.h
index 2817cd2..bc629a7 100644
--- a/media/audio/audio_parameters.h
+++ b/media/audio/audio_parameters.h
@@ -14,6 +14,7 @@ namespace media {
struct MEDIA_EXPORT AudioInputBufferParameters {
double volume;
uint32 size;
+ bool key_pressed;
};
// Use a struct-in-struct approach to ensure that we can calculate the required
diff --git a/media/audio/test_audio_input_controller_factory.cc b/media/audio/test_audio_input_controller_factory.cc
index 64bfb9f..d146231 100644
--- a/media/audio/test_audio_input_controller_factory.cc
+++ b/media/audio/test_audio_input_controller_factory.cc
@@ -12,8 +12,9 @@ TestAudioInputController::TestAudioInputController(
AudioManager* audio_manager,
const AudioParameters& audio_parameters,
EventHandler* event_handler,
- SyncWriter* sync_writer)
- : AudioInputController(event_handler, sync_writer),
+ SyncWriter* sync_writer,
+ UserInputMonitor* user_input_monitor)
+ : AudioInputController(event_handler, sync_writer, user_input_monitor),
audio_parameters_(audio_parameters),
factory_(factory),
event_handler_(event_handler) {
@@ -48,10 +49,11 @@ TestAudioInputControllerFactory::~TestAudioInputControllerFactory() {
AudioInputController* TestAudioInputControllerFactory::Create(
AudioManager* audio_manager,
AudioInputController::EventHandler* event_handler,
- AudioParameters params) {
+ AudioParameters params,
+ UserInputMonitor* user_input_monitor) {
DCHECK(!controller_); // Only one test instance managed at a time.
- controller_ = new TestAudioInputController(this, audio_manager, params,
- event_handler, NULL);
+ controller_ = new TestAudioInputController(
+ this, audio_manager, params, event_handler, NULL, user_input_monitor);
return controller_;
}
diff --git a/media/audio/test_audio_input_controller_factory.h b/media/audio/test_audio_input_controller_factory.h
index 0a17947..4968c01 100644
--- a/media/audio/test_audio_input_controller_factory.h
+++ b/media/audio/test_audio_input_controller_factory.h
@@ -10,6 +10,7 @@
namespace media {
+class UserInputMonitor;
class TestAudioInputControllerFactory;
// TestAudioInputController and TestAudioInputControllerFactory are used for
@@ -56,7 +57,8 @@ class TestAudioInputController : public AudioInputController {
AudioManager* audio_manager,
const AudioParameters& audio_parameters,
EventHandler* event_handler,
- SyncWriter* sync_writer);
+ SyncWriter* sync_writer,
+ UserInputMonitor* user_input_monitor);
// Returns the event handler installed on the AudioInputController.
EventHandler* event_handler() const { return event_handler_; }
@@ -94,7 +96,8 @@ class TestAudioInputControllerFactory : public AudioInputController::Factory {
virtual AudioInputController* Create(
AudioManager* audio_manager,
AudioInputController::EventHandler* event_handler,
- AudioParameters params) OVERRIDE;
+ AudioParameters params,
+ UserInputMonitor* user_input_monitor) OVERRIDE;
void SetDelegateForTests(TestAudioInputControllerDelegate* delegate);
diff --git a/media/base/audio_capturer_source.h b/media/base/audio_capturer_source.h
index deae5e2..b584f8a 100644
--- a/media/base/audio_capturer_source.h
+++ b/media/base/audio_capturer_source.h
@@ -26,7 +26,8 @@ class AudioCapturerSource
// Callback to deliver the captured data from the OS.
virtual void Capture(AudioBus* audio_source,
int audio_delay_milliseconds,
- double volume) = 0;
+ double volume,
+ bool key_pressed) = 0;
// Signals an error has occurred.
virtual void OnCaptureError() = 0;
diff --git a/media/base/user_input_monitor.cc b/media/base/user_input_monitor.cc
new file mode 100644
index 0000000..18b4c80
--- /dev/null
+++ b/media/base/user_input_monitor.cc
@@ -0,0 +1,87 @@
+// Copyright 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/base/user_input_monitor.h"
+
+#include "third_party/skia/include/core/SkPoint.h"
+
+namespace media {
+
+#ifdef DISABLE_USER_INPUT_MONITOR
+scoped_ptr<UserInputMonitor> UserInputMonitor::Create(
+ const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner,
+ const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner) {
+ return scoped_ptr<UserInputMonitor>();
+}
+#endif // DISABLE_USER_INPUT_MONITOR
+
+UserInputMonitor::~UserInputMonitor() {
+ DCHECK(!monitoring_mouse_);
+ DCHECK(!monitoring_keyboard_);
+}
+
+void UserInputMonitor::AddMouseListener(MouseEventListener* listener) {
+ base::AutoLock auto_lock(lock_);
+ mouse_listeners_.AddObserver(listener);
+ if (!monitoring_mouse_) {
+ StartMouseMonitoring();
+ monitoring_mouse_ = true;
+ DVLOG(2) << "Started mouse monitoring.";
+ }
+}
+void UserInputMonitor::RemoveMouseListener(MouseEventListener* listener) {
+ base::AutoLock auto_lock(lock_);
+ mouse_listeners_.RemoveObserver(listener);
+ if (mouse_listeners_.size() == 0) {
+ StopMouseMonitoring();
+ monitoring_mouse_ = false;
+ DVLOG(2) << "Stopped mouse monitoring.";
+ }
+}
+void UserInputMonitor::AddKeyStrokeListener(KeyStrokeListener* listener) {
+ base::AutoLock auto_lock(lock_);
+ key_stroke_listeners_.AddObserver(listener);
+ if (!monitoring_keyboard_) {
+ StartKeyboardMonitoring();
+ monitoring_keyboard_ = true;
+ DVLOG(2) << "Started keyboard monitoring.";
+ }
+}
+void UserInputMonitor::RemoveKeyStrokeListener(KeyStrokeListener* listener) {
+ base::AutoLock auto_lock(lock_);
+ key_stroke_listeners_.RemoveObserver(listener);
+ if (key_stroke_listeners_.size() == 0) {
+ StopKeyboardMonitoring();
+ monitoring_keyboard_ = false;
+ DVLOG(2) << "Stopped keyboard monitoring.";
+ }
+}
+
+UserInputMonitor::UserInputMonitor()
+ : monitoring_mouse_(false), monitoring_keyboard_(false) {}
+
+void UserInputMonitor::OnMouseEvent(const SkIPoint& position) {
+ base::AutoLock auto_lock(lock_);
+ FOR_EACH_OBSERVER(
+ MouseEventListener, mouse_listeners_, OnMouseMoved(position));
+}
+
+void UserInputMonitor::OnKeyboardEvent(ui::EventType event,
+ ui::KeyboardCode key_code) {
+ base::AutoLock auto_lock(lock_);
+ // Updates the pressed keys and maybe notifies the key_stroke_listeners_.
+ if (event == ui::ET_KEY_PRESSED) {
+ if (pressed_keys_.find(key_code) != pressed_keys_.end())
+ return;
+ pressed_keys_.insert(key_code);
+ DVLOG(6) << "Key stroke detected.";
+ FOR_EACH_OBSERVER(KeyStrokeListener, key_stroke_listeners_, OnKeyStroke());
+ } else {
+ DCHECK_EQ(ui::ET_KEY_RELEASED, event);
+ DCHECK(pressed_keys_.find(key_code) != pressed_keys_.end());
+ pressed_keys_.erase(key_code);
+ }
+}
+
+} // namespace media
diff --git a/media/base/user_input_monitor.h b/media/base/user_input_monitor.h
new file mode 100644
index 0000000..9eb82f3
--- /dev/null
+++ b/media/base/user_input_monitor.h
@@ -0,0 +1,99 @@
+// Copyright 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_BASE_USER_INPUT_MONITOR_H_
+#define MEDIA_BASE_USER_INPUT_MONITOR_H_
+
+#include <set>
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
+#include "base/synchronization/lock.h"
+#include "media/base/media_export.h"
+#include "ui/base/events/event_constants.h"
+#include "ui/base/keycodes/keyboard_codes.h"
+
+struct SkIPoint;
+
+namespace base {
+class SingleThreadTaskRunner;
+} // namespace base
+
+namespace media {
+
+// Monitors and notifies about mouse movements and keyboard events.
+// Thread safe. The thread on which the listenters are called is not guaranteed.
+// The callers should not perform expensive/blocking tasks in the callback since
+// it might be called on the browser UI/IO threads.
+class MEDIA_EXPORT UserInputMonitor {
+ public:
+ // The interface to receive mouse movement events.
+ class MEDIA_EXPORT MouseEventListener {
+ public:
+ // |position| is the new mouse position.
+ virtual void OnMouseMoved(const SkIPoint& position) = 0;
+
+ protected:
+ virtual ~MouseEventListener() {}
+ };
+ // The interface to receive key stroke events.
+ class MEDIA_EXPORT KeyStrokeListener {
+ public:
+ // Called when any key is pressed. Called only once until the key is
+ // released, i.e. holding down a key for a long period will generate one
+ // callback just when the key is pressed down.
+ virtual void OnKeyStroke() = 0;
+
+ protected:
+ virtual ~KeyStrokeListener() {}
+ };
+
+ virtual ~UserInputMonitor();
+
+ // Creates a platform-specific instance of UserInputMonitor.
+ // |io_task_runner| is the task runner for an IO thread.
+ // |ui_task_runner| is the task runner for a UI thread.
+ static scoped_ptr<UserInputMonitor> Create(
+ const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner,
+ const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner);
+
+ // The same |listener| should only be added once.
+ // The clients should make sure to call Remove*Listener before |listener| is
+ // destroyed.
+ void AddMouseListener(MouseEventListener* listener);
+ void RemoveMouseListener(MouseEventListener* listener);
+ void AddKeyStrokeListener(KeyStrokeListener* listener);
+ void RemoveKeyStrokeListener(KeyStrokeListener* listener);
+
+ protected:
+ UserInputMonitor();
+
+ // Called by the platform-specific sub-classes to propagate the events to the
+ // listeners.
+ void OnMouseEvent(const SkIPoint& position);
+ void OnKeyboardEvent(ui::EventType event, ui::KeyboardCode key_code);
+
+ private:
+ virtual void StartMouseMonitoring() = 0;
+ virtual void StopMouseMonitoring() = 0;
+ virtual void StartKeyboardMonitoring() = 0;
+ virtual void StopKeyboardMonitoring() = 0;
+
+ base::Lock lock_;
+ ObserverList<MouseEventListener, true> mouse_listeners_;
+ ObserverList<KeyStrokeListener, true> key_stroke_listeners_;
+ bool monitoring_mouse_;
+ bool monitoring_keyboard_;
+ // The set of keys currently held down. Used for convering raw keyboard events
+ // into KeyStrokeListener callbacks.
+ std::set<ui::KeyboardCode> pressed_keys_;
+
+ DISALLOW_COPY_AND_ASSIGN(UserInputMonitor);
+};
+
+} // namespace media
+
+#endif // MEDIA_BASE_USER_INPUT_MONITOR_H_
diff --git a/media/base/user_input_monitor_linux.cc b/media/base/user_input_monitor_linux.cc
new file mode 100644
index 0000000..ee1b774
--- /dev/null
+++ b/media/base/user_input_monitor_linux.cc
@@ -0,0 +1,338 @@
+// Copyright 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/base/user_input_monitor.h"
+
+#include <sys/select.h>
+#include <unistd.h>
+#define XK_MISCELLANY
+#include <X11/keysymdef.h>
+
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/message_loop/message_pump_libevent.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/single_thread_task_runner.h"
+#include "base/threading/non_thread_safe.h"
+#include "third_party/skia/include/core/SkPoint.h"
+#include "ui/base/keycodes/keyboard_code_conversion_x.h"
+
+// These includes need to be later than dictated by the style guide due to
+// Xlib header pollution, specifically the min, max, and Status macros.
+#include <X11/XKBlib.h>
+#include <X11/Xlibint.h>
+#include <X11/extensions/record.h>
+
+namespace media {
+
+namespace {
+
+class UserInputMonitorLinux : public base::NonThreadSafe,
+ public UserInputMonitor {
+ public:
+ UserInputMonitorLinux(
+ const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner);
+ virtual ~UserInputMonitorLinux();
+
+ private:
+ enum EventType {
+ MOUSE_EVENT,
+ KEYBOARD_EVENT
+ };
+
+ // The actual implementation resides in UserInputMonitorLinux::Core class.
+ // Must be called on the io_task_runner thread.
+ class Core : public base::RefCountedThreadSafe<Core>,
+ public base::MessagePumpLibevent::Watcher {
+ public:
+ typedef const base::Callback<void(const SkIPoint&)> MouseCallback;
+ typedef base::Callback<void(ui::EventType event, ui::KeyboardCode key_code)>
+ KeyboardCallback;
+ Core(const MouseCallback& mouse_callback,
+ const KeyboardCallback& keyboard_callback);
+
+ void StartMonitor(EventType type);
+ void StopMonitor(EventType type);
+
+ private:
+ friend class base::RefCountedThreadSafe<Core>;
+ virtual ~Core();
+
+ // base::MessagePumpLibevent::Watcher interface.
+ virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE;
+ virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE;
+
+ // Processes key and mouse events.
+ void ProcessXEvent(xEvent* event);
+ static void ProcessReply(XPointer self, XRecordInterceptData* data);
+
+ // Used to receive base::MessagePumpLibevent::Watcher events.
+ base::MessagePumpLibevent::FileDescriptorWatcher controller_;
+
+ Display* display_;
+ Display* x_record_display_;
+ XRecordRange* x_record_range_[2];
+ XRecordContext x_record_context_;
+ base::Callback<void(const SkIPoint&)> mouse_callback_;
+ base::Callback<void(ui::EventType event, ui::KeyboardCode key_code)>
+ keyboard_callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(Core);
+ };
+
+ virtual void StartMouseMonitoring() OVERRIDE;
+ virtual void StopMouseMonitoring() OVERRIDE;
+ virtual void StartKeyboardMonitoring() OVERRIDE;
+ virtual void StopKeyboardMonitoring() OVERRIDE;
+
+ void OnMouseEvent(const SkIPoint& position);
+ void OnKeyboardEvent(ui::EventType event, ui::KeyboardCode key_code);
+
+ // Task runner on which X Window events are received.
+ scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
+ scoped_refptr<Core> core_;
+
+ DISALLOW_COPY_AND_ASSIGN(UserInputMonitorLinux);
+};
+
+UserInputMonitorLinux::UserInputMonitorLinux(
+ const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner)
+ : io_task_runner_(io_task_runner),
+ core_(new Core(base::Bind(&UserInputMonitorLinux::OnMouseEvent,
+ base::Unretained(this)),
+ base::Bind(&UserInputMonitorLinux::OnKeyboardEvent,
+ base::Unretained(this)))) {}
+
+UserInputMonitorLinux::~UserInputMonitorLinux() {}
+
+void UserInputMonitorLinux::StartMouseMonitoring() {
+ io_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&Core::StartMonitor, core_.get(), MOUSE_EVENT));
+}
+
+void UserInputMonitorLinux::StopMouseMonitoring() {
+ io_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&Core::StopMonitor, core_.get(), MOUSE_EVENT));
+}
+
+void UserInputMonitorLinux::StartKeyboardMonitoring() {
+ io_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&Core::StartMonitor, core_.get(), KEYBOARD_EVENT));
+}
+
+void UserInputMonitorLinux::StopKeyboardMonitoring() {
+ io_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&Core::StopMonitor, core_.get(), KEYBOARD_EVENT));
+}
+
+void UserInputMonitorLinux::OnMouseEvent(const SkIPoint& position) {
+ UserInputMonitor::OnMouseEvent(position);
+}
+
+void UserInputMonitorLinux::OnKeyboardEvent(ui::EventType event,
+ ui::KeyboardCode key_code) {
+ UserInputMonitor::OnKeyboardEvent(event, key_code);
+}
+
+UserInputMonitorLinux::Core::Core(const MouseCallback& mouse_callback,
+ const KeyboardCallback& keyboard_callback)
+ : display_(NULL),
+ x_record_display_(NULL),
+ x_record_context_(0),
+ mouse_callback_(mouse_callback),
+ keyboard_callback_(keyboard_callback) {
+ x_record_range_[0] = NULL;
+ x_record_range_[1] = NULL;
+}
+
+UserInputMonitorLinux::Core::~Core() {
+ DCHECK(!display_);
+ DCHECK(!x_record_display_);
+ DCHECK(!x_record_range_[0]);
+ DCHECK(!x_record_range_[1]);
+ DCHECK(!x_record_context_);
+}
+
+void UserInputMonitorLinux::Core::StartMonitor(EventType type) {
+ DCHECK(base::MessageLoopForIO::current());
+ // TODO(jamiewalch): We should pass the display in. At that point, since
+ // XRecord needs a private connection to the X Server for its data channel
+ // and both channels are used from a separate thread, we'll need to duplicate
+ // them with something like the following:
+ // XOpenDisplay(DisplayString(display));
+ if (!display_)
+ display_ = XOpenDisplay(NULL);
+
+ if (!x_record_display_)
+ x_record_display_ = XOpenDisplay(NULL);
+
+ if (!display_ || !x_record_display_) {
+ LOG(ERROR) << "Couldn't open X display";
+ return;
+ }
+
+ int xr_opcode, xr_event, xr_error;
+ if (!XQueryExtension(display_, "RECORD", &xr_opcode, &xr_event, &xr_error)) {
+ LOG(ERROR) << "X Record extension not available.";
+ return;
+ }
+
+ if (!x_record_range_[type])
+ x_record_range_[type] = XRecordAllocRange();
+
+ if (!x_record_range_[type]) {
+ LOG(ERROR) << "XRecordAllocRange failed.";
+ return;
+ }
+
+ if (type == MOUSE_EVENT) {
+ x_record_range_[type]->device_events.first = MotionNotify;
+ x_record_range_[type]->device_events.last = MotionNotify;
+ } else {
+ DCHECK_EQ(KEYBOARD_EVENT, type);
+ x_record_range_[type]->device_events.first = KeyPress;
+ x_record_range_[type]->device_events.last = KeyRelease;
+ }
+
+ if (x_record_context_) {
+ XRecordDisableContext(display_, x_record_context_);
+ XFlush(display_);
+ XRecordFreeContext(x_record_display_, x_record_context_);
+ x_record_context_ = 0;
+ }
+ XRecordRange** record_range_to_use =
+ (x_record_range_[0] && x_record_range_[1]) ? x_record_range_
+ : &x_record_range_[type];
+ int number_of_ranges = (x_record_range_[0] && x_record_range_[1]) ? 2 : 1;
+
+ XRecordClientSpec client_spec = XRecordAllClients;
+ x_record_context_ = XRecordCreateContext(x_record_display_,
+ 0,
+ &client_spec,
+ 1,
+ record_range_to_use,
+ number_of_ranges);
+ if (!x_record_context_) {
+ LOG(ERROR) << "XRecordCreateContext failed.";
+ return;
+ }
+
+ if (!XRecordEnableContextAsync(x_record_display_,
+ x_record_context_,
+ &Core::ProcessReply,
+ reinterpret_cast<XPointer>(this))) {
+ LOG(ERROR) << "XRecordEnableContextAsync failed.";
+ return;
+ }
+
+ if (!x_record_range_[0] || !x_record_range_[1]) {
+ // Register OnFileCanReadWithoutBlocking() to be called every time there is
+ // something to read from |x_record_display_|.
+ base::MessageLoopForIO* message_loop = base::MessageLoopForIO::current();
+ int result =
+ message_loop->WatchFileDescriptor(ConnectionNumber(x_record_display_),
+ true,
+ base::MessageLoopForIO::WATCH_READ,
+ &controller_,
+ this);
+ if (!result) {
+ LOG(ERROR) << "Failed to create X record task.";
+ return;
+ }
+ }
+
+ // Fetch pending events if any.
+ OnFileCanReadWithoutBlocking(ConnectionNumber(x_record_display_));
+}
+
+void UserInputMonitorLinux::Core::StopMonitor(EventType type) {
+ DCHECK(base::MessageLoopForIO::current());
+
+ if (x_record_range_[type]) {
+ XFree(x_record_range_[type]);
+ x_record_range_[type] = NULL;
+ }
+ if (x_record_range_[0] || x_record_range_[1])
+ return;
+
+ // Context must be disabled via the control channel because we can't send
+ // any X protocol traffic over the data channel while it's recording.
+ if (x_record_context_) {
+ XRecordDisableContext(display_, x_record_context_);
+ XFlush(display_);
+ XRecordFreeContext(x_record_display_, x_record_context_);
+ x_record_context_ = 0;
+
+ controller_.StopWatchingFileDescriptor();
+ if (x_record_display_) {
+ XCloseDisplay(x_record_display_);
+ x_record_display_ = NULL;
+ }
+ if (display_) {
+ XCloseDisplay(display_);
+ display_ = NULL;
+ }
+ }
+}
+
+void UserInputMonitorLinux::Core::OnFileCanReadWithoutBlocking(int fd) {
+ DCHECK(base::MessageLoopForIO::current());
+ XEvent event;
+ // Fetch pending events if any.
+ while (XPending(x_record_display_)) {
+ XNextEvent(x_record_display_, &event);
+ }
+}
+
+void UserInputMonitorLinux::Core::OnFileCanWriteWithoutBlocking(int fd) {
+ NOTREACHED();
+}
+
+void UserInputMonitorLinux::Core::ProcessXEvent(xEvent* event) {
+ if (event->u.u.type == MotionNotify) {
+ SkIPoint position(SkIPoint::Make(event->u.keyButtonPointer.rootX,
+ event->u.keyButtonPointer.rootY));
+ mouse_callback_.Run(position);
+ } else {
+ ui::EventType type;
+ if (event->u.u.type == KeyPress) {
+ type = ui::ET_KEY_PRESSED;
+ } else if (event->u.u.type == KeyRelease) {
+ type = ui::ET_KEY_RELEASED;
+ } else {
+ NOTREACHED();
+ }
+
+ KeySym key_sym = XkbKeycodeToKeysym(display_, event->u.u.detail, 0, 0);
+ ui::KeyboardCode key_code = ui::KeyboardCodeFromXKeysym(key_sym);
+ keyboard_callback_.Run(type, key_code);
+ }
+}
+
+// static
+void UserInputMonitorLinux::Core::ProcessReply(XPointer self,
+ XRecordInterceptData* data) {
+ if (data->category == XRecordFromServer) {
+ xEvent* event = reinterpret_cast<xEvent*>(data->data);
+ reinterpret_cast<Core*>(self)->ProcessXEvent(event);
+ }
+ XRecordFreeData(data);
+}
+
+} // namespace
+
+scoped_ptr<UserInputMonitor> UserInputMonitor::Create(
+ const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner,
+ const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner) {
+ return scoped_ptr<UserInputMonitor>(
+ new UserInputMonitorLinux(io_task_runner));
+}
+
+} // namespace media
diff --git a/media/base/user_input_monitor_mac.mm b/media/base/user_input_monitor_mac.mm
new file mode 100644
index 0000000..4ffad42
--- /dev/null
+++ b/media/base/user_input_monitor_mac.mm
@@ -0,0 +1,16 @@
+// Copyright 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/base/user_input_monitor.h"
+
+namespace media {
+
+// TODO(jiayl): add the implementation.
+scoped_ptr<UserInputMonitor> UserInputMonitor::Create(
+ const scoped_refptr<base::SingleThreadTaskRunner>& input_task_runner,
+ const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner) {
+ return scoped_ptr<UserInputMonitor>();
+}
+
+} // namespace media
diff --git a/media/base/user_input_monitor_win.cc b/media/base/user_input_monitor_win.cc
new file mode 100644
index 0000000..4ffad42
--- /dev/null
+++ b/media/base/user_input_monitor_win.cc
@@ -0,0 +1,16 @@
+// Copyright 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/base/user_input_monitor.h"
+
+namespace media {
+
+// TODO(jiayl): add the implementation.
+scoped_ptr<UserInputMonitor> UserInputMonitor::Create(
+ const scoped_refptr<base::SingleThreadTaskRunner>& input_task_runner,
+ const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner) {
+ return scoped_ptr<UserInputMonitor>();
+}
+
+} // namespace media
diff --git a/media/media.gyp b/media/media.gyp
index 92642a2..800126b 100644
--- a/media/media.gyp
+++ b/media/media.gyp
@@ -304,6 +304,11 @@
'base/stream_parser_buffer.cc',
'base/stream_parser_buffer.h',
'base/text_track.h',
+ 'base/user_input_monitor.cc',
+ 'base/user_input_monitor.h',
+ 'base/user_input_monitor_linux.cc',
+ 'base/user_input_monitor_mac.mm',
+ 'base/user_input_monitor_win.cc',
'base/video_decoder.cc',
'base/video_decoder.h',
'base/video_decoder_config.cc',
@@ -541,6 +546,7 @@
['include', '^base/media\\.cc$'],
['include', '^base/media_stub\\.cc$'],
['include', '^base/media_switches\\.'],
+ ['include', '^base/user_input_monitor\\.'],
['include', '^base/vector_math\\.'],
],
'link_settings': {
@@ -551,6 +557,9 @@
'$(SDKROOT)/System/Library/Frameworks/CoreMIDI.framework',
],
},
+ 'defines': [
+ 'DISABLE_USER_INPUT_MONITOR',
+ ],
}],
['OS=="android"', {
'link_settings': {
@@ -578,6 +587,9 @@
],
}],
],
+ 'defines': [
+ 'DISABLE_USER_INPUT_MONITOR',
+ ],
}],
# A simple WebM encoder for animated avatars on ChromeOS.
['chromeos==1', {
@@ -591,6 +603,15 @@
'webm/chromeos/webm_encoder.cc',
'webm/chromeos/webm_encoder.h',
],
+ 'defines': [
+ # TODO(jiayl): figure out why MediaStreamInfoBarTest.
+ # DenyingCameraDoesNotCauseStickyDenyForMics fails on ChromeOS and
+ # remove this.
+ 'DISABLE_USER_INPUT_MONITOR',
+ ],
+ 'sources!': [
+ 'base/user_input_monitor_linux.cc',
+ ],
}],
['use_alsa==1', {
'link_settings': {
@@ -626,8 +647,16 @@
'-lXdamage',
'-lXext',
'-lXfixes',
+ '-lXtst',
],
},
+ }, { # else: use_x11==0
+ 'sources!': [
+ 'base/user_input_monitor_linux.cc',
+ ],
+ 'defines': [
+ 'DISABLE_USER_INPUT_MONITOR',
+ ],
}],
['use_cras==1', {
'cflags': [