diff --git a/src/core/libraries/avplayer/avplayer.cpp b/src/core/libraries/avplayer/avplayer.cpp index c58bf42e1..73d0b13c0 100644 --- a/src/core/libraries/avplayer/avplayer.cpp +++ b/src/core/libraries/avplayer/avplayer.cpp @@ -159,11 +159,11 @@ s32 PS4_SYSV_ABI sceAvPlayerJumpToTime(AvPlayerHandle handle, uint64_t time) { } s32 PS4_SYSV_ABI sceAvPlayerPause(AvPlayerHandle handle) { - LOG_ERROR(Lib_AvPlayer, "(STUBBED) called"); + LOG_TRACE(Lib_AvPlayer, "(STUBBED) called"); if (handle == nullptr) { return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; } - return ORBIS_OK; + return handle->Pause(); } s32 PS4_SYSV_ABI sceAvPlayerPostInit(AvPlayerHandle handle, AvPlayerPostInitData* data) { @@ -180,11 +180,11 @@ s32 PS4_SYSV_ABI sceAvPlayerPrintf(const char* format, ...) { } s32 PS4_SYSV_ABI sceAvPlayerResume(AvPlayerHandle handle) { - LOG_ERROR(Lib_AvPlayer, "(STUBBED) called"); + LOG_TRACE(Lib_AvPlayer, "(STUBBED) called"); if (handle == nullptr) { return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; } - return ORBIS_OK; + return handle->Resume(); } s32 PS4_SYSV_ABI sceAvPlayerSetAvSyncMode(AvPlayerHandle handle, AvPlayerAvSyncMode sync_mode) { diff --git a/src/core/libraries/avplayer/avplayer_common.h b/src/core/libraries/avplayer/avplayer_common.h index eddd10d5e..2c5f4ec13 100644 --- a/src/core/libraries/avplayer/avplayer_common.h +++ b/src/core/libraries/avplayer/avplayer_common.h @@ -16,6 +16,7 @@ namespace Libraries::AvPlayer { enum class AvState { + Unknown, Initial, AddingSource, Ready, diff --git a/src/core/libraries/avplayer/avplayer_impl.cpp b/src/core/libraries/avplayer/avplayer_impl.cpp index 411908ae3..6d7c8cc3e 100644 --- a/src/core/libraries/avplayer/avplayer_impl.cpp +++ b/src/core/libraries/avplayer/avplayer_impl.cpp @@ -148,7 +148,21 @@ 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; +} + +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; diff --git a/src/core/libraries/avplayer/avplayer_impl.h b/src/core/libraries/avplayer/avplayer_impl.h index 7033e191c..a2812ee0b 100644 --- a/src/core/libraries/avplayer/avplayer_impl.h +++ b/src/core/libraries/avplayer/avplayer_impl.h @@ -28,6 +28,8 @@ public: s32 GetStreamInfo(u32 stream_index, AvPlayerStreamInfo& info); s32 EnableStream(u32 stream_index); s32 Start(); + s32 Pause(); + s32 Resume(); bool GetAudioData(AvPlayerFrameInfo& audio_info); bool GetVideoData(AvPlayerFrameInfo& video_info); bool GetVideoData(AvPlayerFrameInfoEx& video_info); diff --git a/src/core/libraries/avplayer/avplayer_source.cpp b/src/core/libraries/avplayer/avplayer_source.cpp index cc90a1a49..651353a11 100644 --- a/src/core/libraries/avplayer/avplayer_source.cpp +++ b/src/core/libraries/avplayer/avplayer_source.cpp @@ -280,6 +280,16 @@ bool AvPlayerSource::Stop() { return true; } +void AvPlayerSource::Pause() { + m_last_paused_time = std::chrono::high_resolution_clock::now(); + m_is_paused = true; +} + +void AvPlayerSource::Resume() { + m_stalled_time += std::chrono::high_resolution_clock::now() - m_last_paused_time; + m_is_paused = false; +} + bool AvPlayerSource::GetVideoData(AvPlayerFrameInfo& video_info) { if (!IsActive()) { return false; @@ -299,7 +309,7 @@ bool AvPlayerSource::GetVideoData(AvPlayerFrameInfo& video_info) { } bool AvPlayerSource::GetVideoData(AvPlayerFrameInfoEx& video_info) { - if (!IsActive()) { + if (!IsActive() || m_is_paused) { return false; } @@ -313,8 +323,7 @@ bool AvPlayerSource::GetVideoData(AvPlayerFrameInfoEx& video_info) { { using namespace std::chrono; - auto elapsed_time = - duration_cast(high_resolution_clock::now() - m_start_time).count(); + auto elapsed_time = CurrentTime(); if (elapsed_time < frame->info.timestamp) { if (m_stop_cv.WaitFor(milliseconds(frame->info.timestamp - elapsed_time), [&] { return elapsed_time >= frame->info.timestamp; })) { @@ -334,7 +343,7 @@ bool AvPlayerSource::GetVideoData(AvPlayerFrameInfoEx& video_info) { } bool AvPlayerSource::GetAudioData(AvPlayerFrameInfo& audio_info) { - if (!IsActive()) { + if (!IsActive() || m_is_paused) { return false; } @@ -348,8 +357,7 @@ bool AvPlayerSource::GetAudioData(AvPlayerFrameInfo& audio_info) { { using namespace std::chrono; - auto elapsed_time = - duration_cast(high_resolution_clock::now() - m_start_time).count(); + auto elapsed_time = CurrentTime(); if (elapsed_time < frame->info.timestamp) { if (m_stop_cv.WaitFor(milliseconds(frame->info.timestamp - elapsed_time), [&] { return elapsed_time >= frame->info.timestamp; })) { @@ -379,7 +387,8 @@ u64 AvPlayerSource::CurrentTime() { return 0; } using namespace std::chrono; - return duration_cast(high_resolution_clock::now() - m_start_time).count(); + return duration_cast(high_resolution_clock::now() - m_start_time - m_stalled_time) + .count(); } bool AvPlayerSource::IsActive() { diff --git a/src/core/libraries/avplayer/avplayer_source.h b/src/core/libraries/avplayer/avplayer_source.h index 6d0590545..67e7a639a 100644 --- a/src/core/libraries/avplayer/avplayer_source.h +++ b/src/core/libraries/avplayer/avplayer_source.h @@ -130,6 +130,8 @@ public: std::optional HasFrames(u32 num_frames); bool Start(); bool Stop(); + void Pause(); + void Resume(); bool GetAudioData(AvPlayerFrameInfo& audio_info); bool GetVideoData(AvPlayerFrameInfo& video_info); bool GetVideoData(AvPlayerFrameInfoEx& video_info); @@ -170,6 +172,7 @@ private: u32 m_num_output_video_framebuffers{}; std::atomic_bool m_is_looping = false; + std::atomic_bool m_is_paused = false; std::atomic_bool m_is_eof = false; std::unique_ptr m_up_data_streamer; @@ -211,6 +214,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_last_paused_time{}; + std::chrono::high_resolution_clock::duration m_stalled_time{}; }; } // namespace Libraries::AvPlayer diff --git a/src/core/libraries/avplayer/avplayer_state.cpp b/src/core/libraries/avplayer/avplayer_state.cpp index e6885ea2d..aa66d690e 100644 --- a/src/core/libraries/avplayer/avplayer_state.cpp +++ b/src/core/libraries/avplayer/avplayer_state.cpp @@ -185,6 +185,38 @@ bool AvPlayerState::Start() { return true; } +// 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) { + LOG_ERROR(Lib_AvPlayer, "Could not pause playback."); + return false; + } + m_up_source->Pause(); + SetState(AvState::Pause); + 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."); + return false; + } + m_up_source->Resume(); + const auto state = m_previous_state.load(); + SetState(state); + OnPlaybackStateChanged(state); + return true; +} + void AvPlayerState::AvControllerThread(std::stop_token stop) { using std::chrono::milliseconds; Common::SetCurrentThreadName("shadPS4:AvController"); diff --git a/src/core/libraries/avplayer/avplayer_state.h b/src/core/libraries/avplayer/avplayer_state.h index 139c5aa8b..93f0e0a89 100644 --- a/src/core/libraries/avplayer/avplayer_state.h +++ b/src/core/libraries/avplayer/avplayer_state.h @@ -28,6 +28,8 @@ public: bool EnableStream(u32 stream_index); bool Start(); bool Stop(); + bool Pause(); + bool Resume(); bool GetAudioData(AvPlayerFrameInfo& audio_info); bool GetVideoData(AvPlayerFrameInfo& video_info); bool GetVideoData(AvPlayerFrameInfoEx& video_info);