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