diff --git a/src/common/logging/log.h b/src/common/logging/log.h index 5fa430348..de16cccf6 100644 --- a/src/common/logging/log.h +++ b/src/common/logging/log.h @@ -39,14 +39,10 @@ void FmtLogMessage(Class log_class, Level log_level, const char* filename, unsig Common::Log::FmtLogMessage(log_class, log_level, Common::Log::TrimSourcePath(__FILE__), \ __LINE__, __func__, __VA_ARGS__) -#ifdef _DEBUG #define LOG_TRACE(log_class, ...) \ Common::Log::FmtLogMessage(Common::Log::Class::log_class, Common::Log::Level::Trace, \ Common::Log::TrimSourcePath(__FILE__), __LINE__, __func__, \ __VA_ARGS__) -#else -#define LOG_TRACE(log_class, fmt, ...) (void(0)) -#endif #define LOG_DEBUG(log_class, ...) \ Common::Log::FmtLogMessage(Common::Log::Class::log_class, Common::Log::Level::Debug, \ diff --git a/src/common/native_clock.cpp b/src/common/native_clock.cpp index 0c05dbe84..1ea5a4bfe 100644 --- a/src/common/native_clock.cpp +++ b/src/common/native_clock.cpp @@ -5,28 +5,51 @@ #include "common/rdtsc.h" #include "common/uint128.h" +#include + +#ifdef _WIN64 +#include + +#define MM_SHARED_USER_DATA_VA 0x7ffe0000 +#define QpcBias ((ULONGLONG volatile*)(MM_SHARED_USER_DATA_VA + 0x3b0)) +#endif + namespace Common { NativeClock::NativeClock() : rdtsc_frequency{EstimateRDTSCFrequency()}, - ns_rdtsc_factor{GetFixedPoint64Factor(std::nano::den, rdtsc_frequency)}, - us_rdtsc_factor{GetFixedPoint64Factor(std::micro::den, rdtsc_frequency)}, - ms_rdtsc_factor{GetFixedPoint64Factor(std::milli::den, rdtsc_frequency)} {} + us_rdtsc_factor{GetFixedPoint64Factor(std::micro::den, rdtsc_frequency)} {} -u64 NativeClock::GetTimeNS(u64 base_ptc /*= 0*/) const { - return MultiplyHigh(GetUptime() - base_ptc, ns_rdtsc_factor); -} - -u64 NativeClock::GetTimeUS(u64 base_ptc /*= 0*/) const { - return MultiplyHigh(GetUptime() - base_ptc, us_rdtsc_factor); -} - -u64 NativeClock::GetTimeMS(u64 base_ptc /*= 0*/) const { - return MultiplyHigh(GetUptime() - base_ptc, ms_rdtsc_factor); +u64 NativeClock::GetTimeUS(u64 time) const { + return MultiplyHigh(time, us_rdtsc_factor); } u64 NativeClock::GetUptime() const { +#ifdef _WIN64 + LARGE_INTEGER counter; + QueryPerformanceCounter(&counter); + return counter.QuadPart; +#else return FencedRDTSC(); +#endif +} + +u64 NativeClock::GetUnbiasedUptime() const { +#ifdef _WIN64 + ULONGLONG bias = 0; + u64 qpc = 0; + do { + bias = *QpcBias; + qpc = GetUptime(); + } while (bias != *QpcBias); + return qpc - bias; +#else + return GetUptime(); +#endif +} + +u64 NativeClock::GetTscFrequency() const { + return rdtsc_frequency; } } // namespace Common diff --git a/src/common/native_clock.h b/src/common/native_clock.h index 1542c2f3a..2a030b911 100644 --- a/src/common/native_clock.h +++ b/src/common/native_clock.h @@ -3,7 +3,6 @@ #pragma once -#include #include "common/types.h" namespace Common { @@ -12,20 +11,15 @@ class NativeClock final { public: explicit NativeClock(); - u64 GetTscFrequency() const { - return rdtsc_frequency; - } + u64 GetTimeUS(u64 time) const; - u64 GetTimeNS(u64 base_ptc = 0) const; - u64 GetTimeUS(u64 base_ptc = 0) const; - u64 GetTimeMS(u64 base_ptc = 0) const; u64 GetUptime() const; + u64 GetUnbiasedUptime() const; + u64 GetTscFrequency() const; private: u64 rdtsc_frequency; - u64 ns_rdtsc_factor; u64 us_rdtsc_factor; - u64 ms_rdtsc_factor; }; } // namespace Common diff --git a/src/common/rdtsc.cpp b/src/common/rdtsc.cpp index 48d1b7a40..316d8388a 100644 --- a/src/common/rdtsc.cpp +++ b/src/common/rdtsc.cpp @@ -6,11 +6,12 @@ #include "common/uint128.h" #ifdef _WIN64 -#include +#include #endif namespace Common { +#ifndef _WIN64 static constexpr size_t SecondToNanoseconds = 1000000000ULL; template @@ -20,16 +21,7 @@ static u64 RoundToNearest(u64 value) { } static u64 GetTimeNs() { -#ifdef _WIN64 - // GetSystemTimePreciseAsFileTime returns the file time in 100ns units. - static constexpr u64 Multiplier = 100; - // Convert Windows epoch to Unix epoch. - static constexpr u64 WindowsEpochToUnixEpoch = 0x19DB1DED53E8000LL; - FILETIME filetime; - GetSystemTimePreciseAsFileTime(&filetime); - return Multiplier * ((static_cast(filetime.dwHighDateTime) << 32) + - static_cast(filetime.dwLowDateTime) - WindowsEpochToUnixEpoch); -#elif defined(__APPLE__) +#if defined(__APPLE__) return clock_gettime_nsec_np(CLOCK_REALTIME); #else timespec ts; @@ -37,8 +29,14 @@ static u64 GetTimeNs() { return ts.tv_sec * SecondToNanoseconds + ts.tv_nsec; #endif } +#endif u64 EstimateRDTSCFrequency() { +#ifdef _WIN64 + LARGE_INTEGER frequency; + QueryPerformanceFrequency(&frequency); + return frequency.QuadPart; +#else // Discard the first result measuring the rdtsc. FencedRDTSC(); std::this_thread::sleep_for(std::chrono::milliseconds{1}); @@ -55,6 +53,7 @@ u64 EstimateRDTSCFrequency() { const u64 tsc_diff = tsc_end - tsc_start; const u64 tsc_freq = MultiplyAndDivide64(tsc_diff, 1000000000ULL, end_time - start_time); return RoundToNearest<100'000>(tsc_freq); +#endif } } // namespace Common diff --git a/src/core/libraries/avplayer/avplayer.cpp b/src/core/libraries/avplayer/avplayer.cpp index 176fda137..f8298424d 100644 --- a/src/core/libraries/avplayer/avplayer.cpp +++ b/src/core/libraries/avplayer/avplayer.cpp @@ -9,23 +9,22 @@ namespace Libraries::AvPlayer { -s32 PS4_SYSV_ABI sceAvPlayerAddSource(SceAvPlayerHandle handle, const char* filename) { +s32 PS4_SYSV_ABI sceAvPlayerAddSource(AvPlayerHandle 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; + return handle->AddSource(filename); } -s32 PS4_SYSV_ABI sceAvPlayerAddSourceEx(SceAvPlayerHandle handle, SceAvPlayerUriType uriType, - SceAvPlayerSourceDetails* sourceDetails) { - LOG_ERROR(Lib_AvPlayer, "(STUBBED) called"); - if (handle == nullptr) { +s32 PS4_SYSV_ABI sceAvPlayerAddSourceEx(AvPlayerHandle handle, AvPlayerUriType uri_type, + AvPlayerSourceDetails* source_details) { + LOG_TRACE(Lib_AvPlayer, "called"); + if (handle == nullptr || uri_type != AvPlayerUriType::Source) { return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; } - return ORBIS_OK; + const auto path = std::string_view(source_details->uri.name, source_details->uri.length); + return handle->AddSourceEx(path, source_details->source_type); } int PS4_SYSV_ABI sceAvPlayerChangeStream() { @@ -33,20 +32,19 @@ int PS4_SYSV_ABI sceAvPlayerChangeStream() { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceAvPlayerClose(SceAvPlayerHandle handle) { +s32 PS4_SYSV_ABI sceAvPlayerClose(AvPlayerHandle 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; } delete handle; - LOG_TRACE(Lib_AvPlayer, "returning ORBIS_OK"); return ORBIS_OK; } -u64 PS4_SYSV_ABI sceAvPlayerCurrentTime(SceAvPlayerHandle handle) { - LOG_TRACE(Lib_AvPlayer, "called"); +u64 PS4_SYSV_ABI sceAvPlayerCurrentTime(AvPlayerHandle 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->CurrentTime(); @@ -54,7 +52,7 @@ u64 PS4_SYSV_ABI sceAvPlayerCurrentTime(SceAvPlayerHandle handle) { return res; } -s32 PS4_SYSV_ABI sceAvPlayerDisableStream(SceAvPlayerHandle handle, u32 stream_id) { +s32 PS4_SYSV_ABI sceAvPlayerDisableStream(AvPlayerHandle handle, u32 stream_id) { LOG_ERROR(Lib_AvPlayer, "(STUBBED) called"); if (handle == nullptr) { return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; @@ -62,60 +60,67 @@ s32 PS4_SYSV_ABI sceAvPlayerDisableStream(SceAvPlayerHandle handle, u32 stream_i return ORBIS_OK; } -s32 PS4_SYSV_ABI sceAvPlayerEnableStream(SceAvPlayerHandle handle, u32 stream_id) { +s32 PS4_SYSV_ABI sceAvPlayerEnableStream(AvPlayerHandle 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; + return handle->EnableStream(stream_id); } -bool PS4_SYSV_ABI sceAvPlayerGetAudioData(SceAvPlayerHandle handle, SceAvPlayerFrameInfo* p_info) { - LOG_TRACE(Lib_AvPlayer, "called"); +bool PS4_SYSV_ABI sceAvPlayerGetAudioData(AvPlayerHandle handle, AvPlayerFrameInfo* p_info) { + // LOG_TRACE(Lib_AvPlayer, "called"); if (handle == nullptr || p_info == nullptr) { - return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; + LOG_TRACE(Lib_AvPlayer, "returning false"); + return false; } const auto res = handle->GetAudioData(*p_info); - LOG_TRACE(Lib_AvPlayer, "returning {}", res); + if (res) { + LOG_TRACE(Lib_AvPlayer, "returning {}, ts = {}", res, p_info->timestamp); + } else { + LOG_TRACE(Lib_AvPlayer, "returning false"); + } return res; } -s32 PS4_SYSV_ABI sceAvPlayerGetStreamInfo(SceAvPlayerHandle handle, u32 stream_id, - SceAvPlayerStreamInfo* p_info) { +s32 PS4_SYSV_ABI sceAvPlayerGetStreamInfo(AvPlayerHandle handle, u32 stream_id, + AvPlayerStreamInfo* 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; + return handle->GetStreamInfo(stream_id, *p_info); } -bool PS4_SYSV_ABI sceAvPlayerGetVideoData(SceAvPlayerHandle handle, - SceAvPlayerFrameInfo* video_info) { +bool PS4_SYSV_ABI sceAvPlayerGetVideoData(AvPlayerHandle handle, AvPlayerFrameInfo* video_info) { LOG_TRACE(Lib_AvPlayer, "called"); if (handle == nullptr || video_info == nullptr) { - return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; + return false; } - const auto res = handle->GetVideoData(*video_info); - LOG_TRACE(Lib_AvPlayer, "returning {}", res); - return res; + return handle->GetVideoData(*video_info); } -bool PS4_SYSV_ABI sceAvPlayerGetVideoDataEx(SceAvPlayerHandle handle, - SceAvPlayerFrameInfoEx* video_info) { +bool PS4_SYSV_ABI sceAvPlayerGetVideoDataEx(AvPlayerHandle handle, + AvPlayerFrameInfoEx* video_info) { LOG_TRACE(Lib_AvPlayer, "called"); if (handle == nullptr || video_info == nullptr) { - return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; + LOG_TRACE(Lib_AvPlayer, "returning {}", false); + return false; } - const auto res = handle->GetVideoData(*video_info); - LOG_TRACE(Lib_AvPlayer, "returning {}", res); - return res; + + // This is what the original library does, idk why + for (int i = 0; i < 20; ++i) { + if (handle->GetVideoData(*video_info)) { + LOG_TRACE(Lib_AvPlayer, "returning {}, ts = {}", true, video_info->timestamp); + return true; + } + } + + LOG_TRACE(Lib_AvPlayer, "returning {}", false); + return false; } -SceAvPlayerHandle PS4_SYSV_ABI sceAvPlayerInit(SceAvPlayerInitData* data) { +AvPlayerHandle PS4_SYSV_ABI sceAvPlayerInit(AvPlayerInitData* data) { LOG_TRACE(Lib_AvPlayer, "called"); if (data == nullptr) { return nullptr; @@ -125,15 +130,14 @@ SceAvPlayerHandle PS4_SYSV_ABI sceAvPlayerInit(SceAvPlayerInitData* data) { 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."); + LOG_ERROR(Lib_AvPlayer, "All allocators are required for AvPlayer Initialisation."); return nullptr; } return new AvPlayer(*data); } -s32 PS4_SYSV_ABI sceAvPlayerInitEx(const SceAvPlayerInitDataEx* p_data, - SceAvPlayerHandle* p_player) { +s32 PS4_SYSV_ABI sceAvPlayerInitEx(const AvPlayerInitDataEx* p_data, AvPlayerHandle* p_player) { LOG_TRACE(Lib_AvPlayer, "called"); if (p_data == nullptr || p_player == nullptr) { return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; @@ -143,11 +147,11 @@ s32 PS4_SYSV_ABI sceAvPlayerInitEx(const SceAvPlayerInitDataEx* p_data, 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."); + LOG_ERROR(Lib_AvPlayer, "All allocators are required for AvPlayer Initialisation."); return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; } - SceAvPlayerInitData data = {}; + AvPlayerInitData data = {}; data.memory_replacement = p_data->memory_replacement; data.file_replacement = p_data->file_replacement; data.event_replacement = p_data->event_replacement; @@ -159,8 +163,8 @@ s32 PS4_SYSV_ABI sceAvPlayerInitEx(const SceAvPlayerInitDataEx* p_data, return ORBIS_OK; } -bool PS4_SYSV_ABI sceAvPlayerIsActive(SceAvPlayerHandle handle) { - LOG_TRACE(Lib_AvPlayer, "called"); +bool PS4_SYSV_ABI sceAvPlayerIsActive(AvPlayerHandle handle) { + // LOG_TRACE(Lib_AvPlayer, "called"); if (handle == nullptr) { LOG_TRACE(Lib_AvPlayer, "returning false"); return false; @@ -170,7 +174,7 @@ bool PS4_SYSV_ABI sceAvPlayerIsActive(SceAvPlayerHandle handle) { return res; } -s32 PS4_SYSV_ABI sceAvPlayerJumpToTime(SceAvPlayerHandle handle, uint64_t time) { +s32 PS4_SYSV_ABI sceAvPlayerJumpToTime(AvPlayerHandle handle, uint64_t time) { LOG_ERROR(Lib_AvPlayer, "(STUBBED) called, time (msec) = {}", time); if (handle == nullptr) { return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; @@ -178,22 +182,20 @@ s32 PS4_SYSV_ABI sceAvPlayerJumpToTime(SceAvPlayerHandle handle, uint64_t time) return ORBIS_OK; } -s32 PS4_SYSV_ABI sceAvPlayerPause(SceAvPlayerHandle handle) { - LOG_ERROR(Lib_AvPlayer, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceAvPlayerPause(AvPlayerHandle handle) { + LOG_TRACE(Lib_AvPlayer, "called"); if (handle == nullptr) { return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; } - return ORBIS_OK; + return handle->Pause(); } -s32 PS4_SYSV_ABI sceAvPlayerPostInit(SceAvPlayerHandle handle, SceAvPlayerPostInitData* data) { +s32 PS4_SYSV_ABI sceAvPlayerPostInit(AvPlayerHandle handle, AvPlayerPostInitData* 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; + return handle->PostInit(*data); } s32 PS4_SYSV_ABI sceAvPlayerPrintf(const char* format, ...) { @@ -201,29 +203,28 @@ s32 PS4_SYSV_ABI sceAvPlayerPrintf(const char* format, ...) { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceAvPlayerResume(SceAvPlayerHandle handle) { - LOG_ERROR(Lib_AvPlayer, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceAvPlayerResume(AvPlayerHandle handle) { + LOG_TRACE(Lib_AvPlayer, "called"); if (handle == nullptr) { return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; } - return ORBIS_OK; + return handle->Resume(); } -s32 PS4_SYSV_ABI sceAvPlayerSetAvSyncMode(SceAvPlayerHandle handle, - SceAvPlayerAvSyncMode sync_mode) { - LOG_ERROR(Lib_AvPlayer, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceAvPlayerSetAvSyncMode(AvPlayerHandle handle, AvPlayerAvSyncMode sync_mode) { + LOG_TRACE(Lib_AvPlayer, "called"); if (handle == nullptr) { return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; } - return ORBIS_OK; + return handle->SetAvSyncMode(sync_mode); } -s32 PS4_SYSV_ABI sceAvPlayerSetLogCallback(SceAvPlayerLogCallback log_cb, void* user_data) { +s32 PS4_SYSV_ABI sceAvPlayerSetLogCallback(AvPlayerLogCallback log_cb, void* user_data) { LOG_ERROR(Lib_AvPlayer, "(STUBBED) called"); return ORBIS_OK; } -s32 PS4_SYSV_ABI sceAvPlayerSetLooping(SceAvPlayerHandle handle, bool loop_flag) { +s32 PS4_SYSV_ABI sceAvPlayerSetLooping(AvPlayerHandle handle, bool loop_flag) { LOG_TRACE(Lib_AvPlayer, "called, looping = {}", loop_flag); if (handle == nullptr) { return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; @@ -234,7 +235,7 @@ s32 PS4_SYSV_ABI sceAvPlayerSetLooping(SceAvPlayerHandle handle, bool loop_flag) return ORBIS_OK; } -s32 PS4_SYSV_ABI sceAvPlayerSetTrickSpeed(SceAvPlayerHandle handle, s32 trick_speed) { +s32 PS4_SYSV_ABI sceAvPlayerSetTrickSpeed(AvPlayerHandle handle, s32 trick_speed) { LOG_ERROR(Lib_AvPlayer, "(STUBBED) called"); if (handle == nullptr) { return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; @@ -242,35 +243,28 @@ s32 PS4_SYSV_ABI sceAvPlayerSetTrickSpeed(SceAvPlayerHandle handle, s32 trick_sp return ORBIS_OK; } -s32 PS4_SYSV_ABI sceAvPlayerStart(SceAvPlayerHandle handle) { +s32 PS4_SYSV_ABI sceAvPlayerStart(AvPlayerHandle 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; + return handle->Start(); } -s32 PS4_SYSV_ABI sceAvPlayerStop(SceAvPlayerHandle handle) { +s32 PS4_SYSV_ABI sceAvPlayerStop(AvPlayerHandle 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->Stop(); - LOG_TRACE(Lib_AvPlayer, "returning {}", res); - return res; + return handle->Stop(); } -s32 PS4_SYSV_ABI sceAvPlayerStreamCount(SceAvPlayerHandle handle) { +s32 PS4_SYSV_ABI sceAvPlayerStreamCount(AvPlayerHandle 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; + return static_cast(handle->GetStreamCount()); } s32 PS4_SYSV_ABI sceAvPlayerVprintf(const char* format, va_list args) { diff --git a/src/core/libraries/avplayer/avplayer.h b/src/core/libraries/avplayer/avplayer.h index 2d472f801..100f66108 100644 --- a/src/core/libraries/avplayer/avplayer.h +++ b/src/core/libraries/avplayer/avplayer.h @@ -16,38 +16,38 @@ namespace Libraries::AvPlayer { class AvPlayer; -using SceAvPlayerHandle = AvPlayer*; +using AvPlayerHandle = AvPlayer*; -enum class SceAvPlayerUriType : u32 { +enum class AvPlayerUriType : u32 { Source = 0, }; -struct SceAvPlayerUri { +struct AvPlayerUri { const char* name; u32 length; }; -enum class SceAvPlayerSourceType { +enum class AvPlayerSourceType { Unknown = 0, FileMp4 = 1, Hls = 8, }; -enum class SceAvPlayerStreamType : u32 { +enum class AvPlayerStreamType : u32 { Video, Audio, TimedText, Unknown, }; -struct SceAvPlayerSourceDetails { - SceAvPlayerUri uri; +struct AvPlayerSourceDetails { + AvPlayerUri uri; u8 reserved1[64]; - SceAvPlayerSourceType source_type; + AvPlayerSourceType source_type; u8 reserved2[44]; }; -struct SceAvPlayerAudio { +struct AvPlayerAudio { u16 channel_count; u8 reserved1[2]; u32 sample_rate; @@ -55,50 +55,50 @@ struct SceAvPlayerAudio { u8 language_code[4]; }; -struct SceAvPlayerVideo { +struct AvPlayerVideo { u32 width; u32 height; f32 aspect_ratio; char language_code[4]; }; -struct SceAvPlayerTextPosition { +struct AvPlayerTextPosition { u16 top; u16 left; u16 bottom; u16 right; }; -struct SceAvPlayerTimedText { +struct AvPlayerTimedText { u8 language_code[4]; u16 text_size; u16 font_size; - SceAvPlayerTextPosition position; + AvPlayerTextPosition position; }; -union SceAvPlayerStreamDetails { +union AvPlayerStreamDetails { u8 reserved[16]; - SceAvPlayerAudio audio; - SceAvPlayerVideo video; - SceAvPlayerTimedText subs; + AvPlayerAudio audio; + AvPlayerVideo video; + AvPlayerTimedText subs; }; -struct SceAvPlayerFrameInfo { - u8* pData; +struct AvPlayerFrameInfo { + u8* p_data; u8 reserved[4]; u64 timestamp; - SceAvPlayerStreamDetails details; + AvPlayerStreamDetails details; }; -struct SceAvPlayerStreamInfo { - SceAvPlayerStreamType type; +struct AvPlayerStreamInfo { + AvPlayerStreamType type; u8 reserved[4]; - SceAvPlayerStreamDetails details; + AvPlayerStreamDetails details; u64 duration; u64 start_time; }; -struct SceAvPlayerAudioEx { +struct AvPlayerAudioEx { u16 channel_count; u8 reserved[2]; u32 sample_rate; @@ -107,7 +107,7 @@ struct SceAvPlayerAudioEx { u8 reserved1[64]; }; -struct SceAvPlayerVideoEx { +struct AvPlayerVideoEx { u32 width; u32 height; f32 aspect_ratio; @@ -124,53 +124,54 @@ struct SceAvPlayerVideoEx { u8 reserved1[37]; }; -struct SceAvPlayerTimedTextEx { +struct AvPlayerTimedTextEx { u8 language_code[4]; u8 reserved[12]; u8 reserved1[64]; }; -union SceAvPlayerStreamDetailsEx { - SceAvPlayerAudioEx audio; - SceAvPlayerVideoEx video; - SceAvPlayerTimedTextEx subs; +union AvPlayerStreamDetailsEx { + AvPlayerAudioEx audio; + AvPlayerVideoEx video; + AvPlayerTimedTextEx subs; u8 reserved1[80]; }; -struct SceAvPlayerFrameInfoEx { - void* pData; +struct AvPlayerFrameInfoEx { + void* p_data; u8 reserved[4]; u64 timestamp; - SceAvPlayerStreamDetailsEx details; + AvPlayerStreamDetailsEx details; }; -using SceAvPlayerAllocate = void* PS4_SYSV_ABI (*)(void* p, u32 align, u32 size); -using SceAvPlayerDeallocate = void PS4_SYSV_ABI (*)(void* p, void* mem); -using SceAvPlayerAllocateTexture = void* PS4_SYSV_ABI (*)(void* p, u32 align, u32 size); -using SceAvPlayerDeallocateTexture = void PS4_SYSV_ABI (*)(void* p, void* mem); +using AvPlayerAllocate = void* PS4_SYSV_ABI (*)(void* p, u32 align, u32 size); +using AvPlayerDeallocate = void PS4_SYSV_ABI (*)(void* p, void* mem); +using AvPlayerAllocateTexture = void* PS4_SYSV_ABI (*)(void* p, u32 align, u32 size); +using AvPlayerDeallocateTexture = void PS4_SYSV_ABI (*)(void* p, void* mem); -struct SceAvPlayerMemAllocator { +struct AvPlayerMemAllocator { void* object_ptr; - SceAvPlayerAllocate allocate; - SceAvPlayerDeallocate deallocate; - SceAvPlayerAllocateTexture allocate_texture; - SceAvPlayerDeallocateTexture deallocate_texture; + AvPlayerAllocate allocate; + AvPlayerDeallocate deallocate; + AvPlayerAllocateTexture allocate_texture; + AvPlayerDeallocateTexture deallocate_texture; }; -using SceAvPlayerOpenFile = s32 PS4_SYSV_ABI (*)(void* p, const char* name); -using SceAvPlayerCloseFile = s32 PS4_SYSV_ABI (*)(void* p); -using SceAvPlayerReadOffsetFile = s32 PS4_SYSV_ABI (*)(void* p, u8* buf, u64 pos, u32 len); -using SceAvPlayerSizeFile = u64 PS4_SYSV_ABI (*)(void* p); +using AvPlayerOpenFile = s32 PS4_SYSV_ABI (*)(void* p, const char* name); +using AvPlayerCloseFile = s32 PS4_SYSV_ABI (*)(void* p); +using AvPlayerReadOffsetFile = s32 PS4_SYSV_ABI (*)(void* p, u8* buf, u64 pos, u32 len); +using AvPlayerSizeFile = u64 PS4_SYSV_ABI (*)(void* p); -struct SceAvPlayerFileReplacement { +struct AvPlayerFileReplacement { void* object_ptr; - SceAvPlayerOpenFile open; - SceAvPlayerCloseFile close; - SceAvPlayerReadOffsetFile readOffset; - SceAvPlayerSizeFile size; + AvPlayerOpenFile open; + AvPlayerCloseFile close; + AvPlayerReadOffsetFile read_offset; + AvPlayerSizeFile size; }; -enum class SceAvPlayerEvents { +enum class AvPlayerEvents { + Initial = 0x00, StateStop = 0x01, StateReady = 0x02, StatePlay = 0x03, @@ -182,26 +183,26 @@ enum class SceAvPlayerEvents { DrmError = 0x40, }; -using SceAvPlayerEventCallback = void PS4_SYSV_ABI (*)(void* p, SceAvPlayerEvents event, s32 src_id, - void* data); +using AvPlayerEventCallback = void PS4_SYSV_ABI (*)(void* p, AvPlayerEvents event, s32 src_id, + void* data); -struct SceAvPlayerEventReplacement { +struct AvPlayerEventReplacement { void* object_ptr; - SceAvPlayerEventCallback event_callback; + AvPlayerEventCallback event_callback; }; -enum class SceAvPlayerDebuglevels { +enum class AvPlayerDebuglevels { None, Info, Warnings, All, }; -struct SceAvPlayerInitData { - SceAvPlayerMemAllocator memory_replacement; - SceAvPlayerFileReplacement file_replacement; - SceAvPlayerEventReplacement event_replacement; - SceAvPlayerDebuglevels debug_level; +struct AvPlayerInitData { + AvPlayerMemAllocator memory_replacement; + AvPlayerFileReplacement file_replacement; + AvPlayerEventReplacement event_replacement; + AvPlayerDebuglevels debug_level; u32 base_priority; s32 num_output_video_framebuffers; bool auto_start; @@ -209,13 +210,13 @@ struct SceAvPlayerInitData { const char* default_language; }; -struct SceAvPlayerInitDataEx { +struct AvPlayerInitDataEx { size_t this_size; - SceAvPlayerMemAllocator memory_replacement; - SceAvPlayerFileReplacement file_replacement; - SceAvPlayerEventReplacement event_replacement; + AvPlayerMemAllocator memory_replacement; + AvPlayerFileReplacement file_replacement; + AvPlayerEventReplacement event_replacement; const char* default_language; - SceAvPlayerDebuglevels debug_level; + AvPlayerDebuglevels debug_level; u32 audio_decoder_priority; u32 audio_decoder_affinity; u32 video_decoder_priority; @@ -233,25 +234,25 @@ struct SceAvPlayerInitDataEx { u8 reserved[3]; }; -enum class SceAvPlayerVideoDecoderType { +enum class AvPlayerVideoDecoderType { Default = 0, Reserved1, Software, Software2, }; -enum class SceAvPlayerAudioDecoderType { +enum class AvPlayerAudioDecoderType { Default = 0, Reserved1, Reserved2, }; -struct SceAvPlayerDecoderInit { +struct AvPlayerDecoderInit { union { - SceAvPlayerVideoDecoderType video_type; - SceAvPlayerAudioDecoderType audio_type; + AvPlayerVideoDecoderType video_type; + AvPlayerAudioDecoderType audio_type; u8 reserved[4]; - } decoderType; + } decoder_type; union { struct { s32 cpu_affinity_mask; @@ -261,34 +262,34 @@ struct SceAvPlayerDecoderInit { u8 compute_queue_id; u8 enable_interlaced; u8 reserved[16]; - } avcSw2; + } avc_sw2; struct { u8 audio_channel_order; u8 reserved[27]; } aac; u8 reserved[28]; - } decoderParams; + } decoder_params; }; -struct SceAvPlayerHTTPCtx { +struct AvPlayerHTTPCtx { u32 http_context_id; u32 ssl_context_id; }; -struct SceAvPlayerPostInitData { +struct AvPlayerPostInitData { u32 demux_video_buffer_size; - SceAvPlayerDecoderInit video_decoder_init; - SceAvPlayerDecoderInit audio_decoder_init; - SceAvPlayerHTTPCtx http_context; + AvPlayerDecoderInit video_decoder_init; + AvPlayerDecoderInit audio_decoder_init; + AvPlayerHTTPCtx http_context; u8 reserved[56]; }; -enum class SceAvPlayerAvSyncMode { +enum class AvPlayerAvSyncMode { Default = 0, None, }; -using SceAvPlayerLogCallback = int PS4_SYSV_ABI (*)(void* p, const char* format, va_list args); +using AvPlayerLogCallback = int PS4_SYSV_ABI (*)(void* p, const char* format, va_list args); void RegisterlibSceAvPlayer(Core::Loader::SymbolsResolver* sym); diff --git a/src/core/libraries/avplayer/avplayer_common.cpp b/src/core/libraries/avplayer/avplayer_common.cpp index 28d7803a1..f42f690ed 100644 --- a/src/core/libraries/avplayer/avplayer_common.cpp +++ b/src/core/libraries/avplayer/avplayer_common.cpp @@ -13,9 +13,9 @@ static bool iequals(std::string_view l, std::string_view r) { return std::ranges::equal(l, r, [](u8 a, u8 b) { return std::tolower(a) == std::tolower(b); }); } -SceAvPlayerSourceType GetSourceType(std::string_view path) { +AvPlayerSourceType GetSourceType(std::string_view path) { if (path.empty()) { - return SceAvPlayerSourceType::Unknown; + return AvPlayerSourceType::Unknown; } std::string_view name = path; @@ -25,14 +25,14 @@ SceAvPlayerSourceType GetSourceType(std::string_view path) { // -> schema://server.domain/path/to/file.ext/and/beyond name = path.substr(0, path.find_first_of("?#")); if (name.empty()) { - return SceAvPlayerSourceType::Unknown; + return AvPlayerSourceType::Unknown; } } // schema://server.domain/path/to/file.ext/and/beyond -> .ext/and/beyond auto ext = name.substr(name.rfind('.')); if (ext.empty()) { - return SceAvPlayerSourceType::Unknown; + return AvPlayerSourceType::Unknown; } // .ext/and/beyond -> .ext @@ -40,14 +40,14 @@ SceAvPlayerSourceType GetSourceType(std::string_view path) { if (iequals(ext, ".mp4") || iequals(ext, ".m4v") || iequals(ext, ".m3d") || iequals(ext, ".m4a") || iequals(ext, ".mov")) { - return SceAvPlayerSourceType::FileMp4; + return AvPlayerSourceType::FileMp4; } if (iequals(ext, ".m3u8")) { - return SceAvPlayerSourceType::Hls; + return AvPlayerSourceType::Hls; } - return SceAvPlayerSourceType::Unknown; + return AvPlayerSourceType::Unknown; } } // namespace Libraries::AvPlayer diff --git a/src/core/libraries/avplayer/avplayer_common.h b/src/core/libraries/avplayer/avplayer_common.h index dc3cd787f..aedbbd9f9 100644 --- a/src/core/libraries/avplayer/avplayer_common.h +++ b/src/core/libraries/avplayer/avplayer_common.h @@ -16,6 +16,7 @@ namespace Libraries::AvPlayer { enum class AvState { + Unknown, Initial, AddingSource, Ready, @@ -23,12 +24,13 @@ enum class AvState { Stop, EndOfFile, Pause, - C0x08, + PauseOnEOF, Jump, TrickMode, C0x0B, Buffering, Starting, + C0x10, Error, }; @@ -64,6 +66,10 @@ public: m_queue.emplace(std::forward(value)); } + T& Front() { + return m_queue.front(); + } + std::optional Pop() { if (Size() == 0) { return std::nullopt; @@ -84,6 +90,6 @@ private: std::queue m_queue{}; }; -SceAvPlayerSourceType GetSourceType(std::string_view path); +AvPlayerSourceType 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 19faeb273..c610012a5 100644 --- a/src/core/libraries/avplayer/avplayer_file_streamer.cpp +++ b/src/core/libraries/avplayer/avplayer_file_streamer.cpp @@ -14,7 +14,7 @@ constexpr u32 AVPLAYER_AVIO_BUFFER_SIZE = 4096; namespace Libraries::AvPlayer { -AvPlayerFileStreamer::AvPlayerFileStreamer(const SceAvPlayerFileReplacement& file_replacement) +AvPlayerFileStreamer::AvPlayerFileStreamer(const AvPlayerFileReplacement& file_replacement) : m_file_replacement(file_replacement) {} AvPlayerFileStreamer::~AvPlayerFileStreamer() { @@ -51,7 +51,7 @@ s32 AvPlayerFileStreamer::ReadPacket(void* opaque, u8* buffer, s32 size) { 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 read_offset = self->m_file_replacement.read_offset; const auto ptr = self->m_file_replacement.object_ptr; const auto bytes_read = read_offset(ptr, buffer, self->m_position, size); if (bytes_read == 0 && size != 0) { diff --git a/src/core/libraries/avplayer/avplayer_file_streamer.h b/src/core/libraries/avplayer/avplayer_file_streamer.h index bc096bccc..c0cdb4a07 100644 --- a/src/core/libraries/avplayer/avplayer_file_streamer.h +++ b/src/core/libraries/avplayer/avplayer_file_streamer.h @@ -13,7 +13,7 @@ namespace Libraries::AvPlayer { class AvPlayerFileStreamer : public IDataStreamer { public: - AvPlayerFileStreamer(const SceAvPlayerFileReplacement& file_replacement); + AvPlayerFileStreamer(const AvPlayerFileReplacement& file_replacement); ~AvPlayerFileStreamer(); bool Init(std::string_view path) override; @@ -26,7 +26,7 @@ private: static s32 ReadPacket(void* opaque, u8* buffer, s32 size); static s64 Seek(void* opaque, s64 buffer, int whence); - SceAvPlayerFileReplacement m_file_replacement; + AvPlayerFileReplacement m_file_replacement; int m_fd = -1; u64 m_position{}; diff --git a/src/core/libraries/avplayer/avplayer_impl.cpp b/src/core/libraries/avplayer/avplayer_impl.cpp index d9a67134c..379fc9607 100644 --- a/src/core/libraries/avplayer/avplayer_impl.cpp +++ b/src/core/libraries/avplayer/avplayer_impl.cpp @@ -58,7 +58,7 @@ int PS4_SYSV_ABI AvPlayer::ReadOffsetFile(void* handle, u8* buffer, u64 position 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 read_offset = self->m_init_data_original.file_replacement.read_offset; const auto ptr = self->m_init_data_original.file_replacement.object_ptr; return Core::ExecuteGuest(read_offset, ptr, buffer, position, length); } @@ -72,38 +72,46 @@ u64 PS4_SYSV_ABI AvPlayer::SizeFile(void* handle) { return Core::ExecuteGuest(size, ptr); } -SceAvPlayerInitData AvPlayer::StubInitData(const SceAvPlayerInitData& data) { - SceAvPlayerInitData result = data; +AvPlayerInitData AvPlayer::StubInitData(const AvPlayerInitData& data) { + AvPlayerInitData 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) { + data.file_replacement.read_offset == nullptr || data.file_replacement.size == nullptr) { result.file_replacement = {}; } else { 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.read_offset = &AvPlayer::ReadOffsetFile; result.file_replacement.size = &AvPlayer::SizeFile; } return result; } -AvPlayer::AvPlayer(const SceAvPlayerInitData& data) +AvPlayer::AvPlayer(const AvPlayerInitData& 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) { +s32 AvPlayer::PostInit(const AvPlayerPostInitData& data) { m_state->PostInit(data); return ORBIS_OK; } s32 AvPlayer::AddSource(std::string_view path) { - if (path.empty()) { - return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; + return AddSourceEx(path, AvPlayerSourceType::Unknown); +} + +s32 AvPlayer::AddSourceEx(std::string_view path, AvPlayerSourceType source_type) { + if (source_type == AvPlayerSourceType::Unknown) { + source_type = GetSourceType(path); + } + if (source_type == AvPlayerSourceType::Hls) { + LOG_ERROR(Lib_AvPlayer, "HTTP Live Streaming is not implemented"); + return ORBIS_AVPLAYER_ERROR_NOT_SUPPORTED; } if (!m_state->AddSource(path, GetSourceType(path))) { return ORBIS_AVPLAYER_ERROR_OPERATION_FAILED; @@ -111,18 +119,11 @@ s32 AvPlayer::AddSource(std::string_view path) { return ORBIS_OK; } -s32 AvPlayer::GetStreamCount() { - if (m_state == nullptr) { - return ORBIS_AVPLAYER_ERROR_OPERATION_FAILED; - } - const auto res = m_state->GetStreamCount(); - if (AVPLAYER_IS_ERROR(res)) { - return ORBIS_AVPLAYER_ERROR_OPERATION_FAILED; - } - return res; +u32 AvPlayer::GetStreamCount() { + return m_state->GetStreamCount(); } -s32 AvPlayer::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info) { +s32 AvPlayer::GetStreamInfo(u32 stream_index, AvPlayerStreamInfo& info) { if (!m_state->GetStreamInfo(stream_index, info)) { return ORBIS_AVPLAYER_ERROR_OPERATION_FAILED; } @@ -140,27 +141,49 @@ s32 AvPlayer::EnableStream(u32 stream_index) { } s32 AvPlayer::Start() { - if (!m_state->Start()) { + if (m_state == nullptr || !m_state->Start()) { return ORBIS_AVPLAYER_ERROR_OPERATION_FAILED; } return ORBIS_OK; } -bool AvPlayer::GetVideoData(SceAvPlayerFrameInfo& video_info) { +s32 AvPlayer::Pause() { + if (m_state == nullptr || !m_state->Pause()) { + return ORBIS_AVPLAYER_ERROR_OPERATION_FAILED; + } + return ORBIS_OK; +} + +s32 AvPlayer::Resume() { + if (m_state == nullptr || !m_state->Resume()) { + return ORBIS_AVPLAYER_ERROR_OPERATION_FAILED; + } + return ORBIS_OK; +} + +s32 AvPlayer::SetAvSyncMode(AvPlayerAvSyncMode sync_mode) { + if (m_state == nullptr) { + return ORBIS_AVPLAYER_ERROR_OPERATION_FAILED; + } + m_state->SetAvSyncMode(sync_mode); + return ORBIS_OK; +} + +bool AvPlayer::GetVideoData(AvPlayerFrameInfo& video_info) { if (m_state == nullptr) { return false; } return m_state->GetVideoData(video_info); } -bool AvPlayer::GetVideoData(SceAvPlayerFrameInfoEx& video_info) { +bool AvPlayer::GetVideoData(AvPlayerFrameInfoEx& video_info) { if (m_state == nullptr) { return false; } return m_state->GetVideoData(video_info); } -bool AvPlayer::GetAudioData(SceAvPlayerFrameInfo& audio_info) { +bool AvPlayer::GetAudioData(AvPlayerFrameInfo& audio_info) { if (m_state == nullptr) { return false; } diff --git a/src/core/libraries/avplayer/avplayer_impl.h b/src/core/libraries/avplayer/avplayer_impl.h index 984d81499..d951964c7 100644 --- a/src/core/libraries/avplayer/avplayer_impl.h +++ b/src/core/libraries/avplayer/avplayer_impl.h @@ -19,17 +19,21 @@ namespace Libraries::AvPlayer { class AvPlayer { public: - AvPlayer(const SceAvPlayerInitData& data); + AvPlayer(const AvPlayerInitData& data); - s32 PostInit(const SceAvPlayerPostInitData& data); + s32 PostInit(const AvPlayerPostInitData& data); s32 AddSource(std::string_view filename); - s32 GetStreamCount(); - s32 GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info); + s32 AddSourceEx(std::string_view path, AvPlayerSourceType source_type); + u32 GetStreamCount(); + s32 GetStreamInfo(u32 stream_index, AvPlayerStreamInfo& info); s32 EnableStream(u32 stream_index); s32 Start(); - bool GetAudioData(SceAvPlayerFrameInfo& audio_info); - bool GetVideoData(SceAvPlayerFrameInfo& video_info); - bool GetVideoData(SceAvPlayerFrameInfoEx& video_info); + s32 Pause(); + s32 Resume(); + s32 SetAvSyncMode(AvPlayerAvSyncMode sync_mode); + bool GetAudioData(AvPlayerFrameInfo& audio_info); + bool GetVideoData(AvPlayerFrameInfo& video_info); + bool GetVideoData(AvPlayerFrameInfoEx& video_info); bool IsActive(); u64 CurrentTime(); s32 Stop(); @@ -48,10 +52,10 @@ private: 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); + AvPlayerInitData StubInitData(const AvPlayerInitData& data); - SceAvPlayerInitData m_init_data{}; - SceAvPlayerInitData m_init_data_original{}; + AvPlayerInitData m_init_data{}; + AvPlayerInitData m_init_data_original{}; std::mutex m_file_io_mutex{}; std::atomic_bool m_has_source{}; diff --git a/src/core/libraries/avplayer/avplayer_source.cpp b/src/core/libraries/avplayer/avplayer_source.cpp index cf783403c..30474303a 100644 --- a/src/core/libraries/avplayer/avplayer_source.cpp +++ b/src/core/libraries/avplayer/avplayer_source.cpp @@ -5,6 +5,7 @@ #include "common/singleton.h" #include "common/thread.h" #include "core/file_sys/fs.h" +#include "core/libraries/avplayer/avplayer_error.h" #include "core/libraries/avplayer/avplayer_file_streamer.h" #include "core/libraries/avplayer/avplayer_source.h" @@ -29,7 +30,7 @@ AvPlayerSource::~AvPlayerSource() { Stop(); } -bool AvPlayerSource::Init(const SceAvPlayerInitData& init_data, std::string_view path) { +bool AvPlayerSource::Init(const AvPlayerInitData& init_data, std::string_view path) { m_memory_replacement = init_data.memory_replacement, m_num_output_video_framebuffers = std::min(std::max(2, init_data.num_output_video_framebuffers), 16); @@ -76,25 +77,21 @@ s32 AvPlayerSource::GetStreamCount() { return m_avformat_context->nb_streams; } -static SceAvPlayerStreamType CodecTypeToStreamType(AVMediaType codec_type) { +static AvPlayerStreamType CodecTypeToStreamType(AVMediaType codec_type) { switch (codec_type) { case AVMediaType::AVMEDIA_TYPE_VIDEO: - return SceAvPlayerStreamType::Video; + return AvPlayerStreamType::Video; case AVMediaType::AVMEDIA_TYPE_AUDIO: - return SceAvPlayerStreamType::Audio; + return AvPlayerStreamType::Audio; case AVMediaType::AVMEDIA_TYPE_SUBTITLE: - return SceAvPlayerStreamType::TimedText; + return AvPlayerStreamType::TimedText; default: LOG_ERROR(Lib_AvPlayer, "Unexpected AVMediaType {}", magic_enum::enum_name(codec_type)); - return SceAvPlayerStreamType::Unknown; + return AvPlayerStreamType::Unknown; } } -static f32 AVRationalToF32(const AVRational rational) { - return f32(rational.num) / rational.den; -} - -bool AvPlayerSource::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info) { +bool AvPlayerSource::GetStreamInfo(u32 stream_index, AvPlayerStreamInfo& 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); @@ -115,7 +112,7 @@ bool AvPlayerSource::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info LOG_WARNING(Lib_AvPlayer, "Stream {} language is unknown", stream_index); } switch (info.type) { - case SceAvPlayerStreamType::Video: { + case AvPlayerStreamType::Video: { LOG_INFO(Lib_AvPlayer, "Stream {} is a video stream.", stream_index); info.details.video.aspect_ratio = f32(p_stream->codecpar->width) / p_stream->codecpar->height; @@ -133,7 +130,7 @@ bool AvPlayerSource::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info } break; } - case SceAvPlayerStreamType::Audio: { + case AvPlayerStreamType::Audio: { 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; @@ -144,7 +141,7 @@ bool AvPlayerSource::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info } break; } - case SceAvPlayerStreamType::TimedText: { + case AvPlayerStreamType::TimedText: { LOG_WARNING(Lib_AvPlayer, "Stream {} is a timedtext stream.", stream_index); info.details.subs.font_size = 12; info.details.subs.text_size = 12; @@ -264,14 +261,13 @@ bool AvPlayerSource::Stop() { m_demuxer_thread.Stop(); if (m_current_audio_frame.has_value()) { - m_audio_buffers.Push(std::move(m_current_audio_frame.value())); + m_audio_buffers.Push(std::move(m_current_audio_frame->buffer)); m_current_audio_frame.reset(); } if (m_current_video_frame.has_value()) { - m_video_buffers.Push(std::move(m_current_video_frame.value())); + m_video_buffers.Push(std::move(m_current_video_frame->buffer)); m_current_video_frame.reset(); } - m_stop_cv.Notify(); m_audio_packets.Clear(); m_video_packets.Clear(); @@ -280,97 +276,91 @@ bool AvPlayerSource::Stop() { return true; } -bool AvPlayerSource::GetVideoData(SceAvPlayerFrameInfo& video_info) { - if (!IsActive()) { - return false; - } +void AvPlayerSource::Pause() { + m_pause_time = std::chrono::high_resolution_clock::now(); + m_is_paused = true; +} - SceAvPlayerFrameInfoEx info{}; +void AvPlayerSource::Resume() { + m_pause_duration += std::chrono::high_resolution_clock::now() - m_pause_time; + m_is_paused = false; +} + +bool AvPlayerSource::GetVideoData(AvPlayerFrameInfo& video_info) { + AvPlayerFrameInfoEx info{}; if (!GetVideoData(info)) { return false; } video_info = {}; video_info.timestamp = u64(info.timestamp); - video_info.pData = reinterpret_cast(info.pData); + video_info.p_data = reinterpret_cast(info.p_data); 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; } -bool AvPlayerSource::GetVideoData(SceAvPlayerFrameInfoEx& video_info) { - if (!IsActive()) { +bool AvPlayerSource::GetVideoData(AvPlayerFrameInfoEx& video_info) { + if (!IsActive() || m_is_paused) { return false; } - 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_TRACE(Lib_AvPlayer, "Could get video frame. EOF reached."); + if (m_video_frames.Size() == 0) { return false; } - { - using namespace std::chrono; - auto 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; - } + const auto& new_frame = m_video_frames.Front(); + if (m_state.GetSyncMode() == AvPlayerAvSyncMode::Default) { + const auto current_time = CurrentTime(); + if (0 < current_time && current_time < new_frame.info.timestamp) { + LOG_TRACE(Lib_AvPlayer, "{} < {}", current_time, new_frame.info.timestamp); + return false; } } - // return the buffer to the queue + auto frame = m_video_frames.Pop(); if (m_current_video_frame.has_value()) { - m_video_buffers.Push(std::move(m_current_video_frame.value())); + // return the buffer to the queue + m_video_buffers.Push(std::move(m_current_video_frame->buffer)); m_video_buffers_cv.Notify(); } - m_current_video_frame = std::move(frame->buffer); + + if (frame->is_loop && m_is_looping) { + m_state.OnLoop(); + } + video_info = frame->info; + m_current_video_frame = std::move(frame); return true; } -bool AvPlayerSource::GetAudioData(SceAvPlayerFrameInfo& audio_info) { - if (!IsActive()) { +bool AvPlayerSource::GetAudioData(AvPlayerFrameInfo& audio_info) { + if (!IsActive() || m_is_paused) { return false; } - m_audio_frames_cv.Wait([this] { return m_audio_frames.Size() != 0 || m_is_eof; }); + if (m_audio_frames.Size() == 0) { + return false; + } auto frame = m_audio_frames.Pop(); - if (!frame.has_value()) { - LOG_TRACE(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(); - 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())); + // return the buffer to the queue + m_audio_buffers.Push(std::move(m_current_audio_frame->buffer)); m_audio_buffers_cv.Notify(); } - m_current_audio_frame = std::move(frame->buffer); + + if (frame->is_loop && m_is_looping) { + m_state.OnLoop(); + } audio_info = {}; audio_info.timestamp = frame->info.timestamp; - audio_info.pData = reinterpret_cast(frame->info.pData); + audio_info.p_data = reinterpret_cast(frame->info.p_data); audio_info.details.audio.sample_rate = frame->info.details.audio.sample_rate; audio_info.details.audio.size = frame->info.details.audio.size; audio_info.details.audio.channel_count = frame->info.details.audio.channel_count; + m_current_audio_frame = std::move(frame); return true; } @@ -379,7 +369,9 @@ u64 AvPlayerSource::CurrentTime() { return 0; } using namespace std::chrono; - return duration_cast(high_resolution_clock::now() - m_start_time).count(); + return duration_cast(high_resolution_clock::now() - m_start_time - + m_pause_duration) + .count(); } bool AvPlayerSource::IsActive() { @@ -423,8 +415,11 @@ void AvPlayerSource::ReleaseAVFormatContext(AVFormatContext* context) { } } +// Custom flag that is not passed to ffmpeg +// It's here to get rid of the need for a separate structure +#define AV_PKT_FLAG_LOOP 0x100000 + void AvPlayerSource::DemuxerThread(std::stop_token stop) { - using namespace std::chrono; Common::SetCurrentThreadName("shadPS4:AvDemuxer"); if (!m_audio_stream_index.has_value() && !m_video_stream_index.has_value()) { @@ -433,49 +428,46 @@ void AvPlayerSource::DemuxerThread(std::stop_token stop) { } LOG_INFO(Lib_AvPlayer, "Demuxer Thread started"); + bool is_loop = false; while (!stop.stop_requested()) { if (m_video_packets.Size() > 30 && (!m_audio_stream_index.has_value() || m_audio_packets.Size() > 8)) { - std::this_thread::sleep_for(milliseconds(5)); + std::this_thread::sleep_for(std::chrono::milliseconds(5)); continue; } + AVPacketPtr up_packet(av_packet_alloc(), &ReleaseAVPacket); const auto res = av_read_frame(m_avformat_context.get(), up_packet.get()); - if (res < 0) { - if (res == AVERROR_EOF) { - 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(); - return; + if (res >= 0) [[likely]] { + if (is_loop) { + up_packet->flags |= AV_PKT_FLAG_LOOP; + is_loop = false; } + AddPacket(std::move(up_packet)); + continue; + } + + if (res != AVERROR_EOF) [[unlikely]] { + LOG_ERROR(Lib_AvPlayer, "Could not read AV frame: error = {}", res); + m_state.OnError(); break; } - 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(); + + if (!m_is_looping) { + LOG_INFO(Lib_AvPlayer, "EOF reached in demuxer. Exiting."); + break; + } + + is_loop = true; + + LOG_INFO(Lib_AvPlayer, "Looping the source..."); + avio_seek(m_avformat_context->pb, 0, SEEK_SET); + if (m_audio_stream_index.has_value()) { + const auto index = m_audio_stream_index.value(); + av_seek_frame(m_avformat_context.get(), index, 0, 0); + } else if (m_video_stream_index.has_value()) { + const auto index = m_video_stream_index.value(); + av_seek_frame(m_avformat_context.get(), index, 0, 0); } } @@ -493,6 +485,16 @@ void AvPlayerSource::DemuxerThread(std::stop_token stop) { LOG_INFO(Lib_AvPlayer, "Demuxer Thread exited normally"); } +void AvPlayerSource::AddPacket(AVPacketPtr up_packet) { + 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(); + } +} + AvPlayerSource::AVFramePtr AvPlayerSource::ConvertVideoFrame(const AVFrame& frame) { auto nv12_frame = AVFramePtr{av_frame_alloc(), &ReleaseAVFrame}; nv12_frame->pts = frame.pts; @@ -548,7 +550,7 @@ static void CopyNV12Data(u8* dst, const AVFrame& src, bool use_vdec2) { } } -Frame AvPlayerSource::PrepareVideoFrame(FrameBuffer buffer, const AVFrame& frame) { +Frame AvPlayerSource::PrepareVideoFrame(FrameBuffer buffer, const AVFrame& frame, bool is_loop) { ASSERT(frame.format == AV_PIX_FMT_NV12); auto p_buffer = buffer.GetBuffer(); @@ -572,7 +574,7 @@ Frame AvPlayerSource::PrepareVideoFrame(FrameBuffer buffer, const AVFrame& frame .buffer = std::move(buffer), .info = { - .pData = p_buffer, + .p_data = p_buffer, .timestamp = timestamp, .details = { @@ -580,7 +582,7 @@ Frame AvPlayerSource::PrepareVideoFrame(FrameBuffer buffer, const AVFrame& frame { .width = width, .height = height, - .aspect_ratio = AVRationalToF32(frame.sample_aspect_ratio), + .aspect_ratio = (float)av_q2d(frame.sample_aspect_ratio), .crop_left_offset = u32(frame.crop_left), .crop_right_offset = u32(frame.crop_right + (width - frame.width)), .crop_top_offset = u32(frame.crop_top), @@ -592,6 +594,7 @@ Frame AvPlayerSource::PrepareVideoFrame(FrameBuffer buffer, const AVFrame& frame }, }, }, + .is_loop = is_loop, }; } @@ -610,6 +613,8 @@ void AvPlayerSource::VideoDecoderThread(std::stop_token stop) { continue; } + bool is_loop = (packet->get()->flags & AV_PKT_FLAG_LOOP) == AV_PKT_FLAG_LOOP; + packet->get()->flags &= ~AV_PKT_FLAG_LOOP; // Remove the fake flag auto res = avcodec_send_packet(m_video_codec_context.get(), packet->get()); if (res < 0 && res != AVERROR(EAGAIN)) { m_state.OnError(); @@ -645,10 +650,13 @@ void AvPlayerSource::VideoDecoderThread(std::stop_token stop) { } if (up_frame->format != AV_PIX_FMT_NV12) { const auto nv12_frame = ConvertVideoFrame(*up_frame); - m_video_frames.Push(PrepareVideoFrame(std::move(buffer.value()), *nv12_frame)); + m_video_frames.Push( + PrepareVideoFrame(std::move(buffer.value()), *nv12_frame, is_loop)); } else { - m_video_frames.Push(PrepareVideoFrame(std::move(buffer.value()), *up_frame)); + m_video_frames.Push( + PrepareVideoFrame(std::move(buffer.value()), *up_frame, is_loop)); } + is_loop = false; m_video_frames_cv.Notify(); } } @@ -683,7 +691,7 @@ AvPlayerSource::AVFramePtr AvPlayerSource::ConvertAudioFrame(const AVFrame& fram return pcm16_frame; } -Frame AvPlayerSource::PrepareAudioFrame(FrameBuffer buffer, const AVFrame& frame) { +Frame AvPlayerSource::PrepareAudioFrame(FrameBuffer buffer, const AVFrame& frame, bool is_loop) { ASSERT(frame.format == AV_SAMPLE_FMT_S16); ASSERT(frame.nb_samples <= 1024); @@ -702,7 +710,7 @@ Frame AvPlayerSource::PrepareAudioFrame(FrameBuffer buffer, const AVFrame& frame .buffer = std::move(buffer), .info = { - .pData = p_buffer, + .p_data = p_buffer, .timestamp = timestamp, .details = { @@ -714,6 +722,7 @@ Frame AvPlayerSource::PrepareAudioFrame(FrameBuffer buffer, const AVFrame& frame }, }, }, + .is_loop = is_loop, }; } @@ -731,6 +740,8 @@ void AvPlayerSource::AudioDecoderThread(std::stop_token stop) { if (!packet.has_value()) { continue; } + bool is_loop = (packet->get()->flags & AV_PKT_FLAG_LOOP) == AV_PKT_FLAG_LOOP; + packet->get()->flags &= ~AV_PKT_FLAG_LOOP; // Remove the fake flag auto res = avcodec_send_packet(m_audio_codec_context.get(), packet->get()); if (res < 0 && res != AVERROR(EAGAIN)) { m_state.OnError(); @@ -767,10 +778,13 @@ void AvPlayerSource::AudioDecoderThread(std::stop_token stop) { } if (up_frame->format != AV_SAMPLE_FMT_S16) { const auto pcm16_frame = ConvertAudioFrame(*up_frame); - m_audio_frames.Push(PrepareAudioFrame(std::move(buffer.value()), *pcm16_frame)); + m_audio_frames.Push( + PrepareAudioFrame(std::move(buffer.value()), *pcm16_frame, is_loop)); } else { - m_audio_frames.Push(PrepareAudioFrame(std::move(buffer.value()), *up_frame)); + m_audio_frames.Push( + PrepareAudioFrame(std::move(buffer.value()), *up_frame, is_loop)); } + is_loop = false; m_audio_frames_cv.Notify(); } } diff --git a/src/core/libraries/avplayer/avplayer_source.h b/src/core/libraries/avplayer/avplayer_source.h index 7e199c457..5ce18b922 100644 --- a/src/core/libraries/avplayer/avplayer_source.h +++ b/src/core/libraries/avplayer/avplayer_source.h @@ -30,14 +30,17 @@ class AvPlayerStateCallback { public: virtual ~AvPlayerStateCallback() = default; + virtual AvPlayerAvSyncMode GetSyncMode() = 0; + virtual void OnWarning(u32 id) = 0; virtual void OnError() = 0; + virtual void OnLoop() = 0; virtual void OnEOF() = 0; }; class FrameBuffer { public: - FrameBuffer(const SceAvPlayerMemAllocator& memory_replacement, u32 align, u32 size) noexcept + FrameBuffer(const AvPlayerMemAllocator& memory_replacement, u32 align, u32 size) noexcept : m_memory_replacement(memory_replacement), m_data(Allocate(memory_replacement, align, size)) { ASSERT_MSG(m_data, "Could not allocated frame buffer."); @@ -68,22 +71,23 @@ public: } private: - static u8* Allocate(const SceAvPlayerMemAllocator& memory_replacement, u32 align, u32 size) { + static u8* Allocate(const AvPlayerMemAllocator& 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) { + static void Deallocate(const AvPlayerMemAllocator& memory_replacement, void* ptr) { memory_replacement.deallocate(memory_replacement.object_ptr, ptr); } - const SceAvPlayerMemAllocator& m_memory_replacement; + const AvPlayerMemAllocator& m_memory_replacement; u8* m_data = nullptr; }; struct Frame { FrameBuffer buffer; - SceAvPlayerFrameInfoEx info; + AvPlayerFrameInfoEx info; + bool is_loop = false; }; class EventCV { @@ -121,18 +125,20 @@ public: AvPlayerSource(AvPlayerStateCallback& state, bool use_vdec2); ~AvPlayerSource(); - bool Init(const SceAvPlayerInitData& init_data, std::string_view path); + bool Init(const AvPlayerInitData& init_data, std::string_view path); bool FindStreamInfo(); s32 GetStreamCount(); - bool GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info); + bool GetStreamInfo(u32 stream_index, AvPlayerStreamInfo& info); bool EnableStream(u32 stream_index); void SetLooping(bool is_looping); std::optional HasFrames(u32 num_frames); bool Start(); bool Stop(); - bool GetAudioData(SceAvPlayerFrameInfo& audio_info); - bool GetVideoData(SceAvPlayerFrameInfo& video_info); - bool GetVideoData(SceAvPlayerFrameInfoEx& video_info); + void Pause(); + void Resume(); + bool GetAudioData(AvPlayerFrameInfo& audio_info); + bool GetVideoData(AvPlayerFrameInfo& video_info); + bool GetVideoData(AvPlayerFrameInfoEx& video_info); u64 CurrentTime(); bool IsActive(); @@ -156,20 +162,22 @@ private: void AudioDecoderThread(std::stop_token stop); bool HasRunningThreads() const; + void AddPacket(AVPacketPtr up_packet); AVFramePtr ConvertAudioFrame(const AVFrame& frame); AVFramePtr ConvertVideoFrame(const AVFrame& frame); - Frame PrepareAudioFrame(FrameBuffer buffer, const AVFrame& frame); - Frame PrepareVideoFrame(FrameBuffer buffer, const AVFrame& frame); + Frame PrepareAudioFrame(FrameBuffer buffer, const AVFrame& frame, bool is_loop); + Frame PrepareVideoFrame(FrameBuffer buffer, const AVFrame& frame, bool is_loop); AvPlayerStateCallback& m_state; bool m_use_vdec2 = false; - SceAvPlayerMemAllocator m_memory_replacement{}; + AvPlayerMemAllocator m_memory_replacement{}; u32 m_num_output_video_framebuffers{}; std::atomic_bool m_is_looping = false; + std::atomic_bool m_is_paused = false; std::atomic_bool m_is_eof = false; std::unique_ptr m_up_data_streamer; @@ -183,8 +191,8 @@ private: AvPlayerQueue m_audio_frames; AvPlayerQueue m_video_frames; - std::optional m_current_video_frame; - std::optional m_current_audio_frame; + std::optional m_current_video_frame; + std::optional m_current_audio_frame; std::optional m_video_stream_index{}; std::optional m_audio_stream_index{}; @@ -197,8 +205,6 @@ private: EventCV m_video_frames_cv{}; EventCV m_video_buffers_cv{}; - EventCV m_stop_cv{}; - std::mutex m_state_mutex{}; Kernel::Thread m_demuxer_thread{}; Kernel::Thread m_video_decoder_thread{}; @@ -211,6 +217,8 @@ private: SWSContextPtr m_sws_context{nullptr, &ReleaseSWSContext}; std::chrono::high_resolution_clock::time_point m_start_time{}; + std::chrono::high_resolution_clock::time_point m_pause_time{}; + std::chrono::high_resolution_clock::duration m_pause_duration{}; }; } // namespace Libraries::AvPlayer diff --git a/src/core/libraries/avplayer/avplayer_state.cpp b/src/core/libraries/avplayer/avplayer_state.cpp index 143df749c..b8b04e758 100644 --- a/src/core/libraries/avplayer/avplayer_state.cpp +++ b/src/core/libraries/avplayer/avplayer_state.cpp @@ -6,17 +6,18 @@ #include "core/libraries/avplayer/avplayer_error.h" #include "core/libraries/avplayer/avplayer_source.h" #include "core/libraries/avplayer/avplayer_state.h" +#include "core/libraries/kernel/time.h" #include "core/tls.h" #include namespace Libraries::AvPlayer { -void PS4_SYSV_ABI AvPlayerState::AutoPlayEventCallback(void* opaque, SceAvPlayerEvents event_id, +void PS4_SYSV_ABI AvPlayerState::AutoPlayEventCallback(void* opaque, AvPlayerEvents event_id, s32 source_id, void* event_data) { auto const self = reinterpret_cast(opaque); - if (event_id == SceAvPlayerEvents::StateReady) { + if (event_id == AvPlayerEvents::StateReady) { s32 video_stream_index = -1; s32 audio_stream_index = -1; s32 timedtext_stream_index = -1; @@ -30,7 +31,7 @@ void PS4_SYSV_ABI AvPlayerState::AutoPlayEventCallback(void* opaque, SceAvPlayer return; } for (u32 stream_index = 0; stream_index < stream_count; ++stream_index) { - SceAvPlayerStreamInfo info{}; + AvPlayerStreamInfo info{}; if (!self->GetStreamInfo(stream_index, info)) { self->Stop(); return; @@ -38,7 +39,7 @@ void PS4_SYSV_ABI AvPlayerState::AutoPlayEventCallback(void* opaque, SceAvPlayer const std::string_view default_language{self->m_default_language}; switch (info.type) { - case SceAvPlayerStreamType::Video: + case AvPlayerStreamType::Video: if (video_stream_index == -1) { video_stream_index = stream_index; } @@ -47,7 +48,7 @@ void PS4_SYSV_ABI AvPlayerState::AutoPlayEventCallback(void* opaque, SceAvPlayer video_stream_index = stream_index; } break; - case SceAvPlayerStreamType::Audio: + case AvPlayerStreamType::Audio: if (audio_stream_index == -1) { audio_stream_index = stream_index; } @@ -56,7 +57,7 @@ void PS4_SYSV_ABI AvPlayerState::AutoPlayEventCallback(void* opaque, SceAvPlayer audio_stream_index = stream_index; } break; - case SceAvPlayerStreamType::TimedText: + case AvPlayerStreamType::TimedText: if (default_language.empty()) { timedtext_stream_index = stream_index; break; @@ -86,7 +87,7 @@ void PS4_SYSV_ABI AvPlayerState::AutoPlayEventCallback(void* opaque, SceAvPlayer DefaultEventCallback(opaque, event_id, 0, event_data); } -void AvPlayerState::DefaultEventCallback(void* opaque, SceAvPlayerEvents event_id, s32 source_id, +void AvPlayerState::DefaultEventCallback(void* opaque, AvPlayerEvents event_id, s32 source_id, void* event_data) { auto const self = reinterpret_cast(opaque); const auto callback = self->m_event_replacement.event_callback; @@ -97,7 +98,7 @@ void AvPlayerState::DefaultEventCallback(void* opaque, SceAvPlayerEvents event_i } // Called inside GAME thread -AvPlayerState::AvPlayerState(const SceAvPlayerInitData& init_data) +AvPlayerState::AvPlayerState(const AvPlayerInitData& 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; @@ -122,12 +123,12 @@ AvPlayerState::~AvPlayerState() { m_event_queue.Clear(); } -void AvPlayerState::PostInit(const SceAvPlayerPostInitData& post_init_data) { +void AvPlayerState::PostInit(const AvPlayerPostInitData& post_init_data) { m_post_init_data = post_init_data; } // Called inside GAME thread -bool AvPlayerState::AddSource(std::string_view path, SceAvPlayerSourceType source_type) { +bool AvPlayerState::AddSource(std::string_view path, AvPlayerSourceType source_type) { if (path.empty()) { LOG_ERROR(Lib_AvPlayer, "File path is empty."); return false; @@ -141,8 +142,8 @@ bool AvPlayerState::AddSource(std::string_view path, SceAvPlayerSourceType sourc } m_up_source = std::make_unique( - *this, m_post_init_data.video_decoder_init.decoderType.video_type == - SceAvPlayerVideoDecoderType::Software2); + *this, m_post_init_data.video_decoder_init.decoder_type.video_type == + AvPlayerVideoDecoderType::Software2); if (!m_up_source->Init(m_init_data, path)) { SetState(AvState::Error); m_up_source.reset(); @@ -154,17 +155,35 @@ bool AvPlayerState::AddSource(std::string_view path, SceAvPlayerSourceType sourc } // Called inside GAME thread -s32 AvPlayerState::GetStreamCount() { - std::shared_lock lock(m_source_mutex); +u32 AvPlayerState::GetStreamCount() { if (m_up_source == nullptr) { - LOG_ERROR(Lib_AvPlayer, "Could not get stream count. No source."); - return -1; + return 0; } - return m_up_source->GetStreamCount(); + auto count = m_up_source->GetStreamCount(); + if (!m_stream_infos.empty()) { + return count; + } + if (count > 0) { + m_stream_infos.reserve(count); + for (u32 i = 0; i < count; ++i) { + AvPlayerStreamInfo info; + if (!m_up_source->GetStreamInfo(i, info)) { + std::lock_guard guard(m_stream_infos_mutex); + m_stream_infos.clear(); + return 0; + } + std::lock_guard guard(m_stream_infos_mutex); + m_stream_infos.emplace_back(info); + } + } + InitFilters(); + return count; } +void AvPlayerState::InitFilters() {} + // Called inside GAME thread -bool AvPlayerState::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info) { +bool AvPlayerState::GetStreamInfo(u32 stream_index, AvPlayerStreamInfo& 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); @@ -175,6 +194,12 @@ bool AvPlayerState::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info) // Called inside GAME thread bool AvPlayerState::Start() { + if (m_current_state != AvState::Ready && m_current_state != AvState::Stop) { + if (!Stop()) { + return false; + } + } + SetState(AvState::Starting); std::shared_lock lock(m_source_mutex); if (m_up_source == nullptr || !m_up_source->Start()) { LOG_ERROR(Lib_AvPlayer, "Could not start playback."); @@ -185,6 +210,64 @@ bool AvPlayerState::Start() { return true; } +// Called inside GAME thread +bool AvPlayerState::Pause() { + if (m_current_state == AvState::EndOfFile) { + return true; + } + if (m_current_state == AvState::Pause || m_current_state == AvState::Ready || + m_current_state == AvState::Initial || m_current_state == AvState::Unknown || + m_current_state == AvState::AddingSource) { + LOG_ERROR(Lib_AvPlayer, "Could not pause playback."); + return false; + } + std::shared_lock lock(m_source_mutex); + if (m_up_source == nullptr) { + return false; + } + m_up_source->Pause(); + SetState(AvState::Pause); + m_timer.Update(); + OnPlaybackStateChanged(AvState::Pause); + return true; +} + +// Called inside GAME thread +bool AvPlayerState::Resume() { + if (m_current_state == AvState::Initial || m_current_state == AvState::Stop || + m_current_state == AvState::Ready || m_current_state == AvState::AddingSource || + m_current_state == AvState::Error) { + return false; + } + while (m_current_state == AvState::Starting || m_current_state == AvState::Jump || + m_current_state == AvState::TrickMode) { + std::this_thread::sleep_for(std::chrono::microseconds(90)); + } + if (m_current_state == AvState::Buffering || m_current_state == AvState::Pause || + m_current_state == AvState::C0x10) { + m_timer.UpdateVPts(); + std::shared_lock lock(m_source_mutex); + m_up_source->Resume(); + SetState(AvState::Play); + OnPlaybackStateChanged(AvState::Play); + return true; + } + if (m_current_state == AvState::PauseOnEOF) { + m_timer.UpdateVPts(); + SetState(AvState::EndOfFile); + OnPlaybackStateChanged(AvState::EndOfFile); + return true; + } + if (m_current_state == AvState::Play || m_current_state == AvState::EndOfFile) { + return true; + } + return false; +} + +void AvPlayerState::SetAvSyncMode(AvPlayerAvSyncMode sync_mode) { + m_sync_mode = sync_mode; +} + void AvPlayerState::AvControllerThread(std::stop_token stop) { using std::chrono::milliseconds; Common::SetCurrentThreadName("shadPS4:AvController"); @@ -233,21 +316,31 @@ 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) { + if (m_current_state == AvState::Unknown || m_current_state == AvState::Stop) { return false; } - if (!m_up_source->Stop()) { - return false; + while (m_current_state == AvState::Jump || m_current_state == AvState::TrickMode) { + std::this_thread::sleep_for(std::chrono::microseconds(90)); } if (!SetState(AvState::Stop)) { return false; } - OnPlaybackStateChanged(AvState::Stop); + EmitEvent(AvPlayerEvents::StateStop); + { + std::shared_lock lock(m_source_mutex); + if (m_up_source == nullptr || !m_up_source->Stop()) { + return false; + } + } + m_timer.Reset(); return true; } -bool AvPlayerState::GetVideoData(SceAvPlayerFrameInfo& video_info) { +bool AvPlayerState::GetVideoData(AvPlayerFrameInfo& video_info) { + if (m_current_state == AvState::Play || m_current_state == AvState::EndOfFile || + m_current_state == AvState::C0x10) { + return false; + } std::shared_lock lock(m_source_mutex); if (m_up_source == nullptr) { return false; @@ -255,7 +348,11 @@ bool AvPlayerState::GetVideoData(SceAvPlayerFrameInfo& video_info) { return m_up_source->GetVideoData(video_info); } -bool AvPlayerState::GetVideoData(SceAvPlayerFrameInfoEx& video_info) { +bool AvPlayerState::GetVideoData(AvPlayerFrameInfoEx& video_info) { + if (m_current_state == AvState::Play || m_current_state == AvState::EndOfFile || + m_current_state == AvState::C0x10) { + return false; + } std::shared_lock lock(m_source_mutex); if (m_up_source == nullptr) { return false; @@ -263,7 +360,7 @@ bool AvPlayerState::GetVideoData(SceAvPlayerFrameInfoEx& video_info) { return m_up_source->GetVideoData(video_info); } -bool AvPlayerState::GetAudioData(SceAvPlayerFrameInfo& audio_info) { +bool AvPlayerState::GetAudioData(AvPlayerFrameInfo& audio_info) { std::shared_lock lock(m_source_mutex); if (m_up_source == nullptr) { return false; @@ -305,11 +402,20 @@ void AvPlayerState::OnWarning(u32 id) { WarningEvent(id); } +AvPlayerAvSyncMode AvPlayerState::GetSyncMode() { + return m_sync_mode; +} + void AvPlayerState::OnError() { SetState(AvState::Error); OnPlaybackStateChanged(AvState::Error); } +void AvPlayerState::OnLoop() { + auto id = ORBIS_AVPLAYER_ERROR_WAR_LOOPING_BACK; + EmitEvent(AvPlayerEvents::WarningId, &id); +} + void AvPlayerState::OnEOF() { SetState(AvState::EndOfFile); } @@ -318,23 +424,27 @@ void AvPlayerState::OnEOF() { void AvPlayerState::OnPlaybackStateChanged(AvState state) { switch (state) { case AvState::Ready: { - EmitEvent(SceAvPlayerEvents::StateReady); + EmitEvent(AvPlayerEvents::StateReady); break; } case AvState::Play: { - EmitEvent(SceAvPlayerEvents::StatePlay); + EmitEvent(AvPlayerEvents::StatePlay); break; } case AvState::Stop: { - EmitEvent(SceAvPlayerEvents::StateStop); + EmitEvent(AvPlayerEvents::StateStop); break; } case AvState::Pause: { - EmitEvent(SceAvPlayerEvents::StatePause); + EmitEvent(AvPlayerEvents::StatePause); break; } case AvState::Buffering: { - EmitEvent(SceAvPlayerEvents::StateBuffering); + EmitEvent(AvPlayerEvents::StateBuffering); + break; + } + case AvState::EndOfFile: { + EmitEvent(AvPlayerEvents::Initial); break; } default: @@ -366,7 +476,7 @@ std::optional AvPlayerState::OnBufferingCheckEvent(u32 num_frames) { } // Called inside CONTROLLER thread -void AvPlayerState::EmitEvent(SceAvPlayerEvents event_id, void* event_data) { +void AvPlayerState::EmitEvent(AvPlayerEvents 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_init_data.event_replacement.event_callback; if (callback) { @@ -389,7 +499,7 @@ void AvPlayerState::ProcessEvent() { } switch (event->event) { case AvEventType::WarningId: { - OnWarning(event->payload.error); + EmitEvent(AvPlayerEvents::WarningId, &event->payload.error); break; } case AvEventType::RevertState: { @@ -427,6 +537,7 @@ void AvPlayerState::UpdateBufferingState() { if (has_frames.value()) { const auto state = m_previous_state >= AvState::C0x0B ? m_previous_state.load() : AvState::Play; + m_timer.UpdateVPts(); SetState(state); OnPlaybackStateChanged(state); } @@ -448,7 +559,7 @@ bool AvPlayerState::IsStateTransitionValid(AvState state) { switch (m_current_state.load()) { case AvState::Stop: case AvState::EndOfFile: - // case AvState::C0x08: + case AvState::PauseOnEOF: case AvState::Error: return false; default: @@ -459,7 +570,7 @@ bool AvPlayerState::IsStateTransitionValid(AvState state) { switch (m_current_state.load()) { case AvState::Stop: case AvState::EndOfFile: - // case AvState::C0x08: + case AvState::PauseOnEOF: case AvState::Starting: case AvState::Error: return false; @@ -471,7 +582,7 @@ bool AvPlayerState::IsStateTransitionValid(AvState state) { switch (m_current_state.load()) { case AvState::Stop: case AvState::EndOfFile: - // case AvState::C0x08: + case AvState::PauseOnEOF: case AvState::TrickMode: case AvState::Starting: case AvState::Error: @@ -484,7 +595,7 @@ bool AvPlayerState::IsStateTransitionValid(AvState state) { switch (m_current_state.load()) { case AvState::Stop: case AvState::EndOfFile: - // case AvState::C0x08: + case AvState::PauseOnEOF: case AvState::Jump: case AvState::Starting: case AvState::Error: @@ -498,7 +609,7 @@ bool AvPlayerState::IsStateTransitionValid(AvState state) { case AvState::Stop: case AvState::EndOfFile: case AvState::Pause: - // case AvState::C0x08: + case AvState::PauseOnEOF: case AvState::Starting: case AvState::Error: return false; @@ -511,4 +622,70 @@ bool AvPlayerState::IsStateTransitionValid(AvState state) { } } +u64 AvPlayerTimer::Now() { + auto now = Kernel::sceKernelGetProcessTimeCounter(); + const auto frequency = Kernel::sceKernelGetProcessTimeCounterFrequency(); + if (frequency != 0) { + now = ((now % frequency) * 1'000'000) / frequency + (now / frequency) * 1'000'000; + } + return now / 100; +} + +void AvPlayerTimer::Update() { + UpdateAPts(m_v_hint_pts); +} + +void AvPlayerTimer::UpdateAPts(u64 timestamp) { + const auto now = Now(); + + m_last_update_pts = now; + m_a_pts = timestamp; + m_ts_diff = now - m_a_pts; +} + +void AvPlayerTimer::UpdateVPts() { + const auto now = Now(); + + m_last_update_pts = now; + m_v_pts = now; + m_ts_diff = now - m_a_pts; +} + +void AvPlayerTimer::UpdateVHintPts(u64 hint) { + const auto now = Now(); + + m_v_pts = now; + m_v_hint_pts = hint; +} + +void AvPlayerTimer::Reset() { + m_ts_diff = -1; + m_v_pts = -1; + m_v_hint_pts = -1; + m_last_update_pts = 0; + m_a_pts = 0; +} + +AvPlayerTimer::State AvPlayerTimer::GetState(AvPlayerAvSyncMode sync_mode) { + if (m_ts_diff == -1 || m_v_hint_pts == -1) { + return State::Error; + } + + if (sync_mode == AvPlayerAvSyncMode::None) { + return State::Ok; + } + + const s64 diff = (Now() - m_last_update_pts) + (m_a_pts - m_v_hint_pts); + if (diff > 2800) { + return State::VideoBehind; + } + if (diff < -6900) { + return State::VideoAhead; + } + if (diff < 200) { + return State::WaitForSync; + } + return State::Ok; +} + } // namespace Libraries::AvPlayer diff --git a/src/core/libraries/avplayer/avplayer_state.h b/src/core/libraries/avplayer/avplayer_state.h index 48cd17bf2..4bab2b414 100644 --- a/src/core/libraries/avplayer/avplayer_state.h +++ b/src/core/libraries/avplayer/avplayer_state.h @@ -16,45 +16,78 @@ namespace Libraries::AvPlayer { class Stream; class AvDecoder; +class AvPlayerTimer { +public: + enum class State { + Ok, + WaitForSync, + VideoAhead, + VideoBehind, + Error, + }; + + void Update(); + void UpdateAPts(u64 timestamp); + void UpdateVHintPts(u64 hint); + void UpdateVPts(); + void Reset(); + State GetState(AvPlayerAvSyncMode sync_mode); + +private: + static u64 Now(); + + std::atomic m_ts_diff = -1; + std::atomic m_v_pts = -1; + std::atomic m_v_hint_pts = -1; + std::atomic m_last_update_pts = 0; + std::atomic m_a_pts = 0; +}; + class AvPlayerState : public AvPlayerStateCallback { public: - AvPlayerState(const SceAvPlayerInitData& init_data); + AvPlayerState(const AvPlayerInitData& init_data); ~AvPlayerState(); - void PostInit(const SceAvPlayerPostInitData& post_init_data); - bool AddSource(std::string_view filename, SceAvPlayerSourceType source_type); - s32 GetStreamCount(); - bool GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info); + void PostInit(const AvPlayerPostInitData& post_init_data); + bool AddSource(std::string_view filename, AvPlayerSourceType source_type); + u32 GetStreamCount(); + bool GetStreamInfo(u32 stream_index, AvPlayerStreamInfo& info); bool EnableStream(u32 stream_index); bool Start(); bool Stop(); - bool GetAudioData(SceAvPlayerFrameInfo& audio_info); - bool GetVideoData(SceAvPlayerFrameInfo& video_info); - bool GetVideoData(SceAvPlayerFrameInfoEx& video_info); + bool Pause(); + bool Resume(); + void SetAvSyncMode(AvPlayerAvSyncMode sync_mode); + bool GetAudioData(AvPlayerFrameInfo& audio_info); + bool GetVideoData(AvPlayerFrameInfo& video_info); + bool GetVideoData(AvPlayerFrameInfoEx& video_info); bool IsActive(); u64 CurrentTime(); bool SetLooping(bool is_looping); private: // Event Replacement - static void PS4_SYSV_ABI AutoPlayEventCallback(void* handle, SceAvPlayerEvents event_id, + static void PS4_SYSV_ABI AutoPlayEventCallback(void* handle, AvPlayerEvents event_id, s32 source_id, void* event_data); - static void PS4_SYSV_ABI DefaultEventCallback(void* handle, SceAvPlayerEvents event_id, + static void PS4_SYSV_ABI DefaultEventCallback(void* handle, AvPlayerEvents event_id, s32 source_id, void* event_data); + AvPlayerAvSyncMode GetSyncMode() override; void OnWarning(u32 id) override; void OnError() override; + void OnLoop() override; void OnEOF() override; void OnPlaybackStateChanged(AvState state); std::optional OnBufferingCheckEvent(u32 num_frames); - void EmitEvent(SceAvPlayerEvents event_id, void* event_data = nullptr); + void EmitEvent(AvPlayerEvents event_id, void* event_data = nullptr); bool SetState(AvState state); void AvControllerThread(std::stop_token stop); + void InitFilters(); void AddSourceEvent(); void WarningEvent(s32 id); @@ -65,14 +98,16 @@ private: std::unique_ptr m_up_source; - SceAvPlayerInitData m_init_data{}; - SceAvPlayerPostInitData m_post_init_data{}; - SceAvPlayerEventReplacement m_event_replacement{}; + AvPlayerInitData m_init_data{}; + AvPlayerPostInitData m_post_init_data{}; + AvPlayerEventReplacement m_event_replacement{}; bool m_auto_start{}; char m_default_language[4]{}; + AvPlayerAvSyncMode m_sync_mode = AvPlayerAvSyncMode::Default; std::atomic m_current_state; std::atomic m_previous_state; + AvPlayerTimer m_timer; u32 m_thread_priority; u32 m_thread_affinity; std::atomic_uint32_t m_some_event_result{}; @@ -82,6 +117,9 @@ private: std::mutex m_event_handler_mutex{}; Kernel::Thread m_controller_thread{}; AvPlayerQueue m_event_queue{}; + + std::mutex m_stream_infos_mutex; + std::vector m_stream_infos; }; } // namespace Libraries::AvPlayer diff --git a/src/core/libraries/kernel/time.cpp b/src/core/libraries/kernel/time.cpp index 2fe74d0a3..ae3e05ae4 100644 --- a/src/core/libraries/kernel/time.cpp +++ b/src/core/libraries/kernel/time.cpp @@ -28,6 +28,7 @@ namespace Libraries::Kernel { static u64 initial_ptc; +static u64 initial_unbiased_ptc; static std::unique_ptr clock; u64 PS4_SYSV_ABI sceKernelGetTscFrequency() { @@ -35,12 +36,11 @@ u64 PS4_SYSV_ABI sceKernelGetTscFrequency() { } u64 PS4_SYSV_ABI sceKernelGetProcessTime() { - // TODO: this timer should support suspends, so initial ptc needs to be updated on wake up - return clock->GetTimeUS(initial_ptc); + return clock->GetTimeUS(clock->GetUnbiasedUptime() - initial_unbiased_ptc); } u64 PS4_SYSV_ABI sceKernelGetProcessTimeCounter() { - return clock->GetUptime() - initial_ptc; + return clock->GetUnbiasedUptime() - initial_unbiased_ptc; } u64 PS4_SYSV_ABI sceKernelGetProcessTimeCounterFrequency() { @@ -511,6 +511,7 @@ s32 PS4_SYSV_ABI sceKernelConvertUtcToLocaltime(time_t time, time_t* local_time, void RegisterTime(Core::Loader::SymbolsResolver* sym) { clock = std::make_unique(); initial_ptc = clock->GetUptime(); + initial_unbiased_ptc = clock->GetUnbiasedUptime(); // POSIX LIB_FUNCTION("yS8U2TGCe1A", "libkernel", 1, "libkernel", 1, 1, posix_nanosleep);