diff options
-rw-r--r-- | content/browser/renderer_host/media/audio_sync_reader.cc | 15 | ||||
-rw-r--r-- | content/browser/renderer_host/media/audio_sync_reader.h | 1 | ||||
-rw-r--r-- | media/audio/audio_output_controller.cc | 44 | ||||
-rw-r--r-- | media/audio/audio_output_controller.h | 19 | ||||
-rw-r--r-- | media/audio/audio_output_controller_unittest.cc | 54 |
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; |