summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--content/browser/renderer_host/media/audio_sync_reader.cc15
-rw-r--r--content/browser/renderer_host/media/audio_sync_reader.h1
-rw-r--r--media/audio/audio_output_controller.cc44
-rw-r--r--media/audio/audio_output_controller.h19
-rw-r--r--media/audio/audio_output_controller_unittest.cc54
5 files changed, 131 insertions, 2 deletions
diff --git a/content/browser/renderer_host/media/audio_sync_reader.cc b/content/browser/renderer_host/media/audio_sync_reader.cc
index 470405c..f9c8a4a 100644
--- a/content/browser/renderer_host/media/audio_sync_reader.cc
+++ b/content/browser/renderer_host/media/audio_sync_reader.cc
@@ -21,8 +21,21 @@ AudioSyncReader::AudioSyncReader(base::SharedMemory* shared_memory)
AudioSyncReader::~AudioSyncReader() {
}
+bool AudioSyncReader::DataReady() {
+ return !media::IsUnknownDataSize(
+ shared_memory_,
+ media::PacketSizeSizeInBytes(shared_memory_->created_size()));
+}
+
// media::AudioOutputController::SyncReader implementations.
void AudioSyncReader::UpdatePendingBytes(uint32 bytes) {
+ if (bytes != static_cast<uint32>(media::AudioOutputController::kPauseMark)) {
+ // Store unknown length of data into buffer, so we later
+ // can find out if data became available.
+ media::SetUnknownDataSize(
+ shared_memory_,
+ media::PacketSizeSizeInBytes(shared_memory_->created_size()));
+ }
socket_->Send(&bytes, sizeof(bytes));
}
@@ -38,7 +51,7 @@ uint32 AudioSyncReader::Read(void* data, uint32 size) {
// Optimization: if renderer is "new" one that writes length of data we can
// stop yielding the moment length is written -- not ideal solution,
// but better than nothing.
- while (media::IsUnknownDataSize(shared_memory_, max_size) &&
+ while (!DataReady() &&
((base::Time::Now() - previous_call_time_).InMilliseconds() <
kMinIntervalBetweenReadCallsInMs)) {
base::PlatformThread::YieldCurrentThread();
diff --git a/content/browser/renderer_host/media/audio_sync_reader.h b/content/browser/renderer_host/media/audio_sync_reader.h
index ad27b86..6bf9385 100644
--- a/content/browser/renderer_host/media/audio_sync_reader.h
+++ b/content/browser/renderer_host/media/audio_sync_reader.h
@@ -30,6 +30,7 @@ class AudioSyncReader : public media::AudioOutputController::SyncReader {
virtual void UpdatePendingBytes(uint32 bytes);
virtual uint32 Read(void* data, uint32 size);
virtual void Close();
+ virtual bool DataReady();
bool Init();
bool PrepareForeignSocketHandle(base::ProcessHandle process_handle,
diff --git a/media/audio/audio_output_controller.cc b/media/audio/audio_output_controller.cc
index b8536f9..3331701 100644
--- a/media/audio/audio_output_controller.cc
+++ b/media/audio/audio_output_controller.cc
@@ -13,6 +13,10 @@ namespace media {
// Signal a pause in low-latency mode.
const int AudioOutputController::kPauseMark = -1;
+// Polling-related constants.
+const int AudioOutputController::kPollNumAttempts = 3;
+const int AudioOutputController::kPollPauseInMilliseconds = 3;
+
AudioOutputController::AudioOutputController(EventHandler* handler,
uint32 capacity,
SyncReader* sync_reader)
@@ -23,7 +27,8 @@ AudioOutputController::AudioOutputController(EventHandler* handler,
buffer_(0, capacity),
pending_request_(false),
sync_reader_(sync_reader),
- message_loop_(NULL) {
+ message_loop_(NULL),
+ number_polling_attempts_left_(0) {
}
AudioOutputController::~AudioOutputController() {
@@ -173,6 +178,43 @@ void AudioOutputController::DoPlay() {
return;
state_ = kPlaying;
+ if (LowLatencyMode()) {
+ // Ask for first packet.
+ sync_reader_->UpdatePendingBytes(0);
+
+ // Cannot start stream immediately, should give renderer some time
+ // to deliver data.
+ number_polling_attempts_left_ = kPollNumAttempts;
+ message_loop_->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&AudioOutputController::PollAndStartIfDataReady, this),
+ kPollPauseInMilliseconds);
+ } else {
+ StartStream();
+ }
+}
+
+void AudioOutputController::PollAndStartIfDataReady() {
+ DCHECK_EQ(message_loop_, MessageLoop::current());
+
+ // Being paranoic: do nothing if we were stopped/paused
+ // after DoPlay() but before DoStartStream().
+ if (state_ != kPlaying)
+ return;
+
+ if (--number_polling_attempts_left_ == 0 || sync_reader_->DataReady()) {
+ StartStream();
+ } else {
+ message_loop_->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&AudioOutputController::PollAndStartIfDataReady, this),
+ kPollPauseInMilliseconds);
+ }
+}
+
+void AudioOutputController::StartStream() {
+ DCHECK_EQ(message_loop_, MessageLoop::current());
+
// We start the AudioOutputStream lazily.
stream_->Start(this);
diff --git a/media/audio/audio_output_controller.h b/media/audio/audio_output_controller.h
index 88c4e06..2c2246b 100644
--- a/media/audio/audio_output_controller.h
+++ b/media/audio/audio_output_controller.h
@@ -103,6 +103,13 @@ class MEDIA_EXPORT AudioOutputController
// Close this synchronous reader.
virtual void Close() = 0;
+
+ // Poll if data is ready.
+ // Not reliable, as there is no guarantee that renderer is "new-style"
+ // renderer that writes metadata into buffer. After several unsuccessful
+ // attempts caller should assume the data is ready even if that function
+ // returns false.
+ virtual bool DataReady() = 0;
};
virtual ~AudioOutputController();
@@ -163,12 +170,17 @@ class MEDIA_EXPORT AudioOutputController
virtual void OnError(AudioOutputStream* stream, int code);
private:
+ // We are polling sync reader if data became available.
+ static const int kPollNumAttempts;
+ static const int kPollPauseInMilliseconds;
+
AudioOutputController(EventHandler* handler,
uint32 capacity, SyncReader* sync_reader);
// The following methods are executed on the audio controller thread.
void DoCreate(const AudioParameters& params);
void DoPlay();
+ void PollAndStartIfDataReady();
void DoPause();
void DoFlush();
void DoClose(const base::Closure& closed_task);
@@ -178,6 +190,9 @@ class MEDIA_EXPORT AudioOutputController
// Helper method to submit a OnMoreData() call to the event handler.
void SubmitOnMoreData_Locked();
+ // Helper method that starts physical stream.
+ void StartStream();
+
// |handler_| may be called only if |state_| is not kClosed.
EventHandler* handler_;
AudioOutputStream* stream_;
@@ -205,6 +220,10 @@ class MEDIA_EXPORT AudioOutputController
// The message loop of audio thread that this object runs on.
MessageLoop* message_loop_;
+ // When starting stream we wait for data to become available.
+ // Number of times left.
+ int number_polling_attempts_left_;
+
DISALLOW_COPY_AND_ASSIGN(AudioOutputController);
};
diff --git a/media/audio/audio_output_controller_unittest.cc b/media/audio/audio_output_controller_unittest.cc
index cab9929..f6917a7 100644
--- a/media/audio/audio_output_controller_unittest.cc
+++ b/media/audio/audio_output_controller_unittest.cc
@@ -14,6 +14,7 @@
using ::testing::_;
using ::testing::AtLeast;
+using ::testing::DoAll;
using ::testing::Exactly;
using ::testing::InvokeWithoutArgs;
using ::testing::NotNull;
@@ -54,6 +55,7 @@ class MockAudioOutputControllerSyncReader
MOCK_METHOD1(UpdatePendingBytes, void(uint32 bytes));
MOCK_METHOD2(Read, uint32(void* data, uint32 size));
MOCK_METHOD0(Close, void());
+ MOCK_METHOD0(DataReady, bool());
private:
DISALLOW_COPY_AND_ASSIGN(MockAudioOutputControllerSyncReader);
@@ -150,6 +152,58 @@ TEST(AudioOutputControllerTest, PlayAndClose) {
CloseAudioController(controller);
}
+TEST(AudioOutputControllerTest, PlayAndCloseLowLatency) {
+ if (!HasAudioOutputDevices() || IsRunningHeadless())
+ return;
+
+ MockAudioOutputControllerEventHandler event_handler;
+ base::WaitableEvent event(false, false);
+
+ // If OnCreated is called then signal the event.
+ EXPECT_CALL(event_handler, OnCreated(NotNull()))
+ .WillOnce(SignalEvent(&event));
+
+ // OnPlaying() will be called only once.
+ EXPECT_CALL(event_handler, OnPlaying(NotNull()))
+ .Times(Exactly(1));
+
+ MockAudioOutputControllerSyncReader sync_reader;
+ EXPECT_CALL(sync_reader, UpdatePendingBytes(_))
+ .Times(AtLeast(10));
+ EXPECT_CALL(sync_reader, DataReady())
+ .WillOnce(Return(false))
+ .WillOnce(Return(false))
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(sync_reader, Read(_, kHardwareBufferSize))
+ .Times(AtLeast(10))
+ .WillRepeatedly(DoAll(SignalEvent(&event),
+ Return(1)));
+ EXPECT_CALL(sync_reader, Close());
+
+ AudioParameters params(AudioParameters::AUDIO_PCM_LINEAR, kChannelLayout,
+ kSampleRate, kBitsPerSample, kSamplesPerPacket);
+ scoped_refptr<AudioOutputController> controller =
+ AudioOutputController::CreateLowLatency(&event_handler,
+ params,
+ &sync_reader);
+ ASSERT_TRUE(controller.get());
+
+ // Wait for OnCreated() to be called.
+ event.Wait();
+
+ controller->Play();
+
+ // Wait until the date is requested at least 10 times.
+ for (int i = 0; i < 10; i++) {
+ event.Wait();
+ uint8 buf[1];
+ controller->EnqueueData(buf, 0);
+ }
+
+ // Now stop the controller.
+ CloseAudioController(controller);
+}
+
TEST(AudioOutputControllerTest, PlayPauseClose) {
if (!HasAudioOutputDevices() || IsRunningHeadless())
return;