diff --git a/src/common/logging/log.h b/src/common/logging/log.h index 5fa430348..de16cccf6 100644 --- a/src/common/logging/log.h +++ b/src/common/logging/log.h @@ -39,14 +39,10 @@ void FmtLogMessage(Class log_class, Level log_level, const char* filename, unsig Common::Log::FmtLogMessage(log_class, log_level, Common::Log::TrimSourcePath(__FILE__), \ __LINE__, __func__, __VA_ARGS__) -#ifdef _DEBUG #define LOG_TRACE(log_class, ...) \ Common::Log::FmtLogMessage(Common::Log::Class::log_class, Common::Log::Level::Trace, \ Common::Log::TrimSourcePath(__FILE__), __LINE__, __func__, \ __VA_ARGS__) -#else -#define LOG_TRACE(log_class, fmt, ...) (void(0)) -#endif #define LOG_DEBUG(log_class, ...) \ Common::Log::FmtLogMessage(Common::Log::Class::log_class, Common::Log::Level::Debug, \ diff --git a/src/common/native_clock.cpp b/src/common/native_clock.cpp index 0c05dbe84..1ea5a4bfe 100644 --- a/src/common/native_clock.cpp +++ b/src/common/native_clock.cpp @@ -5,28 +5,51 @@ #include "common/rdtsc.h" #include "common/uint128.h" +#include + +#ifdef _WIN64 +#include + +#define MM_SHARED_USER_DATA_VA 0x7ffe0000 +#define QpcBias ((ULONGLONG volatile*)(MM_SHARED_USER_DATA_VA + 0x3b0)) +#endif + namespace Common { NativeClock::NativeClock() : rdtsc_frequency{EstimateRDTSCFrequency()}, - ns_rdtsc_factor{GetFixedPoint64Factor(std::nano::den, rdtsc_frequency)}, - us_rdtsc_factor{GetFixedPoint64Factor(std::micro::den, rdtsc_frequency)}, - ms_rdtsc_factor{GetFixedPoint64Factor(std::milli::den, rdtsc_frequency)} {} + us_rdtsc_factor{GetFixedPoint64Factor(std::micro::den, rdtsc_frequency)} {} -u64 NativeClock::GetTimeNS(u64 base_ptc /*= 0*/) const { - return MultiplyHigh(GetUptime() - base_ptc, ns_rdtsc_factor); -} - -u64 NativeClock::GetTimeUS(u64 base_ptc /*= 0*/) const { - return MultiplyHigh(GetUptime() - base_ptc, us_rdtsc_factor); -} - -u64 NativeClock::GetTimeMS(u64 base_ptc /*= 0*/) const { - return MultiplyHigh(GetUptime() - base_ptc, ms_rdtsc_factor); +u64 NativeClock::GetTimeUS(u64 time) const { + return MultiplyHigh(time, us_rdtsc_factor); } u64 NativeClock::GetUptime() const { +#ifdef _WIN64 + LARGE_INTEGER counter; + QueryPerformanceCounter(&counter); + return counter.QuadPart; +#else return FencedRDTSC(); +#endif +} + +u64 NativeClock::GetUnbiasedUptime() const { +#ifdef _WIN64 + ULONGLONG bias = 0; + u64 qpc = 0; + do { + bias = *QpcBias; + qpc = GetUptime(); + } while (bias != *QpcBias); + return qpc - bias; +#else + return GetUptime(); +#endif +} + +u64 NativeClock::GetTscFrequency() const { + return rdtsc_frequency; } } // namespace Common diff --git a/src/common/native_clock.h b/src/common/native_clock.h index 1542c2f3a..2a030b911 100644 --- a/src/common/native_clock.h +++ b/src/common/native_clock.h @@ -3,7 +3,6 @@ #pragma once -#include #include "common/types.h" namespace Common { @@ -12,20 +11,15 @@ class NativeClock final { public: explicit NativeClock(); - u64 GetTscFrequency() const { - return rdtsc_frequency; - } + u64 GetTimeUS(u64 time) const; - u64 GetTimeNS(u64 base_ptc = 0) const; - u64 GetTimeUS(u64 base_ptc = 0) const; - u64 GetTimeMS(u64 base_ptc = 0) const; u64 GetUptime() const; + u64 GetUnbiasedUptime() const; + u64 GetTscFrequency() const; private: u64 rdtsc_frequency; - u64 ns_rdtsc_factor; u64 us_rdtsc_factor; - u64 ms_rdtsc_factor; }; } // namespace Common diff --git a/src/common/rdtsc.cpp b/src/common/rdtsc.cpp index 48d1b7a40..316d8388a 100644 --- a/src/common/rdtsc.cpp +++ b/src/common/rdtsc.cpp @@ -6,11 +6,12 @@ #include "common/uint128.h" #ifdef _WIN64 -#include +#include #endif namespace Common { +#ifndef _WIN64 static constexpr size_t SecondToNanoseconds = 1000000000ULL; template @@ -20,16 +21,7 @@ static u64 RoundToNearest(u64 value) { } static u64 GetTimeNs() { -#ifdef _WIN64 - // GetSystemTimePreciseAsFileTime returns the file time in 100ns units. - static constexpr u64 Multiplier = 100; - // Convert Windows epoch to Unix epoch. - static constexpr u64 WindowsEpochToUnixEpoch = 0x19DB1DED53E8000LL; - FILETIME filetime; - GetSystemTimePreciseAsFileTime(&filetime); - return Multiplier * ((static_cast(filetime.dwHighDateTime) << 32) + - static_cast(filetime.dwLowDateTime) - WindowsEpochToUnixEpoch); -#elif defined(__APPLE__) +#if defined(__APPLE__) return clock_gettime_nsec_np(CLOCK_REALTIME); #else timespec ts; @@ -37,8 +29,14 @@ static u64 GetTimeNs() { return ts.tv_sec * SecondToNanoseconds + ts.tv_nsec; #endif } +#endif u64 EstimateRDTSCFrequency() { +#ifdef _WIN64 + LARGE_INTEGER frequency; + QueryPerformanceFrequency(&frequency); + return frequency.QuadPart; +#else // Discard the first result measuring the rdtsc. FencedRDTSC(); std::this_thread::sleep_for(std::chrono::milliseconds{1}); @@ -55,6 +53,7 @@ u64 EstimateRDTSCFrequency() { const u64 tsc_diff = tsc_end - tsc_start; const u64 tsc_freq = MultiplyAndDivide64(tsc_diff, 1000000000ULL, end_time - start_time); return RoundToNearest<100'000>(tsc_freq); +#endif } } // namespace Common diff --git a/src/core/libraries/avplayer/avplayer.cpp b/src/core/libraries/avplayer/avplayer.cpp index 8d5a76199..f8298424d 100644 --- a/src/core/libraries/avplayer/avplayer.cpp +++ b/src/core/libraries/avplayer/avplayer.cpp @@ -42,11 +42,14 @@ s32 PS4_SYSV_ABI sceAvPlayerClose(AvPlayerHandle handle) { } u64 PS4_SYSV_ABI sceAvPlayerCurrentTime(AvPlayerHandle handle) { - LOG_TRACE(Lib_AvPlayer, "called"); + // LOG_TRACE(Lib_AvPlayer, "called"); if (handle == nullptr) { + LOG_TRACE(Lib_AvPlayer, "returning 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) { @@ -66,11 +69,18 @@ s32 PS4_SYSV_ABI sceAvPlayerEnableStream(AvPlayerHandle handle, u32 stream_id) { } 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) { + LOG_TRACE(Lib_AvPlayer, "returning 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, @@ -94,9 +104,20 @@ bool PS4_SYSV_ABI sceAvPlayerGetVideoDataEx(AvPlayerHandle handle, AvPlayerFrameInfoEx* video_info) { LOG_TRACE(Lib_AvPlayer, "called"); if (handle == nullptr || video_info == nullptr) { + LOG_TRACE(Lib_AvPlayer, "returning {}", 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) { @@ -143,11 +164,14 @@ s32 PS4_SYSV_ABI sceAvPlayerInitEx(const AvPlayerInitDataEx* p_data, AvPlayerHan } bool PS4_SYSV_ABI sceAvPlayerIsActive(AvPlayerHandle handle) { - LOG_TRACE(Lib_AvPlayer, "called"); + // LOG_TRACE(Lib_AvPlayer, "called"); if (handle == nullptr) { + LOG_TRACE(Lib_AvPlayer, "returning 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) { @@ -240,7 +264,7 @@ s32 PS4_SYSV_ABI sceAvPlayerStreamCount(AvPlayerHandle handle) { if (handle == nullptr) { return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; } - return handle->GetStreamCount(); + return static_cast(handle->GetStreamCount()); } s32 PS4_SYSV_ABI sceAvPlayerVprintf(const char* format, va_list args) { diff --git a/src/core/libraries/avplayer/avplayer.h b/src/core/libraries/avplayer/avplayer.h index 2312acaec..100f66108 100644 --- a/src/core/libraries/avplayer/avplayer.h +++ b/src/core/libraries/avplayer/avplayer.h @@ -171,6 +171,7 @@ struct AvPlayerFileReplacement { }; enum class AvPlayerEvents { + Initial = 0x00, StateStop = 0x01, StateReady = 0x02, StatePlay = 0x03, diff --git a/src/core/libraries/avplayer/avplayer_common.h b/src/core/libraries/avplayer/avplayer_common.h index 1e5715009..aedbbd9f9 100644 --- a/src/core/libraries/avplayer/avplayer_common.h +++ b/src/core/libraries/avplayer/avplayer_common.h @@ -24,12 +24,13 @@ enum class AvState { Stop, EndOfFile, Pause, - C0x08, + PauseOnEOF, Jump, TrickMode, C0x0B, Buffering, Starting, + C0x10, Error, }; diff --git a/src/core/libraries/avplayer/avplayer_impl.cpp b/src/core/libraries/avplayer/avplayer_impl.cpp index 138747da4..379fc9607 100644 --- a/src/core/libraries/avplayer/avplayer_impl.cpp +++ b/src/core/libraries/avplayer/avplayer_impl.cpp @@ -119,15 +119,8 @@ s32 AvPlayer::AddSourceEx(std::string_view path, AvPlayerSourceType source_type) return ORBIS_OK; } -s32 AvPlayer::GetStreamCount() { - if (m_state == nullptr) { - return ORBIS_AVPLAYER_ERROR_OPERATION_FAILED; - } - const auto res = m_state->GetStreamCount(); - if (AVPLAYER_IS_ERROR(res)) { - return ORBIS_AVPLAYER_ERROR_OPERATION_FAILED; - } - return res; +u32 AvPlayer::GetStreamCount() { + return m_state->GetStreamCount(); } s32 AvPlayer::GetStreamInfo(u32 stream_index, AvPlayerStreamInfo& info) { diff --git a/src/core/libraries/avplayer/avplayer_impl.h b/src/core/libraries/avplayer/avplayer_impl.h index 7e4aec43c..d951964c7 100644 --- a/src/core/libraries/avplayer/avplayer_impl.h +++ b/src/core/libraries/avplayer/avplayer_impl.h @@ -24,7 +24,7 @@ public: s32 PostInit(const AvPlayerPostInitData& data); s32 AddSource(std::string_view filename); s32 AddSourceEx(std::string_view path, AvPlayerSourceType source_type); - s32 GetStreamCount(); + u32 GetStreamCount(); s32 GetStreamInfo(u32 stream_index, AvPlayerStreamInfo& info); s32 EnableStream(u32 stream_index); s32 Start(); diff --git a/src/core/libraries/avplayer/avplayer_source.cpp b/src/core/libraries/avplayer/avplayer_source.cpp index 0bbb662be..30474303a 100644 --- a/src/core/libraries/avplayer/avplayer_source.cpp +++ b/src/core/libraries/avplayer/avplayer_source.cpp @@ -313,6 +313,7 @@ bool AvPlayerSource::GetVideoData(AvPlayerFrameInfoEx& video_info) { 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; } } @@ -323,6 +324,11 @@ bool AvPlayerSource::GetVideoData(AvPlayerFrameInfoEx& video_info) { m_video_buffers.Push(std::move(m_current_video_frame->buffer)); m_video_buffers_cv.Notify(); } + + if (frame->is_loop && m_is_looping) { + m_state.OnLoop(); + } + video_info = frame->info; m_current_video_frame = std::move(frame); return true; @@ -344,6 +350,10 @@ bool AvPlayerSource::GetAudioData(AvPlayerFrameInfo& audio_info) { m_audio_buffers_cv.Notify(); } + if (frame->is_loop && m_is_looping) { + m_state.OnLoop(); + } + audio_info = {}; audio_info.timestamp = frame->info.timestamp; audio_info.p_data = reinterpret_cast(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) { - using namespace std::chrono; Common::SetCurrentThreadName("shadPS4:AvDemuxer"); 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"); + 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..."); - 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; + 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); } } @@ -476,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; @@ -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); 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; } + 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(); @@ -628,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(); } } @@ -666,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); @@ -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()) { 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(); @@ -750,10 +778,13 @@ void AvPlayerSource::AudioDecoderThread(std::stop_token stop) { } if (up_frame->format != AV_SAMPLE_FMT_S16) { const auto pcm16_frame = ConvertAudioFrame(*up_frame); - m_audio_frames.Push(PrepareAudioFrame(std::move(buffer.value()), *pcm16_frame)); + m_audio_frames.Push( + PrepareAudioFrame(std::move(buffer.value()), *pcm16_frame, is_loop)); } else { - m_audio_frames.Push(PrepareAudioFrame(std::move(buffer.value()), *up_frame)); + m_audio_frames.Push( + PrepareAudioFrame(std::move(buffer.value()), *up_frame, is_loop)); } + is_loop = false; m_audio_frames_cv.Notify(); } } diff --git a/src/core/libraries/avplayer/avplayer_source.h b/src/core/libraries/avplayer/avplayer_source.h index cb0cef8f6..5ce18b922 100644 --- a/src/core/libraries/avplayer/avplayer_source.h +++ b/src/core/libraries/avplayer/avplayer_source.h @@ -34,6 +34,7 @@ public: virtual void OnWarning(u32 id) = 0; virtual void OnError() = 0; + virtual void OnLoop() = 0; virtual void OnEOF() = 0; }; @@ -86,6 +87,7 @@ private: struct Frame { FrameBuffer buffer; AvPlayerFrameInfoEx info; + bool is_loop = false; }; class EventCV { @@ -160,12 +162,13 @@ 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; diff --git a/src/core/libraries/avplayer/avplayer_state.cpp b/src/core/libraries/avplayer/avplayer_state.cpp index 38811d853..b8b04e758 100644 --- a/src/core/libraries/avplayer/avplayer_state.cpp +++ b/src/core/libraries/avplayer/avplayer_state.cpp @@ -6,6 +6,7 @@ #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 @@ -154,15 +155,33 @@ bool AvPlayerState::AddSource(std::string_view path, AvPlayerSourceType source_t } // Called inside GAME thread -s32 AvPlayerState::GetStreamCount() { - std::shared_lock lock(m_source_mutex); +u32 AvPlayerState::GetStreamCount() { if (m_up_source == nullptr) { - LOG_ERROR(Lib_AvPlayer, "Could not get stream count. No source."); - return -1; + return 0; } - return m_up_source->GetStreamCount(); + auto count = m_up_source->GetStreamCount(); + if (!m_stream_infos.empty()) { + return count; + } + if (count > 0) { + m_stream_infos.reserve(count); + for (u32 i = 0; i < count; ++i) { + AvPlayerStreamInfo info; + if (!m_up_source->GetStreamInfo(i, info)) { + std::lock_guard guard(m_stream_infos_mutex); + m_stream_infos.clear(); + return 0; + } + std::lock_guard guard(m_stream_infos_mutex); + m_stream_infos.emplace_back(info); + } + } + InitFilters(); + return count; } +void AvPlayerState::InitFilters() {} + // Called inside GAME thread bool AvPlayerState::GetStreamInfo(u32 stream_index, AvPlayerStreamInfo& info) { std::shared_lock lock(m_source_mutex); @@ -175,6 +194,12 @@ bool AvPlayerState::GetStreamInfo(u32 stream_index, AvPlayerStreamInfo& 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."); @@ -187,34 +212,56 @@ bool AvPlayerState::Start() { // Called inside GAME thread bool AvPlayerState::Pause() { - std::shared_lock lock(m_source_mutex); if (m_current_state == AvState::EndOfFile) { return true; } - if (m_up_source == nullptr || 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) { + 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() { - std::shared_lock lock(m_source_mutex); - if (m_up_source == nullptr || m_current_state != AvState::Pause) { - LOG_ERROR(Lib_AvPlayer, "Could not resume playback."); + 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; } - m_up_source->Resume(); - const auto state = m_previous_state.load(); - SetState(state); - OnPlaybackStateChanged(state); - return true; + 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) { @@ -269,21 +316,31 @@ bool AvPlayerState::EnableStream(u32 stream_index) { // Called inside GAME thread bool AvPlayerState::Stop() { - std::shared_lock lock(m_source_mutex); - if (m_up_source == nullptr || m_current_state == AvState::Stop) { + if (m_current_state == AvState::Unknown || m_current_state == AvState::Stop) { return false; } - if (!m_up_source->Stop()) { - return false; + while (m_current_state == AvState::Jump || m_current_state == AvState::TrickMode) { + std::this_thread::sleep_for(std::chrono::microseconds(90)); } if (!SetState(AvState::Stop)) { return false; } - OnPlaybackStateChanged(AvState::Stop); + EmitEvent(AvPlayerEvents::StateStop); + { + std::shared_lock lock(m_source_mutex); + if (m_up_source == nullptr || !m_up_source->Stop()) { + return false; + } + } + m_timer.Reset(); return true; } bool AvPlayerState::GetVideoData(AvPlayerFrameInfo& video_info) { + if (m_current_state == AvState::Play || m_current_state == AvState::EndOfFile || + m_current_state == AvState::C0x10) { + return false; + } std::shared_lock lock(m_source_mutex); if (m_up_source == nullptr) { return false; @@ -292,6 +349,10 @@ bool AvPlayerState::GetVideoData(AvPlayerFrameInfo& video_info) { } bool AvPlayerState::GetVideoData(AvPlayerFrameInfoEx& video_info) { + if (m_current_state == AvState::Play || m_current_state == AvState::EndOfFile || + m_current_state == AvState::C0x10) { + return false; + } std::shared_lock lock(m_source_mutex); if (m_up_source == nullptr) { return false; @@ -350,6 +411,11 @@ void AvPlayerState::OnError() { OnPlaybackStateChanged(AvState::Error); } +void AvPlayerState::OnLoop() { + auto id = ORBIS_AVPLAYER_ERROR_WAR_LOOPING_BACK; + EmitEvent(AvPlayerEvents::WarningId, &id); +} + void AvPlayerState::OnEOF() { SetState(AvState::EndOfFile); } @@ -377,6 +443,10 @@ void AvPlayerState::OnPlaybackStateChanged(AvState state) { EmitEvent(AvPlayerEvents::StateBuffering); break; } + case AvState::EndOfFile: { + EmitEvent(AvPlayerEvents::Initial); + break; + } default: break; } @@ -467,6 +537,7 @@ void AvPlayerState::UpdateBufferingState() { if (has_frames.value()) { const auto state = m_previous_state >= AvState::C0x0B ? m_previous_state.load() : AvState::Play; + m_timer.UpdateVPts(); SetState(state); OnPlaybackStateChanged(state); } @@ -488,7 +559,7 @@ bool AvPlayerState::IsStateTransitionValid(AvState state) { switch (m_current_state.load()) { case AvState::Stop: case AvState::EndOfFile: - // case AvState::C0x08: + case AvState::PauseOnEOF: case AvState::Error: return false; default: @@ -499,7 +570,7 @@ bool AvPlayerState::IsStateTransitionValid(AvState state) { switch (m_current_state.load()) { case AvState::Stop: case AvState::EndOfFile: - // case AvState::C0x08: + case AvState::PauseOnEOF: case AvState::Starting: case AvState::Error: return false; @@ -511,7 +582,7 @@ bool AvPlayerState::IsStateTransitionValid(AvState state) { switch (m_current_state.load()) { case AvState::Stop: case AvState::EndOfFile: - // case AvState::C0x08: + case AvState::PauseOnEOF: case AvState::TrickMode: case AvState::Starting: case AvState::Error: @@ -524,7 +595,7 @@ bool AvPlayerState::IsStateTransitionValid(AvState state) { switch (m_current_state.load()) { case AvState::Stop: case AvState::EndOfFile: - // case AvState::C0x08: + case AvState::PauseOnEOF: case AvState::Jump: case AvState::Starting: case AvState::Error: @@ -538,7 +609,7 @@ bool AvPlayerState::IsStateTransitionValid(AvState state) { case AvState::Stop: case AvState::EndOfFile: case AvState::Pause: - // case AvState::C0x08: + case AvState::PauseOnEOF: case AvState::Starting: case AvState::Error: return false; @@ -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 diff --git a/src/core/libraries/avplayer/avplayer_state.h b/src/core/libraries/avplayer/avplayer_state.h index c67855c89..4bab2b414 100644 --- a/src/core/libraries/avplayer/avplayer_state.h +++ b/src/core/libraries/avplayer/avplayer_state.h @@ -16,6 +16,33 @@ namespace Libraries::AvPlayer { class Stream; class AvDecoder; +class AvPlayerTimer { +public: + enum class State { + Ok, + WaitForSync, + VideoAhead, + VideoBehind, + Error, + }; + + void Update(); + void UpdateAPts(u64 timestamp); + void UpdateVHintPts(u64 hint); + void UpdateVPts(); + void Reset(); + State GetState(AvPlayerAvSyncMode sync_mode); + +private: + static u64 Now(); + + std::atomic m_ts_diff = -1; + std::atomic m_v_pts = -1; + std::atomic m_v_hint_pts = -1; + std::atomic m_last_update_pts = 0; + std::atomic m_a_pts = 0; +}; + class AvPlayerState : public AvPlayerStateCallback { public: AvPlayerState(const AvPlayerInitData& init_data); @@ -23,7 +50,7 @@ public: void PostInit(const AvPlayerPostInitData& post_init_data); bool AddSource(std::string_view filename, AvPlayerSourceType source_type); - s32 GetStreamCount(); + u32 GetStreamCount(); bool GetStreamInfo(u32 stream_index, AvPlayerStreamInfo& info); bool EnableStream(u32 stream_index); bool Start(); @@ -49,6 +76,7 @@ private: AvPlayerAvSyncMode GetSyncMode() override; void OnWarning(u32 id) override; void OnError() override; + void OnLoop() override; void OnEOF() override; void OnPlaybackStateChanged(AvState state); @@ -59,6 +87,7 @@ private: void AvControllerThread(std::stop_token stop); + void InitFilters(); void AddSourceEvent(); void WarningEvent(s32 id); @@ -78,6 +107,7 @@ private: std::atomic m_current_state; std::atomic m_previous_state; + AvPlayerTimer m_timer; u32 m_thread_priority; u32 m_thread_affinity; std::atomic_uint32_t m_some_event_result{}; @@ -87,6 +117,9 @@ private: std::mutex m_event_handler_mutex{}; Kernel::Thread m_controller_thread{}; AvPlayerQueue m_event_queue{}; + + std::mutex m_stream_infos_mutex; + std::vector m_stream_infos; }; } // namespace Libraries::AvPlayer diff --git a/src/core/libraries/kernel/time.cpp b/src/core/libraries/kernel/time.cpp index 2fe74d0a3..ae3e05ae4 100644 --- a/src/core/libraries/kernel/time.cpp +++ b/src/core/libraries/kernel/time.cpp @@ -28,6 +28,7 @@ namespace Libraries::Kernel { static u64 initial_ptc; +static u64 initial_unbiased_ptc; static std::unique_ptr clock; u64 PS4_SYSV_ABI sceKernelGetTscFrequency() { @@ -35,12 +36,11 @@ u64 PS4_SYSV_ABI sceKernelGetTscFrequency() { } u64 PS4_SYSV_ABI sceKernelGetProcessTime() { - // TODO: this timer should support suspends, so initial ptc needs to be updated on wake up - return clock->GetTimeUS(initial_ptc); + return clock->GetTimeUS(clock->GetUnbiasedUptime() - initial_unbiased_ptc); } u64 PS4_SYSV_ABI sceKernelGetProcessTimeCounter() { - return clock->GetUptime() - initial_ptc; + return clock->GetUnbiasedUptime() - initial_unbiased_ptc; } u64 PS4_SYSV_ABI sceKernelGetProcessTimeCounterFrequency() { @@ -511,6 +511,7 @@ s32 PS4_SYSV_ABI sceKernelConvertUtcToLocaltime(time_t time, time_t* local_time, void RegisterTime(Core::Loader::SymbolsResolver* sym) { clock = std::make_unique(); initial_ptc = clock->GetUptime(); + initial_unbiased_ptc = clock->GetUnbiasedUptime(); // POSIX LIB_FUNCTION("yS8U2TGCe1A", "libkernel", 1, "libkernel", 1, 1, posix_nanosleep);