Bug 463627 - Audio playback stops after some seconds on Linux - r=kinetik sr=roc

This commit is contained in:
Chris Double 2008-12-16 12:06:22 +13:00
Родитель 06622e85df
Коммит 265f31f893
3 изменённых файлов: 88 добавлений и 45 удалений

Просмотреть файл

@ -40,6 +40,7 @@
#include "nscore.h"
#include "prlog.h"
#include "nsTArray.h"
extern PRLogModuleInfo* gAudioStreamLog;
@ -117,6 +118,12 @@ class nsAudioStream
SampleFormat mFormat;
// When a Write() request is made, and the number of samples
// requested to be written exceeds the buffer size of the audio
// backend, the remaining samples are stored in this variable. They
// will be written on the next Write() request.
nsTArray<short> mBufferOverflow;
PRPackedBool mPaused;
};
#endif

Просмотреть файл

@ -122,49 +122,63 @@ void nsAudioStream::Write(const void* aBuf, PRUint32 aCount)
"Buffer size must be divisible by channel count");
mSamplesBuffered += aCount;
PRUint32 offset = mBufferOverflow.Length();
PRInt32 count = aCount + offset;
if (!mAudioHandle)
return;
nsAutoArrayPtr<short> s_data(new short[aCount]);
nsAutoArrayPtr<short> s_data(new short[count]);
if (s_data) {
for (PRUint32 i=0; i < offset; ++i) {
s_data[i] = mBufferOverflow.ElementAt(i);
}
mBufferOverflow.Clear();
switch (mFormat) {
case FORMAT_U8: {
const PRUint8* buf = static_cast<const PRUint8*>(aBuf);
PRInt32 volume = PRInt32((1 << 16) * mVolume);
for (PRUint32 i = 0; i < aCount; ++i) {
s_data[i] = short(((PRInt32(buf[i]) - 128) * volume) >> 8);
}
break;
}
case FORMAT_S16_LE: {
const short* buf = static_cast<const short*>(aBuf);
PRInt32 volume = PRInt32((1 << 16) * mVolume);
for (PRUint32 i = 0; i < aCount; ++i) {
s_data[i] = short((PRInt32(buf[i]) * volume) >> 16);
}
break;
}
case FORMAT_FLOAT32_LE: {
const float* buf = static_cast<const float*>(aBuf);
for (PRUint32 i= 0; i < aCount; ++i) {
float scaled_value = floorf(0.5 + 32768 * buf[i] * mVolume);
if (buf[i] < 0.0) {
s_data[i] = (scaled_value < -32768.0) ?
-32768 :
short(scaled_value);
} else {
s_data[i] = (scaled_value > 32767.0) ?
32767 :
short(scaled_value);
case FORMAT_U8: {
const PRUint8* buf = static_cast<const PRUint8*>(aBuf);
PRInt32 volume = PRInt32((1 << 16) * mVolume);
for (PRUint32 i = 0; i < aCount; ++i) {
s_data[i + offset] = short(((PRInt32(buf[i]) - 128) * volume) >> 8);
}
break;
}
case FORMAT_S16_LE: {
const short* buf = static_cast<const short*>(aBuf);
PRInt32 volume = PRInt32((1 << 16) * mVolume);
for (PRUint32 i = 0; i < aCount; ++i) {
s_data[i + offset] = short((PRInt32(buf[i]) * volume) >> 16);
}
break;
}
case FORMAT_FLOAT32_LE: {
const float* buf = static_cast<const float*>(aBuf);
for (PRUint32 i = 0; i < aCount; ++i) {
float scaled_value = floorf(0.5 + 32768 * buf[i] * mVolume);
if (buf[i] < 0.0) {
s_data[i + offset] = (scaled_value < -32768.0) ?
-32768 :
short(scaled_value);
} else {
s_data[i+offset] = (scaled_value > 32767.0) ?
32767 :
short(scaled_value);
}
}
break;
}
break;
}
}
if (sa_stream_write(static_cast<sa_stream_t*>(mAudioHandle), s_data.get(), aCount * sizeof(short)) != SA_SUCCESS) {
PRInt32 available = Available();
if (available < count) {
mBufferOverflow.AppendElements(s_data.get() + available, (count - available));
count = available;
}
if (sa_stream_write(static_cast<sa_stream_t*>(mAudioHandle),
s_data.get(), count * sizeof(short)) != SA_SUCCESS) {
PR_LOG(gAudioStreamLog, PR_LOG_ERROR, ("nsAudioStream: sa_stream_write error"));
Shutdown();
}
@ -197,11 +211,21 @@ void nsAudioStream::SetVolume(float aVolume)
void nsAudioStream::Drain()
{
if (!mAudioHandle) {
// mSamplesBuffered already accounts for the data in the
// mBufferOverflow array.
PRUint32 drainTime = (float(mSamplesBuffered) / mRate / mChannels - GetTime()) * 1000.0;
PR_Sleep(PR_MillisecondsToInterval(drainTime));
return;
}
// Write any remaining unwritten sound data in the overflow buffer
if (!mBufferOverflow.IsEmpty()) {
if (sa_stream_write(static_cast<sa_stream_t*>(mAudioHandle),
mBufferOverflow.Elements(),
mBufferOverflow.Length() * sizeof(short)) != SA_SUCCESS)
return;
}
if (sa_stream_drain(static_cast<sa_stream_t*>(mAudioHandle)) != SA_SUCCESS) {
PR_LOG(gAudioStreamLog, PR_LOG_ERROR, ("nsAudioStream: sa_stream_drain error"));
Shutdown();

Просмотреть файл

@ -141,6 +141,15 @@ public:
MOZ_COUNT_DTOR(FrameData);
}
// Write the audio data from the frame to the Audio stream.
void Write(nsAudioStream* aStream)
{
PRUint32 length = mAudioData.Length();
if (length == 0)
return;
aStream->Write(mAudioData.Elements(), length);
}
nsAutoArrayPtr<unsigned char> mVideoData;
nsTArray<float> mAudioData;
@ -260,9 +269,8 @@ public:
void PlayVideo(FrameData* aFrame);
// Play the audio data from the given frame. The decode monitor must
// be locked when calling this method. Returns PR_FALSE if unable to
// write to the audio device without blocking.
PRBool PlayAudio(FrameData* aFrame);
// be locked when calling this method.
void PlayAudio(FrameData* aFrame);
// Called from the main thread to get the current frame time. The decoder
// monitor must be obtained before calling this.
@ -629,13 +637,13 @@ void nsOggDecodeStateMachine::PlayFrame() {
double time = (PR_IntervalToMilliseconds(PR_IntervalNow()-mPlayStartTime-mPauseDuration)/1000.0);
if (time >= frame->mTime) {
mDecodedFrames.Pop();
// Audio for the current frame is played, but video for the next frame
// is displayed, to account for lag from the time the audio is written
// to when it is played. This will go away when we move to a/v sync
// using the audio hardware clock.
PlayVideo(mDecodedFrames.IsEmpty() ? frame : mDecodedFrames.Peek());
PlayAudio(frame);
mDecodedFrames.Pop();
PlayVideo(mDecodedFrames.IsEmpty() ? frame : mDecodedFrames.Peek());
UpdatePlaybackPosition(frame->mDecodedFrameTime);
delete frame;
}
@ -677,17 +685,13 @@ void nsOggDecodeStateMachine::PlayVideo(FrameData* aFrame)
}
}
PRBool nsOggDecodeStateMachine::PlayAudio(FrameData* aFrame)
void nsOggDecodeStateMachine::PlayAudio(FrameData* aFrame)
{
// NS_ASSERTION(PR_InMonitor(mDecoder->GetMonitor()), "PlayAudio() called without acquiring decoder monitor");
if (mAudioStream && aFrame && !aFrame->mAudioData.IsEmpty()) {
if (PRUint32(mAudioStream->Available()) < aFrame->mAudioData.Length())
return PR_FALSE;
if (!mAudioStream)
return;
mAudioStream->Write(aFrame->mAudioData.Elements(), aFrame->mAudioData.Length());
}
return PR_TRUE;
aFrame->Write(mAudioStream);
}
void nsOggDecodeStateMachine::OpenAudioStream()
@ -1054,6 +1058,14 @@ nsresult nsOggDecodeStateMachine::Run()
if (mState != DECODER_STATE_COMPLETED)
continue;
if (mAudioStream) {
mon.Exit();
mAudioStream->Drain();
mon.Enter();
if (mState != DECODER_STATE_COMPLETED)
continue;
}
nsCOMPtr<nsIRunnable> event =
NS_NEW_RUNNABLE_METHOD(nsOggDecoder, mDecoder, PlaybackEnded);
NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);