diff options
-rw-r--r-- | media/audio/audio_input_controller.cc | 28 | ||||
-rw-r--r-- | media/audio/audio_input_controller.h | 10 | ||||
-rw-r--r-- | media/audio/audio_input_controller_unittest.cc | 49 | ||||
-rw-r--r-- | media/audio/win/wavein_input_win.cc | 8 |
4 files changed, 90 insertions, 5 deletions
diff --git a/media/audio/audio_input_controller.cc b/media/audio/audio_input_controller.cc index 311a54e..a8209f5 100644 --- a/media/audio/audio_input_controller.cc +++ b/media/audio/audio_input_controller.cc @@ -7,9 +7,12 @@ #include "base/threading/thread_restrictions.h" #include "media/base/limits.h" -namespace media { +namespace { +const int kMaxInputChannels = 2; +const int kTimerResetInterval = 1; // One second. +} -static const int kMaxInputChannels = 2; +namespace media { // static AudioInputController::Factory* AudioInputController::factory_ = NULL; @@ -18,6 +21,10 @@ AudioInputController::AudioInputController(EventHandler* handler, SyncWriter* sync_writer) : handler_(handler), stream_(NULL), + ALLOW_THIS_IN_INITIALIZER_LIST(no_data_timer_( + base::TimeDelta::FromSeconds(kTimerResetInterval), + this, + &AudioInputController::DoReportNoDataError)), state_(kEmpty), thread_("AudioInputControllerThread"), sync_writer_(sync_writer) { @@ -123,6 +130,9 @@ void AudioInputController::DoCreate(AudioParameters params) { return; } + thread_.message_loop()->PostTask( + FROM_HERE, + NewRunnableMethod(this, &AudioInputController::DoResetNoDataTimer)); state_ = kCreated; handler_->OnCreated(this); } @@ -168,6 +178,16 @@ void AudioInputController::DoReportError(int code) { handler_->OnError(this, code); } +void AudioInputController::DoReportNoDataError() { + DCHECK_EQ(thread_.message_loop(), MessageLoop::current()); + handler_->OnError(this, 0); +} + +void AudioInputController::DoResetNoDataTimer() { + DCHECK_EQ(thread_.message_loop(), MessageLoop::current()); + no_data_timer_.Reset(); +} + void AudioInputController::OnData(AudioInputStream* stream, const uint8* data, uint32 size) { { @@ -176,6 +196,10 @@ void AudioInputController::OnData(AudioInputStream* stream, const uint8* data, return; } + thread_.message_loop()->PostTask( + FROM_HERE, + NewRunnableMethod(this, &AudioInputController::DoResetNoDataTimer)); + // Use SyncSocket if we are in a low-latency mode. if (LowLatencyMode()) { sync_writer_->Write(data, size); diff --git a/media/audio/audio_input_controller.h b/media/audio/audio_input_controller.h index b21eaba..b8789e0 100644 --- a/media/audio/audio_input_controller.h +++ b/media/audio/audio_input_controller.h @@ -9,6 +9,7 @@ #include "base/memory/scoped_ptr.h" #include "base/synchronization/lock.h" #include "base/threading/thread.h" +#include "base/timer.h" #include "media/audio/audio_io.h" #include "media/audio/audio_manager.h" @@ -100,6 +101,7 @@ class AudioInputController // AudioInputController being created directly. #if defined(UNIT_TEST) static void set_factory(Factory* factory) { factory_ = factory; } + AudioInputStream* stream() { return stream_; } #endif // Starts recording in this audio input stream. @@ -138,10 +140,18 @@ class AudioInputController void DoRecord(); void DoClose(); void DoReportError(int code); + void DoReportNoDataError(); + void DoResetNoDataTimer(); EventHandler* handler_; AudioInputStream* stream_; + // |no_data_timer_| is used to call DoReportNoDataError when we stop + // receiving OnData calls without an OnClose call. This can occur when an + // audio input device is unplugged whilst recording on Windows. + // See http://crbug.com/79936 for details. + base::DelayTimer<AudioInputController> no_data_timer_; + // |state_| is written on the audio input controller thread and is read on // the hardware audio thread. These operations need to be locked. But lock // is not required for reading on the audio input controller thread. diff --git a/media/audio/audio_input_controller_unittest.cc b/media/audio/audio_input_controller_unittest.cc index 6a62871..d7ce4db 100644 --- a/media/audio/audio_input_controller_unittest.cc +++ b/media/audio/audio_input_controller_unittest.cc @@ -92,13 +92,60 @@ TEST(AudioInputControllerTest, RecordAndClose) { event.Wait(); event.Reset(); - // Play and then wait for the event to be signaled. + // Record and then wait for the event to be signaled. controller->Record(); event.Wait(); controller->Close(); } +// Test that the AudioInputController reports an error when the input stream +// stops without an OnClose callback. +TEST(AudioInputControllerTest, RecordAndError) { + MockAudioInputControllerEventHandler event_handler; + base::WaitableEvent event(false, false); + int count = 0; + + // If OnCreated is called then signal the event. + EXPECT_CALL(event_handler, OnCreated(NotNull())) + .WillOnce(InvokeWithoutArgs(&event, &base::WaitableEvent::Signal)); + + // OnRecording() will be called only once. + EXPECT_CALL(event_handler, OnRecording(NotNull())) + .Times(Exactly(1)); + + // If OnData is called enough then signal the event. + EXPECT_CALL(event_handler, OnData(NotNull(), NotNull(), _)) + .Times(AtLeast(10)) + .WillRepeatedly(CheckCountAndSignalEvent(&count, 10, &event)); + + // OnError will be called after the data stream stops. + EXPECT_CALL(event_handler, OnError(NotNull(), 0)) + .WillOnce(InvokeWithoutArgs(&event, &base::WaitableEvent::Signal)); + + AudioParameters params(AudioParameters::AUDIO_MOCK, kChannelLayout, + kSampleRate, kBitsPerSample, kSamplesPerPacket); + scoped_refptr<AudioInputController> controller = + AudioInputController::Create(&event_handler, params); + ASSERT_TRUE(controller.get()); + + // Wait for OnCreated() to be called. + event.Wait(); + event.Reset(); + + // Record and then wait for the event to be signaled. + controller->Record(); + event.Wait(); + event.Reset(); + + // Wait for the stream to be stopped. + AudioInputStream* stream = controller->stream(); + stream->Stop(); + event.Wait(); + + controller->Close(); +} + // Test that AudioInputController rejects insanely large packet sizes. TEST(AudioInputControllerTest, SamplesPerPacketTooLarge) { // Create an audio device with a very large packet size. diff --git a/media/audio/win/wavein_input_win.cc b/media/audio/win/wavein_input_win.cc index fdc08b8..fcffe7a 100644 --- a/media/audio/win/wavein_input_win.cc +++ b/media/audio/win/wavein_input_win.cc @@ -8,12 +8,15 @@ #include <mmsystem.h> #pragma comment(lib, "winmm.lib") -#include "base/basictypes.h" #include "base/logging.h" #include "media/audio/audio_io.h" #include "media/audio/audio_util.h" #include "media/audio/win/audio_manager_win.h" +namespace { +const int kStopInputStreamCallbackTimeout = 3000; // Three seconds. +} + // Our sound buffers are allocated once and kept in a linked list using the // the WAVEHDR::dwUser variable. The last buffer points to the first buffer. static WAVEHDR* GetNextBuffer(WAVEHDR* current) { @@ -130,7 +133,8 @@ void PCMWaveInAudioInputStream::Stop() { return; state_ = kStateStopping; // Wait for the callback to finish, it will signal us when ready to be reset. - if (WAIT_OBJECT_0 != ::WaitForSingleObject(stopped_event_, INFINITE)) { + if (WAIT_OBJECT_0 != + ::WaitForSingleObject(stopped_event_, kStopInputStreamCallbackTimeout)) { HandleError(::GetLastError()); return; } |