mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-07-12 12:45:56 +00:00
WIP
This commit is contained in:
parent
577af4038a
commit
550ceb524e
14 changed files with 366 additions and 130 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__), \
|
Common::Log::FmtLogMessage(log_class, log_level, Common::Log::TrimSourcePath(__FILE__), \
|
||||||
__LINE__, __func__, __VA_ARGS__)
|
__LINE__, __func__, __VA_ARGS__)
|
||||||
|
|
||||||
#ifdef _DEBUG
|
|
||||||
#define LOG_TRACE(log_class, ...) \
|
#define LOG_TRACE(log_class, ...) \
|
||||||
Common::Log::FmtLogMessage(Common::Log::Class::log_class, Common::Log::Level::Trace, \
|
Common::Log::FmtLogMessage(Common::Log::Class::log_class, Common::Log::Level::Trace, \
|
||||||
Common::Log::TrimSourcePath(__FILE__), __LINE__, __func__, \
|
Common::Log::TrimSourcePath(__FILE__), __LINE__, __func__, \
|
||||||
__VA_ARGS__)
|
__VA_ARGS__)
|
||||||
#else
|
|
||||||
#define LOG_TRACE(log_class, fmt, ...) (void(0))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define LOG_DEBUG(log_class, ...) \
|
#define LOG_DEBUG(log_class, ...) \
|
||||||
Common::Log::FmtLogMessage(Common::Log::Class::log_class, Common::Log::Level::Debug, \
|
Common::Log::FmtLogMessage(Common::Log::Class::log_class, Common::Log::Level::Debug, \
|
||||||
|
|
|
@ -5,28 +5,51 @@
|
||||||
#include "common/rdtsc.h"
|
#include "common/rdtsc.h"
|
||||||
#include "common/uint128.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 {
|
namespace Common {
|
||||||
|
|
||||||
NativeClock::NativeClock()
|
NativeClock::NativeClock()
|
||||||
: rdtsc_frequency{EstimateRDTSCFrequency()},
|
: rdtsc_frequency{EstimateRDTSCFrequency()},
|
||||||
ns_rdtsc_factor{GetFixedPoint64Factor(std::nano::den, rdtsc_frequency)},
|
us_rdtsc_factor{GetFixedPoint64Factor(std::micro::den, rdtsc_frequency)} {}
|
||||||
us_rdtsc_factor{GetFixedPoint64Factor(std::micro::den, rdtsc_frequency)},
|
|
||||||
ms_rdtsc_factor{GetFixedPoint64Factor(std::milli::den, rdtsc_frequency)} {}
|
|
||||||
|
|
||||||
u64 NativeClock::GetTimeNS(u64 base_ptc /*= 0*/) const {
|
u64 NativeClock::GetTimeUS(u64 time) const {
|
||||||
return MultiplyHigh(GetUptime() - base_ptc, ns_rdtsc_factor);
|
return MultiplyHigh(time, us_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::GetUptime() const {
|
u64 NativeClock::GetUptime() const {
|
||||||
|
#ifdef _WIN64
|
||||||
|
LARGE_INTEGER counter;
|
||||||
|
QueryPerformanceCounter(&counter);
|
||||||
|
return counter.QuadPart;
|
||||||
|
#else
|
||||||
return FencedRDTSC();
|
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
|
} // namespace Common
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
#include "common/types.h"
|
#include "common/types.h"
|
||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
|
@ -12,20 +11,15 @@ class NativeClock final {
|
||||||
public:
|
public:
|
||||||
explicit NativeClock();
|
explicit NativeClock();
|
||||||
|
|
||||||
u64 GetTscFrequency() const {
|
u64 GetTimeUS(u64 time) const;
|
||||||
return rdtsc_frequency;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 GetUptime() const;
|
||||||
|
u64 GetUnbiasedUptime() const;
|
||||||
|
u64 GetTscFrequency() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
u64 rdtsc_frequency;
|
u64 rdtsc_frequency;
|
||||||
u64 ns_rdtsc_factor;
|
|
||||||
u64 us_rdtsc_factor;
|
u64 us_rdtsc_factor;
|
||||||
u64 ms_rdtsc_factor;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Common
|
} // namespace Common
|
||||||
|
|
|
@ -6,11 +6,12 @@
|
||||||
#include "common/uint128.h"
|
#include "common/uint128.h"
|
||||||
|
|
||||||
#ifdef _WIN64
|
#ifdef _WIN64
|
||||||
#include <windows.h>
|
#include <Windows.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
|
|
||||||
|
#ifndef _WIN64
|
||||||
static constexpr size_t SecondToNanoseconds = 1000000000ULL;
|
static constexpr size_t SecondToNanoseconds = 1000000000ULL;
|
||||||
|
|
||||||
template <u64 Nearest>
|
template <u64 Nearest>
|
||||||
|
@ -20,16 +21,7 @@ static u64 RoundToNearest(u64 value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static u64 GetTimeNs() {
|
static u64 GetTimeNs() {
|
||||||
#ifdef _WIN64
|
#if defined(__APPLE__)
|
||||||
// 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__)
|
|
||||||
return clock_gettime_nsec_np(CLOCK_REALTIME);
|
return clock_gettime_nsec_np(CLOCK_REALTIME);
|
||||||
#else
|
#else
|
||||||
timespec ts;
|
timespec ts;
|
||||||
|
@ -37,8 +29,14 @@ static u64 GetTimeNs() {
|
||||||
return ts.tv_sec * SecondToNanoseconds + ts.tv_nsec;
|
return ts.tv_sec * SecondToNanoseconds + ts.tv_nsec;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
u64 EstimateRDTSCFrequency() {
|
u64 EstimateRDTSCFrequency() {
|
||||||
|
#ifdef _WIN64
|
||||||
|
LARGE_INTEGER frequency;
|
||||||
|
QueryPerformanceFrequency(&frequency);
|
||||||
|
return frequency.QuadPart;
|
||||||
|
#else
|
||||||
// Discard the first result measuring the rdtsc.
|
// Discard the first result measuring the rdtsc.
|
||||||
FencedRDTSC();
|
FencedRDTSC();
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds{1});
|
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_diff = tsc_end - tsc_start;
|
||||||
const u64 tsc_freq = MultiplyAndDivide64(tsc_diff, 1000000000ULL, end_time - start_time);
|
const u64 tsc_freq = MultiplyAndDivide64(tsc_diff, 1000000000ULL, end_time - start_time);
|
||||||
return RoundToNearest<100'000>(tsc_freq);
|
return RoundToNearest<100'000>(tsc_freq);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Common
|
} // namespace Common
|
||||||
|
|
|
@ -42,11 +42,14 @@ s32 PS4_SYSV_ABI sceAvPlayerClose(AvPlayerHandle handle) {
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 PS4_SYSV_ABI sceAvPlayerCurrentTime(AvPlayerHandle handle) {
|
u64 PS4_SYSV_ABI sceAvPlayerCurrentTime(AvPlayerHandle handle) {
|
||||||
LOG_TRACE(Lib_AvPlayer, "called");
|
// LOG_TRACE(Lib_AvPlayer, "called");
|
||||||
if (handle == nullptr) {
|
if (handle == nullptr) {
|
||||||
|
LOG_TRACE(Lib_AvPlayer, "returning ORBIS_AVPLAYER_ERROR_INVALID_PARAMS");
|
||||||
return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS;
|
return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS;
|
||||||
}
|
}
|
||||||
return handle->CurrentTime();
|
const auto res = handle->CurrentTime();
|
||||||
|
LOG_TRACE(Lib_AvPlayer, "returning {}", res);
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 PS4_SYSV_ABI sceAvPlayerDisableStream(AvPlayerHandle handle, u32 stream_id) {
|
s32 PS4_SYSV_ABI sceAvPlayerDisableStream(AvPlayerHandle handle, u32 stream_id) {
|
||||||
|
@ -66,11 +69,18 @@ s32 PS4_SYSV_ABI sceAvPlayerEnableStream(AvPlayerHandle handle, u32 stream_id) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PS4_SYSV_ABI sceAvPlayerGetAudioData(AvPlayerHandle handle, AvPlayerFrameInfo* p_info) {
|
bool PS4_SYSV_ABI sceAvPlayerGetAudioData(AvPlayerHandle handle, AvPlayerFrameInfo* p_info) {
|
||||||
LOG_TRACE(Lib_AvPlayer, "called");
|
// LOG_TRACE(Lib_AvPlayer, "called");
|
||||||
if (handle == nullptr || p_info == nullptr) {
|
if (handle == nullptr || p_info == nullptr) {
|
||||||
|
LOG_TRACE(Lib_AvPlayer, "returning false");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return handle->GetAudioData(*p_info);
|
const auto res = handle->GetAudioData(*p_info);
|
||||||
|
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(AvPlayerHandle handle, u32 stream_id,
|
s32 PS4_SYSV_ABI sceAvPlayerGetStreamInfo(AvPlayerHandle handle, u32 stream_id,
|
||||||
|
@ -94,9 +104,20 @@ bool PS4_SYSV_ABI sceAvPlayerGetVideoDataEx(AvPlayerHandle handle,
|
||||||
AvPlayerFrameInfoEx* video_info) {
|
AvPlayerFrameInfoEx* video_info) {
|
||||||
LOG_TRACE(Lib_AvPlayer, "called");
|
LOG_TRACE(Lib_AvPlayer, "called");
|
||||||
if (handle == nullptr || video_info == nullptr) {
|
if (handle == nullptr || video_info == nullptr) {
|
||||||
|
LOG_TRACE(Lib_AvPlayer, "returning {}", false);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return handle->GetVideoData(*video_info);
|
|
||||||
|
// 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
AvPlayerHandle PS4_SYSV_ABI sceAvPlayerInit(AvPlayerInitData* data) {
|
AvPlayerHandle PS4_SYSV_ABI sceAvPlayerInit(AvPlayerInitData* data) {
|
||||||
|
@ -143,11 +164,14 @@ s32 PS4_SYSV_ABI sceAvPlayerInitEx(const AvPlayerInitDataEx* p_data, AvPlayerHan
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PS4_SYSV_ABI sceAvPlayerIsActive(AvPlayerHandle handle) {
|
bool PS4_SYSV_ABI sceAvPlayerIsActive(AvPlayerHandle handle) {
|
||||||
LOG_TRACE(Lib_AvPlayer, "called");
|
// LOG_TRACE(Lib_AvPlayer, "called");
|
||||||
if (handle == nullptr) {
|
if (handle == nullptr) {
|
||||||
|
LOG_TRACE(Lib_AvPlayer, "returning false");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return handle->IsActive();
|
const auto res = handle->IsActive();
|
||||||
|
LOG_TRACE(Lib_AvPlayer, "returning {}", res);
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 PS4_SYSV_ABI sceAvPlayerJumpToTime(AvPlayerHandle handle, uint64_t time) {
|
s32 PS4_SYSV_ABI sceAvPlayerJumpToTime(AvPlayerHandle handle, uint64_t time) {
|
||||||
|
@ -240,7 +264,7 @@ s32 PS4_SYSV_ABI sceAvPlayerStreamCount(AvPlayerHandle handle) {
|
||||||
if (handle == nullptr) {
|
if (handle == nullptr) {
|
||||||
return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS;
|
return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS;
|
||||||
}
|
}
|
||||||
return handle->GetStreamCount();
|
return static_cast<s32>(handle->GetStreamCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 PS4_SYSV_ABI sceAvPlayerVprintf(const char* format, va_list args) {
|
s32 PS4_SYSV_ABI sceAvPlayerVprintf(const char* format, va_list args) {
|
||||||
|
|
|
@ -171,6 +171,7 @@ struct AvPlayerFileReplacement {
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class AvPlayerEvents {
|
enum class AvPlayerEvents {
|
||||||
|
Initial = 0x00,
|
||||||
StateStop = 0x01,
|
StateStop = 0x01,
|
||||||
StateReady = 0x02,
|
StateReady = 0x02,
|
||||||
StatePlay = 0x03,
|
StatePlay = 0x03,
|
||||||
|
|
|
@ -24,12 +24,13 @@ enum class AvState {
|
||||||
Stop,
|
Stop,
|
||||||
EndOfFile,
|
EndOfFile,
|
||||||
Pause,
|
Pause,
|
||||||
C0x08,
|
PauseOnEOF,
|
||||||
Jump,
|
Jump,
|
||||||
TrickMode,
|
TrickMode,
|
||||||
C0x0B,
|
C0x0B,
|
||||||
Buffering,
|
Buffering,
|
||||||
Starting,
|
Starting,
|
||||||
|
C0x10,
|
||||||
Error,
|
Error,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -119,15 +119,8 @@ s32 AvPlayer::AddSourceEx(std::string_view path, AvPlayerSourceType source_type)
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 AvPlayer::GetStreamCount() {
|
u32 AvPlayer::GetStreamCount() {
|
||||||
if (m_state == nullptr) {
|
return m_state->GetStreamCount();
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 AvPlayer::GetStreamInfo(u32 stream_index, AvPlayerStreamInfo& info) {
|
s32 AvPlayer::GetStreamInfo(u32 stream_index, AvPlayerStreamInfo& info) {
|
||||||
|
|
|
@ -24,7 +24,7 @@ public:
|
||||||
s32 PostInit(const AvPlayerPostInitData& data);
|
s32 PostInit(const AvPlayerPostInitData& data);
|
||||||
s32 AddSource(std::string_view filename);
|
s32 AddSource(std::string_view filename);
|
||||||
s32 AddSourceEx(std::string_view path, AvPlayerSourceType source_type);
|
s32 AddSourceEx(std::string_view path, AvPlayerSourceType source_type);
|
||||||
s32 GetStreamCount();
|
u32 GetStreamCount();
|
||||||
s32 GetStreamInfo(u32 stream_index, AvPlayerStreamInfo& info);
|
s32 GetStreamInfo(u32 stream_index, AvPlayerStreamInfo& info);
|
||||||
s32 EnableStream(u32 stream_index);
|
s32 EnableStream(u32 stream_index);
|
||||||
s32 Start();
|
s32 Start();
|
||||||
|
|
|
@ -313,6 +313,7 @@ bool AvPlayerSource::GetVideoData(AvPlayerFrameInfoEx& video_info) {
|
||||||
if (m_state.GetSyncMode() == AvPlayerAvSyncMode::Default) {
|
if (m_state.GetSyncMode() == AvPlayerAvSyncMode::Default) {
|
||||||
const auto current_time = CurrentTime();
|
const auto current_time = CurrentTime();
|
||||||
if (0 < current_time && current_time < new_frame.info.timestamp) {
|
if (0 < current_time && current_time < new_frame.info.timestamp) {
|
||||||
|
LOG_TRACE(Lib_AvPlayer, "{} < {}", current_time, new_frame.info.timestamp);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -323,6 +324,11 @@ bool AvPlayerSource::GetVideoData(AvPlayerFrameInfoEx& video_info) {
|
||||||
m_video_buffers.Push(std::move(m_current_video_frame->buffer));
|
m_video_buffers.Push(std::move(m_current_video_frame->buffer));
|
||||||
m_video_buffers_cv.Notify();
|
m_video_buffers_cv.Notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (frame->is_loop && m_is_looping) {
|
||||||
|
m_state.OnLoop();
|
||||||
|
}
|
||||||
|
|
||||||
video_info = frame->info;
|
video_info = frame->info;
|
||||||
m_current_video_frame = std::move(frame);
|
m_current_video_frame = std::move(frame);
|
||||||
return true;
|
return true;
|
||||||
|
@ -344,6 +350,10 @@ bool AvPlayerSource::GetAudioData(AvPlayerFrameInfo& audio_info) {
|
||||||
m_audio_buffers_cv.Notify();
|
m_audio_buffers_cv.Notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (frame->is_loop && m_is_looping) {
|
||||||
|
m_state.OnLoop();
|
||||||
|
}
|
||||||
|
|
||||||
audio_info = {};
|
audio_info = {};
|
||||||
audio_info.timestamp = frame->info.timestamp;
|
audio_info.timestamp = frame->info.timestamp;
|
||||||
audio_info.p_data = reinterpret_cast<u8*>(frame->info.p_data);
|
audio_info.p_data = reinterpret_cast<u8*>(frame->info.p_data);
|
||||||
|
@ -405,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) {
|
void AvPlayerSource::DemuxerThread(std::stop_token stop) {
|
||||||
using namespace std::chrono;
|
|
||||||
Common::SetCurrentThreadName("shadPS4:AvDemuxer");
|
Common::SetCurrentThreadName("shadPS4:AvDemuxer");
|
||||||
|
|
||||||
if (!m_audio_stream_index.has_value() && !m_video_stream_index.has_value()) {
|
if (!m_audio_stream_index.has_value() && !m_video_stream_index.has_value()) {
|
||||||
|
@ -415,50 +428,46 @@ void AvPlayerSource::DemuxerThread(std::stop_token stop) {
|
||||||
}
|
}
|
||||||
LOG_INFO(Lib_AvPlayer, "Demuxer Thread started");
|
LOG_INFO(Lib_AvPlayer, "Demuxer Thread started");
|
||||||
|
|
||||||
|
bool is_loop = false;
|
||||||
while (!stop.stop_requested()) {
|
while (!stop.stop_requested()) {
|
||||||
if (m_video_packets.Size() > 30 &&
|
if (m_video_packets.Size() > 30 &&
|
||||||
(!m_audio_stream_index.has_value() || m_audio_packets.Size() > 8)) {
|
(!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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
AVPacketPtr up_packet(av_packet_alloc(), &ReleaseAVPacket);
|
AVPacketPtr up_packet(av_packet_alloc(), &ReleaseAVPacket);
|
||||||
const auto res = av_read_frame(m_avformat_context.get(), up_packet.get());
|
const auto res = av_read_frame(m_avformat_context.get(), up_packet.get());
|
||||||
if (res < 0) {
|
if (res >= 0) [[likely]] {
|
||||||
if (res == AVERROR_EOF) {
|
if (is_loop) {
|
||||||
if (m_is_looping) {
|
up_packet->flags |= AV_PKT_FLAG_LOOP;
|
||||||
LOG_INFO(Lib_AvPlayer, "EOF reached in demuxer. Looping the source...");
|
is_loop = false;
|
||||||
m_state.OnWarning(ORBIS_AVPLAYER_ERROR_WAR_LOOPING_BACK);
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
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;
|
break;
|
||||||
}
|
}
|
||||||
if (up_packet->stream_index == m_video_stream_index) {
|
|
||||||
m_video_packets.Push(std::move(up_packet));
|
if (!m_is_looping) {
|
||||||
m_video_packets_cv.Notify();
|
LOG_INFO(Lib_AvPlayer, "EOF reached in demuxer. Exiting.");
|
||||||
} else if (up_packet->stream_index == m_audio_stream_index) {
|
break;
|
||||||
m_audio_packets.Push(std::move(up_packet));
|
}
|
||||||
m_audio_packets_cv.Notify();
|
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -476,6 +485,16 @@ void AvPlayerSource::DemuxerThread(std::stop_token stop) {
|
||||||
LOG_INFO(Lib_AvPlayer, "Demuxer Thread exited normally");
|
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) {
|
AvPlayerSource::AVFramePtr AvPlayerSource::ConvertVideoFrame(const AVFrame& frame) {
|
||||||
auto nv12_frame = AVFramePtr{av_frame_alloc(), &ReleaseAVFrame};
|
auto nv12_frame = AVFramePtr{av_frame_alloc(), &ReleaseAVFrame};
|
||||||
nv12_frame->pts = frame.pts;
|
nv12_frame->pts = frame.pts;
|
||||||
|
@ -531,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);
|
ASSERT(frame.format == AV_PIX_FMT_NV12);
|
||||||
|
|
||||||
auto p_buffer = buffer.GetBuffer();
|
auto p_buffer = buffer.GetBuffer();
|
||||||
|
@ -575,6 +594,7 @@ Frame AvPlayerSource::PrepareVideoFrame(FrameBuffer buffer, const AVFrame& frame
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
.is_loop = is_loop,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -593,6 +613,8 @@ void AvPlayerSource::VideoDecoderThread(std::stop_token stop) {
|
||||||
continue;
|
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());
|
auto res = avcodec_send_packet(m_video_codec_context.get(), packet->get());
|
||||||
if (res < 0 && res != AVERROR(EAGAIN)) {
|
if (res < 0 && res != AVERROR(EAGAIN)) {
|
||||||
m_state.OnError();
|
m_state.OnError();
|
||||||
|
@ -628,10 +650,13 @@ void AvPlayerSource::VideoDecoderThread(std::stop_token stop) {
|
||||||
}
|
}
|
||||||
if (up_frame->format != AV_PIX_FMT_NV12) {
|
if (up_frame->format != AV_PIX_FMT_NV12) {
|
||||||
const auto nv12_frame = ConvertVideoFrame(*up_frame);
|
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 {
|
} 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();
|
m_video_frames_cv.Notify();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -666,7 +691,7 @@ AvPlayerSource::AVFramePtr AvPlayerSource::ConvertAudioFrame(const AVFrame& fram
|
||||||
return pcm16_frame;
|
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.format == AV_SAMPLE_FMT_S16);
|
||||||
ASSERT(frame.nb_samples <= 1024);
|
ASSERT(frame.nb_samples <= 1024);
|
||||||
|
|
||||||
|
@ -697,6 +722,7 @@ Frame AvPlayerSource::PrepareAudioFrame(FrameBuffer buffer, const AVFrame& frame
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
.is_loop = is_loop,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -714,6 +740,8 @@ void AvPlayerSource::AudioDecoderThread(std::stop_token stop) {
|
||||||
if (!packet.has_value()) {
|
if (!packet.has_value()) {
|
||||||
continue;
|
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());
|
auto res = avcodec_send_packet(m_audio_codec_context.get(), packet->get());
|
||||||
if (res < 0 && res != AVERROR(EAGAIN)) {
|
if (res < 0 && res != AVERROR(EAGAIN)) {
|
||||||
m_state.OnError();
|
m_state.OnError();
|
||||||
|
@ -750,10 +778,13 @@ void AvPlayerSource::AudioDecoderThread(std::stop_token stop) {
|
||||||
}
|
}
|
||||||
if (up_frame->format != AV_SAMPLE_FMT_S16) {
|
if (up_frame->format != AV_SAMPLE_FMT_S16) {
|
||||||
const auto pcm16_frame = ConvertAudioFrame(*up_frame);
|
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 {
|
} 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();
|
m_audio_frames_cv.Notify();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,7 @@ public:
|
||||||
|
|
||||||
virtual void OnWarning(u32 id) = 0;
|
virtual void OnWarning(u32 id) = 0;
|
||||||
virtual void OnError() = 0;
|
virtual void OnError() = 0;
|
||||||
|
virtual void OnLoop() = 0;
|
||||||
virtual void OnEOF() = 0;
|
virtual void OnEOF() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -86,6 +87,7 @@ private:
|
||||||
struct Frame {
|
struct Frame {
|
||||||
FrameBuffer buffer;
|
FrameBuffer buffer;
|
||||||
AvPlayerFrameInfoEx info;
|
AvPlayerFrameInfoEx info;
|
||||||
|
bool is_loop = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
class EventCV {
|
class EventCV {
|
||||||
|
@ -160,12 +162,13 @@ private:
|
||||||
void AudioDecoderThread(std::stop_token stop);
|
void AudioDecoderThread(std::stop_token stop);
|
||||||
|
|
||||||
bool HasRunningThreads() const;
|
bool HasRunningThreads() const;
|
||||||
|
void AddPacket(AVPacketPtr up_packet);
|
||||||
|
|
||||||
AVFramePtr ConvertAudioFrame(const AVFrame& frame);
|
AVFramePtr ConvertAudioFrame(const AVFrame& frame);
|
||||||
AVFramePtr ConvertVideoFrame(const AVFrame& frame);
|
AVFramePtr ConvertVideoFrame(const AVFrame& frame);
|
||||||
|
|
||||||
Frame PrepareAudioFrame(FrameBuffer buffer, const AVFrame& frame);
|
Frame PrepareAudioFrame(FrameBuffer buffer, const AVFrame& frame, bool is_loop);
|
||||||
Frame PrepareVideoFrame(FrameBuffer buffer, const AVFrame& frame);
|
Frame PrepareVideoFrame(FrameBuffer buffer, const AVFrame& frame, bool is_loop);
|
||||||
|
|
||||||
AvPlayerStateCallback& m_state;
|
AvPlayerStateCallback& m_state;
|
||||||
bool m_use_vdec2 = false;
|
bool m_use_vdec2 = false;
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include "core/libraries/avplayer/avplayer_error.h"
|
#include "core/libraries/avplayer/avplayer_error.h"
|
||||||
#include "core/libraries/avplayer/avplayer_source.h"
|
#include "core/libraries/avplayer/avplayer_source.h"
|
||||||
#include "core/libraries/avplayer/avplayer_state.h"
|
#include "core/libraries/avplayer/avplayer_state.h"
|
||||||
|
#include "core/libraries/kernel/time.h"
|
||||||
#include "core/tls.h"
|
#include "core/tls.h"
|
||||||
|
|
||||||
#include <magic_enum/magic_enum.hpp>
|
#include <magic_enum/magic_enum.hpp>
|
||||||
|
@ -154,15 +155,33 @@ bool AvPlayerState::AddSource(std::string_view path, AvPlayerSourceType source_t
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called inside GAME thread
|
// Called inside GAME thread
|
||||||
s32 AvPlayerState::GetStreamCount() {
|
u32 AvPlayerState::GetStreamCount() {
|
||||||
std::shared_lock lock(m_source_mutex);
|
|
||||||
if (m_up_source == nullptr) {
|
if (m_up_source == nullptr) {
|
||||||
LOG_ERROR(Lib_AvPlayer, "Could not get stream count. No source.");
|
return 0;
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
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
|
// Called inside GAME thread
|
||||||
bool AvPlayerState::GetStreamInfo(u32 stream_index, AvPlayerStreamInfo& info) {
|
bool AvPlayerState::GetStreamInfo(u32 stream_index, AvPlayerStreamInfo& info) {
|
||||||
std::shared_lock lock(m_source_mutex);
|
std::shared_lock lock(m_source_mutex);
|
||||||
|
@ -175,6 +194,12 @@ bool AvPlayerState::GetStreamInfo(u32 stream_index, AvPlayerStreamInfo& info) {
|
||||||
|
|
||||||
// Called inside GAME thread
|
// Called inside GAME thread
|
||||||
bool AvPlayerState::Start() {
|
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);
|
std::shared_lock lock(m_source_mutex);
|
||||||
if (m_up_source == nullptr || !m_up_source->Start()) {
|
if (m_up_source == nullptr || !m_up_source->Start()) {
|
||||||
LOG_ERROR(Lib_AvPlayer, "Could not start playback.");
|
LOG_ERROR(Lib_AvPlayer, "Could not start playback.");
|
||||||
|
@ -187,34 +212,56 @@ bool AvPlayerState::Start() {
|
||||||
|
|
||||||
// Called inside GAME thread
|
// Called inside GAME thread
|
||||||
bool AvPlayerState::Pause() {
|
bool AvPlayerState::Pause() {
|
||||||
std::shared_lock lock(m_source_mutex);
|
|
||||||
if (m_current_state == AvState::EndOfFile) {
|
if (m_current_state == AvState::EndOfFile) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (m_up_source == nullptr || m_current_state == AvState::Pause ||
|
if (m_current_state == AvState::Pause || m_current_state == AvState::Ready ||
|
||||||
m_current_state == AvState::Ready || m_current_state == AvState::Initial ||
|
m_current_state == AvState::Initial || m_current_state == AvState::Unknown ||
|
||||||
m_current_state == AvState::Unknown || m_current_state == AvState::AddingSource) {
|
m_current_state == AvState::AddingSource) {
|
||||||
LOG_ERROR(Lib_AvPlayer, "Could not pause playback.");
|
LOG_ERROR(Lib_AvPlayer, "Could not pause playback.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
std::shared_lock lock(m_source_mutex);
|
||||||
|
if (m_up_source == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
m_up_source->Pause();
|
m_up_source->Pause();
|
||||||
SetState(AvState::Pause);
|
SetState(AvState::Pause);
|
||||||
|
m_timer.Update();
|
||||||
OnPlaybackStateChanged(AvState::Pause);
|
OnPlaybackStateChanged(AvState::Pause);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called inside GAME thread
|
// Called inside GAME thread
|
||||||
bool AvPlayerState::Resume() {
|
bool AvPlayerState::Resume() {
|
||||||
std::shared_lock lock(m_source_mutex);
|
if (m_current_state == AvState::Initial || m_current_state == AvState::Stop ||
|
||||||
if (m_up_source == nullptr || m_current_state != AvState::Pause) {
|
m_current_state == AvState::Ready || m_current_state == AvState::AddingSource ||
|
||||||
LOG_ERROR(Lib_AvPlayer, "Could not resume playback.");
|
m_current_state == AvState::Error) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
m_up_source->Resume();
|
while (m_current_state == AvState::Starting || m_current_state == AvState::Jump ||
|
||||||
const auto state = m_previous_state.load();
|
m_current_state == AvState::TrickMode) {
|
||||||
SetState(state);
|
std::this_thread::sleep_for(std::chrono::microseconds(90));
|
||||||
OnPlaybackStateChanged(state);
|
}
|
||||||
return true;
|
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) {
|
void AvPlayerState::SetAvSyncMode(AvPlayerAvSyncMode sync_mode) {
|
||||||
|
@ -269,21 +316,31 @@ bool AvPlayerState::EnableStream(u32 stream_index) {
|
||||||
|
|
||||||
// Called inside GAME thread
|
// Called inside GAME thread
|
||||||
bool AvPlayerState::Stop() {
|
bool AvPlayerState::Stop() {
|
||||||
std::shared_lock lock(m_source_mutex);
|
if (m_current_state == AvState::Unknown || m_current_state == AvState::Stop) {
|
||||||
if (m_up_source == nullptr || m_current_state == AvState::Stop) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!m_up_source->Stop()) {
|
while (m_current_state == AvState::Jump || m_current_state == AvState::TrickMode) {
|
||||||
return false;
|
std::this_thread::sleep_for(std::chrono::microseconds(90));
|
||||||
}
|
}
|
||||||
if (!SetState(AvState::Stop)) {
|
if (!SetState(AvState::Stop)) {
|
||||||
return false;
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AvPlayerState::GetVideoData(AvPlayerFrameInfo& 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);
|
std::shared_lock lock(m_source_mutex);
|
||||||
if (m_up_source == nullptr) {
|
if (m_up_source == nullptr) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -292,6 +349,10 @@ bool AvPlayerState::GetVideoData(AvPlayerFrameInfo& video_info) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AvPlayerState::GetVideoData(AvPlayerFrameInfoEx& 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);
|
std::shared_lock lock(m_source_mutex);
|
||||||
if (m_up_source == nullptr) {
|
if (m_up_source == nullptr) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -350,6 +411,11 @@ void AvPlayerState::OnError() {
|
||||||
OnPlaybackStateChanged(AvState::Error);
|
OnPlaybackStateChanged(AvState::Error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AvPlayerState::OnLoop() {
|
||||||
|
auto id = ORBIS_AVPLAYER_ERROR_WAR_LOOPING_BACK;
|
||||||
|
EmitEvent(AvPlayerEvents::WarningId, &id);
|
||||||
|
}
|
||||||
|
|
||||||
void AvPlayerState::OnEOF() {
|
void AvPlayerState::OnEOF() {
|
||||||
SetState(AvState::EndOfFile);
|
SetState(AvState::EndOfFile);
|
||||||
}
|
}
|
||||||
|
@ -377,6 +443,10 @@ void AvPlayerState::OnPlaybackStateChanged(AvState state) {
|
||||||
EmitEvent(AvPlayerEvents::StateBuffering);
|
EmitEvent(AvPlayerEvents::StateBuffering);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case AvState::EndOfFile: {
|
||||||
|
EmitEvent(AvPlayerEvents::Initial);
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -467,6 +537,7 @@ void AvPlayerState::UpdateBufferingState() {
|
||||||
if (has_frames.value()) {
|
if (has_frames.value()) {
|
||||||
const auto state =
|
const auto state =
|
||||||
m_previous_state >= AvState::C0x0B ? m_previous_state.load() : AvState::Play;
|
m_previous_state >= AvState::C0x0B ? m_previous_state.load() : AvState::Play;
|
||||||
|
m_timer.UpdateVPts();
|
||||||
SetState(state);
|
SetState(state);
|
||||||
OnPlaybackStateChanged(state);
|
OnPlaybackStateChanged(state);
|
||||||
}
|
}
|
||||||
|
@ -488,7 +559,7 @@ bool AvPlayerState::IsStateTransitionValid(AvState state) {
|
||||||
switch (m_current_state.load()) {
|
switch (m_current_state.load()) {
|
||||||
case AvState::Stop:
|
case AvState::Stop:
|
||||||
case AvState::EndOfFile:
|
case AvState::EndOfFile:
|
||||||
// case AvState::C0x08:
|
case AvState::PauseOnEOF:
|
||||||
case AvState::Error:
|
case AvState::Error:
|
||||||
return false;
|
return false;
|
||||||
default:
|
default:
|
||||||
|
@ -499,7 +570,7 @@ bool AvPlayerState::IsStateTransitionValid(AvState state) {
|
||||||
switch (m_current_state.load()) {
|
switch (m_current_state.load()) {
|
||||||
case AvState::Stop:
|
case AvState::Stop:
|
||||||
case AvState::EndOfFile:
|
case AvState::EndOfFile:
|
||||||
// case AvState::C0x08:
|
case AvState::PauseOnEOF:
|
||||||
case AvState::Starting:
|
case AvState::Starting:
|
||||||
case AvState::Error:
|
case AvState::Error:
|
||||||
return false;
|
return false;
|
||||||
|
@ -511,7 +582,7 @@ bool AvPlayerState::IsStateTransitionValid(AvState state) {
|
||||||
switch (m_current_state.load()) {
|
switch (m_current_state.load()) {
|
||||||
case AvState::Stop:
|
case AvState::Stop:
|
||||||
case AvState::EndOfFile:
|
case AvState::EndOfFile:
|
||||||
// case AvState::C0x08:
|
case AvState::PauseOnEOF:
|
||||||
case AvState::TrickMode:
|
case AvState::TrickMode:
|
||||||
case AvState::Starting:
|
case AvState::Starting:
|
||||||
case AvState::Error:
|
case AvState::Error:
|
||||||
|
@ -524,7 +595,7 @@ bool AvPlayerState::IsStateTransitionValid(AvState state) {
|
||||||
switch (m_current_state.load()) {
|
switch (m_current_state.load()) {
|
||||||
case AvState::Stop:
|
case AvState::Stop:
|
||||||
case AvState::EndOfFile:
|
case AvState::EndOfFile:
|
||||||
// case AvState::C0x08:
|
case AvState::PauseOnEOF:
|
||||||
case AvState::Jump:
|
case AvState::Jump:
|
||||||
case AvState::Starting:
|
case AvState::Starting:
|
||||||
case AvState::Error:
|
case AvState::Error:
|
||||||
|
@ -538,7 +609,7 @@ bool AvPlayerState::IsStateTransitionValid(AvState state) {
|
||||||
case AvState::Stop:
|
case AvState::Stop:
|
||||||
case AvState::EndOfFile:
|
case AvState::EndOfFile:
|
||||||
case AvState::Pause:
|
case AvState::Pause:
|
||||||
// case AvState::C0x08:
|
case AvState::PauseOnEOF:
|
||||||
case AvState::Starting:
|
case AvState::Starting:
|
||||||
case AvState::Error:
|
case AvState::Error:
|
||||||
return false;
|
return false;
|
||||||
|
@ -551,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
|
} // namespace Libraries::AvPlayer
|
||||||
|
|
|
@ -16,6 +16,33 @@ namespace Libraries::AvPlayer {
|
||||||
class Stream;
|
class Stream;
|
||||||
class AvDecoder;
|
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 {
|
class AvPlayerState : public AvPlayerStateCallback {
|
||||||
public:
|
public:
|
||||||
AvPlayerState(const AvPlayerInitData& init_data);
|
AvPlayerState(const AvPlayerInitData& init_data);
|
||||||
|
@ -23,7 +50,7 @@ public:
|
||||||
|
|
||||||
void PostInit(const AvPlayerPostInitData& post_init_data);
|
void PostInit(const AvPlayerPostInitData& post_init_data);
|
||||||
bool AddSource(std::string_view filename, AvPlayerSourceType source_type);
|
bool AddSource(std::string_view filename, AvPlayerSourceType source_type);
|
||||||
s32 GetStreamCount();
|
u32 GetStreamCount();
|
||||||
bool GetStreamInfo(u32 stream_index, AvPlayerStreamInfo& info);
|
bool GetStreamInfo(u32 stream_index, AvPlayerStreamInfo& info);
|
||||||
bool EnableStream(u32 stream_index);
|
bool EnableStream(u32 stream_index);
|
||||||
bool Start();
|
bool Start();
|
||||||
|
@ -49,6 +76,7 @@ private:
|
||||||
AvPlayerAvSyncMode GetSyncMode() override;
|
AvPlayerAvSyncMode GetSyncMode() override;
|
||||||
void OnWarning(u32 id) override;
|
void OnWarning(u32 id) override;
|
||||||
void OnError() override;
|
void OnError() override;
|
||||||
|
void OnLoop() override;
|
||||||
void OnEOF() override;
|
void OnEOF() override;
|
||||||
|
|
||||||
void OnPlaybackStateChanged(AvState state);
|
void OnPlaybackStateChanged(AvState state);
|
||||||
|
@ -59,6 +87,7 @@ private:
|
||||||
|
|
||||||
void AvControllerThread(std::stop_token stop);
|
void AvControllerThread(std::stop_token stop);
|
||||||
|
|
||||||
|
void InitFilters();
|
||||||
void AddSourceEvent();
|
void AddSourceEvent();
|
||||||
void WarningEvent(s32 id);
|
void WarningEvent(s32 id);
|
||||||
|
|
||||||
|
@ -78,6 +107,7 @@ private:
|
||||||
|
|
||||||
std::atomic<AvState> m_current_state;
|
std::atomic<AvState> m_current_state;
|
||||||
std::atomic<AvState> m_previous_state;
|
std::atomic<AvState> m_previous_state;
|
||||||
|
AvPlayerTimer m_timer;
|
||||||
u32 m_thread_priority;
|
u32 m_thread_priority;
|
||||||
u32 m_thread_affinity;
|
u32 m_thread_affinity;
|
||||||
std::atomic_uint32_t m_some_event_result{};
|
std::atomic_uint32_t m_some_event_result{};
|
||||||
|
@ -87,6 +117,9 @@ private:
|
||||||
std::mutex m_event_handler_mutex{};
|
std::mutex m_event_handler_mutex{};
|
||||||
Kernel::Thread m_controller_thread{};
|
Kernel::Thread m_controller_thread{};
|
||||||
AvPlayerQueue<AvPlayerEvent> m_event_queue{};
|
AvPlayerQueue<AvPlayerEvent> m_event_queue{};
|
||||||
|
|
||||||
|
std::mutex m_stream_infos_mutex;
|
||||||
|
std::vector<AvPlayerStreamInfo> m_stream_infos;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Libraries::AvPlayer
|
} // namespace Libraries::AvPlayer
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
namespace Libraries::Kernel {
|
namespace Libraries::Kernel {
|
||||||
|
|
||||||
static u64 initial_ptc;
|
static u64 initial_ptc;
|
||||||
|
static u64 initial_unbiased_ptc;
|
||||||
static std::unique_ptr<Common::NativeClock> clock;
|
static std::unique_ptr<Common::NativeClock> clock;
|
||||||
|
|
||||||
u64 PS4_SYSV_ABI sceKernelGetTscFrequency() {
|
u64 PS4_SYSV_ABI sceKernelGetTscFrequency() {
|
||||||
|
@ -35,12 +36,11 @@ u64 PS4_SYSV_ABI sceKernelGetTscFrequency() {
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 PS4_SYSV_ABI sceKernelGetProcessTime() {
|
u64 PS4_SYSV_ABI sceKernelGetProcessTime() {
|
||||||
// TODO: this timer should support suspends, so initial ptc needs to be updated on wake up
|
return clock->GetTimeUS(clock->GetUnbiasedUptime() - initial_unbiased_ptc);
|
||||||
return clock->GetTimeUS(initial_ptc);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 PS4_SYSV_ABI sceKernelGetProcessTimeCounter() {
|
u64 PS4_SYSV_ABI sceKernelGetProcessTimeCounter() {
|
||||||
return clock->GetUptime() - initial_ptc;
|
return clock->GetUnbiasedUptime() - initial_unbiased_ptc;
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 PS4_SYSV_ABI sceKernelGetProcessTimeCounterFrequency() {
|
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) {
|
void RegisterTime(Core::Loader::SymbolsResolver* sym) {
|
||||||
clock = std::make_unique<Common::NativeClock>();
|
clock = std::make_unique<Common::NativeClock>();
|
||||||
initial_ptc = clock->GetUptime();
|
initial_ptc = clock->GetUptime();
|
||||||
|
initial_unbiased_ptc = clock->GetUnbiasedUptime();
|
||||||
|
|
||||||
// POSIX
|
// POSIX
|
||||||
LIB_FUNCTION("yS8U2TGCe1A", "libkernel", 1, "libkernel", 1, 1, posix_nanosleep);
|
LIB_FUNCTION("yS8U2TGCe1A", "libkernel", 1, "libkernel", 1, 1, posix_nanosleep);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue