summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authorcpu@chromium.org <cpu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-01-13 03:53:19 +0000
committercpu@chromium.org <cpu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-01-13 03:53:19 +0000
commit143a3873e13984a9aad1950de50f5a68e8e2f49b (patch)
tree4951fd20eede1173c12bb6aa7f25f1c7c2230c17 /media
parent7c79dac6238718b3f8fa9354a68035791aecbde1 (diff)
downloadchromium_src-143a3873e13984a9aad1950de50f5a68e8e2f49b.zip
chromium_src-143a3873e13984a9aad1950de50f5a68e8e2f49b.tar.gz
chromium_src-143a3873e13984a9aad1950de50f5a68e8e2f49b.tar.bz2
More plumbing of the low latency mode
- added flag to factory to request low latency mode - implemented switch from triple buffered to double buffer on windows (mac is already double buffered all the time) - added tests BUG=28292 TEST=ut included Review URL: http://codereview.chromium.org/523073 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@36090 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r--media/audio/audio_output.h28
-rw-r--r--media/audio/linux/alsa_output_unittest.cc2
-rw-r--r--media/audio/win/audio_output_win.cc6
-rw-r--r--media/audio/win/audio_output_win_unittest.cc34
-rw-r--r--media/audio/win/waveout_output_win.cc19
-rw-r--r--media/audio/win/waveout_output_win.h7
6 files changed, 69 insertions, 27 deletions
diff --git a/media/audio/audio_output.h b/media/audio/audio_output.h
index 29712bf..2d249d7 100644
--- a/media/audio/audio_output.h
+++ b/media/audio/audio_output.h
@@ -72,9 +72,11 @@ class AudioOutputStream {
// Open the stream. |packet_size| is the requested buffer allocation which
// the audio source thinks it can usually fill without blocking. Internally
- // two buffers of |packet_size| size are created, one will be locked for
- // playback and one will be ready to be filled in the call to
+ // two or three buffers of |packet_size| size are created, one will be
+ // locked for playback and one will be ready to be filled in the call to
// AudioSourceCallback::OnMoreData().
+ // The number of buffers is controlled by AUDIO_PCM_LOW_LATENCY. See more
+ // information below.
//
// TODO(ajwong): Streams are not reusable, so try to move packet_size into the
// constructor.
@@ -111,11 +113,10 @@ class AudioOutputStream {
class AudioManager {
public:
enum Format {
- AUDIO_PCM_LINEAR = 0, // Pulse code modulation means 'raw' amplitude
- // samples.
- AUDIO_PCM_DELTA, // Delta-encoded pulse code modulation.
- AUDIO_MOCK, // Creates a dummy AudioOutputStream object.
- AUDIO_LAST_FORMAT // Only used for validation of format.
+ AUDIO_PCM_LINEAR = 0, // PCM is 'raw' amplitude samples.
+ AUDIO_PCM_LOW_LATENCY, // Linear PCM, low latency requested.
+ AUDIO_MOCK, // Creates a dummy AudioOutputStream object.
+ AUDIO_LAST_FORMAT // Only used for validation of format.
};
// Telephone quality sample rate, mostly for speech-only audio.
@@ -129,13 +130,18 @@ class AudioManager {
// guarantee that the existing devices support all formats and sample rates.
virtual bool HasAudioDevices() = 0;
- // Factory for all the supported stream formats. At this moment |channels|
- // can be 1 (mono) or 2 (stereo). The |sample_rate| is in hertz and can be
- // any value supported by the underlying platform. For some future formats
- // the |sample_rate| and |bits_per_sample| can take special values.
+ // Factory for all the supported stream formats. The |channels| can be 1 to 5.
+ // The |sample_rate| is in hertz and can be any value supported by the
+ // platform. For some future formats the |sample_rate| and |bits_per_sample|
+ // can take special values.
// Returns NULL if the combination of the parameters is not supported, or if
// we have reached some other platform specific limit.
//
+ // AUDIO_PCM_LOW_LATENCY can be passed to this method and it has two effects:
+ // 1- Instead of triple buffered the audio will be double buffered.
+ // 2- A low latency driver or alternative audio subsystem will be used when
+ // available.
+ //
// Do not free the returned AudioOutputStream. It is owned by AudioManager.
virtual AudioOutputStream* MakeAudioStream(Format format, int channels,
int sample_rate,
diff --git a/media/audio/linux/alsa_output_unittest.cc b/media/audio/linux/alsa_output_unittest.cc
index 70949b5..b426301 100644
--- a/media/audio/linux/alsa_output_unittest.cc
+++ b/media/audio/linux/alsa_output_unittest.cc
@@ -193,7 +193,7 @@ TEST_F(AlsaPcmOutputStreamTest, ConstructedState) {
// Bad format.
test_stream_ = new AlsaPcmOutputStream(kTestDeviceName,
- AudioManager::AUDIO_PCM_DELTA,
+ AudioManager::AUDIO_LAST_FORMAT,
kTestChannels,
kTestSampleRate,
kTestBitsPerSample,
diff --git a/media/audio/win/audio_output_win.cc b/media/audio/win/audio_output_win.cc
index f7eac01..ebfd915 100644
--- a/media/audio/win/audio_output_win.cc
+++ b/media/audio/win/audio_output_win.cc
@@ -54,7 +54,11 @@ AudioOutputStream* AudioManagerWin::MakeAudioStream(Format format, int channels,
if (format == AUDIO_MOCK) {
return FakeAudioOutputStream::MakeFakeStream();
} else if (format == AUDIO_PCM_LINEAR) {
- return new PCMWaveOutAudioOutputStream(this, channels, sample_rate,
+ return new PCMWaveOutAudioOutputStream(this, channels, sample_rate, 3,
+ bits_per_sample, WAVE_MAPPER);
+ } else if (format == AUDIO_PCM_LOW_LATENCY) {
+ // TODO(cpu): waveout cannot hit 20ms latency. Use other method.
+ return new PCMWaveOutAudioOutputStream(this, channels, sample_rate, 2,
bits_per_sample, WAVE_MAPPER);
}
return NULL;
diff --git a/media/audio/win/audio_output_win_unittest.cc b/media/audio/win/audio_output_win_unittest.cc
index c0acfe3..70e9329 100644
--- a/media/audio/win/audio_output_win_unittest.cc
+++ b/media/audio/win/audio_output_win_unittest.cc
@@ -523,6 +523,36 @@ TEST(WinAudioTest, PCMWaveStreamPlayTwice200HzTone44Kss) {
oas->Close();
}
+// With the low latency mode, we have two buffers instead of 3 and we
+// should be able to handle 20ms buffers at 44KHz. See also the SyncSocketBasic
+// test below.
+// TODO(cpu): right now the best we can do is 50ms before it sounds choppy.
+TEST(WinAudioTest, PCMWaveStreamPlay200HzTone44KssLowLatency) {
+ 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_LOW_LATENCY, 1,
+ AudioManager::kAudioCDSampleRate, 16);
+ ASSERT_TRUE(NULL != oas);
+
+ SineWaveAudioSource source(SineWaveAudioSource::FORMAT_16BIT_LINEAR_PCM, 1,
+ 200.0, AudioManager::kAudioCDSampleRate);
+ size_t bytes_50_ms = (AudioManager::kAudioCDSampleRate / 20) * sizeof(uint16);
+
+ EXPECT_TRUE(oas->Open(bytes_50_ms));
+ oas->SetVolume(1.0);
+
+ // Play the wave for .8 seconds.
+ oas->Start(&source);
+ ::Sleep(800);
+ oas->Stop();
+ oas->Close();
+}
+
// Check that the pending bytes value is correct what the stream starts.
TEST(WinAudioTest, PCMWaveStreamPendingBytes) {
if (IsRunningHeadless())
@@ -640,6 +670,8 @@ DWORD __stdcall SyncSocketThread(void* context) {
// The emphasis is to test low-latency with buffers less than 100ms. With
// the waveout api it seems not possible to go below 50ms. In this test
// you should hear a continous 200Hz tone.
+//
+// TODO(cpu): This actually sounds choppy most of the time. Fix it.
TEST(WinAudioTest, SyncSocketBasic) {
if (IsRunningHeadless())
return;
@@ -651,7 +683,7 @@ TEST(WinAudioTest, SyncSocketBasic) {
int sample_rate = AudioManager::kAudioCDSampleRate;
AudioOutputStream* oas =
- audio_man->MakeAudioStream(AudioManager::AUDIO_PCM_LINEAR, 1,
+ audio_man->MakeAudioStream(AudioManager::AUDIO_PCM_LOW_LATENCY, 1,
sample_rate, 16);
ASSERT_TRUE(NULL != oas);
diff --git a/media/audio/win/waveout_output_win.cc b/media/audio/win/waveout_output_win.cc
index 0611da1..e0fdd1e 100644
--- a/media/audio/win/waveout_output_win.cc
+++ b/media/audio/win/waveout_output_win.cc
@@ -30,12 +30,6 @@
// or that we take locks inside WaveCallback() or QueueNextPacket().
namespace {
-
-// We settled for a triple buffering scheme. It seems to strike a good balance
-// between how fast data needs to be provided versus memory usage.
-// Double buffering is insufficient for Vista and produces stutter.
-const size_t kNumBuffers = 3;
-
// Sixty four MB is the maximum buffer size per AudioOutputStream.
const size_t kMaxOpenBufferSize = 1024 * 1024 * 64;
@@ -48,13 +42,14 @@ WAVEHDR* GetNextBuffer(WAVEHDR* current) {
} // namespace
PCMWaveOutAudioOutputStream::PCMWaveOutAudioOutputStream(
- AudioManagerWin* manager, int channels, int sampling_rate,
+ AudioManagerWin* manager, int channels, int sampling_rate, int num_buffers,
char bits_per_sample, UINT device_id)
: state_(PCMA_BRAND_NEW),
manager_(manager),
device_id_(device_id),
waveout_(NULL),
callback_(NULL),
+ num_buffers_(num_buffers),
buffer_(NULL),
buffer_size_(0),
volume_(1),
@@ -81,6 +76,8 @@ bool PCMWaveOutAudioOutputStream::Open(size_t buffer_size) {
return false;
if (buffer_size > kMaxOpenBufferSize)
return false;
+ if (num_buffers_ < 2 || num_buffers_ > 5)
+ return false;
// Open the device. We'll be getting callback in WaveCallback function. They
// occur in a magic, time-critical thread that windows creates.
MMRESULT result = ::waveOutOpen(&waveout_, device_id_, &format_,
@@ -102,7 +99,7 @@ bool PCMWaveOutAudioOutputStream::Open(size_t buffer_size) {
void PCMWaveOutAudioOutputStream::SetupBuffers(size_t rq_size) {
WAVEHDR* last = NULL;
WAVEHDR* first = NULL;
- for (int ix = 0; ix != kNumBuffers; ++ix) {
+ for (int ix = 0; ix != num_buffers_; ++ix) {
size_t sz = sizeof(WAVEHDR) + rq_size;
buffer_ = reinterpret_cast<WAVEHDR*>(new char[sz]);
buffer_->lpData = reinterpret_cast<char*>(buffer_) + sizeof(WAVEHDR);
@@ -125,7 +122,7 @@ void PCMWaveOutAudioOutputStream::SetupBuffers(size_t rq_size) {
void PCMWaveOutAudioOutputStream::FreeBuffers() {
WAVEHDR* current = buffer_;
- for (int ix = 0; ix != kNumBuffers; ++ix) {
+ for (int ix = 0; ix != num_buffers_; ++ix) {
WAVEHDR* next = GetNextBuffer(current);
::waveOutUnprepareHeader(waveout_, current, sizeof(WAVEHDR));
delete[] reinterpret_cast<char*>(current);
@@ -144,7 +141,7 @@ void PCMWaveOutAudioOutputStream::Start(AudioSourceCallback* callback) {
state_ = PCMA_PLAYING;
pending_bytes_ = 0;
WAVEHDR* buffer = buffer_;
- for (int ix = 0; ix != kNumBuffers; ++ix) {
+ for (int ix = 0; ix != num_buffers_; ++ix) {
QueueNextPacket(buffer); // Read more data.
pending_bytes_ += buffer->dwBufferLength;
buffer = GetNextBuffer(buffer);
@@ -157,7 +154,7 @@ void PCMWaveOutAudioOutputStream::Start(AudioSourceCallback* callback) {
}
// Send the buffers to the audio driver. Note that the device is paused
// so we avoid entering the callback method while still here.
- for (int ix = 0; ix != kNumBuffers; ++ix) {
+ for (int ix = 0; ix != num_buffers_; ++ix) {
result = ::waveOutWrite(waveout_, buffer, sizeof(WAVEHDR));
if (result != MMSYSERR_NOERROR) {
HandleError(result);
diff --git a/media/audio/win/waveout_output_win.h b/media/audio/win/waveout_output_win.h
index d965d9f..d587c8c 100644
--- a/media/audio/win/waveout_output_win.h
+++ b/media/audio/win/waveout_output_win.h
@@ -29,7 +29,7 @@ class PCMWaveOutAudioOutputStream : public AudioOutputStream {
// the audio manager who is creating this object and |device_id| which
// is provided by the operating system.
PCMWaveOutAudioOutputStream(AudioManagerWin* manager,
- int channels, int sampling_rate,
+ int channels, int sampling_rate, int num_buffers,
char bits_per_sample, UINT device_id);
virtual ~PCMWaveOutAudioOutputStream();
@@ -80,6 +80,9 @@ class PCMWaveOutAudioOutputStream : public AudioOutputStream {
// We use the callback mostly to periodically request more audio data.
AudioSourceCallback* callback_;
+ // The number of buffers of size |buffer_size_| each to use.
+ const int num_buffers_;
+
// The size in bytes of each audio buffer, we usually have two of these.
size_t buffer_size_;
@@ -87,7 +90,7 @@ class PCMWaveOutAudioOutputStream : public AudioOutputStream {
float volume_;
// Channels from 0 to 6.
- int channels_;
+ const int channels_;
// Number of bytes yet to be played in the hardware buffer.
int pending_bytes_;