From 0b1d7839a34ce9422b41be98a7b8b91a88187b6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A5IGA?= <164882787+Xphalnos@users.noreply.github.com> Date: Thu, 15 Aug 2024 16:49:13 +0200 Subject: [PATCH 01/29] Qt-GUI: Cleaning the option menu (#443) --- src/qt_gui/settings_dialog.cpp | 39 +- src/qt_gui/settings_dialog.ui | 818 ++++++++++++++++----------------- 2 files changed, 411 insertions(+), 446 deletions(-) diff --git a/src/qt_gui/settings_dialog.cpp b/src/qt_gui/settings_dialog.cpp index fd2df0fc..ca47f331 100644 --- a/src/qt_gui/settings_dialog.cpp +++ b/src/qt_gui/settings_dialog.cpp @@ -39,13 +39,28 @@ SettingsDialog::SettingsDialog(std::span physical_devices, QWidge ui->buttonBox->button(QDialogButtonBox::StandardButton::Close)->setFocus(); }); - // EMULATOR TAB + // GENERAL TAB { + connect(ui->userNameLineEdit, &QLineEdit::textChanged, this, + [](const QString& text) { Config::setUserName(text.toStdString()); }); + connect(ui->consoleLanguageComboBox, &QComboBox::currentIndexChanged, this, [](int index) { Config::setLanguage(index); }); - connect(ui->userNameLineEdit, &QLineEdit::textChanged, this, - [](const QString& text) { Config::setUserName(text.toStdString()); }); + connect(ui->fullscreenCheckBox, &QCheckBox::stateChanged, this, + [](int val) { Config::setFullscreenMode(val); }); + + connect(ui->showSplashCheckBox, &QCheckBox::stateChanged, this, + [](int val) { Config::setShowSplash(val); }); + + connect(ui->ps4proCheckBox, &QCheckBox::stateChanged, this, + [](int val) { Config::setNeoMode(val); }); + + connect(ui->logTypeComboBox, &QComboBox::currentTextChanged, this, + [](const QString& text) { Config::setLogType(text.toStdString()); }); + + connect(ui->logFilterLineEdit, &QLineEdit::textChanged, this, + [](const QString& text) { Config::setLogFilter(text.toStdString()); }); } // GPU TAB @@ -74,24 +89,6 @@ SettingsDialog::SettingsDialog(std::span physical_devices, QWidge [](int val) { Config::setDumpPM4(val); }); } - // GENERAL TAB - { - connect(ui->fullscreenCheckBox, &QCheckBox::stateChanged, this, - [](int val) { Config::setFullscreenMode(val); }); - - connect(ui->showSplashCheckBox, &QCheckBox::stateChanged, this, - [](int val) { Config::setShowSplash(val); }); - - connect(ui->ps4proCheckBox, &QCheckBox::stateChanged, this, - [](int val) { Config::setNeoMode(val); }); - - connect(ui->logTypeComboBox, &QComboBox::currentTextChanged, this, - [](const QString& text) { Config::setLogType(text.toStdString()); }); - - connect(ui->logFilterLineEdit, &QLineEdit::textChanged, this, - [](const QString& text) { Config::setLogFilter(text.toStdString()); }); - } - // DEBUG TAB { connect(ui->debugDump, &QCheckBox::stateChanged, this, diff --git a/src/qt_gui/settings_dialog.ui b/src/qt_gui/settings_dialog.ui index 148799c5..3302f9e6 100644 --- a/src/qt_gui/settings_dialog.ui +++ b/src/qt_gui/settings_dialog.ui @@ -1,7 +1,6 @@ - SettingsDialog @@ -46,8 +45,8 @@ 0 0 - 1006 - 720 + 1002 + 710 @@ -59,273 +58,428 @@ 0 - + - Emulator + General - + - + - + - + - Console Language + Emulator Settings - + - + + + 6 + + + 0 + - - Japanese - - - - - English (United States) - - - - - French (France) - - - - - Spanish (Spain) - - - - - German - - - - - Italian - - - - - Dutch - - - - - Portuguese (Portugal) - - - - - Russian - - - - - Korean - - - - - Traditional Chinese - - - - - Simplified Chinese - - - - - Finnish - - - - - Swedish - - - - - Danish - - - - - Norwegian - - - - - Polish - - - - - Portuguese (Brazil) - - - - - English (United Kingdom) - - - - - Turkish - - - - - Spanish (Latin America) - - - - - Arabic - - - - - French (Canada) - - - - - Czech - - - - - Hungarian - - - - - Greek - - - - - Romanian - - - - - Thai - - - - - Vietnamese - - - - - Indonesian - + + + + + Username + + + + + + + + + + + + + + + Console Language + + + + + + + Japanese + + + + + English (United States) + + + + + French (France) + + + + + Spanish (Spain) + + + + + German + + + + + Italian + + + + + Dutch + + + + + Portuguese (Portugal) + + + + + Russian + + + + + Korean + + + + + Traditional Chinese + + + + + Simplified Chinese + + + + + Finnish + + + + + Swedish + + + + + Danish + + + + + Norwegian + + + + + Polish + + + + + Portuguese (Brazil) + + + + + English (United Kingdom) + + + + + Turkish + + + + + Spanish (Latin America) + + + + + Arabic + + + + + French (Canada) + + + + + Czech + + + + + Hungarian + + + + + Greek + + + + + Romanian + + + + + Thai + + + + + Vietnamese + + + + + Indonesian + + + + + + + + + Enable Fullscreen + + + + + + + Show Splash + + + + + + + Is PS4 Pro + + + + + + + Qt::Orientation::Vertical + + + QSizePolicy::Policy::MinimumExpanding + + + + 0 + 0 + + + + + + + + - - - 6 + + + Logger Settings - - 0 - - - - - - - Username + + + + + + 0 - + + 0 + + + 0 + + + 0 + + + + + Log Type + + + + + + + async + + + + + sync + + + + + + + + + + + + + + 6 + + + 0 + + + - + + + Log Filter + + + + + + + - - - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - + + + - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - - Qt::Orientation::Vertical - - - QSizePolicy::Policy::MinimumExpanding - - - - 0 - 0 - - - - - - - - - - 12 - - - 12 - + + + + + Additional Settings + + + + + + Qt::Orientation::Vertical + + + QSizePolicy::Policy::MinimumExpanding + + + + 0 + 0 + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + Qt::Orientation::Vertical + + + QSizePolicy::Policy::MinimumExpanding + + + + 0 + 0 + + + + + + + + + + + 12 + + + 12 + + + @@ -645,192 +799,6 @@ - - - General - - - - - - - - - - Emulator Settings - - - - - - Enable Fullscreen - - - - - - - Show Splash - - - - - - - Is PS4 Pro - - - - - - - Qt::Orientation::Vertical - - - QSizePolicy::Policy::MinimumExpanding - - - - 0 - 0 - - - - - - - - - - - - - - - Logger Settings - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Log Type - - - - - - - async - - - - - sync - - - - - - - - - - - - - - 6 - - - 0 - - - - - - - Log Filter - - - - - - - - - - - - - - - - - - - - - - - Additional Settings - - - - - - Qt::Orientation::Vertical - - - QSizePolicy::Policy::MinimumExpanding - - - - 0 - 0 - - - - - - - - - - - - - - - Qt::Orientation::Vertical - - - QSizePolicy::Policy::MinimumExpanding - - - - 0 - 0 - - - - - - Debug From d32e5848393cd2235c5bb162a9f4d8292b0de3f7 Mon Sep 17 00:00:00 2001 From: psucien Date: Thu, 15 Aug 2024 17:41:53 +0200 Subject: [PATCH 02/29] libraries: vide_out: redundant assert removed --- src/core/libraries/videoout/video_out.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/core/libraries/videoout/video_out.cpp b/src/core/libraries/videoout/video_out.cpp index acfcbad4..ab9eac76 100644 --- a/src/core/libraries/videoout/video_out.cpp +++ b/src/core/libraries/videoout/video_out.cpp @@ -200,7 +200,6 @@ s32 PS4_SYSV_ABI sceVideoOutGetResolutionStatus(s32 handle, SceVideoOutResolutio s32 PS4_SYSV_ABI sceVideoOutOpen(SceUserServiceUserId userId, s32 busType, s32 index, const void* param) { LOG_INFO(Lib_VideoOut, "called"); - ASSERT(userId == UserService::ORBIS_USER_SERVICE_USER_ID_SYSTEM || userId == 0); ASSERT(busType == SCE_VIDEO_OUT_BUS_TYPE_MAIN); if (index != 0) { From da9b26fa1e0a4b9e34dd9f08e7632676c7bfee9a Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Thu, 15 Aug 2024 19:41:42 +0300 Subject: [PATCH 03/29] tagged 0.2.0 release --- src/common/version.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/version.h b/src/common/version.h index 92fd18fb..708d9ec4 100644 --- a/src/common/version.h +++ b/src/common/version.h @@ -8,7 +8,7 @@ namespace Common { -constexpr char VERSION[] = "0.1.1 WIP"; -constexpr bool isRelease = false; +constexpr char VERSION[] = "0.2.0"; +constexpr bool isRelease = true; } // namespace Common From e96e66eedd9a966cb2873972886d086615401d24 Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Thu, 15 Aug 2024 19:58:18 +0300 Subject: [PATCH 04/29] starting 0.2.1 --- src/common/version.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/version.h b/src/common/version.h index 708d9ec4..80de187b 100644 --- a/src/common/version.h +++ b/src/common/version.h @@ -8,7 +8,7 @@ namespace Common { -constexpr char VERSION[] = "0.2.0"; -constexpr bool isRelease = true; +constexpr char VERSION[] = "0.2.1 WIP"; +constexpr bool isRelease = false; } // namespace Common From b5c69189e56d8813b10adb2d2bcf17426b3bdac0 Mon Sep 17 00:00:00 2001 From: Vladislav Mikhalin Date: Fri, 26 Jul 2024 18:34:36 +0300 Subject: [PATCH 05/29] avplayer WIP --- .gitmodules | 3 + CMakeLists.txt | 12 +- externals/CMakeLists.txt | 5 + externals/ffmpeg-core | 1 + src/common/logging/filter.cpp | 2 +- src/core/file_sys/fs.cpp | 4 +- src/core/file_sys/fs.h | 2 +- src/core/libraries/audio/audioout.cpp | 5 +- src/core/libraries/avplayer/avplayer.cpp | 396 ++++++++--- src/core/libraries/avplayer/avplayer.h | 302 +++++++- .../libraries/avplayer/avplayer_common.cpp | 120 ++++ src/core/libraries/avplayer/avplayer_common.h | 179 +++++ .../avplayer/avplayer_data_streamer.h | 20 + .../avplayer/avplayer_file_streamer.cpp | 93 +++ .../avplayer/avplayer_file_streamer.h | 40 ++ src/core/libraries/avplayer/avplayer_impl.cpp | 186 +++++ src/core/libraries/avplayer/avplayer_impl.h | 65 ++ .../libraries/avplayer/avplayer_source.cpp | 658 ++++++++++++++++++ src/core/libraries/avplayer/avplayer_source.h | 169 +++++ .../libraries/avplayer/avplayer_state.cpp | 481 +++++++++++++ src/core/libraries/avplayer/avplayer_state.h | 85 +++ src/core/libraries/error_codes.h | 13 + src/core/libraries/kernel/thread_management.h | 16 +- src/core/libraries/kernel/time_management.h | 1 + 24 files changed, 2721 insertions(+), 137 deletions(-) create mode 160000 externals/ffmpeg-core create mode 100644 src/core/libraries/avplayer/avplayer_common.cpp create mode 100644 src/core/libraries/avplayer/avplayer_common.h create mode 100644 src/core/libraries/avplayer/avplayer_data_streamer.h create mode 100644 src/core/libraries/avplayer/avplayer_file_streamer.cpp create mode 100644 src/core/libraries/avplayer/avplayer_file_streamer.h create mode 100644 src/core/libraries/avplayer/avplayer_impl.cpp create mode 100644 src/core/libraries/avplayer/avplayer_impl.h create mode 100644 src/core/libraries/avplayer/avplayer_source.cpp create mode 100644 src/core/libraries/avplayer/avplayer_source.h create mode 100644 src/core/libraries/avplayer/avplayer_state.cpp create mode 100644 src/core/libraries/avplayer/avplayer_state.h diff --git a/.gitmodules b/.gitmodules index 3a9d8f42..e96f33ec 100644 --- a/.gitmodules +++ b/.gitmodules @@ -61,3 +61,6 @@ [submodule "externals/date"] path = externals/date url = https://github.com/HowardHinnant/date.git +[submodule "externals/ffmpeg-core"] + path = externals/ffmpeg-core + url = https://github.com/RPCS3/ffmpeg-core.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 3685b7f8..c01e9e98 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -184,6 +184,16 @@ set(SYSTEM_LIBS src/core/libraries/system/commondialog.cpp src/core/libraries/disc_map/disc_map.cpp src/core/libraries/disc_map/disc_map.h src/core/libraries/disc_map/disc_map_codes.h + src/core/libraries/avplayer/avplayer_common.cpp + src/core/libraries/avplayer/avplayer_common.h + src/core/libraries/avplayer/avplayer_file_streamer.cpp + src/core/libraries/avplayer/avplayer_file_streamer.h + src/core/libraries/avplayer/avplayer_impl.cpp + src/core/libraries/avplayer/avplayer_impl.h + src/core/libraries/avplayer/avplayer_source.cpp + src/core/libraries/avplayer/avplayer_source.h + src/core/libraries/avplayer/avplayer_state.cpp + src/core/libraries/avplayer/avplayer_state.h src/core/libraries/avplayer/avplayer.cpp src/core/libraries/avplayer/avplayer.h ) @@ -588,7 +598,7 @@ endif() create_target_directory_groups(shadps4) -target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient RenderDoc::API) +target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient RenderDoc::API ffmpeg) target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::SPIRV glslang::glslang SDL3::SDL3) if (APPLE) diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 9ebdd878..bc313232 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -47,6 +47,11 @@ else() endif() endif() +if (NOT TARGET ffmpeg) + set(ARCHITECTURE "x86_64") + add_subdirectory(ffmpeg-core) +endif() + # Zlib-Ng if (NOT TARGET zlib-ng::zlib) set(ZLIB_ENABLE_TESTS OFF) diff --git a/externals/ffmpeg-core b/externals/ffmpeg-core new file mode 160000 index 00000000..e30b7d7f --- /dev/null +++ b/externals/ffmpeg-core @@ -0,0 +1 @@ +Subproject commit e30b7d7fe228bfb3f6e41ce1040b44a15eb7d5e0 diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp index a514652d..2c4a20de 100644 --- a/src/common/logging/filter.cpp +++ b/src/common/logging/filter.cpp @@ -106,12 +106,12 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) { SUB(Lib, DiscMap) \ SUB(Lib, Png) \ SUB(Lib, PlayGo) \ + SUB(Lib, Random) \ SUB(Lib, Usbd) \ SUB(Lib, Ajm) \ SUB(Lib, ErrorDialog) \ SUB(Lib, ImeDialog) \ SUB(Lib, AvPlayer) \ - SUB(Lib, Random) \ CLS(Frontend) \ CLS(Render) \ SUB(Render, Vulkan) \ diff --git a/src/core/file_sys/fs.cpp b/src/core/file_sys/fs.cpp index a6d5c3ea..40d8212b 100644 --- a/src/core/file_sys/fs.cpp +++ b/src/core/file_sys/fs.cpp @@ -25,9 +25,9 @@ void MntPoints::UnmountAll() { m_mnt_pairs.clear(); } -std::filesystem::path MntPoints::GetHostPath(const std::string& guest_directory) { +std::filesystem::path MntPoints::GetHostPath(std::string_view guest_directory) { // Evil games like Turok2 pass double slashes e.g /app0//game.kpf - auto corrected_path = guest_directory; + std::string corrected_path(guest_directory); size_t pos = corrected_path.find("//"); while (pos != std::string::npos) { corrected_path.replace(pos, 2, "/"); diff --git a/src/core/file_sys/fs.h b/src/core/file_sys/fs.h index d636f8bf..b0fb6324 100644 --- a/src/core/file_sys/fs.h +++ b/src/core/file_sys/fs.h @@ -31,7 +31,7 @@ public: void Unmount(const std::filesystem::path& host_folder, const std::string& guest_folder); void UnmountAll(); - std::filesystem::path GetHostPath(const std::string& guest_directory); + std::filesystem::path GetHostPath(std::string_view guest_directory); const MntPair* GetMount(const std::string& guest_path) { const auto it = std::ranges::find_if( diff --git a/src/core/libraries/audio/audioout.cpp b/src/core/libraries/audio/audioout.cpp index eac3845f..08929383 100644 --- a/src/core/libraries/audio/audioout.cpp +++ b/src/core/libraries/audio/audioout.cpp @@ -175,7 +175,6 @@ int PS4_SYSV_ABI sceAudioOutGetLastOutputTime() { } int PS4_SYSV_ABI sceAudioOutGetPortState(s32 handle, OrbisAudioOutPortState* state) { - int type = 0; int channels_num = 0; @@ -235,11 +234,11 @@ int PS4_SYSV_ABI sceAudioOutGetSystemState() { } int PS4_SYSV_ABI sceAudioOutInit() { + LOG_INFO(Lib_AudioOut, "called"); if (audio != nullptr) { return ORBIS_AUDIO_OUT_ERROR_ALREADY_INIT; } audio = std::make_unique(); - LOG_INFO(Lib_AudioOut, "called"); return ORBIS_OK; } @@ -324,10 +323,12 @@ int PS4_SYSV_ABI sceAudioOutOpenEx() { } s32 PS4_SYSV_ABI sceAudioOutOutput(s32 handle, const void* ptr) { + LOG_TRACE(Lib_AudioOut, "called"); return audio->AudioOutOutput(handle, ptr); } int PS4_SYSV_ABI sceAudioOutOutputs(OrbisAudioOutOutputParam* param, u32 num) { + LOG_TRACE(Lib_AudioOut, "called"); for (u32 i = 0; i < num; i++) { if (auto err = audio->AudioOutOutput(param[i].handle, param[i].ptr); err != 0) return err; diff --git a/src/core/libraries/avplayer/avplayer.cpp b/src/core/libraries/avplayer/avplayer.cpp index dd9f42b2..41f8d076 100644 --- a/src/core/libraries/avplayer/avplayer.cpp +++ b/src/core/libraries/avplayer/avplayer.cpp @@ -1,21 +1,36 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -// Generated By moduleGenerator #include "avplayer.h" + +#include "avplayer_impl.h" #include "common/logging/log.h" #include "core/libraries/error_codes.h" +#include "core/libraries/kernel/thread_management.h" #include "core/libraries/libs.h" +#include + namespace Libraries::AvPlayer { -int PS4_SYSV_ABI sceAvPlayerAddSource() { - LOG_ERROR(Lib_AvPlayer, "(STUBBED) called"); - return ORBIS_OK; +using namespace Kernel; + +s32 PS4_SYSV_ABI sceAvPlayerAddSource(SceAvPlayerHandle handle, const char* filename) { + LOG_TRACE(Lib_AvPlayer, "filename = {}", filename); + if (handle == nullptr) { + return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; + } + const auto res = handle->AddSource(filename); + LOG_TRACE(Lib_AvPlayer, "returning {}", res); + return res; } -int PS4_SYSV_ABI sceAvPlayerAddSourceEx() { +s32 PS4_SYSV_ABI sceAvPlayerAddSourceEx(SceAvPlayerHandle handle, SceAvPlayerUriType uriType, + SceAvPlayerSourceDetails* sourceDetails) { LOG_ERROR(Lib_AvPlayer, "(STUBBED) called"); + if (handle == nullptr) { + return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; + } return ORBIS_OK; } @@ -24,122 +39,307 @@ int PS4_SYSV_ABI sceAvPlayerChangeStream() { return ORBIS_OK; } -int PS4_SYSV_ABI sceAvPlayerClose() { +s32 PS4_SYSV_ABI sceAvPlayerClose(SceAvPlayerHandle handle) { + LOG_TRACE(Lib_AvPlayer, "called"); + if (handle == nullptr) { + return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; + } + delete handle; + return ORBIS_OK; +} + +u64 PS4_SYSV_ABI sceAvPlayerCurrentTime(SceAvPlayerHandle handle) { + LOG_TRACE(Lib_AvPlayer, "called"); + if (handle == nullptr) { + return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; + } + const auto res = handle->CurrentTime(); + LOG_TRACE(Lib_AvPlayer, "returning {}", res); + return res; +} + +s32 PS4_SYSV_ABI sceAvPlayerDisableStream(SceAvPlayerHandle handle, u32 stream_id) { + LOG_ERROR(Lib_AvPlayer, "(STUBBED) called"); + if (handle == nullptr) { + return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; + } + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceAvPlayerEnableStream(SceAvPlayerHandle handle, u32 stream_id) { + LOG_TRACE(Lib_AvPlayer, "stream_id = {}", stream_id); + if (handle == nullptr) { + return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; + } + const auto res = handle->EnableStream(stream_id); + LOG_TRACE(Lib_AvPlayer, "returning {}", res); + return res; +} + +bool PS4_SYSV_ABI sceAvPlayerGetAudioData(SceAvPlayerHandle handle, SceAvPlayerFrameInfo* p_info) { + LOG_TRACE(Lib_AvPlayer, "called"); + if (handle == nullptr || p_info == nullptr) { + return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; + } + const auto res = handle->GetAudioData(*p_info); + LOG_TRACE(Lib_AvPlayer, "returning {}", res); + return res; +} + +s32 PS4_SYSV_ABI sceAvPlayerGetStreamInfo(SceAvPlayerHandle handle, u32 stream_id, + SceAvPlayerStreamInfo* p_info) { + LOG_TRACE(Lib_AvPlayer, "stream_id = {}", stream_id); + if (handle == nullptr || p_info == nullptr) { + return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; + } + const auto res = handle->GetStreamInfo(stream_id, *p_info); + LOG_TRACE(Lib_AvPlayer, "returning {}", res); + return res; +} + +bool PS4_SYSV_ABI sceAvPlayerGetVideoData(SceAvPlayerHandle handle, + SceAvPlayerFrameInfo* video_info) { + LOG_TRACE(Lib_AvPlayer, "called"); + if (handle == nullptr || video_info == nullptr) { + return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; + } + const auto res = handle->GetVideoData(*video_info); + LOG_TRACE(Lib_AvPlayer, "returning {}", res); + return res; +} + +bool PS4_SYSV_ABI sceAvPlayerGetVideoDataEx(SceAvPlayerHandle handle, + SceAvPlayerFrameInfoEx* video_info) { + LOG_TRACE(Lib_AvPlayer, "called"); + if (handle == nullptr || video_info == nullptr) { + return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; + } + const auto res = handle->GetVideoData(*video_info); + LOG_TRACE(Lib_AvPlayer, "returning {}", res); + return res; +} + +constexpr u32 GetPriority(u32 base, u32 offset) { + // (27D <= base_priority <= 2FC) + offset <= 2FF + return std::min(std::min(std::max(637u, base), 764u) + offset, 767u); +} + +SceAvPlayerHandle PS4_SYSV_ABI sceAvPlayerInit(SceAvPlayerInitData* data) { + LOG_TRACE(Lib_AvPlayer, "called"); + if (data == nullptr) { + return nullptr; + } + + if (data->memory_replacement.allocate == nullptr || + data->memory_replacement.allocate_texture == nullptr || + data->memory_replacement.deallocate == nullptr || + data->memory_replacement.deallocate_texture == nullptr) { + LOG_ERROR(Lib_AvPlayer, "All allocators are required for AVPlayer Initialisation."); + return nullptr; + } + + ThreadPriorities priorities{}; + const u32 base_priority = data->base_priority != 0 ? data->base_priority : 700; + priorities.video_decoder_priority = GetPriority(base_priority, 5); + priorities.audio_decoder_priority = GetPriority(base_priority, 6); + priorities.demuxer_priority = GetPriority(base_priority, 9); + priorities.controller_priority = GetPriority(base_priority, 2); + // priorities.http_streaming_priority = GetPriority(base_priority, 10); + // priorities.file_streaming_priority = GetPriority(priorities.http_streaming_priority, 15); + // priorities.maxPriority = priorities.http_streaming_priority; + + const auto player = new AvPlayer(); + player->Init(*data, priorities); + return player; +} + +s32 PS4_SYSV_ABI sceAvPlayerInitEx(const SceAvPlayerInitDataEx* p_data, + SceAvPlayerHandle* p_player) { + LOG_TRACE(Lib_AvPlayer, "called"); + if (p_data == nullptr || p_player == nullptr) { + return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; + } + + if (p_data->memory_replacement.allocate == nullptr || + p_data->memory_replacement.allocate_texture == nullptr || + p_data->memory_replacement.deallocate == nullptr || + p_data->memory_replacement.deallocate_texture == nullptr) { + LOG_ERROR(Lib_AvPlayer, "All allocators are required for AVPlayer Initialisation."); + return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; + } + + SceAvPlayerInitData data = {}; + data.memory_replacement = p_data->memory_replacement; + data.file_replacement = p_data->file_replacement; + data.event_replacement = p_data->event_replacement; + data.default_language = p_data->default_language; + data.num_output_video_framebuffers = p_data->num_output_video_framebuffers; + data.auto_start = p_data->auto_start; + + ThreadPriorities priorities{}; + s32 base_priority = 0; + const auto res = scePthreadGetprio(scePthreadSelf(), &base_priority); + if (res != 0 || base_priority == 0) { + base_priority = 700; + } + + if (p_data->video_decoder_priority != 0) { + priorities.video_decoder_priority = p_data->video_decoder_priority; + } else { + priorities.video_decoder_priority = GetPriority(base_priority, 5); + } + priorities.video_decoder_affinity = p_data->video_decoder_affinity; + + if (p_data->audio_decoder_priority != 0) { + priorities.audio_decoder_priority = p_data->audio_decoder_priority; + } else { + priorities.audio_decoder_priority = GetPriority(base_priority, 6); + } + priorities.audio_decoder_affinity = p_data->audio_decoder_affinity; + + if (p_data->controller_priority != 0) { + priorities.controller_priority = p_data->controller_priority; + } else { + priorities.controller_priority = GetPriority(base_priority, 2); + } + priorities.controller_affinity = p_data->controller_affinity; + + if (p_data->demuxer_priority != 0) { + priorities.demuxer_priority = p_data->demuxer_priority; + } else { + priorities.demuxer_priority = GetPriority(base_priority, 9); + } + priorities.demuxer_affinity = p_data->demuxer_affinity; + + // if (p_data->http_streaming_priority != 0) { + // priorities.http_streaming_priority = p_data->http_streaming_priority; + // } else { + // priorities.http_streaming_priority = GetPriority(base_priority, 10); + // } + // priorities.http_streaming_affinity = p_data->http_streaming_affinity; + + // if (p_data->file_streaming_priority != 0) { + // priorities.file_streaming_priority = p_data->file_streaming_priority; + // } else { + // priorities.file_streaming_priority = GetPriority(base_priority, 15); + // } + // priorities.http_streaming_affinity = p_data->http_streaming_affinity; + + const auto player = new AvPlayer(); + player->Init(data, priorities); + *p_player = player; + return ORBIS_OK; +} + +bool PS4_SYSV_ABI sceAvPlayerIsActive(SceAvPlayerHandle handle) { + LOG_TRACE(Lib_AvPlayer, "called"); + if (handle == nullptr) { + LOG_TRACE(Lib_AvPlayer, "returning ORBIS_AVPLAYER_ERROR_INVALID_PARAMS"); + return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; + } + const auto res = handle->IsActive(); + LOG_TRACE(Lib_AvPlayer, "returning {}", res); + return res; +} + +s32 PS4_SYSV_ABI sceAvPlayerJumpToTime(SceAvPlayerHandle handle, uint64_t jump_time_msec) { + LOG_ERROR(Lib_AvPlayer, "(STUBBED) called"); + if (handle == nullptr) { + return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; + } + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceAvPlayerPause(SceAvPlayerHandle handle) { + LOG_ERROR(Lib_AvPlayer, "(STUBBED) called"); + if (handle == nullptr) { + return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; + } + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceAvPlayerPostInit(SceAvPlayerHandle handle, SceAvPlayerPostInitData* data) { + LOG_TRACE(Lib_AvPlayer, "called"); + if (handle == nullptr || data == nullptr) { + return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; + } + const auto res = handle->PostInit(*data); + LOG_TRACE(Lib_AvPlayer, "returning {}", res); + return res; +} + +s32 PS4_SYSV_ABI sceAvPlayerPrintf(const char* format, ...) { LOG_ERROR(Lib_AvPlayer, "(STUBBED) called"); return ORBIS_OK; } -int PS4_SYSV_ABI sceAvPlayerCurrentTime() { +s32 PS4_SYSV_ABI sceAvPlayerResume(SceAvPlayerHandle handle) { + LOG_ERROR(Lib_AvPlayer, "(STUBBED) called"); + if (handle == nullptr) { + return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; + } + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceAvPlayerSetAvSyncMode(SceAvPlayerHandle handle, + SceAvPlayerAvSyncMode sync_mode) { + LOG_ERROR(Lib_AvPlayer, "(STUBBED) called"); + if (handle == nullptr) { + return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; + } + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceAvPlayerSetLogCallback(SceAvPlayerLogCallback logCb, void* user_data) { LOG_ERROR(Lib_AvPlayer, "(STUBBED) called"); return ORBIS_OK; } -int PS4_SYSV_ABI sceAvPlayerDisableStream() { +s32 PS4_SYSV_ABI sceAvPlayerSetLooping(SceAvPlayerHandle handle, bool loop_flag) { LOG_ERROR(Lib_AvPlayer, "(STUBBED) called"); + if (handle == nullptr) { + return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; + } return ORBIS_OK; } -int PS4_SYSV_ABI sceAvPlayerEnableStream() { +s32 PS4_SYSV_ABI sceAvPlayerSetTrickSpeed(SceAvPlayerHandle handle, s32 trick_speed) { LOG_ERROR(Lib_AvPlayer, "(STUBBED) called"); + if (handle == nullptr) { + return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; + } return ORBIS_OK; } -int PS4_SYSV_ABI sceAvPlayerGetAudioData() { - LOG_ERROR(Lib_AvPlayer, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceAvPlayerStart(SceAvPlayerHandle handle) { + LOG_TRACE(Lib_AvPlayer, "called"); + if (handle == nullptr) { + return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; + } + const auto res = handle->Start(); + LOG_TRACE(Lib_AvPlayer, "returning {}", res); + return res; } -int PS4_SYSV_ABI sceAvPlayerGetStreamInfo() { +s32 PS4_SYSV_ABI sceAvPlayerStop(SceAvPlayerHandle handle) { LOG_ERROR(Lib_AvPlayer, "(STUBBED) called"); - return ORBIS_OK; + if (handle == nullptr) { + return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; + } + return handle->Stop(); } -int PS4_SYSV_ABI sceAvPlayerGetVideoData() { - LOG_ERROR(Lib_AvPlayer, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceAvPlayerStreamCount(SceAvPlayerHandle handle) { + LOG_TRACE(Lib_AvPlayer, "called"); + if (handle == nullptr) { + return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; + } + const auto res = handle->GetStreamCount(); + LOG_TRACE(Lib_AvPlayer, "returning {}", res); + return res; } -int PS4_SYSV_ABI sceAvPlayerGetVideoDataEx() { - LOG_ERROR(Lib_AvPlayer, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceAvPlayerInit() { - LOG_ERROR(Lib_AvPlayer, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceAvPlayerInitEx() { - LOG_ERROR(Lib_AvPlayer, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceAvPlayerIsActive() { - LOG_ERROR(Lib_AvPlayer, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceAvPlayerJumpToTime() { - LOG_ERROR(Lib_AvPlayer, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceAvPlayerPause() { - LOG_ERROR(Lib_AvPlayer, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceAvPlayerPostInit() { - LOG_ERROR(Lib_AvPlayer, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceAvPlayerPrintf() { - LOG_ERROR(Lib_AvPlayer, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceAvPlayerResume() { - LOG_ERROR(Lib_AvPlayer, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceAvPlayerSetAvSyncMode() { - LOG_ERROR(Lib_AvPlayer, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceAvPlayerSetLogCallback() { - LOG_ERROR(Lib_AvPlayer, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceAvPlayerSetLooping() { - LOG_ERROR(Lib_AvPlayer, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceAvPlayerSetTrickSpeed() { - LOG_ERROR(Lib_AvPlayer, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceAvPlayerStart() { - LOG_ERROR(Lib_AvPlayer, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceAvPlayerStop() { - LOG_ERROR(Lib_AvPlayer, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceAvPlayerStreamCount() { - LOG_ERROR(Lib_AvPlayer, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceAvPlayerVprintf() { +s32 PS4_SYSV_ABI sceAvPlayerVprintf(const char* format, va_list args) { LOG_ERROR(Lib_AvPlayer, "(STUBBED) called"); return ORBIS_OK; } diff --git a/src/core/libraries/avplayer/avplayer.h b/src/core/libraries/avplayer/avplayer.h index 39a619ee..f5589441 100644 --- a/src/core/libraries/avplayer/avplayer.h +++ b/src/core/libraries/avplayer/avplayer.h @@ -11,33 +11,279 @@ class SymbolsResolver; namespace Libraries::AvPlayer { -int PS4_SYSV_ABI sceAvPlayerAddSource(); -int PS4_SYSV_ABI sceAvPlayerAddSourceEx(); -int PS4_SYSV_ABI sceAvPlayerChangeStream(); -int PS4_SYSV_ABI sceAvPlayerClose(); -int PS4_SYSV_ABI sceAvPlayerCurrentTime(); -int PS4_SYSV_ABI sceAvPlayerDisableStream(); -int PS4_SYSV_ABI sceAvPlayerEnableStream(); -int PS4_SYSV_ABI sceAvPlayerGetAudioData(); -int PS4_SYSV_ABI sceAvPlayerGetStreamInfo(); -int PS4_SYSV_ABI sceAvPlayerGetVideoData(); -int PS4_SYSV_ABI sceAvPlayerGetVideoDataEx(); -int PS4_SYSV_ABI sceAvPlayerInit(); -int PS4_SYSV_ABI sceAvPlayerInitEx(); -int PS4_SYSV_ABI sceAvPlayerIsActive(); -int PS4_SYSV_ABI sceAvPlayerJumpToTime(); -int PS4_SYSV_ABI sceAvPlayerPause(); -int PS4_SYSV_ABI sceAvPlayerPostInit(); -int PS4_SYSV_ABI sceAvPlayerPrintf(); -int PS4_SYSV_ABI sceAvPlayerResume(); -int PS4_SYSV_ABI sceAvPlayerSetAvSyncMode(); -int PS4_SYSV_ABI sceAvPlayerSetLogCallback(); -int PS4_SYSV_ABI sceAvPlayerSetLooping(); -int PS4_SYSV_ABI sceAvPlayerSetTrickSpeed(); -int PS4_SYSV_ABI sceAvPlayerStart(); -int PS4_SYSV_ABI sceAvPlayerStop(); -int PS4_SYSV_ABI sceAvPlayerStreamCount(); -int PS4_SYSV_ABI sceAvPlayerVprintf(); +class AvPlayer; + +using SceAvPlayerHandle = AvPlayer*; + +enum SceAvPlayerUriType { SCE_AVPLAYER_URI_TYPE_SOURCE = 0 }; + +struct SceAvPlayerUri { + const char* name; + u32 length; +}; + +enum SceAvPlayerSourceType { + SCE_AVPLAYER_SOURCE_TYPE_UNKNOWN = 0, + SCE_AVPLAYER_SOURCE_TYPE_FILE_MP4 = 1, + SCE_AVPLAYER_SOURCE_TYPE_HLS = 8 +}; + +struct SceAvPlayerSourceDetails { + SceAvPlayerUri uri; + u8 reserved1[64]; + SceAvPlayerSourceType source_type; + u8 reserved2[44]; +}; + +struct SceAvPlayerAudio { + u16 channel_count; + u8 reserved1[2]; + u32 sample_rate; + u32 size; + u8 language_code[4]; +}; + +struct SceAvPlayerVideo { + u32 width; + u32 height; + f32 aspect_ratio; + u8 language_code[4]; +}; + +struct SceAvPlayerTextPosition { + u16 top; + u16 left; + u16 bottom; + u16 right; +}; + +struct SceAvPlayerTimedText { + u8 language_code[4]; + u16 text_size; + u16 font_size; + SceAvPlayerTextPosition position; +}; + +union SceAvPlayerStreamDetails { + u8 reserved[16]; + SceAvPlayerAudio audio; + SceAvPlayerVideo video; + SceAvPlayerTimedText subs; +}; + +struct SceAvPlayerFrameInfo { + u8* pData; + u8 reserved[4]; + u64 timestamp; + SceAvPlayerStreamDetails details; +}; + +struct SceAvPlayerStreamInfo { + u32 type; + u8 reserved[4]; + SceAvPlayerStreamDetails details; + u64 duration; + u64 start_time; +}; + +struct SceAvPlayerAudioEx { + u16 channel_count; + u8 reserved[2]; + u32 sample_rate; + u32 size; + u8 language_code[4]; + u8 reserved1[64]; +}; + +struct SceAvPlayerVideoEx { + u32 width; + u32 height; + f32 aspect_ratio; + u8 language_code[4]; + u32 framerate; + u32 crop_left_offset; + u32 crop_right_offset; + u32 crop_top_offset; + u32 crop_bottom_offset; + u32 pitch; + u8 luma_bit_depth; + u8 chroma_bit_depth; + bool video_full_range_flag; + u8 reserved1[37]; +}; + +struct SceAvPlayerTimedTextEx { + u8 language_code[4]; + u8 reserved[12]; + u8 reserved1[64]; +}; + +union SceAvPlayerStreamDetailsEx { + SceAvPlayerAudioEx audio; + SceAvPlayerVideoEx video; + SceAvPlayerTimedTextEx subs; + u8 reserved1[80]; +}; + +struct SceAvPlayerFrameInfoEx { + void* pData; + u8 reserved[4]; + u64 timestamp; + SceAvPlayerStreamDetailsEx details; +}; + +typedef void* PS4_SYSV_ABI (*SceAvPlayerAllocate)(void* p, u32 align, u32 size); +typedef void PS4_SYSV_ABI (*SceAvPlayerDeallocate)(void* p, void* mem); +typedef void* PS4_SYSV_ABI (*SceAvPlayerAllocateTexture)(void* p, u32 align, u32 size); +typedef void PS4_SYSV_ABI (*SceAvPlayerDeallocateTexture)(void* p, void* mem); + +struct SceAvPlayerMemAllocator { + void* object_ptr; + SceAvPlayerAllocate allocate; + SceAvPlayerDeallocate deallocate; + SceAvPlayerAllocateTexture allocate_texture; + SceAvPlayerDeallocateTexture deallocate_texture; +}; + +typedef s32 PS4_SYSV_ABI (*SceAvPlayerOpenFile)(void* p, const char* name); +typedef s32 PS4_SYSV_ABI (*SceAvPlayerCloseFile)(void* p); +typedef s32 PS4_SYSV_ABI (*SceAvPlayerReadOffsetFile)(void* p, u8* buf, u64 pos, u32 len); +typedef u64 PS4_SYSV_ABI (*SceAvPlayerSizeFile)(void* p); + +struct SceAvPlayerFileReplacement { + void* object_ptr; + SceAvPlayerOpenFile open; + SceAvPlayerCloseFile close; + SceAvPlayerReadOffsetFile readOffset; + SceAvPlayerSizeFile size; +}; + +typedef void PS4_SYSV_ABI (*SceAvPlayerEventCallback)(void* p, s32 event, s32 src_id, void* data); + +struct SceAvPlayerEventReplacement { + void* object_ptr; + SceAvPlayerEventCallback event_callback; +}; + +enum SceAvPlayerDebuglevels { + SCE_AVPLAYER_DBG_NONE, + SCE_AVPLAYER_DBG_INFO, + SCE_AVPLAYER_DBG_WARNINGS, + SCE_AVPLAYER_DBG_ALL +}; + +struct SceAvPlayerInitData { + SceAvPlayerMemAllocator memory_replacement; + SceAvPlayerFileReplacement file_replacement; + SceAvPlayerEventReplacement event_replacement; + SceAvPlayerDebuglevels debug_level; + u32 base_priority; + s32 num_output_video_framebuffers; + bool auto_start; + u8 reserved[3]; + const char* default_language; +}; + +struct SceAvPlayerInitDataEx { + size_t this_size; + SceAvPlayerMemAllocator memory_replacement; + SceAvPlayerFileReplacement file_replacement; + SceAvPlayerEventReplacement event_replacement; + const char* default_language; + SceAvPlayerDebuglevels debug_level; + u32 audio_decoder_priority; + u32 audio_decoder_affinity; + u32 video_decoder_priority; + u32 video_decoder_affinity; + u32 demuxer_priority; + u32 demuxer_affinity; + u32 controller_priority; + u32 controller_affinity; + u32 http_streaming_priority; + u32 http_streaming_affinity; + u32 file_streaming_priority; + u32 file_streaming_affinity; + s32 num_output_video_framebuffers; + bool auto_start; + u8 reserved[3]; +}; + +enum SceAvPlayerStreamType { + SCE_AVPLAYER_VIDEO, + SCE_AVPLAYER_AUDIO, + SCE_AVPLAYER_TIMEDTEXT, + SCE_AVPLAYER_UNKNOWN +}; + +enum SceAvPlayerVideoDecoderType { + SCE_AVPLAYER_VIDEO_DECODER_TYPE_DEFAULT = 0, + SCE_AVPLAYER_VIDEO_DECODER_TYPE_RESERVED1, + SCE_AVPLAYER_VIDEO_DECODER_TYPE_SOFTWARE, + SCE_AVPLAYER_VIDEO_DECODER_TYPE_SOFTWARE2 +}; + +enum SceAvPlayerAudioDecoderType { + SCE_AVPLAYER_AUDIO_DECODER_TYPE_DEFAULT = 0, + SCE_AVPLAYER_AUDIO_DECODER_TYPE_RESERVED1, + SCE_AVPLAYER_AUDIO_DECODER_TYPE_RESERVED2 +}; + +struct SceAvPlayerDecoderInit { + union { + SceAvPlayerVideoDecoderType video_type; + SceAvPlayerAudioDecoderType audio_type; + u8 reserved[4]; + } decoderType; + union { + struct { + s32 cpu_affinity_mask; + s32 cpu_thread_priority; + u8 decode_pipeline_depth; + u8 compute_pipe_id; + u8 compute_queue_id; + u8 enable_interlaced; + u8 reserved[16]; + } avcSw2; + struct { + u8 audio_channel_order; + u8 reserved[27]; + } aac; + u8 reserved[28]; + } decoderParams; +}; + +struct SceAvPlayerHTTPCtx { + u32 http_context_id; + u32 ssl_context_id; +}; + +struct SceAvPlayerPostInitData { + u32 demux_video_buffer_size; + SceAvPlayerDecoderInit video_decoder_init; + SceAvPlayerDecoderInit audio_decoder_init; + SceAvPlayerHTTPCtx http_context; + u8 reserved[56]; +}; + +enum SceAvPlayerAvSyncMode { + SCE_AVPLAYER_AV_SYNC_MODE_DEFAULT = 0, + SCE_AVPLAYER_AV_SYNC_MODE_NONE +}; + +typedef int PS4_SYSV_ABI (*SceAvPlayerLogCallback)(void* p, const char* format, va_list args); + +enum SceAvPlayerEvents { + SCE_AVPLAYER_STATE_STOP = 0x01, + SCE_AVPLAYER_STATE_READY = 0x02, + SCE_AVPLAYER_STATE_PLAY = 0x03, + SCE_AVPLAYER_STATE_PAUSE = 0x04, + SCE_AVPLAYER_STATE_BUFFERING = 0x05, + SCE_AVPLAYER_TIMED_TEXT_DELIVERY = 0x10, + SCE_AVPLAYER_WARNING_ID = 0x20, + SCE_AVPLAYER_ENCRYPTION = 0x30, + SCE_AVPLAYER_DRM_ERROR = 0x40 +}; void RegisterlibSceAvPlayer(Core::Loader::SymbolsResolver* sym); -} // namespace Libraries::AvPlayer \ No newline at end of file + +} // namespace Libraries::AvPlayer diff --git a/src/core/libraries/avplayer/avplayer_common.cpp b/src/core/libraries/avplayer/avplayer_common.cpp new file mode 100644 index 00000000..3536c030 --- /dev/null +++ b/src/core/libraries/avplayer/avplayer_common.cpp @@ -0,0 +1,120 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "avplayer.h" +#include "avplayer_common.h" + +#include // std::equal +#include // std::tolower +#include // std::string_view + +namespace Libraries::AvPlayer { + +using namespace Kernel; + +Kernel::ScePthreadMutex CreateMutex(int type, const char* name) { + ScePthreadMutexattr attr{}; + ScePthreadMutex mutex{}; + if (scePthreadMutexattrInit(&attr) == 0) { + if (scePthreadMutexattrSettype(&attr, type) == 0) { + if (scePthreadMutexInit(&mutex, &attr, name) != 0) { + if (mutex != nullptr) { + scePthreadMutexDestroy(&mutex); + } + return nullptr; + } + } + } + if (attr != nullptr) { + scePthreadMutexattrDestroy(&attr); + } + return mutex; +} + +ScePthread CreateThread(Kernel::PthreadEntryFunc func, const ThreadParameters& params) { + ScePthreadAttr attr; + if (scePthreadAttrInit(&attr) != 0) { + return nullptr; + } + if (scePthreadAttrSetinheritsched(&attr, 0) != 0) { + scePthreadAttrDestroy(&attr); + return nullptr; + } + + SceKernelSchedParam param{.sched_priority = static_cast(params.priority)}; + if (scePthreadAttrSetschedparam(&attr, ¶m) != 0) { + scePthreadAttrDestroy(&attr); + return nullptr; + } + if (scePthreadAttrSetstacksize(&attr, std::min(params.stack_size, 0x4000u)) != 0) { + scePthreadAttrDestroy(&attr); + return nullptr; + } + if (scePthreadAttrSetdetachstate(&attr, 0) != 0) { + scePthreadAttrDestroy(&attr); + return nullptr; + } + if (params.affinity > 0) { + if (scePthreadAttrSetaffinity(&attr, params.affinity) != 0) { + scePthreadAttrDestroy(&attr); + return nullptr; + } + } + + ScePthread thread{}; + if (scePthreadCreate(&thread, &attr, func, params.p_user_data, params.thread_name) != 0) { + scePthreadAttrDestroy(&attr); + return nullptr; + } + + scePthreadAttrDestroy(&attr); + return thread; +} + +static bool ichar_equals(char a, char b) { + return std::tolower(static_cast(a)) == + std::tolower(static_cast(b)); +} + +static bool iequals(std::string_view l, std::string_view r) { + return std::ranges::equal(l, r, ichar_equals); +} + +SceAvPlayerSourceType GetSourceType(std::string_view path) { + if (path.empty()) { + return SCE_AVPLAYER_SOURCE_TYPE_UNKNOWN; + } + + std::string_view name = path; + if (path.find("://") != std::string_view::npos) { + // This path is a URI. Strip HTTP parameters from it. + // schema://server.domain/path/file.ext/and/beyond?param=value#paragraph -> + // -> schema://server.domain/path/to/file.ext/and/beyond + name = path.substr(0, path.find_first_of("?#")); + if (name.empty()) { + return SCE_AVPLAYER_SOURCE_TYPE_UNKNOWN; + } + } + + // schema://server.domain/path/to/file.ext/and/beyond -> .ext/and/beyond + auto ext = name.substr(name.rfind('.')); + if (ext.empty()) { + return SCE_AVPLAYER_SOURCE_TYPE_UNKNOWN; + } + + // .ext/and/beyond -> .ext + ext = ext.substr(0, ext.find('/')); + + if (iequals(ext, ".mp4") || iequals(ext, ".m4v") || iequals(ext, ".m3d") || + iequals(ext, ".m4a") || iequals(ext, ".mov")) { + return SCE_AVPLAYER_SOURCE_TYPE_FILE_MP4; + } + + if (iequals(ext, ".m3u8")) { + return SCE_AVPLAYER_SOURCE_TYPE_HLS; + } + + return SCE_AVPLAYER_SOURCE_TYPE_UNKNOWN; +} + +} // namespace Libraries::AvPlayer diff --git a/src/core/libraries/avplayer/avplayer_common.h b/src/core/libraries/avplayer/avplayer_common.h new file mode 100644 index 00000000..5faf2388 --- /dev/null +++ b/src/core/libraries/avplayer/avplayer_common.h @@ -0,0 +1,179 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "avplayer.h" + +#include "common/assert.h" +#include "common/logging/log.h" +#include "core/libraries/kernel/thread_management.h" + +#include +#include +#include + +#define AVPLAYER_IS_ERROR(x) ((x) < 0) + +namespace Libraries::AvPlayer { + +enum class AvState { + Initial, + AddingSource, + Ready, + Play, + Stop, + EndOfFile, + Pause, + C0x08, + Jump, + TrickMode, + C0x0B, + Buffering, + Starting, + C0x0E, + C0x0F, + C0x10, + Error, +}; + +enum class AvEventType { + ChangeFlowState = 21, + WarningId = 22, + RevertState = 30, + AddSource = 40, + Error = 255, +}; + +struct ThreadPriorities { + u32 audio_decoder_priority; + u32 audio_decoder_affinity; + u32 video_decoder_priority; + u32 video_decoder_affinity; + u32 demuxer_priority; + u32 demuxer_affinity; + u32 controller_priority; + u32 controller_affinity; + // u32 http_streaming_priority; + // u32 http_streaming_affinity; + // u32 file_streaming_priority; + // u32 file_streaming_affinity; + // u32 maxPriority; + // u32 maxAffinity; +}; + +union AvPlayerEventData { + u32 num_frames; // 20 + AvState state; // AvEventType::ChangeFlowState + s32 error; // AvEventType::WarningId + u32 attempt; // AvEventType::AddSource +}; + +struct AvPlayerEvent { + AvEventType event; + AvPlayerEventData payload; +}; + +Kernel::ScePthreadMutex CreateMutex(int type, const char* name); + +class PthreadMutex { +public: + using ScePthreadMutex = Kernel::ScePthreadMutex; + + PthreadMutex() = default; + + PthreadMutex(const PthreadMutex&) = delete; + PthreadMutex& operator=(const PthreadMutex&) = delete; + + PthreadMutex(PthreadMutex&& r) : m_mutex(r.m_mutex) { + r.m_mutex = nullptr; + } + PthreadMutex& operator=(PthreadMutex&& r) { + std::swap(m_mutex, r.m_mutex); + return *this; + } + + PthreadMutex(int type, const char* name) : m_mutex(CreateMutex(type, name)) {} + ~PthreadMutex() { + if (m_mutex != nullptr) { + Kernel::scePthreadMutexDestroy(&m_mutex); + } + } + + operator ScePthreadMutex() { + return m_mutex; + } + + int Lock() { + return Kernel::scePthreadMutexLock(&m_mutex); + } + + int Unlock() { + return Kernel::scePthreadMutexUnlock(&m_mutex); + } + + // implement BasicLockable to use std::lock_guard + // NOLINTNEXTLINE(readability-identifier-naming) + void lock() { + ASSERT_MSG(Lock() >= 0, "Could not lock the mutex"); + } + + // NOLINTNEXTLINE(readability-identifier-naming) + void unlock() { + ASSERT_MSG(Unlock() >= 0, "Could not unlock the mutex"); + } + + operator bool() { + return m_mutex != nullptr; + } + +private: + ScePthreadMutex m_mutex{}; +}; + +template +class AvPlayerQueue { +public: + AvPlayerQueue() : m_mutex(PTHREAD_MUTEX_ERRORCHECK, "SceAvPlayer0StlHandler") {} + + size_t Size() { + return m_queue.size(); + } + + void Push(T&& value) { + std::lock_guard guard(m_mutex); + m_queue.emplace(std::forward(value)); + } + + std::optional Pop() { + if (Size() == 0) { + return std::nullopt; + } + std::lock_guard guard(m_mutex); + auto result = std::move(m_queue.front()); + m_queue.pop(); + return result; + } + + void Clear() { + std::lock_guard guard(m_mutex); + m_queue = {}; + } + +private: + PthreadMutex m_mutex{}; + std::queue m_queue{}; +}; + +struct ThreadParameters { + void* p_user_data; + const char* thread_name; + u32 stack_size; + u32 priority; + u32 affinity; +}; + +Kernel::ScePthread CreateThread(Kernel::PthreadEntryFunc func, const ThreadParameters& params); +SceAvPlayerSourceType GetSourceType(std::string_view path); + +} // namespace Libraries::AvPlayer diff --git a/src/core/libraries/avplayer/avplayer_data_streamer.h b/src/core/libraries/avplayer/avplayer_data_streamer.h new file mode 100644 index 00000000..04097bb4 --- /dev/null +++ b/src/core/libraries/avplayer/avplayer_data_streamer.h @@ -0,0 +1,20 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "avplayer.h" + +#include "common/types.h" + +struct AVIOContext; + +namespace Libraries::AvPlayer { + +class IDataStreamer { +public: + virtual ~IDataStreamer() = default; + virtual AVIOContext* GetContext() = 0; +}; + +} // namespace Libraries::AvPlayer diff --git a/src/core/libraries/avplayer/avplayer_file_streamer.cpp b/src/core/libraries/avplayer/avplayer_file_streamer.cpp new file mode 100644 index 00000000..1c54c454 --- /dev/null +++ b/src/core/libraries/avplayer/avplayer_file_streamer.cpp @@ -0,0 +1,93 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "avplayer_file_streamer.h" + +#include "avplayer_common.h" + +#include + +extern "C" { +#include +#include +} + +#define AVPLAYER_AVIO_BUFFER_SIZE 4096 + +namespace Libraries::AvPlayer { + +AvPlayerFileStreamer::AvPlayerFileStreamer(SceAvPlayerFileReplacement& file_replacement, + std::string_view path) + : m_file_replacement(file_replacement) { + Init(path); +} + +AvPlayerFileStreamer::~AvPlayerFileStreamer() { + if (m_avio_context != nullptr) { + avio_context_free(&m_avio_context); + } + if (m_avio_buffer != nullptr) { + av_free(m_avio_buffer); + } + if (m_file_replacement.close != nullptr && m_fd >= 0) { + const auto close = m_file_replacement.close; + const auto ptr = m_file_replacement.object_ptr; + close(ptr); + } +} + +s32 AvPlayerFileStreamer::Init(std::string_view path) { + const auto ptr = m_file_replacement.object_ptr; + m_fd = m_file_replacement.open(ptr, path.data()); + if (m_fd < 0) { + return -1; + } + m_file_size = m_file_replacement.size(ptr); + m_avio_buffer = reinterpret_cast(av_malloc(AVPLAYER_AVIO_BUFFER_SIZE)); + m_avio_context = + avio_alloc_context(m_avio_buffer, AVPLAYER_AVIO_BUFFER_SIZE, 0, this, + &AvPlayerFileStreamer::ReadPacket, nullptr, &AvPlayerFileStreamer::Seek); + return 0; +} + +s32 AvPlayerFileStreamer::ReadPacket(void* opaque, u8* buffer, s32 size) { + const auto self = reinterpret_cast(opaque); + if (self->m_position >= self->m_file_size) { + return AVERROR_EOF; + } + if (self->m_position + size > self->m_file_size) { + size = self->m_file_size - self->m_position; + } + const auto read_offset = self->m_file_replacement.readOffset; + const auto ptr = self->m_file_replacement.object_ptr; + const auto bytes_read = read_offset(ptr, buffer, self->m_position, size); + if (size != 0 && bytes_read == 0) { + return AVERROR_EOF; + } + self->m_position += bytes_read; + return bytes_read; +} + +s64 AvPlayerFileStreamer::Seek(void* opaque, s64 offset, int whence) { + const auto self = reinterpret_cast(opaque); + if (whence & AVSEEK_SIZE) { + return self->m_file_size; + } + + if (whence == SEEK_CUR) { + self->m_position = + std::min(u64(std::max(0ll, s64(self->m_position) + offset)), self->m_file_size); + return self->m_position; + } else if (whence == SEEK_SET) { + self->m_position = std::min(u64(std::max(0ll, offset)), self->m_file_size); + return self->m_position; + } else if (whence == SEEK_END) { + self->m_position = + std::min(u64(std::max(0ll, s64(self->m_file_size) + offset)), self->m_file_size); + return self->m_position; + } + + return -1; +} + +} // namespace Libraries::AvPlayer diff --git a/src/core/libraries/avplayer/avplayer_file_streamer.h b/src/core/libraries/avplayer/avplayer_file_streamer.h new file mode 100644 index 00000000..9f1442f9 --- /dev/null +++ b/src/core/libraries/avplayer/avplayer_file_streamer.h @@ -0,0 +1,40 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "avplayer.h" +#include "avplayer_data_streamer.h" + +#include +#include + +struct AVIOContext; + +namespace Libraries::AvPlayer { + +class AvPlayerFileStreamer : public IDataStreamer { +public: + AvPlayerFileStreamer(SceAvPlayerFileReplacement& file_replacement, std::string_view path); + ~AvPlayerFileStreamer(); + + AVIOContext* GetContext() override { + return m_avio_context; + } + +private: + s32 Init(std::string_view path); + + static s32 ReadPacket(void* opaque, u8* buffer, s32 size); + static s64 Seek(void* opaque, s64 buffer, int whence); + + SceAvPlayerFileReplacement m_file_replacement; + + int m_fd = -1; + u64 m_position{}; + u64 m_file_size{}; + u8* m_avio_buffer{}; + AVIOContext* m_avio_context{}; +}; + +} // namespace Libraries::AvPlayer diff --git a/src/core/libraries/avplayer/avplayer_impl.cpp b/src/core/libraries/avplayer/avplayer_impl.cpp new file mode 100644 index 00000000..58793da7 --- /dev/null +++ b/src/core/libraries/avplayer/avplayer_impl.cpp @@ -0,0 +1,186 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "avplayer_common.h" +#include "avplayer_file_streamer.h" +#include "avplayer_impl.h" + +#include "common/logging/log.h" +#include "core/libraries/error_codes.h" +#include "core/libraries/kernel/libkernel.h" + +using namespace Libraries::Kernel; + +namespace Libraries::AvPlayer { + +void* PS4_SYSV_ABI AvPlayer::Allocate(void* handle, u32 alignment, u32 size) { + const auto* const self = reinterpret_cast(handle); + const auto allocate = self->m_init_data_original.memory_replacement.allocate; + const auto ptr = self->m_init_data_original.memory_replacement.object_ptr; + return allocate(ptr, alignment, size); +} + +void PS4_SYSV_ABI AvPlayer::Deallocate(void* handle, void* memory) { + const auto* const self = reinterpret_cast(handle); + const auto deallocate = self->m_init_data_original.memory_replacement.deallocate; + const auto ptr = self->m_init_data_original.memory_replacement.object_ptr; + return deallocate(ptr, memory); +} + +void* PS4_SYSV_ABI AvPlayer::AllocateTexture(void* handle, u32 alignment, u32 size) { + const auto* const self = reinterpret_cast(handle); + const auto allocate = self->m_init_data_original.memory_replacement.allocate_texture; + const auto ptr = self->m_init_data_original.memory_replacement.object_ptr; + return allocate(ptr, alignment, size); +} + +void PS4_SYSV_ABI AvPlayer::DeallocateTexture(void* handle, void* memory) { + const auto* const self = reinterpret_cast(handle); + const auto deallocate = self->m_init_data_original.memory_replacement.deallocate_texture; + const auto ptr = self->m_init_data_original.memory_replacement.object_ptr; + return deallocate(ptr, memory); +} + +int PS4_SYSV_ABI AvPlayer::OpenFile(void* handle, const char* filename) { + auto const self = reinterpret_cast(handle); + std::lock_guard guard(self->m_file_io_mutex); + + const auto open = self->m_init_data_original.file_replacement.open; + const auto ptr = self->m_init_data_original.file_replacement.object_ptr; + return open(ptr, filename); +} + +int PS4_SYSV_ABI AvPlayer::CloseFile(void* handle) { + auto const self = reinterpret_cast(handle); + std::lock_guard guard(self->m_file_io_mutex); + + const auto close = self->m_init_data_original.file_replacement.close; + const auto ptr = self->m_init_data_original.file_replacement.object_ptr; + return close(ptr); +} + +int PS4_SYSV_ABI AvPlayer::ReadOffsetFile(void* handle, u8* buffer, u64 position, u32 length) { + auto const self = reinterpret_cast(handle); + std::lock_guard guard(self->m_file_io_mutex); + + const auto read_offset = self->m_init_data_original.file_replacement.readOffset; + const auto ptr = self->m_init_data_original.file_replacement.object_ptr; + return read_offset(ptr, buffer, position, length); +} + +u64 PS4_SYSV_ABI AvPlayer::SizeFile(void* handle) { + auto const self = reinterpret_cast(handle); + std::lock_guard guard(self->m_file_io_mutex); + + const auto size = self->m_init_data_original.file_replacement.size; + const auto ptr = self->m_init_data_original.file_replacement.object_ptr; + return size(ptr); +} + +AvPlayer::AvPlayer() : m_file_io_mutex(PTHREAD_MUTEX_ERRORCHECK, "SceAvPlayerFileIOLock") {} + +void AvPlayer::Init(const SceAvPlayerInitData& data, const ThreadPriorities& priorities) { + m_init_data = data; + m_init_data_original = data; + + m_init_data.memory_replacement.object_ptr = this; + m_init_data.memory_replacement.allocate = &AvPlayer::Allocate; + m_init_data.memory_replacement.deallocate = &AvPlayer::Deallocate; + m_init_data.memory_replacement.allocate_texture = &AvPlayer::AllocateTexture; + m_init_data.memory_replacement.deallocate_texture = &AvPlayer::DeallocateTexture; + if (data.file_replacement.open == nullptr || data.file_replacement.close == nullptr || + data.file_replacement.readOffset == nullptr || data.file_replacement.size == nullptr) { + m_init_data.file_replacement = {}; + } else { + m_init_data.file_replacement.object_ptr = this; + m_init_data.file_replacement.open = &AvPlayer::OpenFile; + m_init_data.file_replacement.close = &AvPlayer::CloseFile; + m_init_data.file_replacement.readOffset = &AvPlayer::ReadOffsetFile; + m_init_data.file_replacement.size = &AvPlayer::SizeFile; + } + + m_state = std::make_unique(m_init_data, priorities); +} + +s32 AvPlayer::PostInit(const SceAvPlayerPostInitData& data) { + m_post_init_data = data; + return ORBIS_OK; +} + +s32 AvPlayer::AddSource(std::string_view path) { + if (path.empty()) { + return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; + } + + if (AVPLAYER_IS_ERROR(m_state->AddSource(path, GetSourceType(path)))) { + return ORBIS_AVPLAYER_ERROR_OPERATION_FAILED; + } + + return ORBIS_OK; +} + +s32 AvPlayer::GetStreamCount() { + return m_state->GetStreamCount(); +} + +s32 AvPlayer::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info) { + if (AVPLAYER_IS_ERROR(m_state->GetStreamInfo(stream_index, info))) { + return ORBIS_AVPLAYER_ERROR_OPERATION_FAILED; + } + return ORBIS_OK; +} + +s32 AvPlayer::EnableStream(u32 stream_id) { + if (m_state == nullptr) { + return ORBIS_AVPLAYER_ERROR_OPERATION_FAILED; + } + return m_state->EnableStream(stream_id); +} + +s32 AvPlayer::Start() { + return m_state->Start(); +} + +bool AvPlayer::GetVideoData(SceAvPlayerFrameInfo& video_info) { + if (m_state == nullptr) { + return false; + } + return m_state->GetVideoData(video_info); +} + +bool AvPlayer::GetVideoData(SceAvPlayerFrameInfoEx& video_info) { + if (m_state == nullptr) { + return false; + } + return m_state->GetVideoData(video_info); +} + +bool AvPlayer::GetAudioData(SceAvPlayerFrameInfo& audio_info) { + if (m_state == nullptr) { + return false; + } + return m_state->GetAudioData(audio_info); +} + +bool AvPlayer::IsActive() { + if (m_state == nullptr) { + return false; + } + return m_state->IsActive(); +} + +u64 AvPlayer::CurrentTime() { + if (m_state == nullptr) { + return 0; + } + return m_state->CurrentTime(); +} + +s32 AvPlayer::Stop() { + if (m_state == nullptr || !m_state->Stop()) { + return ORBIS_AVPLAYER_ERROR_OPERATION_FAILED; + } + return ORBIS_OK; +} + +} // namespace Libraries::AvPlayer diff --git a/src/core/libraries/avplayer/avplayer_impl.h b/src/core/libraries/avplayer/avplayer_impl.h new file mode 100644 index 00000000..fe3abcd1 --- /dev/null +++ b/src/core/libraries/avplayer/avplayer_impl.h @@ -0,0 +1,65 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "avplayer.h" +#include "avplayer_data_streamer.h" +#include "avplayer_state.h" + +#include "core/libraries/kernel/thread_management.h" + +extern "C" { +#include +#include +} + +#include +#include + +namespace Libraries::AvPlayer { + +class AvPlayer { +public: + // Memory Replacement + static void* PS4_SYSV_ABI Allocate(void* handle, u32 alignment, u32 size); + static void PS4_SYSV_ABI Deallocate(void* handle, void* memory); + static void* PS4_SYSV_ABI AllocateTexture(void* handle, u32 alignment, u32 size); + static void PS4_SYSV_ABI DeallocateTexture(void* handle, void* memory); + + // File Replacement + static int PS4_SYSV_ABI OpenFile(void* handle, const char* filename); + static int PS4_SYSV_ABI CloseFile(void* handle); + static int PS4_SYSV_ABI ReadOffsetFile(void* handle, u8* buffer, u64 position, u32 length); + static u64 PS4_SYSV_ABI SizeFile(void* handle); + + AvPlayer(); + + void Init(const SceAvPlayerInitData& data, const ThreadPriorities& priorities); + + s32 PostInit(const SceAvPlayerPostInitData& data); + s32 AddSource(std::string_view filename); + s32 GetStreamCount(); + s32 GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info); + s32 EnableStream(u32 stream_id); + s32 Start(); + bool GetAudioData(SceAvPlayerFrameInfo& audio_info); + bool GetVideoData(SceAvPlayerFrameInfo& video_info); + bool GetVideoData(SceAvPlayerFrameInfoEx& video_info); + bool IsActive(); + u64 CurrentTime(); + s32 Stop(); + +private: + using ScePthreadMutex = Kernel::ScePthreadMutex; + + std::unique_ptr m_state{}; + SceAvPlayerInitData m_init_data{}; + SceAvPlayerInitData m_init_data_original{}; + SceAvPlayerPostInitData m_post_init_data{}; + PthreadMutex m_file_io_mutex{}; + + std::atomic_bool m_has_source{}; +}; + +} // namespace Libraries::AvPlayer diff --git a/src/core/libraries/avplayer/avplayer_source.cpp b/src/core/libraries/avplayer/avplayer_source.cpp new file mode 100644 index 00000000..c1dc6d57 --- /dev/null +++ b/src/core/libraries/avplayer/avplayer_source.cpp @@ -0,0 +1,658 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "avplayer_source.h" + +#include "avplayer_file_streamer.h" + +#include "common/singleton.h" +#include "core/file_sys/fs.h" +#include "core/libraries/kernel/time_management.h" + +#include + +extern "C" { +#include +#include +#include +#include +#include +} + +namespace Libraries::AvPlayer { + +using namespace Kernel; + +AvPlayerSource::AvPlayerSource(AvPlayerStateCallback& state) : m_state(state) {} + +AvPlayerSource::~AvPlayerSource() { + if (!m_video_frame_storage.empty()) { + m_memory_replacement.deallocate(m_memory_replacement.object_ptr, + m_video_frame_storage.data()); + } + if (!m_audio_frame_storage.empty()) { + m_memory_replacement.deallocate(m_memory_replacement.object_ptr, + m_audio_frame_storage.data()); + } + Stop(); +} + +s32 AvPlayerSource::Init(std::string_view path, SceAvPlayerMemAllocator& memory_replacement, + SceAvPlayerFileReplacement& file_replacement, ThreadPriorities& priorities, + SceAvPlayerSourceType source_type) { + if (m_avformat_context != nullptr) { + return -1; + } + + m_priorities = priorities; + m_memory_replacement = memory_replacement; + + m_avformat_context = avformat_alloc_context(); + if (m_avformat_context == nullptr) { + return -1; + } + if (file_replacement.open != nullptr) { + m_up_data_streamer = std::make_unique(file_replacement, path); + m_avformat_context->pb = m_up_data_streamer->GetContext(); + if (avformat_open_input(&m_avformat_context, nullptr, nullptr, nullptr) < 0) { + return -1; + } + } else { + const auto mnt = Common::Singleton::Instance(); + const auto filepath = mnt->GetHostPath(path); + if (AVPLAYER_IS_ERROR(avformat_open_input(&m_avformat_context, filepath.string().c_str(), + nullptr, nullptr))) { + return -1; + } + } + + return 0; +} + +bool AvPlayerSource::FindStreamInfo() { + if (m_avformat_context == nullptr) { + return false; + } + if (m_avformat_context->nb_streams > 0) { + return true; + } + return avformat_find_stream_info(m_avformat_context, nullptr) == 0; +} + +s32 AvPlayerSource::GetStreamCount() { + if (m_avformat_context == nullptr) { + return -1; + } + LOG_DEBUG(Lib_AvPlayer, "num streams: {}", m_avformat_context->nb_streams); + return m_avformat_context->nb_streams; +} + +static s32 CodecTypeToStreamType(AVMediaType codec_type) { + switch (codec_type) { + case AVMediaType::AVMEDIA_TYPE_VIDEO: + return SCE_AVPLAYER_VIDEO; + case AVMediaType::AVMEDIA_TYPE_AUDIO: + return SCE_AVPLAYER_AUDIO; + case AVMediaType::AVMEDIA_TYPE_SUBTITLE: + return SCE_AVPLAYER_TIMEDTEXT; + default: + LOG_ERROR(Lib_AvPlayer, "Unexpected AVMediaType {}", magic_enum::enum_name(codec_type)); + return -1; + } +} + +static f32 AVRationalToF32(AVRational rational) { + return f32(rational.num) / rational.den; +} + +s32 AvPlayerSource::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info) { + info = {}; + if (m_avformat_context == nullptr || stream_index >= m_avformat_context->nb_streams) { + return -1; + } + const auto p_stream = m_avformat_context->streams[stream_index]; + if (p_stream == nullptr || p_stream->codecpar == nullptr) { + return -1; + } + info.type = CodecTypeToStreamType(p_stream->codecpar->codec_type); + info.start_time = p_stream->start_time; + info.duration = p_stream->duration; + const auto p_lang_node = av_dict_get(p_stream->metadata, "language", nullptr, 0); + if (p_lang_node == nullptr) { + return -1; + } + LOG_DEBUG(Lib_AvPlayer, "Stream {} language = {}", stream_index, p_lang_node->value); + switch (info.type) { + case SCE_AVPLAYER_VIDEO: + LOG_DEBUG(Lib_AvPlayer, "Stream {} is a video stream.", stream_index); + info.details.video.aspect_ratio = AVRationalToF32(p_stream->codecpar->sample_aspect_ratio); + info.details.video.width = p_stream->codecpar->width; + info.details.video.height = p_stream->codecpar->height; + std::memcpy(info.details.video.language_code, p_lang_node->value, + std::min(strlen(p_lang_node->value), 3ull)); + break; + case SCE_AVPLAYER_AUDIO: + LOG_DEBUG(Lib_AvPlayer, "Stream {} is an audio stream.", stream_index); + info.details.audio.channel_count = p_stream->codecpar->ch_layout.nb_channels; + info.details.audio.sample_rate = p_stream->codecpar->sample_rate; + info.details.audio.size = 0; // sceAvPlayerGetStreamInfo() is expected to set this to 0 + std::memcpy(info.details.audio.language_code, p_lang_node->value, + std::min(strlen(p_lang_node->value), 3ull)); + break; + case SCE_AVPLAYER_TIMEDTEXT: + LOG_WARNING(Lib_AvPlayer, "Stream {} is a timedtext stream.", stream_index); + info.details.subs.font_size = 12; + info.details.subs.text_size = 12; + std::memcpy(info.details.subs.language_code, p_lang_node->value, + std::min(strlen(p_lang_node->value), 3ull)); + break; + default: + return -1; + } + return 0; +} + +static AVPixelFormat GetPreferredVideoFormat(AVCodecContext* s, const AVPixelFormat* fmt) { + auto curr = fmt; + while (*curr != AV_PIX_FMT_NONE) { + LOG_TRACE(Lib_AvPlayer, "Supported format: {}", magic_enum::enum_name(*fmt)); + if (*curr == AV_PIX_FMT_NV12) { + return AV_PIX_FMT_NV12; + } + ++curr; + } + return AV_PIX_FMT_NONE; +} + +s32 AvPlayerSource::EnableStream(u32 stream_index) { + if (m_avformat_context == nullptr || stream_index >= m_avformat_context->nb_streams) { + return -1; + } + const auto stream = m_avformat_context->streams[stream_index]; + const auto decoder = avcodec_find_decoder(stream->codecpar->codec_id); + if (decoder == nullptr) { + return -1; + } + switch (stream->codecpar->codec_type) { + case AVMediaType::AVMEDIA_TYPE_VIDEO: { + m_video_stream_index = stream_index; + m_video_codec_context = + AVCodecContextPtr(avcodec_alloc_context3(decoder), &ReleaseAVCodecContext); + if (avcodec_parameters_to_context(m_video_codec_context.get(), stream->codecpar) < 0) { + return -1; + } + if (avcodec_open2(m_video_codec_context.get(), decoder, nullptr) < 0) { + return -1; + } + LOG_INFO(Lib_AvPlayer, "Video stream {} enabled", stream_index); + break; + } + case AVMediaType::AVMEDIA_TYPE_AUDIO: { + m_audio_stream_index = stream_index; + m_audio_codec_context = + AVCodecContextPtr(avcodec_alloc_context3(decoder), &ReleaseAVCodecContext); + if (avcodec_parameters_to_context(m_audio_codec_context.get(), stream->codecpar) < 0) { + return -1; + } + if (avcodec_open2(m_audio_codec_context.get(), decoder, nullptr) < 0) { + return -1; + } + LOG_INFO(Lib_AvPlayer, "Audio stream {} enabled", stream_index); + break; + } + default: + LOG_WARNING(Lib_AvPlayer, "Unknown stream type {} for stream {}", + magic_enum::enum_name(stream->codecpar->codec_type), stream_index); + break; + } + return 0; +} + +u8* AvPlayerSource::GetVideoBuffer(AVFrame* frame) { + const auto size = (frame->width * frame->height * 3) / 2; + if (m_video_frame_storage.size() < size) { + if (!m_video_frame_storage.empty()) { + m_memory_replacement.deallocate(m_memory_replacement.object_ptr, + m_video_frame_storage.data()); + } + const auto ptr = reinterpret_cast( + m_memory_replacement.allocate(m_memory_replacement.object_ptr, frame->width, size)); + m_video_frame_storage = std::span(ptr, size); + } + return m_video_frame_storage.data(); +} + +u8* AvPlayerSource::GetAudioBuffer(AVFrame* frame) { + const auto size = frame->ch_layout.nb_channels * frame->nb_samples * sizeof(u16); + if (m_audio_frame_storage.size() < size) { + if (!m_audio_frame_storage.empty()) { + m_memory_replacement.deallocate(m_memory_replacement.object_ptr, + m_audio_frame_storage.data()); + } + const auto ptr = reinterpret_cast( + m_memory_replacement.allocate(m_memory_replacement.object_ptr, 0x40, size)); + m_audio_frame_storage = std::span(ptr, size); + } + return m_audio_frame_storage.data(); +} + +void AvPlayerSource::SetLooping(bool is_looping) { + m_is_looping = is_looping; +} + +std::optional AvPlayerSource::HasFrames(u32 num_frames) { + return m_video_frames.Size() > num_frames; +} + +s32 AvPlayerSource::Start() { + if (m_audio_codec_context == nullptr && m_video_codec_context == nullptr) { + return -1; + } + { + ThreadParameters demuxer_params{ + .p_user_data = this, + .thread_name = "AvPlayer_Demuxer", + .stack_size = 0x4000, + .priority = m_priorities.demuxer_priority, + .affinity = m_priorities.demuxer_affinity, + }; + m_demuxer_thread = CreateThread(&DemuxerThread, demuxer_params); + if (m_demuxer_thread == nullptr) { + return -1; + } + } + if (m_video_codec_context != nullptr) { + ThreadParameters video_decoder_params{ + .p_user_data = this, + .thread_name = "AvPlayer_VideoDecoder", + .stack_size = 0x4000, + .priority = m_priorities.video_decoder_priority, + .affinity = m_priorities.video_decoder_affinity, + }; + m_video_decoder_thread = CreateThread(&VideoDecoderThread, video_decoder_params); + if (m_video_decoder_thread == nullptr) { + return -1; + } + } + if (m_audio_codec_context != nullptr) { + ThreadParameters audio_decoder_params{ + .p_user_data = this, + .thread_name = "AvPlayer_AudioDecoder", + .stack_size = 0x4000, + .priority = m_priorities.audio_decoder_priority, + .affinity = m_priorities.audio_decoder_affinity, + }; + m_audio_decoder_thread = CreateThread(&AudioDecoderThread, audio_decoder_params); + if (m_audio_decoder_thread == nullptr) { + return -1; + } + } + m_start_time = std::chrono::high_resolution_clock::now(); + return 0; +} + +bool AvPlayerSource::Stop() { + m_is_stop = true; + + void* res = nullptr; + if (m_video_decoder_thread != nullptr) { + scePthreadJoin(m_video_decoder_thread, &res); + } + if (m_audio_decoder_thread != nullptr) { + scePthreadJoin(m_audio_decoder_thread, &res); + } + if (m_demuxer_thread != nullptr) { + scePthreadJoin(m_demuxer_thread, &res); + } + return true; +} + +bool AvPlayerSource::GetVideoData(SceAvPlayerFrameInfo& video_info) { + SceAvPlayerFrameInfoEx info{}; + if (!GetVideoData(info)) { + return false; + } + video_info = {}; + video_info.timestamp = u64(info.timestamp); + video_info.pData = reinterpret_cast(info.pData); + video_info.details.video.aspect_ratio = info.details.video.aspect_ratio; + video_info.details.video.width = info.details.video.width; + video_info.details.video.height = info.details.video.height; + return true; +} + +static void CopyNV12Data(u8* dst, const AVFrame& src) { + std::memcpy(dst, src.data[0], src.width * src.height); + std::memcpy(dst + src.width * src.height, src.data[1], (src.width * src.height) / 2); +} + +bool AvPlayerSource::GetVideoData(SceAvPlayerFrameInfoEx& video_info) { + while (m_video_frames.Size() == 0 && !m_is_eof) { + sceKernelUsleep(5000); + } + + auto frame = m_video_frames.Pop(); + if (!frame.has_value()) { + return false; + } + + auto& up_frame = *frame; + + const auto pkt_dts = u64(up_frame->pkt_dts) * 1000; + const auto stream = m_avformat_context->streams[m_video_stream_index.value()]; + const auto time_base = stream->time_base; + const auto den = time_base.den; + const auto num = time_base.num; + const auto timestamp = (num != 0 && den > 1) ? (pkt_dts * num) / den : pkt_dts; + + { + using namespace std::chrono; + auto elapsed_time = + duration_cast(high_resolution_clock::now() - m_start_time).count(); + while (elapsed_time < timestamp) { + sceKernelUsleep((timestamp - elapsed_time) * 1000); + elapsed_time = + duration_cast(high_resolution_clock::now() - m_start_time).count(); + } + } + + auto buffer = GetVideoBuffer(up_frame.get()); + + CopyNV12Data(buffer, *up_frame); + + video_info = {}; + video_info.timestamp = timestamp; + video_info.pData = buffer; + video_info.details.video.width = up_frame->width; + video_info.details.video.height = up_frame->height; + video_info.details.video.aspect_ratio = AVRationalToF32(up_frame->sample_aspect_ratio); + video_info.details.video.pitch = up_frame->linesize[0]; + video_info.details.video.luma_bit_depth = 8; + video_info.details.video.chroma_bit_depth = 8; + + m_last_video_timestamp = timestamp; + return true; +} + +bool AvPlayerSource::GetAudioData(SceAvPlayerFrameInfo& audio_info) { + while (m_audio_frames.Size() == 0 && !m_is_eof) { + sceKernelUsleep(5000); + } + + auto frame = m_audio_frames.Pop(); + if (!frame.has_value()) { + return false; + } + + const auto& up_frame = *frame; + + const auto pkt_dts = u64(up_frame->pkt_dts) * 1000; + const auto stream = m_avformat_context->streams[m_audio_stream_index.value()]; + const auto time_base = stream->time_base; + const auto den = time_base.den; + const auto num = time_base.num; + const auto timestamp = (num != 0 && den > 1) ? (pkt_dts * num) / den : pkt_dts; + + { + using namespace std::chrono; + auto elapsed_time = + duration_cast(high_resolution_clock::now() - m_start_time).count(); + while (elapsed_time < timestamp) { + sceKernelUsleep((timestamp - elapsed_time) * 1000); + elapsed_time = + duration_cast(high_resolution_clock::now() - m_start_time).count(); + } + } + + auto buffer = GetAudioBuffer(up_frame.get()); + const auto size = up_frame->ch_layout.nb_channels * up_frame->nb_samples * sizeof(u16); + std::memcpy(buffer, up_frame->data[0], size); + + audio_info = {}; + audio_info.timestamp = timestamp; + audio_info.pData = m_audio_frame_storage.data(); + audio_info.details.audio.size = u32(m_audio_frame_storage.size()); + audio_info.details.audio.channel_count = up_frame->ch_layout.nb_channels; + return true; +} + +u64 AvPlayerSource::CurrentTime() { + // using namespace std::chrono; + // return duration_cast(high_resolution_clock::now() - m_start_time).count(); + return m_last_video_timestamp; +} + +bool AvPlayerSource::IsActive() { + return !m_is_stop && (!m_is_eof || m_audio_packets.Size() != 0 || m_video_packets.Size() != 0 || + m_video_frames.Size() != 0 || m_audio_frames.Size() != 0); +} + +void AvPlayerSource::ReleaseAVPacket(AVPacket* packet) { + if (packet != nullptr) { + av_packet_free(&packet); + } +} + +void AvPlayerSource::ReleaseAVFrame(AVFrame* frame) { + if (frame != nullptr) { + av_frame_free(&frame); + } +} + +void AvPlayerSource::ReleaseAVCodecContext(AVCodecContext* context) { + if (context != nullptr) { + avcodec_free_context(&context); + } +} + +void AvPlayerSource::ReleaseSWRContext(SwrContext* context) { + if (context != nullptr) { + swr_free(&context); + } +} + +void AvPlayerSource::ReleaseSWSContext(SwsContext* context) { + if (context != nullptr) { + sws_freeContext(context); + } +} + +void* AvPlayerSource::DemuxerThread(void* opaque) { + LOG_TRACE(Lib_AvPlayer, "Demuxer Thread started"); + const auto self = reinterpret_cast(opaque); + if (!self->m_audio_stream_index.has_value() && !self->m_video_stream_index.has_value()) { + return nullptr; + } + + while (!self->m_is_stop) { + if (self->m_video_packets.Size() > 60) { + sceKernelUsleep(5000); + continue; + } + AVPacketPtr up_packet(av_packet_alloc(), &ReleaseAVPacket); + const auto res = av_read_frame(self->m_avformat_context, up_packet.get()); + if (res < 0) { + if (res == AVERROR_EOF) { + LOG_TRACE(Lib_AvPlayer, "EOF reached in demuxer"); + break; + } else { + LOG_ERROR(Lib_AvPlayer, "Could not read AV frame: error = {}", res); + self->m_state.OnError(); + scePthreadExit(0); + } + break; + } + if (up_packet->stream_index == self->m_video_stream_index) { + self->m_video_packets.Push(std::move(up_packet)); + } else if (up_packet->stream_index == self->m_audio_stream_index) { + self->m_audio_packets.Push(std::move(up_packet)); + } + } + + self->m_is_eof = true; + + void* res; + if (self->m_video_decoder_thread) { + scePthreadJoin(self->m_video_decoder_thread, &res); + } + if (self->m_audio_decoder_thread) { + scePthreadJoin(self->m_audio_decoder_thread, &res); + } + self->m_state.OnEOF(); + + LOG_TRACE(Lib_AvPlayer, "Demuxer Thread exited normaly"); + scePthreadExit(0); +} + +AvPlayerSource::AVFramePtr AvPlayerSource::ConvertVideoFrame(const AVFrame& frame) { + auto nv12_frame = AVFramePtr{av_frame_alloc(), &ReleaseAVFrame}; + nv12_frame->pts = frame.pts; + nv12_frame->pkt_dts = frame.pkt_dts; + nv12_frame->format = AV_PIX_FMT_NV12; + nv12_frame->width = frame.width; + nv12_frame->height = frame.height; + nv12_frame->sample_aspect_ratio = frame.sample_aspect_ratio; + + av_frame_get_buffer(nv12_frame.get(), 0); + + if (m_sws_context == nullptr) { + m_sws_context = + SWSContextPtr(sws_getContext(frame.width, frame.height, AVPixelFormat(frame.format), + frame.width, frame.height, AV_PIX_FMT_NV12, + SWS_FAST_BILINEAR, nullptr, nullptr, nullptr), + &ReleaseSWSContext); + } + const auto res = sws_scale(m_sws_context.get(), frame.data, frame.linesize, 0, frame.height, + nv12_frame->data, nv12_frame->linesize); + if (res < 0) { + LOG_ERROR(Lib_AvPlayer, "Could not convert to NV12: {}", av_err2str(res)); + return AVFramePtr{nullptr, &ReleaseAVFrame}; + } + return nv12_frame; +} + +void* AvPlayerSource::VideoDecoderThread(void* opaque) { + LOG_TRACE(Lib_AvPlayer, "Video Decoder Thread started"); + const auto self = reinterpret_cast(opaque); + + while ((!self->m_is_eof || self->m_video_packets.Size() != 0) && !self->m_is_stop) { + if (self->m_video_frames.Size() > 60 || self->m_video_packets.Size() == 0) { + sceKernelUsleep(5000); + continue; + } + const auto packet = self->m_video_packets.Pop(); + if (!packet.has_value()) { + continue; + } + auto res = avcodec_send_packet(self->m_video_codec_context.get(), packet->get()); + if (res < 0 && res != AVERROR(EAGAIN)) { + self->m_state.OnError(); + LOG_ERROR(Lib_AvPlayer, "Could not send packet to the video codec. Error = {}", + av_err2str(res)); + scePthreadExit(nullptr); + } + while (res >= 0) { + auto up_frame = AVFramePtr(av_frame_alloc(), &ReleaseAVFrame); + res = avcodec_receive_frame(self->m_video_codec_context.get(), up_frame.get()); + if (res < 0) { + if (res == AVERROR_EOF) { + LOG_TRACE(Lib_AvPlayer, "EOF reached in video decoder"); + scePthreadExit(nullptr); + } else if (res != AVERROR(EAGAIN)) { + LOG_ERROR(Lib_AvPlayer, + "Could not receive frame from the video codec. Error = {}", + av_err2str(res)); + self->m_state.OnError(); + scePthreadExit(nullptr); + } + } else { + LOG_TRACE(Lib_AvPlayer, "Producing Video Frame. Num Frames: {}", + self->m_video_frames.Size()); + if (up_frame->format != AV_PIX_FMT_NV12) { + self->m_video_frames.Push(self->ConvertVideoFrame(*up_frame)); + } else { + self->m_video_frames.Push(std::move(up_frame)); + } + } + } + } + + LOG_TRACE(Lib_AvPlayer, "Video Decoder Thread exited normaly"); + scePthreadExit(nullptr); +} + +AvPlayerSource::AVFramePtr AvPlayerSource::ConvertAudioFrame(const AVFrame& frame) { + auto pcm16_frame = AVFramePtr{av_frame_alloc(), &ReleaseAVFrame}; + pcm16_frame->pts = frame.pts; + pcm16_frame->pkt_dts = frame.pkt_dts; + pcm16_frame->format = AV_SAMPLE_FMT_S16; + pcm16_frame->ch_layout = frame.ch_layout; + pcm16_frame->sample_rate = frame.sample_rate; + + if (m_swr_context == nullptr) { + SwrContext* swr_context = nullptr; + AVChannelLayout in_ch_layout = frame.ch_layout; + AVChannelLayout out_ch_layout = frame.ch_layout; + swr_alloc_set_opts2(&swr_context, &out_ch_layout, AV_SAMPLE_FMT_S16, frame.sample_rate, + &in_ch_layout, AVSampleFormat(frame.format), frame.sample_rate, 0, + nullptr); + m_swr_context = SWRContextPtr(swr_context, &ReleaseSWRContext); + swr_init(m_swr_context.get()); + } + const auto res = swr_convert_frame(m_swr_context.get(), pcm16_frame.get(), &frame); + if (res < 0) { + LOG_ERROR(Lib_AvPlayer, "Could not convert to NV12: {}", av_err2str(res)); + return AVFramePtr{nullptr, &ReleaseAVFrame}; + } + return pcm16_frame; +} + +void* AvPlayerSource::AudioDecoderThread(void* opaque) { + LOG_TRACE(Lib_AvPlayer, "Audio Decoder Thread started"); + const auto self = reinterpret_cast(opaque); + + while ((!self->m_is_eof || self->m_audio_packets.Size() != 0) && !self->m_is_stop) { + if (self->m_audio_frames.Size() > 60 || self->m_audio_packets.Size() == 0) { + sceKernelUsleep(5000); + continue; + } + const auto packet = self->m_audio_packets.Pop(); + if (!packet.has_value()) { + continue; + } + auto res = avcodec_send_packet(self->m_audio_codec_context.get(), packet->get()); + if (res < 0 && res != AVERROR(EAGAIN)) { + self->m_state.OnError(); + LOG_ERROR(Lib_AvPlayer, "Could not send packet to the audio codec. Error = {}", + av_err2str(res)); + scePthreadExit(nullptr); + } + while (res >= 0) { + auto up_frame = AVFramePtr(av_frame_alloc(), &ReleaseAVFrame); + res = avcodec_receive_frame(self->m_audio_codec_context.get(), up_frame.get()); + if (res < 0) { + if (res == AVERROR_EOF) { + LOG_TRACE(Lib_AvPlayer, "EOF reached in audio decoder"); + scePthreadExit(nullptr); + } else if (res != AVERROR(EAGAIN)) { + self->m_state.OnError(); + LOG_ERROR(Lib_AvPlayer, + "Could not receive frame from the audio codec. Error = {}", + av_err2str(res)); + scePthreadExit(nullptr); + } + } else { + if (up_frame->format != AV_SAMPLE_FMT_S16) { + self->m_audio_frames.Push(self->ConvertAudioFrame(*up_frame)); + } else { + self->m_audio_frames.Push(std::move(up_frame)); + } + } + } + } + + LOG_TRACE(Lib_AvPlayer, "Audio Decoder Thread exited normaly"); + scePthreadExit(nullptr); +} + +} // namespace Libraries::AvPlayer diff --git a/src/core/libraries/avplayer/avplayer_source.h b/src/core/libraries/avplayer/avplayer_source.h new file mode 100644 index 00000000..81f77a62 --- /dev/null +++ b/src/core/libraries/avplayer/avplayer_source.h @@ -0,0 +1,169 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "avplayer.h" +#include "avplayer_common.h" +#include "avplayer_data_streamer.h" + +#include "common/types.h" +#include "core/libraries/kernel/thread_management.h" + +#include +#include +#include +#include + +struct AVCodecContext; +struct AVFormatContext; +struct AVFrame; +struct AVIOContext; +struct AVPacket; +struct SwrContext; +struct SwsContext; + +namespace Libraries::AvPlayer { + +class AvPlayerStateCallback { +public: + virtual ~AvPlayerStateCallback() = default; + + virtual void OnWarning(u32 id) = 0; + virtual void OnError() = 0; + virtual void OnEOF() = 0; +}; + +class FrameBuffer { +public: + FrameBuffer(const SceAvPlayerMemAllocator& memory_replacement, u32 align, u32 size) noexcept + : m_memory_replacement(memory_replacement), + m_data(Allocate(memory_replacement, align, size), size) {} + + ~FrameBuffer() { + if (!m_data.empty()) { + Deallocate(m_memory_replacement, m_data.data()); + m_data = {}; + } + } + + FrameBuffer(const FrameBuffer&) noexcept = delete; + FrameBuffer& operator=(const FrameBuffer&) noexcept = delete; + + FrameBuffer(FrameBuffer&& r) noexcept + : m_memory_replacement(r.m_memory_replacement), m_data(r.m_data) { + r.m_data = {}; + }; + + FrameBuffer& operator=(FrameBuffer&& r) noexcept { + m_memory_replacement = r.m_memory_replacement; + std::swap(m_data, r.m_data); + return *this; + } + + u8* GetBuffer() const noexcept { + return m_data.data(); + } + +private: + static u8* Allocate(const SceAvPlayerMemAllocator& memory_replacement, u32 align, u32 size) { + return reinterpret_cast( + memory_replacement.allocate(memory_replacement.object_ptr, align, size)); + } + + static void Deallocate(const SceAvPlayerMemAllocator& memory_replacement, void* ptr) { + memory_replacement.deallocate(memory_replacement.object_ptr, ptr); + } + + SceAvPlayerMemAllocator m_memory_replacement; + std::span m_data; +}; + +class AvPlayerSource { +public: + AvPlayerSource(AvPlayerStateCallback& state); + ~AvPlayerSource(); + + s32 Init(std::string_view path, SceAvPlayerMemAllocator& memory_replacement, + SceAvPlayerFileReplacement& file_replacement, ThreadPriorities& priorities, + SceAvPlayerSourceType source_type); + + bool FindStreamInfo(); + s32 GetStreamCount(); + s32 GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info); + s32 EnableStream(u32 stream_index); + void SetLooping(bool is_looping); + std::optional HasFrames(u32 num_frames); + s32 Start(); + bool Stop(); + bool GetAudioData(SceAvPlayerFrameInfo& audio_info); + bool GetVideoData(SceAvPlayerFrameInfo& video_info); + bool GetVideoData(SceAvPlayerFrameInfoEx& video_info); + u64 CurrentTime(); + bool IsActive(); + +private: + using ScePthread = Kernel::ScePthread; + + static void* PS4_SYSV_ABI DemuxerThread(void* opaque); + static void* PS4_SYSV_ABI VideoDecoderThread(void* opaque); + static void* PS4_SYSV_ABI AudioDecoderThread(void* opaque); + + static void ReleaseAVPacket(AVPacket* packet); + static void ReleaseAVFrame(AVFrame* frame); + static void ReleaseAVCodecContext(AVCodecContext* context); + static void ReleaseSWRContext(SwrContext* context); + static void ReleaseSWSContext(SwsContext* context); + + using AVPacketPtr = std::unique_ptr; + using AVFramePtr = std::unique_ptr; + using AVCodecContextPtr = std::unique_ptr; + using SWRContextPtr = std::unique_ptr; + using SWSContextPtr = std::unique_ptr; + + u8* GetVideoBuffer(AVFrame* frame); + u8* GetAudioBuffer(AVFrame* frame); + + AVFramePtr ConvertAudioFrame(const AVFrame& frame); + AVFramePtr ConvertVideoFrame(const AVFrame& frame); + + u64 m_last_video_timestamp{}; + + AvPlayerStateCallback& m_state; + + ThreadPriorities m_priorities; + SceAvPlayerMemAllocator m_memory_replacement; + + std::atomic_bool m_is_looping = false; + std::atomic_bool m_is_eof = false; + std::atomic_bool m_is_stop = false; + std::unique_ptr m_up_data_streamer; + + AVFormatContext* m_avformat_context{}; + + AvPlayerQueue m_audio_packets; + AvPlayerQueue m_video_packets; + + AvPlayerQueue m_audio_frames; + AvPlayerQueue m_video_frames; + + std::span m_video_frame_storage; + std::span m_audio_frame_storage; + + AVCodecContextPtr m_video_codec_context{nullptr, &ReleaseAVCodecContext}; + AVCodecContextPtr m_audio_codec_context{nullptr, &ReleaseAVCodecContext}; + + std::optional m_video_stream_index{}; + std::optional m_audio_stream_index{}; + + ScePthread m_demuxer_thread{}; + ScePthread m_video_decoder_thread{}; + ScePthread m_audio_decoder_thread{}; + + SWRContextPtr m_swr_context{nullptr, &ReleaseSWRContext}; + SWSContextPtr m_sws_context{nullptr, &ReleaseSWSContext}; + + std::chrono::high_resolution_clock::time_point m_start_time{}; +}; + +} // namespace Libraries::AvPlayer diff --git a/src/core/libraries/avplayer/avplayer_state.cpp b/src/core/libraries/avplayer/avplayer_state.cpp new file mode 100644 index 00000000..3048819e --- /dev/null +++ b/src/core/libraries/avplayer/avplayer_state.cpp @@ -0,0 +1,481 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "avplayer_file_streamer.h" +#include "avplayer_source.h" +#include "avplayer_state.h" + +#include "core/libraries/error_codes.h" +#include "core/libraries/kernel/time_management.h" + +#include + +namespace Libraries::AvPlayer { + +using namespace Kernel; + +void PS4_SYSV_ABI AvPlayerState::AutoPlayEventCallback(void* opaque, s32 event_id, s32 source_id, + void* event_data) { + auto const self = reinterpret_cast(opaque); + + if (event_id == SCE_AVPLAYER_STATE_READY) { + s32 video_stream_index = -1; + s32 audio_stream_index = -1; + s32 timedtext_stream_index = -1; + const s32 stream_count = self->GetStreamCount(); + if (AVPLAYER_IS_ERROR(stream_count)) { + return; + } + if (stream_count == 0) { + self->Stop(); + return; + } + for (u32 stream_index = 0; stream_index < stream_count; ++stream_index) { + SceAvPlayerStreamInfo info{}; + self->GetStreamInfo(stream_index, info); + + const std::string_view default_language( + reinterpret_cast(self->m_default_language)); + switch (info.type) { + case SCE_AVPLAYER_VIDEO: + if (video_stream_index == -1) { + if (default_language.empty()) { + video_stream_index = stream_index; + break; + } + if (default_language == + reinterpret_cast(info.details.video.language_code)) { + video_stream_index = stream_index; + } + } + break; + case SCE_AVPLAYER_AUDIO: + if (audio_stream_index == -1) { + if (default_language.empty()) { + audio_stream_index = stream_index; + break; + } + if (default_language == + reinterpret_cast(info.details.video.language_code)) { + audio_stream_index = stream_index; + } + } + break; + case SCE_AVPLAYER_TIMEDTEXT: + if (timedtext_stream_index == -1) { + if (default_language.empty()) { + timedtext_stream_index = stream_index; + break; + } + if (default_language == + reinterpret_cast(info.details.video.language_code)) { + timedtext_stream_index = stream_index; + } + } + break; + } + } + + if (video_stream_index != -1) { + self->EnableStream(video_stream_index); + } + if (audio_stream_index != -1) { + self->EnableStream(audio_stream_index); + } + if (timedtext_stream_index != -1) { + self->EnableStream(timedtext_stream_index); + } + self->Start(); + return; + } + + const auto callback = self->m_user_event_replacement.event_callback; + const auto ptr = self->m_user_event_replacement.object_ptr; + if (callback != nullptr) { + callback(ptr, event_id, 0, event_data); + } +} + +// Called inside GAME thread +AvPlayerState::AvPlayerState(const SceAvPlayerInitData& init_data, + const ThreadPriorities& priorities) + : m_event_handler_mutex(PTHREAD_MUTEX_ERRORCHECK, "SceAvPlayerEventHandler"), + m_state_machine_mutex(PTHREAD_MUTEX_ERRORCHECK, "SceAvPlayerStateMachine") { + if (init_data.event_replacement.event_callback == nullptr || init_data.auto_start) { + m_auto_start = true; + m_event_replacement.event_callback = &AvPlayerState::AutoPlayEventCallback; + m_event_replacement.object_ptr = this; + } else { + m_event_replacement = init_data.event_replacement; + } + m_user_event_replacement = init_data.event_replacement; + + const auto& memory_replacement = init_data.memory_replacement; + if (memory_replacement.allocate != nullptr && memory_replacement.deallocate != nullptr && + memory_replacement.allocate_texture != nullptr && + memory_replacement.deallocate_texture != nullptr) { + m_memory_replacement = memory_replacement; + } + + if (init_data.event_replacement.event_callback != nullptr) { + m_event_replacement = init_data.event_replacement; + m_auto_start = init_data.auto_start; + } else { + m_auto_start = true; + } + + m_file_replacement = init_data.file_replacement; + m_thread_priorities = priorities; + + if (init_data.default_language != nullptr) { + std::memcpy(m_default_language, init_data.default_language, sizeof(m_default_language)); + } + SetState(AvState::Initial); + StartControllerThread(); +} + +AvPlayerState::~AvPlayerState() { + m_event_queue.Clear(); + if (m_up_source && m_current_state == AvState::Play) { + m_up_source->Stop(); + OnPlaybackStateChanged(AvState::Stop); + } +} + +// Called inside GAME thread +s32 AvPlayerState::AddSource(std::string_view path, SceAvPlayerSourceType source_type) { + if (path.empty()) { + return -1; + } + + if (m_up_source) { + return -1; + } + + m_up_source = std::make_unique(*this); + if (AVPLAYER_IS_ERROR(m_up_source->Init(path, m_memory_replacement, m_file_replacement, + m_thread_priorities, source_type))) { + return -1; + } + AddSourceEvent(); + + return 0; +} + +// Called inside GAME thread +s32 AvPlayerState::GetStreamCount() { + return m_up_source->GetStreamCount(); +} + +// Called inside GAME thread +s32 AvPlayerState::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info) { + return m_up_source->GetStreamInfo(stream_index, info); +} + +// Called inside GAME thread +s32 AvPlayerState::Start() { + if (m_up_source == nullptr) { + return -1; + } + return m_up_source->Start(); +} + +void* AvPlayerState::AvControllerThread(void* p_user_data) { + AvPlayerState* self = reinterpret_cast(p_user_data); + while (self->m_quit.load() == 0) { + if (self->m_event_queue.Size() != 0) { + self->ProcessEvent(); + continue; + } + sceKernelUsleep(5000); + self->UpdateBufferingState(); + } + scePthreadExit(0); +} + +// Called inside GAME thread +void AvPlayerState::AddSourceEvent() { + SetState(AvState::AddingSource); + m_event_queue.Push(AvPlayerEvent{ + .event = AvEventType::AddSource, + }); +} + +// Called inside GAME thread +int AvPlayerState::StartControllerThread() { + m_quit.store(0); + + ThreadParameters params{ + .p_user_data = this, + .thread_name = "AvPlayer_Controller", + .stack_size = 0x4000, + .priority = m_thread_priority, + .affinity = m_thread_affinity, + }; + m_controller_thread = CreateThread(&AvControllerThread, params); + if (m_controller_thread == nullptr) { + return -1; + } + return 0; +} + +// Called inside GAME thread +s32 AvPlayerState::EnableStream(u32 stream_id) { + if (m_up_source == nullptr) { + return -1; + } + return m_up_source->EnableStream(stream_id); +} + +// Called inside GAME thread +bool AvPlayerState::Stop() { + if (m_up_source == nullptr) { + return false; + } + SetState(AvState::Stop); + OnPlaybackStateChanged(AvState::Stop); + return m_up_source->Stop(); +} + +bool AvPlayerState::GetVideoData(SceAvPlayerFrameInfo& video_info) { + if (m_up_source == nullptr) { + return false; + } + return m_up_source->GetVideoData(video_info); +} + +bool AvPlayerState::GetVideoData(SceAvPlayerFrameInfoEx& video_info) { + if (m_up_source == nullptr) { + return false; + } + return m_up_source->GetVideoData(video_info); +} + +bool AvPlayerState::GetAudioData(SceAvPlayerFrameInfo& audio_info) { + if (m_up_source == nullptr) { + return false; + } + return m_up_source->GetAudioData(audio_info); +} + +bool AvPlayerState::IsActive() { + if (m_up_source == nullptr) { + return false; + } + return m_current_state != AvState::Stop && m_current_state != AvState::Error && + m_up_source->IsActive(); +} + +u64 AvPlayerState::CurrentTime() { + if (m_up_source == nullptr) { + return 0; + } + return m_up_source->CurrentTime(); +} + +void AvPlayerState::OnWarning(u32 id) { + EmitEvent(SCE_AVPLAYER_WARNING_ID, &id); +} + +void AvPlayerState::OnError() { + SetState(AvState::Error); + OnPlaybackStateChanged(AvState::Error); +} + +void AvPlayerState::OnEOF() { + Stop(); +} + +// Called inside CONTROLLER thread +void AvPlayerState::OnPlaybackStateChanged(AvState state) { + switch (state) { + case AvState::Ready: { + EmitEvent(SCE_AVPLAYER_STATE_READY); + break; + } + case AvState::Play: { + EmitEvent(SCE_AVPLAYER_STATE_PLAY); + break; + } + case AvState::Stop: { + EmitEvent(SCE_AVPLAYER_STATE_STOP); + break; + } + case AvState::Pause: { + EmitEvent(SCE_AVPLAYER_STATE_PAUSE); + break; + } + case AvState::Buffering: { + EmitEvent(SCE_AVPLAYER_STATE_BUFFERING); + break; + } + default: + break; + } +} + +// Called inside CONTROLLER and GAME threads +bool AvPlayerState::SetState(AvState state) { + std::lock_guard guard(m_state_machine_mutex); + + if (!IsStateTransitionValid(state)) { + return false; + } + m_previous_state.store(m_current_state); + m_current_state.store(state); + return true; +} + +// Called inside CONTROLLER thread +std::optional AvPlayerState::OnBufferingCheckEvent(u32 num_frames) { + if (!m_up_source) { + return std::nullopt; + } + return m_up_source->HasFrames(num_frames); +} + +// Called inside CONTROLLER thread +void AvPlayerState::EmitEvent(SceAvPlayerEvents event_id, void* event_data) { + LOG_INFO(Lib_AvPlayer, "Sending event to the game: id = {}", magic_enum::enum_name(event_id)); + const auto callback = m_event_replacement.event_callback; + if (callback) { + const auto ptr = m_event_replacement.object_ptr; + callback(ptr, event_id, 0, event_data); + } +} + +// Called inside CONTROLLER thread +int AvPlayerState::ProcessEvent() { + if (m_current_state.load() == AvState::Jump) { + return -2; + } + + std::lock_guard guard(m_event_handler_mutex); + + auto event = m_event_queue.Pop(); + if (!event.has_value()) { + return -1; + } + switch (event->event) { + case AvEventType::RevertState: { + SetState(m_previous_state.load()); + break; + } + case AvEventType::AddSource: { + if (m_up_source->FindStreamInfo()) { + SetState(AvState::Ready); + OnPlaybackStateChanged(AvState::Ready); + } else { + OnWarning(ORBIS_AVPLAYER_ERROR_NOT_SUPPORTED); + SetState(AvState::Error); + } + break; + } + case AvEventType::Error: { + OnWarning(event->payload.error); + SetState(AvState::Error); + break; + } + default: + break; + } + + return 0; +} + +// Called inside CONTROLLER thread +int AvPlayerState::UpdateBufferingState() { + if (m_current_state == AvState::Buffering) { + const auto has_frames = OnBufferingCheckEvent(10); + if (!has_frames.has_value()) { + return -1; + } + if (has_frames.value()) { + const auto state = + m_previous_state >= AvState::C0x0B ? m_previous_state.load() : AvState::Play; + SetState(state); + OnPlaybackStateChanged(state); + } + } else if (m_current_state == AvState::Play) { + const auto has_frames = OnBufferingCheckEvent(0); + if (!has_frames.has_value()) { + return -1; + } + if (!has_frames.value()) { + SetState(AvState::Buffering); + OnPlaybackStateChanged(AvState::Buffering); + } + } + return 0; +} + +bool AvPlayerState::IsStateTransitionValid(AvState state) { + switch (state) { + case AvState::Play: { + switch (m_current_state.load()) { + case AvState::Stop: + case AvState::EndOfFile: + // case AvState::C0x08: + case AvState::Error: + return false; + default: + return true; + } + } + case AvState::Pause: { + switch (m_current_state.load()) { + case AvState::Stop: + case AvState::EndOfFile: + // case AvState::C0x08: + case AvState::Starting: + case AvState::Error: + return false; + default: + return true; + } + } + case AvState::Jump: { + switch (m_current_state.load()) { + case AvState::Stop: + case AvState::EndOfFile: + // case AvState::C0x08: + case AvState::TrickMode: + case AvState::Starting: + case AvState::Error: + return false; + default: + return true; + } + } + case AvState::TrickMode: { + switch (m_current_state.load()) { + case AvState::Stop: + case AvState::EndOfFile: + // case AvState::C0x08: + case AvState::Jump: + case AvState::Starting: + case AvState::Error: + return false; + default: + return true; + } + } + case AvState::Buffering: { + switch (m_current_state.load()) { + case AvState::Stop: + case AvState::EndOfFile: + case AvState::Pause: + // case AvState::C0x08: + case AvState::Starting: + case AvState::Error: + return false; + default: + return true; + } + } + default: + return true; + } +} + +} // namespace Libraries::AvPlayer diff --git a/src/core/libraries/avplayer/avplayer_state.h b/src/core/libraries/avplayer/avplayer_state.h new file mode 100644 index 00000000..fc822884 --- /dev/null +++ b/src/core/libraries/avplayer/avplayer_state.h @@ -0,0 +1,85 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "avplayer.h" +#include "avplayer_data_streamer.h" +#include "avplayer_source.h" + +#include "core/libraries/kernel/thread_management.h" + +#include + +namespace Libraries::AvPlayer { + +class Stream; +class AvDecoder; + +class AvPlayerState : public AvPlayerStateCallback { +public: + AvPlayerState(const SceAvPlayerInitData& init_data, const ThreadPriorities& priorities); + ~AvPlayerState(); + + s32 AddSource(std::string_view filename, SceAvPlayerSourceType source_type); + s32 GetStreamCount(); + s32 GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info); + s32 EnableStream(u32 stream_id); + s32 Start(); + bool Stop(); + bool GetAudioData(SceAvPlayerFrameInfo& audio_info); + bool GetVideoData(SceAvPlayerFrameInfo& video_info); + bool GetVideoData(SceAvPlayerFrameInfoEx& video_info); + bool IsActive(); + u64 CurrentTime(); + +private: + using ScePthreadMutex = Kernel::ScePthreadMutex; + using ScePthread = Kernel::ScePthread; + + // Event Replacement + static void PS4_SYSV_ABI AutoPlayEventCallback(void* handle, s32 event_id, s32 source_id, + void* event_data); + + void OnWarning(u32 id) override; + void OnError() override; + void OnEOF() override; + + void OnPlaybackStateChanged(AvState state); + std::optional OnBufferingCheckEvent(u32 num_frames); + + void EmitEvent(SceAvPlayerEvents event_id, void* event_data = nullptr); + bool SetState(AvState state); + + static void* PS4_SYSV_ABI AvControllerThread(void* p_user_data); + + void AddSourceEvent(); + int StartControllerThread(); + int ProcessEvent(); + int UpdateBufferingState(); + bool IsStateTransitionValid(AvState state); + + std::unique_ptr m_up_source; + + SceAvPlayerMemAllocator m_memory_replacement{}; + SceAvPlayerFileReplacement m_file_replacement{}; + SceAvPlayerEventReplacement m_event_replacement{}; + SceAvPlayerEventReplacement m_user_event_replacement{}; + ThreadPriorities m_thread_priorities{}; + bool m_auto_start{}; + u8 m_default_language[4]{}; + + std::atomic_int32_t m_quit; + std::atomic m_current_state; + std::atomic m_previous_state; + u32 m_thread_priority; + u32 m_thread_affinity; + std::atomic_uint32_t m_some_event_result{}; + + PthreadMutex m_state_machine_mutex{}; + PthreadMutex m_event_handler_mutex{}; + ScePthread m_controller_thread{}; + AvPlayerQueue m_event_queue{}; +}; + +} // namespace Libraries::AvPlayer diff --git a/src/core/libraries/error_codes.h b/src/core/libraries/error_codes.h index 5eabaaf6..74aeef67 100644 --- a/src/core/libraries/error_codes.h +++ b/src/core/libraries/error_codes.h @@ -457,5 +457,18 @@ constexpr int ORBIS_NP_TROPHY_ERROR_HANDLE_EXCEEDS_MAX = 0x80551624; constexpr int ORBIS_NP_TROPHY_ERROR_CONTEXT_ALREADY_EXISTS = 0x80551613; constexpr int ORBIS_NP_TROPHY_ERROR_CONTEXT_EXCEEDS_MAX = 0x80551622; +// AvPlayer library +constexpr int ORBIS_AVPLAYER_ERROR_INVALID_PARAMS = 0x806A0001; +constexpr int ORBIS_AVPLAYER_ERROR_OPERATION_FAILED = 0x806A0002; +constexpr int ORBIS_AVPLAYER_ERROR_NO_MEMORY = 0x806A0003; +constexpr int ORBIS_AVPLAYER_ERROR_NOT_SUPPORTED = 0x806A0004; +constexpr int ORBIS_AVPLAYER_ERROR_WAR_FILE_NONINTERLEAVED = 0x806A00A0; +constexpr int ORBIS_AVPLAYER_ERROR_WAR_LOOPING_BACK = 0x806A00A1; +constexpr int ORBIS_AVPLAYER_ERROR_WAR_JUMP_COMPLETE = 0x806A00A3; +constexpr int ORBIS_AVPLAYER_ERROR_INFO_MARLIN_ENCRY = 0x806A00B0; +constexpr int ORBIS_AVPLAYER_ERROR_INFO_PLAYREADY_ENCRY = 0x806A00B4; +constexpr int ORBIS_AVPLAYER_ERROR_INFO_AES_ENCRY = 0x806A00B5; +constexpr int ORBIS_AVPLAYER_ERROR_INFO_OTHER_ENCRY = 0x806A00BF; + // AppContent library constexpr int ORBIS_APP_CONTENT_ERROR_PARAMETER = 0x80D90002; diff --git a/src/core/libraries/kernel/thread_management.h b/src/core/libraries/kernel/thread_management.h index c5935275..bd555ccc 100644 --- a/src/core/libraries/kernel/thread_management.h +++ b/src/core/libraries/kernel/thread_management.h @@ -158,19 +158,24 @@ void init_pthreads(); void pthreadInitSelfMainThread(); int PS4_SYSV_ABI scePthreadAttrInit(ScePthreadAttr* attr); +int PS4_SYSV_ABI scePthreadAttrDestroy(ScePthreadAttr* attr); int PS4_SYSV_ABI scePthreadAttrSetdetachstate(ScePthreadAttr* attr, int detachstate); int PS4_SYSV_ABI scePthreadAttrSetinheritsched(ScePthreadAttr* attr, int inheritSched); int PS4_SYSV_ABI scePthreadAttrSetschedparam(ScePthreadAttr* attr, const SceKernelSchedParam* param); int PS4_SYSV_ABI scePthreadAttrSetschedpolicy(ScePthreadAttr* attr, int policy); -ScePthread PS4_SYSV_ABI scePthreadSelf(); int PS4_SYSV_ABI scePthreadAttrSetaffinity(ScePthreadAttr* pattr, const /*SceKernelCpumask*/ u64 mask); +int PS4_SYSV_ABI scePthreadAttrSetstacksize(ScePthreadAttr* attr, size_t stack_size); + +ScePthread PS4_SYSV_ABI scePthreadSelf(); int PS4_SYSV_ABI scePthreadSetaffinity(ScePthread thread, const /*SceKernelCpumask*/ u64 mask); int PS4_SYSV_ABI scePthreadGetaffinity(ScePthread thread, /*SceKernelCpumask*/ u64* mask); int PS4_SYSV_ABI scePthreadCreate(ScePthread* thread, const ScePthreadAttr* attr, PthreadEntryFunc start_routine, void* arg, const char* name); - +[[noreturn]] void PS4_SYSV_ABI scePthreadExit(void* value_ptr); +int PS4_SYSV_ABI scePthreadJoin(ScePthread thread, void** res); +int PS4_SYSV_ABI scePthreadGetprio(ScePthread thread, int* prio); int PS4_SYSV_ABI scePthreadSetprio(ScePthread thread, int prio); /*** @@ -178,11 +183,14 @@ int PS4_SYSV_ABI scePthreadSetprio(ScePthread thread, int prio); */ int PS4_SYSV_ABI scePthreadMutexInit(ScePthreadMutex* mutex, const ScePthreadMutexattr* attr, const char* name); +int PS4_SYSV_ABI scePthreadMutexDestroy(ScePthreadMutex* mutex); +int PS4_SYSV_ABI scePthreadMutexLock(ScePthreadMutex* mutex); +int PS4_SYSV_ABI scePthreadMutexUnlock(ScePthreadMutex* mutex); + +int PS4_SYSV_ABI scePthreadMutexattrDestroy(ScePthreadMutexattr* attr); int PS4_SYSV_ABI scePthreadMutexattrInit(ScePthreadMutexattr* attr); int PS4_SYSV_ABI scePthreadMutexattrSettype(ScePthreadMutexattr* attr, int type); int PS4_SYSV_ABI scePthreadMutexattrSetprotocol(ScePthreadMutexattr* attr, int protocol); -int PS4_SYSV_ABI scePthreadMutexLock(ScePthreadMutex* mutex); -int PS4_SYSV_ABI scePthreadMutexUnlock(ScePthreadMutex* mutex); /**** * Cond calls */ diff --git a/src/core/libraries/kernel/time_management.h b/src/core/libraries/kernel/time_management.h index a28f8c13..8934171d 100644 --- a/src/core/libraries/kernel/time_management.h +++ b/src/core/libraries/kernel/time_management.h @@ -50,6 +50,7 @@ u64 PS4_SYSV_ABI sceKernelGetProcessTime(); u64 PS4_SYSV_ABI sceKernelGetProcessTimeCounter(); u64 PS4_SYSV_ABI sceKernelGetProcessTimeCounterFrequency(); u64 PS4_SYSV_ABI sceKernelReadTsc(); +int PS4_SYSV_ABI sceKernelUsleep(u32 microseconds); void timeSymbolsRegister(Core::Loader::SymbolsResolver* sym); From e33ff10212113a4756d88f125a325e1a55168f68 Mon Sep 17 00:00:00 2001 From: Vladislav Mikhalin Date: Mon, 12 Aug 2024 19:01:02 +0300 Subject: [PATCH 06/29] Added some logs, fixed some crashes, fixed align. --- src/audio_core/sdl_audio.cpp | 2 +- src/core/libraries/avplayer/avplayer.cpp | 16 +- src/core/libraries/avplayer/avplayer.h | 2 + .../avplayer/avplayer_file_streamer.cpp | 33 +- .../avplayer/avplayer_file_streamer.h | 5 +- src/core/libraries/avplayer/avplayer_impl.cpp | 18 +- src/core/libraries/avplayer/avplayer_impl.h | 6 +- .../libraries/avplayer/avplayer_source.cpp | 406 ++++++++++-------- src/core/libraries/avplayer/avplayer_source.h | 67 +-- .../libraries/avplayer/avplayer_state.cpp | 160 +++---- src/core/libraries/avplayer/avplayer_state.h | 10 +- 11 files changed, 398 insertions(+), 327 deletions(-) diff --git a/src/audio_core/sdl_audio.cpp b/src/audio_core/sdl_audio.cpp index 141d338e..aac67d8c 100644 --- a/src/audio_core/sdl_audio.cpp +++ b/src/audio_core/sdl_audio.cpp @@ -100,7 +100,7 @@ s32 SDLAudio::AudioOutOutput(s32 handle, const void* ptr) { int result = SDL_PutAudioStreamData(port.stream, ptr, port.samples_num * port.sample_size * port.channels_num); // TODO find a correct value 8192 is estimated - while (SDL_GetAudioStreamAvailable(port.stream) > 8192) { + while (SDL_GetAudioStreamAvailable(port.stream) > 65536) { SDL_Delay(0); } diff --git a/src/core/libraries/avplayer/avplayer.cpp b/src/core/libraries/avplayer/avplayer.cpp index 41f8d076..e8e99bca 100644 --- a/src/core/libraries/avplayer/avplayer.cpp +++ b/src/core/libraries/avplayer/avplayer.cpp @@ -9,7 +9,9 @@ #include "core/libraries/kernel/thread_management.h" #include "core/libraries/libs.h" -#include +#include // std::max, std::min + +#include // va_list namespace Libraries::AvPlayer { @@ -148,9 +150,7 @@ SceAvPlayerHandle PS4_SYSV_ABI sceAvPlayerInit(SceAvPlayerInitData* data) { // priorities.file_streaming_priority = GetPriority(priorities.http_streaming_priority, 15); // priorities.maxPriority = priorities.http_streaming_priority; - const auto player = new AvPlayer(); - player->Init(*data, priorities); - return player; + return new AvPlayer(*data, priorities); } s32 PS4_SYSV_ABI sceAvPlayerInitEx(const SceAvPlayerInitDataEx* p_data, @@ -225,9 +225,7 @@ s32 PS4_SYSV_ABI sceAvPlayerInitEx(const SceAvPlayerInitDataEx* p_data, // } // priorities.http_streaming_affinity = p_data->http_streaming_affinity; - const auto player = new AvPlayer(); - player->Init(data, priorities); - *p_player = player; + *p_player = new AvPlayer(data, priorities); return ORBIS_OK; } @@ -290,13 +288,13 @@ s32 PS4_SYSV_ABI sceAvPlayerSetAvSyncMode(SceAvPlayerHandle handle, return ORBIS_OK; } -s32 PS4_SYSV_ABI sceAvPlayerSetLogCallback(SceAvPlayerLogCallback logCb, void* user_data) { +s32 PS4_SYSV_ABI sceAvPlayerSetLogCallback(SceAvPlayerLogCallback log_cb, void* user_data) { LOG_ERROR(Lib_AvPlayer, "(STUBBED) called"); return ORBIS_OK; } s32 PS4_SYSV_ABI sceAvPlayerSetLooping(SceAvPlayerHandle handle, bool loop_flag) { - LOG_ERROR(Lib_AvPlayer, "(STUBBED) called"); + LOG_ERROR(Lib_AvPlayer, "(STUBBED) called, looping = {}", loop_flag); if (handle == nullptr) { return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; } diff --git a/src/core/libraries/avplayer/avplayer.h b/src/core/libraries/avplayer/avplayer.h index f5589441..e88419c1 100644 --- a/src/core/libraries/avplayer/avplayer.h +++ b/src/core/libraries/avplayer/avplayer.h @@ -5,6 +5,8 @@ #include "common/types.h" +#include // size_t + namespace Core::Loader { class SymbolsResolver; } diff --git a/src/core/libraries/avplayer/avplayer_file_streamer.cpp b/src/core/libraries/avplayer/avplayer_file_streamer.cpp index 1c54c454..43055fd5 100644 --- a/src/core/libraries/avplayer/avplayer_file_streamer.cpp +++ b/src/core/libraries/avplayer/avplayer_file_streamer.cpp @@ -12,23 +12,30 @@ extern "C" { #include } +#include // std::max, std::min + #define AVPLAYER_AVIO_BUFFER_SIZE 4096 namespace Libraries::AvPlayer { -AvPlayerFileStreamer::AvPlayerFileStreamer(SceAvPlayerFileReplacement& file_replacement, +AvPlayerFileStreamer::AvPlayerFileStreamer(const SceAvPlayerFileReplacement& file_replacement, std::string_view path) : m_file_replacement(file_replacement) { - Init(path); + const auto ptr = m_file_replacement.object_ptr; + m_fd = m_file_replacement.open(ptr, path.data()); + ASSERT(m_fd >= 0); + m_file_size = m_file_replacement.size(ptr); + // avio_buffer is deallocated in `avio_context_free` + const auto avio_buffer = reinterpret_cast(av_malloc(AVPLAYER_AVIO_BUFFER_SIZE)); + m_avio_context = + avio_alloc_context(avio_buffer, AVPLAYER_AVIO_BUFFER_SIZE, 0, this, + &AvPlayerFileStreamer::ReadPacket, nullptr, &AvPlayerFileStreamer::Seek); } AvPlayerFileStreamer::~AvPlayerFileStreamer() { if (m_avio_context != nullptr) { avio_context_free(&m_avio_context); } - if (m_avio_buffer != nullptr) { - av_free(m_avio_buffer); - } if (m_file_replacement.close != nullptr && m_fd >= 0) { const auto close = m_file_replacement.close; const auto ptr = m_file_replacement.object_ptr; @@ -36,20 +43,6 @@ AvPlayerFileStreamer::~AvPlayerFileStreamer() { } } -s32 AvPlayerFileStreamer::Init(std::string_view path) { - const auto ptr = m_file_replacement.object_ptr; - m_fd = m_file_replacement.open(ptr, path.data()); - if (m_fd < 0) { - return -1; - } - m_file_size = m_file_replacement.size(ptr); - m_avio_buffer = reinterpret_cast(av_malloc(AVPLAYER_AVIO_BUFFER_SIZE)); - m_avio_context = - avio_alloc_context(m_avio_buffer, AVPLAYER_AVIO_BUFFER_SIZE, 0, this, - &AvPlayerFileStreamer::ReadPacket, nullptr, &AvPlayerFileStreamer::Seek); - return 0; -} - s32 AvPlayerFileStreamer::ReadPacket(void* opaque, u8* buffer, s32 size) { const auto self = reinterpret_cast(opaque); if (self->m_position >= self->m_file_size) { @@ -61,7 +54,7 @@ s32 AvPlayerFileStreamer::ReadPacket(void* opaque, u8* buffer, s32 size) { const auto read_offset = self->m_file_replacement.readOffset; const auto ptr = self->m_file_replacement.object_ptr; const auto bytes_read = read_offset(ptr, buffer, self->m_position, size); - if (size != 0 && bytes_read == 0) { + if (bytes_read == 0 && size != 0) { return AVERROR_EOF; } self->m_position += bytes_read; diff --git a/src/core/libraries/avplayer/avplayer_file_streamer.h b/src/core/libraries/avplayer/avplayer_file_streamer.h index 9f1442f9..658ce8c1 100644 --- a/src/core/libraries/avplayer/avplayer_file_streamer.h +++ b/src/core/libraries/avplayer/avplayer_file_streamer.h @@ -15,7 +15,7 @@ namespace Libraries::AvPlayer { class AvPlayerFileStreamer : public IDataStreamer { public: - AvPlayerFileStreamer(SceAvPlayerFileReplacement& file_replacement, std::string_view path); + AvPlayerFileStreamer(const SceAvPlayerFileReplacement& file_replacement, std::string_view path); ~AvPlayerFileStreamer(); AVIOContext* GetContext() override { @@ -23,8 +23,6 @@ public: } private: - s32 Init(std::string_view path); - static s32 ReadPacket(void* opaque, u8* buffer, s32 size); static s64 Seek(void* opaque, s64 buffer, int whence); @@ -33,7 +31,6 @@ private: int m_fd = -1; u64 m_position{}; u64 m_file_size{}; - u8* m_avio_buffer{}; AVIOContext* m_avio_context{}; }; diff --git a/src/core/libraries/avplayer/avplayer_impl.cpp b/src/core/libraries/avplayer/avplayer_impl.cpp index 58793da7..f08356bd 100644 --- a/src/core/libraries/avplayer/avplayer_impl.cpp +++ b/src/core/libraries/avplayer/avplayer_impl.cpp @@ -77,11 +77,9 @@ u64 PS4_SYSV_ABI AvPlayer::SizeFile(void* handle) { return size(ptr); } -AvPlayer::AvPlayer() : m_file_io_mutex(PTHREAD_MUTEX_ERRORCHECK, "SceAvPlayerFileIOLock") {} - -void AvPlayer::Init(const SceAvPlayerInitData& data, const ThreadPriorities& priorities) { - m_init_data = data; - m_init_data_original = data; +AvPlayer::AvPlayer(const SceAvPlayerInitData& data, const ThreadPriorities& priorities) + : m_file_io_mutex(PTHREAD_MUTEX_ERRORCHECK, "AvPlayer_FileIO"), m_init_data(data), + m_init_data_original(data) { m_init_data.memory_replacement.object_ptr = this; m_init_data.memory_replacement.allocate = &AvPlayer::Allocate; @@ -120,6 +118,9 @@ s32 AvPlayer::AddSource(std::string_view path) { } s32 AvPlayer::GetStreamCount() { + if (m_state == nullptr) { + return ORBIS_AVPLAYER_ERROR_OPERATION_FAILED; + } return m_state->GetStreamCount(); } @@ -130,11 +131,14 @@ s32 AvPlayer::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info) { return ORBIS_OK; } -s32 AvPlayer::EnableStream(u32 stream_id) { +s32 AvPlayer::EnableStream(u32 stream_index) { if (m_state == nullptr) { return ORBIS_AVPLAYER_ERROR_OPERATION_FAILED; } - return m_state->EnableStream(stream_id); + if (!m_state->EnableStream(stream_index)) { + return ORBIS_AVPLAYER_ERROR_OPERATION_FAILED; + } + return ORBIS_OK; } s32 AvPlayer::Start() { diff --git a/src/core/libraries/avplayer/avplayer_impl.h b/src/core/libraries/avplayer/avplayer_impl.h index fe3abcd1..98f959e6 100644 --- a/src/core/libraries/avplayer/avplayer_impl.h +++ b/src/core/libraries/avplayer/avplayer_impl.h @@ -33,15 +33,13 @@ public: static int PS4_SYSV_ABI ReadOffsetFile(void* handle, u8* buffer, u64 position, u32 length); static u64 PS4_SYSV_ABI SizeFile(void* handle); - AvPlayer(); - - void Init(const SceAvPlayerInitData& data, const ThreadPriorities& priorities); + AvPlayer(const SceAvPlayerInitData& data, const ThreadPriorities& priorities); s32 PostInit(const SceAvPlayerPostInitData& data); s32 AddSource(std::string_view filename); s32 GetStreamCount(); s32 GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info); - s32 EnableStream(u32 stream_id); + s32 EnableStream(u32 stream_index); s32 Start(); bool GetAudioData(SceAvPlayerFrameInfo& audio_info); bool GetVideoData(SceAvPlayerFrameInfo& video_info); diff --git a/src/core/libraries/avplayer/avplayer_source.cpp b/src/core/libraries/avplayer/avplayer_source.cpp index c1dc6d57..c3d3dc55 100644 --- a/src/core/libraries/avplayer/avplayer_source.cpp +++ b/src/core/libraries/avplayer/avplayer_source.cpp @@ -23,67 +23,47 @@ namespace Libraries::AvPlayer { using namespace Kernel; -AvPlayerSource::AvPlayerSource(AvPlayerStateCallback& state) : m_state(state) {} - -AvPlayerSource::~AvPlayerSource() { - if (!m_video_frame_storage.empty()) { - m_memory_replacement.deallocate(m_memory_replacement.object_ptr, - m_video_frame_storage.data()); - } - if (!m_audio_frame_storage.empty()) { - m_memory_replacement.deallocate(m_memory_replacement.object_ptr, - m_audio_frame_storage.data()); - } - Stop(); -} - -s32 AvPlayerSource::Init(std::string_view path, SceAvPlayerMemAllocator& memory_replacement, - SceAvPlayerFileReplacement& file_replacement, ThreadPriorities& priorities, - SceAvPlayerSourceType source_type) { - if (m_avformat_context != nullptr) { - return -1; - } - - m_priorities = priorities; - m_memory_replacement = memory_replacement; - - m_avformat_context = avformat_alloc_context(); - if (m_avformat_context == nullptr) { - return -1; - } - if (file_replacement.open != nullptr) { - m_up_data_streamer = std::make_unique(file_replacement, path); - m_avformat_context->pb = m_up_data_streamer->GetContext(); - if (avformat_open_input(&m_avformat_context, nullptr, nullptr, nullptr) < 0) { - return -1; - } +AvPlayerSource::AvPlayerSource(AvPlayerStateCallback& state, std::string_view path, + const SceAvPlayerInitData& init_data, ThreadPriorities& priorities, + SceAvPlayerSourceType source_type) + : m_state(state), m_priorities(priorities), m_memory_replacement(init_data.memory_replacement), + m_num_output_video_framebuffers(init_data.num_output_video_framebuffers) { + AVFormatContext* context = avformat_alloc_context(); + if (init_data.file_replacement.open != nullptr) { + m_up_data_streamer = + std::make_unique(init_data.file_replacement, path); + context->pb = m_up_data_streamer->GetContext(); + ASSERT(!AVPLAYER_IS_ERROR(avformat_open_input(&context, nullptr, nullptr, nullptr))); } else { const auto mnt = Common::Singleton::Instance(); const auto filepath = mnt->GetHostPath(path); - if (AVPLAYER_IS_ERROR(avformat_open_input(&m_avformat_context, filepath.string().c_str(), - nullptr, nullptr))) { - return -1; - } + ASSERT(!AVPLAYER_IS_ERROR( + avformat_open_input(&context, filepath.string().c_str(), nullptr, nullptr))); } + m_avformat_context = AVFormatContextPtr(context, &ReleaseAVFormatContext); +} - return 0; +AvPlayerSource::~AvPlayerSource() { + Stop(); } bool AvPlayerSource::FindStreamInfo() { if (m_avformat_context == nullptr) { + LOG_ERROR(Lib_AvPlayer, "Could not find stream info. NULL context."); return false; } if (m_avformat_context->nb_streams > 0) { return true; } - return avformat_find_stream_info(m_avformat_context, nullptr) == 0; + return avformat_find_stream_info(m_avformat_context.get(), nullptr) == 0; } s32 AvPlayerSource::GetStreamCount() { if (m_avformat_context == nullptr) { + LOG_ERROR(Lib_AvPlayer, "Could not get stream count. NULL context."); return -1; } - LOG_DEBUG(Lib_AvPlayer, "num streams: {}", m_avformat_context->nb_streams); + LOG_INFO(Lib_AvPlayer, "Stream Count: {}", m_avformat_context->nb_streams); return m_avformat_context->nb_streams; } @@ -108,70 +88,68 @@ static f32 AVRationalToF32(AVRational rational) { s32 AvPlayerSource::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info) { info = {}; if (m_avformat_context == nullptr || stream_index >= m_avformat_context->nb_streams) { + LOG_ERROR(Lib_AvPlayer, "Could not get stream {} info.", stream_index); return -1; } const auto p_stream = m_avformat_context->streams[stream_index]; if (p_stream == nullptr || p_stream->codecpar == nullptr) { + LOG_ERROR(Lib_AvPlayer, "Could not get stream {} info. NULL stream.", stream_index); return -1; } info.type = CodecTypeToStreamType(p_stream->codecpar->codec_type); info.start_time = p_stream->start_time; info.duration = p_stream->duration; const auto p_lang_node = av_dict_get(p_stream->metadata, "language", nullptr, 0); - if (p_lang_node == nullptr) { - return -1; + if (p_lang_node != nullptr) { + LOG_INFO(Lib_AvPlayer, "Stream {} language = {}", stream_index, p_lang_node->value); + } else { + LOG_WARNING(Lib_AvPlayer, "Stream {} language is unknown", stream_index); } - LOG_DEBUG(Lib_AvPlayer, "Stream {} language = {}", stream_index, p_lang_node->value); switch (info.type) { case SCE_AVPLAYER_VIDEO: - LOG_DEBUG(Lib_AvPlayer, "Stream {} is a video stream.", stream_index); + LOG_INFO(Lib_AvPlayer, "Stream {} is a video stream.", stream_index); info.details.video.aspect_ratio = AVRationalToF32(p_stream->codecpar->sample_aspect_ratio); info.details.video.width = p_stream->codecpar->width; info.details.video.height = p_stream->codecpar->height; - std::memcpy(info.details.video.language_code, p_lang_node->value, - std::min(strlen(p_lang_node->value), 3ull)); + if (p_lang_node != nullptr) { + std::memcpy(info.details.video.language_code, p_lang_node->value, + std::min(strlen(p_lang_node->value), 3ull)); + } break; case SCE_AVPLAYER_AUDIO: - LOG_DEBUG(Lib_AvPlayer, "Stream {} is an audio stream.", stream_index); + LOG_INFO(Lib_AvPlayer, "Stream {} is an audio stream.", stream_index); info.details.audio.channel_count = p_stream->codecpar->ch_layout.nb_channels; info.details.audio.sample_rate = p_stream->codecpar->sample_rate; info.details.audio.size = 0; // sceAvPlayerGetStreamInfo() is expected to set this to 0 - std::memcpy(info.details.audio.language_code, p_lang_node->value, - std::min(strlen(p_lang_node->value), 3ull)); + if (p_lang_node != nullptr) { + std::memcpy(info.details.audio.language_code, p_lang_node->value, + std::min(strlen(p_lang_node->value), 3ull)); + } break; case SCE_AVPLAYER_TIMEDTEXT: LOG_WARNING(Lib_AvPlayer, "Stream {} is a timedtext stream.", stream_index); info.details.subs.font_size = 12; info.details.subs.text_size = 12; - std::memcpy(info.details.subs.language_code, p_lang_node->value, - std::min(strlen(p_lang_node->value), 3ull)); + if (p_lang_node != nullptr) { + std::memcpy(info.details.subs.language_code, p_lang_node->value, + std::min(strlen(p_lang_node->value), 3ull)); + } break; default: + LOG_ERROR(Lib_AvPlayer, "Stream {} type is unknown: {}.", stream_index, info.type); return -1; } return 0; } -static AVPixelFormat GetPreferredVideoFormat(AVCodecContext* s, const AVPixelFormat* fmt) { - auto curr = fmt; - while (*curr != AV_PIX_FMT_NONE) { - LOG_TRACE(Lib_AvPlayer, "Supported format: {}", magic_enum::enum_name(*fmt)); - if (*curr == AV_PIX_FMT_NV12) { - return AV_PIX_FMT_NV12; - } - ++curr; - } - return AV_PIX_FMT_NONE; -} - -s32 AvPlayerSource::EnableStream(u32 stream_index) { +bool AvPlayerSource::EnableStream(u32 stream_index) { if (m_avformat_context == nullptr || stream_index >= m_avformat_context->nb_streams) { - return -1; + return false; } const auto stream = m_avformat_context->streams[stream_index]; const auto decoder = avcodec_find_decoder(stream->codecpar->codec_id); if (decoder == nullptr) { - return -1; + return false; } switch (stream->codecpar->codec_type) { case AVMediaType::AVMEDIA_TYPE_VIDEO: { @@ -179,10 +157,18 @@ s32 AvPlayerSource::EnableStream(u32 stream_index) { m_video_codec_context = AVCodecContextPtr(avcodec_alloc_context3(decoder), &ReleaseAVCodecContext); if (avcodec_parameters_to_context(m_video_codec_context.get(), stream->codecpar) < 0) { - return -1; + LOG_ERROR(Lib_AvPlayer, "Could not copy stream {} avcodec parameters to context.", + stream_index); + return false; } if (avcodec_open2(m_video_codec_context.get(), decoder, nullptr) < 0) { - return -1; + LOG_ERROR(Lib_AvPlayer, "Could not open avcodec for video stream {}.", stream_index); + return false; + } + const auto width = m_video_codec_context->width; + const auto size = (width * m_video_codec_context->height * 3) / 2; + for (u64 index = 0; index < m_num_output_video_framebuffers; ++index) { + m_video_buffers.Push(FrameBuffer(m_memory_replacement, 0x100, size)); } LOG_INFO(Lib_AvPlayer, "Video stream {} enabled", stream_index); break; @@ -192,10 +178,19 @@ s32 AvPlayerSource::EnableStream(u32 stream_index) { m_audio_codec_context = AVCodecContextPtr(avcodec_alloc_context3(decoder), &ReleaseAVCodecContext); if (avcodec_parameters_to_context(m_audio_codec_context.get(), stream->codecpar) < 0) { - return -1; + LOG_ERROR(Lib_AvPlayer, "Could not copy stream {} avcodec parameters to context.", + stream_index); + return false; } if (avcodec_open2(m_audio_codec_context.get(), decoder, nullptr) < 0) { - return -1; + LOG_ERROR(Lib_AvPlayer, "Could not open avcodec for audio stream {}.", stream_index); + return false; + } + const auto num_channels = m_audio_codec_context->ch_layout.nb_channels; + const auto align = num_channels * sizeof(u16); + const auto size = num_channels * sizeof(u16) * 1024; + for (u64 index = 0; index < 2; ++index) { + m_audio_buffers.Push(FrameBuffer(m_memory_replacement, 0x100, size)); } LOG_INFO(Lib_AvPlayer, "Audio stream {} enabled", stream_index); break; @@ -205,35 +200,7 @@ s32 AvPlayerSource::EnableStream(u32 stream_index) { magic_enum::enum_name(stream->codecpar->codec_type), stream_index); break; } - return 0; -} - -u8* AvPlayerSource::GetVideoBuffer(AVFrame* frame) { - const auto size = (frame->width * frame->height * 3) / 2; - if (m_video_frame_storage.size() < size) { - if (!m_video_frame_storage.empty()) { - m_memory_replacement.deallocate(m_memory_replacement.object_ptr, - m_video_frame_storage.data()); - } - const auto ptr = reinterpret_cast( - m_memory_replacement.allocate(m_memory_replacement.object_ptr, frame->width, size)); - m_video_frame_storage = std::span(ptr, size); - } - return m_video_frame_storage.data(); -} - -u8* AvPlayerSource::GetAudioBuffer(AVFrame* frame) { - const auto size = frame->ch_layout.nb_channels * frame->nb_samples * sizeof(u16); - if (m_audio_frame_storage.size() < size) { - if (!m_audio_frame_storage.empty()) { - m_memory_replacement.deallocate(m_memory_replacement.object_ptr, - m_audio_frame_storage.data()); - } - const auto ptr = reinterpret_cast( - m_memory_replacement.allocate(m_memory_replacement.object_ptr, 0x40, size)); - m_audio_frame_storage = std::span(ptr, size); - } - return m_audio_frame_storage.data(); + return true; } void AvPlayerSource::SetLooping(bool is_looping) { @@ -241,11 +208,12 @@ void AvPlayerSource::SetLooping(bool is_looping) { } std::optional AvPlayerSource::HasFrames(u32 num_frames) { - return m_video_frames.Size() > num_frames; + return m_video_frames.Size() > num_frames || m_is_eof; } s32 AvPlayerSource::Start() { if (m_audio_codec_context == nullptr && m_video_codec_context == nullptr) { + LOG_ERROR(Lib_AvPlayer, "Could not start playback. NULL context."); return -1; } { @@ -258,6 +226,7 @@ s32 AvPlayerSource::Start() { }; m_demuxer_thread = CreateThread(&DemuxerThread, demuxer_params); if (m_demuxer_thread == nullptr) { + LOG_ERROR(Lib_AvPlayer, "Could not create DEMUXER thread."); return -1; } } @@ -271,6 +240,7 @@ s32 AvPlayerSource::Start() { }; m_video_decoder_thread = CreateThread(&VideoDecoderThread, video_decoder_params); if (m_video_decoder_thread == nullptr) { + LOG_ERROR(Lib_AvPlayer, "Could not create VIDEO DECODER thread."); return -1; } } @@ -284,6 +254,7 @@ s32 AvPlayerSource::Start() { }; m_audio_decoder_thread = CreateThread(&AudioDecoderThread, audio_decoder_params); if (m_audio_decoder_thread == nullptr) { + LOG_ERROR(Lib_AvPlayer, "Could not create AUDIO DECODER thread."); return -1; } } @@ -292,6 +263,11 @@ s32 AvPlayerSource::Start() { } bool AvPlayerSource::Stop() { + if (m_is_stop) { + LOG_WARNING(Lib_AvPlayer, "Could not stop playback: already stopped."); + return false; + } + m_is_stop = true; void* res = nullptr; @@ -304,6 +280,18 @@ bool AvPlayerSource::Stop() { if (m_demuxer_thread != nullptr) { scePthreadJoin(m_demuxer_thread, &res); } + m_audio_packets.Clear(); + m_video_packets.Clear(); + m_audio_frames.Clear(); + m_video_frames.Clear(); + if (m_current_audio_frame.has_value()) { + m_audio_buffers.Push(std::move(m_current_audio_frame.value())); + m_current_audio_frame.reset(); + } + if (m_current_video_frame.has_value()) { + m_video_buffers.Push(std::move(m_current_video_frame.value())); + m_current_video_frame.reset(); + } return true; } @@ -333,44 +321,27 @@ bool AvPlayerSource::GetVideoData(SceAvPlayerFrameInfoEx& video_info) { auto frame = m_video_frames.Pop(); if (!frame.has_value()) { + LOG_WARNING(Lib_AvPlayer, "Could get video frame: no frames."); return false; } - auto& up_frame = *frame; - - const auto pkt_dts = u64(up_frame->pkt_dts) * 1000; - const auto stream = m_avformat_context->streams[m_video_stream_index.value()]; - const auto time_base = stream->time_base; - const auto den = time_base.den; - const auto num = time_base.num; - const auto timestamp = (num != 0 && den > 1) ? (pkt_dts * num) / den : pkt_dts; - { using namespace std::chrono; auto elapsed_time = duration_cast(high_resolution_clock::now() - m_start_time).count(); - while (elapsed_time < timestamp) { - sceKernelUsleep((timestamp - elapsed_time) * 1000); + while (elapsed_time < frame->info.timestamp) { + sceKernelUsleep((frame->info.timestamp - elapsed_time) * 1000); elapsed_time = duration_cast(high_resolution_clock::now() - m_start_time).count(); } } - auto buffer = GetVideoBuffer(up_frame.get()); - - CopyNV12Data(buffer, *up_frame); - - video_info = {}; - video_info.timestamp = timestamp; - video_info.pData = buffer; - video_info.details.video.width = up_frame->width; - video_info.details.video.height = up_frame->height; - video_info.details.video.aspect_ratio = AVRationalToF32(up_frame->sample_aspect_ratio); - video_info.details.video.pitch = up_frame->linesize[0]; - video_info.details.video.luma_bit_depth = 8; - video_info.details.video.chroma_bit_depth = 8; - - m_last_video_timestamp = timestamp; + // return the buffer to the queue + if (m_current_video_frame.has_value()) { + m_video_buffers.Push(std::move(m_current_video_frame.value())); + } + m_current_video_frame = std::move(frame->buffer); + video_info = frame->info; return true; } @@ -381,45 +352,38 @@ bool AvPlayerSource::GetAudioData(SceAvPlayerFrameInfo& audio_info) { auto frame = m_audio_frames.Pop(); if (!frame.has_value()) { + LOG_WARNING(Lib_AvPlayer, "Could get audio frame: no frames."); return false; } - const auto& up_frame = *frame; - - const auto pkt_dts = u64(up_frame->pkt_dts) * 1000; - const auto stream = m_avformat_context->streams[m_audio_stream_index.value()]; - const auto time_base = stream->time_base; - const auto den = time_base.den; - const auto num = time_base.num; - const auto timestamp = (num != 0 && den > 1) ? (pkt_dts * num) / den : pkt_dts; - { using namespace std::chrono; auto elapsed_time = duration_cast(high_resolution_clock::now() - m_start_time).count(); - while (elapsed_time < timestamp) { - sceKernelUsleep((timestamp - elapsed_time) * 1000); + while (elapsed_time < frame->info.timestamp) { + sceKernelUsleep((frame->info.timestamp - elapsed_time) * 1000); elapsed_time = duration_cast(high_resolution_clock::now() - m_start_time).count(); } } - auto buffer = GetAudioBuffer(up_frame.get()); - const auto size = up_frame->ch_layout.nb_channels * up_frame->nb_samples * sizeof(u16); - std::memcpy(buffer, up_frame->data[0], size); + // return the buffer to the queue + if (m_current_audio_frame.has_value()) { + m_audio_buffers.Push(std::move(m_current_audio_frame.value())); + } + m_current_audio_frame = std::move(frame->buffer); audio_info = {}; - audio_info.timestamp = timestamp; - audio_info.pData = m_audio_frame_storage.data(); - audio_info.details.audio.size = u32(m_audio_frame_storage.size()); - audio_info.details.audio.channel_count = up_frame->ch_layout.nb_channels; + audio_info.timestamp = frame->info.timestamp; + audio_info.pData = reinterpret_cast(frame->info.pData); + audio_info.details.audio.size = frame->info.details.audio.size; + audio_info.details.audio.channel_count = frame->info.details.audio.channel_count; return true; } u64 AvPlayerSource::CurrentTime() { - // using namespace std::chrono; - // return duration_cast(high_resolution_clock::now() - m_start_time).count(); - return m_last_video_timestamp; + using namespace std::chrono; + return duration_cast(high_resolution_clock::now() - m_start_time).count(); } bool AvPlayerSource::IsActive() { @@ -457,12 +421,19 @@ void AvPlayerSource::ReleaseSWSContext(SwsContext* context) { } } +void AvPlayerSource::ReleaseAVFormatContext(AVFormatContext* context) { + if (context != nullptr) { + avformat_close_input(&context); + } +} + void* AvPlayerSource::DemuxerThread(void* opaque) { - LOG_TRACE(Lib_AvPlayer, "Demuxer Thread started"); const auto self = reinterpret_cast(opaque); if (!self->m_audio_stream_index.has_value() && !self->m_video_stream_index.has_value()) { + LOG_WARNING(Lib_AvPlayer, "Could not start DEMUXER thread. No streams enabled."); return nullptr; } + LOG_INFO(Lib_AvPlayer, "Demuxer Thread started"); while (!self->m_is_stop) { if (self->m_video_packets.Size() > 60) { @@ -470,10 +441,10 @@ void* AvPlayerSource::DemuxerThread(void* opaque) { continue; } AVPacketPtr up_packet(av_packet_alloc(), &ReleaseAVPacket); - const auto res = av_read_frame(self->m_avformat_context, up_packet.get()); + const auto res = av_read_frame(self->m_avformat_context.get(), up_packet.get()); if (res < 0) { if (res == AVERROR_EOF) { - LOG_TRACE(Lib_AvPlayer, "EOF reached in demuxer"); + LOG_INFO(Lib_AvPlayer, "EOF reached in demuxer"); break; } else { LOG_ERROR(Lib_AvPlayer, "Could not read AV frame: error = {}", res); @@ -500,7 +471,7 @@ void* AvPlayerSource::DemuxerThread(void* opaque) { } self->m_state.OnEOF(); - LOG_TRACE(Lib_AvPlayer, "Demuxer Thread exited normaly"); + LOG_INFO(Lib_AvPlayer, "Demuxer Thread exited normaly"); scePthreadExit(0); } @@ -531,12 +502,47 @@ AvPlayerSource::AVFramePtr AvPlayerSource::ConvertVideoFrame(const AVFrame& fram return nv12_frame; } +Frame AvPlayerSource::PrepareVideoFrame(FrameBuffer buffer, const AVFrame& frame) { + ASSERT(frame.format == AV_PIX_FMT_NV12); + + auto p_buffer = buffer.GetBuffer(); + CopyNV12Data(p_buffer, frame); + + const auto pkt_dts = u64(frame.pkt_dts) * 1000; + const auto stream = m_avformat_context->streams[m_video_stream_index.value()]; + const auto time_base = stream->time_base; + const auto den = time_base.den; + const auto num = time_base.num; + const auto timestamp = (num != 0 && den > 1) ? (pkt_dts * num) / den : pkt_dts; + + return Frame{ + .buffer = std::move(buffer), + .info = + { + .pData = p_buffer, + .timestamp = timestamp, + .details = + { + .video = + { + .width = u32(frame.width), + .height = u32(frame.height), + .aspect_ratio = AVRationalToF32(frame.sample_aspect_ratio), + .pitch = u32(frame.linesize[0]), + .luma_bit_depth = 8, + .chroma_bit_depth = 8, + }, + }, + }, + }; +} + void* AvPlayerSource::VideoDecoderThread(void* opaque) { - LOG_TRACE(Lib_AvPlayer, "Video Decoder Thread started"); + LOG_INFO(Lib_AvPlayer, "Video Decoder Thread started"); const auto self = reinterpret_cast(opaque); while ((!self->m_is_eof || self->m_video_packets.Size() != 0) && !self->m_is_stop) { - if (self->m_video_frames.Size() > 60 || self->m_video_packets.Size() == 0) { + if (self->m_video_packets.Size() == 0) { sceKernelUsleep(5000); continue; } @@ -544,6 +550,7 @@ void* AvPlayerSource::VideoDecoderThread(void* opaque) { if (!packet.has_value()) { continue; } + auto res = avcodec_send_packet(self->m_video_codec_context.get(), packet->get()); if (res < 0 && res != AVERROR(EAGAIN)) { self->m_state.OnError(); @@ -552,11 +559,15 @@ void* AvPlayerSource::VideoDecoderThread(void* opaque) { scePthreadExit(nullptr); } while (res >= 0) { + if (self->m_video_buffers.Size() == 0 && !self->m_is_stop) { + sceKernelUsleep(5000); + continue; + } auto up_frame = AVFramePtr(av_frame_alloc(), &ReleaseAVFrame); res = avcodec_receive_frame(self->m_video_codec_context.get(), up_frame.get()); if (res < 0) { if (res == AVERROR_EOF) { - LOG_TRACE(Lib_AvPlayer, "EOF reached in video decoder"); + LOG_INFO(Lib_AvPlayer, "EOF reached in video decoder"); scePthreadExit(nullptr); } else if (res != AVERROR(EAGAIN)) { LOG_ERROR(Lib_AvPlayer, @@ -566,18 +577,26 @@ void* AvPlayerSource::VideoDecoderThread(void* opaque) { scePthreadExit(nullptr); } } else { - LOG_TRACE(Lib_AvPlayer, "Producing Video Frame. Num Frames: {}", - self->m_video_frames.Size()); - if (up_frame->format != AV_PIX_FMT_NV12) { - self->m_video_frames.Push(self->ConvertVideoFrame(*up_frame)); - } else { - self->m_video_frames.Push(std::move(up_frame)); + auto buffer = self->m_video_buffers.Pop(); + if (!buffer.has_value()) { + // Video buffers queue was cleared. This means that player was stopped. + break; } + if (up_frame->format != AV_PIX_FMT_NV12) { + const auto nv12_frame = self->ConvertVideoFrame(*up_frame); + self->m_video_frames.Push( + self->PrepareVideoFrame(std::move(buffer.value()), *nv12_frame)); + } else { + self->m_video_frames.Push( + self->PrepareVideoFrame(std::move(buffer.value()), *up_frame)); + } + LOG_TRACE(Lib_AvPlayer, "Produced Video Frame. Num Frames: {}", + self->m_video_frames.Size()); } } } - LOG_TRACE(Lib_AvPlayer, "Video Decoder Thread exited normaly"); + LOG_INFO(Lib_AvPlayer, "Video Decoder Thread exited normaly"); scePthreadExit(nullptr); } @@ -607,12 +626,45 @@ AvPlayerSource::AVFramePtr AvPlayerSource::ConvertAudioFrame(const AVFrame& fram return pcm16_frame; } +Frame AvPlayerSource::PrepareAudioFrame(FrameBuffer buffer, const AVFrame& frame) { + ASSERT(frame.format == AV_SAMPLE_FMT_S16); + ASSERT(frame.nb_samples <= 1024); + + auto p_buffer = buffer.GetBuffer(); + const auto size = frame.ch_layout.nb_channels * frame.nb_samples * sizeof(u16); + std::memcpy(p_buffer, frame.data[0], size); + + const auto pkt_dts = u64(frame.pkt_dts) * 1000; + const auto stream = m_avformat_context->streams[m_audio_stream_index.value()]; + const auto time_base = stream->time_base; + const auto den = time_base.den; + const auto num = time_base.num; + const auto timestamp = (num != 0 && den > 1) ? (pkt_dts * num) / den : pkt_dts; + + return Frame{ + .buffer = std::move(buffer), + .info = + { + .pData = p_buffer, + .timestamp = timestamp, + .details = + { + .audio = + { + .channel_count = u16(frame.ch_layout.nb_channels), + .size = u32(size), + }, + }, + }, + }; +} + void* AvPlayerSource::AudioDecoderThread(void* opaque) { - LOG_TRACE(Lib_AvPlayer, "Audio Decoder Thread started"); + LOG_INFO(Lib_AvPlayer, "Audio Decoder Thread started"); const auto self = reinterpret_cast(opaque); while ((!self->m_is_eof || self->m_audio_packets.Size() != 0) && !self->m_is_stop) { - if (self->m_audio_frames.Size() > 60 || self->m_audio_packets.Size() == 0) { + if (self->m_audio_packets.Size() == 0) { sceKernelUsleep(5000); continue; } @@ -628,11 +680,15 @@ void* AvPlayerSource::AudioDecoderThread(void* opaque) { scePthreadExit(nullptr); } while (res >= 0) { + if (self->m_audio_buffers.Size() == 0 && !self->m_is_stop) { + sceKernelUsleep(5000); + continue; + } auto up_frame = AVFramePtr(av_frame_alloc(), &ReleaseAVFrame); res = avcodec_receive_frame(self->m_audio_codec_context.get(), up_frame.get()); if (res < 0) { if (res == AVERROR_EOF) { - LOG_TRACE(Lib_AvPlayer, "EOF reached in audio decoder"); + LOG_INFO(Lib_AvPlayer, "EOF reached in audio decoder"); scePthreadExit(nullptr); } else if (res != AVERROR(EAGAIN)) { self->m_state.OnError(); @@ -642,16 +698,26 @@ void* AvPlayerSource::AudioDecoderThread(void* opaque) { scePthreadExit(nullptr); } } else { - if (up_frame->format != AV_SAMPLE_FMT_S16) { - self->m_audio_frames.Push(self->ConvertAudioFrame(*up_frame)); - } else { - self->m_audio_frames.Push(std::move(up_frame)); + auto buffer = self->m_audio_buffers.Pop(); + if (!buffer.has_value()) { + // Audio buffers queue was cleared. This means that player was stopped. + break; } + if (up_frame->format != AV_SAMPLE_FMT_S16) { + const auto pcm16_frame = self->ConvertAudioFrame(*up_frame); + self->m_audio_frames.Push( + self->PrepareAudioFrame(std::move(buffer.value()), *pcm16_frame)); + } else { + self->m_audio_frames.Push( + self->PrepareAudioFrame(std::move(buffer.value()), *up_frame)); + } + LOG_TRACE(Lib_AvPlayer, "Produced Audio Frame. Num Frames: {}", + self->m_audio_frames.Size()); } } } - LOG_TRACE(Lib_AvPlayer, "Audio Decoder Thread exited normaly"); + LOG_INFO(Lib_AvPlayer, "Audio Decoder Thread exited normaly"); scePthreadExit(nullptr); } diff --git a/src/core/libraries/avplayer/avplayer_source.h b/src/core/libraries/avplayer/avplayer_source.h index 81f77a62..67ad44d8 100644 --- a/src/core/libraries/avplayer/avplayer_source.h +++ b/src/core/libraries/avplayer/avplayer_source.h @@ -38,11 +38,13 @@ class FrameBuffer { public: FrameBuffer(const SceAvPlayerMemAllocator& memory_replacement, u32 align, u32 size) noexcept : m_memory_replacement(memory_replacement), - m_data(Allocate(memory_replacement, align, size), size) {} + m_data(Allocate(memory_replacement, align, size)) { + ASSERT_MSG(m_data, "Could not allocated frame buffer."); + } ~FrameBuffer() { - if (!m_data.empty()) { - Deallocate(m_memory_replacement, m_data.data()); + if (m_data != nullptr) { + Deallocate(m_memory_replacement, m_data); m_data = {}; } } @@ -52,17 +54,16 @@ public: FrameBuffer(FrameBuffer&& r) noexcept : m_memory_replacement(r.m_memory_replacement), m_data(r.m_data) { - r.m_data = {}; + r.m_data = nullptr; }; FrameBuffer& operator=(FrameBuffer&& r) noexcept { - m_memory_replacement = r.m_memory_replacement; std::swap(m_data, r.m_data); return *this; } u8* GetBuffer() const noexcept { - return m_data.data(); + return m_data; } private: @@ -75,23 +76,26 @@ private: memory_replacement.deallocate(memory_replacement.object_ptr, ptr); } - SceAvPlayerMemAllocator m_memory_replacement; - std::span m_data; + const SceAvPlayerMemAllocator& m_memory_replacement; + u8* m_data = nullptr; +}; + +struct Frame { + FrameBuffer buffer; + SceAvPlayerFrameInfoEx info; }; class AvPlayerSource { public: - AvPlayerSource(AvPlayerStateCallback& state); + AvPlayerSource(AvPlayerStateCallback& state, std::string_view path, + const SceAvPlayerInitData& init_data, ThreadPriorities& priorities, + SceAvPlayerSourceType source_type); ~AvPlayerSource(); - s32 Init(std::string_view path, SceAvPlayerMemAllocator& memory_replacement, - SceAvPlayerFileReplacement& file_replacement, ThreadPriorities& priorities, - SceAvPlayerSourceType source_type); - bool FindStreamInfo(); s32 GetStreamCount(); s32 GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info); - s32 EnableStream(u32 stream_index); + bool EnableStream(u32 stream_index); void SetLooping(bool is_looping); std::optional HasFrames(u32 num_frames); s32 Start(); @@ -114,52 +118,55 @@ private: static void ReleaseAVCodecContext(AVCodecContext* context); static void ReleaseSWRContext(SwrContext* context); static void ReleaseSWSContext(SwsContext* context); + static void ReleaseAVFormatContext(AVFormatContext* context); using AVPacketPtr = std::unique_ptr; using AVFramePtr = std::unique_ptr; using AVCodecContextPtr = std::unique_ptr; using SWRContextPtr = std::unique_ptr; using SWSContextPtr = std::unique_ptr; - - u8* GetVideoBuffer(AVFrame* frame); - u8* GetAudioBuffer(AVFrame* frame); + using AVFormatContextPtr = std::unique_ptr; AVFramePtr ConvertAudioFrame(const AVFrame& frame); AVFramePtr ConvertVideoFrame(const AVFrame& frame); - u64 m_last_video_timestamp{}; + Frame PrepareAudioFrame(FrameBuffer buffer, const AVFrame& frame); + Frame PrepareVideoFrame(FrameBuffer buffer, const AVFrame& frame); AvPlayerStateCallback& m_state; - ThreadPriorities m_priorities; - SceAvPlayerMemAllocator m_memory_replacement; + ThreadPriorities m_priorities{}; + SceAvPlayerMemAllocator m_memory_replacement{}; + u64 m_num_output_video_framebuffers{}; std::atomic_bool m_is_looping = false; std::atomic_bool m_is_eof = false; std::atomic_bool m_is_stop = false; + std::unique_ptr m_up_data_streamer; - AVFormatContext* m_avformat_context{}; + AvPlayerQueue m_audio_buffers; + AvPlayerQueue m_video_buffers; AvPlayerQueue m_audio_packets; AvPlayerQueue m_video_packets; - AvPlayerQueue m_audio_frames; - AvPlayerQueue m_video_frames; + AvPlayerQueue m_audio_frames; + AvPlayerQueue m_video_frames; - std::span m_video_frame_storage; - std::span m_audio_frame_storage; + std::optional m_current_video_frame; + std::optional m_current_audio_frame; - AVCodecContextPtr m_video_codec_context{nullptr, &ReleaseAVCodecContext}; - AVCodecContextPtr m_audio_codec_context{nullptr, &ReleaseAVCodecContext}; - - std::optional m_video_stream_index{}; - std::optional m_audio_stream_index{}; + std::optional m_video_stream_index{}; + std::optional m_audio_stream_index{}; ScePthread m_demuxer_thread{}; ScePthread m_video_decoder_thread{}; ScePthread m_audio_decoder_thread{}; + AVFormatContextPtr m_avformat_context{nullptr, &ReleaseAVFormatContext}; + AVCodecContextPtr m_video_codec_context{nullptr, &ReleaseAVCodecContext}; + AVCodecContextPtr m_audio_codec_context{nullptr, &ReleaseAVCodecContext}; SWRContextPtr m_swr_context{nullptr, &ReleaseSWRContext}; SWSContextPtr m_sws_context{nullptr, &ReleaseSWSContext}; diff --git a/src/core/libraries/avplayer/avplayer_state.cpp b/src/core/libraries/avplayer/avplayer_state.cpp index 3048819e..e7aa4571 100644 --- a/src/core/libraries/avplayer/avplayer_state.cpp +++ b/src/core/libraries/avplayer/avplayer_state.cpp @@ -39,38 +39,29 @@ void PS4_SYSV_ABI AvPlayerState::AutoPlayEventCallback(void* opaque, s32 event_i switch (info.type) { case SCE_AVPLAYER_VIDEO: if (video_stream_index == -1) { - if (default_language.empty()) { - video_stream_index = stream_index; - break; - } - if (default_language == - reinterpret_cast(info.details.video.language_code)) { - video_stream_index = stream_index; - } + video_stream_index = stream_index; + } + if (!default_language.empty() && + default_language == reinterpret_cast(info.details.video.language_code)) { + video_stream_index = stream_index; } break; case SCE_AVPLAYER_AUDIO: if (audio_stream_index == -1) { - if (default_language.empty()) { - audio_stream_index = stream_index; - break; - } - if (default_language == - reinterpret_cast(info.details.video.language_code)) { - audio_stream_index = stream_index; - } + audio_stream_index = stream_index; + } + if (!default_language.empty() && + default_language == reinterpret_cast(info.details.video.language_code)) { + audio_stream_index = stream_index; } break; case SCE_AVPLAYER_TIMEDTEXT: - if (timedtext_stream_index == -1) { - if (default_language.empty()) { - timedtext_stream_index = stream_index; - break; - } - if (default_language == - reinterpret_cast(info.details.video.language_code)) { - timedtext_stream_index = stream_index; - } + if (default_language.empty()) { + timedtext_stream_index = stream_index; + break; + } + if (default_language == reinterpret_cast(info.details.video.language_code)) { + timedtext_stream_index = stream_index; } break; } @@ -89,8 +80,9 @@ void PS4_SYSV_ABI AvPlayerState::AutoPlayEventCallback(void* opaque, s32 event_i return; } - const auto callback = self->m_user_event_replacement.event_callback; - const auto ptr = self->m_user_event_replacement.object_ptr; + // Pass other events to the game + const auto callback = self->m_event_replacement.event_callback; + const auto ptr = self->m_event_replacement.object_ptr; if (callback != nullptr) { callback(ptr, event_id, 0, event_data); } @@ -99,34 +91,15 @@ void PS4_SYSV_ABI AvPlayerState::AutoPlayEventCallback(void* opaque, s32 event_i // Called inside GAME thread AvPlayerState::AvPlayerState(const SceAvPlayerInitData& init_data, const ThreadPriorities& priorities) - : m_event_handler_mutex(PTHREAD_MUTEX_ERRORCHECK, "SceAvPlayerEventHandler"), - m_state_machine_mutex(PTHREAD_MUTEX_ERRORCHECK, "SceAvPlayerStateMachine") { - if (init_data.event_replacement.event_callback == nullptr || init_data.auto_start) { + : m_init_data(init_data), m_event_replacement(init_data.event_replacement), + m_thread_priorities(priorities), + m_event_handler_mutex(PTHREAD_MUTEX_ERRORCHECK, "AvPlayer_EventHandler"), + m_state_machine_mutex(PTHREAD_MUTEX_ERRORCHECK, "AvPlayer_StateMachine") { + if (m_event_replacement.event_callback == nullptr || init_data.auto_start) { m_auto_start = true; - m_event_replacement.event_callback = &AvPlayerState::AutoPlayEventCallback; - m_event_replacement.object_ptr = this; - } else { - m_event_replacement = init_data.event_replacement; + m_init_data.event_replacement.event_callback = &AvPlayerState::AutoPlayEventCallback; + m_init_data.event_replacement.object_ptr = this; } - m_user_event_replacement = init_data.event_replacement; - - const auto& memory_replacement = init_data.memory_replacement; - if (memory_replacement.allocate != nullptr && memory_replacement.deallocate != nullptr && - memory_replacement.allocate_texture != nullptr && - memory_replacement.deallocate_texture != nullptr) { - m_memory_replacement = memory_replacement; - } - - if (init_data.event_replacement.event_callback != nullptr) { - m_event_replacement = init_data.event_replacement; - m_auto_start = init_data.auto_start; - } else { - m_auto_start = true; - } - - m_file_replacement = init_data.file_replacement; - m_thread_priorities = priorities; - if (init_data.default_language != nullptr) { std::memcpy(m_default_language, init_data.default_language, sizeof(m_default_language)); } @@ -135,54 +108,67 @@ AvPlayerState::AvPlayerState(const SceAvPlayerInitData& init_data, } AvPlayerState::~AvPlayerState() { - m_event_queue.Clear(); if (m_up_source && m_current_state == AvState::Play) { m_up_source->Stop(); - OnPlaybackStateChanged(AvState::Stop); } + m_quit = true; + if (m_controller_thread != nullptr) { + void* res = nullptr; + scePthreadJoin(m_controller_thread, &res); + } + m_event_queue.Clear(); } // Called inside GAME thread s32 AvPlayerState::AddSource(std::string_view path, SceAvPlayerSourceType source_type) { if (path.empty()) { + LOG_ERROR(Lib_AvPlayer, "File path is empty."); return -1; } - if (m_up_source) { + if (m_up_source != nullptr) { + LOG_ERROR(Lib_AvPlayer, "Only one source is supported."); return -1; } - m_up_source = std::make_unique(*this); - if (AVPLAYER_IS_ERROR(m_up_source->Init(path, m_memory_replacement, m_file_replacement, - m_thread_priorities, source_type))) { - return -1; - } + m_up_source = std::make_unique(*this, path, m_init_data, m_thread_priorities, + source_type); AddSourceEvent(); - return 0; } // Called inside GAME thread s32 AvPlayerState::GetStreamCount() { + if (m_up_source == nullptr) { + LOG_ERROR(Lib_AvPlayer, "Could not get stream count. No source."); + return -1; + } return m_up_source->GetStreamCount(); } // Called inside GAME thread s32 AvPlayerState::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info) { + if (m_up_source == nullptr) { + LOG_ERROR(Lib_AvPlayer, "Could not get stream {} info. No source.", stream_index); + return -1; + } return m_up_source->GetStreamInfo(stream_index, info); } // Called inside GAME thread s32 AvPlayerState::Start() { - if (m_up_source == nullptr) { + if (m_up_source == nullptr || m_up_source->Start() < 0) { + LOG_ERROR(Lib_AvPlayer, "Could not start playback."); return -1; } - return m_up_source->Start(); + SetState(AvState::Play); + OnPlaybackStateChanged(AvState::Play); + return 0; } void* AvPlayerState::AvControllerThread(void* p_user_data) { AvPlayerState* self = reinterpret_cast(p_user_data); - while (self->m_quit.load() == 0) { + while (!self->m_quit) { if (self->m_event_queue.Size() != 0) { self->ProcessEvent(); continue; @@ -201,6 +187,16 @@ void AvPlayerState::AddSourceEvent() { }); } +void AvPlayerState::WarningEvent(s32 id) { + m_event_queue.Push(AvPlayerEvent{ + .event = AvEventType::WarningId, + .payload = + { + .error = id, + }, + }); +} + // Called inside GAME thread int AvPlayerState::StartControllerThread() { m_quit.store(0); @@ -214,25 +210,28 @@ int AvPlayerState::StartControllerThread() { }; m_controller_thread = CreateThread(&AvControllerThread, params); if (m_controller_thread == nullptr) { + LOG_ERROR(Lib_AvPlayer, "Could not create CONTROLLER thread."); return -1; } return 0; } // Called inside GAME thread -s32 AvPlayerState::EnableStream(u32 stream_id) { +bool AvPlayerState::EnableStream(u32 stream_index) { if (m_up_source == nullptr) { - return -1; + return false; } - return m_up_source->EnableStream(stream_id); + return m_up_source->EnableStream(stream_index); } // Called inside GAME thread bool AvPlayerState::Stop() { - if (m_up_source == nullptr) { + if (m_up_source == nullptr || m_current_state == AvState::Stop) { + return false; + } + if (!SetState(AvState::Stop)) { return false; } - SetState(AvState::Stop); OnPlaybackStateChanged(AvState::Stop); return m_up_source->Stop(); } @@ -268,13 +267,16 @@ bool AvPlayerState::IsActive() { u64 AvPlayerState::CurrentTime() { if (m_up_source == nullptr) { + LOG_ERROR(Lib_AvPlayer, "Could not get current time. No source."); return 0; } return m_up_source->CurrentTime(); } +// May be called from different threads void AvPlayerState::OnWarning(u32 id) { - EmitEvent(SCE_AVPLAYER_WARNING_ID, &id); + // Forward to CONTROLLER thread + WarningEvent(id); } void AvPlayerState::OnError() { @@ -282,9 +284,7 @@ void AvPlayerState::OnError() { OnPlaybackStateChanged(AvState::Error); } -void AvPlayerState::OnEOF() { - Stop(); -} +void AvPlayerState::OnEOF() {} // Called inside CONTROLLER thread void AvPlayerState::OnPlaybackStateChanged(AvState state) { @@ -319,6 +319,8 @@ bool AvPlayerState::SetState(AvState state) { std::lock_guard guard(m_state_machine_mutex); if (!IsStateTransitionValid(state)) { + LOG_ERROR(Lib_AvPlayer, "Invalid state transition: {} -> {}", + magic_enum::enum_name(m_current_state.load()), magic_enum::enum_name(state)); return false; } m_previous_state.store(m_current_state); @@ -337,16 +339,16 @@ std::optional AvPlayerState::OnBufferingCheckEvent(u32 num_frames) { // Called inside CONTROLLER thread void AvPlayerState::EmitEvent(SceAvPlayerEvents event_id, void* event_data) { LOG_INFO(Lib_AvPlayer, "Sending event to the game: id = {}", magic_enum::enum_name(event_id)); - const auto callback = m_event_replacement.event_callback; + const auto callback = m_init_data.event_replacement.event_callback; if (callback) { - const auto ptr = m_event_replacement.object_ptr; + const auto ptr = m_init_data.event_replacement.object_ptr; callback(ptr, event_id, 0, event_data); } } // Called inside CONTROLLER thread int AvPlayerState::ProcessEvent() { - if (m_current_state.load() == AvState::Jump) { + if (m_current_state == AvState::Jump) { return -2; } @@ -357,6 +359,10 @@ int AvPlayerState::ProcessEvent() { return -1; } switch (event->event) { + case AvEventType::WarningId: { + OnWarning(event->payload.error); + break; + } case AvEventType::RevertState: { SetState(m_previous_state.load()); break; diff --git a/src/core/libraries/avplayer/avplayer_state.h b/src/core/libraries/avplayer/avplayer_state.h index fc822884..cfddab09 100644 --- a/src/core/libraries/avplayer/avplayer_state.h +++ b/src/core/libraries/avplayer/avplayer_state.h @@ -24,7 +24,7 @@ public: s32 AddSource(std::string_view filename, SceAvPlayerSourceType source_type); s32 GetStreamCount(); s32 GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info); - s32 EnableStream(u32 stream_id); + bool EnableStream(u32 stream_index); s32 Start(); bool Stop(); bool GetAudioData(SceAvPlayerFrameInfo& audio_info); @@ -54,6 +54,8 @@ private: static void* PS4_SYSV_ABI AvControllerThread(void* p_user_data); void AddSourceEvent(); + void WarningEvent(s32 id); + int StartControllerThread(); int ProcessEvent(); int UpdateBufferingState(); @@ -61,15 +63,13 @@ private: std::unique_ptr m_up_source; - SceAvPlayerMemAllocator m_memory_replacement{}; - SceAvPlayerFileReplacement m_file_replacement{}; + SceAvPlayerInitData m_init_data{}; SceAvPlayerEventReplacement m_event_replacement{}; - SceAvPlayerEventReplacement m_user_event_replacement{}; ThreadPriorities m_thread_priorities{}; bool m_auto_start{}; u8 m_default_language[4]{}; - std::atomic_int32_t m_quit; + std::atomic_bool m_quit; std::atomic m_current_state; std::atomic m_previous_state; u32 m_thread_priority; From 0d6e8e227a7c71223ad5d154538a1e1213a661d3 Mon Sep 17 00:00:00 2001 From: Vladislav Mikhalin Date: Wed, 14 Aug 2024 21:30:44 +0300 Subject: [PATCH 07/29] Fixed some sound and threading issues. Details: * Switched SDL audio mutex to RW lock. This fixes games that continiously call SetVolume in a different thread (like Ghostbusters) * Added contition to buffer audio packets independent of video packets. This fixes choppy audio across many games. * Increased the number of audio frame buffers from 2 to 4. Just in case. * Migrated to std::jthread and std::mutex from pthreads. * Fixed a race condition with joins on avplayer close that caused a crash. --- src/audio_core/sdl_audio.cpp | 8 +- src/audio_core/sdl_audio.h | 4 +- src/core/libraries/audio/audioout.cpp | 4 +- src/core/libraries/avplayer/avplayer.cpp | 67 +---- src/core/libraries/avplayer/avplayer.h | 1 + .../libraries/avplayer/avplayer_common.cpp | 59 ---- src/core/libraries/avplayer/avplayer_common.h | 90 +------ .../avplayer/avplayer_file_streamer.cpp | 6 +- src/core/libraries/avplayer/avplayer_impl.cpp | 43 +-- src/core/libraries/avplayer/avplayer_impl.h | 34 +-- .../libraries/avplayer/avplayer_source.cpp | 254 ++++++++---------- src/core/libraries/avplayer/avplayer_source.h | 25 +- .../libraries/avplayer/avplayer_state.cpp | 74 ++--- src/core/libraries/avplayer/avplayer_state.h | 21 +- src/core/libraries/kernel/thread_management.h | 16 +- src/core/libraries/kernel/time_management.h | 1 - src/core/libraries/save_data/savedata.cpp | 5 +- 17 files changed, 231 insertions(+), 481 deletions(-) diff --git a/src/audio_core/sdl_audio.cpp b/src/audio_core/sdl_audio.cpp index aac67d8c..9c8c5991 100644 --- a/src/audio_core/sdl_audio.cpp +++ b/src/audio_core/sdl_audio.cpp @@ -13,7 +13,7 @@ namespace Audio { int SDLAudio::AudioOutOpen(int type, u32 samples_num, u32 freq, Libraries::AudioOut::OrbisAudioOutParamFormat format) { using Libraries::AudioOut::OrbisAudioOutParamFormat; - std::scoped_lock lock{m_mutex}; + std::unique_lock lock{m_mutex}; for (int id = 0; id < portsOut.size(); id++) { auto& port = portsOut[id]; if (!port.isOpen) { @@ -88,7 +88,7 @@ int SDLAudio::AudioOutOpen(int type, u32 samples_num, u32 freq, } s32 SDLAudio::AudioOutOutput(s32 handle, const void* ptr) { - std::scoped_lock lock{m_mutex}; + std::shared_lock lock{m_mutex}; auto& port = portsOut[handle - 1]; if (!port.isOpen) { return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT; @@ -109,7 +109,7 @@ s32 SDLAudio::AudioOutOutput(s32 handle, const void* ptr) { bool SDLAudio::AudioOutSetVolume(s32 handle, s32 bitflag, s32* volume) { using Libraries::AudioOut::OrbisAudioOutParamFormat; - std::scoped_lock lock{m_mutex}; + std::shared_lock lock{m_mutex}; auto& port = portsOut[handle - 1]; if (!port.isOpen) { return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT; @@ -147,7 +147,7 @@ bool SDLAudio::AudioOutSetVolume(s32 handle, s32 bitflag, s32* volume) { } bool SDLAudio::AudioOutGetStatus(s32 handle, int* type, int* channels_num) { - std::scoped_lock lock{m_mutex}; + std::shared_lock lock{m_mutex}; auto& port = portsOut[handle - 1]; *type = port.type; *channels_num = port.channels_num; diff --git a/src/audio_core/sdl_audio.h b/src/audio_core/sdl_audio.h index d20c4455..7844bd61 100644 --- a/src/audio_core/sdl_audio.h +++ b/src/audio_core/sdl_audio.h @@ -3,7 +3,7 @@ #pragma once -#include +#include #include #include "core/libraries/audio/audioout.h" @@ -32,7 +32,7 @@ private: int volume[8] = {}; SDL_AudioStream* stream = nullptr; }; - std::mutex m_mutex; + std::shared_mutex m_mutex; std::array portsOut; // main up to 8 ports , BGM 1 port , voice up to 4 ports , // personal up to 4 ports , padspk up to 5 ports , aux 1 port }; diff --git a/src/core/libraries/audio/audioout.cpp b/src/core/libraries/audio/audioout.cpp index 08929383..cb676afc 100644 --- a/src/core/libraries/audio/audioout.cpp +++ b/src/core/libraries/audio/audioout.cpp @@ -234,7 +234,7 @@ int PS4_SYSV_ABI sceAudioOutGetSystemState() { } int PS4_SYSV_ABI sceAudioOutInit() { - LOG_INFO(Lib_AudioOut, "called"); + LOG_TRACE(Lib_AudioOut, "called"); if (audio != nullptr) { return ORBIS_AUDIO_OUT_ERROR_ALREADY_INIT; } @@ -323,12 +323,10 @@ int PS4_SYSV_ABI sceAudioOutOpenEx() { } s32 PS4_SYSV_ABI sceAudioOutOutput(s32 handle, const void* ptr) { - LOG_TRACE(Lib_AudioOut, "called"); return audio->AudioOutOutput(handle, ptr); } int PS4_SYSV_ABI sceAudioOutOutputs(OrbisAudioOutOutputParam* param, u32 num) { - LOG_TRACE(Lib_AudioOut, "called"); for (u32 i = 0; i < num; i++) { if (auto err = audio->AudioOutOutput(param[i].handle, param[i].ptr); err != 0) return err; diff --git a/src/core/libraries/avplayer/avplayer.cpp b/src/core/libraries/avplayer/avplayer.cpp index e8e99bca..8fccc659 100644 --- a/src/core/libraries/avplayer/avplayer.cpp +++ b/src/core/libraries/avplayer/avplayer.cpp @@ -11,8 +11,6 @@ #include // std::max, std::min -#include // va_list - namespace Libraries::AvPlayer { using namespace Kernel; @@ -140,17 +138,7 @@ SceAvPlayerHandle PS4_SYSV_ABI sceAvPlayerInit(SceAvPlayerInitData* data) { return nullptr; } - ThreadPriorities priorities{}; - const u32 base_priority = data->base_priority != 0 ? data->base_priority : 700; - priorities.video_decoder_priority = GetPriority(base_priority, 5); - priorities.audio_decoder_priority = GetPriority(base_priority, 6); - priorities.demuxer_priority = GetPriority(base_priority, 9); - priorities.controller_priority = GetPriority(base_priority, 2); - // priorities.http_streaming_priority = GetPriority(base_priority, 10); - // priorities.file_streaming_priority = GetPriority(priorities.http_streaming_priority, 15); - // priorities.maxPriority = priorities.http_streaming_priority; - - return new AvPlayer(*data, priorities); + return new AvPlayer(*data); } s32 PS4_SYSV_ABI sceAvPlayerInitEx(const SceAvPlayerInitDataEx* p_data, @@ -176,56 +164,7 @@ s32 PS4_SYSV_ABI sceAvPlayerInitEx(const SceAvPlayerInitDataEx* p_data, data.num_output_video_framebuffers = p_data->num_output_video_framebuffers; data.auto_start = p_data->auto_start; - ThreadPriorities priorities{}; - s32 base_priority = 0; - const auto res = scePthreadGetprio(scePthreadSelf(), &base_priority); - if (res != 0 || base_priority == 0) { - base_priority = 700; - } - - if (p_data->video_decoder_priority != 0) { - priorities.video_decoder_priority = p_data->video_decoder_priority; - } else { - priorities.video_decoder_priority = GetPriority(base_priority, 5); - } - priorities.video_decoder_affinity = p_data->video_decoder_affinity; - - if (p_data->audio_decoder_priority != 0) { - priorities.audio_decoder_priority = p_data->audio_decoder_priority; - } else { - priorities.audio_decoder_priority = GetPriority(base_priority, 6); - } - priorities.audio_decoder_affinity = p_data->audio_decoder_affinity; - - if (p_data->controller_priority != 0) { - priorities.controller_priority = p_data->controller_priority; - } else { - priorities.controller_priority = GetPriority(base_priority, 2); - } - priorities.controller_affinity = p_data->controller_affinity; - - if (p_data->demuxer_priority != 0) { - priorities.demuxer_priority = p_data->demuxer_priority; - } else { - priorities.demuxer_priority = GetPriority(base_priority, 9); - } - priorities.demuxer_affinity = p_data->demuxer_affinity; - - // if (p_data->http_streaming_priority != 0) { - // priorities.http_streaming_priority = p_data->http_streaming_priority; - // } else { - // priorities.http_streaming_priority = GetPriority(base_priority, 10); - // } - // priorities.http_streaming_affinity = p_data->http_streaming_affinity; - - // if (p_data->file_streaming_priority != 0) { - // priorities.file_streaming_priority = p_data->file_streaming_priority; - // } else { - // priorities.file_streaming_priority = GetPriority(base_priority, 15); - // } - // priorities.http_streaming_affinity = p_data->http_streaming_affinity; - - *p_player = new AvPlayer(data, priorities); + *p_player = new AvPlayer(data); return ORBIS_OK; } @@ -320,7 +259,7 @@ s32 PS4_SYSV_ABI sceAvPlayerStart(SceAvPlayerHandle handle) { } s32 PS4_SYSV_ABI sceAvPlayerStop(SceAvPlayerHandle handle) { - LOG_ERROR(Lib_AvPlayer, "(STUBBED) called"); + LOG_TRACE(Lib_AvPlayer, "called"); if (handle == nullptr) { return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; } diff --git a/src/core/libraries/avplayer/avplayer.h b/src/core/libraries/avplayer/avplayer.h index e88419c1..360f06b6 100644 --- a/src/core/libraries/avplayer/avplayer.h +++ b/src/core/libraries/avplayer/avplayer.h @@ -5,6 +5,7 @@ #include "common/types.h" +#include // va_list #include // size_t namespace Core::Loader { diff --git a/src/core/libraries/avplayer/avplayer_common.cpp b/src/core/libraries/avplayer/avplayer_common.cpp index 3536c030..306603e2 100644 --- a/src/core/libraries/avplayer/avplayer_common.cpp +++ b/src/core/libraries/avplayer/avplayer_common.cpp @@ -12,65 +12,6 @@ namespace Libraries::AvPlayer { using namespace Kernel; -Kernel::ScePthreadMutex CreateMutex(int type, const char* name) { - ScePthreadMutexattr attr{}; - ScePthreadMutex mutex{}; - if (scePthreadMutexattrInit(&attr) == 0) { - if (scePthreadMutexattrSettype(&attr, type) == 0) { - if (scePthreadMutexInit(&mutex, &attr, name) != 0) { - if (mutex != nullptr) { - scePthreadMutexDestroy(&mutex); - } - return nullptr; - } - } - } - if (attr != nullptr) { - scePthreadMutexattrDestroy(&attr); - } - return mutex; -} - -ScePthread CreateThread(Kernel::PthreadEntryFunc func, const ThreadParameters& params) { - ScePthreadAttr attr; - if (scePthreadAttrInit(&attr) != 0) { - return nullptr; - } - if (scePthreadAttrSetinheritsched(&attr, 0) != 0) { - scePthreadAttrDestroy(&attr); - return nullptr; - } - - SceKernelSchedParam param{.sched_priority = static_cast(params.priority)}; - if (scePthreadAttrSetschedparam(&attr, ¶m) != 0) { - scePthreadAttrDestroy(&attr); - return nullptr; - } - if (scePthreadAttrSetstacksize(&attr, std::min(params.stack_size, 0x4000u)) != 0) { - scePthreadAttrDestroy(&attr); - return nullptr; - } - if (scePthreadAttrSetdetachstate(&attr, 0) != 0) { - scePthreadAttrDestroy(&attr); - return nullptr; - } - if (params.affinity > 0) { - if (scePthreadAttrSetaffinity(&attr, params.affinity) != 0) { - scePthreadAttrDestroy(&attr); - return nullptr; - } - } - - ScePthread thread{}; - if (scePthreadCreate(&thread, &attr, func, params.p_user_data, params.thread_name) != 0) { - scePthreadAttrDestroy(&attr); - return nullptr; - } - - scePthreadAttrDestroy(&attr); - return thread; -} - static bool ichar_equals(char a, char b) { return std::tolower(static_cast(a)) == std::tolower(static_cast(b)); diff --git a/src/core/libraries/avplayer/avplayer_common.h b/src/core/libraries/avplayer/avplayer_common.h index 5faf2388..a53696ec 100644 --- a/src/core/libraries/avplayer/avplayer_common.h +++ b/src/core/libraries/avplayer/avplayer_common.h @@ -31,9 +31,6 @@ enum class AvState { C0x0B, Buffering, Starting, - C0x0E, - C0x0F, - C0x10, Error, }; @@ -45,23 +42,6 @@ enum class AvEventType { Error = 255, }; -struct ThreadPriorities { - u32 audio_decoder_priority; - u32 audio_decoder_affinity; - u32 video_decoder_priority; - u32 video_decoder_affinity; - u32 demuxer_priority; - u32 demuxer_affinity; - u32 controller_priority; - u32 controller_affinity; - // u32 http_streaming_priority; - // u32 http_streaming_affinity; - // u32 file_streaming_priority; - // u32 file_streaming_affinity; - // u32 maxPriority; - // u32 maxAffinity; -}; - union AvPlayerEventData { u32 num_frames; // 20 AvState state; // AvEventType::ChangeFlowState @@ -74,68 +54,9 @@ struct AvPlayerEvent { AvPlayerEventData payload; }; -Kernel::ScePthreadMutex CreateMutex(int type, const char* name); - -class PthreadMutex { -public: - using ScePthreadMutex = Kernel::ScePthreadMutex; - - PthreadMutex() = default; - - PthreadMutex(const PthreadMutex&) = delete; - PthreadMutex& operator=(const PthreadMutex&) = delete; - - PthreadMutex(PthreadMutex&& r) : m_mutex(r.m_mutex) { - r.m_mutex = nullptr; - } - PthreadMutex& operator=(PthreadMutex&& r) { - std::swap(m_mutex, r.m_mutex); - return *this; - } - - PthreadMutex(int type, const char* name) : m_mutex(CreateMutex(type, name)) {} - ~PthreadMutex() { - if (m_mutex != nullptr) { - Kernel::scePthreadMutexDestroy(&m_mutex); - } - } - - operator ScePthreadMutex() { - return m_mutex; - } - - int Lock() { - return Kernel::scePthreadMutexLock(&m_mutex); - } - - int Unlock() { - return Kernel::scePthreadMutexUnlock(&m_mutex); - } - - // implement BasicLockable to use std::lock_guard - // NOLINTNEXTLINE(readability-identifier-naming) - void lock() { - ASSERT_MSG(Lock() >= 0, "Could not lock the mutex"); - } - - // NOLINTNEXTLINE(readability-identifier-naming) - void unlock() { - ASSERT_MSG(Unlock() >= 0, "Could not unlock the mutex"); - } - - operator bool() { - return m_mutex != nullptr; - } - -private: - ScePthreadMutex m_mutex{}; -}; - template class AvPlayerQueue { public: - AvPlayerQueue() : m_mutex(PTHREAD_MUTEX_ERRORCHECK, "SceAvPlayer0StlHandler") {} - size_t Size() { return m_queue.size(); } @@ -161,19 +82,10 @@ public: } private: - PthreadMutex m_mutex{}; + std::mutex m_mutex{}; std::queue m_queue{}; }; -struct ThreadParameters { - void* p_user_data; - const char* thread_name; - u32 stack_size; - u32 priority; - u32 affinity; -}; - -Kernel::ScePthread CreateThread(Kernel::PthreadEntryFunc func, const ThreadParameters& params); SceAvPlayerSourceType GetSourceType(std::string_view path); } // namespace Libraries::AvPlayer diff --git a/src/core/libraries/avplayer/avplayer_file_streamer.cpp b/src/core/libraries/avplayer/avplayer_file_streamer.cpp index 43055fd5..4b1ddb6e 100644 --- a/src/core/libraries/avplayer/avplayer_file_streamer.cpp +++ b/src/core/libraries/avplayer/avplayer_file_streamer.cpp @@ -69,14 +69,14 @@ s64 AvPlayerFileStreamer::Seek(void* opaque, s64 offset, int whence) { if (whence == SEEK_CUR) { self->m_position = - std::min(u64(std::max(0ll, s64(self->m_position) + offset)), self->m_file_size); + std::min(u64(std::max(0i64, s64(self->m_position) + offset)), self->m_file_size); return self->m_position; } else if (whence == SEEK_SET) { - self->m_position = std::min(u64(std::max(0ll, offset)), self->m_file_size); + self->m_position = std::min(u64(std::max(0i64, offset)), self->m_file_size); return self->m_position; } else if (whence == SEEK_END) { self->m_position = - std::min(u64(std::max(0ll, s64(self->m_file_size) + offset)), self->m_file_size); + std::min(u64(std::max(0i64, s64(self->m_file_size) + offset)), self->m_file_size); return self->m_position; } diff --git a/src/core/libraries/avplayer/avplayer_impl.cpp b/src/core/libraries/avplayer/avplayer_impl.cpp index f08356bd..1114254f 100644 --- a/src/core/libraries/avplayer/avplayer_impl.cpp +++ b/src/core/libraries/avplayer/avplayer_impl.cpp @@ -77,29 +77,30 @@ u64 PS4_SYSV_ABI AvPlayer::SizeFile(void* handle) { return size(ptr); } -AvPlayer::AvPlayer(const SceAvPlayerInitData& data, const ThreadPriorities& priorities) - : m_file_io_mutex(PTHREAD_MUTEX_ERRORCHECK, "AvPlayer_FileIO"), m_init_data(data), - m_init_data_original(data) { - - m_init_data.memory_replacement.object_ptr = this; - m_init_data.memory_replacement.allocate = &AvPlayer::Allocate; - m_init_data.memory_replacement.deallocate = &AvPlayer::Deallocate; - m_init_data.memory_replacement.allocate_texture = &AvPlayer::AllocateTexture; - m_init_data.memory_replacement.deallocate_texture = &AvPlayer::DeallocateTexture; +SceAvPlayerInitData AvPlayer::StubInitData(const SceAvPlayerInitData& data) { + SceAvPlayerInitData result = data; + result.memory_replacement.object_ptr = this; + result.memory_replacement.allocate = &AvPlayer::Allocate; + result.memory_replacement.deallocate = &AvPlayer::Deallocate; + result.memory_replacement.allocate_texture = &AvPlayer::AllocateTexture; + result.memory_replacement.deallocate_texture = &AvPlayer::DeallocateTexture; if (data.file_replacement.open == nullptr || data.file_replacement.close == nullptr || data.file_replacement.readOffset == nullptr || data.file_replacement.size == nullptr) { - m_init_data.file_replacement = {}; + result.file_replacement = {}; } else { - m_init_data.file_replacement.object_ptr = this; - m_init_data.file_replacement.open = &AvPlayer::OpenFile; - m_init_data.file_replacement.close = &AvPlayer::CloseFile; - m_init_data.file_replacement.readOffset = &AvPlayer::ReadOffsetFile; - m_init_data.file_replacement.size = &AvPlayer::SizeFile; + result.file_replacement.object_ptr = this; + result.file_replacement.open = &AvPlayer::OpenFile; + result.file_replacement.close = &AvPlayer::CloseFile; + result.file_replacement.readOffset = &AvPlayer::ReadOffsetFile; + result.file_replacement.size = &AvPlayer::SizeFile; } - - m_state = std::make_unique(m_init_data, priorities); + return result; } +AvPlayer::AvPlayer(const SceAvPlayerInitData& data) + : m_init_data(StubInitData(data)), m_init_data_original(data), + m_state(std::make_unique(m_init_data)) {} + s32 AvPlayer::PostInit(const SceAvPlayerPostInitData& data) { m_post_init_data = data; return ORBIS_OK; @@ -109,11 +110,9 @@ s32 AvPlayer::AddSource(std::string_view path) { if (path.empty()) { return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; } - if (AVPLAYER_IS_ERROR(m_state->AddSource(path, GetSourceType(path)))) { return ORBIS_AVPLAYER_ERROR_OPERATION_FAILED; } - return ORBIS_OK; } @@ -121,7 +120,11 @@ s32 AvPlayer::GetStreamCount() { if (m_state == nullptr) { return ORBIS_AVPLAYER_ERROR_OPERATION_FAILED; } - return m_state->GetStreamCount(); + const auto res = m_state->GetStreamCount(); + if (AVPLAYER_IS_ERROR(res)) { + return ORBIS_AVPLAYER_ERROR_OPERATION_FAILED; + } + return res; } s32 AvPlayer::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info) { diff --git a/src/core/libraries/avplayer/avplayer_impl.h b/src/core/libraries/avplayer/avplayer_impl.h index 98f959e6..df4acfee 100644 --- a/src/core/libraries/avplayer/avplayer_impl.h +++ b/src/core/libraries/avplayer/avplayer_impl.h @@ -9,6 +9,8 @@ #include "core/libraries/kernel/thread_management.h" +#include + extern "C" { #include #include @@ -21,19 +23,7 @@ namespace Libraries::AvPlayer { class AvPlayer { public: - // Memory Replacement - static void* PS4_SYSV_ABI Allocate(void* handle, u32 alignment, u32 size); - static void PS4_SYSV_ABI Deallocate(void* handle, void* memory); - static void* PS4_SYSV_ABI AllocateTexture(void* handle, u32 alignment, u32 size); - static void PS4_SYSV_ABI DeallocateTexture(void* handle, void* memory); - - // File Replacement - static int PS4_SYSV_ABI OpenFile(void* handle, const char* filename); - static int PS4_SYSV_ABI CloseFile(void* handle); - static int PS4_SYSV_ABI ReadOffsetFile(void* handle, u8* buffer, u64 position, u32 length); - static u64 PS4_SYSV_ABI SizeFile(void* handle); - - AvPlayer(const SceAvPlayerInitData& data, const ThreadPriorities& priorities); + AvPlayer(const SceAvPlayerInitData& data); s32 PostInit(const SceAvPlayerPostInitData& data); s32 AddSource(std::string_view filename); @@ -51,13 +41,27 @@ public: private: using ScePthreadMutex = Kernel::ScePthreadMutex; - std::unique_ptr m_state{}; + // Memory Replacement + static void* PS4_SYSV_ABI Allocate(void* handle, u32 alignment, u32 size); + static void PS4_SYSV_ABI Deallocate(void* handle, void* memory); + static void* PS4_SYSV_ABI AllocateTexture(void* handle, u32 alignment, u32 size); + static void PS4_SYSV_ABI DeallocateTexture(void* handle, void* memory); + + // File Replacement + static int PS4_SYSV_ABI OpenFile(void* handle, const char* filename); + static int PS4_SYSV_ABI CloseFile(void* handle); + static int PS4_SYSV_ABI ReadOffsetFile(void* handle, u8* buffer, u64 position, u32 length); + static u64 PS4_SYSV_ABI SizeFile(void* handle); + + SceAvPlayerInitData StubInitData(const SceAvPlayerInitData& data); + SceAvPlayerInitData m_init_data{}; SceAvPlayerInitData m_init_data_original{}; SceAvPlayerPostInitData m_post_init_data{}; - PthreadMutex m_file_io_mutex{}; + std::mutex m_file_io_mutex{}; std::atomic_bool m_has_source{}; + std::unique_ptr m_state{}; }; } // namespace Libraries::AvPlayer diff --git a/src/core/libraries/avplayer/avplayer_source.cpp b/src/core/libraries/avplayer/avplayer_source.cpp index c3d3dc55..e235b2c2 100644 --- a/src/core/libraries/avplayer/avplayer_source.cpp +++ b/src/core/libraries/avplayer/avplayer_source.cpp @@ -24,9 +24,9 @@ namespace Libraries::AvPlayer { using namespace Kernel; AvPlayerSource::AvPlayerSource(AvPlayerStateCallback& state, std::string_view path, - const SceAvPlayerInitData& init_data, ThreadPriorities& priorities, + const SceAvPlayerInitData& init_data, SceAvPlayerSourceType source_type) - : m_state(state), m_priorities(priorities), m_memory_replacement(init_data.memory_replacement), + : m_state(state), m_memory_replacement(init_data.memory_replacement), m_num_output_video_framebuffers(init_data.num_output_video_framebuffers) { AVFormatContext* context = avformat_alloc_context(); if (init_data.file_replacement.open != nullptr) { @@ -113,7 +113,7 @@ s32 AvPlayerSource::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info) info.details.video.height = p_stream->codecpar->height; if (p_lang_node != nullptr) { std::memcpy(info.details.video.language_code, p_lang_node->value, - std::min(strlen(p_lang_node->value), 3ull)); + std::min(strlen(p_lang_node->value), size_t(3))); } break; case SCE_AVPLAYER_AUDIO: @@ -123,7 +123,7 @@ s32 AvPlayerSource::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info) info.details.audio.size = 0; // sceAvPlayerGetStreamInfo() is expected to set this to 0 if (p_lang_node != nullptr) { std::memcpy(info.details.audio.language_code, p_lang_node->value, - std::min(strlen(p_lang_node->value), 3ull)); + std::min(strlen(p_lang_node->value), size_t(3))); } break; case SCE_AVPLAYER_TIMEDTEXT: @@ -132,7 +132,7 @@ s32 AvPlayerSource::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info) info.details.subs.text_size = 12; if (p_lang_node != nullptr) { std::memcpy(info.details.subs.language_code, p_lang_node->value, - std::min(strlen(p_lang_node->value), 3ull)); + std::min(strlen(p_lang_node->value), size_t(3))); } break; default: @@ -189,7 +189,7 @@ bool AvPlayerSource::EnableStream(u32 stream_index) { const auto num_channels = m_audio_codec_context->ch_layout.nb_channels; const auto align = num_channels * sizeof(u16); const auto size = num_channels * sizeof(u16) * 1024; - for (u64 index = 0; index < 2; ++index) { + for (u64 index = 0; index < 4; ++index) { m_audio_buffers.Push(FrameBuffer(m_memory_replacement, 0x100, size)); } LOG_INFO(Lib_AvPlayer, "Audio stream {} enabled", stream_index); @@ -212,78 +212,41 @@ std::optional AvPlayerSource::HasFrames(u32 num_frames) { } s32 AvPlayerSource::Start() { + std::unique_lock lock(m_state_mutex); + if (m_audio_codec_context == nullptr && m_video_codec_context == nullptr) { LOG_ERROR(Lib_AvPlayer, "Could not start playback. NULL context."); return -1; } - { - ThreadParameters demuxer_params{ - .p_user_data = this, - .thread_name = "AvPlayer_Demuxer", - .stack_size = 0x4000, - .priority = m_priorities.demuxer_priority, - .affinity = m_priorities.demuxer_affinity, - }; - m_demuxer_thread = CreateThread(&DemuxerThread, demuxer_params); - if (m_demuxer_thread == nullptr) { - LOG_ERROR(Lib_AvPlayer, "Could not create DEMUXER thread."); - return -1; - } - } - if (m_video_codec_context != nullptr) { - ThreadParameters video_decoder_params{ - .p_user_data = this, - .thread_name = "AvPlayer_VideoDecoder", - .stack_size = 0x4000, - .priority = m_priorities.video_decoder_priority, - .affinity = m_priorities.video_decoder_affinity, - }; - m_video_decoder_thread = CreateThread(&VideoDecoderThread, video_decoder_params); - if (m_video_decoder_thread == nullptr) { - LOG_ERROR(Lib_AvPlayer, "Could not create VIDEO DECODER thread."); - return -1; - } - } - if (m_audio_codec_context != nullptr) { - ThreadParameters audio_decoder_params{ - .p_user_data = this, - .thread_name = "AvPlayer_AudioDecoder", - .stack_size = 0x4000, - .priority = m_priorities.audio_decoder_priority, - .affinity = m_priorities.audio_decoder_affinity, - }; - m_audio_decoder_thread = CreateThread(&AudioDecoderThread, audio_decoder_params); - if (m_audio_decoder_thread == nullptr) { - LOG_ERROR(Lib_AvPlayer, "Could not create AUDIO DECODER thread."); - return -1; - } - } + m_demuxer_thread = std::jthread([this](std::stop_token stop) { this->DemuxerThread(stop); }); + m_video_decoder_thread = + std::jthread([this](std::stop_token stop) { this->VideoDecoderThread(stop); }); + m_audio_decoder_thread = + std::jthread([this](std::stop_token stop) { this->AudioDecoderThread(stop); }); m_start_time = std::chrono::high_resolution_clock::now(); return 0; } bool AvPlayerSource::Stop() { - if (m_is_stop) { + std::unique_lock lock(m_state_mutex); + + if (!HasRunningThreads()) { LOG_WARNING(Lib_AvPlayer, "Could not stop playback: already stopped."); return false; } - m_is_stop = true; - - void* res = nullptr; - if (m_video_decoder_thread != nullptr) { - scePthreadJoin(m_video_decoder_thread, &res); + m_video_decoder_thread.request_stop(); + m_audio_decoder_thread.request_stop(); + m_demuxer_thread.request_stop(); + if (m_demuxer_thread.joinable()) { + m_demuxer_thread.join(); } - if (m_audio_decoder_thread != nullptr) { - scePthreadJoin(m_audio_decoder_thread, &res); + if (m_video_decoder_thread.joinable()) { + m_video_decoder_thread.join(); } - if (m_demuxer_thread != nullptr) { - scePthreadJoin(m_demuxer_thread, &res); + if (m_audio_decoder_thread.joinable()) { + m_audio_decoder_thread.join(); } - m_audio_packets.Clear(); - m_video_packets.Clear(); - m_audio_frames.Clear(); - m_video_frames.Clear(); if (m_current_audio_frame.has_value()) { m_audio_buffers.Push(std::move(m_current_audio_frame.value())); m_current_audio_frame.reset(); @@ -292,10 +255,19 @@ bool AvPlayerSource::Stop() { m_video_buffers.Push(std::move(m_current_video_frame.value())); m_current_video_frame.reset(); } + + m_audio_packets.Clear(); + m_video_packets.Clear(); + m_audio_frames.Clear(); + m_video_frames.Clear(); return true; } bool AvPlayerSource::GetVideoData(SceAvPlayerFrameInfo& video_info) { + if (!IsActive()) { + return false; + } + SceAvPlayerFrameInfoEx info{}; if (!GetVideoData(info)) { return false; @@ -315,8 +287,13 @@ static void CopyNV12Data(u8* dst, const AVFrame& src) { } bool AvPlayerSource::GetVideoData(SceAvPlayerFrameInfoEx& video_info) { + if (!IsActive()) { + return false; + } + + using namespace std::chrono; while (m_video_frames.Size() == 0 && !m_is_eof) { - sceKernelUsleep(5000); + std::this_thread::sleep_for(milliseconds(5)); } auto frame = m_video_frames.Pop(); @@ -326,11 +303,10 @@ bool AvPlayerSource::GetVideoData(SceAvPlayerFrameInfoEx& video_info) { } { - using namespace std::chrono; auto elapsed_time = duration_cast(high_resolution_clock::now() - m_start_time).count(); while (elapsed_time < frame->info.timestamp) { - sceKernelUsleep((frame->info.timestamp - elapsed_time) * 1000); + std::this_thread::sleep_for(milliseconds(frame->info.timestamp - elapsed_time)); elapsed_time = duration_cast(high_resolution_clock::now() - m_start_time).count(); } @@ -346,8 +322,13 @@ bool AvPlayerSource::GetVideoData(SceAvPlayerFrameInfoEx& video_info) { } bool AvPlayerSource::GetAudioData(SceAvPlayerFrameInfo& audio_info) { + if (!IsActive()) { + return false; + } + + using namespace std::chrono; while (m_audio_frames.Size() == 0 && !m_is_eof) { - sceKernelUsleep(5000); + std::this_thread::sleep_for(milliseconds(5)); } auto frame = m_audio_frames.Pop(); @@ -357,11 +338,10 @@ bool AvPlayerSource::GetAudioData(SceAvPlayerFrameInfo& audio_info) { } { - using namespace std::chrono; auto elapsed_time = duration_cast(high_resolution_clock::now() - m_start_time).count(); while (elapsed_time < frame->info.timestamp) { - sceKernelUsleep((frame->info.timestamp - elapsed_time) * 1000); + std::this_thread::sleep_for(milliseconds(frame->info.timestamp - elapsed_time)); elapsed_time = duration_cast(high_resolution_clock::now() - m_start_time).count(); } @@ -387,8 +367,8 @@ u64 AvPlayerSource::CurrentTime() { } bool AvPlayerSource::IsActive() { - return !m_is_stop && (!m_is_eof || m_audio_packets.Size() != 0 || m_video_packets.Size() != 0 || - m_video_frames.Size() != 0 || m_audio_frames.Size() != 0); + return !m_is_eof || m_audio_packets.Size() != 0 || m_video_packets.Size() != 0 || + m_video_frames.Size() != 0 || m_audio_frames.Size() != 0; } void AvPlayerSource::ReleaseAVPacket(AVPacket* packet) { @@ -427,52 +407,51 @@ void AvPlayerSource::ReleaseAVFormatContext(AVFormatContext* context) { } } -void* AvPlayerSource::DemuxerThread(void* opaque) { - const auto self = reinterpret_cast(opaque); - if (!self->m_audio_stream_index.has_value() && !self->m_video_stream_index.has_value()) { +void AvPlayerSource::DemuxerThread(std::stop_token stop) { + using namespace std::chrono; + if (!m_audio_stream_index.has_value() && !m_video_stream_index.has_value()) { LOG_WARNING(Lib_AvPlayer, "Could not start DEMUXER thread. No streams enabled."); - return nullptr; + return; } LOG_INFO(Lib_AvPlayer, "Demuxer Thread started"); - while (!self->m_is_stop) { - if (self->m_video_packets.Size() > 60) { - sceKernelUsleep(5000); + while (!stop.stop_requested()) { + if (m_video_packets.Size() > 30 && m_audio_packets.Size() > 8) { + std::this_thread::sleep_for(milliseconds(5)); continue; } AVPacketPtr up_packet(av_packet_alloc(), &ReleaseAVPacket); - const auto res = av_read_frame(self->m_avformat_context.get(), up_packet.get()); + const auto res = av_read_frame(m_avformat_context.get(), up_packet.get()); if (res < 0) { if (res == AVERROR_EOF) { LOG_INFO(Lib_AvPlayer, "EOF reached in demuxer"); break; } else { LOG_ERROR(Lib_AvPlayer, "Could not read AV frame: error = {}", res); - self->m_state.OnError(); - scePthreadExit(0); + m_state.OnError(); + return; } break; } - if (up_packet->stream_index == self->m_video_stream_index) { - self->m_video_packets.Push(std::move(up_packet)); - } else if (up_packet->stream_index == self->m_audio_stream_index) { - self->m_audio_packets.Push(std::move(up_packet)); + if (up_packet->stream_index == m_video_stream_index) { + m_video_packets.Push(std::move(up_packet)); + } else if (up_packet->stream_index == m_audio_stream_index) { + m_audio_packets.Push(std::move(up_packet)); } } - self->m_is_eof = true; + m_is_eof = true; void* res; - if (self->m_video_decoder_thread) { - scePthreadJoin(self->m_video_decoder_thread, &res); + if (m_video_decoder_thread.joinable()) { + m_video_decoder_thread.join(); } - if (self->m_audio_decoder_thread) { - scePthreadJoin(self->m_audio_decoder_thread, &res); + if (m_audio_decoder_thread.joinable()) { + m_audio_decoder_thread.join(); } - self->m_state.OnEOF(); + m_state.OnEOF(); LOG_INFO(Lib_AvPlayer, "Demuxer Thread exited normaly"); - scePthreadExit(0); } AvPlayerSource::AVFramePtr AvPlayerSource::ConvertVideoFrame(const AVFrame& frame) { @@ -537,67 +516,63 @@ Frame AvPlayerSource::PrepareVideoFrame(FrameBuffer buffer, const AVFrame& frame }; } -void* AvPlayerSource::VideoDecoderThread(void* opaque) { +void AvPlayerSource::VideoDecoderThread(std::stop_token stop) { + using namespace std::chrono; LOG_INFO(Lib_AvPlayer, "Video Decoder Thread started"); - const auto self = reinterpret_cast(opaque); - - while ((!self->m_is_eof || self->m_video_packets.Size() != 0) && !self->m_is_stop) { - if (self->m_video_packets.Size() == 0) { - sceKernelUsleep(5000); + while ((!m_is_eof || m_video_packets.Size() != 0) && !stop.stop_requested()) { + if (m_video_packets.Size() == 0) { + std::this_thread::sleep_for(milliseconds(5)); continue; } - const auto packet = self->m_video_packets.Pop(); + const auto packet = m_video_packets.Pop(); if (!packet.has_value()) { continue; } - auto res = avcodec_send_packet(self->m_video_codec_context.get(), packet->get()); + auto res = avcodec_send_packet(m_video_codec_context.get(), packet->get()); if (res < 0 && res != AVERROR(EAGAIN)) { - self->m_state.OnError(); + m_state.OnError(); LOG_ERROR(Lib_AvPlayer, "Could not send packet to the video codec. Error = {}", av_err2str(res)); - scePthreadExit(nullptr); + return; } while (res >= 0) { - if (self->m_video_buffers.Size() == 0 && !self->m_is_stop) { - sceKernelUsleep(5000); + if (m_video_buffers.Size() == 0 && !stop.stop_requested()) { + std::this_thread::sleep_for(milliseconds(5)); continue; } auto up_frame = AVFramePtr(av_frame_alloc(), &ReleaseAVFrame); - res = avcodec_receive_frame(self->m_video_codec_context.get(), up_frame.get()); + res = avcodec_receive_frame(m_video_codec_context.get(), up_frame.get()); if (res < 0) { if (res == AVERROR_EOF) { LOG_INFO(Lib_AvPlayer, "EOF reached in video decoder"); - scePthreadExit(nullptr); + return; } else if (res != AVERROR(EAGAIN)) { LOG_ERROR(Lib_AvPlayer, "Could not receive frame from the video codec. Error = {}", av_err2str(res)); - self->m_state.OnError(); - scePthreadExit(nullptr); + m_state.OnError(); + return; } } else { - auto buffer = self->m_video_buffers.Pop(); + auto buffer = m_video_buffers.Pop(); if (!buffer.has_value()) { // Video buffers queue was cleared. This means that player was stopped. break; } if (up_frame->format != AV_PIX_FMT_NV12) { - const auto nv12_frame = self->ConvertVideoFrame(*up_frame); - self->m_video_frames.Push( - self->PrepareVideoFrame(std::move(buffer.value()), *nv12_frame)); + const auto nv12_frame = ConvertVideoFrame(*up_frame); + m_video_frames.Push(PrepareVideoFrame(std::move(buffer.value()), *nv12_frame)); } else { - self->m_video_frames.Push( - self->PrepareVideoFrame(std::move(buffer.value()), *up_frame)); + m_video_frames.Push(PrepareVideoFrame(std::move(buffer.value()), *up_frame)); } LOG_TRACE(Lib_AvPlayer, "Produced Video Frame. Num Frames: {}", - self->m_video_frames.Size()); + m_video_frames.Size()); } } } LOG_INFO(Lib_AvPlayer, "Video Decoder Thread exited normaly"); - scePthreadExit(nullptr); } AvPlayerSource::AVFramePtr AvPlayerSource::ConvertAudioFrame(const AVFrame& frame) { @@ -659,66 +634,67 @@ Frame AvPlayerSource::PrepareAudioFrame(FrameBuffer buffer, const AVFrame& frame }; } -void* AvPlayerSource::AudioDecoderThread(void* opaque) { +void AvPlayerSource::AudioDecoderThread(std::stop_token stop) { + using namespace std::chrono; LOG_INFO(Lib_AvPlayer, "Audio Decoder Thread started"); - const auto self = reinterpret_cast(opaque); - - while ((!self->m_is_eof || self->m_audio_packets.Size() != 0) && !self->m_is_stop) { - if (self->m_audio_packets.Size() == 0) { - sceKernelUsleep(5000); + while ((!m_is_eof || m_audio_packets.Size() != 0) && !stop.stop_requested()) { + if (m_audio_packets.Size() == 0) { + std::this_thread::sleep_for(milliseconds(5)); continue; } - const auto packet = self->m_audio_packets.Pop(); + const auto packet = m_audio_packets.Pop(); if (!packet.has_value()) { continue; } - auto res = avcodec_send_packet(self->m_audio_codec_context.get(), packet->get()); + auto res = avcodec_send_packet(m_audio_codec_context.get(), packet->get()); if (res < 0 && res != AVERROR(EAGAIN)) { - self->m_state.OnError(); + m_state.OnError(); LOG_ERROR(Lib_AvPlayer, "Could not send packet to the audio codec. Error = {}", av_err2str(res)); - scePthreadExit(nullptr); + return; } while (res >= 0) { - if (self->m_audio_buffers.Size() == 0 && !self->m_is_stop) { - sceKernelUsleep(5000); + if (m_audio_buffers.Size() == 0 && !stop.stop_requested()) { + std::this_thread::sleep_for(milliseconds(5)); continue; } auto up_frame = AVFramePtr(av_frame_alloc(), &ReleaseAVFrame); - res = avcodec_receive_frame(self->m_audio_codec_context.get(), up_frame.get()); + res = avcodec_receive_frame(m_audio_codec_context.get(), up_frame.get()); if (res < 0) { if (res == AVERROR_EOF) { LOG_INFO(Lib_AvPlayer, "EOF reached in audio decoder"); - scePthreadExit(nullptr); + return; } else if (res != AVERROR(EAGAIN)) { - self->m_state.OnError(); + m_state.OnError(); LOG_ERROR(Lib_AvPlayer, "Could not receive frame from the audio codec. Error = {}", av_err2str(res)); - scePthreadExit(nullptr); + return; } } else { - auto buffer = self->m_audio_buffers.Pop(); + auto buffer = m_audio_buffers.Pop(); if (!buffer.has_value()) { // Audio buffers queue was cleared. This means that player was stopped. break; } if (up_frame->format != AV_SAMPLE_FMT_S16) { - const auto pcm16_frame = self->ConvertAudioFrame(*up_frame); - self->m_audio_frames.Push( - self->PrepareAudioFrame(std::move(buffer.value()), *pcm16_frame)); + const auto pcm16_frame = ConvertAudioFrame(*up_frame); + m_audio_frames.Push(PrepareAudioFrame(std::move(buffer.value()), *pcm16_frame)); } else { - self->m_audio_frames.Push( - self->PrepareAudioFrame(std::move(buffer.value()), *up_frame)); + m_audio_frames.Push(PrepareAudioFrame(std::move(buffer.value()), *up_frame)); } LOG_TRACE(Lib_AvPlayer, "Produced Audio Frame. Num Frames: {}", - self->m_audio_frames.Size()); + m_audio_frames.Size()); } } } LOG_INFO(Lib_AvPlayer, "Audio Decoder Thread exited normaly"); - scePthreadExit(nullptr); +} + +bool AvPlayerSource::HasRunningThreads() const { + return m_demuxer_thread.joinable() || m_video_decoder_thread.joinable() || + m_audio_decoder_thread.joinable(); } } // namespace Libraries::AvPlayer diff --git a/src/core/libraries/avplayer/avplayer_source.h b/src/core/libraries/avplayer/avplayer_source.h index 67ad44d8..c2f9ac02 100644 --- a/src/core/libraries/avplayer/avplayer_source.h +++ b/src/core/libraries/avplayer/avplayer_source.h @@ -12,8 +12,11 @@ #include #include +#include #include +#include #include +#include struct AVCodecContext; struct AVFormatContext; @@ -88,8 +91,7 @@ struct Frame { class AvPlayerSource { public: AvPlayerSource(AvPlayerStateCallback& state, std::string_view path, - const SceAvPlayerInitData& init_data, ThreadPriorities& priorities, - SceAvPlayerSourceType source_type); + const SceAvPlayerInitData& init_data, SceAvPlayerSourceType source_type); ~AvPlayerSource(); bool FindStreamInfo(); @@ -109,10 +111,6 @@ public: private: using ScePthread = Kernel::ScePthread; - static void* PS4_SYSV_ABI DemuxerThread(void* opaque); - static void* PS4_SYSV_ABI VideoDecoderThread(void* opaque); - static void* PS4_SYSV_ABI AudioDecoderThread(void* opaque); - static void ReleaseAVPacket(AVPacket* packet); static void ReleaseAVFrame(AVFrame* frame); static void ReleaseAVCodecContext(AVCodecContext* context); @@ -127,6 +125,12 @@ private: using SWSContextPtr = std::unique_ptr; using AVFormatContextPtr = std::unique_ptr; + void DemuxerThread(std::stop_token stop); + void VideoDecoderThread(std::stop_token stop); + void AudioDecoderThread(std::stop_token stop); + + bool HasRunningThreads() const; + AVFramePtr ConvertAudioFrame(const AVFrame& frame); AVFramePtr ConvertVideoFrame(const AVFrame& frame); @@ -135,13 +139,11 @@ private: AvPlayerStateCallback& m_state; - ThreadPriorities m_priorities{}; SceAvPlayerMemAllocator m_memory_replacement{}; u64 m_num_output_video_framebuffers{}; std::atomic_bool m_is_looping = false; std::atomic_bool m_is_eof = false; - std::atomic_bool m_is_stop = false; std::unique_ptr m_up_data_streamer; @@ -160,9 +162,10 @@ private: std::optional m_video_stream_index{}; std::optional m_audio_stream_index{}; - ScePthread m_demuxer_thread{}; - ScePthread m_video_decoder_thread{}; - ScePthread m_audio_decoder_thread{}; + std::mutex m_state_mutex; + std::jthread m_demuxer_thread{}; + std::jthread m_video_decoder_thread{}; + std::jthread m_audio_decoder_thread{}; AVFormatContextPtr m_avformat_context{nullptr, &ReleaseAVFormatContext}; AVCodecContextPtr m_video_codec_context{nullptr, &ReleaseAVCodecContext}; diff --git a/src/core/libraries/avplayer/avplayer_state.cpp b/src/core/libraries/avplayer/avplayer_state.cpp index e7aa4571..061d1da6 100644 --- a/src/core/libraries/avplayer/avplayer_state.cpp +++ b/src/core/libraries/avplayer/avplayer_state.cpp @@ -89,12 +89,8 @@ void PS4_SYSV_ABI AvPlayerState::AutoPlayEventCallback(void* opaque, s32 event_i } // Called inside GAME thread -AvPlayerState::AvPlayerState(const SceAvPlayerInitData& init_data, - const ThreadPriorities& priorities) - : m_init_data(init_data), m_event_replacement(init_data.event_replacement), - m_thread_priorities(priorities), - m_event_handler_mutex(PTHREAD_MUTEX_ERRORCHECK, "AvPlayer_EventHandler"), - m_state_machine_mutex(PTHREAD_MUTEX_ERRORCHECK, "AvPlayer_StateMachine") { +AvPlayerState::AvPlayerState(const SceAvPlayerInitData& init_data) + : m_init_data(init_data), m_event_replacement(init_data.event_replacement) { if (m_event_replacement.event_callback == nullptr || init_data.auto_start) { m_auto_start = true; m_init_data.event_replacement.event_callback = &AvPlayerState::AutoPlayEventCallback; @@ -111,10 +107,9 @@ AvPlayerState::~AvPlayerState() { if (m_up_source && m_current_state == AvState::Play) { m_up_source->Stop(); } - m_quit = true; - if (m_controller_thread != nullptr) { - void* res = nullptr; - scePthreadJoin(m_controller_thread, &res); + if (m_controller_thread.joinable()) { + m_controller_thread.request_stop(); + m_controller_thread.join(); } m_event_queue.Clear(); } @@ -131,8 +126,7 @@ s32 AvPlayerState::AddSource(std::string_view path, SceAvPlayerSourceType source return -1; } - m_up_source = std::make_unique(*this, path, m_init_data, m_thread_priorities, - source_type); + m_up_source = std::make_unique(*this, path, m_init_data, source_type); AddSourceEvent(); return 0; } @@ -166,17 +160,17 @@ s32 AvPlayerState::Start() { return 0; } -void* AvPlayerState::AvControllerThread(void* p_user_data) { - AvPlayerState* self = reinterpret_cast(p_user_data); - while (!self->m_quit) { - if (self->m_event_queue.Size() != 0) { - self->ProcessEvent(); +void AvPlayerState::AvControllerThread(std::stop_token stop) { + using std::chrono::milliseconds; + + while (!stop.stop_requested()) { + if (m_event_queue.Size() != 0) { + ProcessEvent(); continue; } - sceKernelUsleep(5000); - self->UpdateBufferingState(); + std::this_thread::sleep_for(milliseconds(5)); + UpdateBufferingState(); } - scePthreadExit(0); } // Called inside GAME thread @@ -198,22 +192,9 @@ void AvPlayerState::WarningEvent(s32 id) { } // Called inside GAME thread -int AvPlayerState::StartControllerThread() { - m_quit.store(0); - - ThreadParameters params{ - .p_user_data = this, - .thread_name = "AvPlayer_Controller", - .stack_size = 0x4000, - .priority = m_thread_priority, - .affinity = m_thread_affinity, - }; - m_controller_thread = CreateThread(&AvControllerThread, params); - if (m_controller_thread == nullptr) { - LOG_ERROR(Lib_AvPlayer, "Could not create CONTROLLER thread."); - return -1; - } - return 0; +void AvPlayerState::StartControllerThread() { + m_controller_thread = + std::jthread([this](std::stop_token stop) { this->AvControllerThread(stop); }); } // Called inside GAME thread @@ -262,7 +243,7 @@ bool AvPlayerState::IsActive() { return false; } return m_current_state != AvState::Stop && m_current_state != AvState::Error && - m_up_source->IsActive(); + m_current_state != AvState::EndOfFile && m_up_source->IsActive(); } u64 AvPlayerState::CurrentTime() { @@ -284,7 +265,9 @@ void AvPlayerState::OnError() { OnPlaybackStateChanged(AvState::Error); } -void AvPlayerState::OnEOF() {} +void AvPlayerState::OnEOF() { + SetState(AvState::EndOfFile); +} // Called inside CONTROLLER thread void AvPlayerState::OnPlaybackStateChanged(AvState state) { @@ -347,16 +330,16 @@ void AvPlayerState::EmitEvent(SceAvPlayerEvents event_id, void* event_data) { } // Called inside CONTROLLER thread -int AvPlayerState::ProcessEvent() { +void AvPlayerState::ProcessEvent() { if (m_current_state == AvState::Jump) { - return -2; + return; } std::lock_guard guard(m_event_handler_mutex); auto event = m_event_queue.Pop(); if (!event.has_value()) { - return -1; + return; } switch (event->event) { case AvEventType::WarningId: { @@ -385,16 +368,14 @@ int AvPlayerState::ProcessEvent() { default: break; } - - return 0; } // Called inside CONTROLLER thread -int AvPlayerState::UpdateBufferingState() { +void AvPlayerState::UpdateBufferingState() { if (m_current_state == AvState::Buffering) { const auto has_frames = OnBufferingCheckEvent(10); if (!has_frames.has_value()) { - return -1; + return; } if (has_frames.value()) { const auto state = @@ -405,14 +386,13 @@ int AvPlayerState::UpdateBufferingState() { } else if (m_current_state == AvState::Play) { const auto has_frames = OnBufferingCheckEvent(0); if (!has_frames.has_value()) { - return -1; + return; } if (!has_frames.value()) { SetState(AvState::Buffering); OnPlaybackStateChanged(AvState::Buffering); } } - return 0; } bool AvPlayerState::IsStateTransitionValid(AvState state) { diff --git a/src/core/libraries/avplayer/avplayer_state.h b/src/core/libraries/avplayer/avplayer_state.h index cfddab09..8aacc575 100644 --- a/src/core/libraries/avplayer/avplayer_state.h +++ b/src/core/libraries/avplayer/avplayer_state.h @@ -10,6 +10,9 @@ #include "core/libraries/kernel/thread_management.h" #include +#include +#include +#include namespace Libraries::AvPlayer { @@ -18,7 +21,7 @@ class AvDecoder; class AvPlayerState : public AvPlayerStateCallback { public: - AvPlayerState(const SceAvPlayerInitData& init_data, const ThreadPriorities& priorities); + AvPlayerState(const SceAvPlayerInitData& init_data); ~AvPlayerState(); s32 AddSource(std::string_view filename, SceAvPlayerSourceType source_type); @@ -51,34 +54,32 @@ private: void EmitEvent(SceAvPlayerEvents event_id, void* event_data = nullptr); bool SetState(AvState state); - static void* PS4_SYSV_ABI AvControllerThread(void* p_user_data); + void AvControllerThread(std::stop_token stop); void AddSourceEvent(); void WarningEvent(s32 id); - int StartControllerThread(); - int ProcessEvent(); - int UpdateBufferingState(); + void StartControllerThread(); + void ProcessEvent(); + void UpdateBufferingState(); bool IsStateTransitionValid(AvState state); std::unique_ptr m_up_source; SceAvPlayerInitData m_init_data{}; SceAvPlayerEventReplacement m_event_replacement{}; - ThreadPriorities m_thread_priorities{}; bool m_auto_start{}; u8 m_default_language[4]{}; - std::atomic_bool m_quit; std::atomic m_current_state; std::atomic m_previous_state; u32 m_thread_priority; u32 m_thread_affinity; std::atomic_uint32_t m_some_event_result{}; - PthreadMutex m_state_machine_mutex{}; - PthreadMutex m_event_handler_mutex{}; - ScePthread m_controller_thread{}; + std::mutex m_state_machine_mutex{}; + std::mutex m_event_handler_mutex{}; + std::jthread m_controller_thread{}; AvPlayerQueue m_event_queue{}; }; diff --git a/src/core/libraries/kernel/thread_management.h b/src/core/libraries/kernel/thread_management.h index bd555ccc..c5935275 100644 --- a/src/core/libraries/kernel/thread_management.h +++ b/src/core/libraries/kernel/thread_management.h @@ -158,24 +158,19 @@ void init_pthreads(); void pthreadInitSelfMainThread(); int PS4_SYSV_ABI scePthreadAttrInit(ScePthreadAttr* attr); -int PS4_SYSV_ABI scePthreadAttrDestroy(ScePthreadAttr* attr); int PS4_SYSV_ABI scePthreadAttrSetdetachstate(ScePthreadAttr* attr, int detachstate); int PS4_SYSV_ABI scePthreadAttrSetinheritsched(ScePthreadAttr* attr, int inheritSched); int PS4_SYSV_ABI scePthreadAttrSetschedparam(ScePthreadAttr* attr, const SceKernelSchedParam* param); int PS4_SYSV_ABI scePthreadAttrSetschedpolicy(ScePthreadAttr* attr, int policy); +ScePthread PS4_SYSV_ABI scePthreadSelf(); int PS4_SYSV_ABI scePthreadAttrSetaffinity(ScePthreadAttr* pattr, const /*SceKernelCpumask*/ u64 mask); -int PS4_SYSV_ABI scePthreadAttrSetstacksize(ScePthreadAttr* attr, size_t stack_size); - -ScePthread PS4_SYSV_ABI scePthreadSelf(); int PS4_SYSV_ABI scePthreadSetaffinity(ScePthread thread, const /*SceKernelCpumask*/ u64 mask); int PS4_SYSV_ABI scePthreadGetaffinity(ScePthread thread, /*SceKernelCpumask*/ u64* mask); int PS4_SYSV_ABI scePthreadCreate(ScePthread* thread, const ScePthreadAttr* attr, PthreadEntryFunc start_routine, void* arg, const char* name); -[[noreturn]] void PS4_SYSV_ABI scePthreadExit(void* value_ptr); -int PS4_SYSV_ABI scePthreadJoin(ScePthread thread, void** res); -int PS4_SYSV_ABI scePthreadGetprio(ScePthread thread, int* prio); + int PS4_SYSV_ABI scePthreadSetprio(ScePthread thread, int prio); /*** @@ -183,14 +178,11 @@ int PS4_SYSV_ABI scePthreadSetprio(ScePthread thread, int prio); */ int PS4_SYSV_ABI scePthreadMutexInit(ScePthreadMutex* mutex, const ScePthreadMutexattr* attr, const char* name); -int PS4_SYSV_ABI scePthreadMutexDestroy(ScePthreadMutex* mutex); -int PS4_SYSV_ABI scePthreadMutexLock(ScePthreadMutex* mutex); -int PS4_SYSV_ABI scePthreadMutexUnlock(ScePthreadMutex* mutex); - -int PS4_SYSV_ABI scePthreadMutexattrDestroy(ScePthreadMutexattr* attr); int PS4_SYSV_ABI scePthreadMutexattrInit(ScePthreadMutexattr* attr); int PS4_SYSV_ABI scePthreadMutexattrSettype(ScePthreadMutexattr* attr, int type); int PS4_SYSV_ABI scePthreadMutexattrSetprotocol(ScePthreadMutexattr* attr, int protocol); +int PS4_SYSV_ABI scePthreadMutexLock(ScePthreadMutex* mutex); +int PS4_SYSV_ABI scePthreadMutexUnlock(ScePthreadMutex* mutex); /**** * Cond calls */ diff --git a/src/core/libraries/kernel/time_management.h b/src/core/libraries/kernel/time_management.h index 8934171d..a28f8c13 100644 --- a/src/core/libraries/kernel/time_management.h +++ b/src/core/libraries/kernel/time_management.h @@ -50,7 +50,6 @@ u64 PS4_SYSV_ABI sceKernelGetProcessTime(); u64 PS4_SYSV_ABI sceKernelGetProcessTimeCounter(); u64 PS4_SYSV_ABI sceKernelGetProcessTimeCounterFrequency(); u64 PS4_SYSV_ABI sceKernelReadTsc(); -int PS4_SYSV_ABI sceKernelUsleep(u32 microseconds); void timeSymbolsRegister(Core::Loader::SymbolsResolver* sym); diff --git a/src/core/libraries/save_data/savedata.cpp b/src/core/libraries/save_data/savedata.cpp index 64237994..98ff4fdd 100644 --- a/src/core/libraries/save_data/savedata.cpp +++ b/src/core/libraries/save_data/savedata.cpp @@ -792,10 +792,11 @@ int PS4_SYSV_ABI sceSaveDataTransferringMount() { } s32 PS4_SYSV_ABI sceSaveDataUmount(const OrbisSaveDataMountPoint* mountPoint) { - LOG_INFO(Lib_SaveData, "mountPoint = {}", std::string(mountPoint->data)); - if (std::string(mountPoint->data).empty()) { + if (mountPoint->data == nullptr) { + LOG_WARNING(Lib_SaveData, "mountPoint = nullptr"); return ORBIS_SAVE_DATA_ERROR_NOT_MOUNTED; } + LOG_INFO(Lib_SaveData, "mountPoint = {}", mountPoint->data); const auto& mount_dir = Common::FS::GetUserPath(Common::FS::PathType::SaveDataDir) / std::to_string(1) / game_serial / mountPoint->data; auto* mnt = Common::Singleton::Instance(); From 5c4ac98d499ac10392dfda4e4559c0db659a1fb5 Mon Sep 17 00:00:00 2001 From: Vladislav Mikhalin Date: Wed, 14 Aug 2024 22:00:01 +0300 Subject: [PATCH 08/29] fixing build on linux and mac --- src/audio_core/sdl_audio.cpp | 10 +++++++--- src/core/libraries/avplayer/avplayer.cpp | 7 ------- src/core/libraries/avplayer/avplayer_file_streamer.cpp | 6 +++--- src/core/libraries/avplayer/avplayer_source.h | 3 +-- src/core/libraries/avplayer/avplayer_state.h | 3 +-- src/core/libraries/save_data/savedata.cpp | 5 ++--- 6 files changed, 14 insertions(+), 20 deletions(-) diff --git a/src/audio_core/sdl_audio.cpp b/src/audio_core/sdl_audio.cpp index 9c8c5991..f544c52f 100644 --- a/src/audio_core/sdl_audio.cpp +++ b/src/audio_core/sdl_audio.cpp @@ -1,12 +1,16 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "sdl_audio.h" + +#include "common/assert.h" +#include "core/libraries/error_codes.h" + #include #include #include -#include "common/assert.h" -#include "core/libraries/error_codes.h" -#include "sdl_audio.h" + +#include // std::unique_lock namespace Audio { diff --git a/src/core/libraries/avplayer/avplayer.cpp b/src/core/libraries/avplayer/avplayer.cpp index 8fccc659..8a1152a3 100644 --- a/src/core/libraries/avplayer/avplayer.cpp +++ b/src/core/libraries/avplayer/avplayer.cpp @@ -9,8 +9,6 @@ #include "core/libraries/kernel/thread_management.h" #include "core/libraries/libs.h" -#include // std::max, std::min - namespace Libraries::AvPlayer { using namespace Kernel; @@ -119,11 +117,6 @@ bool PS4_SYSV_ABI sceAvPlayerGetVideoDataEx(SceAvPlayerHandle handle, return res; } -constexpr u32 GetPriority(u32 base, u32 offset) { - // (27D <= base_priority <= 2FC) + offset <= 2FF - return std::min(std::min(std::max(637u, base), 764u) + offset, 767u); -} - SceAvPlayerHandle PS4_SYSV_ABI sceAvPlayerInit(SceAvPlayerInitData* data) { LOG_TRACE(Lib_AvPlayer, "called"); if (data == nullptr) { diff --git a/src/core/libraries/avplayer/avplayer_file_streamer.cpp b/src/core/libraries/avplayer/avplayer_file_streamer.cpp index 4b1ddb6e..dc1386a4 100644 --- a/src/core/libraries/avplayer/avplayer_file_streamer.cpp +++ b/src/core/libraries/avplayer/avplayer_file_streamer.cpp @@ -69,14 +69,14 @@ s64 AvPlayerFileStreamer::Seek(void* opaque, s64 offset, int whence) { if (whence == SEEK_CUR) { self->m_position = - std::min(u64(std::max(0i64, s64(self->m_position) + offset)), self->m_file_size); + std::min(u64(std::max(s64(0), s64(self->m_position) + offset)), self->m_file_size); return self->m_position; } else if (whence == SEEK_SET) { - self->m_position = std::min(u64(std::max(0i64, offset)), self->m_file_size); + self->m_position = std::min(u64(std::max(s64(0), offset)), self->m_file_size); return self->m_position; } else if (whence == SEEK_END) { self->m_position = - std::min(u64(std::max(0i64, s64(self->m_file_size) + offset)), self->m_file_size); + std::min(u64(std::max(s64(0), s64(self->m_file_size) + offset)), self->m_file_size); return self->m_position; } diff --git a/src/core/libraries/avplayer/avplayer_source.h b/src/core/libraries/avplayer/avplayer_source.h index c2f9ac02..b3fbb30d 100644 --- a/src/core/libraries/avplayer/avplayer_source.h +++ b/src/core/libraries/avplayer/avplayer_source.h @@ -8,15 +8,14 @@ #include "avplayer_data_streamer.h" #include "common/types.h" +#include "common/polyfill_thread.h" #include "core/libraries/kernel/thread_management.h" #include #include #include #include -#include #include -#include struct AVCodecContext; struct AVFormatContext; diff --git a/src/core/libraries/avplayer/avplayer_state.h b/src/core/libraries/avplayer/avplayer_state.h index 8aacc575..eed3265f 100644 --- a/src/core/libraries/avplayer/avplayer_state.h +++ b/src/core/libraries/avplayer/avplayer_state.h @@ -7,12 +7,11 @@ #include "avplayer_data_streamer.h" #include "avplayer_source.h" +#include "common/polyfill_thread.h" #include "core/libraries/kernel/thread_management.h" #include #include -#include -#include namespace Libraries::AvPlayer { diff --git a/src/core/libraries/save_data/savedata.cpp b/src/core/libraries/save_data/savedata.cpp index 98ff4fdd..20496d76 100644 --- a/src/core/libraries/save_data/savedata.cpp +++ b/src/core/libraries/save_data/savedata.cpp @@ -792,11 +792,10 @@ int PS4_SYSV_ABI sceSaveDataTransferringMount() { } s32 PS4_SYSV_ABI sceSaveDataUmount(const OrbisSaveDataMountPoint* mountPoint) { - if (mountPoint->data == nullptr) { - LOG_WARNING(Lib_SaveData, "mountPoint = nullptr"); + LOG_INFO(Lib_SaveData, "mountPoint = {}", mountPoint->data); + if (std::string_view(mountPoint->data).empty()) { return ORBIS_SAVE_DATA_ERROR_NOT_MOUNTED; } - LOG_INFO(Lib_SaveData, "mountPoint = {}", mountPoint->data); const auto& mount_dir = Common::FS::GetUserPath(Common::FS::PathType::SaveDataDir) / std::to_string(1) / game_serial / mountPoint->data; auto* mnt = Common::Singleton::Instance(); From b3ef959b25a25f4657fc761d0ca27dde50dbfec5 Mon Sep 17 00:00:00 2001 From: Vladislav Mikhalin Date: Thu, 15 Aug 2024 21:59:59 +0300 Subject: [PATCH 09/29] Fixed threading, migrated to CVs, added looping --- .gitmodules | 2 +- src/core/libraries/avplayer/avplayer.cpp | 7 +- src/core/libraries/avplayer/avplayer_impl.cpp | 7 ++ src/core/libraries/avplayer/avplayer_impl.h | 1 + .../libraries/avplayer/avplayer_source.cpp | 103 +++++++++++------- src/core/libraries/avplayer/avplayer_source.h | 47 +++++++- .../libraries/avplayer/avplayer_state.cpp | 40 +++++-- src/core/libraries/avplayer/avplayer_state.h | 3 + 8 files changed, 161 insertions(+), 49 deletions(-) diff --git a/.gitmodules b/.gitmodules index e96f33ec..60fb5fbb 100644 --- a/.gitmodules +++ b/.gitmodules @@ -63,4 +63,4 @@ url = https://github.com/HowardHinnant/date.git [submodule "externals/ffmpeg-core"] path = externals/ffmpeg-core - url = https://github.com/RPCS3/ffmpeg-core.git + url = https://github.com/shadps4-emu/ext-ffmpeg-core diff --git a/src/core/libraries/avplayer/avplayer.cpp b/src/core/libraries/avplayer/avplayer.cpp index 8a1152a3..78e03d78 100644 --- a/src/core/libraries/avplayer/avplayer.cpp +++ b/src/core/libraries/avplayer/avplayer.cpp @@ -230,6 +230,9 @@ s32 PS4_SYSV_ABI sceAvPlayerSetLooping(SceAvPlayerHandle handle, bool loop_flag) if (handle == nullptr) { return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; } + if (!handle->SetLooping(loop_flag)) { + return ORBIS_AVPLAYER_ERROR_OPERATION_FAILED; + } return ORBIS_OK; } @@ -256,7 +259,9 @@ s32 PS4_SYSV_ABI sceAvPlayerStop(SceAvPlayerHandle handle) { if (handle == nullptr) { return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; } - return handle->Stop(); + const auto res = handle->Stop(); + LOG_TRACE(Lib_AvPlayer, "returning {}", res); + return res; } s32 PS4_SYSV_ABI sceAvPlayerStreamCount(SceAvPlayerHandle handle) { diff --git a/src/core/libraries/avplayer/avplayer_impl.cpp b/src/core/libraries/avplayer/avplayer_impl.cpp index 1114254f..cdfff827 100644 --- a/src/core/libraries/avplayer/avplayer_impl.cpp +++ b/src/core/libraries/avplayer/avplayer_impl.cpp @@ -190,4 +190,11 @@ s32 AvPlayer::Stop() { return ORBIS_OK; } +bool AvPlayer::SetLooping(bool is_looping) { + if (m_state == nullptr) { + return false; + } + return m_state->SetLooping(is_looping); +} + } // namespace Libraries::AvPlayer diff --git a/src/core/libraries/avplayer/avplayer_impl.h b/src/core/libraries/avplayer/avplayer_impl.h index df4acfee..09989d39 100644 --- a/src/core/libraries/avplayer/avplayer_impl.h +++ b/src/core/libraries/avplayer/avplayer_impl.h @@ -37,6 +37,7 @@ public: bool IsActive(); u64 CurrentTime(); s32 Stop(); + bool SetLooping(bool is_looping); private: using ScePthreadMutex = Kernel::ScePthreadMutex; diff --git a/src/core/libraries/avplayer/avplayer_source.cpp b/src/core/libraries/avplayer/avplayer_source.cpp index e235b2c2..964e0fbd 100644 --- a/src/core/libraries/avplayer/avplayer_source.cpp +++ b/src/core/libraries/avplayer/avplayer_source.cpp @@ -27,7 +27,8 @@ AvPlayerSource::AvPlayerSource(AvPlayerStateCallback& state, std::string_view pa const SceAvPlayerInitData& init_data, SceAvPlayerSourceType source_type) : m_state(state), m_memory_replacement(init_data.memory_replacement), - m_num_output_video_framebuffers(init_data.num_output_video_framebuffers) { + m_num_output_video_framebuffers( + std::min(std::max(2, init_data.num_output_video_framebuffers), 16)) { AVFormatContext* context = avformat_alloc_context(); if (init_data.file_replacement.open != nullptr) { m_up_data_streamer = @@ -208,7 +209,7 @@ void AvPlayerSource::SetLooping(bool is_looping) { } std::optional AvPlayerSource::HasFrames(u32 num_frames) { - return m_video_frames.Size() > num_frames || m_is_eof; + return m_video_packets.Size() > num_frames || m_is_eof; } s32 AvPlayerSource::Start() { @@ -255,6 +256,7 @@ bool AvPlayerSource::Stop() { m_video_buffers.Push(std::move(m_current_video_frame.value())); m_current_video_frame.reset(); } + m_stop_cv.Notify(); m_audio_packets.Clear(); m_video_packets.Clear(); @@ -291,30 +293,30 @@ bool AvPlayerSource::GetVideoData(SceAvPlayerFrameInfoEx& video_info) { return false; } - using namespace std::chrono; - while (m_video_frames.Size() == 0 && !m_is_eof) { - std::this_thread::sleep_for(milliseconds(5)); - } + m_video_frames_cv.Wait([this]() { return m_video_frames.Size() != 0 || m_is_eof; }); auto frame = m_video_frames.Pop(); if (!frame.has_value()) { - LOG_WARNING(Lib_AvPlayer, "Could get video frame: no frames."); + LOG_WARNING(Lib_AvPlayer, "Could get video frame. EOF reached."); return false; } { + using namespace std::chrono; auto elapsed_time = duration_cast(high_resolution_clock::now() - m_start_time).count(); - while (elapsed_time < frame->info.timestamp) { - std::this_thread::sleep_for(milliseconds(frame->info.timestamp - elapsed_time)); - elapsed_time = - duration_cast(high_resolution_clock::now() - m_start_time).count(); + if (elapsed_time < frame->info.timestamp) { + if (m_stop_cv.WaitFor(milliseconds(frame->info.timestamp - elapsed_time), + [&]() { return elapsed_time >= frame->info.timestamp; })) { + return false; + } } } // return the buffer to the queue if (m_current_video_frame.has_value()) { m_video_buffers.Push(std::move(m_current_video_frame.value())); + m_video_buffers_cv.Notify(); } m_current_video_frame = std::move(frame->buffer); video_info = frame->info; @@ -326,30 +328,30 @@ bool AvPlayerSource::GetAudioData(SceAvPlayerFrameInfo& audio_info) { return false; } - using namespace std::chrono; - while (m_audio_frames.Size() == 0 && !m_is_eof) { - std::this_thread::sleep_for(milliseconds(5)); - } + m_audio_frames_cv.Wait([this]() { return m_audio_frames.Size() != 0 || m_is_eof; }); auto frame = m_audio_frames.Pop(); if (!frame.has_value()) { - LOG_WARNING(Lib_AvPlayer, "Could get audio frame: no frames."); + LOG_WARNING(Lib_AvPlayer, "Could get audio frame. EOF reached."); return false; } { + using namespace std::chrono; auto elapsed_time = duration_cast(high_resolution_clock::now() - m_start_time).count(); - while (elapsed_time < frame->info.timestamp) { - std::this_thread::sleep_for(milliseconds(frame->info.timestamp - elapsed_time)); - elapsed_time = - duration_cast(high_resolution_clock::now() - m_start_time).count(); + if (elapsed_time < frame->info.timestamp) { + if (m_stop_cv.WaitFor(milliseconds(frame->info.timestamp - elapsed_time), + [&]() { return elapsed_time >= frame->info.timestamp; })) { + return false; + } } } // return the buffer to the queue if (m_current_audio_frame.has_value()) { m_audio_buffers.Push(std::move(m_current_audio_frame.value())); + m_audio_buffers_cv.Notify(); } m_current_audio_frame = std::move(frame->buffer); @@ -424,8 +426,26 @@ void AvPlayerSource::DemuxerThread(std::stop_token stop) { const auto res = av_read_frame(m_avformat_context.get(), up_packet.get()); if (res < 0) { if (res == AVERROR_EOF) { - LOG_INFO(Lib_AvPlayer, "EOF reached in demuxer"); - break; + if (m_is_looping) { + LOG_INFO(Lib_AvPlayer, "EOF reached in demuxer. Looping the source..."); + avio_seek(m_avformat_context->pb, 0, SEEK_SET); + if (m_video_stream_index.has_value()) { + const auto index = m_video_stream_index.value(); + const auto stream = m_avformat_context->streams[index]; + avformat_seek_file(m_avformat_context.get(), index, 0, 0, stream->duration, + 0); + } + if (m_audio_stream_index.has_value()) { + const auto index = m_audio_stream_index.value(); + const auto stream = m_avformat_context->streams[index]; + avformat_seek_file(m_avformat_context.get(), index, 0, 0, stream->duration, + 0); + } + continue; + } else { + LOG_INFO(Lib_AvPlayer, "EOF reached in demuxer. Exiting."); + break; + } } else { LOG_ERROR(Lib_AvPlayer, "Could not read AV frame: error = {}", res); m_state.OnError(); @@ -435,14 +455,20 @@ void AvPlayerSource::DemuxerThread(std::stop_token stop) { } if (up_packet->stream_index == m_video_stream_index) { m_video_packets.Push(std::move(up_packet)); + m_video_packets_cv.Notify(); } else if (up_packet->stream_index == m_audio_stream_index) { m_audio_packets.Push(std::move(up_packet)); + m_audio_packets_cv.Notify(); } } m_is_eof = true; - void* res; + m_video_packets_cv.Notify(); + m_audio_packets_cv.Notify(); + m_video_frames_cv.Notify(); + m_audio_frames_cv.Notify(); + if (m_video_decoder_thread.joinable()) { m_video_decoder_thread.join(); } @@ -457,7 +483,7 @@ void AvPlayerSource::DemuxerThread(std::stop_token stop) { AvPlayerSource::AVFramePtr AvPlayerSource::ConvertVideoFrame(const AVFrame& frame) { auto nv12_frame = AVFramePtr{av_frame_alloc(), &ReleaseAVFrame}; nv12_frame->pts = frame.pts; - nv12_frame->pkt_dts = frame.pkt_dts; + nv12_frame->pkt_dts = frame.pkt_dts < 0 ? 0 : frame.pkt_dts; nv12_frame->format = AV_PIX_FMT_NV12; nv12_frame->width = frame.width; nv12_frame->height = frame.height; @@ -520,8 +546,8 @@ void AvPlayerSource::VideoDecoderThread(std::stop_token stop) { using namespace std::chrono; LOG_INFO(Lib_AvPlayer, "Video Decoder Thread started"); while ((!m_is_eof || m_video_packets.Size() != 0) && !stop.stop_requested()) { - if (m_video_packets.Size() == 0) { - std::this_thread::sleep_for(milliseconds(5)); + if (!m_video_packets_cv.Wait( + stop, [this]() { return m_video_packets.Size() != 0 || m_is_eof; })) { continue; } const auto packet = m_video_packets.Pop(); @@ -537,8 +563,10 @@ void AvPlayerSource::VideoDecoderThread(std::stop_token stop) { return; } while (res >= 0) { - if (m_video_buffers.Size() == 0 && !stop.stop_requested()) { - std::this_thread::sleep_for(milliseconds(5)); + if (!m_video_buffers_cv.Wait(stop, [this]() { return m_video_buffers.Size() != 0; })) { + break; + } + if (m_video_buffers.Size() == 0) { continue; } auto up_frame = AVFramePtr(av_frame_alloc(), &ReleaseAVFrame); @@ -566,8 +594,7 @@ void AvPlayerSource::VideoDecoderThread(std::stop_token stop) { } else { m_video_frames.Push(PrepareVideoFrame(std::move(buffer.value()), *up_frame)); } - LOG_TRACE(Lib_AvPlayer, "Produced Video Frame. Num Frames: {}", - m_video_frames.Size()); + m_video_frames_cv.Notify(); } } } @@ -578,7 +605,7 @@ void AvPlayerSource::VideoDecoderThread(std::stop_token stop) { AvPlayerSource::AVFramePtr AvPlayerSource::ConvertAudioFrame(const AVFrame& frame) { auto pcm16_frame = AVFramePtr{av_frame_alloc(), &ReleaseAVFrame}; pcm16_frame->pts = frame.pts; - pcm16_frame->pkt_dts = frame.pkt_dts; + pcm16_frame->pkt_dts = frame.pkt_dts < 0 ? 0 : frame.pkt_dts; pcm16_frame->format = AV_SAMPLE_FMT_S16; pcm16_frame->ch_layout = frame.ch_layout; pcm16_frame->sample_rate = frame.sample_rate; @@ -638,8 +665,8 @@ void AvPlayerSource::AudioDecoderThread(std::stop_token stop) { using namespace std::chrono; LOG_INFO(Lib_AvPlayer, "Audio Decoder Thread started"); while ((!m_is_eof || m_audio_packets.Size() != 0) && !stop.stop_requested()) { - if (m_audio_packets.Size() == 0) { - std::this_thread::sleep_for(milliseconds(5)); + if (!m_audio_packets_cv.Wait( + stop, [this]() { return m_audio_packets.Size() != 0 || m_is_eof; })) { continue; } const auto packet = m_audio_packets.Pop(); @@ -654,10 +681,13 @@ void AvPlayerSource::AudioDecoderThread(std::stop_token stop) { return; } while (res >= 0) { - if (m_audio_buffers.Size() == 0 && !stop.stop_requested()) { - std::this_thread::sleep_for(milliseconds(5)); + if (!m_audio_buffers_cv.Wait(stop, [this]() { return m_audio_buffers.Size() != 0; })) { + break; + } + if (m_audio_buffers.Size() == 0) { continue; } + auto up_frame = AVFramePtr(av_frame_alloc(), &ReleaseAVFrame); res = avcodec_receive_frame(m_audio_codec_context.get(), up_frame.get()); if (res < 0) { @@ -683,8 +713,7 @@ void AvPlayerSource::AudioDecoderThread(std::stop_token stop) { } else { m_audio_frames.Push(PrepareAudioFrame(std::move(buffer.value()), *up_frame)); } - LOG_TRACE(Lib_AvPlayer, "Produced Audio Frame. Num Frames: {}", - m_audio_frames.Size()); + m_audio_frames_cv.Notify(); } } } diff --git a/src/core/libraries/avplayer/avplayer_source.h b/src/core/libraries/avplayer/avplayer_source.h index b3fbb30d..7144e7ee 100644 --- a/src/core/libraries/avplayer/avplayer_source.h +++ b/src/core/libraries/avplayer/avplayer_source.h @@ -7,12 +7,13 @@ #include "avplayer_common.h" #include "avplayer_data_streamer.h" -#include "common/types.h" #include "common/polyfill_thread.h" +#include "common/types.h" #include "core/libraries/kernel/thread_management.h" #include #include +#include #include #include #include @@ -87,6 +88,36 @@ struct Frame { SceAvPlayerFrameInfoEx info; }; +class EventCV { +public: + template + void Wait(Pred pred) { + std::unique_lock lock(m_mutex); + m_cv.wait(lock, std::move(pred)); + } + + template + bool Wait(std::stop_token stop, Pred pred) { + std::unique_lock lock(m_mutex); + return m_cv.wait(lock, std::move(stop), std::move(pred)); + } + + template + bool WaitFor(std::chrono::duration timeout, Pred pred) { + std::unique_lock lock(m_mutex); + return m_cv.wait_for(lock, timeout, std::move(pred)); + } + + void Notify() { + std::unique_lock lock(m_mutex); + m_cv.notify_all(); + } + +private: + std::mutex m_mutex{}; + std::condition_variable_any m_cv{}; +}; + class AvPlayerSource { public: AvPlayerSource(AvPlayerStateCallback& state, std::string_view path, @@ -139,7 +170,7 @@ private: AvPlayerStateCallback& m_state; SceAvPlayerMemAllocator m_memory_replacement{}; - u64 m_num_output_video_framebuffers{}; + u32 m_num_output_video_framebuffers{}; std::atomic_bool m_is_looping = false; std::atomic_bool m_is_eof = false; @@ -161,7 +192,17 @@ private: std::optional m_video_stream_index{}; std::optional m_audio_stream_index{}; - std::mutex m_state_mutex; + EventCV m_audio_packets_cv{}; + EventCV m_audio_frames_cv{}; + EventCV m_audio_buffers_cv{}; + + EventCV m_video_packets_cv{}; + EventCV m_video_frames_cv{}; + EventCV m_video_buffers_cv{}; + + EventCV m_stop_cv{}; + + std::mutex m_state_mutex{}; std::jthread m_demuxer_thread{}; std::jthread m_video_decoder_thread{}; std::jthread m_audio_decoder_thread{}; diff --git a/src/core/libraries/avplayer/avplayer_state.cpp b/src/core/libraries/avplayer/avplayer_state.cpp index 061d1da6..884cd940 100644 --- a/src/core/libraries/avplayer/avplayer_state.cpp +++ b/src/core/libraries/avplayer/avplayer_state.cpp @@ -104,8 +104,9 @@ AvPlayerState::AvPlayerState(const SceAvPlayerInitData& init_data) } AvPlayerState::~AvPlayerState() { - if (m_up_source && m_current_state == AvState::Play) { - m_up_source->Stop(); + { + std::unique_lock lock(m_source_mutex); + m_up_source.reset(); } if (m_controller_thread.joinable()) { m_controller_thread.request_stop(); @@ -121,18 +122,22 @@ s32 AvPlayerState::AddSource(std::string_view path, SceAvPlayerSourceType source return -1; } - if (m_up_source != nullptr) { - LOG_ERROR(Lib_AvPlayer, "Only one source is supported."); - return -1; - } + { + std::unique_lock lock(m_source_mutex); + if (m_up_source != nullptr) { + LOG_ERROR(Lib_AvPlayer, "Only one source is supported."); + return -1; + } - m_up_source = std::make_unique(*this, path, m_init_data, source_type); + m_up_source = std::make_unique(*this, path, m_init_data, source_type); + } AddSourceEvent(); return 0; } // Called inside GAME thread s32 AvPlayerState::GetStreamCount() { + std::shared_lock lock(m_source_mutex); if (m_up_source == nullptr) { LOG_ERROR(Lib_AvPlayer, "Could not get stream count. No source."); return -1; @@ -142,6 +147,7 @@ s32 AvPlayerState::GetStreamCount() { // Called inside GAME thread s32 AvPlayerState::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info) { + std::shared_lock lock(m_source_mutex); if (m_up_source == nullptr) { LOG_ERROR(Lib_AvPlayer, "Could not get stream {} info. No source.", stream_index); return -1; @@ -151,6 +157,7 @@ s32 AvPlayerState::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info) // Called inside GAME thread s32 AvPlayerState::Start() { + std::shared_lock lock(m_source_mutex); if (m_up_source == nullptr || m_up_source->Start() < 0) { LOG_ERROR(Lib_AvPlayer, "Could not start playback."); return -1; @@ -199,6 +206,7 @@ void AvPlayerState::StartControllerThread() { // Called inside GAME thread bool AvPlayerState::EnableStream(u32 stream_index) { + std::shared_lock lock(m_source_mutex); if (m_up_source == nullptr) { return false; } @@ -207,6 +215,7 @@ bool AvPlayerState::EnableStream(u32 stream_index) { // Called inside GAME thread bool AvPlayerState::Stop() { + std::shared_lock lock(m_source_mutex); if (m_up_source == nullptr || m_current_state == AvState::Stop) { return false; } @@ -218,6 +227,7 @@ bool AvPlayerState::Stop() { } bool AvPlayerState::GetVideoData(SceAvPlayerFrameInfo& video_info) { + std::shared_lock lock(m_source_mutex); if (m_up_source == nullptr) { return false; } @@ -225,6 +235,7 @@ bool AvPlayerState::GetVideoData(SceAvPlayerFrameInfo& video_info) { } bool AvPlayerState::GetVideoData(SceAvPlayerFrameInfoEx& video_info) { + std::shared_lock lock(m_source_mutex); if (m_up_source == nullptr) { return false; } @@ -232,6 +243,7 @@ bool AvPlayerState::GetVideoData(SceAvPlayerFrameInfoEx& video_info) { } bool AvPlayerState::GetAudioData(SceAvPlayerFrameInfo& audio_info) { + std::shared_lock lock(m_source_mutex); if (m_up_source == nullptr) { return false; } @@ -239,6 +251,7 @@ bool AvPlayerState::GetAudioData(SceAvPlayerFrameInfo& audio_info) { } bool AvPlayerState::IsActive() { + std::shared_lock lock(m_source_mutex); if (m_up_source == nullptr) { return false; } @@ -247,6 +260,7 @@ bool AvPlayerState::IsActive() { } u64 AvPlayerState::CurrentTime() { + std::shared_lock lock(m_source_mutex); if (m_up_source == nullptr) { LOG_ERROR(Lib_AvPlayer, "Could not get current time. No source."); return 0; @@ -254,6 +268,16 @@ u64 AvPlayerState::CurrentTime() { return m_up_source->CurrentTime(); } +bool AvPlayerState::SetLooping(bool is_looping) { + std::shared_lock lock(m_source_mutex); + if (m_up_source == nullptr) { + LOG_ERROR(Lib_AvPlayer, "Could not set loop flag. No source."); + return false; + } + m_up_source->SetLooping(is_looping); + return true; +} + // May be called from different threads void AvPlayerState::OnWarning(u32 id) { // Forward to CONTROLLER thread @@ -313,6 +337,7 @@ bool AvPlayerState::SetState(AvState state) { // Called inside CONTROLLER thread std::optional AvPlayerState::OnBufferingCheckEvent(u32 num_frames) { + std::shared_lock lock(m_source_mutex); if (!m_up_source) { return std::nullopt; } @@ -351,6 +376,7 @@ void AvPlayerState::ProcessEvent() { break; } case AvEventType::AddSource: { + std::shared_lock lock(m_source_mutex); if (m_up_source->FindStreamInfo()) { SetState(AvState::Ready); OnPlaybackStateChanged(AvState::Ready); diff --git a/src/core/libraries/avplayer/avplayer_state.h b/src/core/libraries/avplayer/avplayer_state.h index eed3265f..ff80b6ce 100644 --- a/src/core/libraries/avplayer/avplayer_state.h +++ b/src/core/libraries/avplayer/avplayer_state.h @@ -12,6 +12,7 @@ #include #include +#include namespace Libraries::AvPlayer { @@ -34,6 +35,7 @@ public: bool GetVideoData(SceAvPlayerFrameInfoEx& video_info); bool IsActive(); u64 CurrentTime(); + bool SetLooping(bool is_looping); private: using ScePthreadMutex = Kernel::ScePthreadMutex; @@ -76,6 +78,7 @@ private: u32 m_thread_affinity; std::atomic_uint32_t m_some_event_result{}; + std::shared_mutex m_source_mutex{}; std::mutex m_state_machine_mutex{}; std::mutex m_event_handler_mutex{}; std::jthread m_controller_thread{}; From 62741434db967488884dd8d72fb99f0c5bef9fb9 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Thu, 15 Aug 2024 23:22:35 -0700 Subject: [PATCH 10/29] Enable -fexperimental-library when using clang libc++ --- CMakeLists.txt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3685b7f8..09262d83 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -96,6 +96,15 @@ if(HAVE_SEM_TIMEDWAIT OR WIN32) add_compile_options(-DHAVE_SEM_TIMEDWAIT) endif() +if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") + # libc++ requires -fexperimental-library to enable std::jthread and std::stop_token support. + include(CheckCXXSymbolExists) + check_cxx_symbol_exists(_LIBCPP_VERSION version LIBCPP) + if(LIBCPP) + add_compile_options(-fexperimental-library) + endif() +endif() + add_subdirectory(externals) include_directories(src) From 23dddca1f0febc085d0254064a60c5a526575576 Mon Sep 17 00:00:00 2001 From: Vladislav Mikhalin Date: Fri, 16 Aug 2024 10:30:48 +0300 Subject: [PATCH 11/29] last minute fixes --- src/core/libraries/avplayer/avplayer.cpp | 6 ++--- .../libraries/avplayer/avplayer_source.cpp | 25 ++++++++++--------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/core/libraries/avplayer/avplayer.cpp b/src/core/libraries/avplayer/avplayer.cpp index 78e03d78..bd1f6b50 100644 --- a/src/core/libraries/avplayer/avplayer.cpp +++ b/src/core/libraries/avplayer/avplayer.cpp @@ -172,8 +172,8 @@ bool PS4_SYSV_ABI sceAvPlayerIsActive(SceAvPlayerHandle handle) { return res; } -s32 PS4_SYSV_ABI sceAvPlayerJumpToTime(SceAvPlayerHandle handle, uint64_t jump_time_msec) { - LOG_ERROR(Lib_AvPlayer, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceAvPlayerJumpToTime(SceAvPlayerHandle handle, uint64_t time) { + LOG_ERROR(Lib_AvPlayer, "(STUBBED) called, time (msec) = {}", time); if (handle == nullptr) { return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; } @@ -226,7 +226,7 @@ s32 PS4_SYSV_ABI sceAvPlayerSetLogCallback(SceAvPlayerLogCallback log_cb, void* } s32 PS4_SYSV_ABI sceAvPlayerSetLooping(SceAvPlayerHandle handle, bool loop_flag) { - LOG_ERROR(Lib_AvPlayer, "(STUBBED) called, looping = {}", loop_flag); + LOG_TRACE(Lib_AvPlayer, "called, looping = {}", loop_flag); if (handle == nullptr) { return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; } diff --git a/src/core/libraries/avplayer/avplayer_source.cpp b/src/core/libraries/avplayer/avplayer_source.cpp index 964e0fbd..776d389f 100644 --- a/src/core/libraries/avplayer/avplayer_source.cpp +++ b/src/core/libraries/avplayer/avplayer_source.cpp @@ -82,7 +82,7 @@ static s32 CodecTypeToStreamType(AVMediaType codec_type) { } } -static f32 AVRationalToF32(AVRational rational) { +static f32 AVRationalToF32(const AVRational& rational) { return f32(rational.num) / rational.den; } @@ -109,7 +109,8 @@ s32 AvPlayerSource::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info) switch (info.type) { case SCE_AVPLAYER_VIDEO: LOG_INFO(Lib_AvPlayer, "Stream {} is a video stream.", stream_index); - info.details.video.aspect_ratio = AVRationalToF32(p_stream->codecpar->sample_aspect_ratio); + info.details.video.aspect_ratio = + f32(p_stream->codecpar->width) / p_stream->codecpar->height; info.details.video.width = p_stream->codecpar->width; info.details.video.height = p_stream->codecpar->height; if (p_lang_node != nullptr) { @@ -293,7 +294,7 @@ bool AvPlayerSource::GetVideoData(SceAvPlayerFrameInfoEx& video_info) { return false; } - m_video_frames_cv.Wait([this]() { return m_video_frames.Size() != 0 || m_is_eof; }); + m_video_frames_cv.Wait([this] { return m_video_frames.Size() != 0 || m_is_eof; }); auto frame = m_video_frames.Pop(); if (!frame.has_value()) { @@ -307,7 +308,7 @@ bool AvPlayerSource::GetVideoData(SceAvPlayerFrameInfoEx& video_info) { duration_cast(high_resolution_clock::now() - m_start_time).count(); if (elapsed_time < frame->info.timestamp) { if (m_stop_cv.WaitFor(milliseconds(frame->info.timestamp - elapsed_time), - [&]() { return elapsed_time >= frame->info.timestamp; })) { + [&] { return elapsed_time >= frame->info.timestamp; })) { return false; } } @@ -328,7 +329,7 @@ bool AvPlayerSource::GetAudioData(SceAvPlayerFrameInfo& audio_info) { return false; } - m_audio_frames_cv.Wait([this]() { return m_audio_frames.Size() != 0 || m_is_eof; }); + m_audio_frames_cv.Wait([this] { return m_audio_frames.Size() != 0 || m_is_eof; }); auto frame = m_audio_frames.Pop(); if (!frame.has_value()) { @@ -342,7 +343,7 @@ bool AvPlayerSource::GetAudioData(SceAvPlayerFrameInfo& audio_info) { duration_cast(high_resolution_clock::now() - m_start_time).count(); if (elapsed_time < frame->info.timestamp) { if (m_stop_cv.WaitFor(milliseconds(frame->info.timestamp - elapsed_time), - [&]() { return elapsed_time >= frame->info.timestamp; })) { + [&] { return elapsed_time >= frame->info.timestamp; })) { return false; } } @@ -546,8 +547,8 @@ void AvPlayerSource::VideoDecoderThread(std::stop_token stop) { using namespace std::chrono; LOG_INFO(Lib_AvPlayer, "Video Decoder Thread started"); while ((!m_is_eof || m_video_packets.Size() != 0) && !stop.stop_requested()) { - if (!m_video_packets_cv.Wait( - stop, [this]() { return m_video_packets.Size() != 0 || m_is_eof; })) { + if (!m_video_packets_cv.Wait(stop, + [this] { return m_video_packets.Size() != 0 || m_is_eof; })) { continue; } const auto packet = m_video_packets.Pop(); @@ -563,7 +564,7 @@ void AvPlayerSource::VideoDecoderThread(std::stop_token stop) { return; } while (res >= 0) { - if (!m_video_buffers_cv.Wait(stop, [this]() { return m_video_buffers.Size() != 0; })) { + if (!m_video_buffers_cv.Wait(stop, [this] { return m_video_buffers.Size() != 0; })) { break; } if (m_video_buffers.Size() == 0) { @@ -665,8 +666,8 @@ void AvPlayerSource::AudioDecoderThread(std::stop_token stop) { using namespace std::chrono; LOG_INFO(Lib_AvPlayer, "Audio Decoder Thread started"); while ((!m_is_eof || m_audio_packets.Size() != 0) && !stop.stop_requested()) { - if (!m_audio_packets_cv.Wait( - stop, [this]() { return m_audio_packets.Size() != 0 || m_is_eof; })) { + if (!m_audio_packets_cv.Wait(stop, + [this] { return m_audio_packets.Size() != 0 || m_is_eof; })) { continue; } const auto packet = m_audio_packets.Pop(); @@ -681,7 +682,7 @@ void AvPlayerSource::AudioDecoderThread(std::stop_token stop) { return; } while (res >= 0) { - if (!m_audio_buffers_cv.Wait(stop, [this]() { return m_audio_buffers.Size() != 0; })) { + if (!m_audio_buffers_cv.Wait(stop, [this] { return m_audio_buffers.Size() != 0; })) { break; } if (m_audio_buffers.Size() == 0) { From 910e96c420b9f65298e1eb79926855abf7f7167d Mon Sep 17 00:00:00 2001 From: Xphalnos <164882787+Xphalnos@users.noreply.github.com> Date: Fri, 16 Aug 2024 09:45:59 +0200 Subject: [PATCH 12/29] Qt-GUI: Improvements --- src/qt_gui/settings_dialog.ui | 130 +++++++++++++++++++--------------- 1 file changed, 73 insertions(+), 57 deletions(-) diff --git a/src/qt_gui/settings_dialog.ui b/src/qt_gui/settings_dialog.ui index 3302f9e6..eb621cb3 100644 --- a/src/qt_gui/settings_dialog.ui +++ b/src/qt_gui/settings_dialog.ui @@ -11,8 +11,8 @@ 0 0 - 1024 - 768 + 1280 + 720 @@ -21,6 +21,12 @@ 0 + + + 10 + false + + Settings @@ -45,8 +51,8 @@ 0 0 - 1002 - 710 + 1258 + 660 @@ -66,11 +72,11 @@ - + - Emulator Settings + System @@ -262,27 +268,6 @@ - - - - Enable Fullscreen - - - - - - - Show Splash - - - - - - - Is PS4 Pro - - - @@ -306,10 +291,57 @@ + + + + Emulator + + + + + + Enable Fullscreen + + + + + + + Show Splash + + + + + + + Is PS4 Pro + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + + + + - Logger Settings + Logger @@ -384,35 +416,6 @@ - - - - - - Additional Settings - - - - - - Qt::Orientation::Vertical - - - QSizePolicy::Policy::MinimumExpanding - - - - 0 - 0 - - - - - - - - - @@ -430,6 +433,19 @@ 0 + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + From c1fb5d5bca4d066b9e2087cc522299b63568debb Mon Sep 17 00:00:00 2001 From: Herman Semenov Date: Fri, 16 Aug 2024 08:36:05 +0000 Subject: [PATCH 13/29] core,shader_recompiler: added const ref filesystem::path and removed if type size less 16 (#446) --- src/core/file_format/pkg.cpp | 2 +- src/core/file_format/pkg.h | 2 +- src/core/file_format/trp.cpp | 4 ++-- src/core/file_format/trp.h | 4 ++-- src/shader_recompiler/ir/attribute.h | 2 +- src/shader_recompiler/ir/condition.h | 2 +- src/shader_recompiler/ir/passes/ssa_rewrite_pass.cpp | 8 ++++---- src/shader_recompiler/runtime_info.h | 2 +- 8 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/core/file_format/pkg.cpp b/src/core/file_format/pkg.cpp index 6d5fb0d4..336d8101 100644 --- a/src/core/file_format/pkg.cpp +++ b/src/core/file_format/pkg.cpp @@ -350,7 +350,7 @@ bool PKG::Extract(const std::filesystem::path& filepath, const std::filesystem:: return true; } -void PKG::ExtractFiles(const int& index) { +void PKG::ExtractFiles(const int index) { int inode_number = fsTable[index].inode; int inode_type = fsTable[index].type; std::string inode_name = fsTable[index].name; diff --git a/src/core/file_format/pkg.h b/src/core/file_format/pkg.h index 3fef6c1c..b6b09a19 100644 --- a/src/core/file_format/pkg.h +++ b/src/core/file_format/pkg.h @@ -104,7 +104,7 @@ public: ~PKG(); bool Open(const std::filesystem::path& filepath); - void ExtractFiles(const int& index); + void ExtractFiles(const int index); bool Extract(const std::filesystem::path& filepath, const std::filesystem::path& extract, std::string& failreason); diff --git a/src/core/file_format/trp.cpp b/src/core/file_format/trp.cpp index b4d4c95e..f122709e 100644 --- a/src/core/file_format/trp.cpp +++ b/src/core/file_format/trp.cpp @@ -6,7 +6,7 @@ TRP::TRP() = default; TRP::~TRP() = default; -void TRP::GetNPcommID(std::filesystem::path trophyPath, int index) { +void TRP::GetNPcommID(const std::filesystem::path& trophyPath, int index) { std::filesystem::path trpPath = trophyPath / "sce_sys/npbind.dat"; Common::FS::IOFile npbindFile(trpPath, Common::FS::FileAccessMode::Read); if (!npbindFile.IsOpen()) { @@ -27,7 +27,7 @@ static void removePadding(std::vector& vec) { } } -bool TRP::Extract(std::filesystem::path trophyPath) { +bool TRP::Extract(const std::filesystem::path& trophyPath) { std::string title = trophyPath.filename().string(); std::filesystem::path gameSysDir = trophyPath / "sce_sys/trophy/"; if (!std::filesystem::exists(gameSysDir)) { diff --git a/src/core/file_format/trp.h b/src/core/file_format/trp.h index 6d1f13bd..56f49002 100644 --- a/src/core/file_format/trp.h +++ b/src/core/file_format/trp.h @@ -33,8 +33,8 @@ class TRP { public: TRP(); ~TRP(); - bool Extract(std::filesystem::path trophyPath); - void GetNPcommID(std::filesystem::path trophyPath, int index); + bool Extract(const std::filesystem::path& trophyPath); + void GetNPcommID(const std::filesystem::path& trophyPath, int index); private: Crypto crypto; diff --git a/src/shader_recompiler/ir/attribute.h b/src/shader_recompiler/ir/attribute.h index 3f95ff7a..2c67411f 100644 --- a/src/shader_recompiler/ir/attribute.h +++ b/src/shader_recompiler/ir/attribute.h @@ -105,7 +105,7 @@ struct fmt::formatter { constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); } - auto format(const Shader::IR::Attribute& attribute, format_context& ctx) const { + auto format(const Shader::IR::Attribute attribute, format_context& ctx) const { return fmt::format_to(ctx.out(), "{}", Shader::IR::NameOf(attribute)); } }; diff --git a/src/shader_recompiler/ir/condition.h b/src/shader_recompiler/ir/condition.h index 4b60be67..da986c48 100644 --- a/src/shader_recompiler/ir/condition.h +++ b/src/shader_recompiler/ir/condition.h @@ -44,7 +44,7 @@ constexpr std::string_view NameOf(Condition condition) { template <> struct fmt::formatter : formatter { - auto format(const Shader::IR::Condition& cond, format_context& ctx) const { + auto format(const Shader::IR::Condition cond, format_context& ctx) const { return formatter::format(NameOf(cond), ctx); } }; diff --git a/src/shader_recompiler/ir/passes/ssa_rewrite_pass.cpp b/src/shader_recompiler/ir/passes/ssa_rewrite_pass.cpp index 80591492..eef73a65 100644 --- a/src/shader_recompiler/ir/passes/ssa_rewrite_pass.cpp +++ b/src/shader_recompiler/ir/passes/ssa_rewrite_pass.cpp @@ -129,19 +129,19 @@ IR::Opcode UndefOpcode(IR::VectorReg) noexcept { return IR::Opcode::UndefU32; } -IR::Opcode UndefOpcode(const VccLoTag&) noexcept { +IR::Opcode UndefOpcode(const VccLoTag) noexcept { return IR::Opcode::UndefU32; } -IR::Opcode UndefOpcode(const SccLoTag&) noexcept { +IR::Opcode UndefOpcode(const SccLoTag) noexcept { return IR::Opcode::UndefU32; } -IR::Opcode UndefOpcode(const VccHiTag&) noexcept { +IR::Opcode UndefOpcode(const VccHiTag) noexcept { return IR::Opcode::UndefU32; } -IR::Opcode UndefOpcode(const FlagTag&) noexcept { +IR::Opcode UndefOpcode(const FlagTag) noexcept { return IR::Opcode::UndefU1; } diff --git a/src/shader_recompiler/runtime_info.h b/src/shader_recompiler/runtime_info.h index 9b592e12..674d7c5f 100644 --- a/src/shader_recompiler/runtime_info.h +++ b/src/shader_recompiler/runtime_info.h @@ -226,7 +226,7 @@ struct fmt::formatter { constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); } - auto format(const Shader::Stage& stage, format_context& ctx) const { + auto format(const Shader::Stage stage, format_context& ctx) const { constexpr static std::array names = {"fs", "vs", "gs", "es", "hs", "ls", "cs"}; return fmt::format_to(ctx.out(), "{}", names[static_cast(stage)]); } From ad60ae1d404d6f4417195d37ab66caa40a757af6 Mon Sep 17 00:00:00 2001 From: Alexandre Bouvier Date: Fri, 16 Aug 2024 15:56:47 +0000 Subject: [PATCH 14/29] cmake: prefer system ffmpeg library (#451) --- CMakeLists.txt | 3 ++- cmake/FindFFmpeg.cmake | 23 +++++++++++++++++++++++ externals/CMakeLists.txt | 3 ++- 3 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 cmake/FindFFmpeg.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index ff03befc..009006a4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -67,6 +67,7 @@ configure_file("${CMAKE_CURRENT_SOURCE_DIR}/src/common/scm_rev.cpp.in" "${CMAKE_ find_package(Boost 1.84.0 CONFIG) find_package(cryptopp 8.9.0 MODULE) +find_package(FFmpeg 5.1.2 MODULE) find_package(fmt 10.2.1 CONFIG) find_package(glslang 14.2.0 CONFIG) find_package(magic_enum 0.9.6 CONFIG) @@ -607,7 +608,7 @@ endif() create_target_directory_groups(shadps4) -target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient RenderDoc::API ffmpeg) +target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient RenderDoc::API FFmpeg::ffmpeg) target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::SPIRV glslang::glslang SDL3::SDL3) if (APPLE) diff --git a/cmake/FindFFmpeg.cmake b/cmake/FindFFmpeg.cmake new file mode 100644 index 00000000..9c45844b --- /dev/null +++ b/cmake/FindFFmpeg.cmake @@ -0,0 +1,23 @@ +# SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +# SPDX-License-Identifier: GPL-2.0-or-later + +find_package(PkgConfig QUIET) +pkg_check_modules(FFMPEG QUIET IMPORTED_TARGET libavcodec libavfilter libavformat libavutil libswresample libswscale) + +find_file(FFMPEG_VERSION_FILE libavutil/ffversion.h HINTS "${FFMPEG_libavutil_INCLUDEDIR}") +if (FFMPEG_VERSION_FILE) + file(STRINGS "${FFMPEG_VERSION_FILE}" FFMPEG_VERSION_LINE REGEX "FFMPEG_VERSION") + string(REGEX MATCH "[0-9.]+" FFMPEG_VERSION "${FFMPEG_VERSION_LINE}") + unset(FFMPEG_VERSION_LINE) + unset(FFMPEG_VERSION_FILE) +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(FFmpeg + REQUIRED_VARS FFMPEG_LINK_LIBRARIES + VERSION_VAR FFMPEG_VERSION +) + +if (FFmpeg_FOUND AND NOT TARGET FFmpeg::ffmpeg) + add_library(FFmpeg::ffmpeg ALIAS PkgConfig::FFMPEG) +endif() diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index bc313232..0b19034d 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -47,9 +47,10 @@ else() endif() endif() -if (NOT TARGET ffmpeg) +if (NOT TARGET FFmpeg::ffmpeg) set(ARCHITECTURE "x86_64") add_subdirectory(ffmpeg-core) + add_library(FFmpeg::ffmpeg ALIAS ffmpeg) endif() # Zlib-Ng From 444cdfbba545d339084ef9e14d90bda566aa9ee8 Mon Sep 17 00:00:00 2001 From: Vinicius Rangel Date: Fri, 16 Aug 2024 13:49:15 -0300 Subject: [PATCH 15/29] gpu: check right register for primitive restart index (#453) @red-prig suggestion Not sure if it is possible to have trash in the register (maybe if primitive restart is toggled off), but just to make sure. --- src/video_core/amdgpu/liverpool.h | 9 ++++++--- src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp | 6 +++--- src/video_core/renderer_vulkan/vk_graphics_pipeline.h | 3 ++- src/video_core/renderer_vulkan/vk_pipeline_cache.cpp | 3 ++- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/video_core/amdgpu/liverpool.h b/src/video_core/amdgpu/liverpool.h index 706da8ec..e888bdcc 100644 --- a/src/video_core/amdgpu/liverpool.h +++ b/src/video_core/amdgpu/liverpool.h @@ -933,7 +933,7 @@ struct Liverpool { std::array viewport_scissors; std::array viewport_depths; INSERT_PADDING_WORDS(0xA103 - 0xA0D4); - u32 primitive_reset_index; + u32 primitive_restart_index; INSERT_PADDING_WORDS(1); BlendConstants blend_constants; INSERT_PADDING_WORDS(0xA10B - 0xA105 - 4); @@ -973,7 +973,9 @@ struct Liverpool { IndexBufferType index_buffer_type; INSERT_PADDING_WORDS(0xA2A1 - 0xA29E - 2); u32 enable_primitive_id; - INSERT_PADDING_WORDS(0xA2A8 - 0xA2A1 - 1); + INSERT_PADDING_WORDS(3); + u32 enable_primitive_restart; + INSERT_PADDING_WORDS(0xA2A8 - 0xA2A5 - 1); u32 vgt_instance_step_rate_0; u32 vgt_instance_step_rate_1; INSERT_PADDING_WORDS(0xA2D5 - 0xA2A9 - 1); @@ -1160,7 +1162,7 @@ static_assert(GFX6_3D_REG_INDEX(depth_buffer.depth_slice) == 0xA017); static_assert(GFX6_3D_REG_INDEX(color_target_mask) == 0xA08E); static_assert(GFX6_3D_REG_INDEX(color_shader_mask) == 0xA08F); static_assert(GFX6_3D_REG_INDEX(viewport_scissors) == 0xA094); -static_assert(GFX6_3D_REG_INDEX(primitive_reset_index) == 0xA103); +static_assert(GFX6_3D_REG_INDEX(primitive_restart_index) == 0xA103); static_assert(GFX6_3D_REG_INDEX(stencil_control) == 0xA10B); static_assert(GFX6_3D_REG_INDEX(viewports) == 0xA10F); static_assert(GFX6_3D_REG_INDEX(clip_user_data) == 0xA16F); @@ -1181,6 +1183,7 @@ static_assert(GFX6_3D_REG_INDEX(vs_output_control) == 0xA207); static_assert(GFX6_3D_REG_INDEX(index_size) == 0xA29D); static_assert(GFX6_3D_REG_INDEX(index_buffer_type) == 0xA29F); static_assert(GFX6_3D_REG_INDEX(enable_primitive_id) == 0xA2A1); +static_assert(GFX6_3D_REG_INDEX(enable_primitive_restart) == 0xA2A5); static_assert(GFX6_3D_REG_INDEX(vgt_instance_step_rate_0) == 0xA2A8); static_assert(GFX6_3D_REG_INDEX(vgt_instance_step_rate_1) == 0xA2A9); static_assert(GFX6_3D_REG_INDEX(stage_enable) == 0xA2D5); diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 0c516dba..6bfe471c 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -86,10 +86,10 @@ GraphicsPipeline::GraphicsPipeline(const Instance& instance_, Scheduler& schedul const vk::PipelineInputAssemblyStateCreateInfo input_assembly = { .topology = LiverpoolToVK::PrimitiveType(key.prim_type), - .primitiveRestartEnable = key.prim_restart_index != 0, + .primitiveRestartEnable = key.enable_primitive_restart != 0, }; - ASSERT_MSG(key.prim_restart_index == 0 || key.prim_restart_index == 0xFFFF, - "Primitive restart index other than 0xFFFF is not supported"); + ASSERT_MSG(!key.enable_primitive_restart || key.primitive_restart_index == 0xFFFF, + "Primitive restart index other than 0xFFFF is not supported yet"); const vk::PipelineRasterizationStateCreateInfo raster_state = { .depthClampEnable = false, diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h index fc507091..bc8e9913 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h @@ -39,7 +39,8 @@ struct GraphicsPipelineKey { Liverpool::StencilRefMask stencil_ref_front; Liverpool::StencilRefMask stencil_ref_back; Liverpool::PrimitiveType prim_type; - u32 prim_restart_index; + u32 enable_primitive_restart; + u32 primitive_restart_index; Liverpool::PolygonMode polygon_mode; Liverpool::CullMode cull_mode; Liverpool::FrontFace front_face; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 0a94ce6d..faf298a3 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -165,7 +165,8 @@ void PipelineCache::RefreshGraphicsKey() { key.stencil_ref_front = regs.stencil_ref_front; key.stencil_ref_back = regs.stencil_ref_back; key.prim_type = regs.primitive_type; - key.prim_restart_index = regs.primitive_reset_index; + key.enable_primitive_restart = regs.enable_primitive_restart; + key.primitive_restart_index = regs.primitive_restart_index & 1; key.polygon_mode = regs.polygon_control.PolyMode(); key.cull_mode = regs.polygon_control.CullingMode(); key.clip_space = regs.clipper_control.clip_space; From 154771cca5c28438741c200de7c3463bf1c48bfa Mon Sep 17 00:00:00 2001 From: Alexandre Bouvier Date: Fri, 16 Aug 2024 16:49:32 +0000 Subject: [PATCH 16/29] cmake: prefer system renderdoc library (#452) --- CMakeLists.txt | 2 +- cmake/FindRenderDoc.cmake | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 cmake/FindRenderDoc.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 009006a4..a248ed1a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -71,6 +71,7 @@ find_package(FFmpeg 5.1.2 MODULE) find_package(fmt 10.2.1 CONFIG) find_package(glslang 14.2.0 CONFIG) find_package(magic_enum 0.9.6 CONFIG) +find_package(RenderDoc 1.6.0 MODULE) find_package(SDL3 3.1.2 CONFIG) find_package(toml11 3.8.1 CONFIG) find_package(tsl-robin-map 1.3.0 CONFIG) @@ -80,7 +81,6 @@ find_package(xbyak 7.07 CONFIG) find_package(xxHash 0.8.2 MODULE) find_package(zlib-ng 2.2.0 MODULE) find_package(Zydis 4.1.0 CONFIG) -find_package(RenderDoc MODULE) if (APPLE) find_package(date 3.0.1 CONFIG) diff --git a/cmake/FindRenderDoc.cmake b/cmake/FindRenderDoc.cmake new file mode 100644 index 00000000..e4cf8a6d --- /dev/null +++ b/cmake/FindRenderDoc.cmake @@ -0,0 +1,25 @@ +# SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +# SPDX-License-Identifier: GPL-2.0-or-later + +find_path(RENDERDOC_INCLUDE_DIR renderdoc_app.h) + +if (RENDERDOC_INCLUDE_DIR AND EXISTS "${RENDERDOC_INCLUDE_DIR}/renderdoc_app.h") + file(STRINGS "${RENDERDOC_INCLUDE_DIR}/renderdoc_app.h" RENDERDOC_VERSION_LINE REGEX "typedef struct RENDERDOC_API") + string(REGEX REPLACE ".*typedef struct RENDERDOC_API_([0-9]+)_([0-9]+)_([0-9]+).*" "\\1.\\2.\\3" RENDERDOC_VERSION "${RENDERDOC_VERSION_LINE}") + unset(RENDERDOC_VERSION_LINE) +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(RenderDoc + REQUIRED_VARS RENDERDOC_INCLUDE_DIR + VERSION_VAR RENDERDOC_VERSION +) + +if (RenderDoc_FOUND AND NOT TARGET RenderDoc::API) + add_library(RenderDoc::API INTERFACE IMPORTED) + set_target_properties(RenderDoc::API PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${RENDERDOC_INCLUDE_DIR}" + ) +endif() + +mark_as_advanced(RENDERDOC_INCLUDE_DIR) From ff33b00c3ac6c3178cff80a58e0380a8983fc7fb Mon Sep 17 00:00:00 2001 From: Vinicius Rangel Date: Fri, 16 Aug 2024 14:03:19 -0300 Subject: [PATCH 17/29] gpu: primitive_restart bit check typo (#454) --- src/video_core/renderer_vulkan/vk_pipeline_cache.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index faf298a3..6974d850 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -165,8 +165,8 @@ void PipelineCache::RefreshGraphicsKey() { key.stencil_ref_front = regs.stencil_ref_front; key.stencil_ref_back = regs.stencil_ref_back; key.prim_type = regs.primitive_type; - key.enable_primitive_restart = regs.enable_primitive_restart; - key.primitive_restart_index = regs.primitive_restart_index & 1; + key.enable_primitive_restart = regs.enable_primitive_restart & 1; + key.primitive_restart_index = regs.primitive_restart_index; key.polygon_mode = regs.polygon_control.PolyMode(); key.cull_mode = regs.polygon_control.CullingMode(); key.clip_space = regs.clipper_control.clip_space; From 1d1c88ad312456f4bc3c1eb00c2680c2137d095a Mon Sep 17 00:00:00 2001 From: TheTurtle <47210458+raphaelthegreat@users.noreply.github.com> Date: Fri, 16 Aug 2024 20:05:37 +0300 Subject: [PATCH 18/29] control_flow_graph: Initial divergence handling (#434) * control_flow_graph: Initial divergence handling * cfg: Handle additional case * spirv: Handle tgid enable bits * clang format * spirv: Use proper format * translator: Add more instructions --- src/core/libraries/network/net.cpp | 2 +- .../spirv/emit_spirv_context_get_set.cpp | 10 +- .../backend/spirv/spirv_emit_context.cpp | 4 +- .../backend/spirv/spirv_emit_context.h | 4 +- .../frontend/control_flow_graph.cpp | 120 ++++++++++++++---- .../frontend/control_flow_graph.h | 19 +++ .../frontend/translate/scalar_alu.cpp | 2 + .../frontend/translate/translate.cpp | 12 +- .../frontend/translate/vector_memory.cpp | 5 + src/shader_recompiler/runtime_info.h | 1 + src/video_core/amdgpu/liverpool.h | 5 + src/video_core/buffer_cache/buffer_cache.cpp | 2 +- src/video_core/buffer_cache/buffer_cache.h | 1 - .../renderer_vulkan/vk_pipeline_cache.cpp | 3 + 14 files changed, 154 insertions(+), 36 deletions(-) diff --git a/src/core/libraries/network/net.cpp b/src/core/libraries/network/net.cpp index 958f9264..2c03dde3 100644 --- a/src/core/libraries/network/net.cpp +++ b/src/core/libraries/network/net.cpp @@ -10,7 +10,7 @@ #include #endif -#include +#include "common/assert.h" #include "common/logging/log.h" #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp index bbf259fe..f933ed3c 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp @@ -323,7 +323,7 @@ static Id ComponentOffset(EmitContext& ctx, Id address, u32 stride, u32 bit_offs static Id GetBufferFormatValue(EmitContext& ctx, u32 handle, Id address, u32 comp) { auto& buffer = ctx.buffers[handle]; - const auto format = buffer.buffer.GetDataFmt(); + const auto format = buffer.dfmt; switch (format) { case AmdGpu::DataFormat::FormatInvalid: return ctx.f32_zero_value; @@ -348,7 +348,7 @@ static Id GetBufferFormatValue(EmitContext& ctx, u32 handle, Id address, u32 com // uint index = address / 4; Id index = ctx.OpShiftRightLogical(ctx.U32[1], address, ctx.ConstU32(2u)); - const u32 stride = buffer.buffer.GetStride(); + const u32 stride = buffer.stride; if (stride > 4) { const u32 index_offset = u32(AmdGpu::ComponentOffset(format, comp) / 32); if (index_offset > 0) { @@ -360,7 +360,7 @@ static Id GetBufferFormatValue(EmitContext& ctx, u32 handle, Id address, u32 com const u32 bit_offset = AmdGpu::ComponentOffset(format, comp) % 32; const u32 bit_width = AmdGpu::ComponentBits(format, comp); - const auto num_format = buffer.buffer.GetNumberFmt(); + const auto num_format = buffer.nfmt; if (num_format == AmdGpu::NumberFormat::Float) { if (bit_width == 32) { return ctx.OpLoad(ctx.F32[1], ptr); @@ -486,8 +486,8 @@ static Id ConvertF32ToFormat(EmitContext& ctx, Id value, AmdGpu::NumberFormat fo template static void EmitStoreBufferFormatF32xN(EmitContext& ctx, u32 handle, Id address, Id value) { auto& buffer = ctx.buffers[handle]; - const auto format = buffer.buffer.GetDataFmt(); - const auto num_format = buffer.buffer.GetNumberFmt(); + const auto format = buffer.dfmt; + const auto num_format = buffer.nfmt; switch (format) { case AmdGpu::DataFormat::FormatInvalid: diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index 4b732ecd..8b99482f 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -363,7 +363,9 @@ void EmitContext::DefineBuffers() { .binding = binding++, .data_types = data_types, .pointer_type = pointer_type, - .buffer = buffer.GetVsharp(info), + .dfmt = buffer.dfmt, + .nfmt = buffer.nfmt, + .stride = buffer.GetVsharp(info).GetStride(), }); interfaces.push_back(id); i++; diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.h b/src/shader_recompiler/backend/spirv/spirv_emit_context.h index 81237a9a..768b591f 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.h +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.h @@ -207,7 +207,9 @@ public: u32 binding; const VectorIds* data_types; Id pointer_type; - AmdGpu::Buffer buffer; + AmdGpu::DataFormat dfmt; + AmdGpu::NumberFormat nfmt; + u32 stride; }; u32& binding; diff --git a/src/shader_recompiler/frontend/control_flow_graph.cpp b/src/shader_recompiler/frontend/control_flow_graph.cpp index 2925c05d..4f3ab86e 100644 --- a/src/shader_recompiler/frontend/control_flow_graph.cpp +++ b/src/shader_recompiler/frontend/control_flow_graph.cpp @@ -35,15 +35,22 @@ static IR::Condition MakeCondition(Opcode opcode) { return IR::Condition::Execz; case Opcode::S_CBRANCH_EXECNZ: return IR::Condition::Execnz; + case Opcode::S_AND_SAVEEXEC_B64: + case Opcode::S_ANDN2_B64: + return IR::Condition::Execnz; default: return IR::Condition::True; } } +static constexpr size_t LabelReserveSize = 32; + CFG::CFG(Common::ObjectPool& block_pool_, std::span inst_list_) : block_pool{block_pool_}, inst_list{inst_list_} { index_to_pc.resize(inst_list.size() + 1); + labels.reserve(LabelReserveSize); EmitLabels(); + EmitDivergenceLabels(); EmitBlocks(); LinkBlocks(); } @@ -51,14 +58,7 @@ CFG::CFG(Common::ObjectPool& block_pool_, std::span inst_l void CFG::EmitLabels() { // Always set a label at entry point. u32 pc = 0; - labels.push_back(pc); - - const auto add_label = [this](u32 address) { - const auto it = std::ranges::find(labels, address); - if (it == labels.end()) { - labels.push_back(address); - } - }; + AddLabel(pc); // Iterate instruction list and add labels to branch targets. for (u32 i = 0; i < inst_list.size(); i++) { @@ -66,15 +66,15 @@ void CFG::EmitLabels() { const GcnInst inst = inst_list[i]; if (inst.IsUnconditionalBranch()) { const u32 target = inst.BranchTarget(pc); - add_label(target); + AddLabel(target); } else if (inst.IsConditionalBranch()) { const u32 true_label = inst.BranchTarget(pc); const u32 false_label = pc + inst.length; - add_label(true_label); - add_label(false_label); + AddLabel(true_label); + AddLabel(false_label); } else if (inst.opcode == Opcode::S_ENDPGM) { const u32 next_label = pc + inst.length; - add_label(next_label); + AddLabel(next_label); } pc += inst.length; } @@ -84,16 +84,70 @@ void CFG::EmitLabels() { std::ranges::sort(labels); } -void CFG::EmitBlocks() { - const auto get_index = [this](Label label) -> size_t { - if (label == 0) { - return 0ULL; - } - const auto it_index = std::ranges::lower_bound(index_to_pc, label); - ASSERT(it_index != index_to_pc.end() || label > index_to_pc.back()); - return std::distance(index_to_pc.begin(), it_index); +void CFG::EmitDivergenceLabels() { + const auto is_open_scope = [](const GcnInst& inst) { + // An open scope instruction is an instruction that modifies EXEC + // but also saves the previous value to restore later. This indicates + // we are entering a scope. + return inst.opcode == Opcode::S_AND_SAVEEXEC_B64 || + // While this instruction does not save EXEC it is often used paired + // with SAVEEXEC to mask the threads that didn't pass the condition + // of initial branch. + inst.opcode == Opcode::S_ANDN2_B64; + }; + const auto is_close_scope = [](const GcnInst& inst) { + // Closing an EXEC scope can be either a branch instruction + // (typical case when S_AND_SAVEEXEC_B64 is right before a branch) + // or by a move instruction to EXEC that restores the backup. + return (inst.opcode == Opcode::S_MOV_B64 && inst.dst[0].field == OperandField::ExecLo) || + // Sometimes compiler might insert instructions between the SAVEEXEC and the branch. + // Those instructions need to be wrapped in the condition as well so allow branch + // as end scope instruction. + inst.opcode == Opcode::S_CBRANCH_EXECZ || inst.opcode == Opcode::S_ANDN2_B64; }; + // Since we will be adding new labels, avoid iterating those as well. + const size_t end_size = labels.size(); + for (u32 l = 0; l < end_size; l++) { + const Label start = labels[l]; + // Stop if we reached end of existing labels. + if (l == end_size - 1) { + break; + } + const Label end = labels[l + 1]; + const size_t end_index = GetIndex(end); + + s32 curr_begin = -1; + for (size_t index = GetIndex(start); index < end_index; index++) { + const auto& inst = inst_list[index]; + if (is_close_scope(inst) && curr_begin != -1) { + // If there are no instructions inside scope don't do anything. + if (index - curr_begin == 1) { + curr_begin = -1; + continue; + } + // Add a label to the instruction right after the open scope call. + // It is the start of a new basic block. + const auto& save_inst = inst_list[curr_begin]; + const Label label = index_to_pc[curr_begin] + save_inst.length; + AddLabel(label); + // Add a label to the close scope instruction as well. + AddLabel(index_to_pc[index]); + // Reset scope begin. + curr_begin = -1; + } + // Mark a potential start of an exec scope. + if (is_open_scope(inst)) { + curr_begin = index; + } + } + } + + // Sort labels to make sure block insertion is correct. + std::ranges::sort(labels); +} + +void CFG::EmitBlocks() { for (auto it = labels.begin(); it != labels.end(); it++) { const Label start = *it; const auto next_it = std::next(it); @@ -102,8 +156,10 @@ void CFG::EmitBlocks() { // Last label is special. return; } + // The end label is the start instruction of next block. + // The end instruction of this block is the previous one. const Label end = *next_it; - const size_t end_index = get_index(end) - 1; + const size_t end_index = GetIndex(end) - 1; const auto& end_inst = inst_list[end_index]; // Insert block between the labels using the last instruction @@ -111,7 +167,7 @@ void CFG::EmitBlocks() { Block* block = block_pool.Create(); block->begin = start; block->end = end; - block->begin_index = get_index(start); + block->begin_index = GetIndex(start); block->end_index = end_index; block->end_inst = end_inst; block->cond = MakeCondition(end_inst.opcode); @@ -126,8 +182,26 @@ void CFG::LinkBlocks() { return &*it; }; - for (auto& block : blocks) { + for (auto it = blocks.begin(); it != blocks.end(); it++) { + auto& block = *it; const auto end_inst{block.end_inst}; + // Handle divergence block inserted here. + if (end_inst.opcode == Opcode::S_AND_SAVEEXEC_B64 || + end_inst.opcode == Opcode::S_ANDN2_B64) { + // Blocks are stored ordered by address in the set + auto next_it = std::next(it); + auto* target_block = &(*next_it); + ++target_block->num_predecessors; + block.branch_true = target_block; + + auto merge_it = std::next(next_it); + auto* merge_block = &(*merge_it); + ++merge_block->num_predecessors; + block.branch_false = merge_block; + block.end_class = EndClass::Branch; + continue; + } + // If the block doesn't end with a branch we simply // need to link with the next block. if (!end_inst.IsTerminateInstruction()) { diff --git a/src/shader_recompiler/frontend/control_flow_graph.h b/src/shader_recompiler/frontend/control_flow_graph.h index ebe614ee..d98d4b05 100644 --- a/src/shader_recompiler/frontend/control_flow_graph.h +++ b/src/shader_recompiler/frontend/control_flow_graph.h @@ -3,11 +3,13 @@ #pragma once +#include #include #include #include #include +#include "common/assert.h" #include "common/object_pool.h" #include "common/types.h" #include "shader_recompiler/frontend/instruction.h" @@ -55,9 +57,26 @@ public: private: void EmitLabels(); + void EmitDivergenceLabels(); void EmitBlocks(); void LinkBlocks(); + void AddLabel(Label address) { + const auto it = std::ranges::find(labels, address); + if (it == labels.end()) { + labels.push_back(address); + } + }; + + size_t GetIndex(Label label) { + if (label == 0) { + return 0ULL; + } + const auto it_index = std::ranges::lower_bound(index_to_pc, label); + ASSERT(it_index != index_to_pc.end() || label > index_to_pc.back()); + return std::distance(index_to_pc.begin(), it_index); + }; + public: Common::ObjectPool& block_pool; std::span inst_list; diff --git a/src/shader_recompiler/frontend/translate/scalar_alu.cpp b/src/shader_recompiler/frontend/translate/scalar_alu.cpp index 795b148d..812d93ba 100644 --- a/src/shader_recompiler/frontend/translate/scalar_alu.cpp +++ b/src/shader_recompiler/frontend/translate/scalar_alu.cpp @@ -29,6 +29,8 @@ void Translator::EmitScalarAlu(const GcnInst& inst) { return S_CMP(ConditionOp::LG, true, inst); case Opcode::S_CMP_GT_I32: return S_CMP(ConditionOp::GT, true, inst); + case Opcode::S_CMP_LE_I32: + return S_CMP(ConditionOp::LE, true, inst); case Opcode::S_CMP_GE_I32: return S_CMP(ConditionOp::GE, true, inst); case Opcode::S_CMP_EQ_I32: diff --git a/src/shader_recompiler/frontend/translate/translate.cpp b/src/shader_recompiler/frontend/translate/translate.cpp index d48e4def..4070560a 100644 --- a/src/shader_recompiler/frontend/translate/translate.cpp +++ b/src/shader_recompiler/frontend/translate/translate.cpp @@ -64,9 +64,15 @@ void Translator::EmitPrologue() { ir.SetVectorReg(dst_vreg++, ir.GetAttributeU32(IR::Attribute::LocalInvocationId, 1)); ir.SetVectorReg(dst_vreg++, ir.GetAttributeU32(IR::Attribute::LocalInvocationId, 2)); - ir.SetScalarReg(dst_sreg++, ir.GetAttributeU32(IR::Attribute::WorkgroupId, 0)); - ir.SetScalarReg(dst_sreg++, ir.GetAttributeU32(IR::Attribute::WorkgroupId, 1)); - ir.SetScalarReg(dst_sreg++, ir.GetAttributeU32(IR::Attribute::WorkgroupId, 2)); + if (info.tgid_enable[0]) { + ir.SetScalarReg(dst_sreg++, ir.GetAttributeU32(IR::Attribute::WorkgroupId, 0)); + } + if (info.tgid_enable[1]) { + ir.SetScalarReg(dst_sreg++, ir.GetAttributeU32(IR::Attribute::WorkgroupId, 1)); + } + if (info.tgid_enable[2]) { + ir.SetScalarReg(dst_sreg++, ir.GetAttributeU32(IR::Attribute::WorkgroupId, 2)); + } break; default: throw NotImplementedException("Unknown shader stage"); diff --git a/src/shader_recompiler/frontend/translate/vector_memory.cpp b/src/shader_recompiler/frontend/translate/vector_memory.cpp index 63f6c3b4..01a549f4 100644 --- a/src/shader_recompiler/frontend/translate/vector_memory.cpp +++ b/src/shader_recompiler/frontend/translate/vector_memory.cpp @@ -91,6 +91,11 @@ void Translator::EmitVectorMemory(const GcnInst& inst) { case Opcode::BUFFER_STORE_FORMAT_XYZW: return BUFFER_STORE_FORMAT(4, false, true, inst); + case Opcode::TBUFFER_STORE_FORMAT_X: + return BUFFER_STORE_FORMAT(1, true, true, inst); + case Opcode::TBUFFER_STORE_FORMAT_XYZ: + return BUFFER_STORE_FORMAT(3, true, true, inst); + case Opcode::BUFFER_STORE_DWORD: return BUFFER_STORE_FORMAT(1, false, false, inst); case Opcode::BUFFER_STORE_DWORDX2: diff --git a/src/shader_recompiler/runtime_info.h b/src/shader_recompiler/runtime_info.h index 674d7c5f..b1eb6aea 100644 --- a/src/shader_recompiler/runtime_info.h +++ b/src/shader_recompiler/runtime_info.h @@ -180,6 +180,7 @@ struct Info { SamplerResourceList samplers; std::array workgroup_size{}; + std::array tgid_enable; u32 num_user_data; u32 num_input_vgprs; diff --git a/src/video_core/amdgpu/liverpool.h b/src/video_core/amdgpu/liverpool.h index e888bdcc..92a24795 100644 --- a/src/video_core/amdgpu/liverpool.h +++ b/src/video_core/amdgpu/liverpool.h @@ -130,6 +130,7 @@ struct Liverpool { BitField<0, 6, u64> num_vgprs; BitField<6, 4, u64> num_sgprs; BitField<33, 5, u64> num_user_regs; + BitField<39, 3, u64> tgid_enable; BitField<47, 9, u64> lds_dwords; } settings; INSERT_PADDING_WORDS(1); @@ -148,6 +149,10 @@ struct Liverpool { return settings.lds_dwords.Value() * 128 * 4; } + bool IsTgidEnabled(u32 i) const noexcept { + return (settings.tgid_enable.Value() >> i) & 1; + } + std::span Code() const { const u32* code = Address(); BinaryInfo bininfo; diff --git a/src/video_core/buffer_cache/buffer_cache.cpp b/src/video_core/buffer_cache/buffer_cache.cpp index 2246807a..02d6b2ce 100644 --- a/src/video_core/buffer_cache/buffer_cache.cpp +++ b/src/video_core/buffer_cache/buffer_cache.cpp @@ -13,7 +13,7 @@ namespace VideoCore { -static constexpr size_t StagingBufferSize = 256_MB; +static constexpr size_t StagingBufferSize = 512_MB; static constexpr size_t UboStreamBufferSize = 64_MB; BufferCache::BufferCache(const Vulkan::Instance& instance_, Vulkan::Scheduler& scheduler_, diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 33ea3f86..2bcc4f0e 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -3,7 +3,6 @@ #pragma once -#include #include #include #include diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 6974d850..c11705e7 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -93,6 +93,8 @@ Shader::Info MakeShaderInfo(Shader::Stage stage, std::span user_d info.num_user_data = cs_pgm.settings.num_user_regs; info.workgroup_size = {cs_pgm.num_thread_x.full, cs_pgm.num_thread_y.full, cs_pgm.num_thread_z.full}; + info.tgid_enable = {cs_pgm.IsTgidEnabled(0), cs_pgm.IsTgidEnabled(1), + cs_pgm.IsTgidEnabled(2)}; info.shared_memory_size = cs_pgm.SharedMemSize(); break; } @@ -324,6 +326,7 @@ std::unique_ptr PipelineCache::CreateComputePipeline() { Shader::Info info = MakeShaderInfo(Shader::Stage::Compute, cs_pgm.user_data, liverpool->regs); info.pgm_base = cs_pgm.Address(); + info.pgm_hash = compute_key; auto program = Shader::TranslateProgram(inst_pool, block_pool, code, std::move(info), profile); From 9e810b75244dcc2e02bf518ff93d52968e7f7657 Mon Sep 17 00:00:00 2001 From: Dzmitry Dubrova Date: Fri, 16 Aug 2024 20:11:55 +0300 Subject: [PATCH 19/29] core: Some small pad stubs (#424) * core: Some small pad stubs * core: handle scePadSetLightBar wrong lightbar setting --- src/core/libraries/pad/pad.cpp | 23 ++++++++++++++++++----- src/core/libraries/pad/pad.h | 16 +++++++++++++++- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/src/core/libraries/pad/pad.cpp b/src/core/libraries/pad/pad.cpp index c9e332d2..305b20bd 100644 --- a/src/core/libraries/pad/pad.cpp +++ b/src/core/libraries/pad/pad.cpp @@ -105,7 +105,7 @@ int PS4_SYSV_ABI scePadGetControllerInformation(s32 handle, OrbisPadControllerIn pInfo->stickInfo.deadZoneRight = 2; pInfo->connectionType = ORBIS_PAD_PORT_TYPE_STANDARD; pInfo->connectedCount = 1; - pInfo->connected = 1; + pInfo->connected = true; pInfo->deviceClass = ORBIS_PAD_DEVICE_CLASS_STANDARD; return SCE_OK; } @@ -125,9 +125,16 @@ int PS4_SYSV_ABI scePadGetDeviceInfo() { return ORBIS_OK; } -int PS4_SYSV_ABI scePadGetExtControllerInformation() { - LOG_ERROR(Lib_Pad, "(STUBBED) called"); - return ORBIS_OK; +int PS4_SYSV_ABI scePadGetExtControllerInformation(s32 handle, + OrbisPadExtendedControllerInformation* pInfo) { + LOG_INFO(Lib_Pad, "called handle = {}", handle); + + pInfo->padType1 = 0; + pInfo->padType2 = 0; + pInfo->capability = 0; + + auto res = scePadGetControllerInformation(handle, &pInfo->base); + return res; } int PS4_SYSV_ABI scePadGetExtensionUnitInfo() { @@ -237,7 +244,7 @@ int PS4_SYSV_ABI scePadOpen(s32 userId, s32 type, s32 index, const OrbisPadOpenP int PS4_SYSV_ABI scePadOpenExt() { LOG_ERROR(Lib_Pad, "(STUBBED) called"); - return ORBIS_OK; + return 1; // dummy } int PS4_SYSV_ABI scePadOpenExt2() { @@ -422,6 +429,12 @@ int PS4_SYSV_ABI scePadSetLightBar(s32 handle, const OrbisPadLightBarParam* pPar if (pParam != nullptr) { LOG_INFO(Lib_Pad, "scePadSetLightBar called handle = {} rgb = {} {} {}", handle, pParam->r, pParam->g, pParam->b); + + if (pParam->r < 0xD && pParam->g < 0xD && pParam->b < 0xD) { + LOG_INFO(Lib_Pad, "Invalid lightbar setting"); + return ORBIS_PAD_ERROR_INVALID_LIGHTBAR_SETTING; + } + auto* controller = Common::Singleton::Instance(); controller->SetLightBarRGB(pParam->r, pParam->g, pParam->b); return ORBIS_OK; diff --git a/src/core/libraries/pad/pad.h b/src/core/libraries/pad/pad.h index 4f854290..3e9c14a1 100644 --- a/src/core/libraries/pad/pad.h +++ b/src/core/libraries/pad/pad.h @@ -212,6 +212,19 @@ struct OrbisPadControllerInformation { u8 reserve[8]; }; +struct OrbisPadExtendedControllerInformation { + OrbisPadControllerInformation base; + u16 padType1; + u16 padType2; + u8 capability; + + union { + u8 quantityOfSelectorSwitch; + int maxPhysicalWheelAngle; + u8 data[8]; + }; +}; + struct OrbisPadOpenParam { u8 reserve[8]; }; @@ -248,7 +261,8 @@ int PS4_SYSV_ABI scePadGetControllerInformation(s32 handle, OrbisPadControllerIn int PS4_SYSV_ABI scePadGetDataInternal(); int PS4_SYSV_ABI scePadGetDeviceId(); int PS4_SYSV_ABI scePadGetDeviceInfo(); -int PS4_SYSV_ABI scePadGetExtControllerInformation(); +int PS4_SYSV_ABI scePadGetExtControllerInformation(s32 handle, + OrbisPadExtendedControllerInformation* pInfo); int PS4_SYSV_ABI scePadGetExtensionUnitInfo(); int PS4_SYSV_ABI scePadGetFeatureReport(); int PS4_SYSV_ABI scePadGetHandle(s32 userId, s32 type, s32 index); From dcb057dd7f627ff3e9f7fafc3769f324ad3f9f2b Mon Sep 17 00:00:00 2001 From: Dzmitry Dubrova Date: Fri, 16 Aug 2024 20:16:15 +0300 Subject: [PATCH 20/29] misc changes, part ?/? (#441) * gui: add option to boot a game by choosing elf file * core: some small implementations * fs: implement open func * add some validations * spirv: add image format * video_core: add eR16Uint to formats --- src/core/libraries/kernel/event_queues.cpp | 3 +++ src/core/libraries/kernel/event_queues.h | 1 + src/core/libraries/kernel/file_system.cpp | 10 ++++++++ src/core/libraries/kernel/libkernel.cpp | 1 + src/core/libraries/videoout/video_out.cpp | 25 +++++++++++++++++++ src/core/libraries/videoout/video_out.h | 2 ++ src/qt_gui/main_window.cpp | 22 ++++++++++++++++ src/qt_gui/main_window.h | 1 + src/qt_gui/main_window_ui.h | 5 ++++ .../backend/spirv/spirv_emit_context.cpp | 4 +++ .../renderer_vulkan/liverpool_to_vk.cpp | 1 + 11 files changed, 75 insertions(+) diff --git a/src/core/libraries/kernel/event_queues.cpp b/src/core/libraries/kernel/event_queues.cpp index bb3d8ba7..540c20c4 100644 --- a/src/core/libraries/kernel/event_queues.cpp +++ b/src/core/libraries/kernel/event_queues.cpp @@ -208,4 +208,7 @@ int PS4_SYSV_ABI sceKernelDeleteUserEvent(SceKernelEqueue eq, int id) { return ORBIS_OK; } +s16 PS4_SYSV_ABI sceKernelGetEventFilter(const SceKernelEvent* ev) { + return ev->filter; +} } // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/event_queues.h b/src/core/libraries/kernel/event_queues.h index 0f9c42a9..d400ff18 100644 --- a/src/core/libraries/kernel/event_queues.h +++ b/src/core/libraries/kernel/event_queues.h @@ -21,5 +21,6 @@ int PS4_SYSV_ABI sceKernelDeleteUserEvent(SceKernelEqueue eq, int id); int PS4_SYSV_ABI sceKernelAddUserEvent(SceKernelEqueue eq, int id); int PS4_SYSV_ABI sceKernelAddUserEventEdge(SceKernelEqueue eq, int id); s32 PS4_SYSV_ABI sceKernelAddHRTimerEvent(SceKernelEqueue eq, int id, timespec* ts, void* udata); +s16 PS4_SYSV_ABI sceKernelGetEventFilter(const SceKernelEvent* ev); } // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/file_system.cpp b/src/core/libraries/kernel/file_system.cpp index f8386347..990b11d6 100644 --- a/src/core/libraries/kernel/file_system.cpp +++ b/src/core/libraries/kernel/file_system.cpp @@ -112,6 +112,15 @@ int PS4_SYSV_ABI posix_open(const char* path, int flags, /* SceKernelMode*/ u16 return result; } +int PS4_SYSV_ABI open(const char* filename, const char* mode) { + LOG_INFO(Kernel_Fs, "open redirect to sceKernelOpen"); + int result = sceKernelOpen(filename, ORBIS_KERNEL_O_RDWR, 0); + if (result < 0) { + return -1; + } + return result; +} + int PS4_SYSV_ABI sceKernelClose(int d) { if (d < 3) { // d probably hold an error code return ORBIS_KERNEL_ERROR_EPERM; @@ -498,6 +507,7 @@ void fileSystemSymbolsRegister(Core::Loader::SymbolsResolver* sym) { std::srand(std::time(nullptr)); LIB_FUNCTION("1G3lF1Gg1k8", "libkernel", 1, "libkernel", 1, 1, sceKernelOpen); LIB_FUNCTION("wuCroIGjt2g", "libScePosix", 1, "libkernel", 1, 1, posix_open); + LIB_FUNCTION("wuCroIGjt2g", "libkernel", 1, "libkernel", 1, 1, open); LIB_FUNCTION("UK2Tl2DWUns", "libkernel", 1, "libkernel", 1, 1, sceKernelClose); LIB_FUNCTION("bY-PO6JhzhQ", "libkernel", 1, "libkernel", 1, 1, posix_close); LIB_FUNCTION("bY-PO6JhzhQ", "libScePosix", 1, "libkernel", 1, 1, posix_close); diff --git a/src/core/libraries/kernel/libkernel.cpp b/src/core/libraries/kernel/libkernel.cpp index 9657ba04..77c5be8a 100644 --- a/src/core/libraries/kernel/libkernel.cpp +++ b/src/core/libraries/kernel/libkernel.cpp @@ -423,6 +423,7 @@ void LibKernel_Register(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("F6e0kwo4cnk", "libkernel", 1, "libkernel", 1, 1, sceKernelTriggerUserEvent); LIB_FUNCTION("LJDwdSNTnDg", "libkernel", 1, "libkernel", 1, 1, sceKernelDeleteUserEvent); LIB_FUNCTION("mJ7aghmgvfc", "libkernel", 1, "libkernel", 1, 1, sceKernelGetEventId); + LIB_FUNCTION("23CPPI1tyBY", "libkernel", 1, "libkernel", 1, 1, sceKernelGetEventFilter); // misc LIB_FUNCTION("WslcK1FQcGI", "libkernel", 1, "libkernel", 1, 1, sceKernelIsNeoMode); diff --git a/src/core/libraries/videoout/video_out.cpp b/src/core/libraries/videoout/video_out.cpp index ab9eac76..d13062cd 100644 --- a/src/core/libraries/videoout/video_out.cpp +++ b/src/core/libraries/videoout/video_out.cpp @@ -151,6 +151,28 @@ s32 PS4_SYSV_ABI sceVideoOutSubmitFlip(s32 handle, s32 bufferIndex, s32 flipMode return ORBIS_OK; } +int PS4_SYSV_ABI sceVideoOutGetEventId(const Kernel::SceKernelEvent* ev) { + if (ev == nullptr) { + return SCE_VIDEO_OUT_ERROR_INVALID_ADDRESS; + } + if (ev->filter != Kernel::SceKernelEvent::Filter::VideoOut) { + return ORBIS_VIDEO_OUT_ERROR_INVALID_EVENT_QUEUE; + } + return ev->ident; +} + +int PS4_SYSV_ABI sceVideoOutGetEventData(const Kernel::SceKernelEvent* ev, int64_t* data) { + if (ev == nullptr || data == nullptr) { + return SCE_VIDEO_OUT_ERROR_INVALID_ADDRESS; + } + if (ev->filter != Kernel::SceKernelEvent::Filter::VideoOut) { + return ORBIS_VIDEO_OUT_ERROR_INVALID_EVENT_QUEUE; + } + + *data = ev->data; + return ORBIS_OK; +} + s32 PS4_SYSV_ABI sceVideoOutGetFlipStatus(s32 handle, FlipStatus* status) { if (!status) { LOG_ERROR(Lib_VideoOut, "Flip status is null"); @@ -302,6 +324,9 @@ void RegisterLib(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("kGVLc3htQE8", "libSceVideoOut", 1, "libSceVideoOut", 0, 0, sceVideoOutGetDeviceCapabilityInfo); LIB_FUNCTION("j6RaAUlaLv0", "libSceVideoOut", 1, "libSceVideoOut", 0, 0, sceVideoOutWaitVblank); + LIB_FUNCTION("U2JJtSqNKZI", "libSceVideoOut", 1, "libSceVideoOut", 0, 0, sceVideoOutGetEventId); + LIB_FUNCTION("rWUTcKdkUzQ", "libSceVideoOut", 1, "libSceVideoOut", 0, 0, + sceVideoOutGetEventData); // openOrbis appears to have libSceVideoOut_v1 module libSceVideoOut_v1.1 LIB_FUNCTION("Up36PTk687E", "libSceVideoOut", 1, "libSceVideoOut", 1, 1, sceVideoOutOpen); diff --git a/src/core/libraries/videoout/video_out.h b/src/core/libraries/videoout/video_out.h index b4423efd..63cd8fed 100644 --- a/src/core/libraries/videoout/video_out.h +++ b/src/core/libraries/videoout/video_out.h @@ -104,6 +104,8 @@ s32 PS4_SYSV_ABI sceVideoOutGetResolutionStatus(s32 handle, SceVideoOutResolutio s32 PS4_SYSV_ABI sceVideoOutOpen(SceUserServiceUserId userId, s32 busType, s32 index, const void* param); s32 PS4_SYSV_ABI sceVideoOutClose(s32 handle); +int PS4_SYSV_ABI sceVideoOutGetEventId(const Kernel::SceKernelEvent* ev); +int PS4_SYSV_ABI sceVideoOutGetEventData(const Kernel::SceKernelEvent* ev, int64_t* data); // Internal system functions void sceVideoOutGetBufferLabelAddress(s32 handle, uintptr_t* label_addr); diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp index aec2e7a5..58f925b1 100644 --- a/src/qt_gui/main_window.cpp +++ b/src/qt_gui/main_window.cpp @@ -328,6 +328,7 @@ void MainWindow::CreateConnects() { // Package install. connect(ui->bootInstallPkgAct, &QAction::triggered, this, &MainWindow::InstallPkg); + connect(ui->bootGameAct, &QAction::triggered, this, &MainWindow::BootGame); connect(ui->gameInstallPathAct, &QAction::triggered, this, &MainWindow::InstallDirectory); // elf viewer @@ -484,6 +485,27 @@ void MainWindow::InstallPkg() { } } +void MainWindow::BootGame() { + QFileDialog dialog; + dialog.setFileMode(QFileDialog::ExistingFile); + dialog.setNameFilter(tr("ELF files (*.bin *.elf *.oelf)")); + if (dialog.exec()) { + QStringList fileNames = dialog.selectedFiles(); + int nFiles = fileNames.size(); + + if (nFiles > 1) { + QMessageBox::critical(nullptr, "Game Boot", QString("Only one file can be selected!")); + } else { + std::filesystem::path path(fileNames[0].toStdString()); +#ifdef _WIN64 + path = std::filesystem::path(fileNames[0].toStdWString()); +#endif + Core::Emulator emulator; + emulator.Run(path); + } + } +} + void MainWindow::InstallDragDropPkg(std::filesystem::path file, int pkgNum, int nPkg) { if (Loader::DetectFileType(file) == Loader::FileTypes::Pkg) { pkg = PKG(); diff --git a/src/qt_gui/main_window.h b/src/qt_gui/main_window.h index 35fd0bf6..3aa4453e 100644 --- a/src/qt_gui/main_window.h +++ b/src/qt_gui/main_window.h @@ -61,6 +61,7 @@ private: void SetLastIconSizeBullet(); void SetUiIcons(bool isWhite); void InstallPkg(); + void BootGame(); void AddRecentFiles(QString filePath); QIcon RecolorIcon(const QIcon& icon, bool isWhite); bool isIconBlack = false; diff --git a/src/qt_gui/main_window_ui.h b/src/qt_gui/main_window_ui.h index 06e5cf7f..18d0b94f 100644 --- a/src/qt_gui/main_window_ui.h +++ b/src/qt_gui/main_window_ui.h @@ -30,6 +30,7 @@ QT_BEGIN_NAMESPACE class Ui_MainWindow { public: QAction* bootInstallPkgAct; + QAction* bootGameAct; QAction* addElfFolderAct; QAction* exitAct; QAction* showGameListAct; @@ -92,6 +93,8 @@ public: bootInstallPkgAct = new QAction(MainWindow); bootInstallPkgAct->setObjectName("bootInstallPkgAct"); bootInstallPkgAct->setIcon(QIcon(":images/file_icon.png")); + bootGameAct = new QAction(MainWindow); + bootGameAct->setObjectName("bootGameAct"); addElfFolderAct = new QAction(MainWindow); addElfFolderAct->setObjectName("addElfFolderAct"); exitAct = new QAction(MainWindow); @@ -251,6 +254,7 @@ public: menuBar->addAction(menuView->menuAction()); menuBar->addAction(menuSettings->menuAction()); menuFile->addAction(bootInstallPkgAct); + menuFile->addAction(bootGameAct); menuFile->addAction(addElfFolderAct); menuFile->addSeparator(); menuFile->addAction(menuRecent->menuAction()); @@ -290,6 +294,7 @@ public: QCoreApplication::translate("MainWindow", "Open/Add Elf Folder", nullptr)); bootInstallPkgAct->setText( QCoreApplication::translate("MainWindow", "Install Packages (PKG)", nullptr)); + bootGameAct->setText(QCoreApplication::translate("MainWindow", "Boot Game", nullptr)); #if QT_CONFIG(tooltip) bootInstallPkgAct->setToolTip(QCoreApplication::translate( "MainWindow", "Install application from a .pkg file", nullptr)); diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index 8b99482f..d61e108f 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -397,6 +397,10 @@ spv::ImageFormat GetFormat(const AmdGpu::Image& image) { image.GetNumberFmt() == AmdGpu::NumberFormat::Float) { return spv::ImageFormat::R16f; } + if (image.GetDataFmt() == AmdGpu::DataFormat::Format16 && + image.GetNumberFmt() == AmdGpu::NumberFormat::Uint) { + return spv::ImageFormat::R16ui; + } if (image.GetDataFmt() == AmdGpu::DataFormat::Format16_16 && image.GetNumberFmt() == AmdGpu::NumberFormat::Float) { return spv::ImageFormat::Rg16f; diff --git a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp index 4fc32ab2..e86d0652 100644 --- a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp +++ b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp @@ -329,6 +329,7 @@ std::span GetAllFormats() { vk::Format::eR16G16Sint, vk::Format::eR16G16Snorm, vk::Format::eR16Sfloat, + vk::Format::eR16Uint, vk::Format::eR16Unorm, vk::Format::eR32G32B32A32Sfloat, vk::Format::eR32G32B32A32Sint, From 558fcf6597f19821481b109c46ed3ab358cd5984 Mon Sep 17 00:00:00 2001 From: Xphalnos <164882787+Xphalnos@users.noreply.github.com> Date: Fri, 16 Aug 2024 19:48:18 +0200 Subject: [PATCH 21/29] Reduce window size --- src/qt_gui/settings_dialog.ui | 104 ++-------------------------------- 1 file changed, 6 insertions(+), 98 deletions(-) diff --git a/src/qt_gui/settings_dialog.ui b/src/qt_gui/settings_dialog.ui index eb621cb3..83b45b1d 100644 --- a/src/qt_gui/settings_dialog.ui +++ b/src/qt_gui/settings_dialog.ui @@ -11,8 +11,8 @@ 0 0 - 1280 - 720 + 854 + 480 @@ -51,8 +51,8 @@ 0 0 - 1258 - 660 + 832 + 420 @@ -68,7 +68,7 @@ General - + @@ -418,37 +418,6 @@ - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Qt::Orientation::Vertical - - - - 20 - 40 - - - - - - - @@ -467,58 +436,13 @@ - - - - Qt::Orientation::Vertical - - - QSizePolicy::Policy::MinimumExpanding - - - - 0 - 0 - - - - - - - - - - - 12 - - - 12 - - - - - - - Qt::Orientation::Vertical - - - QSizePolicy::Policy::MinimumExpanding - - - - 0 - 0 - - - - GPU - + @@ -797,22 +721,6 @@ - - - - Qt::Orientation::Vertical - - - QSizePolicy::Policy::MinimumExpanding - - - - 0 - 0 - - - - From 786db8074233f9fb18377912f5bd6ed80e67ef08 Mon Sep 17 00:00:00 2001 From: Stephen Miller Date: Fri, 16 Aug 2024 14:33:48 -0500 Subject: [PATCH 22/29] Improve posix_sem functions Use ErrSceToPosix to update g_posix_errno appropriately after sem function calls. --- .../libraries/kernel/thread_management.cpp | 147 +++++++++++++++++- 1 file changed, 140 insertions(+), 7 deletions(-) diff --git a/src/core/libraries/kernel/thread_management.cpp b/src/core/libraries/kernel/thread_management.cpp index 6319b7c2..a38ea626 100644 --- a/src/core/libraries/kernel/thread_management.cpp +++ b/src/core/libraries/kernel/thread_management.cpp @@ -11,6 +11,7 @@ #include "common/singleton.h" #include "common/thread.h" #include "core/libraries/error_codes.h" +#include "core/libraries/kernel/libkernel.h" #include "core/libraries/kernel/thread_management.h" #include "core/libraries/kernel/threads/threads.h" #include "core/libraries/libs.h" @@ -1374,15 +1375,69 @@ int PS4_SYSV_ABI posix_pthread_detach(ScePthread thread) { } int PS4_SYSV_ABI posix_sem_init(sem_t* sem, int pshared, unsigned int value) { - return sem_init(sem, pshared, value); + int result = sem_init(sem, pshared, value); + if (result == -1) { + switch (errno) { + case ENOMEM: + ErrSceToPosix(ORBIS_KERNEL_ERROR_ENOMEM); + break; + case EPERM: + ErrSceToPosix(ORBIS_KERNEL_ERROR_EPERM); + break; + case ENOSPC: + ErrSceToPosix(ORBIS_KERNEL_ERROR_ENOSPC); + break; + case EINVAL: + default: + ErrSceToPosix(ORBIS_KERNEL_ERROR_EINVAL); + break; + } + } + return result; } int PS4_SYSV_ABI posix_sem_wait(sem_t* sem) { - return sem_wait(sem); + int result = sem_wait(sem); + if (result == -1) { + switch (errno) { + case ENOMEM: + ErrSceToPosix(ORBIS_KERNEL_ERROR_ENOMEM); + break; + case EPERM: + ErrSceToPosix(ORBIS_KERNEL_ERROR_EPERM); + break; + case ETIMEDOUT: + ErrSceToPosix(ORBIS_KERNEL_ERROR_ETIMEDOUT); + break; + case EINVAL: + default: + ErrSceToPosix(ORBIS_KERNEL_ERROR_EINVAL); + break; + } + } + return result; } int PS4_SYSV_ABI posix_sem_trywait(sem_t* sem) { - return sem_trywait(sem); + int result = sem_trywait(sem); + if (result == -1) { + switch (errno) { + case ENOMEM: + ErrSceToPosix(ORBIS_KERNEL_ERROR_ENOMEM); + break; + case EPERM: + ErrSceToPosix(ORBIS_KERNEL_ERROR_EPERM); + break; + case EAGAIN : + ErrSceToPosix(ORBIS_KERNEL_ERROR_EAGAIN); + break; + case EINVAL: + default: + ErrSceToPosix(ORBIS_KERNEL_ERROR_EINVAL); + break; + } + } + return result; } #ifndef HAVE_SEM_TIMEDWAIT @@ -1416,19 +1471,97 @@ int sem_timedwait(sem_t* sem, const struct timespec* abstime) { #endif int PS4_SYSV_ABI posix_sem_timedwait(sem_t* sem, const timespec* t) { - return sem_timedwait(sem, t); + int result = sem_timedwait(sem, t); + if (result == -1) { + switch (errno) { + case ENOMEM: + ErrSceToPosix(ORBIS_KERNEL_ERROR_ENOMEM); + break; + case EPERM: + ErrSceToPosix(ORBIS_KERNEL_ERROR_EPERM); + break; + case ETIMEDOUT: + ErrSceToPosix(ORBIS_KERNEL_ERROR_ETIMEDOUT); + break; + case EINVAL: + default: + ErrSceToPosix(ORBIS_KERNEL_ERROR_EINVAL); + break; + } + } + return result; } int PS4_SYSV_ABI posix_sem_post(sem_t* sem) { - return sem_post(sem); + int result = sem_post(sem); + if (result == -1) { + switch (errno) { + case ENOMEM: + ErrSceToPosix(ORBIS_KERNEL_ERROR_ENOMEM); + break; + case EPERM: + ErrSceToPosix(ORBIS_KERNEL_ERROR_EPERM); + break; + case ERANGE: + ErrSceToPosix(ORBIS_KERNEL_ERROR_ERANGE); + break; + case EINVAL: + default: + ErrSceToPosix(ORBIS_KERNEL_ERROR_EINVAL); + break; + } + } + return result; } int PS4_SYSV_ABI posix_sem_destroy(sem_t* sem) { - return sem_destroy(sem); + int result = sem_destroy(sem); + if (result == -1) { + switch (errno) { + case ENOMEM: + ErrSceToPosix(ORBIS_KERNEL_ERROR_ENOMEM); + break; + case EPERM: + ErrSceToPosix(ORBIS_KERNEL_ERROR_EPERM); + break; + case ETIMEDOUT: + ErrSceToPosix(ORBIS_KERNEL_ERROR_ETIMEDOUT); + break; + case EDEADLK: + ErrSceToPosix(ORBIS_KERNEL_ERROR_EDEADLK); + break; + case EINVAL: + default: + ErrSceToPosix(ORBIS_KERNEL_ERROR_EINVAL); + break; + } + } + return result; } int PS4_SYSV_ABI posix_sem_getvalue(sem_t* sem, int* sval) { - return sem_getvalue(sem, sval); + int result = sem_getvalue(sem, sval); + if (result == -1) { + switch (errno) { + case ENOMEM: + ErrSceToPosix(ORBIS_KERNEL_ERROR_ENOMEM); + break; + case EPERM: + ErrSceToPosix(ORBIS_KERNEL_ERROR_EPERM); + break; + case ETIMEDOUT: + ErrSceToPosix(ORBIS_KERNEL_ERROR_ETIMEDOUT); + break; + case EDEADLK: + ErrSceToPosix(ORBIS_KERNEL_ERROR_EDEADLK); + break; + case EINVAL: + default: + ErrSceToPosix(ORBIS_KERNEL_ERROR_EINVAL); + break; + } + } + return result; } int PS4_SYSV_ABI posix_pthread_attr_getstacksize(const pthread_attr_t* attr, size_t* size) { From f36440dc0994b55780a5ce3cf9ef1a5e40adbdc7 Mon Sep 17 00:00:00 2001 From: Stephen Miller Date: Fri, 16 Aug 2024 14:55:55 -0500 Subject: [PATCH 23/29] clang-format fix --- src/core/libraries/kernel/thread_management.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/libraries/kernel/thread_management.cpp b/src/core/libraries/kernel/thread_management.cpp index a38ea626..c904fc91 100644 --- a/src/core/libraries/kernel/thread_management.cpp +++ b/src/core/libraries/kernel/thread_management.cpp @@ -1428,7 +1428,7 @@ int PS4_SYSV_ABI posix_sem_trywait(sem_t* sem) { case EPERM: ErrSceToPosix(ORBIS_KERNEL_ERROR_EPERM); break; - case EAGAIN : + case EAGAIN: ErrSceToPosix(ORBIS_KERNEL_ERROR_EAGAIN); break; case EINVAL: From 6510af90beedeba69f3c216ac7fdd75b9ac83f0c Mon Sep 17 00:00:00 2001 From: Stephen Miller Date: Fri, 16 Aug 2024 15:07:19 -0500 Subject: [PATCH 24/29] another clang-format fix --- src/core/libraries/kernel/thread_management.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/core/libraries/kernel/thread_management.cpp b/src/core/libraries/kernel/thread_management.cpp index c904fc91..8eb7d79b 100644 --- a/src/core/libraries/kernel/thread_management.cpp +++ b/src/core/libraries/kernel/thread_management.cpp @@ -1388,6 +1388,7 @@ int PS4_SYSV_ABI posix_sem_init(sem_t* sem, int pshared, unsigned int value) { ErrSceToPosix(ORBIS_KERNEL_ERROR_ENOSPC); break; case EINVAL: + default: ErrSceToPosix(ORBIS_KERNEL_ERROR_EINVAL); break; @@ -1410,6 +1411,7 @@ int PS4_SYSV_ABI posix_sem_wait(sem_t* sem) { ErrSceToPosix(ORBIS_KERNEL_ERROR_ETIMEDOUT); break; case EINVAL: + default: ErrSceToPosix(ORBIS_KERNEL_ERROR_EINVAL); break; @@ -1432,6 +1434,7 @@ int PS4_SYSV_ABI posix_sem_trywait(sem_t* sem) { ErrSceToPosix(ORBIS_KERNEL_ERROR_EAGAIN); break; case EINVAL: + default: ErrSceToPosix(ORBIS_KERNEL_ERROR_EINVAL); break; @@ -1484,6 +1487,7 @@ int PS4_SYSV_ABI posix_sem_timedwait(sem_t* sem, const timespec* t) { ErrSceToPosix(ORBIS_KERNEL_ERROR_ETIMEDOUT); break; case EINVAL: + default: ErrSceToPosix(ORBIS_KERNEL_ERROR_EINVAL); break; @@ -1506,6 +1510,7 @@ int PS4_SYSV_ABI posix_sem_post(sem_t* sem) { ErrSceToPosix(ORBIS_KERNEL_ERROR_ERANGE); break; case EINVAL: + default: ErrSceToPosix(ORBIS_KERNEL_ERROR_EINVAL); break; @@ -1531,6 +1536,7 @@ int PS4_SYSV_ABI posix_sem_destroy(sem_t* sem) { ErrSceToPosix(ORBIS_KERNEL_ERROR_EDEADLK); break; case EINVAL: + default: ErrSceToPosix(ORBIS_KERNEL_ERROR_EINVAL); break; @@ -1556,6 +1562,7 @@ int PS4_SYSV_ABI posix_sem_getvalue(sem_t* sem, int* sval) { ErrSceToPosix(ORBIS_KERNEL_ERROR_EDEADLK); break; case EINVAL: + default: ErrSceToPosix(ORBIS_KERNEL_ERROR_EINVAL); break; From 93f14e8ae95e753953c75a8c6d62763ce3b75b35 Mon Sep 17 00:00:00 2001 From: Stephen Miller Date: Fri, 16 Aug 2024 15:10:20 -0500 Subject: [PATCH 25/29] Might fix clang-format? --- .../libraries/kernel/thread_management.cpp | 21 +++++++------------ 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/src/core/libraries/kernel/thread_management.cpp b/src/core/libraries/kernel/thread_management.cpp index 8eb7d79b..19621424 100644 --- a/src/core/libraries/kernel/thread_management.cpp +++ b/src/core/libraries/kernel/thread_management.cpp @@ -1388,8 +1388,7 @@ int PS4_SYSV_ABI posix_sem_init(sem_t* sem, int pshared, unsigned int value) { ErrSceToPosix(ORBIS_KERNEL_ERROR_ENOSPC); break; case EINVAL: - - default: + default: ErrSceToPosix(ORBIS_KERNEL_ERROR_EINVAL); break; } @@ -1411,8 +1410,7 @@ int PS4_SYSV_ABI posix_sem_wait(sem_t* sem) { ErrSceToPosix(ORBIS_KERNEL_ERROR_ETIMEDOUT); break; case EINVAL: - - default: + default: ErrSceToPosix(ORBIS_KERNEL_ERROR_EINVAL); break; } @@ -1434,8 +1432,7 @@ int PS4_SYSV_ABI posix_sem_trywait(sem_t* sem) { ErrSceToPosix(ORBIS_KERNEL_ERROR_EAGAIN); break; case EINVAL: - - default: + default: ErrSceToPosix(ORBIS_KERNEL_ERROR_EINVAL); break; } @@ -1487,8 +1484,7 @@ int PS4_SYSV_ABI posix_sem_timedwait(sem_t* sem, const timespec* t) { ErrSceToPosix(ORBIS_KERNEL_ERROR_ETIMEDOUT); break; case EINVAL: - - default: + default: ErrSceToPosix(ORBIS_KERNEL_ERROR_EINVAL); break; } @@ -1510,8 +1506,7 @@ int PS4_SYSV_ABI posix_sem_post(sem_t* sem) { ErrSceToPosix(ORBIS_KERNEL_ERROR_ERANGE); break; case EINVAL: - - default: + default: ErrSceToPosix(ORBIS_KERNEL_ERROR_EINVAL); break; } @@ -1536,8 +1531,7 @@ int PS4_SYSV_ABI posix_sem_destroy(sem_t* sem) { ErrSceToPosix(ORBIS_KERNEL_ERROR_EDEADLK); break; case EINVAL: - - default: + default: ErrSceToPosix(ORBIS_KERNEL_ERROR_EINVAL); break; } @@ -1562,8 +1556,7 @@ int PS4_SYSV_ABI posix_sem_getvalue(sem_t* sem, int* sval) { ErrSceToPosix(ORBIS_KERNEL_ERROR_EDEADLK); break; case EINVAL: - - default: + default: ErrSceToPosix(ORBIS_KERNEL_ERROR_EINVAL); break; } From 9fce6f7c01f1328994e2ea91433cbe65f5444d84 Mon Sep 17 00:00:00 2001 From: Stephen Miller Date: Fri, 16 Aug 2024 17:20:21 -0500 Subject: [PATCH 26/29] Add SetPosixErrno function I used a switch statement for future proofing, as some codes differ between Windows, Mac, Linux, or Orbis. Right now I've only added the codes that should be possible to encounter. --- src/core/libraries/kernel/libkernel.cpp | 31 +++++ src/core/libraries/kernel/libkernel.h | 1 + .../libraries/kernel/thread_management.cpp | 118 ++---------------- 3 files changed, 39 insertions(+), 111 deletions(-) diff --git a/src/core/libraries/kernel/libkernel.cpp b/src/core/libraries/kernel/libkernel.cpp index 9657ba04..93afbc4f 100644 --- a/src/core/libraries/kernel/libkernel.cpp +++ b/src/core/libraries/kernel/libkernel.cpp @@ -125,6 +125,37 @@ int ErrnoToSceKernelError(int e) { return res > SCE_KERNEL_ERROR_ESTOP ? SCE_KERNEL_ERROR_UNKNOWN : res; } +void SetPosixErrno(int e) { + //Some error numbers are different between supported OSes or the PS4 + switch (e) { + case EPERM: + g_posix_errno = POSIX_EPERM; + break; + case EAGAIN: + g_posix_errno = POSIX_EAGAIN; + break; + case ENOMEM: + g_posix_errno = POSIX_ENOMEM; + break; + case EINVAL: + g_posix_errno = POSIX_EINVAL; + break; + case ENOSPC: + g_posix_errno = POSIX_ENOSPC; + break; + case ERANGE: + g_posix_errno = POSIX_ERANGE; + break; + case EDEADLK: + g_posix_errno = POSIX_EDEADLK; + break; + case ETIMEDOUT: + g_posix_errno = POSIX_ETIMEDOUT; + break; + default: + g_posix_errno = e; + } +} int PS4_SYSV_ABI sceKernelMmap(void* addr, u64 len, int prot, int flags, int fd, size_t offset, void** res) { LOG_INFO(Kernel_Vmm, "called addr = {}, len = {}, prot = {}, flags = {}, fd = {}, offset = {}", diff --git a/src/core/libraries/kernel/libkernel.h b/src/core/libraries/kernel/libkernel.h index 5b22dea4..5b7f1e72 100644 --- a/src/core/libraries/kernel/libkernel.h +++ b/src/core/libraries/kernel/libkernel.h @@ -14,6 +14,7 @@ namespace Libraries::Kernel { void ErrSceToPosix(int result); int ErrnoToSceKernelError(int e); +void SetPosixErrno(int e); struct OrbisTimesec { time_t t; diff --git a/src/core/libraries/kernel/thread_management.cpp b/src/core/libraries/kernel/thread_management.cpp index 19621424..68953269 100644 --- a/src/core/libraries/kernel/thread_management.cpp +++ b/src/core/libraries/kernel/thread_management.cpp @@ -1377,21 +1377,7 @@ int PS4_SYSV_ABI posix_pthread_detach(ScePthread thread) { int PS4_SYSV_ABI posix_sem_init(sem_t* sem, int pshared, unsigned int value) { int result = sem_init(sem, pshared, value); if (result == -1) { - switch (errno) { - case ENOMEM: - ErrSceToPosix(ORBIS_KERNEL_ERROR_ENOMEM); - break; - case EPERM: - ErrSceToPosix(ORBIS_KERNEL_ERROR_EPERM); - break; - case ENOSPC: - ErrSceToPosix(ORBIS_KERNEL_ERROR_ENOSPC); - break; - case EINVAL: - default: - ErrSceToPosix(ORBIS_KERNEL_ERROR_EINVAL); - break; - } + SetPosixErrno(errno); } return result; } @@ -1399,21 +1385,7 @@ int PS4_SYSV_ABI posix_sem_init(sem_t* sem, int pshared, unsigned int value) { int PS4_SYSV_ABI posix_sem_wait(sem_t* sem) { int result = sem_wait(sem); if (result == -1) { - switch (errno) { - case ENOMEM: - ErrSceToPosix(ORBIS_KERNEL_ERROR_ENOMEM); - break; - case EPERM: - ErrSceToPosix(ORBIS_KERNEL_ERROR_EPERM); - break; - case ETIMEDOUT: - ErrSceToPosix(ORBIS_KERNEL_ERROR_ETIMEDOUT); - break; - case EINVAL: - default: - ErrSceToPosix(ORBIS_KERNEL_ERROR_EINVAL); - break; - } + SetPosixErrno(errno); } return result; } @@ -1421,21 +1393,7 @@ int PS4_SYSV_ABI posix_sem_wait(sem_t* sem) { int PS4_SYSV_ABI posix_sem_trywait(sem_t* sem) { int result = sem_trywait(sem); if (result == -1) { - switch (errno) { - case ENOMEM: - ErrSceToPosix(ORBIS_KERNEL_ERROR_ENOMEM); - break; - case EPERM: - ErrSceToPosix(ORBIS_KERNEL_ERROR_EPERM); - break; - case EAGAIN: - ErrSceToPosix(ORBIS_KERNEL_ERROR_EAGAIN); - break; - case EINVAL: - default: - ErrSceToPosix(ORBIS_KERNEL_ERROR_EINVAL); - break; - } + SetPosixErrno(errno); } return result; } @@ -1473,21 +1431,7 @@ int sem_timedwait(sem_t* sem, const struct timespec* abstime) { int PS4_SYSV_ABI posix_sem_timedwait(sem_t* sem, const timespec* t) { int result = sem_timedwait(sem, t); if (result == -1) { - switch (errno) { - case ENOMEM: - ErrSceToPosix(ORBIS_KERNEL_ERROR_ENOMEM); - break; - case EPERM: - ErrSceToPosix(ORBIS_KERNEL_ERROR_EPERM); - break; - case ETIMEDOUT: - ErrSceToPosix(ORBIS_KERNEL_ERROR_ETIMEDOUT); - break; - case EINVAL: - default: - ErrSceToPosix(ORBIS_KERNEL_ERROR_EINVAL); - break; - } + SetPosixErrno(errno); } return result; } @@ -1495,21 +1439,7 @@ int PS4_SYSV_ABI posix_sem_timedwait(sem_t* sem, const timespec* t) { int PS4_SYSV_ABI posix_sem_post(sem_t* sem) { int result = sem_post(sem); if (result == -1) { - switch (errno) { - case ENOMEM: - ErrSceToPosix(ORBIS_KERNEL_ERROR_ENOMEM); - break; - case EPERM: - ErrSceToPosix(ORBIS_KERNEL_ERROR_EPERM); - break; - case ERANGE: - ErrSceToPosix(ORBIS_KERNEL_ERROR_ERANGE); - break; - case EINVAL: - default: - ErrSceToPosix(ORBIS_KERNEL_ERROR_EINVAL); - break; - } + SetPosixErrno(errno); } return result; } @@ -1517,24 +1447,7 @@ int PS4_SYSV_ABI posix_sem_post(sem_t* sem) { int PS4_SYSV_ABI posix_sem_destroy(sem_t* sem) { int result = sem_destroy(sem); if (result == -1) { - switch (errno) { - case ENOMEM: - ErrSceToPosix(ORBIS_KERNEL_ERROR_ENOMEM); - break; - case EPERM: - ErrSceToPosix(ORBIS_KERNEL_ERROR_EPERM); - break; - case ETIMEDOUT: - ErrSceToPosix(ORBIS_KERNEL_ERROR_ETIMEDOUT); - break; - case EDEADLK: - ErrSceToPosix(ORBIS_KERNEL_ERROR_EDEADLK); - break; - case EINVAL: - default: - ErrSceToPosix(ORBIS_KERNEL_ERROR_EINVAL); - break; - } + SetPosixErrno(errno); } return result; } @@ -1542,24 +1455,7 @@ int PS4_SYSV_ABI posix_sem_destroy(sem_t* sem) { int PS4_SYSV_ABI posix_sem_getvalue(sem_t* sem, int* sval) { int result = sem_getvalue(sem, sval); if (result == -1) { - switch (errno) { - case ENOMEM: - ErrSceToPosix(ORBIS_KERNEL_ERROR_ENOMEM); - break; - case EPERM: - ErrSceToPosix(ORBIS_KERNEL_ERROR_EPERM); - break; - case ETIMEDOUT: - ErrSceToPosix(ORBIS_KERNEL_ERROR_ETIMEDOUT); - break; - case EDEADLK: - ErrSceToPosix(ORBIS_KERNEL_ERROR_EDEADLK); - break; - case EINVAL: - default: - ErrSceToPosix(ORBIS_KERNEL_ERROR_EINVAL); - break; - } + SetPosixErrno(errno); } return result; } From 2935ca0fef9df989ab210475e61a25964401cc12 Mon Sep 17 00:00:00 2001 From: Stephen Miller Date: Fri, 16 Aug 2024 17:22:06 -0500 Subject: [PATCH 27/29] clang-format fix --- src/core/libraries/kernel/libkernel.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/libraries/kernel/libkernel.cpp b/src/core/libraries/kernel/libkernel.cpp index 93afbc4f..781feef8 100644 --- a/src/core/libraries/kernel/libkernel.cpp +++ b/src/core/libraries/kernel/libkernel.cpp @@ -126,7 +126,7 @@ int ErrnoToSceKernelError(int e) { } void SetPosixErrno(int e) { - //Some error numbers are different between supported OSes or the PS4 + // Some error numbers are different between supported OSes or the PS4 switch (e) { case EPERM: g_posix_errno = POSIX_EPERM; @@ -146,7 +146,7 @@ void SetPosixErrno(int e) { case ERANGE: g_posix_errno = POSIX_ERANGE; break; - case EDEADLK: + case EDEADLK: g_posix_errno = POSIX_EDEADLK; break; case ETIMEDOUT: From 73adc3ed1b6c28798bf1ebdb12492ad26e375aee Mon Sep 17 00:00:00 2001 From: Xphalnos <164882787+Xphalnos@users.noreply.github.com> Date: Sat, 17 Aug 2024 08:22:31 +0200 Subject: [PATCH 28/29] Logger update --- src/qt_gui/settings_dialog.ui | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/qt_gui/settings_dialog.ui b/src/qt_gui/settings_dialog.ui index 83b45b1d..11ba38d0 100644 --- a/src/qt_gui/settings_dialog.ui +++ b/src/qt_gui/settings_dialog.ui @@ -23,7 +23,7 @@ - 10 + 11 false @@ -52,7 +52,7 @@ 0 0 832 - 420 + 418 @@ -411,6 +411,19 @@ + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + From 3be2e4b2b88b6234a12dee05015c5b7ca5409648 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A5IGA?= <164882787+Xphalnos@users.noreply.github.com> Date: Sat, 17 Aug 2024 18:13:37 +0200 Subject: [PATCH 29/29] About Window (#458) --- CMakeLists.txt | 5 +- src/qt_gui/about_dialog.cpp | 13 +++++ src/qt_gui/about_dialog.h | 21 +++++++ src/qt_gui/about_dialog.ui | 110 ++++++++++++++++++++++++++++++++++++ src/qt_gui/main_window.cpp | 6 ++ src/qt_gui/main_window_ui.h | 10 ++++ 6 files changed, 164 insertions(+), 1 deletion(-) create mode 100644 src/qt_gui/about_dialog.cpp create mode 100644 src/qt_gui/about_dialog.h create mode 100644 src/qt_gui/about_dialog.ui diff --git a/CMakeLists.txt b/CMakeLists.txt index a248ed1a..74e0b32f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -547,7 +547,10 @@ set(EMULATOR src/emulator.cpp if(ENABLE_QT_GUI) qt_add_resources(RESOURCE_FILES src/shadps4.qrc) -set(QT_GUI src/qt_gui/main_window_ui.h +set(QT_GUI src/qt_gui/about_dialog.cpp + src/qt_gui/about_dialog.h + src/qt_gui/about_dialog.ui + src/qt_gui/main_window_ui.h src/qt_gui/main_window.cpp src/qt_gui/main_window.h src/qt_gui/gui_context_menus.h diff --git a/src/qt_gui/about_dialog.cpp b/src/qt_gui/about_dialog.cpp new file mode 100644 index 00000000..a932d65a --- /dev/null +++ b/src/qt_gui/about_dialog.cpp @@ -0,0 +1,13 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "about_dialog.h" +#include "ui_about_dialog.h" + +AboutDialog::AboutDialog(QWidget* parent) : QDialog(parent), ui(new Ui::AboutDialog) { + ui->setupUi(this); +} + +AboutDialog::~AboutDialog() { + delete ui; +} diff --git a/src/qt_gui/about_dialog.h b/src/qt_gui/about_dialog.h new file mode 100644 index 00000000..8c802221 --- /dev/null +++ b/src/qt_gui/about_dialog.h @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +namespace Ui { +class AboutDialog; +} + +class AboutDialog : public QDialog { + Q_OBJECT + +public: + explicit AboutDialog(QWidget* parent = nullptr); + ~AboutDialog(); + +private: + Ui::AboutDialog* ui; +}; \ No newline at end of file diff --git a/src/qt_gui/about_dialog.ui b/src/qt_gui/about_dialog.ui new file mode 100644 index 00000000..2b60476b --- /dev/null +++ b/src/qt_gui/about_dialog.ui @@ -0,0 +1,110 @@ + + + + AboutDialog + + + + 0 + 0 + 780 + 320 + + + + About shadPS4 + + + + :/images/shadps4.ico:/images/shadps4.ico + + + + + 10 + 30 + 271 + 261 + + + + QFrame::Shape::NoFrame + + + + + + :/images/shadps4.ico + + + true + + + true + + + + + + 310 + 40 + 171 + 41 + + + + + 24 + true + + + + shadPS4 + + + + + + 310 + 90 + 451 + 101 + + + + + 14 + + + + shadPS4 is an experimental open-source emulator for the PlayStation 4. + + + true + + + + + + 310 + 180 + 451 + 101 + + + + + 14 + + + + This software should not be used to play games you have not legally obtained. + + + true + + + + + diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp index 58f925b1..f862c064 100644 --- a/src/qt_gui/main_window.cpp +++ b/src/qt_gui/main_window.cpp @@ -9,6 +9,7 @@ #include #include +#include "about_dialog.h" #include "common/io_file.h" #include "common/version.h" #include "core/file_format/pkg.h" @@ -206,6 +207,11 @@ void MainWindow::CreateConnects() { settingsDialog->exec(); }); + connect(ui->aboutAct, &QAction::triggered, this, [this]() { + auto aboutDialog = new AboutDialog(this); + aboutDialog->exec(); + }); + connect(ui->setIconSizeTinyAct, &QAction::triggered, this, [this]() { if (isTableList) { m_game_list_frame->icon_size = diff --git a/src/qt_gui/main_window_ui.h b/src/qt_gui/main_window_ui.h index 18d0b94f..b7132c64 100644 --- a/src/qt_gui/main_window_ui.h +++ b/src/qt_gui/main_window_ui.h @@ -45,6 +45,7 @@ public: QAction* gameInstallPathAct; QAction* dumpGameListAct; QAction* pkgViewerAct; + QAction* aboutAct; QAction* setThemeDark; QAction* setThemeLight; QAction* setThemeGreen; @@ -70,6 +71,7 @@ public: QMenu* menuSettings; QMenu* menuUtils; QMenu* menuThemes; + QMenu* menuAbout; QToolBar* toolBar; void setupUi(QMainWindow* MainWindow) { @@ -139,6 +141,8 @@ public: pkgViewerAct->setObjectName("pkgViewer"); pkgViewerAct->setObjectName("pkgViewer"); pkgViewerAct->setIcon(QIcon(":images/file_icon.png")); + aboutAct = new QAction(MainWindow); + aboutAct->setObjectName("aboutAct"); setThemeDark = new QAction(MainWindow); setThemeDark->setObjectName("setThemeDark"); setThemeDark->setCheckable(true); @@ -245,6 +249,8 @@ public: menuThemes = new QMenu(menuView); menuThemes->setObjectName("menuThemes"); menuThemes->setIcon(QIcon(":images/themes_icon.png")); + menuAbout = new QMenu(menuBar); + menuAbout->setObjectName("menuAbout"); MainWindow->setMenuBar(menuBar); toolBar = new QToolBar(MainWindow); toolBar->setObjectName("toolBar"); @@ -253,6 +259,7 @@ public: menuBar->addAction(menuFile->menuAction()); menuBar->addAction(menuView->menuAction()); menuBar->addAction(menuSettings->menuAction()); + menuBar->addAction(menuAbout->menuAction()); menuFile->addAction(bootInstallPkgAct); menuFile->addAction(bootGameAct); menuFile->addAction(addElfFolderAct); @@ -282,6 +289,7 @@ public: menuSettings->addAction(menuUtils->menuAction()); menuUtils->addAction(dumpGameListAct); menuUtils->addAction(pkgViewerAct); + menuAbout->addAction(aboutAct); retranslateUi(MainWindow); @@ -295,6 +303,7 @@ public: bootInstallPkgAct->setText( QCoreApplication::translate("MainWindow", "Install Packages (PKG)", nullptr)); bootGameAct->setText(QCoreApplication::translate("MainWindow", "Boot Game", nullptr)); + aboutAct->setText(QCoreApplication::translate("MainWindow", "About", nullptr)); #if QT_CONFIG(tooltip) bootInstallPkgAct->setToolTip(QCoreApplication::translate( "MainWindow", "Install application from a .pkg file", nullptr)); @@ -337,6 +346,7 @@ public: menuSettings->setTitle(QCoreApplication::translate("MainWindow", "Settings", nullptr)); menuUtils->setTitle(QCoreApplication::translate("MainWindow", "Utils", nullptr)); menuThemes->setTitle(QCoreApplication::translate("MainWindow", "Themes", nullptr)); + menuAbout->setTitle(QCoreApplication::translate("MainWindow", "About", nullptr)); setThemeDark->setText(QCoreApplication::translate("MainWindow", "Dark", nullptr)); setThemeLight->setText(QCoreApplication::translate("MainWindow", "Light", nullptr)); setThemeGreen->setText(QCoreApplication::translate("MainWindow", "Green", nullptr));