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: private:
void prepareDecoder(); void prepareDecoder();
void bufferMore(uint bytes); inline bool bufferMore(uint bytes);
private: private:
CStreamFileSource(const CStreamFileSource &); CStreamFileSource(const CStreamFileSource &);

View file

@ -55,6 +55,9 @@ public:
virtual void setLooping(bool l); virtual void setLooping(bool l);
/// Play /// Play
virtual void play(); virtual void play();
protected:
void stopInt();
public:
/// Stop playing /// Stop playing
virtual void stop(); virtual void stop();
/// Get playing state. Return false even if the source has stopped on its own. /// 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_Sound.setMusicFilePath(filepath, async, loop);
m_Source = new CStreamFileSource(&m_Sound, false, NULL, NULL, NULL, NULL); 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(); m_Source->play();

View file

@ -65,13 +65,18 @@ void CStreamFileSource::play()
if (m_Thread->isRunning() && m_WaitingForPlay) if (m_Thread->isRunning() && m_WaitingForPlay)
{ {
nldebug("play waiting %s", getStreamFileSound()->getFilePath().c_str());
if (m_NextBuffer || !m_FreeBuffers) if (m_NextBuffer || !m_FreeBuffers)
{ {
nldebug("play waiting, play stream %s", getStreamFileSound()->getFilePath().c_str());
CStreamSource::play(); CStreamSource::play();
if (!_Playing && !m_WaitingForPlay)
{
nldebug("playing not possible or necessary for some reason");
}
} }
else else
{ {
nldebug("play waiting, hop onto waiting list %s", getStreamFileSound()->getFilePath().c_str());
m_WaitingForPlay = true; m_WaitingForPlay = true;
CAudioMixerUser *mixer = CAudioMixerUser::instance(); CAudioMixerUser *mixer = CAudioMixerUser::instance();
mixer->addSourceWaitingForPlay(this); mixer->addSourceWaitingForPlay(this);
@ -79,7 +84,7 @@ void CStreamFileSource::play()
} }
else if (!_Playing) else if (!_Playing)
{ {
nldebug("play waiting %s", getStreamFileSound()->getFilePath().c_str()); nldebug("play go %s", getStreamFileSound()->getFilePath().c_str());
if (!m_WaitingForPlay) if (!m_WaitingForPlay)
{ {
// thread may be stopping from stop call // thread may be stopping from stop call
@ -98,12 +103,18 @@ void CStreamFileSource::play()
if (!getStreamFileSound()->getAsync()) if (!getStreamFileSound()->getAsync())
{ {
// wait until at least one buffer is ready // wait until at least one buffer is ready
while (!(m_NextBuffer || !m_FreeBuffers) && m_WaitingForPlay) while (!(m_NextBuffer || !m_FreeBuffers) && m_WaitingForPlay && m_Thread->isRunning())
NLMISC::nlSleep(10);
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"); 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 else
@ -135,7 +146,19 @@ void CStreamFileSource::stop()
{ {
nldebug("stop %s", getStreamFileSound()->getFilePath().c_str()); 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 // thread will check _Playing to stop
} }
@ -229,30 +252,41 @@ void CStreamFileSource::prepareDecoder()
this->preAllocate(bytes * 2); 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); uint8 *buffer = this->lock(bytes * 2);
if (buffer) if (buffer)
{ {
uint32 result = m_AudioDecoder->getNextBytes(buffer, bytes, bytes * 2); uint32 result = m_AudioDecoder->getNextBytes(buffer, bytes, bytes * 2);
this->unlock(result); this->unlock(result);
return true;
} }
return false;
} }
void CStreamFileSource::run() void CStreamFileSource::run()
{ {
nldebug("run"); nldebug("run %s", getStreamFileSound()->getFilePath().c_str());
uint dumpI = 0;
bool looping = _Looping; bool looping = _Looping;
if (getStreamFileSound()->getAsync()) if (getStreamFileSound()->getAsync())
prepareDecoder(); prepareDecoder();
uint samples, bytes; uint samples, bytes;
this->getRecommendedBufferSize(samples, bytes); this->getRecommendedBufferSize(samples, bytes);
bufferMore(bytes); uint32 recSleep = 40;
uint32 doSleep = 10;
while (_Playing || m_WaitingForPlay) while (_Playing || m_WaitingForPlay)
{ {
if (!m_AudioDecoder->isMusicEnded()) 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; bool newLooping = _Looping;
if (looping != newLooping) if (looping != newLooping)
{ {
@ -260,14 +294,19 @@ void CStreamFileSource::run()
looping = newLooping; looping = newLooping;
} }
bufferMore(bytes); // reduce sleeping time if nothing was buffered
NLMISC::nlSleep(this->getRecommendedSleepTime()); if (bufferMore(bytes)) recSleep = doSleep = this->getRecommendedSleepTime();
else doSleep = recSleep >> 2; // /4
NLMISC::nlSleep(doSleep);
} }
else else
{ {
// wait until done playing buffers // 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); NLMISC::nlSleep(40);
}
// stop the physical source // stop the physical source
// if (hasPhysicalSource()) // if (hasPhysicalSource())
// getPhysicalSource()->stop(); // getPhysicalSource()->stop();
@ -284,6 +323,11 @@ void CStreamFileSource::run()
delete m_AudioDecoder; delete m_AudioDecoder;
m_AudioDecoder = NULL; m_AudioDecoder = NULL;
} }
// drop buffers
m_FreeBuffers = 3;
m_NextBuffer = 0;
nldebug("run end %s", getStreamFileSound()->getFilePath().c_str());
} }
} /* namespace NLSOUND */ } /* namespace NLSOUND */

View file

@ -160,7 +160,8 @@ void CStreamSource::play()
_SpawnEndCb(this, _CbUserParam); _SpawnEndCb(this, _CbUserParam);
delete this; 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; return;
} }
@ -216,6 +217,7 @@ void CStreamSource::play()
_SpawnEndCb(this, _CbUserParam); _SpawnEndCb(this, _CbUserParam);
delete this; delete this;
} }
m_WaitingForPlay = false;
return; return;
} }
} }
@ -232,8 +234,7 @@ void CStreamSource::play()
#endif #endif
} }
/// Stop playing void CStreamSource::stopInt()
void CStreamSource::stop()
{ {
CAutoMutex<CMutex> autoMutex(m_BufferMutex); CAutoMutex<CMutex> autoMutex(m_BufferMutex);
@ -248,7 +249,10 @@ void CStreamSource::stop()
} }
if (!_Playing) if (!_Playing)
{
m_WaitingForPlay = false;
return; return;
}
if (hasPhysicalSource()) if (hasPhysicalSource())
releasePhysicalSource(); releasePhysicalSource();
@ -258,14 +262,20 @@ void CStreamSource::stop()
m_FreeBuffers = 3; m_FreeBuffers = 3;
m_NextBuffer = 0; m_NextBuffer = 0;
m_WaitingForPlay = false;
}
/// Stop playing
void CStreamSource::stop()
{
stopInt();
if (_Spawn) if (_Spawn)
{ {
if (_SpawnEndCb != NULL) if (_SpawnEndCb != NULL)
_SpawnEndCb(this, _CbUserParam); _SpawnEndCb(this, _CbUserParam);
delete this; delete this;
} }
m_WaitingForPlay = false;
} }
void CStreamSource::setPos(const NLMISC::CVector& pos) void CStreamSource::setPos(const NLMISC::CVector& pos)