Changed: #795 #1460 Make sure the streaming thread safely stops in all cases

This commit is contained in:
kaetemi 2012-04-12 00:25:26 +02:00
parent 3a36329dd7
commit 029e4d4173
5 changed files with 80 additions and 20 deletions

View file

@ -86,7 +86,7 @@ public:
private:
void prepareDecoder();
void bufferMore(uint bytes);
inline bool bufferMore(uint bytes);
private:
CStreamFileSource(const CStreamFileSource &);

View file

@ -55,6 +55,9 @@ public:
virtual void setLooping(bool l);
/// Play
virtual void play();
protected:
void stopInt();
public:
/// Stop playing
virtual void stop();
/// Get playing state. Return false even if the source has stopped on its own.

View file

@ -60,6 +60,9 @@ bool CSourceMusicChannel::play(const std::string &filepath, bool async, bool loo
m_Sound.setMusicFilePath(filepath, async, loop);
m_Source = new CStreamFileSource(&m_Sound, false, NULL, NULL, NULL, NULL);
m_Source->setSourceRelativeMode(true);
m_Source->setPos(NLMISC::CVector::Null);
m_Source->setRelativeGain(m_Gain);
m_Source->play();

View file

@ -65,13 +65,18 @@ void CStreamFileSource::play()
if (m_Thread->isRunning() && m_WaitingForPlay)
{
nldebug("play waiting %s", getStreamFileSound()->getFilePath().c_str());
if (m_NextBuffer || !m_FreeBuffers)
{
nldebug("play waiting, play stream %s", getStreamFileSound()->getFilePath().c_str());
CStreamSource::play();
if (!_Playing && !m_WaitingForPlay)
{
nldebug("playing not possible or necessary for some reason");
}
}
else
{
nldebug("play waiting, hop onto waiting list %s", getStreamFileSound()->getFilePath().c_str());
m_WaitingForPlay = true;
CAudioMixerUser *mixer = CAudioMixerUser::instance();
mixer->addSourceWaitingForPlay(this);
@ -79,7 +84,7 @@ void CStreamFileSource::play()
}
else if (!_Playing)
{
nldebug("play waiting %s", getStreamFileSound()->getFilePath().c_str());
nldebug("play go %s", getStreamFileSound()->getFilePath().c_str());
if (!m_WaitingForPlay)
{
// thread may be stopping from stop call
@ -98,14 +103,20 @@ void CStreamFileSource::play()
if (!getStreamFileSound()->getAsync())
{
// wait until at least one buffer is ready
while (!(m_NextBuffer || !m_FreeBuffers) && m_WaitingForPlay)
NLMISC::nlSleep(10);
while (!(m_NextBuffer || !m_FreeBuffers) && m_WaitingForPlay && m_Thread->isRunning())
{
nldebug("wait buffer");
NLMISC::nlSleep(100);
}
if (m_WaitingForPlay && m_Thread->isRunning())
{
CStreamSource::play();
if (!_Playing)
{
nlwarning("Failed to synchronously start playing a file stream source. This happens when all physical tracks are in use. Use a Highest Priority sound");
}
}
}
else
{
CAudioMixerUser *mixer = CAudioMixerUser::instance();
@ -135,7 +146,19 @@ void CStreamFileSource::stop()
{
nldebug("stop %s", getStreamFileSound()->getFilePath().c_str());
CStreamSource::stop();
CStreamSource::stopInt();
nldebug("stopInt ok");
if (_Spawn)
{
if (_SpawnEndCb != NULL)
_SpawnEndCb(this, _CbUserParam);
m_Thread->wait();
delete this;
}
nldebug("stop ok");
// thread will check _Playing to stop
}
@ -229,30 +252,41 @@ void CStreamFileSource::prepareDecoder()
this->preAllocate(bytes * 2);
}
void CStreamFileSource::bufferMore(uint bytes) // buffer from bytes (minimum) to bytes * 2 (maximum)
inline bool CStreamFileSource::bufferMore(uint bytes) // buffer from bytes (minimum) to bytes * 2 (maximum)
{
uint8 *buffer = this->lock(bytes * 2);
if (buffer)
{
uint32 result = m_AudioDecoder->getNextBytes(buffer, bytes, bytes * 2);
this->unlock(result);
return true;
}
return false;
}
void CStreamFileSource::run()
{
nldebug("run");
nldebug("run %s", getStreamFileSound()->getFilePath().c_str());
uint dumpI = 0;
bool looping = _Looping;
if (getStreamFileSound()->getAsync())
prepareDecoder();
uint samples, bytes;
this->getRecommendedBufferSize(samples, bytes);
bufferMore(bytes);
uint32 recSleep = 40;
uint32 doSleep = 10;
while (_Playing || m_WaitingForPlay)
{
if (!m_AudioDecoder->isMusicEnded())
{
++dumpI;
if (!(dumpI % 100))
{
nldebug("buffer %s %s %s", _Playing ? "PLAYING" : "NP", m_WaitingForPlay ? "WAITING" : "NW", getStreamFileSound()->getFilePath().c_str());
nldebug("gain %f", hasPhysicalSource() ? getPhysicalSource()->getGain() : -1.0f);
}
bool newLooping = _Looping;
if (looping != newLooping)
{
@ -260,14 +294,19 @@ void CStreamFileSource::run()
looping = newLooping;
}
bufferMore(bytes);
NLMISC::nlSleep(this->getRecommendedSleepTime());
// reduce sleeping time if nothing was buffered
if (bufferMore(bytes)) recSleep = doSleep = this->getRecommendedSleepTime();
else doSleep = recSleep >> 2; // /4
NLMISC::nlSleep(doSleep);
}
else
{
// wait until done playing buffers
while (this->hasFilledBuffersAvailable())
while (this->hasFilledBuffersAvailable() && (_Playing || m_WaitingForPlay))
{
nldebug("music ended, wait until done %s", getStreamFileSound()->getFilePath().c_str());
NLMISC::nlSleep(40);
}
// stop the physical source
// if (hasPhysicalSource())
// getPhysicalSource()->stop();
@ -284,6 +323,11 @@ void CStreamFileSource::run()
delete m_AudioDecoder;
m_AudioDecoder = NULL;
}
// drop buffers
m_FreeBuffers = 3;
m_NextBuffer = 0;
nldebug("run end %s", getStreamFileSound()->getFilePath().c_str());
}
} /* namespace NLSOUND */

View file

@ -160,7 +160,8 @@ void CStreamSource::play()
_SpawnEndCb(this, _CbUserParam);
delete this;
}
// nldebug("CStreamSource %p : play FAILED !", (CAudioMixerUser::IMixerEvent*)this);
nldebug("CStreamSource %p : play FAILED, source is too far away !", (CAudioMixerUser::IMixerEvent*)this);
m_WaitingForPlay = false;
return;
}
@ -216,6 +217,7 @@ void CStreamSource::play()
_SpawnEndCb(this, _CbUserParam);
delete this;
}
m_WaitingForPlay = false;
return;
}
}
@ -232,8 +234,7 @@ void CStreamSource::play()
#endif
}
/// Stop playing
void CStreamSource::stop()
void CStreamSource::stopInt()
{
CAutoMutex<CMutex> autoMutex(m_BufferMutex);
@ -248,7 +249,10 @@ void CStreamSource::stop()
}
if (!_Playing)
{
m_WaitingForPlay = false;
return;
}
if (hasPhysicalSource())
releasePhysicalSource();
@ -258,14 +262,20 @@ void CStreamSource::stop()
m_FreeBuffers = 3;
m_NextBuffer = 0;
m_WaitingForPlay = false;
}
/// Stop playing
void CStreamSource::stop()
{
stopInt();
if (_Spawn)
{
if (_SpawnEndCb != NULL)
_SpawnEndCb(this, _CbUserParam);
delete this;
}
m_WaitingForPlay = false;
}
void CStreamSource::setPos(const NLMISC::CVector& pos)