зеркало из https://github.com/mozilla/gecko-dev.git
Bug 463627 - Audio playback stops after some seconds on Linux - r=kinetik sr=roc
This commit is contained in:
Родитель
4ae82003d3
Коммит
aad3ca91b3
|
@ -40,6 +40,7 @@
|
||||||
|
|
||||||
#include "nscore.h"
|
#include "nscore.h"
|
||||||
#include "prlog.h"
|
#include "prlog.h"
|
||||||
|
#include "nsTArray.h"
|
||||||
|
|
||||||
extern PRLogModuleInfo* gAudioStreamLog;
|
extern PRLogModuleInfo* gAudioStreamLog;
|
||||||
|
|
||||||
|
@ -117,6 +118,12 @@ class nsAudioStream
|
||||||
|
|
||||||
SampleFormat mFormat;
|
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;
|
PRPackedBool mPaused;
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -122,49 +122,63 @@ void nsAudioStream::Write(const void* aBuf, PRUint32 aCount)
|
||||||
"Buffer size must be divisible by channel count");
|
"Buffer size must be divisible by channel count");
|
||||||
|
|
||||||
mSamplesBuffered += aCount;
|
mSamplesBuffered += aCount;
|
||||||
|
PRUint32 offset = mBufferOverflow.Length();
|
||||||
|
PRInt32 count = aCount + offset;
|
||||||
|
|
||||||
if (!mAudioHandle)
|
if (!mAudioHandle)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
nsAutoArrayPtr<short> s_data(new short[aCount]);
|
nsAutoArrayPtr<short> s_data(new short[count]);
|
||||||
|
|
||||||
if (s_data) {
|
if (s_data) {
|
||||||
|
for (PRUint32 i=0; i < offset; ++i) {
|
||||||
|
s_data[i] = mBufferOverflow.ElementAt(i);
|
||||||
|
}
|
||||||
|
mBufferOverflow.Clear();
|
||||||
|
|
||||||
switch (mFormat) {
|
switch (mFormat) {
|
||||||
case FORMAT_U8: {
|
case FORMAT_U8: {
|
||||||
const PRUint8* buf = static_cast<const PRUint8*>(aBuf);
|
const PRUint8* buf = static_cast<const PRUint8*>(aBuf);
|
||||||
PRInt32 volume = PRInt32((1 << 16) * mVolume);
|
PRInt32 volume = PRInt32((1 << 16) * mVolume);
|
||||||
for (PRUint32 i = 0; i < aCount; ++i) {
|
for (PRUint32 i = 0; i < aCount; ++i) {
|
||||||
s_data[i] = short(((PRInt32(buf[i]) - 128) * volume) >> 8);
|
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] = 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);
|
|
||||||
}
|
}
|
||||||
|
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"));
|
PR_LOG(gAudioStreamLog, PR_LOG_ERROR, ("nsAudioStream: sa_stream_write error"));
|
||||||
Shutdown();
|
Shutdown();
|
||||||
}
|
}
|
||||||
|
@ -197,11 +211,21 @@ void nsAudioStream::SetVolume(float aVolume)
|
||||||
void nsAudioStream::Drain()
|
void nsAudioStream::Drain()
|
||||||
{
|
{
|
||||||
if (!mAudioHandle) {
|
if (!mAudioHandle) {
|
||||||
|
// mSamplesBuffered already accounts for the data in the
|
||||||
|
// mBufferOverflow array.
|
||||||
PRUint32 drainTime = (float(mSamplesBuffered) / mRate / mChannels - GetTime()) * 1000.0;
|
PRUint32 drainTime = (float(mSamplesBuffered) / mRate / mChannels - GetTime()) * 1000.0;
|
||||||
PR_Sleep(PR_MillisecondsToInterval(drainTime));
|
PR_Sleep(PR_MillisecondsToInterval(drainTime));
|
||||||
return;
|
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) {
|
if (sa_stream_drain(static_cast<sa_stream_t*>(mAudioHandle)) != SA_SUCCESS) {
|
||||||
PR_LOG(gAudioStreamLog, PR_LOG_ERROR, ("nsAudioStream: sa_stream_drain error"));
|
PR_LOG(gAudioStreamLog, PR_LOG_ERROR, ("nsAudioStream: sa_stream_drain error"));
|
||||||
Shutdown();
|
Shutdown();
|
||||||
|
|
|
@ -141,6 +141,15 @@ public:
|
||||||
MOZ_COUNT_DTOR(FrameData);
|
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;
|
nsAutoArrayPtr<unsigned char> mVideoData;
|
||||||
nsTArray<float> mAudioData;
|
nsTArray<float> mAudioData;
|
||||||
|
@ -260,9 +269,8 @@ public:
|
||||||
void PlayVideo(FrameData* aFrame);
|
void PlayVideo(FrameData* aFrame);
|
||||||
|
|
||||||
// Play the audio data from the given frame. The decode monitor must
|
// Play the audio data from the given frame. The decode monitor must
|
||||||
// be locked when calling this method. Returns PR_FALSE if unable to
|
// be locked when calling this method.
|
||||||
// write to the audio device without blocking.
|
void PlayAudio(FrameData* aFrame);
|
||||||
PRBool PlayAudio(FrameData* aFrame);
|
|
||||||
|
|
||||||
// Called from the main thread to get the current frame time. The decoder
|
// Called from the main thread to get the current frame time. The decoder
|
||||||
// monitor must be obtained before calling this.
|
// monitor must be obtained before calling this.
|
||||||
|
@ -629,13 +637,13 @@ void nsOggDecodeStateMachine::PlayFrame() {
|
||||||
|
|
||||||
double time = (PR_IntervalToMilliseconds(PR_IntervalNow()-mPlayStartTime-mPauseDuration)/1000.0);
|
double time = (PR_IntervalToMilliseconds(PR_IntervalNow()-mPlayStartTime-mPauseDuration)/1000.0);
|
||||||
if (time >= frame->mTime) {
|
if (time >= frame->mTime) {
|
||||||
mDecodedFrames.Pop();
|
|
||||||
// Audio for the current frame is played, but video for the next frame
|
// 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
|
// 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
|
// to when it is played. This will go away when we move to a/v sync
|
||||||
// using the audio hardware clock.
|
// using the audio hardware clock.
|
||||||
PlayVideo(mDecodedFrames.IsEmpty() ? frame : mDecodedFrames.Peek());
|
|
||||||
PlayAudio(frame);
|
PlayAudio(frame);
|
||||||
|
mDecodedFrames.Pop();
|
||||||
|
PlayVideo(mDecodedFrames.IsEmpty() ? frame : mDecodedFrames.Peek());
|
||||||
UpdatePlaybackPosition(frame->mDecodedFrameTime);
|
UpdatePlaybackPosition(frame->mDecodedFrameTime);
|
||||||
delete frame;
|
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");
|
// NS_ASSERTION(PR_InMonitor(mDecoder->GetMonitor()), "PlayAudio() called without acquiring decoder monitor");
|
||||||
if (mAudioStream && aFrame && !aFrame->mAudioData.IsEmpty()) {
|
if (!mAudioStream)
|
||||||
if (PRUint32(mAudioStream->Available()) < aFrame->mAudioData.Length())
|
return;
|
||||||
return PR_FALSE;
|
|
||||||
|
|
||||||
mAudioStream->Write(aFrame->mAudioData.Elements(), aFrame->mAudioData.Length());
|
aFrame->Write(mAudioStream);
|
||||||
}
|
|
||||||
|
|
||||||
return PR_TRUE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void nsOggDecodeStateMachine::OpenAudioStream()
|
void nsOggDecodeStateMachine::OpenAudioStream()
|
||||||
|
@ -1054,6 +1058,14 @@ nsresult nsOggDecodeStateMachine::Run()
|
||||||
if (mState != DECODER_STATE_COMPLETED)
|
if (mState != DECODER_STATE_COMPLETED)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if (mAudioStream) {
|
||||||
|
mon.Exit();
|
||||||
|
mAudioStream->Drain();
|
||||||
|
mon.Enter();
|
||||||
|
if (mState != DECODER_STATE_COMPLETED)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
nsCOMPtr<nsIRunnable> event =
|
nsCOMPtr<nsIRunnable> event =
|
||||||
NS_NEW_RUNNABLE_METHOD(nsOggDecoder, mDecoder, PlaybackEnded);
|
NS_NEW_RUNNABLE_METHOD(nsOggDecoder, mDecoder, PlaybackEnded);
|
||||||
NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
|
NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
|
||||||
|
|
Загрузка…
Ссылка в новой задаче