summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--media/audio/audio_input_controller.cc28
-rw-r--r--media/audio/audio_input_controller.h10
-rw-r--r--media/audio/audio_input_controller_unittest.cc49
-rw-r--r--media/audio/win/wavein_input_win.cc8
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;
}