Bug 793274 - Make sure to have enough frames pushed to the AudioStream before starting it. r=kinetik

This commit is contained in:
Paul Adenot 2012-11-26 15:13:08 +01:00
parent 57c654953f
commit e8ac187b02
4 changed files with 97 additions and 15 deletions

View File

@ -176,6 +176,9 @@ nsHTMLAudioElement::MozWriteAudio(const JS::Value& aData, JSContext* aCx, uint32
// AudioDataValue is 'float', but it's not worth it for this deprecated API.
nsAutoArrayPtr<AudioDataValue> audioData(new AudioDataValue[writeLen * mChannels]);
ConvertAudioSamples(frames, audioData.get(), writeLen * mChannels);
if (!mAudioStream->IsStarted()) {
mAudioStream->Start();
}
nsresult rv = mAudioStream->Write(audioData.get(), writeLen);
if (NS_FAILED(rv)) {

View File

@ -59,6 +59,8 @@ class NativeAudioStream : public AudioStream
uint32_t Available();
void SetVolume(double aVolume);
void Drain();
nsresult Start();
bool IsStarted();
void Pause();
void Resume();
int64_t GetPosition();
@ -181,6 +183,7 @@ AudioStream::AudioStream()
: mInRate(0),
mOutRate(0),
mChannels(0),
mWritten(0),
mAudioClock(this)
{}
@ -276,6 +279,11 @@ nsresult AudioStream::SetPreservesPitch(bool aPreservesPitch)
return NS_OK;
}
int64_t AudioStream::GetWritten()
{
return mWritten;
}
NativeAudioStream::NativeAudioStream() :
mVolume(1.0),
mAudioHandle(0),
@ -386,6 +394,8 @@ nsresult NativeAudioStream::Write(const AudioDataValue* aBuf, uint32_t aFrames)
written = WriteToBackend(aBuf, samples);
}
mWritten += aFrames;
if (written == -1) {
PR_LOG(gAudioStreamLog, PR_LOG_ERROR, ("NativeAudioStream: sa_stream_write error"));
mInError = true;
@ -455,6 +465,19 @@ void NativeAudioStream::Drain()
}
}
nsresult NativeAudioStream::Start()
{
// Since sydneyaudio is a push API, the playback is started when enough frames
// have been written. Hence, Start() is a noop.
return NS_OK;
}
bool NativeAudioStream::IsStarted()
{
// See the comment for the |Start()| method.
return true;
}
void NativeAudioStream::Pause()
{
if (mInError)
@ -597,6 +620,8 @@ class BufferedAudioStream : public AudioStream
uint32_t Available();
void SetVolume(double aVolume);
void Drain();
nsresult Start();
bool IsStarted();
void Pause();
void Resume();
int64_t GetPosition();
@ -782,24 +807,19 @@ BufferedAudioStream::Write(const AudioDataValue* aBuf, uint32_t aFrames)
src += available;
bytesToCopy -= available;
if (mState != STARTED) {
int r;
{
MonitorAutoUnlock mon(mMonitor);
r = cubeb_stream_start(mCubebStream);
}
mState = r == CUBEB_OK ? STARTED : ERRORED;
}
if (mState != STARTED) {
return NS_ERROR_FAILURE;
}
if (bytesToCopy > 0) {
// If we are not playing, but our buffer is full, start playing to make
// room for soon-to-be-decoded data.
if (!IsStarted()) {
MonitorAutoUnlock mon(mMonitor);
Start();
}
mon.Wait();
}
}
mWritten += aFrames;
return NS_OK;
}
@ -838,6 +858,26 @@ BufferedAudioStream::Drain()
}
}
nsresult
BufferedAudioStream::Start()
{
if (!mCubebStream) {
return NS_ERROR_FAILURE;
}
if (mState != STARTED) {
int r = cubeb_stream_start(mCubebStream);
mState = r == CUBEB_OK ? STARTED : ERRORED;
return mState == STARTED ? NS_OK : NS_ERROR_FAILURE;
}
return NS_OK;
}
bool
BufferedAudioStream::IsStarted()
{
return mState == STARTED ? true : false;
}
void
BufferedAudioStream::Pause()
{

View File

@ -44,6 +44,8 @@ class AudioClock
// Get the current pitch preservation state.
// Called on the audio thread.
bool GetPreservesPitch();
// Get the number of frames written to the backend.
int64_t GetWritten();
private:
// This AudioStream holds a strong reference to this AudioClock. This
// pointer is garanteed to always be valid.
@ -128,10 +130,20 @@ public:
// Block until buffered audio data has been consumed.
virtual void Drain() = 0;
// Pause audio playback
// Start the stream.
virtual nsresult Start() = 0;
// Check if the stream is started.
virtual bool IsStarted() = 0;
// Return the number of frames written so far in the stream. This allow the
// caller to check if it is safe to start the stream, if needed.
virtual int64_t GetWritten();
// Pause audio playback.
virtual void Pause() = 0;
// Resume audio playback
// Resume audio playback.
virtual void Resume() = 0;
// Return the position in microseconds of the audio frame being played by
@ -171,6 +183,8 @@ protected:
// Output rate in Hz (characteristic of the playback rate)
int mOutRate;
int mChannels;
// Number of frames written to the buffers.
int64_t mWritten;
AudioClock mAudioClock;
nsAutoPtr<soundtouch::SoundTouch> mTimeStretcher;
};

View File

@ -109,6 +109,9 @@ static const uint32_t QUICK_BUFFERING_LOW_DATA_USECS = 1000000;
// QUICK_BUFFERING_LOW_DATA_USECS.
PR_STATIC_ASSERT(QUICK_BUFFERING_LOW_DATA_USECS <= AMPLE_AUDIO_USECS);
// This value has been chosen empirically.
static const uint32_t AUDIOSTREAM_MIN_WRITE_BEFORE_START_USECS = 200000;
static TimeDuration UsecsToDuration(int64_t aUsecs) {
return TimeDuration::FromMilliseconds(static_cast<double>(aUsecs) / USECS_PER_MS);
}
@ -952,6 +955,19 @@ bool MediaDecoderStateMachine::IsPlaying()
return !mPlayStartTime.IsNull();
}
// If we have already written enough frames to the AudioStream, start the
// playback.
static void
StartAudioStreamPlaybackIfNeeded(AudioStream* aStream)
{
// We want to have enough data in the buffer to start the stream.
if (!aStream->IsStarted() &&
static_cast<double>(aStream->GetWritten()) / aStream->GetRate() >=
static_cast<double>(AUDIOSTREAM_MIN_WRITE_BEFORE_START_USECS) / USECS_PER_S) {
aStream->Start();
}
}
static void WriteSilence(AudioStream* aStream, uint32_t aFrames)
{
uint32_t numSamples = aFrames * aStream->GetChannels();
@ -959,6 +975,8 @@ static void WriteSilence(AudioStream* aStream, uint32_t aFrames)
buf.SetLength(numSamples);
memset(buf.Elements(), 0, numSamples * sizeof(AudioDataValue));
aStream->Write(buf.Elements(), aFrames);
StartAudioStreamPlaybackIfNeeded(aStream);
}
void MediaDecoderStateMachine::AudioLoop()
@ -1109,6 +1127,11 @@ void MediaDecoderStateMachine::AudioLoop()
mState != DECODER_STATE_SHUTDOWN &&
!mStopAudioThread)
{
// If the media was too short to trigger the start of the audio stream,
// start it now.
if (!mAudioStream->IsStarted()) {
mAudioStream->Start();
}
// Last frame pushed to audio hardware, wait for the audio to finish,
// before the audio thread terminates.
bool seeking = false;
@ -1213,6 +1236,8 @@ uint32_t MediaDecoderStateMachine::PlayFromAudioQueue(uint64_t aFrameOffset,
mAudioStream->Write(audio->mAudioData,
audio->mFrames);
StartAudioStreamPlaybackIfNeeded(mAudioStream);
offset = audio->mOffset;
frames = audio->mFrames;