mirror of
https://github.com/RetroDECK/ES-DE.git
synced 2024-11-22 06:05:38 +00:00
Multiple thread safety improvements to AudioManager and VideoFFmpegComponent.
Also some general refactoring and re-enabling of some SDL_AudioStream functions.
This commit is contained in:
parent
b742951dc0
commit
6bc4a09c9b
|
@ -12,7 +12,6 @@
|
|||
#if defined(BUILD_VLC_PLAYER)
|
||||
#include "components/VideoVlcComponent.h"
|
||||
#endif
|
||||
#include "AudioManager.h"
|
||||
#include "Sound.h"
|
||||
#include "views/ViewController.h"
|
||||
|
||||
|
@ -59,7 +58,7 @@ bool MediaViewer::startMediaViewer(FileData* game)
|
|||
|
||||
void MediaViewer::stopMediaViewer()
|
||||
{
|
||||
NavigationSounds::getInstance()->playThemeNavigationSound(SCROLLSOUND);
|
||||
NavigationSounds::getInstance().playThemeNavigationSound(SCROLLSOUND);
|
||||
ViewController::get()->onStopVideo();
|
||||
|
||||
if (mVideo) {
|
||||
|
@ -197,7 +196,7 @@ void MediaViewer::findMedia()
|
|||
void MediaViewer::showNext()
|
||||
{
|
||||
if (mHasImages && mCurrentImageIndex != static_cast<int>(mImageFiles.size()) - 1)
|
||||
NavigationSounds::getInstance()->playThemeNavigationSound(SCROLLSOUND);
|
||||
NavigationSounds::getInstance().playThemeNavigationSound(SCROLLSOUND);
|
||||
|
||||
bool showedVideo = false;
|
||||
|
||||
|
@ -229,7 +228,7 @@ void MediaViewer::showNext()
|
|||
void MediaViewer::showPrevious()
|
||||
{
|
||||
if ((mHasVideo && mDisplayingImage) || (!mHasVideo && mCurrentImageIndex != 0))
|
||||
NavigationSounds::getInstance()->playThemeNavigationSound(SCROLLSOUND);
|
||||
NavigationSounds::getInstance().playThemeNavigationSound(SCROLLSOUND);
|
||||
|
||||
if (mCurrentImageIndex == 0 && !mHasVideo) {
|
||||
return;
|
||||
|
|
|
@ -310,7 +310,7 @@ GuiGamelistOptions::~GuiGamelistOptions()
|
|||
}
|
||||
|
||||
if (mSystem->getRootFolder()->getChildren().size() != 0 && mSystem->getName() != "recent")
|
||||
NavigationSounds::getInstance()->playThemeNavigationSound(SCROLLSOUND);
|
||||
NavigationSounds::getInstance().playThemeNavigationSound(SCROLLSOUND);
|
||||
}
|
||||
|
||||
void GuiGamelistOptions::openGamelistFilter()
|
||||
|
@ -356,7 +356,7 @@ void GuiGamelistOptions::startEditMode()
|
|||
}
|
||||
|
||||
if (mSystem->getRootFolder()->getChildren().size() == 0)
|
||||
NavigationSounds::getInstance()->playThemeNavigationSound(SCROLLSOUND);
|
||||
NavigationSounds::getInstance().playThemeNavigationSound(SCROLLSOUND);
|
||||
delete this;
|
||||
}
|
||||
|
||||
|
@ -364,7 +364,7 @@ void GuiGamelistOptions::exitEditMode()
|
|||
{
|
||||
CollectionSystemsManager::get()->exitEditMode();
|
||||
if (mSystem->getRootFolder()->getChildren().size() == 0)
|
||||
NavigationSounds::getInstance()->playThemeNavigationSound(SCROLLSOUND);
|
||||
NavigationSounds::getInstance().playThemeNavigationSound(SCROLLSOUND);
|
||||
delete this;
|
||||
}
|
||||
|
||||
|
|
|
@ -704,8 +704,7 @@ int main(int argc, char* argv[])
|
|||
MameNames::deinit();
|
||||
CollectionSystemsManager::deinit();
|
||||
SystemData::deleteSystems();
|
||||
NavigationSounds::getInstance()->deinit();
|
||||
Settings::deinit();
|
||||
NavigationSounds::getInstance().deinit();
|
||||
|
||||
#if defined(FREEIMAGE_LIB)
|
||||
// Call this ONLY when linking with FreeImage as a static library.
|
||||
|
|
|
@ -284,14 +284,14 @@ bool SystemView::input(InputConfig* config, Input input)
|
|||
if (config->isMappedTo("a", input)) {
|
||||
stopScrolling();
|
||||
ViewController::get()->goToGameList(getSelected());
|
||||
NavigationSounds::getInstance()->playThemeNavigationSound(SELECTSOUND);
|
||||
NavigationSounds::getInstance().playThemeNavigationSound(SELECTSOUND);
|
||||
return true;
|
||||
}
|
||||
if (Settings::getInstance()->getBool("RandomAddButton") &&
|
||||
(config->isMappedTo("leftthumbstickclick", input) ||
|
||||
config->isMappedTo("rightthumbstickclick", input))) {
|
||||
// Get a random system and jump to it.
|
||||
NavigationSounds::getInstance()->playThemeNavigationSound(SYSTEMBROWSESOUND);
|
||||
NavigationSounds::getInstance().playThemeNavigationSound(SYSTEMBROWSESOUND);
|
||||
setCursor(SystemData::getRandomSystem(getSelected()));
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -77,7 +77,7 @@ protected:
|
|||
void onCursorChanged(const CursorState& state) override;
|
||||
virtual void onScroll() override
|
||||
{
|
||||
NavigationSounds::getInstance()->playThemeNavigationSound(SYSTEMBROWSESOUND);
|
||||
NavigationSounds::getInstance().playThemeNavigationSound(SYSTEMBROWSESOUND);
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
|
||||
#include "views/ViewController.h"
|
||||
|
||||
#include "AudioManager.h"
|
||||
#include "FileFilterIndex.h"
|
||||
#include "InputManager.h"
|
||||
#include "Log.h"
|
||||
|
@ -413,7 +412,7 @@ void ViewController::goToNextGameList()
|
|||
assert(mState.viewing == GAME_LIST);
|
||||
SystemData* system = getState().getSystem();
|
||||
assert(system);
|
||||
NavigationSounds::getInstance()->playThemeNavigationSound(QUICKSYSSELECTSOUND);
|
||||
NavigationSounds::getInstance().playThemeNavigationSound(QUICKSYSSELECTSOUND);
|
||||
mNextSystem = true;
|
||||
goToGameList(system->getNext());
|
||||
}
|
||||
|
@ -423,7 +422,7 @@ void ViewController::goToPrevGameList()
|
|||
assert(mState.viewing == GAME_LIST);
|
||||
SystemData* system = getState().getSystem();
|
||||
assert(system);
|
||||
NavigationSounds::getInstance()->playThemeNavigationSound(QUICKSYSSELECTSOUND);
|
||||
NavigationSounds::getInstance().playThemeNavigationSound(QUICKSYSSELECTSOUND);
|
||||
mNextSystem = false;
|
||||
goToGameList(system->getPrev());
|
||||
}
|
||||
|
@ -717,7 +716,7 @@ void ViewController::launch(FileData* game)
|
|||
if (durationString != "disabled")
|
||||
mWindow->displayLaunchScreen(game->getSourceFileData());
|
||||
|
||||
NavigationSounds::getInstance()->playThemeNavigationSound(LAUNCHSOUND);
|
||||
NavigationSounds::getInstance().playThemeNavigationSound(LAUNCHSOUND);
|
||||
|
||||
// This is just a dummy animation in order for the launch screen or notification popup
|
||||
// to be displayed briefly, and for the navigation sound playing to be able to complete.
|
||||
|
@ -976,13 +975,13 @@ void ViewController::preload()
|
|||
bool themeSoundSupport = false;
|
||||
for (SystemData* system : SystemData::sSystemVector) {
|
||||
if (system->getTheme()->hasView("all")) {
|
||||
NavigationSounds::getInstance()->loadThemeNavigationSounds(system->getTheme());
|
||||
NavigationSounds::getInstance().loadThemeNavigationSounds(system->getTheme().get());
|
||||
themeSoundSupport = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!SystemData::sSystemVector.empty() && !themeSoundSupport)
|
||||
NavigationSounds::getInstance()->loadThemeNavigationSounds(nullptr);
|
||||
NavigationSounds::getInstance().loadThemeNavigationSounds(nullptr);
|
||||
}
|
||||
|
||||
void ViewController::reloadGameListView(IGameListView* view, bool reloadTheme)
|
||||
|
@ -1070,17 +1069,17 @@ void ViewController::reloadAll()
|
|||
|
||||
// Load navigation sounds, either from the theme if it supports it, or otherwise from
|
||||
// the bundled fallback sound files.
|
||||
NavigationSounds::getInstance()->deinit();
|
||||
NavigationSounds::getInstance().deinit();
|
||||
bool themeSoundSupport = false;
|
||||
for (SystemData* system : SystemData::sSystemVector) {
|
||||
if (system->getTheme()->hasView("all")) {
|
||||
NavigationSounds::getInstance()->loadThemeNavigationSounds(system->getTheme());
|
||||
NavigationSounds::getInstance().loadThemeNavigationSounds(system->getTheme().get());
|
||||
themeSoundSupport = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!SystemData::sSystemVector.empty() && !themeSoundSupport)
|
||||
NavigationSounds::getInstance()->loadThemeNavigationSounds(nullptr);
|
||||
NavigationSounds::getInstance().loadThemeNavigationSounds(nullptr);
|
||||
|
||||
mCurrentView->onShow();
|
||||
updateHelpPrompts();
|
||||
|
|
|
@ -168,15 +168,15 @@ bool GridGameListView::input(InputConfig* config, Input input)
|
|||
if (input.value == 0 &&
|
||||
(config->isMappedLike("left", input) || config->isMappedLike("right", input) ||
|
||||
(config->isMappedLike("up", input)) || (config->isMappedLike("down", input))))
|
||||
NavigationSounds::getInstance()->playThemeNavigationSound(SCROLLSOUND);
|
||||
NavigationSounds::getInstance().playThemeNavigationSound(SCROLLSOUND);
|
||||
|
||||
if (input.value != 0 && config->isMappedLike("righttrigger", input)) {
|
||||
NavigationSounds::getInstance()->playThemeNavigationSound(SCROLLSOUND);
|
||||
NavigationSounds::getInstance().playThemeNavigationSound(SCROLLSOUND);
|
||||
mGrid.setCursor(mGrid.getLast());
|
||||
}
|
||||
|
||||
if (input.value != 0 && config->isMappedLike("lefttrigger", input)) {
|
||||
NavigationSounds::getInstance()->playThemeNavigationSound(SCROLLSOUND);
|
||||
NavigationSounds::getInstance().playThemeNavigationSound(SCROLLSOUND);
|
||||
mGrid.setCursor(mGrid.getFirst());
|
||||
}
|
||||
|
||||
|
|
|
@ -114,7 +114,7 @@ bool ISimpleGameListView::input(InputConfig* config, Input input)
|
|||
// It's a folder.
|
||||
if (cursor->getChildren().size() > 0) {
|
||||
ViewController::get()->cancelViewTransitions();
|
||||
NavigationSounds::getInstance()->playThemeNavigationSound(SELECTSOUND);
|
||||
NavigationSounds::getInstance().playThemeNavigationSound(SELECTSOUND);
|
||||
mCursorStack.push(cursor);
|
||||
populateList(cursor->getChildrenListToDisplay(), cursor);
|
||||
|
||||
|
@ -140,7 +140,7 @@ bool ISimpleGameListView::input(InputConfig* config, Input input)
|
|||
updateHelpPrompts();
|
||||
}
|
||||
else {
|
||||
NavigationSounds::getInstance()->playThemeNavigationSound(SCROLLSOUND);
|
||||
NavigationSounds::getInstance().playThemeNavigationSound(SCROLLSOUND);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -151,7 +151,7 @@ bool ISimpleGameListView::input(InputConfig* config, Input input)
|
|||
if (mCursorStack.size()) {
|
||||
// Save the position to the cursor stack history.
|
||||
mCursorStackHistory.push_back(getCursor());
|
||||
NavigationSounds::getInstance()->playThemeNavigationSound(BACKSOUND);
|
||||
NavigationSounds::getInstance().playThemeNavigationSound(BACKSOUND);
|
||||
populateList(mCursorStack.top()->getParent()->getChildrenListToDisplay(),
|
||||
mCursorStack.top()->getParent());
|
||||
setCursor(mCursorStack.top());
|
||||
|
@ -161,7 +161,7 @@ bool ISimpleGameListView::input(InputConfig* config, Input input)
|
|||
updateHelpPrompts();
|
||||
}
|
||||
else {
|
||||
NavigationSounds::getInstance()->playThemeNavigationSound(BACKSOUND);
|
||||
NavigationSounds::getInstance().playThemeNavigationSound(BACKSOUND);
|
||||
onPauseVideo();
|
||||
onFocusLost();
|
||||
stopListScrolling();
|
||||
|
@ -178,14 +178,14 @@ bool ISimpleGameListView::input(InputConfig* config, Input input)
|
|||
}
|
||||
else if (config->isMappedTo("x", input)) {
|
||||
if (getCursor()->getType() == PLACEHOLDER) {
|
||||
NavigationSounds::getInstance()->playThemeNavigationSound(SCROLLSOUND);
|
||||
NavigationSounds::getInstance().playThemeNavigationSound(SCROLLSOUND);
|
||||
return true;
|
||||
}
|
||||
else if (config->isMappedTo("x", input) &&
|
||||
mRoot->getSystem()->getThemeFolder() == "custom-collections" &&
|
||||
mCursorStack.empty() &&
|
||||
ViewController::get()->getState().viewing == ViewController::GAME_LIST) {
|
||||
NavigationSounds::getInstance()->playThemeNavigationSound(SCROLLSOUND);
|
||||
NavigationSounds::getInstance().playThemeNavigationSound(SCROLLSOUND);
|
||||
// Jump to the randomly selected game.
|
||||
if (mRandomGame) {
|
||||
stopListScrolling();
|
||||
|
@ -197,7 +197,7 @@ bool ISimpleGameListView::input(InputConfig* config, Input input)
|
|||
else if (mRoot->getSystem()->isGameSystem()) {
|
||||
stopListScrolling();
|
||||
ViewController::get()->cancelViewTransitions();
|
||||
NavigationSounds::getInstance()->playThemeNavigationSound(SCROLLSOUND);
|
||||
NavigationSounds::getInstance().playThemeNavigationSound(SCROLLSOUND);
|
||||
mWindow->startMediaViewer(getCursor());
|
||||
return true;
|
||||
}
|
||||
|
@ -228,7 +228,7 @@ bool ISimpleGameListView::input(InputConfig* config, Input input)
|
|||
if (mRoot->getSystem()->isGameSystem() && getCursor()->getType() != PLACEHOLDER) {
|
||||
stopListScrolling();
|
||||
// Jump to a random game.
|
||||
NavigationSounds::getInstance()->playThemeNavigationSound(SCROLLSOUND);
|
||||
NavigationSounds::getInstance().playThemeNavigationSound(SCROLLSOUND);
|
||||
FileData* randomGame = getCursor()->getSystem()->getRandomGame(getCursor());
|
||||
if (randomGame)
|
||||
setCursor(randomGame);
|
||||
|
@ -241,7 +241,7 @@ bool ISimpleGameListView::input(InputConfig* config, Input input)
|
|||
ViewController::get()->getState().viewing == ViewController::GAME_LIST) {
|
||||
// Jump to the randomly selected game.
|
||||
if (mRandomGame) {
|
||||
NavigationSounds::getInstance()->playThemeNavigationSound(SELECTSOUND);
|
||||
NavigationSounds::getInstance().playThemeNavigationSound(SELECTSOUND);
|
||||
// If there is already an mCursorStackHistory entry for the collection, then
|
||||
// remove it so we don't get multiple entries.
|
||||
std::vector<FileData*> listEntries =
|
||||
|
@ -257,7 +257,7 @@ bool ISimpleGameListView::input(InputConfig* config, Input input)
|
|||
updateHelpPrompts();
|
||||
}
|
||||
else {
|
||||
NavigationSounds::getInstance()->playThemeNavigationSound(SCROLLSOUND);
|
||||
NavigationSounds::getInstance().playThemeNavigationSound(SCROLLSOUND);
|
||||
}
|
||||
}
|
||||
else if (config->isMappedTo("y", input) &&
|
||||
|
@ -272,19 +272,19 @@ bool ISimpleGameListView::input(InputConfig* config, Input input)
|
|||
if (CollectionSystemsManager::get()->isEditing() &&
|
||||
mRoot->getSystem()->isGameSystem() && getCursor()->getType() != PLACEHOLDER &&
|
||||
getCursor()->getParent()->getPath() == "collections") {
|
||||
NavigationSounds::getInstance()->playThemeNavigationSound(FAVORITESOUND);
|
||||
NavigationSounds::getInstance().playThemeNavigationSound(FAVORITESOUND);
|
||||
mWindow->queueInfoPopup("CAN'T ADD CUSTOM COLLECTIONS TO CUSTOM COLLECTIONS", 4000);
|
||||
}
|
||||
// Notify the user if attempting to add a placeholder to a custom collection.
|
||||
if (CollectionSystemsManager::get()->isEditing() &&
|
||||
mRoot->getSystem()->isGameSystem() && getCursor()->getType() == PLACEHOLDER) {
|
||||
NavigationSounds::getInstance()->playThemeNavigationSound(FAVORITESOUND);
|
||||
NavigationSounds::getInstance().playThemeNavigationSound(FAVORITESOUND);
|
||||
mWindow->queueInfoPopup("CAN'T ADD PLACEHOLDERS TO CUSTOM COLLECTIONS", 4000);
|
||||
}
|
||||
else if (mRoot->getSystem()->isGameSystem() && getCursor()->getType() != PLACEHOLDER &&
|
||||
getCursor()->getParent()->getPath() != "collections") {
|
||||
if (getCursor()->getType() == GAME || getCursor()->getType() == FOLDER)
|
||||
NavigationSounds::getInstance()->playThemeNavigationSound(FAVORITESOUND);
|
||||
NavigationSounds::getInstance().playThemeNavigationSound(FAVORITESOUND);
|
||||
// When marking or unmarking a game as favorite, don't jump to the new position
|
||||
// it gets after the gamelist sorting. Instead retain the cursor position in the
|
||||
// list using the logic below.
|
||||
|
@ -474,7 +474,7 @@ bool ISimpleGameListView::input(InputConfig* config, Input input)
|
|||
}
|
||||
}
|
||||
else if (config->isMappedTo("y", input) && getCursor()->isPlaceHolder()) {
|
||||
NavigationSounds::getInstance()->playThemeNavigationSound(SCROLLSOUND);
|
||||
NavigationSounds::getInstance().playThemeNavigationSound(SCROLLSOUND);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,16 +14,6 @@
|
|||
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
std::shared_ptr<AudioManager> AudioManager::sInstance;
|
||||
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()
|
||||
{
|
||||
// Init on construction.
|
||||
|
@ -36,13 +26,10 @@ AudioManager::~AudioManager()
|
|||
deinit();
|
||||
}
|
||||
|
||||
std::shared_ptr<AudioManager>& AudioManager::getInstance()
|
||||
AudioManager& AudioManager::getInstance()
|
||||
{
|
||||
// Check if an AudioManager instance is already created, and if not then create it.
|
||||
if (sInstance == nullptr)
|
||||
sInstance = std::shared_ptr<AudioManager>(new AudioManager);
|
||||
|
||||
return sInstance;
|
||||
static AudioManager instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void AudioManager::init()
|
||||
|
@ -126,15 +113,12 @@ void AudioManager::init()
|
|||
|
||||
void AudioManager::deinit()
|
||||
{
|
||||
// Due to bugs in SDL, freeing the stream causes random crashes. This is reported to the
|
||||
// user on some operating systems such as macOS, and it's annoying to have a crash at the
|
||||
// end of debugging session. So we'll simply disable the function until it has been properly
|
||||
// fixed in the SDL library.
|
||||
// SDL_FreeAudioStream(sConversionStream);
|
||||
SDL_LockAudioDevice(sAudioDevice);
|
||||
SDL_FreeAudioStream(sConversionStream);
|
||||
SDL_UnlockAudioDevice(sAudioDevice);
|
||||
|
||||
SDL_CloseAudio();
|
||||
SDL_QuitSubSystem(SDL_INIT_AUDIO);
|
||||
sInstance = nullptr;
|
||||
}
|
||||
|
||||
void AudioManager::mixAudio(void* /*unused*/, Uint8* stream, int len)
|
||||
|
@ -172,13 +156,10 @@ void AudioManager::mixAudio(void* /*unused*/, Uint8* stream, int len)
|
|||
soundIt++;
|
||||
}
|
||||
|
||||
int streamLength = 0;
|
||||
|
||||
// Process video stream audio generated by VideoFFmpegComponent.
|
||||
if (!mIsClearingStream)
|
||||
streamLength = SDL_AudioStreamAvailable(sConversionStream);
|
||||
int streamLength = SDL_AudioStreamAvailable(sConversionStream);
|
||||
|
||||
if (streamLength <= 0 || mIsClearingStream) {
|
||||
if (streamLength <= 0) {
|
||||
// If nothing is playing, pause the device until there is more audio to output.
|
||||
if (!stillPlaying)
|
||||
SDL_PauseAudioDevice(sAudioDevice, 1);
|
||||
|
@ -213,7 +194,10 @@ void AudioManager::mixAudio(void* /*unused*/, Uint8* stream, int len)
|
|||
// 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) {
|
||||
std::unique_lock<std::mutex> audioLock{mAudioLock};
|
||||
bool muteStream = sMuteStream;
|
||||
audioLock.unlock();
|
||||
if (muteStream) {
|
||||
SDL_MixAudioFormat(stream, &converted.at(0), sAudioFormat.format, processedLength, 0);
|
||||
}
|
||||
else {
|
||||
|
@ -227,13 +211,13 @@ void AudioManager::mixAudio(void* /*unused*/, Uint8* stream, int len)
|
|||
SDL_PauseAudioDevice(sAudioDevice, 1);
|
||||
}
|
||||
|
||||
void AudioManager::registerSound(std::shared_ptr<Sound>& sound)
|
||||
void AudioManager::registerSound(std::shared_ptr<Sound> sound)
|
||||
{
|
||||
// Add sound to sound vector.
|
||||
sSoundVector.push_back(sound);
|
||||
}
|
||||
|
||||
void AudioManager::unregisterSound(std::shared_ptr<Sound>& sound)
|
||||
void AudioManager::unregisterSound(std::shared_ptr<Sound> sound)
|
||||
{
|
||||
for (unsigned int i = 0; i < sSoundVector.size(); i++) {
|
||||
if (sSoundVector.at(i) == sound) {
|
||||
|
@ -286,32 +270,36 @@ void AudioManager::setupAudioStream(int sampleRate)
|
|||
|
||||
void AudioManager::processStream(const void* samples, unsigned count)
|
||||
{
|
||||
if (mIsClearingStream)
|
||||
return;
|
||||
SDL_LockAudioDevice(sAudioDevice);
|
||||
|
||||
if (SDL_AudioStreamPut(sConversionStream, samples, count * sizeof(Uint8)) == -1) {
|
||||
LOG(LogError) << "Failed to put samples in the conversion stream:";
|
||||
LOG(LogError) << SDL_GetError();
|
||||
SDL_UnlockAudioDevice(sAudioDevice);
|
||||
return;
|
||||
}
|
||||
|
||||
if (count > 0)
|
||||
SDL_PauseAudioDevice(sAudioDevice, 0);
|
||||
|
||||
SDL_UnlockAudioDevice(sAudioDevice);
|
||||
}
|
||||
|
||||
void AudioManager::clearStream()
|
||||
{
|
||||
// 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);
|
||||
SDL_LockAudioDevice(sAudioDevice);
|
||||
SDL_AudioStreamClear(sConversionStream);
|
||||
SDL_UnlockAudioDevice(sAudioDevice);
|
||||
|
||||
// TEMPORARY: For evaluating if SDL_AudioStreamClear is stable.
|
||||
return;
|
||||
|
||||
// If sSoundVector is empty it means we are shutting down. In this case don't attempt
|
||||
// to clear the stream as this could lead to a crash.
|
||||
if (sSoundVector.empty())
|
||||
return;
|
||||
|
||||
mIsClearingStream = true;
|
||||
SDL_LockAudioDevice(sAudioDevice);
|
||||
|
||||
// This code is required as there's seemingly a bug in SDL_AudioStreamAvailable().
|
||||
// The function sometimes returns 0 even if there is data left in the buffer, possibly
|
||||
|
@ -323,13 +311,13 @@ void AudioManager::clearStream()
|
|||
// Fortunately the SDL_AudioStreamGet() function acts correctly on any arbitrary sample size
|
||||
// so we can actually clear the entire buffer. If this workaround was not implemented, there
|
||||
// would be a sound glitch when some samples from the previous video would play any time a
|
||||
// new video was started (assuming the issue was triggered be some remaining buffer data).
|
||||
// new video was started (assuming the issue was triggered by some remaining stream data).
|
||||
std::vector<Uint8> writeBuffer(10000);
|
||||
if (SDL_AudioStreamPut(sConversionStream, reinterpret_cast<const void*>(&writeBuffer.at(0)),
|
||||
10000) == -1) {
|
||||
LOG(LogError) << "Failed to put samples in the conversion stream:";
|
||||
LOG(LogError) << SDL_GetError();
|
||||
mIsClearingStream = false;
|
||||
SDL_UnlockAudioDevice(sAudioDevice);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -338,5 +326,5 @@ void AudioManager::clearStream()
|
|||
std::vector<Uint8> readBuffer(length);
|
||||
SDL_AudioStreamGet(sConversionStream, static_cast<void*>(&readBuffer.at(0)), length);
|
||||
|
||||
mIsClearingStream = false;
|
||||
SDL_UnlockAudioDevice(sAudioDevice);
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
#include <SDL2/SDL_audio.h>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
class Sound;
|
||||
|
@ -19,13 +20,13 @@ class AudioManager
|
|||
{
|
||||
public:
|
||||
virtual ~AudioManager();
|
||||
static std::shared_ptr<AudioManager>& getInstance();
|
||||
static AudioManager& getInstance();
|
||||
|
||||
void init();
|
||||
void deinit();
|
||||
|
||||
void registerSound(std::shared_ptr<Sound>& sound);
|
||||
void unregisterSound(std::shared_ptr<Sound>& sound);
|
||||
void registerSound(std::shared_ptr<Sound> sound);
|
||||
void unregisterSound(std::shared_ptr<Sound> sound);
|
||||
|
||||
void play();
|
||||
void stop();
|
||||
|
@ -35,25 +36,33 @@ public:
|
|||
void processStream(const void* samples, unsigned count);
|
||||
void clearStream();
|
||||
|
||||
void muteStream() { sMuteStream = true; }
|
||||
void unmuteStream() { sMuteStream = false; }
|
||||
void muteStream()
|
||||
{
|
||||
std::unique_lock<std::mutex> audioLock{mAudioLock};
|
||||
sMuteStream = true;
|
||||
}
|
||||
void unmuteStream()
|
||||
{
|
||||
std::unique_lock<std::mutex> audioLock{mAudioLock};
|
||||
sMuteStream = false;
|
||||
}
|
||||
|
||||
bool getHasAudioDevice() { return sHasAudioDevice; }
|
||||
|
||||
static SDL_AudioDeviceID sAudioDevice;
|
||||
static SDL_AudioSpec sAudioFormat;
|
||||
inline static SDL_AudioDeviceID sAudioDevice = 0;
|
||||
inline static SDL_AudioSpec sAudioFormat;
|
||||
|
||||
private:
|
||||
AudioManager();
|
||||
|
||||
static void mixAudio(void* unused, Uint8* stream, int len);
|
||||
static void mixAudio2(void* unused, Uint8* stream, int len);
|
||||
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;
|
||||
|
||||
inline static std::mutex mAudioLock;
|
||||
inline static SDL_AudioStream* sConversionStream;
|
||||
inline static std::vector<std::shared_ptr<Sound>> sSoundVector;
|
||||
inline static bool sMuteStream = false;
|
||||
inline static bool sHasAudioDevice = true;
|
||||
};
|
||||
|
||||
#endif // ES_CORE_AUDIO_MANAGER_H
|
||||
|
|
|
@ -15,8 +15,6 @@
|
|||
#include "ThemeData.h"
|
||||
#include "resources/ResourceManager.h"
|
||||
|
||||
NavigationSounds* NavigationSounds::sInstance = nullptr;
|
||||
|
||||
std::map<std::string, std::shared_ptr<Sound>> Sound::sMap;
|
||||
|
||||
std::shared_ptr<Sound> Sound::get(const std::string& path)
|
||||
|
@ -26,16 +24,16 @@ std::shared_ptr<Sound> Sound::get(const std::string& path)
|
|||
return it->second;
|
||||
|
||||
std::shared_ptr<Sound> sound = std::shared_ptr<Sound>(new Sound(path));
|
||||
AudioManager::getInstance()->registerSound(sound);
|
||||
AudioManager::getInstance().registerSound(sound);
|
||||
sMap[path] = sound;
|
||||
return sound;
|
||||
}
|
||||
|
||||
std::shared_ptr<Sound> Sound::getFromTheme(const std::shared_ptr<ThemeData>& theme,
|
||||
std::shared_ptr<Sound> Sound::getFromTheme(ThemeData* const theme,
|
||||
const std::string& view,
|
||||
const std::string& element)
|
||||
{
|
||||
if (!theme) {
|
||||
if (theme == nullptr) {
|
||||
LOG(LogDebug) << "Sound::getFromTheme(): Using fallback sound file for \"" << element
|
||||
<< "\"";
|
||||
return get(ResourceManager::getInstance()->getResourcePath(":/sounds/" + element + ".wav"));
|
||||
|
@ -57,7 +55,7 @@ Sound::Sound(const std::string& path)
|
|||
: mSampleData(nullptr)
|
||||
, mSamplePos(0)
|
||||
, mSampleLength(0)
|
||||
, playing(false)
|
||||
, mPlaying(false)
|
||||
{
|
||||
loadFile(path);
|
||||
}
|
||||
|
@ -127,7 +125,7 @@ void Sound::init()
|
|||
|
||||
void Sound::deinit()
|
||||
{
|
||||
playing = false;
|
||||
mPlaying = false;
|
||||
|
||||
if (mSampleData != nullptr) {
|
||||
SDL_LockAudioDevice(AudioManager::sAudioDevice);
|
||||
|
@ -148,28 +146,28 @@ void Sound::play()
|
|||
if (!Settings::getInstance()->getBool("NavigationSounds"))
|
||||
return;
|
||||
|
||||
if (!AudioManager::getInstance()->getHasAudioDevice())
|
||||
if (!AudioManager::getInstance().getHasAudioDevice())
|
||||
return;
|
||||
|
||||
SDL_LockAudioDevice(AudioManager::sAudioDevice);
|
||||
|
||||
if (playing)
|
||||
if (mPlaying)
|
||||
// Replay from start. rewind the sample to the beginning.
|
||||
mSamplePos = 0;
|
||||
else
|
||||
// Flag our sample as playing.
|
||||
playing = true;
|
||||
mPlaying = true;
|
||||
|
||||
SDL_UnlockAudioDevice(AudioManager::sAudioDevice);
|
||||
// Tell the AudioManager to start playing samples.
|
||||
AudioManager::getInstance()->play();
|
||||
AudioManager::getInstance().play();
|
||||
}
|
||||
|
||||
void Sound::stop()
|
||||
{
|
||||
// Flag our sample as not playing and rewind its position.
|
||||
SDL_LockAudioDevice(AudioManager::sAudioDevice);
|
||||
playing = false;
|
||||
mPlaying = false;
|
||||
mSamplePos = 0;
|
||||
SDL_UnlockAudioDevice(AudioManager::sAudioDevice);
|
||||
}
|
||||
|
@ -179,34 +177,27 @@ void Sound::setPosition(Uint32 newPosition)
|
|||
mSamplePos = newPosition;
|
||||
if (mSamplePos >= mSampleLength) {
|
||||
// Got to or beyond the end of the sample. stop playing.
|
||||
playing = false;
|
||||
mPlaying = false;
|
||||
mSamplePos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
NavigationSounds* NavigationSounds::getInstance()
|
||||
NavigationSounds& NavigationSounds::getInstance()
|
||||
{
|
||||
if (sInstance == nullptr)
|
||||
sInstance = new NavigationSounds();
|
||||
|
||||
return sInstance;
|
||||
static NavigationSounds instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void NavigationSounds::deinit()
|
||||
{
|
||||
if (sInstance) {
|
||||
for (auto sound : navigationSounds) {
|
||||
AudioManager::getInstance()->unregisterSound(sound);
|
||||
for (auto sound : mNavigationSounds) {
|
||||
AudioManager::getInstance().unregisterSound(sound);
|
||||
sound->deinit();
|
||||
}
|
||||
navigationSounds.clear();
|
||||
delete sInstance;
|
||||
mNavigationSounds.clear();
|
||||
}
|
||||
|
||||
sInstance = nullptr;
|
||||
}
|
||||
|
||||
void NavigationSounds::loadThemeNavigationSounds(const std::shared_ptr<ThemeData>& theme)
|
||||
void NavigationSounds::loadThemeNavigationSounds(ThemeData* const theme)
|
||||
{
|
||||
if (theme) {
|
||||
LOG(LogDebug) << "NavigationSounds::loadThemeNavigationSounds(): "
|
||||
|
@ -218,21 +209,21 @@ void NavigationSounds::loadThemeNavigationSounds(const std::shared_ptr<ThemeData
|
|||
"Theme set does not include navigation sound support, using fallback sounds";
|
||||
}
|
||||
|
||||
navigationSounds.push_back(Sound::getFromTheme(theme, "all", "systembrowse"));
|
||||
navigationSounds.push_back(Sound::getFromTheme(theme, "all", "quicksysselect"));
|
||||
navigationSounds.push_back(Sound::getFromTheme(theme, "all", "select"));
|
||||
navigationSounds.push_back(Sound::getFromTheme(theme, "all", "back"));
|
||||
navigationSounds.push_back(Sound::getFromTheme(theme, "all", "scroll"));
|
||||
navigationSounds.push_back(Sound::getFromTheme(theme, "all", "favorite"));
|
||||
navigationSounds.push_back(Sound::getFromTheme(theme, "all", "launch"));
|
||||
mNavigationSounds.push_back(Sound::getFromTheme(theme, "all", "systembrowse"));
|
||||
mNavigationSounds.push_back(Sound::getFromTheme(theme, "all", "quicksysselect"));
|
||||
mNavigationSounds.push_back(Sound::getFromTheme(theme, "all", "select"));
|
||||
mNavigationSounds.push_back(Sound::getFromTheme(theme, "all", "back"));
|
||||
mNavigationSounds.push_back(Sound::getFromTheme(theme, "all", "scroll"));
|
||||
mNavigationSounds.push_back(Sound::getFromTheme(theme, "all", "favorite"));
|
||||
mNavigationSounds.push_back(Sound::getFromTheme(theme, "all", "launch"));
|
||||
}
|
||||
|
||||
void NavigationSounds::playThemeNavigationSound(NavigationSoundsID soundID)
|
||||
{
|
||||
NavigationSounds::getInstance()->navigationSounds[soundID]->play();
|
||||
NavigationSounds::getInstance().mNavigationSounds[soundID]->play();
|
||||
}
|
||||
|
||||
bool NavigationSounds::isPlayingThemeNavigationSound(NavigationSoundsID soundID)
|
||||
{
|
||||
return NavigationSounds::getInstance()->navigationSounds[soundID]->isPlaying();
|
||||
return NavigationSounds::getInstance().mNavigationSounds[soundID]->isPlaying();
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ public:
|
|||
void loadFile(const std::string& path);
|
||||
|
||||
void play();
|
||||
bool isPlaying() const { return playing; }
|
||||
bool isPlaying() const { return mPlaying; }
|
||||
void stop();
|
||||
|
||||
const Uint8* getData() const { return mSampleData; }
|
||||
|
@ -39,7 +39,7 @@ public:
|
|||
Uint32 getLength() const { return mSampleLength; }
|
||||
|
||||
static std::shared_ptr<Sound> get(const std::string& path);
|
||||
static std::shared_ptr<Sound> getFromTheme(const std::shared_ptr<ThemeData>& theme,
|
||||
static std::shared_ptr<Sound> getFromTheme(ThemeData* const theme,
|
||||
const std::string& view,
|
||||
const std::string& elem);
|
||||
|
||||
|
@ -52,7 +52,7 @@ private:
|
|||
Uint8* mSampleData;
|
||||
Uint32 mSamplePos;
|
||||
Uint32 mSampleLength;
|
||||
bool playing;
|
||||
bool mPlaying;
|
||||
};
|
||||
|
||||
enum NavigationSoundsID {
|
||||
|
@ -68,16 +68,15 @@ enum NavigationSoundsID {
|
|||
class NavigationSounds
|
||||
{
|
||||
public:
|
||||
static NavigationSounds* getInstance();
|
||||
static NavigationSounds& getInstance();
|
||||
|
||||
void deinit();
|
||||
void loadThemeNavigationSounds(const std::shared_ptr<ThemeData>& theme);
|
||||
void loadThemeNavigationSounds(ThemeData* const theme);
|
||||
void playThemeNavigationSound(NavigationSoundsID soundID);
|
||||
bool isPlayingThemeNavigationSound(NavigationSoundsID soundID);
|
||||
|
||||
private:
|
||||
static NavigationSounds* sInstance;
|
||||
std::vector<std::shared_ptr<Sound>> navigationSounds;
|
||||
std::vector<std::shared_ptr<Sound>> mNavigationSounds;
|
||||
};
|
||||
|
||||
#endif // ES_CORE_SOUND_H
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
#if defined(BUILD_VLC_PLAYER)
|
||||
#include "components/VideoVlcComponent.h"
|
||||
#endif
|
||||
#include "AudioManager.h"
|
||||
#include "InputManager.h"
|
||||
#include "Log.h"
|
||||
#include "Sound.h"
|
||||
|
@ -219,7 +218,7 @@ void Window::input(InputConfig* config, Input input)
|
|||
else if (config->isMappedTo("y", input) && input.value != 0) {
|
||||
// Jump to the game in its gamelist, but do not launch it.
|
||||
stopScreensaver();
|
||||
NavigationSounds::getInstance()->playThemeNavigationSound(SCROLLSOUND);
|
||||
NavigationSounds::getInstance().playThemeNavigationSound(SCROLLSOUND);
|
||||
mScreensaver->goToGame();
|
||||
// To force handling the wake up process.
|
||||
mSleeping = true;
|
||||
|
|
|
@ -102,8 +102,8 @@ public:
|
|||
protected:
|
||||
virtual void onScroll() override
|
||||
{
|
||||
if (!NavigationSounds::getInstance()->isPlayingThemeNavigationSound(SCROLLSOUND))
|
||||
NavigationSounds::getInstance()->playThemeNavigationSound(SCROLLSOUND);
|
||||
if (!NavigationSounds::getInstance().isPlayingThemeNavigationSound(SCROLLSOUND))
|
||||
NavigationSounds::getInstance().playThemeNavigationSound(SCROLLSOUND);
|
||||
}
|
||||
virtual void onCursorChanged(const CursorState& state) override;
|
||||
|
||||
|
|
|
@ -90,9 +90,12 @@ void VideoComponent::setImage(std::string path)
|
|||
|
||||
void VideoComponent::onShow()
|
||||
{
|
||||
std::unique_lock<std::mutex> playerLock(mPlayerMutex);
|
||||
mBlockPlayer = false;
|
||||
mPause = false;
|
||||
mShowing = true;
|
||||
playerLock.unlock();
|
||||
|
||||
manageState();
|
||||
}
|
||||
|
||||
|
@ -110,22 +113,31 @@ void VideoComponent::onStopVideo()
|
|||
|
||||
void VideoComponent::onPauseVideo()
|
||||
{
|
||||
std::unique_lock<std::mutex> playerLock(mPlayerMutex);
|
||||
mBlockPlayer = true;
|
||||
mPause = true;
|
||||
playerLock.unlock();
|
||||
|
||||
manageState();
|
||||
}
|
||||
|
||||
void VideoComponent::onUnpauseVideo()
|
||||
{
|
||||
std::unique_lock<std::mutex> playerLock(mPlayerMutex);
|
||||
mBlockPlayer = false;
|
||||
mPause = false;
|
||||
playerLock.unlock();
|
||||
|
||||
manageState();
|
||||
}
|
||||
|
||||
void VideoComponent::onScreensaverActivate()
|
||||
{
|
||||
std::unique_lock<std::mutex> playerLock(mPlayerMutex);
|
||||
mBlockPlayer = true;
|
||||
mPause = true;
|
||||
playerLock.unlock();
|
||||
|
||||
if (Settings::getInstance()->getString("ScreensaverType") == "dim")
|
||||
stopVideo();
|
||||
else
|
||||
|
@ -158,15 +170,20 @@ void VideoComponent::onGameLaunchedDeactivate()
|
|||
void VideoComponent::topWindow(bool isTop)
|
||||
{
|
||||
if (isTop) {
|
||||
std::unique_lock<std::mutex> playerLock(mPlayerMutex);
|
||||
mBlockPlayer = false;
|
||||
mPause = false;
|
||||
playerLock.unlock();
|
||||
|
||||
// Stop video when closing the menu to force a reload of the
|
||||
// static image (if the theme is configured as such).
|
||||
stopVideo();
|
||||
}
|
||||
else {
|
||||
std::unique_lock<std::mutex> playerLock(mPlayerMutex);
|
||||
mBlockPlayer = true;
|
||||
mPause = true;
|
||||
playerLock.unlock();
|
||||
}
|
||||
manageState();
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "GuiComponent.h"
|
||||
#include "components/ImageComponent.h"
|
||||
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
class MediaViewer;
|
||||
|
@ -109,6 +110,7 @@ private:
|
|||
protected:
|
||||
Window* mWindow;
|
||||
ImageComponent mStaticImage;
|
||||
std::mutex mPlayerMutex;
|
||||
|
||||
unsigned mVideoWidth;
|
||||
unsigned mVideoHeight;
|
||||
|
|
|
@ -130,7 +130,10 @@ void VideoFFmpegComponent::render(const glm::mat4& parentTrans)
|
|||
|
||||
if (mIsPlaying && mFormatContext) {
|
||||
unsigned int color;
|
||||
if (mDecodedFrame && mFadeIn < 1) {
|
||||
std::unique_lock<std::mutex> pictureLock(mPictureMutex);
|
||||
bool decodedFrame = mDecodedFrame;
|
||||
pictureLock.unlock();
|
||||
if (decodedFrame && mFadeIn < 1) {
|
||||
const unsigned int fadeIn = static_cast<int>(mFadeIn * 255.0f);
|
||||
color =
|
||||
Renderer::convertRGBAToABGR((fadeIn << 24) | (fadeIn << 16) | (fadeIn << 8) | 255);
|
||||
|
@ -159,11 +162,13 @@ void VideoFFmpegComponent::render(const glm::mat4& parentTrans)
|
|||
for (int i = 0; i < 4; i++)
|
||||
vertices[i].pos = glm::round(vertices[i].pos);
|
||||
|
||||
// This is needed to avoid a slight gap before the video starts playing.
|
||||
if (!mDecodedFrame)
|
||||
return;
|
||||
pictureLock.lock();
|
||||
|
||||
mPictureMutex.lock();
|
||||
// This is needed to avoid a slight gap before the video starts playing.
|
||||
if (!mDecodedFrame) {
|
||||
pictureLock.unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mOutputPicture.hasBeenRendered) {
|
||||
// Move the contents of mOutputPicture to a temporary vector in order to call
|
||||
|
@ -188,7 +193,7 @@ void VideoFFmpegComponent::render(const glm::mat4& parentTrans)
|
|||
mOutputPicture.hasBeenRendered = true;
|
||||
}
|
||||
|
||||
mPictureMutex.unlock();
|
||||
pictureLock.unlock();
|
||||
|
||||
if (pictureSize > 0) {
|
||||
// Build a texture for the video frame.
|
||||
|
@ -196,7 +201,7 @@ void VideoFFmpegComponent::render(const glm::mat4& parentTrans)
|
|||
}
|
||||
}
|
||||
else {
|
||||
mPictureMutex.unlock();
|
||||
pictureLock.unlock();
|
||||
}
|
||||
|
||||
mTexture->bind();
|
||||
|
@ -225,13 +230,12 @@ void VideoFFmpegComponent::updatePlayer()
|
|||
return;
|
||||
|
||||
// Output any audio that has been added by the processing thread.
|
||||
mAudioMutex.lock();
|
||||
std::unique_lock<std::mutex> audioLock(mAudioMutex);
|
||||
if (mOutputAudio.size()) {
|
||||
AudioManager::getInstance()->processStream(&mOutputAudio.at(0),
|
||||
AudioManager::getInstance().processStream(&mOutputAudio.at(0),
|
||||
static_cast<unsigned int>(mOutputAudio.size()));
|
||||
mOutputAudio.clear();
|
||||
}
|
||||
mAudioMutex.unlock();
|
||||
|
||||
if (mIsActuallyPlaying && mStartTimeAccumulation) {
|
||||
mAccumulatedTime +=
|
||||
|
@ -243,8 +247,10 @@ void VideoFFmpegComponent::updatePlayer()
|
|||
|
||||
mTimeReference = std::chrono::high_resolution_clock::now();
|
||||
|
||||
audioLock.unlock();
|
||||
|
||||
if (!mFrameProcessingThread) {
|
||||
AudioManager::getInstance()->unmuteStream();
|
||||
AudioManager::getInstance().unmuteStream();
|
||||
mFrameProcessingThread =
|
||||
std::make_unique<std::thread>(&VideoFFmpegComponent::frameProcessing, this);
|
||||
}
|
||||
|
@ -262,15 +268,19 @@ void VideoFFmpegComponent::frameProcessing()
|
|||
if (mAudioCodecContext)
|
||||
audioFilter = setupAudioFilters();
|
||||
|
||||
while (mIsPlaying && !mPause && videoFilter && (!mAudioCodecContext || audioFilter)) {
|
||||
bool isPlaying = true;
|
||||
bool pause = false;
|
||||
|
||||
while (isPlaying && !pause && videoFilter && (!mAudioCodecContext || audioFilter)) {
|
||||
readFrames();
|
||||
if (!mIsPlaying)
|
||||
break;
|
||||
getProcessedFrames();
|
||||
if (!mIsPlaying)
|
||||
break;
|
||||
outputFrames();
|
||||
|
||||
std::unique_lock<std::mutex> playerLock(mPlayerMutex);
|
||||
isPlaying = mIsPlaying;
|
||||
pause = mPause;
|
||||
playerLock.unlock();
|
||||
|
||||
// This 1 ms wait makes sure that the thread does not consume all available CPU cycles.
|
||||
SDL_Delay(1);
|
||||
}
|
||||
|
@ -428,7 +438,7 @@ bool VideoFFmpegComponent::setupAudioFilters()
|
|||
{
|
||||
int returnValue = 0;
|
||||
char errorMessage[512];
|
||||
const int outSampleRates[] = {AudioManager::getInstance()->sAudioFormat.freq, -1};
|
||||
const int outSampleRates[] = {AudioManager::getInstance().sAudioFormat.freq, -1};
|
||||
const enum AVSampleFormat outSampleFormats[] = {AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_NONE};
|
||||
|
||||
mAFilterInputs = avfilter_inout_alloc();
|
||||
|
@ -662,9 +672,11 @@ void VideoFFmpegComponent::readFrames()
|
|||
}
|
||||
}
|
||||
|
||||
if (readFrameReturn < 0)
|
||||
if (readFrameReturn < 0) {
|
||||
std::unique_lock<std::mutex> playerLock(mPlayerMutex);
|
||||
mEndOfVideo = true;
|
||||
}
|
||||
}
|
||||
|
||||
void VideoFFmpegComponent::getProcessedFrames()
|
||||
{
|
||||
|
@ -739,6 +751,7 @@ void VideoFFmpegComponent::outputFrames()
|
|||
// there is an audio track.
|
||||
if (!mAudioCodecContext || (mAudioCodecContext && !mAudioFrameQueue.empty())) {
|
||||
if (!mStartTimeAccumulation) {
|
||||
std::unique_lock<std::mutex> audioLock(mAudioMutex);
|
||||
mTimeReference = std::chrono::high_resolution_clock::now();
|
||||
mStartTimeAccumulation = true;
|
||||
mIsActuallyPlaying = true;
|
||||
|
@ -748,7 +761,11 @@ void VideoFFmpegComponent::outputFrames()
|
|||
// Process the audio frames that have a PTS value below mAccumulatedTime (plus a small
|
||||
// buffer to avoid underflows).
|
||||
while (!mAudioFrameQueue.empty()) {
|
||||
if (mAudioFrameQueue.front().pts < mAccumulatedTime + AUDIO_BUFFER) {
|
||||
std::unique_lock<std::mutex> audioLock(mAudioMutex);
|
||||
auto accumulatedTime = mAccumulatedTime;
|
||||
audioLock.unlock();
|
||||
|
||||
if (mAudioFrameQueue.front().pts < accumulatedTime + AUDIO_BUFFER) {
|
||||
// Enable only when needed, as this generates a lot of debug output.
|
||||
if (DEBUG_VIDEO) {
|
||||
LOG(LogDebug) << "Processing audio frame with PTS: "
|
||||
|
@ -770,13 +787,13 @@ void VideoFFmpegComponent::outputFrames()
|
|||
|
||||
if (outputSound) {
|
||||
// The audio is output to AudioManager from updatePlayer() in the main thread.
|
||||
mAudioMutex.lock();
|
||||
audioLock.lock();
|
||||
|
||||
mOutputAudio.insert(mOutputAudio.end(),
|
||||
mAudioFrameQueue.front().resampledData.begin(),
|
||||
mAudioFrameQueue.front().resampledData.end());
|
||||
|
||||
mAudioMutex.unlock();
|
||||
audioLock.unlock();
|
||||
}
|
||||
mAudioFrameQueue.pop();
|
||||
mAudioFrameCount++;
|
||||
|
@ -786,11 +803,19 @@ void VideoFFmpegComponent::outputFrames()
|
|||
}
|
||||
}
|
||||
|
||||
std::unique_lock<std::mutex> playerLock(mPlayerMutex);
|
||||
bool isActuallyPlaying = mIsActuallyPlaying;
|
||||
playerLock.unlock();
|
||||
|
||||
// Process all available video frames that have a PTS value below mAccumulatedTime.
|
||||
// But if more than one frame is processed here, it means that the computer can't
|
||||
// keep up for some reason.
|
||||
while (mIsActuallyPlaying && !mVideoFrameQueue.empty()) {
|
||||
if (mVideoFrameQueue.front().pts < mAccumulatedTime) {
|
||||
while (isActuallyPlaying && !mVideoFrameQueue.empty()) {
|
||||
std::unique_lock<std::mutex> audioLock(mAudioMutex);
|
||||
double accumulatedTime = mAccumulatedTime;
|
||||
audioLock.unlock();
|
||||
|
||||
if (mVideoFrameQueue.front().pts < accumulatedTime) {
|
||||
// Enable only when needed, as this generates a lot of debug output.
|
||||
if (DEBUG_VIDEO) {
|
||||
LOG(LogDebug) << "Processing video frame with PTS: "
|
||||
|
@ -808,7 +833,7 @@ void VideoFFmpegComponent::outputFrames()
|
|||
}
|
||||
}
|
||||
|
||||
mPictureMutex.lock();
|
||||
std::unique_lock<std::mutex> pictureLock(mPictureMutex);
|
||||
|
||||
// Give some leeway for frames that have not yet been rendered but that have pts
|
||||
// values with a time difference relative to the frame duration that is under a
|
||||
|
@ -818,10 +843,10 @@ void VideoFFmpegComponent::outputFrames()
|
|||
// can't keep up. This approach primarily decreases stuttering for videos with frame
|
||||
// rates close to, or at, the rendering frame rate, for example 59.94 and 60 FPS.
|
||||
if (mDecodedFrame && !mOutputPicture.hasBeenRendered) {
|
||||
double timeDifference = mAccumulatedTime - mVideoFrameQueue.front().pts -
|
||||
double timeDifference = accumulatedTime - mVideoFrameQueue.front().pts -
|
||||
mVideoFrameQueue.front().frameDuration * 2.0l;
|
||||
if (timeDifference < mVideoFrameQueue.front().frameDuration) {
|
||||
mPictureMutex.unlock();
|
||||
pictureLock.unlock();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -835,12 +860,12 @@ void VideoFFmpegComponent::outputFrames()
|
|||
mOutputPicture.height = mVideoFrameQueue.front().height;
|
||||
mOutputPicture.hasBeenRendered = false;
|
||||
|
||||
mPictureMutex.unlock();
|
||||
mDecodedFrame = true;
|
||||
|
||||
pictureLock.unlock();
|
||||
|
||||
mVideoFrameQueue.pop();
|
||||
mVideoFrameCount++;
|
||||
|
||||
mDecodedFrame = true;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
|
@ -1383,16 +1408,18 @@ void VideoFFmpegComponent::startVideo()
|
|||
|
||||
void VideoFFmpegComponent::stopVideo()
|
||||
{
|
||||
std::unique_lock<std::mutex> playerLock(mPlayerMutex);
|
||||
mIsPlaying = false;
|
||||
mIsActuallyPlaying = false;
|
||||
mStartDelayed = false;
|
||||
mPause = false;
|
||||
mEndOfVideo = false;
|
||||
playerLock.unlock();
|
||||
mTexture.reset();
|
||||
|
||||
if (mFrameProcessingThread) {
|
||||
if (mWindow->getVideoPlayerCount() == 0)
|
||||
AudioManager::getInstance()->muteStream();
|
||||
AudioManager::getInstance().muteStream();
|
||||
// Wait for the thread execution to complete.
|
||||
mFrameProcessingThread->join();
|
||||
mFrameProcessingThread.reset();
|
||||
|
@ -1404,7 +1431,7 @@ void VideoFFmpegComponent::stopVideo()
|
|||
std::queue<AudioFrame>().swap(mAudioFrameQueue);
|
||||
|
||||
// Clear the audio buffer.
|
||||
AudioManager::getInstance()->clearStream();
|
||||
AudioManager::getInstance().clearStream();
|
||||
|
||||
if (mFormatContext) {
|
||||
av_frame_free(&mVideoFrame);
|
||||
|
@ -1427,12 +1454,16 @@ void VideoFFmpegComponent::stopVideo()
|
|||
void VideoFFmpegComponent::pauseVideo()
|
||||
{
|
||||
if (mPause && mWindow->getVideoPlayerCount() == 0)
|
||||
AudioManager::getInstance()->muteStream();
|
||||
AudioManager::getInstance().muteStream();
|
||||
}
|
||||
|
||||
void VideoFFmpegComponent::handleLooping()
|
||||
{
|
||||
if (mIsPlaying && mEndOfVideo) {
|
||||
std::unique_lock<std::mutex> playerLock(mPlayerMutex);
|
||||
bool endOfVideo = mEndOfVideo;
|
||||
playerLock.unlock();
|
||||
|
||||
if (mIsPlaying && endOfVideo) {
|
||||
// If the screensaver video swap time is set to 0, it means we should
|
||||
// skip to the next game when the video has finished playing.
|
||||
if (mScreensaverMode &&
|
||||
|
|
Loading…
Reference in a new issue