mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-04-19 19:14:48 +00:00
Merge 359c49a960
into 69777e2ffa
This commit is contained in:
commit
f759c6c984
17 changed files with 664 additions and 412 deletions
|
@ -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, \
|
||||
|
|
|
@ -5,28 +5,51 @@
|
|||
#include "common/rdtsc.h"
|
||||
#include "common/uint128.h"
|
||||
|
||||
#include <chrono>
|
||||
|
||||
#ifdef _WIN64
|
||||
#include <Windows.h>
|
||||
|
||||
#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
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#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
|
||||
|
|
|
@ -6,11 +6,12 @@
|
|||
#include "common/uint128.h"
|
||||
|
||||
#ifdef _WIN64
|
||||
#include <windows.h>
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
namespace Common {
|
||||
|
||||
#ifndef _WIN64
|
||||
static constexpr size_t SecondToNanoseconds = 1000000000ULL;
|
||||
|
||||
template <u64 Nearest>
|
||||
|
@ -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<u64>(filetime.dwHighDateTime) << 32) +
|
||||
static_cast<u64>(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
|
||||
|
|
|
@ -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,64 @@ 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) {
|
||||
LOG_TRACE(Lib_AvPlayer, "called");
|
||||
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;
|
||||
// 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 +127,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 +144,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 +160,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 +171,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 +179,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 +200,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 +232,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 +240,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 handle->GetStreamCount();
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceAvPlayerVprintf(const char* format, va_list args) {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<T>(value));
|
||||
}
|
||||
|
||||
T& Front() {
|
||||
return m_queue.front();
|
||||
}
|
||||
|
||||
std::optional<T> Pop() {
|
||||
if (Size() == 0) {
|
||||
return std::nullopt;
|
||||
|
@ -84,6 +90,6 @@ private:
|
|||
std::queue<T> m_queue{};
|
||||
};
|
||||
|
||||
SceAvPlayerSourceType GetSourceType(std::string_view path);
|
||||
AvPlayerSourceType GetSourceType(std::string_view path);
|
||||
|
||||
} // namespace Libraries::AvPlayer
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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{};
|
||||
|
|
|
@ -58,7 +58,7 @@ int PS4_SYSV_ABI AvPlayer::ReadOffsetFile(void* handle, u8* buffer, u64 position
|
|||
auto const self = reinterpret_cast<AvPlayer*>(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<AvPlayerState>(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;
|
||||
|
@ -122,7 +130,7 @@ s32 AvPlayer::GetStreamCount() {
|
|||
return res;
|
||||
}
|
||||
|
||||
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 +148,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;
|
||||
}
|
||||
|
|
|
@ -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 AddSourceEx(std::string_view path, AvPlayerSourceType source_type);
|
||||
s32 GetStreamCount();
|
||||
s32 GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info);
|
||||
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{};
|
||||
|
|
|
@ -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<u8*>(info.pData);
|
||||
video_info.p_data = reinterpret_cast<u8*>(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<milliseconds>(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<milliseconds>(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<u8*>(frame->info.pData);
|
||||
audio_info.p_data = reinterpret_cast<u8*>(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<milliseconds>(high_resolution_clock::now() - m_start_time).count();
|
||||
return duration_cast<milliseconds>(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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<u8*>(
|
||||
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<bool> 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<IDataStreamer> m_up_data_streamer;
|
||||
|
@ -183,8 +191,8 @@ private:
|
|||
AvPlayerQueue<Frame> m_audio_frames;
|
||||
AvPlayerQueue<Frame> m_video_frames;
|
||||
|
||||
std::optional<FrameBuffer> m_current_video_frame;
|
||||
std::optional<FrameBuffer> m_current_audio_frame;
|
||||
std::optional<Frame> m_current_video_frame;
|
||||
std::optional<Frame> m_current_audio_frame;
|
||||
|
||||
std::optional<s32> m_video_stream_index{};
|
||||
std::optional<s32> 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
|
||||
|
|
|
@ -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 <magic_enum/magic_enum.hpp>
|
||||
|
||||
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<AvPlayerState*>(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<AvPlayerState*>(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<AvPlayerSource>(
|
||||
*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();
|
||||
|
@ -164,7 +165,7 @@ s32 AvPlayerState::GetStreamCount() {
|
|||
}
|
||||
|
||||
// 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 +176,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 +192,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 +298,27 @@ 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) {
|
||||
std::shared_lock lock(m_source_mutex);
|
||||
if (m_up_source == nullptr) {
|
||||
return false;
|
||||
|
@ -255,7 +326,7 @@ 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) {
|
||||
std::shared_lock lock(m_source_mutex);
|
||||
if (m_up_source == nullptr) {
|
||||
return false;
|
||||
|
@ -263,7 +334,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 +376,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 +398,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 +450,7 @@ std::optional<bool> 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 +473,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 +511,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 +533,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 +544,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 +556,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 +569,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 +583,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 +596,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
|
||||
|
|
|
@ -16,41 +16,73 @@ 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<s64> m_ts_diff = -1;
|
||||
std::atomic<s64> m_v_pts = -1;
|
||||
std::atomic<s64> m_v_hint_pts = -1;
|
||||
std::atomic<u64> m_last_update_pts = 0;
|
||||
std::atomic<u64> 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);
|
||||
void PostInit(const AvPlayerPostInitData& post_init_data);
|
||||
bool AddSource(std::string_view filename, AvPlayerSourceType source_type);
|
||||
s32 GetStreamCount();
|
||||
bool GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info);
|
||||
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<bool> 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);
|
||||
|
@ -65,14 +97,16 @@ private:
|
|||
|
||||
std::unique_ptr<AvPlayerSource> 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<AvState> m_current_state;
|
||||
std::atomic<AvState> m_previous_state;
|
||||
AvPlayerTimer m_timer;
|
||||
u32 m_thread_priority;
|
||||
u32 m_thread_affinity;
|
||||
std::atomic_uint32_t m_some_event_result{};
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
namespace Libraries::Kernel {
|
||||
|
||||
static u64 initial_ptc;
|
||||
static u64 initial_unbiased_ptc;
|
||||
static std::unique_ptr<Common::NativeClock> clock;
|
||||
|
||||
u64 PS4_SYSV_ABI sceKernelGetTscFrequency() {
|
||||
|
@ -36,12 +37,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() {
|
||||
|
@ -172,7 +172,7 @@ static s32 clock_gettime(u32 clock_id, struct timespec* ts) {
|
|||
}
|
||||
#endif
|
||||
|
||||
int PS4_SYSV_ABI orbis_clock_gettime(s32 clock_id, struct OrbisKernelTimespec* ts) {
|
||||
int PS4_SYSV_ABI orbis_clock_gettime(s32 clock_id, OrbisKernelTimespec* ts) {
|
||||
if (ts == nullptr) {
|
||||
return ORBIS_KERNEL_ERROR_EFAULT;
|
||||
}
|
||||
|
@ -444,6 +444,7 @@ int PS4_SYSV_ABI sceKernelConvertUtcToLocaltime(time_t time, time_t* local_time,
|
|||
void RegisterTime(Core::Loader::SymbolsResolver* sym) {
|
||||
clock = std::make_unique<Common::NativeClock>();
|
||||
initial_ptc = clock->GetUptime();
|
||||
initial_unbiased_ptc = clock->GetUnbiasedUptime();
|
||||
LIB_FUNCTION("4J2sUJmuHZQ", "libkernel", 1, "libkernel", 1, 1, sceKernelGetProcessTime);
|
||||
LIB_FUNCTION("fgxnMeTNUtY", "libkernel", 1, "libkernel", 1, 1, sceKernelGetProcessTimeCounter);
|
||||
LIB_FUNCTION("BNowx2l588E", "libkernel", 1, "libkernel", 1, 1,
|
||||
|
|
Loading…
Add table
Reference in a new issue