From c91662befabd35f448e799c047f202c337d13c32 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sat, 29 May 2021 10:52:40 +0200 Subject: [PATCH] Implemented a stable solution to video stream muting and clearing. --- es-core/src/AudioManager.cpp | 53 +++++++++++++++++++++++++++++------- es-core/src/AudioManager.h | 7 ++++- 2 files changed, 49 insertions(+), 11 deletions(-) diff --git a/es-core/src/AudioManager.cpp b/es-core/src/AudioManager.cpp index 54125f0e6..28073d9a1 100644 --- a/es-core/src/AudioManager.cpp +++ b/es-core/src/AudioManager.cpp @@ -19,7 +19,9 @@ std::vector> 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(&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(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(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 readBuffer(length); + int processedLength = SDL_AudioStreamGet(sConversionStream, + static_cast(&readBuffer.at(0)), length); + if (processedLength <= 0) { + break; + } + } + + mIsClearingStream = false; } diff --git a/es-core/src/AudioManager.h b/es-core/src/AudioManager.h index 5a39dc04d..e600220ed 100644 --- a/es-core/src/AudioManager.h +++ b/es-core/src/AudioManager.h @@ -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> sSoundVector; static std::shared_ptr sInstance; + static bool sMuteStream; static bool sHasAudioDevice; + static bool mIsClearingStream; }; #endif // ES_CORE_AUDIO_MANAGER_H