diff options
author | jiayl@chromium.org <jiayl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-08-15 22:02:40 +0000 |
---|---|---|
committer | jiayl@chromium.org <jiayl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-08-15 22:02:40 +0000 |
commit | 61f697f26142f5081aaee176ccd82a2af170eabf (patch) | |
tree | 2dab2a85e1824e8b94ff4beafbaaff180f5778e0 | |
parent | 92be5a41c4a56014723da338a53f5cbed515fe77 (diff) | |
download | chromium_src-61f697f26142f5081aaee176ccd82a2af170eabf.zip chromium_src-61f697f26142f5081aaee176ccd82a2af170eabf.tar.gz chromium_src-61f697f26142f5081aaee176ccd82a2af170eabf.tar.bz2 |
Adding key press detection in the browser process.
It works like this on the browser side:
A new object UserInputMonitor is created on BrowserMainLoop and passed to AudioInputRendererHost to pass to AudioInputController.
AudioInputController::DoRecord calls UserInputMonitor::AddKeyStrokeListener --> UserInputMonitor listens to system key events (only implemented on Linux) --> AudioInputController::OnKeyPressed is called and sets key_pressed_ --> When AudioInputController::OnData called, it writes key_pressed_ to shared memory along with the audio data buffer.
On the renderer side a new param "key_pressed" is added through the code path of passing the flag to the webrtc voice engine.
This CL includes all these changes except the implementation of UserInputMonitor for Windows and Mac. The impl of UserInputMonitor is mostly copied from remoting/host/local_input_monitor_linux.cc
BUG=
Committed: https://src.chromium.org/viewvc/chrome?view=rev&revision=217768
Review URL: https://chromiumcodereview.appspot.com/21183002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@217844 0039d316-1c4b-4281-b951-d872f2087c98
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)); @@ -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': [ |