зеркало из https://github.com/mozilla/pjs.git
Bug 463627 - Audio playback stops after some seconds on Linux - r=kinetik sr=roc
This commit is contained in:
Родитель
06622e85df
Коммит
265f31f893
|
@ -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);
|
||||
|
|
Загрузка…
Ссылка в новой задаче