Implemented a stable solution to video stream muting and clearing.

This commit is contained in:
Leon Styhre 2021-05-29 10:52:40 +02:00
parent 17fec1aac7
commit c91662befa
2 changed files with 49 additions and 11 deletions

View file

@ -19,7 +19,9 @@ std::vector<std::shared_ptr<Sound>> AudioManager::sSoundVector;
SDL_AudioDeviceID AudioManager::sAudioDevice = 0;
SDL_AudioSpec AudioManager::sAudioFormat;
SDL_AudioStream* AudioManager::sConversionStream;
bool AudioManager::sMuteStream = false;
bool AudioManager::sHasAudioDevice = true;
bool AudioManager::mIsClearingStream = false;
AudioManager::AudioManager()
{
@ -115,8 +117,6 @@ void AudioManager::init()
if (Settings::getInstance()->getInt("SoundVolumeVideos") < 0)
Settings::getInstance()->setInt("SoundVolumeVideos", 0);
// Use the default sample rate initially, this will possibly be changed by the
// video player when a video is started.
setupAudioStream(sRequestedAudioFormat.freq);
}
@ -163,10 +163,13 @@ void AudioManager::mixAudio(void* /*unused*/, Uint8* stream, int len)
soundIt++;
}
// Process video stream audio generated by VideoFFmpegComponent.
int streamLength = SDL_AudioStreamAvailable(sConversionStream);
int streamLength = 0;
if (streamLength == 0) {
// Process video stream audio generated by VideoFFmpegComponent.
if (!mIsClearingStream)
streamLength = SDL_AudioStreamAvailable(sConversionStream);
if (streamLength <= 0 || mIsClearingStream) {
// If nothing is playing, pause the device until there is more audio to output.
if (!stillPlaying)
SDL_PauseAudioDevice(sAudioDevice, 1);
@ -186,7 +189,7 @@ void AudioManager::mixAudio(void* /*unused*/, Uint8* stream, int len)
int processedLength = SDL_AudioStreamGet(sConversionStream,
static_cast<void*>(&converted.at(0)), chunkLength);
if (processedLength == -1) {
if (processedLength < 0) {
LOG(LogError) << "AudioManager::mixAudio(): Couldn't convert sound chunk:";
LOG(LogError) << SDL_GetError();
return;
@ -197,9 +200,17 @@ void AudioManager::mixAudio(void* /*unused*/, Uint8* stream, int len)
// "/ processedLength / streamLength: " << chunkLength << " / " <<
// " / " << processedLength << " / " << streamLength;
SDL_MixAudioFormat(stream, &converted.at(0), sAudioFormat.format, processedLength,
static_cast<int>(Settings::getInstance()->
getInt("SoundVolumeVideos") * 1.28f));
// This mute flag is used to make sure that the audio buffer already sent to the
// stream is not played when the video player has been stopped. Otherwise there would
// be a short time period when the audio would keep playing after the video was stopped
// and before the stream was cleared in clearStream().
if (sMuteStream) {
SDL_MixAudioFormat(stream, &converted.at(0), sAudioFormat.format, processedLength, 0);
}
else {
SDL_MixAudioFormat(stream, &converted.at(0), sAudioFormat.format, processedLength,
static_cast<int>(Settings::getInstance()->getInt("SoundVolumeVideos") * 1.28f));
}
// If nothing is playing, pause the device until there is more audio to output.
if (!stillPlaying && SDL_AudioStreamAvailable(sConversionStream) == 0)
@ -264,6 +275,9 @@ void AudioManager::setupAudioStream(int sampleRate)
void AudioManager::processStream(const void* samples, unsigned count)
{
if (mIsClearingStream)
return;
if (SDL_AudioStreamPut(sConversionStream, samples, count * sizeof(Uint8)) == -1) {
LOG(LogError) << "Failed to put samples in the conversion stream:";
LOG(LogError) << SDL_GetError();
@ -276,5 +290,24 @@ void AudioManager::processStream(const void* samples, unsigned count)
void AudioManager::clearStream()
{
SDL_AudioStreamClear(sConversionStream);
// The SDL_AudioStreamClear() function is unstable and causes random crashes, so
// we have to implement a workaround instead where SDL_AudioStreamGet() is used
// to empty the stream.
// SDL_AudioStreamClear(sConversionStream);
mIsClearingStream = true;
int streamSize;
int length = sAudioFormat.samples * 4;
while ((streamSize = SDL_AudioStreamAvailable(sConversionStream)) > 0) {
std::vector<Uint8> readBuffer(length);
int processedLength = SDL_AudioStreamGet(sConversionStream,
static_cast<void*>(&readBuffer.at(0)), length);
if (processedLength <= 0) {
break;
}
}
mIsClearingStream = false;
}

View file

@ -35,7 +35,10 @@ public:
void processStream(const void* samples, unsigned count);
void clearStream();
bool getHasAudioDevice() { return sHasAudioDevice; };
void muteStream() { sMuteStream = true; }
void unmuteStream() { sMuteStream = false; }
bool getHasAudioDevice() { return sHasAudioDevice; }
static SDL_AudioDeviceID sAudioDevice;
static SDL_AudioSpec sAudioFormat;
@ -48,7 +51,9 @@ private:
static SDL_AudioStream* sConversionStream;
static std::vector<std::shared_ptr<Sound>> sSoundVector;
static std::shared_ptr<AudioManager> sInstance;
static bool sMuteStream;
static bool sHasAudioDevice;
static bool mIsClearingStream;
};
#endif // ES_CORE_AUDIO_MANAGER_H