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_AudioDeviceID AudioManager::sAudioDevice = 0;
SDL_AudioSpec AudioManager::sAudioFormat; SDL_AudioSpec AudioManager::sAudioFormat;
SDL_AudioStream* AudioManager::sConversionStream; SDL_AudioStream* AudioManager::sConversionStream;
bool AudioManager::sMuteStream = false;
bool AudioManager::sHasAudioDevice = true; bool AudioManager::sHasAudioDevice = true;
bool AudioManager::mIsClearingStream = false;
AudioManager::AudioManager() AudioManager::AudioManager()
{ {
@ -115,8 +117,6 @@ void AudioManager::init()
if (Settings::getInstance()->getInt("SoundVolumeVideos") < 0) if (Settings::getInstance()->getInt("SoundVolumeVideos") < 0)
Settings::getInstance()->setInt("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); setupAudioStream(sRequestedAudioFormat.freq);
} }
@ -163,10 +163,13 @@ void AudioManager::mixAudio(void* /*unused*/, Uint8* stream, int len)
soundIt++; soundIt++;
} }
// Process video stream audio generated by VideoFFmpegComponent. int streamLength = 0;
int streamLength = SDL_AudioStreamAvailable(sConversionStream);
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 nothing is playing, pause the device until there is more audio to output.
if (!stillPlaying) if (!stillPlaying)
SDL_PauseAudioDevice(sAudioDevice, 1); SDL_PauseAudioDevice(sAudioDevice, 1);
@ -186,7 +189,7 @@ void AudioManager::mixAudio(void* /*unused*/, Uint8* stream, int len)
int processedLength = SDL_AudioStreamGet(sConversionStream, int processedLength = SDL_AudioStreamGet(sConversionStream,
static_cast<void*>(&converted.at(0)), chunkLength); static_cast<void*>(&converted.at(0)), chunkLength);
if (processedLength == -1) { if (processedLength < 0) {
LOG(LogError) << "AudioManager::mixAudio(): Couldn't convert sound chunk:"; LOG(LogError) << "AudioManager::mixAudio(): Couldn't convert sound chunk:";
LOG(LogError) << SDL_GetError(); LOG(LogError) << SDL_GetError();
return; return;
@ -197,9 +200,17 @@ void AudioManager::mixAudio(void* /*unused*/, Uint8* stream, int len)
// "/ processedLength / streamLength: " << chunkLength << " / " << // "/ processedLength / streamLength: " << chunkLength << " / " <<
// " / " << processedLength << " / " << streamLength; // " / " << processedLength << " / " << streamLength;
SDL_MixAudioFormat(stream, &converted.at(0), sAudioFormat.format, processedLength, // This mute flag is used to make sure that the audio buffer already sent to the
static_cast<int>(Settings::getInstance()-> // stream is not played when the video player has been stopped. Otherwise there would
getInt("SoundVolumeVideos") * 1.28f)); // 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 nothing is playing, pause the device until there is more audio to output.
if (!stillPlaying && SDL_AudioStreamAvailable(sConversionStream) == 0) if (!stillPlaying && SDL_AudioStreamAvailable(sConversionStream) == 0)
@ -264,6 +275,9 @@ void AudioManager::setupAudioStream(int sampleRate)
void AudioManager::processStream(const void* samples, unsigned count) void AudioManager::processStream(const void* samples, unsigned count)
{ {
if (mIsClearingStream)
return;
if (SDL_AudioStreamPut(sConversionStream, samples, count * sizeof(Uint8)) == -1) { if (SDL_AudioStreamPut(sConversionStream, samples, count * sizeof(Uint8)) == -1) {
LOG(LogError) << "Failed to put samples in the conversion stream:"; LOG(LogError) << "Failed to put samples in the conversion stream:";
LOG(LogError) << SDL_GetError(); LOG(LogError) << SDL_GetError();
@ -276,5 +290,24 @@ void AudioManager::processStream(const void* samples, unsigned count)
void AudioManager::clearStream() 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 processStream(const void* samples, unsigned count);
void clearStream(); void clearStream();
bool getHasAudioDevice() { return sHasAudioDevice; }; void muteStream() { sMuteStream = true; }
void unmuteStream() { sMuteStream = false; }
bool getHasAudioDevice() { return sHasAudioDevice; }
static SDL_AudioDeviceID sAudioDevice; static SDL_AudioDeviceID sAudioDevice;
static SDL_AudioSpec sAudioFormat; static SDL_AudioSpec sAudioFormat;
@ -48,7 +51,9 @@ private:
static SDL_AudioStream* sConversionStream; static SDL_AudioStream* sConversionStream;
static std::vector<std::shared_ptr<Sound>> sSoundVector; static std::vector<std::shared_ptr<Sound>> sSoundVector;
static std::shared_ptr<AudioManager> sInstance; static std::shared_ptr<AudioManager> sInstance;
static bool sMuteStream;
static bool sHasAudioDevice; static bool sHasAudioDevice;
static bool mIsClearingStream;
}; };
#endif // ES_CORE_AUDIO_MANAGER_H #endif // ES_CORE_AUDIO_MANAGER_H