diff options
Diffstat (limited to 'media')
-rw-r--r-- | media/audio/audio_output.h | 6 | ||||
-rw-r--r-- | media/audio/fake_audio_output_stream.cc | 2 | ||||
-rw-r--r-- | media/audio/linux/alsa_output.cc | 8 | ||||
-rw-r--r-- | media/audio/linux/alsa_output.h | 3 | ||||
-rw-r--r-- | media/audio/linux/alsa_output_unittest.cc | 8 | ||||
-rw-r--r-- | media/audio/mac/audio_output_mac.cc | 4 | ||||
-rw-r--r-- | media/audio/mac/audio_output_mac_unittest.cc | 2 | ||||
-rw-r--r-- | media/audio/simple_sources.cc | 8 | ||||
-rw-r--r-- | media/audio/simple_sources.h | 8 | ||||
-rw-r--r-- | media/audio/simple_sources_unittest.cc | 2 | ||||
-rw-r--r-- | media/audio/win/audio_output_win_unittest.cc | 76 | ||||
-rw-r--r-- | media/audio/win/waveout_output_win.cc | 18 | ||||
-rw-r--r-- | media/audio/win/waveout_output_win.h | 3 | ||||
-rw-r--r-- | media/filters/audio_renderer_impl.cc | 11 | ||||
-rw-r--r-- | media/filters/audio_renderer_impl.h | 4 |
15 files changed, 126 insertions, 37 deletions
diff --git a/media/audio/audio_output.h b/media/audio/audio_output.h index f27858b..e6a1ee5 100644 --- a/media/audio/audio_output.h +++ b/media/audio/audio_output.h @@ -53,8 +53,10 @@ class AudioOutputStream { // buffer size is usually what is specified in Open(). The source // will return the number of bytes it filled. The expected structure of // |dest| is platform and format specific. - virtual size_t OnMoreData(AudioOutputStream* stream, - void* dest, size_t max_size) = 0; + // |pending_bytes| is the number of bytes will be played before the + // requested data is played. + virtual size_t OnMoreData(AudioOutputStream* stream, void* dest, + size_t max_size, int pending_bytes) = 0; // The stream is done with this callback. After this call the audio source // can go away or be destroyed. diff --git a/media/audio/fake_audio_output_stream.cc b/media/audio/fake_audio_output_stream.cc index 6eb5417..af69a2f 100644 --- a/media/audio/fake_audio_output_stream.cc +++ b/media/audio/fake_audio_output_stream.cc @@ -33,7 +33,7 @@ bool FakeAudioOutputStream::Open(size_t packet_size) { void FakeAudioOutputStream::Start(AudioSourceCallback* callback) { callback_ = callback; memset(buffer_.get(), 0, packet_size_); - callback_->OnMoreData(this, buffer_.get(), packet_size_); + callback_->OnMoreData(this, buffer_.get(), packet_size_, 0); } void FakeAudioOutputStream::Stop() { diff --git a/media/audio/linux/alsa_output.cc b/media/audio/linux/alsa_output.cc index 522b8c4..8a59e33 100644 --- a/media/audio/linux/alsa_output.cc +++ b/media/audio/linux/alsa_output.cc @@ -390,8 +390,9 @@ void AlsaPcmOutputStream::BufferPacket(Packet* packet) { // Request more data if we don't have any cached. if (packet->used >= packet->size) { packet->used = 0; + // TODO(hclam): Provide pending bytes. packet->size = shared_data_.OnMoreData(this, packet->buffer.get(), - packet->capacity); + packet->capacity, 0); CHECK(packet->size <= packet->capacity) << "Data source overran buffer."; // This should not happen, but incase it does, drop any trailing bytes @@ -629,10 +630,11 @@ void AlsaPcmOutputStream::SharedData::set_volume(float v) { size_t AlsaPcmOutputStream::SharedData::OnMoreData(AudioOutputStream* stream, void* dest, - size_t max_size) { + size_t max_size, + int pending_bytes) { AutoLock l(lock_); if (source_callback_) { - return source_callback_->OnMoreData(stream, dest, max_size); + return source_callback_->OnMoreData(stream, dest, max_size, pending_bytes); } return 0; diff --git a/media/audio/linux/alsa_output.h b/media/audio/linux/alsa_output.h index 7d5f381..409b158 100644 --- a/media/audio/linux/alsa_output.h +++ b/media/audio/linux/alsa_output.h @@ -168,7 +168,8 @@ class AlsaPcmOutputStream : // is passed into the output stream, but ownership is not transfered which // requires a synchronization on access of the |source_callback_| to avoid // using a deleted callback. - size_t OnMoreData(AudioOutputStream* stream, void* dest, size_t max_size); + size_t OnMoreData(AudioOutputStream* stream, void* dest, + size_t max_size, int pending_bytes); void OnClose(AudioOutputStream* stream); void OnError(AudioOutputStream* stream, int code); diff --git a/media/audio/linux/alsa_output_unittest.cc b/media/audio/linux/alsa_output_unittest.cc index 8e262b1..d736d93 100644 --- a/media/audio/linux/alsa_output_unittest.cc +++ b/media/audio/linux/alsa_output_unittest.cc @@ -40,8 +40,8 @@ class MockAlsaWrapper : public AlsaWrapper { class MockAudioSourceCallback : public AudioOutputStream::AudioSourceCallback { public: - MOCK_METHOD3(OnMoreData, size_t(AudioOutputStream* stream, - void* dest, size_t max_size)); + MOCK_METHOD4(OnMoreData, size_t(AudioOutputStream* stream, void* dest, + size_t max_size, int pending_bytes)); MOCK_METHOD1(OnClose, void(AudioOutputStream* stream)); MOCK_METHOD2(OnError, void(AudioOutputStream* stream, int code)); }; @@ -273,7 +273,7 @@ TEST_F(AlsaPcmOutputStreamTest, StartStop) { // Expect the pre-roll. MockAudioSourceCallback mock_callback; EXPECT_CALL(mock_callback, - OnMoreData(test_stream_.get(), _, kTestPacketSize)) + OnMoreData(test_stream_.get(), _, kTestPacketSize, 0)) .Times(2) .WillRepeatedly(Return(kTestPacketSize)); EXPECT_CALL(mock_alsa_wrapper_, PcmWritei(kFakeHandle, _, _)) @@ -363,7 +363,7 @@ TEST_F(AlsaPcmOutputStreamTest, BufferPacket) { MockAudioSourceCallback mock_callback; EXPECT_CALL(mock_callback, OnMoreData(test_stream_.get(), packet_.buffer.get(), - packet_.capacity)) + packet_.capacity, 0)) .WillOnce(Return(10)); test_stream_->shared_data_.set_source_callback(&mock_callback); diff --git a/media/audio/mac/audio_output_mac.cc b/media/audio/mac/audio_output_mac.cc index 657e91a..2f79233 100644 --- a/media/audio/mac/audio_output_mac.cc +++ b/media/audio/mac/audio_output_mac.cc @@ -160,7 +160,9 @@ void PCMQueueOutAudioOutputStream::RenderCallback(void* p_this, if (!source) return; size_t capacity = buffer->mAudioDataBytesCapacity; - size_t filled = source->OnMoreData(audio_stream, buffer->mAudioData, capacity); + // TODO(hclam): Provide pending bytes. + size_t filled = source->OnMoreData(audio_stream, buffer->mAudioData, + capacity, 0); if (filled > capacity) { // User probably overran our buffer. audio_stream->HandleError(0); diff --git a/media/audio/mac/audio_output_mac_unittest.cc b/media/audio/mac/audio_output_mac_unittest.cc index d6abefd..d6908cb 100644 --- a/media/audio/mac/audio_output_mac_unittest.cc +++ b/media/audio/mac/audio_output_mac_unittest.cc @@ -19,7 +19,7 @@ TEST(MacAudioTest, SineWaveAudio16MonoTest) { // TODO(cpu): Put the real test when the mock renderer is ported. int16 buffer[samples] = { 0xffff }; - source.OnMoreData(NULL, buffer, sizeof(buffer)); + source.OnMoreData(NULL, buffer, sizeof(buffer), 0); EXPECT_EQ(0, buffer[0]); EXPECT_EQ(5126, buffer[1]); } diff --git a/media/audio/simple_sources.cc b/media/audio/simple_sources.cc index eaf671d..d9b30c7 100644 --- a/media/audio/simple_sources.cc +++ b/media/audio/simple_sources.cc @@ -25,8 +25,8 @@ SineWaveAudioSource::SineWaveAudioSource(Format format, int channels, // The implementation could be more efficient if a lookup table is constructed // but it is efficient enough for our simple needs. -size_t SineWaveAudioSource::OnMoreData(AudioOutputStream* stream, - void* dest, size_t max_size) { +size_t SineWaveAudioSource::OnMoreData(AudioOutputStream* stream, void* dest, + size_t max_size, int pending_bytes) { const double kTwoPi = 2.0 * 3.141592653589; double f = freq_ / sample_freq_; int16* sin_tbl = reinterpret_cast<int16*>(dest); @@ -59,8 +59,8 @@ PushSource::~PushSource() { CleanUp(); } -size_t PushSource::OnMoreData(AudioOutputStream* stream, - void* dest, size_t max_size) { +size_t PushSource::OnMoreData(AudioOutputStream* stream, void* dest, + size_t max_size, int pending_bytes) { size_t copied = 0; while (copied < max_size) { AutoLock auto_lock(lock_); diff --git a/media/audio/simple_sources.h b/media/audio/simple_sources.h index 4a3db8d..d43fcdf 100644 --- a/media/audio/simple_sources.h +++ b/media/audio/simple_sources.h @@ -26,7 +26,7 @@ class SineWaveAudioSource : public AudioOutputStream::AudioSourceCallback { // Implementation of AudioSourceCallback. virtual size_t OnMoreData(AudioOutputStream* stream, - void* dest, size_t max_size); + void* dest, size_t max_size, int pending_bytes); virtual void OnClose(AudioOutputStream* stream); virtual void OnError(AudioOutputStream* stream, int code); @@ -73,12 +73,12 @@ class PushSource : public AudioOutputStream::AudioSourceCallback, // Implementation of AudioSourceCallback. virtual size_t OnMoreData(AudioOutputStream* stream, - void* dest, size_t max_size); + void* dest, size_t max_size, int pending_bytes); virtual void OnClose(AudioOutputStream* stream); virtual void OnError(AudioOutputStream* stream, int code); private: - // Defines the unit of playback. We own the memory pointed by |buffer|. + // Defines the unit of playback. We own the memory pointed by |buffer|. struct Packet { char* buffer; size_t size; @@ -88,7 +88,7 @@ class PushSource : public AudioOutputStream::AudioSourceCallback, void CleanUp(); const size_t packet_size_; - typedef std::list<Packet> PacketList; + typedef std::list<Packet> PacketList; PacketList packets_; size_t buffered_bytes_; size_t front_buffer_consumed_; diff --git a/media/audio/simple_sources_unittest.cc b/media/audio/simple_sources_unittest.cc index ef6a5a7..51eb8db 100644 --- a/media/audio/simple_sources_unittest.cc +++ b/media/audio/simple_sources_unittest.cc @@ -54,7 +54,7 @@ TEST(SimpleSourcesTest, PushSourceSmallerWrite) { // Read everything from the push source. for (size_t i = 0; i < kDataSize; i += kReadSize) { size_t size = std::min(kDataSize - i , kReadSize); - EXPECT_EQ(size, push_source.OnMoreData(NULL, read_data.get(), size)); + EXPECT_EQ(size, push_source.OnMoreData(NULL, read_data.get(), size, 0)); EXPECT_EQ(0, memcmp(data.get() + i, read_data.get(), size)); } EXPECT_EQ(0u, push_source.UnProcessedBytes()); diff --git a/media/audio/win/audio_output_win_unittest.cc b/media/audio/win/audio_output_win_unittest.cc index 2393957..38a32f8 100644 --- a/media/audio/win/audio_output_win_unittest.cc +++ b/media/audio/win/audio_output_win_unittest.cc @@ -9,8 +9,17 @@ #include "base/file_util.h" #include "media/audio/audio_output.h" #include "media/audio/simple_sources.h" +#include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +using ::testing::_; +using ::testing::AnyNumber; +using ::testing::DoAll; +using ::testing::InSequence; +using ::testing::NiceMock; +using ::testing::NotNull; +using ::testing::Return; + namespace { const wchar_t kAudioFile1_16b_m_16K[] @@ -27,7 +36,7 @@ class TestSourceBasic : public AudioOutputStream::AudioSourceCallback { } // AudioSourceCallback::OnMoreData implementation: virtual size_t OnMoreData(AudioOutputStream* stream, - void* dest, size_t max_size) { + void* dest, size_t max_size, int pending_bytes) { ++callback_count_; // Touch the first byte to make sure memory is good. if (max_size) @@ -83,9 +92,9 @@ class TestSourceTripleBuffer : public TestSourceBasic { } // Override of TestSourceBasic::OnMoreData. virtual size_t OnMoreData(AudioOutputStream* stream, - void* dest, size_t max_size) { + void* dest, size_t max_size, int pending_bytes) { // Call the base, which increments the callback_count_. - TestSourceBasic::OnMoreData(stream, dest, max_size); + TestSourceBasic::OnMoreData(stream, dest, max_size, 0); if (callback_count() % kNumBuffers == 2) { set_error(!CompareExistingIfNotNULL(2, dest)); } else if (callback_count() % kNumBuffers == 1) { @@ -119,9 +128,9 @@ class TestSourceLaggy : public TestSourceBasic { : laggy_after_buffer_(laggy_after_buffer), lag_in_ms_(lag_in_ms) { } virtual size_t OnMoreData(AudioOutputStream* stream, - void* dest, size_t max_size) { + void* dest, size_t max_size, int pending_bytes) { // Call the base, which increments the callback_count_. - TestSourceBasic::OnMoreData(stream, dest, max_size); + TestSourceBasic::OnMoreData(stream, dest, max_size, 0); if (callback_count() > kNumBuffers) { ::Sleep(lag_in_ms_); } @@ -132,6 +141,14 @@ class TestSourceLaggy : public TestSourceBasic { int lag_in_ms_; }; +class MockAudioSource : public AudioOutputStream::AudioSourceCallback { + public: + MOCK_METHOD4(OnMoreData, size_t(AudioOutputStream* stream, void* dest, + size_t max_size, int pending_bytes)); + MOCK_METHOD1(OnClose, void(AudioOutputStream* stream)); + MOCK_METHOD2(OnError, void(AudioOutputStream* stream, int code)); +}; + // Helper class to memory map an entire file. The mapping is read-only. Don't // use for gigabyte-sized files. Attempts to write to this memory generate // memory access violations. @@ -479,12 +496,12 @@ TEST(WinAudioTest, PCMWaveStreamPlayTwice200HzTone44Kss) { if (!audio_man->HasAudioDevices()) return; AudioOutputStream* oas = - audio_man->MakeAudioStream(AudioManager::AUDIO_PCM_LINEAR, 1, - AudioManager::kAudioCDSampleRate, 16); + audio_man->MakeAudioStream(AudioManager::AUDIO_PCM_LINEAR, 1, + AudioManager::kAudioCDSampleRate, 16); ASSERT_TRUE(NULL != oas); SineWaveAudioSource source(SineWaveAudioSource::FORMAT_16BIT_LINEAR_PCM, 1, - 200.0, AudioManager::kAudioCDSampleRate); + 200.0, AudioManager::kAudioCDSampleRate); size_t bytes_100_ms = (AudioManager::kAudioCDSampleRate / 10) * 2; EXPECT_TRUE(oas->Open(bytes_100_ms)); @@ -506,3 +523,46 @@ TEST(WinAudioTest, PCMWaveStreamPlayTwice200HzTone44Kss) { oas->Close(); } +// Check that the pending bytes value is correct what the stream starts. +TEST(WinAudioTest, PCMWaveStreamPendingBytes) { + if (IsRunningHeadless()) + return; + AudioManager* audio_man = AudioManager::GetAudioManager(); + ASSERT_TRUE(NULL != audio_man); + if (!audio_man->HasAudioDevices()) + return; + AudioOutputStream* oas = + audio_man->MakeAudioStream(AudioManager::AUDIO_PCM_LINEAR, 1, + AudioManager::kAudioCDSampleRate, 16); + ASSERT_TRUE(NULL != oas); + + NiceMock<MockAudioSource> source; + size_t bytes_100_ms = (AudioManager::kAudioCDSampleRate / 10) * 2; + EXPECT_TRUE(oas->Open(bytes_100_ms)); + + // We expect the amount of pending bytes will reaching 2 times of + // |bytes_100_ms| because the audio output stream has a triple buffer scheme. + // And then we will try to provide zero data so the amount of pending bytes + // will go down and eventually read zero. + InSequence s; + EXPECT_CALL(source, OnMoreData(oas, NotNull(), bytes_100_ms, 0)) + .WillOnce(Return(bytes_100_ms)); + EXPECT_CALL(source, OnMoreData(oas, NotNull(), bytes_100_ms, bytes_100_ms)) + .WillOnce(Return(bytes_100_ms)); + EXPECT_CALL(source, OnMoreData(oas, NotNull(), + bytes_100_ms, 2 * bytes_100_ms)) + .WillOnce(Return(bytes_100_ms)); + EXPECT_CALL(source, OnMoreData(oas, NotNull(), + bytes_100_ms, 2 * bytes_100_ms)) + .WillOnce(Return(0)); + EXPECT_CALL(source, OnMoreData(oas, NotNull(), bytes_100_ms, bytes_100_ms)) + .WillOnce(Return(0)); + EXPECT_CALL(source, OnMoreData(oas, NotNull(), bytes_100_ms, 0)) + .Times(AnyNumber()) + .WillRepeatedly(Return(0)); + + oas->Start(&source); + ::Sleep(500); + oas->Stop(); + oas->Close(); +} diff --git a/media/audio/win/waveout_output_win.cc b/media/audio/win/waveout_output_win.cc index 0b4cc6f..21532b7 100644 --- a/media/audio/win/waveout_output_win.cc +++ b/media/audio/win/waveout_output_win.cc @@ -58,7 +58,8 @@ PCMWaveOutAudioOutputStream::PCMWaveOutAudioOutputStream( buffer_(NULL), buffer_size_(0), volume_(1), - channels_(channels) { + channels_(channels), + pending_bytes_(0) { format_.wFormatTag = WAVE_FORMAT_PCM; format_.nChannels = channels > 2 ? 2 : channels; format_.nSamplesPerSec = sampling_rate; @@ -144,6 +145,7 @@ void PCMWaveOutAudioOutputStream::Start(AudioSourceCallback* callback) { WAVEHDR* buffer = buffer_; for (int ix = 0; ix != kNumBuffers; ++ix) { QueueNextPacket(buffer); // Read more data. + pending_bytes_ += buffer->dwBufferLength; buffer = GetNextBuffer(buffer); } buffer = buffer_; @@ -235,8 +237,12 @@ void PCMWaveOutAudioOutputStream::HandleError(MMRESULT error) { void PCMWaveOutAudioOutputStream::QueueNextPacket(WAVEHDR *buffer) { // Call the source which will fill our buffer with pleasant sounds and // return to us how many bytes were used. + // If we are down sampling to a smaller number of channels, we need to + // scale up the amount of pending bytes. // TODO(fbarchard): Handle used 0 by queueing more. - size_t used = callback_->OnMoreData(this, buffer->lpData, buffer_size_); + int scaled_pending_bytes = pending_bytes_ * channels_ / format_.nChannels; + size_t used = callback_->OnMoreData(this, buffer->lpData, buffer_size_, + scaled_pending_bytes); if (used <= buffer_size_) { buffer->dwBufferLength = used * format_.nChannels / channels_; if (channels_ > 2 && format_.nChannels == 2) { @@ -248,7 +254,6 @@ void PCMWaveOutAudioOutputStream::QueueNextPacket(WAVEHDR *buffer) { format_.nChannels, format_.wBitsPerSample >> 3, volume_); } - } else { HandleError(0); return; @@ -282,6 +287,11 @@ void PCMWaveOutAudioOutputStream::WaveCallback(HWAVEOUT hwo, UINT msg, // Not sure if ever hit this but just in case. return; } + + // Before we queue the next packet, we need to adjust the number of pending + // bytes since the last write to hardware. + obj->pending_bytes_ -= buffer->dwBufferLength; + obj->QueueNextPacket(buffer); // Time to send the buffer to the audio driver. Since we are reusing @@ -290,6 +300,8 @@ void PCMWaveOutAudioOutputStream::WaveCallback(HWAVEOUT hwo, UINT msg, if (result != MMSYSERR_NOERROR) obj->HandleError(result); + obj->pending_bytes_ += buffer->dwBufferLength; + } else if (msg == WOM_CLOSE) { // We can be closed before calling Start, so it is possible to have a // null callback at this point. diff --git a/media/audio/win/waveout_output_win.h b/media/audio/win/waveout_output_win.h index e072851..4f090ba 100644 --- a/media/audio/win/waveout_output_win.h +++ b/media/audio/win/waveout_output_win.h @@ -89,6 +89,9 @@ class PCMWaveOutAudioOutputStream : public AudioOutputStream { // Channels from 0 to 6. int channels_; + // Number of bytes yet to be played in the hardware buffer. + int pending_bytes_; + // The id assigned by the operating system to the selected wave output // hardware device. Usually this is just -1 which means 'default device'. UINT device_id_; diff --git a/media/filters/audio_renderer_impl.cc b/media/filters/audio_renderer_impl.cc index 6576679..cf31af7 100644 --- a/media/filters/audio_renderer_impl.cc +++ b/media/filters/audio_renderer_impl.cc @@ -13,7 +13,8 @@ static const size_t kSamplesPerBuffer = 8*1024; AudioRendererImpl::AudioRendererImpl() : AudioRendererBase(), - stream_(NULL) { + stream_(NULL), + bytes_per_second_(0) { } AudioRendererImpl::~AudioRendererImpl() { @@ -48,7 +49,7 @@ void AudioRendererImpl::SetVolume(float volume) { } size_t AudioRendererImpl::OnMoreData(AudioOutputStream* stream, void* dest_void, - size_t len) { + size_t len, int pending_bytes) { // TODO(scherkus): handle end of stream. if (!stream_) return 0; @@ -56,7 +57,9 @@ size_t AudioRendererImpl::OnMoreData(AudioOutputStream* stream, void* dest_void, // TODO(scherkus): Maybe change OnMoreData to pass in char/uint8 or similar. // TODO(fbarchard): Waveout_output_win.h should handle zero length buffers // without clicking. - return FillBuffer(static_cast<uint8*>(dest_void), len, base::TimeDelta()); + base::TimeDelta delay = base::TimeDelta::FromMicroseconds( + base::Time::kMicrosecondsPerSecond * pending_bytes / bytes_per_second_); + return FillBuffer(static_cast<uint8*>(dest_void), len, delay); } void AudioRendererImpl::OnClose(AudioOutputStream* stream) { @@ -78,6 +81,8 @@ bool AudioRendererImpl::OnInitialize(const MediaFormat& media_format) { return false; } + bytes_per_second_ = sample_rate * channels * sample_bits / 8; + // Create our audio stream. stream_ = AudioManager::GetAudioManager()->MakeAudioStream( AudioManager::AUDIO_PCM_LINEAR, channels, sample_rate, sample_bits); diff --git a/media/filters/audio_renderer_impl.h b/media/filters/audio_renderer_impl.h index 1a6c5a1..b15c83d 100644 --- a/media/filters/audio_renderer_impl.h +++ b/media/filters/audio_renderer_impl.h @@ -43,7 +43,8 @@ class AudioRendererImpl : public AudioRendererBase, virtual void SetVolume(float volume); // AudioSourceCallback implementation. - virtual size_t OnMoreData(AudioOutputStream* stream, void* dest, size_t len); + virtual size_t OnMoreData(AudioOutputStream* stream, void* dest, + size_t len, int pending_bytes); virtual void OnClose(AudioOutputStream* stream); virtual void OnError(AudioOutputStream* stream, int code); @@ -60,6 +61,7 @@ class AudioRendererImpl : public AudioRendererBase, private: // Audio output stream device. AudioOutputStream* stream_; + int bytes_per_second_; DISALLOW_COPY_AND_ASSIGN(AudioRendererImpl); }; |