Fixed some sound and threading issues.
Details: * Switched SDL audio mutex to RW lock. This fixes games that continiously call SetVolume in a different thread (like Ghostbusters) * Added contition to buffer audio packets independent of video packets. This fixes choppy audio across many games. * Increased the number of audio frame buffers from 2 to 4. Just in case. * Migrated to std::jthread and std::mutex from pthreads. * Fixed a race condition with joins on avplayer close that caused a crash.
This commit is contained in:
parent
e33ff10212
commit
0d6e8e227a
|
@ -13,7 +13,7 @@ namespace Audio {
|
||||||
int SDLAudio::AudioOutOpen(int type, u32 samples_num, u32 freq,
|
int SDLAudio::AudioOutOpen(int type, u32 samples_num, u32 freq,
|
||||||
Libraries::AudioOut::OrbisAudioOutParamFormat format) {
|
Libraries::AudioOut::OrbisAudioOutParamFormat format) {
|
||||||
using Libraries::AudioOut::OrbisAudioOutParamFormat;
|
using Libraries::AudioOut::OrbisAudioOutParamFormat;
|
||||||
std::scoped_lock lock{m_mutex};
|
std::unique_lock lock{m_mutex};
|
||||||
for (int id = 0; id < portsOut.size(); id++) {
|
for (int id = 0; id < portsOut.size(); id++) {
|
||||||
auto& port = portsOut[id];
|
auto& port = portsOut[id];
|
||||||
if (!port.isOpen) {
|
if (!port.isOpen) {
|
||||||
|
@ -88,7 +88,7 @@ int SDLAudio::AudioOutOpen(int type, u32 samples_num, u32 freq,
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 SDLAudio::AudioOutOutput(s32 handle, const void* ptr) {
|
s32 SDLAudio::AudioOutOutput(s32 handle, const void* ptr) {
|
||||||
std::scoped_lock lock{m_mutex};
|
std::shared_lock lock{m_mutex};
|
||||||
auto& port = portsOut[handle - 1];
|
auto& port = portsOut[handle - 1];
|
||||||
if (!port.isOpen) {
|
if (!port.isOpen) {
|
||||||
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
|
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
|
||||||
|
@ -109,7 +109,7 @@ s32 SDLAudio::AudioOutOutput(s32 handle, const void* ptr) {
|
||||||
|
|
||||||
bool SDLAudio::AudioOutSetVolume(s32 handle, s32 bitflag, s32* volume) {
|
bool SDLAudio::AudioOutSetVolume(s32 handle, s32 bitflag, s32* volume) {
|
||||||
using Libraries::AudioOut::OrbisAudioOutParamFormat;
|
using Libraries::AudioOut::OrbisAudioOutParamFormat;
|
||||||
std::scoped_lock lock{m_mutex};
|
std::shared_lock lock{m_mutex};
|
||||||
auto& port = portsOut[handle - 1];
|
auto& port = portsOut[handle - 1];
|
||||||
if (!port.isOpen) {
|
if (!port.isOpen) {
|
||||||
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
|
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
|
||||||
|
@ -147,7 +147,7 @@ bool SDLAudio::AudioOutSetVolume(s32 handle, s32 bitflag, s32* volume) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SDLAudio::AudioOutGetStatus(s32 handle, int* type, int* channels_num) {
|
bool SDLAudio::AudioOutGetStatus(s32 handle, int* type, int* channels_num) {
|
||||||
std::scoped_lock lock{m_mutex};
|
std::shared_lock lock{m_mutex};
|
||||||
auto& port = portsOut[handle - 1];
|
auto& port = portsOut[handle - 1];
|
||||||
*type = port.type;
|
*type = port.type;
|
||||||
*channels_num = port.channels_num;
|
*channels_num = port.channels_num;
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <mutex>
|
#include <shared_mutex>
|
||||||
#include <SDL3/SDL_audio.h>
|
#include <SDL3/SDL_audio.h>
|
||||||
#include "core/libraries/audio/audioout.h"
|
#include "core/libraries/audio/audioout.h"
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ private:
|
||||||
int volume[8] = {};
|
int volume[8] = {};
|
||||||
SDL_AudioStream* stream = nullptr;
|
SDL_AudioStream* stream = nullptr;
|
||||||
};
|
};
|
||||||
std::mutex m_mutex;
|
std::shared_mutex m_mutex;
|
||||||
std::array<PortOut, 22> portsOut; // main up to 8 ports , BGM 1 port , voice up to 4 ports ,
|
std::array<PortOut, 22> portsOut; // main up to 8 ports , BGM 1 port , voice up to 4 ports ,
|
||||||
// personal up to 4 ports , padspk up to 5 ports , aux 1 port
|
// personal up to 4 ports , padspk up to 5 ports , aux 1 port
|
||||||
};
|
};
|
||||||
|
|
|
@ -234,7 +234,7 @@ int PS4_SYSV_ABI sceAudioOutGetSystemState() {
|
||||||
}
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI sceAudioOutInit() {
|
int PS4_SYSV_ABI sceAudioOutInit() {
|
||||||
LOG_INFO(Lib_AudioOut, "called");
|
LOG_TRACE(Lib_AudioOut, "called");
|
||||||
if (audio != nullptr) {
|
if (audio != nullptr) {
|
||||||
return ORBIS_AUDIO_OUT_ERROR_ALREADY_INIT;
|
return ORBIS_AUDIO_OUT_ERROR_ALREADY_INIT;
|
||||||
}
|
}
|
||||||
|
@ -323,12 +323,10 @@ int PS4_SYSV_ABI sceAudioOutOpenEx() {
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 PS4_SYSV_ABI sceAudioOutOutput(s32 handle, const void* ptr) {
|
s32 PS4_SYSV_ABI sceAudioOutOutput(s32 handle, const void* ptr) {
|
||||||
LOG_TRACE(Lib_AudioOut, "called");
|
|
||||||
return audio->AudioOutOutput(handle, ptr);
|
return audio->AudioOutOutput(handle, ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI sceAudioOutOutputs(OrbisAudioOutOutputParam* param, u32 num) {
|
int PS4_SYSV_ABI sceAudioOutOutputs(OrbisAudioOutOutputParam* param, u32 num) {
|
||||||
LOG_TRACE(Lib_AudioOut, "called");
|
|
||||||
for (u32 i = 0; i < num; i++) {
|
for (u32 i = 0; i < num; i++) {
|
||||||
if (auto err = audio->AudioOutOutput(param[i].handle, param[i].ptr); err != 0)
|
if (auto err = audio->AudioOutOutput(param[i].handle, param[i].ptr); err != 0)
|
||||||
return err;
|
return err;
|
||||||
|
|
|
@ -11,8 +11,6 @@
|
||||||
|
|
||||||
#include <algorithm> // std::max, std::min
|
#include <algorithm> // std::max, std::min
|
||||||
|
|
||||||
#include <stdarg.h> // va_list
|
|
||||||
|
|
||||||
namespace Libraries::AvPlayer {
|
namespace Libraries::AvPlayer {
|
||||||
|
|
||||||
using namespace Kernel;
|
using namespace Kernel;
|
||||||
|
@ -140,17 +138,7 @@ SceAvPlayerHandle PS4_SYSV_ABI sceAvPlayerInit(SceAvPlayerInitData* data) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
ThreadPriorities priorities{};
|
return new AvPlayer(*data);
|
||||||
const u32 base_priority = data->base_priority != 0 ? data->base_priority : 700;
|
|
||||||
priorities.video_decoder_priority = GetPriority(base_priority, 5);
|
|
||||||
priorities.audio_decoder_priority = GetPriority(base_priority, 6);
|
|
||||||
priorities.demuxer_priority = GetPriority(base_priority, 9);
|
|
||||||
priorities.controller_priority = GetPriority(base_priority, 2);
|
|
||||||
// priorities.http_streaming_priority = GetPriority(base_priority, 10);
|
|
||||||
// priorities.file_streaming_priority = GetPriority(priorities.http_streaming_priority, 15);
|
|
||||||
// priorities.maxPriority = priorities.http_streaming_priority;
|
|
||||||
|
|
||||||
return new AvPlayer(*data, priorities);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 PS4_SYSV_ABI sceAvPlayerInitEx(const SceAvPlayerInitDataEx* p_data,
|
s32 PS4_SYSV_ABI sceAvPlayerInitEx(const SceAvPlayerInitDataEx* p_data,
|
||||||
|
@ -176,56 +164,7 @@ s32 PS4_SYSV_ABI sceAvPlayerInitEx(const SceAvPlayerInitDataEx* p_data,
|
||||||
data.num_output_video_framebuffers = p_data->num_output_video_framebuffers;
|
data.num_output_video_framebuffers = p_data->num_output_video_framebuffers;
|
||||||
data.auto_start = p_data->auto_start;
|
data.auto_start = p_data->auto_start;
|
||||||
|
|
||||||
ThreadPriorities priorities{};
|
*p_player = new AvPlayer(data);
|
||||||
s32 base_priority = 0;
|
|
||||||
const auto res = scePthreadGetprio(scePthreadSelf(), &base_priority);
|
|
||||||
if (res != 0 || base_priority == 0) {
|
|
||||||
base_priority = 700;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (p_data->video_decoder_priority != 0) {
|
|
||||||
priorities.video_decoder_priority = p_data->video_decoder_priority;
|
|
||||||
} else {
|
|
||||||
priorities.video_decoder_priority = GetPriority(base_priority, 5);
|
|
||||||
}
|
|
||||||
priorities.video_decoder_affinity = p_data->video_decoder_affinity;
|
|
||||||
|
|
||||||
if (p_data->audio_decoder_priority != 0) {
|
|
||||||
priorities.audio_decoder_priority = p_data->audio_decoder_priority;
|
|
||||||
} else {
|
|
||||||
priorities.audio_decoder_priority = GetPriority(base_priority, 6);
|
|
||||||
}
|
|
||||||
priorities.audio_decoder_affinity = p_data->audio_decoder_affinity;
|
|
||||||
|
|
||||||
if (p_data->controller_priority != 0) {
|
|
||||||
priorities.controller_priority = p_data->controller_priority;
|
|
||||||
} else {
|
|
||||||
priorities.controller_priority = GetPriority(base_priority, 2);
|
|
||||||
}
|
|
||||||
priorities.controller_affinity = p_data->controller_affinity;
|
|
||||||
|
|
||||||
if (p_data->demuxer_priority != 0) {
|
|
||||||
priorities.demuxer_priority = p_data->demuxer_priority;
|
|
||||||
} else {
|
|
||||||
priorities.demuxer_priority = GetPriority(base_priority, 9);
|
|
||||||
}
|
|
||||||
priorities.demuxer_affinity = p_data->demuxer_affinity;
|
|
||||||
|
|
||||||
// if (p_data->http_streaming_priority != 0) {
|
|
||||||
// priorities.http_streaming_priority = p_data->http_streaming_priority;
|
|
||||||
// } else {
|
|
||||||
// priorities.http_streaming_priority = GetPriority(base_priority, 10);
|
|
||||||
// }
|
|
||||||
// priorities.http_streaming_affinity = p_data->http_streaming_affinity;
|
|
||||||
|
|
||||||
// if (p_data->file_streaming_priority != 0) {
|
|
||||||
// priorities.file_streaming_priority = p_data->file_streaming_priority;
|
|
||||||
// } else {
|
|
||||||
// priorities.file_streaming_priority = GetPriority(base_priority, 15);
|
|
||||||
// }
|
|
||||||
// priorities.http_streaming_affinity = p_data->http_streaming_affinity;
|
|
||||||
|
|
||||||
*p_player = new AvPlayer(data, priorities);
|
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -320,7 +259,7 @@ s32 PS4_SYSV_ABI sceAvPlayerStart(SceAvPlayerHandle handle) {
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 PS4_SYSV_ABI sceAvPlayerStop(SceAvPlayerHandle handle) {
|
s32 PS4_SYSV_ABI sceAvPlayerStop(SceAvPlayerHandle handle) {
|
||||||
LOG_ERROR(Lib_AvPlayer, "(STUBBED) called");
|
LOG_TRACE(Lib_AvPlayer, "called");
|
||||||
if (handle == nullptr) {
|
if (handle == nullptr) {
|
||||||
return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS;
|
return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
#include "common/types.h"
|
#include "common/types.h"
|
||||||
|
|
||||||
|
#include <stdarg.h> // va_list
|
||||||
#include <stddef.h> // size_t
|
#include <stddef.h> // size_t
|
||||||
|
|
||||||
namespace Core::Loader {
|
namespace Core::Loader {
|
||||||
|
|
|
@ -12,65 +12,6 @@ namespace Libraries::AvPlayer {
|
||||||
|
|
||||||
using namespace Kernel;
|
using namespace Kernel;
|
||||||
|
|
||||||
Kernel::ScePthreadMutex CreateMutex(int type, const char* name) {
|
|
||||||
ScePthreadMutexattr attr{};
|
|
||||||
ScePthreadMutex mutex{};
|
|
||||||
if (scePthreadMutexattrInit(&attr) == 0) {
|
|
||||||
if (scePthreadMutexattrSettype(&attr, type) == 0) {
|
|
||||||
if (scePthreadMutexInit(&mutex, &attr, name) != 0) {
|
|
||||||
if (mutex != nullptr) {
|
|
||||||
scePthreadMutexDestroy(&mutex);
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (attr != nullptr) {
|
|
||||||
scePthreadMutexattrDestroy(&attr);
|
|
||||||
}
|
|
||||||
return mutex;
|
|
||||||
}
|
|
||||||
|
|
||||||
ScePthread CreateThread(Kernel::PthreadEntryFunc func, const ThreadParameters& params) {
|
|
||||||
ScePthreadAttr attr;
|
|
||||||
if (scePthreadAttrInit(&attr) != 0) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
if (scePthreadAttrSetinheritsched(&attr, 0) != 0) {
|
|
||||||
scePthreadAttrDestroy(&attr);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
SceKernelSchedParam param{.sched_priority = static_cast<int>(params.priority)};
|
|
||||||
if (scePthreadAttrSetschedparam(&attr, ¶m) != 0) {
|
|
||||||
scePthreadAttrDestroy(&attr);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
if (scePthreadAttrSetstacksize(&attr, std::min(params.stack_size, 0x4000u)) != 0) {
|
|
||||||
scePthreadAttrDestroy(&attr);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
if (scePthreadAttrSetdetachstate(&attr, 0) != 0) {
|
|
||||||
scePthreadAttrDestroy(&attr);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
if (params.affinity > 0) {
|
|
||||||
if (scePthreadAttrSetaffinity(&attr, params.affinity) != 0) {
|
|
||||||
scePthreadAttrDestroy(&attr);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ScePthread thread{};
|
|
||||||
if (scePthreadCreate(&thread, &attr, func, params.p_user_data, params.thread_name) != 0) {
|
|
||||||
scePthreadAttrDestroy(&attr);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
scePthreadAttrDestroy(&attr);
|
|
||||||
return thread;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool ichar_equals(char a, char b) {
|
static bool ichar_equals(char a, char b) {
|
||||||
return std::tolower(static_cast<unsigned char>(a)) ==
|
return std::tolower(static_cast<unsigned char>(a)) ==
|
||||||
std::tolower(static_cast<unsigned char>(b));
|
std::tolower(static_cast<unsigned char>(b));
|
||||||
|
|
|
@ -31,9 +31,6 @@ enum class AvState {
|
||||||
C0x0B,
|
C0x0B,
|
||||||
Buffering,
|
Buffering,
|
||||||
Starting,
|
Starting,
|
||||||
C0x0E,
|
|
||||||
C0x0F,
|
|
||||||
C0x10,
|
|
||||||
Error,
|
Error,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -45,23 +42,6 @@ enum class AvEventType {
|
||||||
Error = 255,
|
Error = 255,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ThreadPriorities {
|
|
||||||
u32 audio_decoder_priority;
|
|
||||||
u32 audio_decoder_affinity;
|
|
||||||
u32 video_decoder_priority;
|
|
||||||
u32 video_decoder_affinity;
|
|
||||||
u32 demuxer_priority;
|
|
||||||
u32 demuxer_affinity;
|
|
||||||
u32 controller_priority;
|
|
||||||
u32 controller_affinity;
|
|
||||||
// u32 http_streaming_priority;
|
|
||||||
// u32 http_streaming_affinity;
|
|
||||||
// u32 file_streaming_priority;
|
|
||||||
// u32 file_streaming_affinity;
|
|
||||||
// u32 maxPriority;
|
|
||||||
// u32 maxAffinity;
|
|
||||||
};
|
|
||||||
|
|
||||||
union AvPlayerEventData {
|
union AvPlayerEventData {
|
||||||
u32 num_frames; // 20
|
u32 num_frames; // 20
|
||||||
AvState state; // AvEventType::ChangeFlowState
|
AvState state; // AvEventType::ChangeFlowState
|
||||||
|
@ -74,68 +54,9 @@ struct AvPlayerEvent {
|
||||||
AvPlayerEventData payload;
|
AvPlayerEventData payload;
|
||||||
};
|
};
|
||||||
|
|
||||||
Kernel::ScePthreadMutex CreateMutex(int type, const char* name);
|
|
||||||
|
|
||||||
class PthreadMutex {
|
|
||||||
public:
|
|
||||||
using ScePthreadMutex = Kernel::ScePthreadMutex;
|
|
||||||
|
|
||||||
PthreadMutex() = default;
|
|
||||||
|
|
||||||
PthreadMutex(const PthreadMutex&) = delete;
|
|
||||||
PthreadMutex& operator=(const PthreadMutex&) = delete;
|
|
||||||
|
|
||||||
PthreadMutex(PthreadMutex&& r) : m_mutex(r.m_mutex) {
|
|
||||||
r.m_mutex = nullptr;
|
|
||||||
}
|
|
||||||
PthreadMutex& operator=(PthreadMutex&& r) {
|
|
||||||
std::swap(m_mutex, r.m_mutex);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
PthreadMutex(int type, const char* name) : m_mutex(CreateMutex(type, name)) {}
|
|
||||||
~PthreadMutex() {
|
|
||||||
if (m_mutex != nullptr) {
|
|
||||||
Kernel::scePthreadMutexDestroy(&m_mutex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
operator ScePthreadMutex() {
|
|
||||||
return m_mutex;
|
|
||||||
}
|
|
||||||
|
|
||||||
int Lock() {
|
|
||||||
return Kernel::scePthreadMutexLock(&m_mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
int Unlock() {
|
|
||||||
return Kernel::scePthreadMutexUnlock(&m_mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
// implement BasicLockable to use std::lock_guard
|
|
||||||
// NOLINTNEXTLINE(readability-identifier-naming)
|
|
||||||
void lock() {
|
|
||||||
ASSERT_MSG(Lock() >= 0, "Could not lock the mutex");
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOLINTNEXTLINE(readability-identifier-naming)
|
|
||||||
void unlock() {
|
|
||||||
ASSERT_MSG(Unlock() >= 0, "Could not unlock the mutex");
|
|
||||||
}
|
|
||||||
|
|
||||||
operator bool() {
|
|
||||||
return m_mutex != nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
ScePthreadMutex m_mutex{};
|
|
||||||
};
|
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
class AvPlayerQueue {
|
class AvPlayerQueue {
|
||||||
public:
|
public:
|
||||||
AvPlayerQueue() : m_mutex(PTHREAD_MUTEX_ERRORCHECK, "SceAvPlayer0StlHandler") {}
|
|
||||||
|
|
||||||
size_t Size() {
|
size_t Size() {
|
||||||
return m_queue.size();
|
return m_queue.size();
|
||||||
}
|
}
|
||||||
|
@ -161,19 +82,10 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PthreadMutex m_mutex{};
|
std::mutex m_mutex{};
|
||||||
std::queue<T> m_queue{};
|
std::queue<T> m_queue{};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ThreadParameters {
|
|
||||||
void* p_user_data;
|
|
||||||
const char* thread_name;
|
|
||||||
u32 stack_size;
|
|
||||||
u32 priority;
|
|
||||||
u32 affinity;
|
|
||||||
};
|
|
||||||
|
|
||||||
Kernel::ScePthread CreateThread(Kernel::PthreadEntryFunc func, const ThreadParameters& params);
|
|
||||||
SceAvPlayerSourceType GetSourceType(std::string_view path);
|
SceAvPlayerSourceType GetSourceType(std::string_view path);
|
||||||
|
|
||||||
} // namespace Libraries::AvPlayer
|
} // namespace Libraries::AvPlayer
|
||||||
|
|
|
@ -69,14 +69,14 @@ s64 AvPlayerFileStreamer::Seek(void* opaque, s64 offset, int whence) {
|
||||||
|
|
||||||
if (whence == SEEK_CUR) {
|
if (whence == SEEK_CUR) {
|
||||||
self->m_position =
|
self->m_position =
|
||||||
std::min(u64(std::max(0ll, s64(self->m_position) + offset)), self->m_file_size);
|
std::min(u64(std::max(0i64, s64(self->m_position) + offset)), self->m_file_size);
|
||||||
return self->m_position;
|
return self->m_position;
|
||||||
} else if (whence == SEEK_SET) {
|
} else if (whence == SEEK_SET) {
|
||||||
self->m_position = std::min(u64(std::max(0ll, offset)), self->m_file_size);
|
self->m_position = std::min(u64(std::max(0i64, offset)), self->m_file_size);
|
||||||
return self->m_position;
|
return self->m_position;
|
||||||
} else if (whence == SEEK_END) {
|
} else if (whence == SEEK_END) {
|
||||||
self->m_position =
|
self->m_position =
|
||||||
std::min(u64(std::max(0ll, s64(self->m_file_size) + offset)), self->m_file_size);
|
std::min(u64(std::max(0i64, s64(self->m_file_size) + offset)), self->m_file_size);
|
||||||
return self->m_position;
|
return self->m_position;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -77,29 +77,30 @@ u64 PS4_SYSV_ABI AvPlayer::SizeFile(void* handle) {
|
||||||
return size(ptr);
|
return size(ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
AvPlayer::AvPlayer(const SceAvPlayerInitData& data, const ThreadPriorities& priorities)
|
SceAvPlayerInitData AvPlayer::StubInitData(const SceAvPlayerInitData& data) {
|
||||||
: m_file_io_mutex(PTHREAD_MUTEX_ERRORCHECK, "AvPlayer_FileIO"), m_init_data(data),
|
SceAvPlayerInitData result = data;
|
||||||
m_init_data_original(data) {
|
result.memory_replacement.object_ptr = this;
|
||||||
|
result.memory_replacement.allocate = &AvPlayer::Allocate;
|
||||||
m_init_data.memory_replacement.object_ptr = this;
|
result.memory_replacement.deallocate = &AvPlayer::Deallocate;
|
||||||
m_init_data.memory_replacement.allocate = &AvPlayer::Allocate;
|
result.memory_replacement.allocate_texture = &AvPlayer::AllocateTexture;
|
||||||
m_init_data.memory_replacement.deallocate = &AvPlayer::Deallocate;
|
result.memory_replacement.deallocate_texture = &AvPlayer::DeallocateTexture;
|
||||||
m_init_data.memory_replacement.allocate_texture = &AvPlayer::AllocateTexture;
|
|
||||||
m_init_data.memory_replacement.deallocate_texture = &AvPlayer::DeallocateTexture;
|
|
||||||
if (data.file_replacement.open == nullptr || data.file_replacement.close == nullptr ||
|
if (data.file_replacement.open == nullptr || data.file_replacement.close == nullptr ||
|
||||||
data.file_replacement.readOffset == nullptr || data.file_replacement.size == nullptr) {
|
data.file_replacement.readOffset == nullptr || data.file_replacement.size == nullptr) {
|
||||||
m_init_data.file_replacement = {};
|
result.file_replacement = {};
|
||||||
} else {
|
} else {
|
||||||
m_init_data.file_replacement.object_ptr = this;
|
result.file_replacement.object_ptr = this;
|
||||||
m_init_data.file_replacement.open = &AvPlayer::OpenFile;
|
result.file_replacement.open = &AvPlayer::OpenFile;
|
||||||
m_init_data.file_replacement.close = &AvPlayer::CloseFile;
|
result.file_replacement.close = &AvPlayer::CloseFile;
|
||||||
m_init_data.file_replacement.readOffset = &AvPlayer::ReadOffsetFile;
|
result.file_replacement.readOffset = &AvPlayer::ReadOffsetFile;
|
||||||
m_init_data.file_replacement.size = &AvPlayer::SizeFile;
|
result.file_replacement.size = &AvPlayer::SizeFile;
|
||||||
}
|
}
|
||||||
|
return result;
|
||||||
m_state = std::make_unique<AvPlayerState>(m_init_data, priorities);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AvPlayer::AvPlayer(const SceAvPlayerInitData& 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 SceAvPlayerPostInitData& data) {
|
||||||
m_post_init_data = data;
|
m_post_init_data = data;
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
|
@ -109,11 +110,9 @@ s32 AvPlayer::AddSource(std::string_view path) {
|
||||||
if (path.empty()) {
|
if (path.empty()) {
|
||||||
return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS;
|
return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (AVPLAYER_IS_ERROR(m_state->AddSource(path, GetSourceType(path)))) {
|
if (AVPLAYER_IS_ERROR(m_state->AddSource(path, GetSourceType(path)))) {
|
||||||
return ORBIS_AVPLAYER_ERROR_OPERATION_FAILED;
|
return ORBIS_AVPLAYER_ERROR_OPERATION_FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,7 +120,11 @@ s32 AvPlayer::GetStreamCount() {
|
||||||
if (m_state == nullptr) {
|
if (m_state == nullptr) {
|
||||||
return ORBIS_AVPLAYER_ERROR_OPERATION_FAILED;
|
return ORBIS_AVPLAYER_ERROR_OPERATION_FAILED;
|
||||||
}
|
}
|
||||||
return m_state->GetStreamCount();
|
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, SceAvPlayerStreamInfo& info) {
|
s32 AvPlayer::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info) {
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
|
|
||||||
#include "core/libraries/kernel/thread_management.h"
|
#include "core/libraries/kernel/thread_management.h"
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include <libavcodec/avcodec.h>
|
#include <libavcodec/avcodec.h>
|
||||||
#include <libavformat/avformat.h>
|
#include <libavformat/avformat.h>
|
||||||
|
@ -21,19 +23,7 @@ namespace Libraries::AvPlayer {
|
||||||
|
|
||||||
class AvPlayer {
|
class AvPlayer {
|
||||||
public:
|
public:
|
||||||
// Memory Replacement
|
AvPlayer(const SceAvPlayerInitData& data);
|
||||||
static void* PS4_SYSV_ABI Allocate(void* handle, u32 alignment, u32 size);
|
|
||||||
static void PS4_SYSV_ABI Deallocate(void* handle, void* memory);
|
|
||||||
static void* PS4_SYSV_ABI AllocateTexture(void* handle, u32 alignment, u32 size);
|
|
||||||
static void PS4_SYSV_ABI DeallocateTexture(void* handle, void* memory);
|
|
||||||
|
|
||||||
// File Replacement
|
|
||||||
static int PS4_SYSV_ABI OpenFile(void* handle, const char* filename);
|
|
||||||
static int PS4_SYSV_ABI CloseFile(void* handle);
|
|
||||||
static int PS4_SYSV_ABI ReadOffsetFile(void* handle, u8* buffer, u64 position, u32 length);
|
|
||||||
static u64 PS4_SYSV_ABI SizeFile(void* handle);
|
|
||||||
|
|
||||||
AvPlayer(const SceAvPlayerInitData& data, const ThreadPriorities& priorities);
|
|
||||||
|
|
||||||
s32 PostInit(const SceAvPlayerPostInitData& data);
|
s32 PostInit(const SceAvPlayerPostInitData& data);
|
||||||
s32 AddSource(std::string_view filename);
|
s32 AddSource(std::string_view filename);
|
||||||
|
@ -51,13 +41,27 @@ public:
|
||||||
private:
|
private:
|
||||||
using ScePthreadMutex = Kernel::ScePthreadMutex;
|
using ScePthreadMutex = Kernel::ScePthreadMutex;
|
||||||
|
|
||||||
std::unique_ptr<AvPlayerState> m_state{};
|
// Memory Replacement
|
||||||
|
static void* PS4_SYSV_ABI Allocate(void* handle, u32 alignment, u32 size);
|
||||||
|
static void PS4_SYSV_ABI Deallocate(void* handle, void* memory);
|
||||||
|
static void* PS4_SYSV_ABI AllocateTexture(void* handle, u32 alignment, u32 size);
|
||||||
|
static void PS4_SYSV_ABI DeallocateTexture(void* handle, void* memory);
|
||||||
|
|
||||||
|
// File Replacement
|
||||||
|
static int PS4_SYSV_ABI OpenFile(void* handle, const char* filename);
|
||||||
|
static int PS4_SYSV_ABI CloseFile(void* handle);
|
||||||
|
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);
|
||||||
|
|
||||||
SceAvPlayerInitData m_init_data{};
|
SceAvPlayerInitData m_init_data{};
|
||||||
SceAvPlayerInitData m_init_data_original{};
|
SceAvPlayerInitData m_init_data_original{};
|
||||||
SceAvPlayerPostInitData m_post_init_data{};
|
SceAvPlayerPostInitData m_post_init_data{};
|
||||||
PthreadMutex m_file_io_mutex{};
|
std::mutex m_file_io_mutex{};
|
||||||
|
|
||||||
std::atomic_bool m_has_source{};
|
std::atomic_bool m_has_source{};
|
||||||
|
std::unique_ptr<AvPlayerState> m_state{};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Libraries::AvPlayer
|
} // namespace Libraries::AvPlayer
|
||||||
|
|
|
@ -24,9 +24,9 @@ namespace Libraries::AvPlayer {
|
||||||
using namespace Kernel;
|
using namespace Kernel;
|
||||||
|
|
||||||
AvPlayerSource::AvPlayerSource(AvPlayerStateCallback& state, std::string_view path,
|
AvPlayerSource::AvPlayerSource(AvPlayerStateCallback& state, std::string_view path,
|
||||||
const SceAvPlayerInitData& init_data, ThreadPriorities& priorities,
|
const SceAvPlayerInitData& init_data,
|
||||||
SceAvPlayerSourceType source_type)
|
SceAvPlayerSourceType source_type)
|
||||||
: m_state(state), m_priorities(priorities), m_memory_replacement(init_data.memory_replacement),
|
: m_state(state), m_memory_replacement(init_data.memory_replacement),
|
||||||
m_num_output_video_framebuffers(init_data.num_output_video_framebuffers) {
|
m_num_output_video_framebuffers(init_data.num_output_video_framebuffers) {
|
||||||
AVFormatContext* context = avformat_alloc_context();
|
AVFormatContext* context = avformat_alloc_context();
|
||||||
if (init_data.file_replacement.open != nullptr) {
|
if (init_data.file_replacement.open != nullptr) {
|
||||||
|
@ -113,7 +113,7 @@ s32 AvPlayerSource::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info)
|
||||||
info.details.video.height = p_stream->codecpar->height;
|
info.details.video.height = p_stream->codecpar->height;
|
||||||
if (p_lang_node != nullptr) {
|
if (p_lang_node != nullptr) {
|
||||||
std::memcpy(info.details.video.language_code, p_lang_node->value,
|
std::memcpy(info.details.video.language_code, p_lang_node->value,
|
||||||
std::min(strlen(p_lang_node->value), 3ull));
|
std::min(strlen(p_lang_node->value), size_t(3)));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SCE_AVPLAYER_AUDIO:
|
case SCE_AVPLAYER_AUDIO:
|
||||||
|
@ -123,7 +123,7 @@ s32 AvPlayerSource::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info)
|
||||||
info.details.audio.size = 0; // sceAvPlayerGetStreamInfo() is expected to set this to 0
|
info.details.audio.size = 0; // sceAvPlayerGetStreamInfo() is expected to set this to 0
|
||||||
if (p_lang_node != nullptr) {
|
if (p_lang_node != nullptr) {
|
||||||
std::memcpy(info.details.audio.language_code, p_lang_node->value,
|
std::memcpy(info.details.audio.language_code, p_lang_node->value,
|
||||||
std::min(strlen(p_lang_node->value), 3ull));
|
std::min(strlen(p_lang_node->value), size_t(3)));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SCE_AVPLAYER_TIMEDTEXT:
|
case SCE_AVPLAYER_TIMEDTEXT:
|
||||||
|
@ -132,7 +132,7 @@ s32 AvPlayerSource::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info)
|
||||||
info.details.subs.text_size = 12;
|
info.details.subs.text_size = 12;
|
||||||
if (p_lang_node != nullptr) {
|
if (p_lang_node != nullptr) {
|
||||||
std::memcpy(info.details.subs.language_code, p_lang_node->value,
|
std::memcpy(info.details.subs.language_code, p_lang_node->value,
|
||||||
std::min(strlen(p_lang_node->value), 3ull));
|
std::min(strlen(p_lang_node->value), size_t(3)));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -189,7 +189,7 @@ bool AvPlayerSource::EnableStream(u32 stream_index) {
|
||||||
const auto num_channels = m_audio_codec_context->ch_layout.nb_channels;
|
const auto num_channels = m_audio_codec_context->ch_layout.nb_channels;
|
||||||
const auto align = num_channels * sizeof(u16);
|
const auto align = num_channels * sizeof(u16);
|
||||||
const auto size = num_channels * sizeof(u16) * 1024;
|
const auto size = num_channels * sizeof(u16) * 1024;
|
||||||
for (u64 index = 0; index < 2; ++index) {
|
for (u64 index = 0; index < 4; ++index) {
|
||||||
m_audio_buffers.Push(FrameBuffer(m_memory_replacement, 0x100, size));
|
m_audio_buffers.Push(FrameBuffer(m_memory_replacement, 0x100, size));
|
||||||
}
|
}
|
||||||
LOG_INFO(Lib_AvPlayer, "Audio stream {} enabled", stream_index);
|
LOG_INFO(Lib_AvPlayer, "Audio stream {} enabled", stream_index);
|
||||||
|
@ -212,78 +212,41 @@ std::optional<bool> AvPlayerSource::HasFrames(u32 num_frames) {
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 AvPlayerSource::Start() {
|
s32 AvPlayerSource::Start() {
|
||||||
|
std::unique_lock lock(m_state_mutex);
|
||||||
|
|
||||||
if (m_audio_codec_context == nullptr && m_video_codec_context == nullptr) {
|
if (m_audio_codec_context == nullptr && m_video_codec_context == nullptr) {
|
||||||
LOG_ERROR(Lib_AvPlayer, "Could not start playback. NULL context.");
|
LOG_ERROR(Lib_AvPlayer, "Could not start playback. NULL context.");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
{
|
m_demuxer_thread = std::jthread([this](std::stop_token stop) { this->DemuxerThread(stop); });
|
||||||
ThreadParameters demuxer_params{
|
m_video_decoder_thread =
|
||||||
.p_user_data = this,
|
std::jthread([this](std::stop_token stop) { this->VideoDecoderThread(stop); });
|
||||||
.thread_name = "AvPlayer_Demuxer",
|
m_audio_decoder_thread =
|
||||||
.stack_size = 0x4000,
|
std::jthread([this](std::stop_token stop) { this->AudioDecoderThread(stop); });
|
||||||
.priority = m_priorities.demuxer_priority,
|
|
||||||
.affinity = m_priorities.demuxer_affinity,
|
|
||||||
};
|
|
||||||
m_demuxer_thread = CreateThread(&DemuxerThread, demuxer_params);
|
|
||||||
if (m_demuxer_thread == nullptr) {
|
|
||||||
LOG_ERROR(Lib_AvPlayer, "Could not create DEMUXER thread.");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (m_video_codec_context != nullptr) {
|
|
||||||
ThreadParameters video_decoder_params{
|
|
||||||
.p_user_data = this,
|
|
||||||
.thread_name = "AvPlayer_VideoDecoder",
|
|
||||||
.stack_size = 0x4000,
|
|
||||||
.priority = m_priorities.video_decoder_priority,
|
|
||||||
.affinity = m_priorities.video_decoder_affinity,
|
|
||||||
};
|
|
||||||
m_video_decoder_thread = CreateThread(&VideoDecoderThread, video_decoder_params);
|
|
||||||
if (m_video_decoder_thread == nullptr) {
|
|
||||||
LOG_ERROR(Lib_AvPlayer, "Could not create VIDEO DECODER thread.");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (m_audio_codec_context != nullptr) {
|
|
||||||
ThreadParameters audio_decoder_params{
|
|
||||||
.p_user_data = this,
|
|
||||||
.thread_name = "AvPlayer_AudioDecoder",
|
|
||||||
.stack_size = 0x4000,
|
|
||||||
.priority = m_priorities.audio_decoder_priority,
|
|
||||||
.affinity = m_priorities.audio_decoder_affinity,
|
|
||||||
};
|
|
||||||
m_audio_decoder_thread = CreateThread(&AudioDecoderThread, audio_decoder_params);
|
|
||||||
if (m_audio_decoder_thread == nullptr) {
|
|
||||||
LOG_ERROR(Lib_AvPlayer, "Could not create AUDIO DECODER thread.");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
m_start_time = std::chrono::high_resolution_clock::now();
|
m_start_time = std::chrono::high_resolution_clock::now();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AvPlayerSource::Stop() {
|
bool AvPlayerSource::Stop() {
|
||||||
if (m_is_stop) {
|
std::unique_lock lock(m_state_mutex);
|
||||||
|
|
||||||
|
if (!HasRunningThreads()) {
|
||||||
LOG_WARNING(Lib_AvPlayer, "Could not stop playback: already stopped.");
|
LOG_WARNING(Lib_AvPlayer, "Could not stop playback: already stopped.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_is_stop = true;
|
m_video_decoder_thread.request_stop();
|
||||||
|
m_audio_decoder_thread.request_stop();
|
||||||
void* res = nullptr;
|
m_demuxer_thread.request_stop();
|
||||||
if (m_video_decoder_thread != nullptr) {
|
if (m_demuxer_thread.joinable()) {
|
||||||
scePthreadJoin(m_video_decoder_thread, &res);
|
m_demuxer_thread.join();
|
||||||
}
|
}
|
||||||
if (m_audio_decoder_thread != nullptr) {
|
if (m_video_decoder_thread.joinable()) {
|
||||||
scePthreadJoin(m_audio_decoder_thread, &res);
|
m_video_decoder_thread.join();
|
||||||
}
|
}
|
||||||
if (m_demuxer_thread != nullptr) {
|
if (m_audio_decoder_thread.joinable()) {
|
||||||
scePthreadJoin(m_demuxer_thread, &res);
|
m_audio_decoder_thread.join();
|
||||||
}
|
}
|
||||||
m_audio_packets.Clear();
|
|
||||||
m_video_packets.Clear();
|
|
||||||
m_audio_frames.Clear();
|
|
||||||
m_video_frames.Clear();
|
|
||||||
if (m_current_audio_frame.has_value()) {
|
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.value()));
|
||||||
m_current_audio_frame.reset();
|
m_current_audio_frame.reset();
|
||||||
|
@ -292,10 +255,19 @@ bool AvPlayerSource::Stop() {
|
||||||
m_video_buffers.Push(std::move(m_current_video_frame.value()));
|
m_video_buffers.Push(std::move(m_current_video_frame.value()));
|
||||||
m_current_video_frame.reset();
|
m_current_video_frame.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_audio_packets.Clear();
|
||||||
|
m_video_packets.Clear();
|
||||||
|
m_audio_frames.Clear();
|
||||||
|
m_video_frames.Clear();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AvPlayerSource::GetVideoData(SceAvPlayerFrameInfo& video_info) {
|
bool AvPlayerSource::GetVideoData(SceAvPlayerFrameInfo& video_info) {
|
||||||
|
if (!IsActive()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
SceAvPlayerFrameInfoEx info{};
|
SceAvPlayerFrameInfoEx info{};
|
||||||
if (!GetVideoData(info)) {
|
if (!GetVideoData(info)) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -315,8 +287,13 @@ static void CopyNV12Data(u8* dst, const AVFrame& src) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AvPlayerSource::GetVideoData(SceAvPlayerFrameInfoEx& video_info) {
|
bool AvPlayerSource::GetVideoData(SceAvPlayerFrameInfoEx& video_info) {
|
||||||
|
if (!IsActive()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
using namespace std::chrono;
|
||||||
while (m_video_frames.Size() == 0 && !m_is_eof) {
|
while (m_video_frames.Size() == 0 && !m_is_eof) {
|
||||||
sceKernelUsleep(5000);
|
std::this_thread::sleep_for(milliseconds(5));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto frame = m_video_frames.Pop();
|
auto frame = m_video_frames.Pop();
|
||||||
|
@ -326,11 +303,10 @@ bool AvPlayerSource::GetVideoData(SceAvPlayerFrameInfoEx& video_info) {
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
using namespace std::chrono;
|
|
||||||
auto elapsed_time =
|
auto elapsed_time =
|
||||||
duration_cast<milliseconds>(high_resolution_clock::now() - m_start_time).count();
|
duration_cast<milliseconds>(high_resolution_clock::now() - m_start_time).count();
|
||||||
while (elapsed_time < frame->info.timestamp) {
|
while (elapsed_time < frame->info.timestamp) {
|
||||||
sceKernelUsleep((frame->info.timestamp - elapsed_time) * 1000);
|
std::this_thread::sleep_for(milliseconds(frame->info.timestamp - elapsed_time));
|
||||||
elapsed_time =
|
elapsed_time =
|
||||||
duration_cast<milliseconds>(high_resolution_clock::now() - m_start_time).count();
|
duration_cast<milliseconds>(high_resolution_clock::now() - m_start_time).count();
|
||||||
}
|
}
|
||||||
|
@ -346,8 +322,13 @@ bool AvPlayerSource::GetVideoData(SceAvPlayerFrameInfoEx& video_info) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AvPlayerSource::GetAudioData(SceAvPlayerFrameInfo& audio_info) {
|
bool AvPlayerSource::GetAudioData(SceAvPlayerFrameInfo& audio_info) {
|
||||||
|
if (!IsActive()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
using namespace std::chrono;
|
||||||
while (m_audio_frames.Size() == 0 && !m_is_eof) {
|
while (m_audio_frames.Size() == 0 && !m_is_eof) {
|
||||||
sceKernelUsleep(5000);
|
std::this_thread::sleep_for(milliseconds(5));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto frame = m_audio_frames.Pop();
|
auto frame = m_audio_frames.Pop();
|
||||||
|
@ -357,11 +338,10 @@ bool AvPlayerSource::GetAudioData(SceAvPlayerFrameInfo& audio_info) {
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
using namespace std::chrono;
|
|
||||||
auto elapsed_time =
|
auto elapsed_time =
|
||||||
duration_cast<milliseconds>(high_resolution_clock::now() - m_start_time).count();
|
duration_cast<milliseconds>(high_resolution_clock::now() - m_start_time).count();
|
||||||
while (elapsed_time < frame->info.timestamp) {
|
while (elapsed_time < frame->info.timestamp) {
|
||||||
sceKernelUsleep((frame->info.timestamp - elapsed_time) * 1000);
|
std::this_thread::sleep_for(milliseconds(frame->info.timestamp - elapsed_time));
|
||||||
elapsed_time =
|
elapsed_time =
|
||||||
duration_cast<milliseconds>(high_resolution_clock::now() - m_start_time).count();
|
duration_cast<milliseconds>(high_resolution_clock::now() - m_start_time).count();
|
||||||
}
|
}
|
||||||
|
@ -387,8 +367,8 @@ u64 AvPlayerSource::CurrentTime() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AvPlayerSource::IsActive() {
|
bool AvPlayerSource::IsActive() {
|
||||||
return !m_is_stop && (!m_is_eof || m_audio_packets.Size() != 0 || m_video_packets.Size() != 0 ||
|
return !m_is_eof || m_audio_packets.Size() != 0 || m_video_packets.Size() != 0 ||
|
||||||
m_video_frames.Size() != 0 || m_audio_frames.Size() != 0);
|
m_video_frames.Size() != 0 || m_audio_frames.Size() != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AvPlayerSource::ReleaseAVPacket(AVPacket* packet) {
|
void AvPlayerSource::ReleaseAVPacket(AVPacket* packet) {
|
||||||
|
@ -427,52 +407,51 @@ void AvPlayerSource::ReleaseAVFormatContext(AVFormatContext* context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void* AvPlayerSource::DemuxerThread(void* opaque) {
|
void AvPlayerSource::DemuxerThread(std::stop_token stop) {
|
||||||
const auto self = reinterpret_cast<AvPlayerSource*>(opaque);
|
using namespace std::chrono;
|
||||||
if (!self->m_audio_stream_index.has_value() && !self->m_video_stream_index.has_value()) {
|
if (!m_audio_stream_index.has_value() && !m_video_stream_index.has_value()) {
|
||||||
LOG_WARNING(Lib_AvPlayer, "Could not start DEMUXER thread. No streams enabled.");
|
LOG_WARNING(Lib_AvPlayer, "Could not start DEMUXER thread. No streams enabled.");
|
||||||
return nullptr;
|
return;
|
||||||
}
|
}
|
||||||
LOG_INFO(Lib_AvPlayer, "Demuxer Thread started");
|
LOG_INFO(Lib_AvPlayer, "Demuxer Thread started");
|
||||||
|
|
||||||
while (!self->m_is_stop) {
|
while (!stop.stop_requested()) {
|
||||||
if (self->m_video_packets.Size() > 60) {
|
if (m_video_packets.Size() > 30 && m_audio_packets.Size() > 8) {
|
||||||
sceKernelUsleep(5000);
|
std::this_thread::sleep_for(milliseconds(5));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
AVPacketPtr up_packet(av_packet_alloc(), &ReleaseAVPacket);
|
AVPacketPtr up_packet(av_packet_alloc(), &ReleaseAVPacket);
|
||||||
const auto res = av_read_frame(self->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) {
|
||||||
if (res == AVERROR_EOF) {
|
if (res == AVERROR_EOF) {
|
||||||
LOG_INFO(Lib_AvPlayer, "EOF reached in demuxer");
|
LOG_INFO(Lib_AvPlayer, "EOF reached in demuxer");
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
LOG_ERROR(Lib_AvPlayer, "Could not read AV frame: error = {}", res);
|
LOG_ERROR(Lib_AvPlayer, "Could not read AV frame: error = {}", res);
|
||||||
self->m_state.OnError();
|
m_state.OnError();
|
||||||
scePthreadExit(0);
|
return;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (up_packet->stream_index == self->m_video_stream_index) {
|
if (up_packet->stream_index == m_video_stream_index) {
|
||||||
self->m_video_packets.Push(std::move(up_packet));
|
m_video_packets.Push(std::move(up_packet));
|
||||||
} else if (up_packet->stream_index == self->m_audio_stream_index) {
|
} else if (up_packet->stream_index == m_audio_stream_index) {
|
||||||
self->m_audio_packets.Push(std::move(up_packet));
|
m_audio_packets.Push(std::move(up_packet));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self->m_is_eof = true;
|
m_is_eof = true;
|
||||||
|
|
||||||
void* res;
|
void* res;
|
||||||
if (self->m_video_decoder_thread) {
|
if (m_video_decoder_thread.joinable()) {
|
||||||
scePthreadJoin(self->m_video_decoder_thread, &res);
|
m_video_decoder_thread.join();
|
||||||
}
|
}
|
||||||
if (self->m_audio_decoder_thread) {
|
if (m_audio_decoder_thread.joinable()) {
|
||||||
scePthreadJoin(self->m_audio_decoder_thread, &res);
|
m_audio_decoder_thread.join();
|
||||||
}
|
}
|
||||||
self->m_state.OnEOF();
|
m_state.OnEOF();
|
||||||
|
|
||||||
LOG_INFO(Lib_AvPlayer, "Demuxer Thread exited normaly");
|
LOG_INFO(Lib_AvPlayer, "Demuxer Thread exited normaly");
|
||||||
scePthreadExit(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AvPlayerSource::AVFramePtr AvPlayerSource::ConvertVideoFrame(const AVFrame& frame) {
|
AvPlayerSource::AVFramePtr AvPlayerSource::ConvertVideoFrame(const AVFrame& frame) {
|
||||||
|
@ -537,67 +516,63 @@ Frame AvPlayerSource::PrepareVideoFrame(FrameBuffer buffer, const AVFrame& frame
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
void* AvPlayerSource::VideoDecoderThread(void* opaque) {
|
void AvPlayerSource::VideoDecoderThread(std::stop_token stop) {
|
||||||
|
using namespace std::chrono;
|
||||||
LOG_INFO(Lib_AvPlayer, "Video Decoder Thread started");
|
LOG_INFO(Lib_AvPlayer, "Video Decoder Thread started");
|
||||||
const auto self = reinterpret_cast<AvPlayerSource*>(opaque);
|
while ((!m_is_eof || m_video_packets.Size() != 0) && !stop.stop_requested()) {
|
||||||
|
if (m_video_packets.Size() == 0) {
|
||||||
while ((!self->m_is_eof || self->m_video_packets.Size() != 0) && !self->m_is_stop) {
|
std::this_thread::sleep_for(milliseconds(5));
|
||||||
if (self->m_video_packets.Size() == 0) {
|
|
||||||
sceKernelUsleep(5000);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const auto packet = self->m_video_packets.Pop();
|
const auto packet = m_video_packets.Pop();
|
||||||
if (!packet.has_value()) {
|
if (!packet.has_value()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto res = avcodec_send_packet(self->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)) {
|
||||||
self->m_state.OnError();
|
m_state.OnError();
|
||||||
LOG_ERROR(Lib_AvPlayer, "Could not send packet to the video codec. Error = {}",
|
LOG_ERROR(Lib_AvPlayer, "Could not send packet to the video codec. Error = {}",
|
||||||
av_err2str(res));
|
av_err2str(res));
|
||||||
scePthreadExit(nullptr);
|
return;
|
||||||
}
|
}
|
||||||
while (res >= 0) {
|
while (res >= 0) {
|
||||||
if (self->m_video_buffers.Size() == 0 && !self->m_is_stop) {
|
if (m_video_buffers.Size() == 0 && !stop.stop_requested()) {
|
||||||
sceKernelUsleep(5000);
|
std::this_thread::sleep_for(milliseconds(5));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
auto up_frame = AVFramePtr(av_frame_alloc(), &ReleaseAVFrame);
|
auto up_frame = AVFramePtr(av_frame_alloc(), &ReleaseAVFrame);
|
||||||
res = avcodec_receive_frame(self->m_video_codec_context.get(), up_frame.get());
|
res = avcodec_receive_frame(m_video_codec_context.get(), up_frame.get());
|
||||||
if (res < 0) {
|
if (res < 0) {
|
||||||
if (res == AVERROR_EOF) {
|
if (res == AVERROR_EOF) {
|
||||||
LOG_INFO(Lib_AvPlayer, "EOF reached in video decoder");
|
LOG_INFO(Lib_AvPlayer, "EOF reached in video decoder");
|
||||||
scePthreadExit(nullptr);
|
return;
|
||||||
} else if (res != AVERROR(EAGAIN)) {
|
} else if (res != AVERROR(EAGAIN)) {
|
||||||
LOG_ERROR(Lib_AvPlayer,
|
LOG_ERROR(Lib_AvPlayer,
|
||||||
"Could not receive frame from the video codec. Error = {}",
|
"Could not receive frame from the video codec. Error = {}",
|
||||||
av_err2str(res));
|
av_err2str(res));
|
||||||
self->m_state.OnError();
|
m_state.OnError();
|
||||||
scePthreadExit(nullptr);
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
auto buffer = self->m_video_buffers.Pop();
|
auto buffer = m_video_buffers.Pop();
|
||||||
if (!buffer.has_value()) {
|
if (!buffer.has_value()) {
|
||||||
// Video buffers queue was cleared. This means that player was stopped.
|
// Video buffers queue was cleared. This means that player was stopped.
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (up_frame->format != AV_PIX_FMT_NV12) {
|
if (up_frame->format != AV_PIX_FMT_NV12) {
|
||||||
const auto nv12_frame = self->ConvertVideoFrame(*up_frame);
|
const auto nv12_frame = ConvertVideoFrame(*up_frame);
|
||||||
self->m_video_frames.Push(
|
m_video_frames.Push(PrepareVideoFrame(std::move(buffer.value()), *nv12_frame));
|
||||||
self->PrepareVideoFrame(std::move(buffer.value()), *nv12_frame));
|
|
||||||
} else {
|
} else {
|
||||||
self->m_video_frames.Push(
|
m_video_frames.Push(PrepareVideoFrame(std::move(buffer.value()), *up_frame));
|
||||||
self->PrepareVideoFrame(std::move(buffer.value()), *up_frame));
|
|
||||||
}
|
}
|
||||||
LOG_TRACE(Lib_AvPlayer, "Produced Video Frame. Num Frames: {}",
|
LOG_TRACE(Lib_AvPlayer, "Produced Video Frame. Num Frames: {}",
|
||||||
self->m_video_frames.Size());
|
m_video_frames.Size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_INFO(Lib_AvPlayer, "Video Decoder Thread exited normaly");
|
LOG_INFO(Lib_AvPlayer, "Video Decoder Thread exited normaly");
|
||||||
scePthreadExit(nullptr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AvPlayerSource::AVFramePtr AvPlayerSource::ConvertAudioFrame(const AVFrame& frame) {
|
AvPlayerSource::AVFramePtr AvPlayerSource::ConvertAudioFrame(const AVFrame& frame) {
|
||||||
|
@ -659,66 +634,67 @@ Frame AvPlayerSource::PrepareAudioFrame(FrameBuffer buffer, const AVFrame& frame
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
void* AvPlayerSource::AudioDecoderThread(void* opaque) {
|
void AvPlayerSource::AudioDecoderThread(std::stop_token stop) {
|
||||||
|
using namespace std::chrono;
|
||||||
LOG_INFO(Lib_AvPlayer, "Audio Decoder Thread started");
|
LOG_INFO(Lib_AvPlayer, "Audio Decoder Thread started");
|
||||||
const auto self = reinterpret_cast<AvPlayerSource*>(opaque);
|
while ((!m_is_eof || m_audio_packets.Size() != 0) && !stop.stop_requested()) {
|
||||||
|
if (m_audio_packets.Size() == 0) {
|
||||||
while ((!self->m_is_eof || self->m_audio_packets.Size() != 0) && !self->m_is_stop) {
|
std::this_thread::sleep_for(milliseconds(5));
|
||||||
if (self->m_audio_packets.Size() == 0) {
|
|
||||||
sceKernelUsleep(5000);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const auto packet = self->m_audio_packets.Pop();
|
const auto packet = m_audio_packets.Pop();
|
||||||
if (!packet.has_value()) {
|
if (!packet.has_value()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
auto res = avcodec_send_packet(self->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)) {
|
||||||
self->m_state.OnError();
|
m_state.OnError();
|
||||||
LOG_ERROR(Lib_AvPlayer, "Could not send packet to the audio codec. Error = {}",
|
LOG_ERROR(Lib_AvPlayer, "Could not send packet to the audio codec. Error = {}",
|
||||||
av_err2str(res));
|
av_err2str(res));
|
||||||
scePthreadExit(nullptr);
|
return;
|
||||||
}
|
}
|
||||||
while (res >= 0) {
|
while (res >= 0) {
|
||||||
if (self->m_audio_buffers.Size() == 0 && !self->m_is_stop) {
|
if (m_audio_buffers.Size() == 0 && !stop.stop_requested()) {
|
||||||
sceKernelUsleep(5000);
|
std::this_thread::sleep_for(milliseconds(5));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
auto up_frame = AVFramePtr(av_frame_alloc(), &ReleaseAVFrame);
|
auto up_frame = AVFramePtr(av_frame_alloc(), &ReleaseAVFrame);
|
||||||
res = avcodec_receive_frame(self->m_audio_codec_context.get(), up_frame.get());
|
res = avcodec_receive_frame(m_audio_codec_context.get(), up_frame.get());
|
||||||
if (res < 0) {
|
if (res < 0) {
|
||||||
if (res == AVERROR_EOF) {
|
if (res == AVERROR_EOF) {
|
||||||
LOG_INFO(Lib_AvPlayer, "EOF reached in audio decoder");
|
LOG_INFO(Lib_AvPlayer, "EOF reached in audio decoder");
|
||||||
scePthreadExit(nullptr);
|
return;
|
||||||
} else if (res != AVERROR(EAGAIN)) {
|
} else if (res != AVERROR(EAGAIN)) {
|
||||||
self->m_state.OnError();
|
m_state.OnError();
|
||||||
LOG_ERROR(Lib_AvPlayer,
|
LOG_ERROR(Lib_AvPlayer,
|
||||||
"Could not receive frame from the audio codec. Error = {}",
|
"Could not receive frame from the audio codec. Error = {}",
|
||||||
av_err2str(res));
|
av_err2str(res));
|
||||||
scePthreadExit(nullptr);
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
auto buffer = self->m_audio_buffers.Pop();
|
auto buffer = m_audio_buffers.Pop();
|
||||||
if (!buffer.has_value()) {
|
if (!buffer.has_value()) {
|
||||||
// Audio buffers queue was cleared. This means that player was stopped.
|
// Audio buffers queue was cleared. This means that player was stopped.
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (up_frame->format != AV_SAMPLE_FMT_S16) {
|
if (up_frame->format != AV_SAMPLE_FMT_S16) {
|
||||||
const auto pcm16_frame = self->ConvertAudioFrame(*up_frame);
|
const auto pcm16_frame = ConvertAudioFrame(*up_frame);
|
||||||
self->m_audio_frames.Push(
|
m_audio_frames.Push(PrepareAudioFrame(std::move(buffer.value()), *pcm16_frame));
|
||||||
self->PrepareAudioFrame(std::move(buffer.value()), *pcm16_frame));
|
|
||||||
} else {
|
} else {
|
||||||
self->m_audio_frames.Push(
|
m_audio_frames.Push(PrepareAudioFrame(std::move(buffer.value()), *up_frame));
|
||||||
self->PrepareAudioFrame(std::move(buffer.value()), *up_frame));
|
|
||||||
}
|
}
|
||||||
LOG_TRACE(Lib_AvPlayer, "Produced Audio Frame. Num Frames: {}",
|
LOG_TRACE(Lib_AvPlayer, "Produced Audio Frame. Num Frames: {}",
|
||||||
self->m_audio_frames.Size());
|
m_audio_frames.Size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_INFO(Lib_AvPlayer, "Audio Decoder Thread exited normaly");
|
LOG_INFO(Lib_AvPlayer, "Audio Decoder Thread exited normaly");
|
||||||
scePthreadExit(nullptr);
|
}
|
||||||
|
|
||||||
|
bool AvPlayerSource::HasRunningThreads() const {
|
||||||
|
return m_demuxer_thread.joinable() || m_video_decoder_thread.joinable() ||
|
||||||
|
m_audio_decoder_thread.joinable();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Libraries::AvPlayer
|
} // namespace Libraries::AvPlayer
|
||||||
|
|
|
@ -12,8 +12,11 @@
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#include <mutex>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <stop_token>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
struct AVCodecContext;
|
struct AVCodecContext;
|
||||||
struct AVFormatContext;
|
struct AVFormatContext;
|
||||||
|
@ -88,8 +91,7 @@ struct Frame {
|
||||||
class AvPlayerSource {
|
class AvPlayerSource {
|
||||||
public:
|
public:
|
||||||
AvPlayerSource(AvPlayerStateCallback& state, std::string_view path,
|
AvPlayerSource(AvPlayerStateCallback& state, std::string_view path,
|
||||||
const SceAvPlayerInitData& init_data, ThreadPriorities& priorities,
|
const SceAvPlayerInitData& init_data, SceAvPlayerSourceType source_type);
|
||||||
SceAvPlayerSourceType source_type);
|
|
||||||
~AvPlayerSource();
|
~AvPlayerSource();
|
||||||
|
|
||||||
bool FindStreamInfo();
|
bool FindStreamInfo();
|
||||||
|
@ -109,10 +111,6 @@ public:
|
||||||
private:
|
private:
|
||||||
using ScePthread = Kernel::ScePthread;
|
using ScePthread = Kernel::ScePthread;
|
||||||
|
|
||||||
static void* PS4_SYSV_ABI DemuxerThread(void* opaque);
|
|
||||||
static void* PS4_SYSV_ABI VideoDecoderThread(void* opaque);
|
|
||||||
static void* PS4_SYSV_ABI AudioDecoderThread(void* opaque);
|
|
||||||
|
|
||||||
static void ReleaseAVPacket(AVPacket* packet);
|
static void ReleaseAVPacket(AVPacket* packet);
|
||||||
static void ReleaseAVFrame(AVFrame* frame);
|
static void ReleaseAVFrame(AVFrame* frame);
|
||||||
static void ReleaseAVCodecContext(AVCodecContext* context);
|
static void ReleaseAVCodecContext(AVCodecContext* context);
|
||||||
|
@ -127,6 +125,12 @@ private:
|
||||||
using SWSContextPtr = std::unique_ptr<SwsContext, decltype(&ReleaseSWSContext)>;
|
using SWSContextPtr = std::unique_ptr<SwsContext, decltype(&ReleaseSWSContext)>;
|
||||||
using AVFormatContextPtr = std::unique_ptr<AVFormatContext, decltype(&ReleaseAVFormatContext)>;
|
using AVFormatContextPtr = std::unique_ptr<AVFormatContext, decltype(&ReleaseAVFormatContext)>;
|
||||||
|
|
||||||
|
void DemuxerThread(std::stop_token stop);
|
||||||
|
void VideoDecoderThread(std::stop_token stop);
|
||||||
|
void AudioDecoderThread(std::stop_token stop);
|
||||||
|
|
||||||
|
bool HasRunningThreads() const;
|
||||||
|
|
||||||
AVFramePtr ConvertAudioFrame(const AVFrame& frame);
|
AVFramePtr ConvertAudioFrame(const AVFrame& frame);
|
||||||
AVFramePtr ConvertVideoFrame(const AVFrame& frame);
|
AVFramePtr ConvertVideoFrame(const AVFrame& frame);
|
||||||
|
|
||||||
|
@ -135,13 +139,11 @@ private:
|
||||||
|
|
||||||
AvPlayerStateCallback& m_state;
|
AvPlayerStateCallback& m_state;
|
||||||
|
|
||||||
ThreadPriorities m_priorities{};
|
|
||||||
SceAvPlayerMemAllocator m_memory_replacement{};
|
SceAvPlayerMemAllocator m_memory_replacement{};
|
||||||
u64 m_num_output_video_framebuffers{};
|
u64 m_num_output_video_framebuffers{};
|
||||||
|
|
||||||
std::atomic_bool m_is_looping = false;
|
std::atomic_bool m_is_looping = false;
|
||||||
std::atomic_bool m_is_eof = false;
|
std::atomic_bool m_is_eof = false;
|
||||||
std::atomic_bool m_is_stop = false;
|
|
||||||
|
|
||||||
std::unique_ptr<IDataStreamer> m_up_data_streamer;
|
std::unique_ptr<IDataStreamer> m_up_data_streamer;
|
||||||
|
|
||||||
|
@ -160,9 +162,10 @@ private:
|
||||||
std::optional<s32> m_video_stream_index{};
|
std::optional<s32> m_video_stream_index{};
|
||||||
std::optional<s32> m_audio_stream_index{};
|
std::optional<s32> m_audio_stream_index{};
|
||||||
|
|
||||||
ScePthread m_demuxer_thread{};
|
std::mutex m_state_mutex;
|
||||||
ScePthread m_video_decoder_thread{};
|
std::jthread m_demuxer_thread{};
|
||||||
ScePthread m_audio_decoder_thread{};
|
std::jthread m_video_decoder_thread{};
|
||||||
|
std::jthread m_audio_decoder_thread{};
|
||||||
|
|
||||||
AVFormatContextPtr m_avformat_context{nullptr, &ReleaseAVFormatContext};
|
AVFormatContextPtr m_avformat_context{nullptr, &ReleaseAVFormatContext};
|
||||||
AVCodecContextPtr m_video_codec_context{nullptr, &ReleaseAVCodecContext};
|
AVCodecContextPtr m_video_codec_context{nullptr, &ReleaseAVCodecContext};
|
||||||
|
|
|
@ -89,12 +89,8 @@ void PS4_SYSV_ABI AvPlayerState::AutoPlayEventCallback(void* opaque, s32 event_i
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called inside GAME thread
|
// Called inside GAME thread
|
||||||
AvPlayerState::AvPlayerState(const SceAvPlayerInitData& init_data,
|
AvPlayerState::AvPlayerState(const SceAvPlayerInitData& init_data)
|
||||||
const ThreadPriorities& priorities)
|
: m_init_data(init_data), m_event_replacement(init_data.event_replacement) {
|
||||||
: m_init_data(init_data), m_event_replacement(init_data.event_replacement),
|
|
||||||
m_thread_priorities(priorities),
|
|
||||||
m_event_handler_mutex(PTHREAD_MUTEX_ERRORCHECK, "AvPlayer_EventHandler"),
|
|
||||||
m_state_machine_mutex(PTHREAD_MUTEX_ERRORCHECK, "AvPlayer_StateMachine") {
|
|
||||||
if (m_event_replacement.event_callback == nullptr || init_data.auto_start) {
|
if (m_event_replacement.event_callback == nullptr || init_data.auto_start) {
|
||||||
m_auto_start = true;
|
m_auto_start = true;
|
||||||
m_init_data.event_replacement.event_callback = &AvPlayerState::AutoPlayEventCallback;
|
m_init_data.event_replacement.event_callback = &AvPlayerState::AutoPlayEventCallback;
|
||||||
|
@ -111,10 +107,9 @@ AvPlayerState::~AvPlayerState() {
|
||||||
if (m_up_source && m_current_state == AvState::Play) {
|
if (m_up_source && m_current_state == AvState::Play) {
|
||||||
m_up_source->Stop();
|
m_up_source->Stop();
|
||||||
}
|
}
|
||||||
m_quit = true;
|
if (m_controller_thread.joinable()) {
|
||||||
if (m_controller_thread != nullptr) {
|
m_controller_thread.request_stop();
|
||||||
void* res = nullptr;
|
m_controller_thread.join();
|
||||||
scePthreadJoin(m_controller_thread, &res);
|
|
||||||
}
|
}
|
||||||
m_event_queue.Clear();
|
m_event_queue.Clear();
|
||||||
}
|
}
|
||||||
|
@ -131,8 +126,7 @@ s32 AvPlayerState::AddSource(std::string_view path, SceAvPlayerSourceType source
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_up_source = std::make_unique<AvPlayerSource>(*this, path, m_init_data, m_thread_priorities,
|
m_up_source = std::make_unique<AvPlayerSource>(*this, path, m_init_data, source_type);
|
||||||
source_type);
|
|
||||||
AddSourceEvent();
|
AddSourceEvent();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -166,17 +160,17 @@ s32 AvPlayerState::Start() {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void* AvPlayerState::AvControllerThread(void* p_user_data) {
|
void AvPlayerState::AvControllerThread(std::stop_token stop) {
|
||||||
AvPlayerState* self = reinterpret_cast<AvPlayerState*>(p_user_data);
|
using std::chrono::milliseconds;
|
||||||
while (!self->m_quit) {
|
|
||||||
if (self->m_event_queue.Size() != 0) {
|
while (!stop.stop_requested()) {
|
||||||
self->ProcessEvent();
|
if (m_event_queue.Size() != 0) {
|
||||||
|
ProcessEvent();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
sceKernelUsleep(5000);
|
std::this_thread::sleep_for(milliseconds(5));
|
||||||
self->UpdateBufferingState();
|
UpdateBufferingState();
|
||||||
}
|
}
|
||||||
scePthreadExit(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called inside GAME thread
|
// Called inside GAME thread
|
||||||
|
@ -198,22 +192,9 @@ void AvPlayerState::WarningEvent(s32 id) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called inside GAME thread
|
// Called inside GAME thread
|
||||||
int AvPlayerState::StartControllerThread() {
|
void AvPlayerState::StartControllerThread() {
|
||||||
m_quit.store(0);
|
m_controller_thread =
|
||||||
|
std::jthread([this](std::stop_token stop) { this->AvControllerThread(stop); });
|
||||||
ThreadParameters params{
|
|
||||||
.p_user_data = this,
|
|
||||||
.thread_name = "AvPlayer_Controller",
|
|
||||||
.stack_size = 0x4000,
|
|
||||||
.priority = m_thread_priority,
|
|
||||||
.affinity = m_thread_affinity,
|
|
||||||
};
|
|
||||||
m_controller_thread = CreateThread(&AvControllerThread, params);
|
|
||||||
if (m_controller_thread == nullptr) {
|
|
||||||
LOG_ERROR(Lib_AvPlayer, "Could not create CONTROLLER thread.");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called inside GAME thread
|
// Called inside GAME thread
|
||||||
|
@ -262,7 +243,7 @@ bool AvPlayerState::IsActive() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return m_current_state != AvState::Stop && m_current_state != AvState::Error &&
|
return m_current_state != AvState::Stop && m_current_state != AvState::Error &&
|
||||||
m_up_source->IsActive();
|
m_current_state != AvState::EndOfFile && m_up_source->IsActive();
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 AvPlayerState::CurrentTime() {
|
u64 AvPlayerState::CurrentTime() {
|
||||||
|
@ -284,7 +265,9 @@ void AvPlayerState::OnError() {
|
||||||
OnPlaybackStateChanged(AvState::Error);
|
OnPlaybackStateChanged(AvState::Error);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AvPlayerState::OnEOF() {}
|
void AvPlayerState::OnEOF() {
|
||||||
|
SetState(AvState::EndOfFile);
|
||||||
|
}
|
||||||
|
|
||||||
// Called inside CONTROLLER thread
|
// Called inside CONTROLLER thread
|
||||||
void AvPlayerState::OnPlaybackStateChanged(AvState state) {
|
void AvPlayerState::OnPlaybackStateChanged(AvState state) {
|
||||||
|
@ -347,16 +330,16 @@ void AvPlayerState::EmitEvent(SceAvPlayerEvents event_id, void* event_data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called inside CONTROLLER thread
|
// Called inside CONTROLLER thread
|
||||||
int AvPlayerState::ProcessEvent() {
|
void AvPlayerState::ProcessEvent() {
|
||||||
if (m_current_state == AvState::Jump) {
|
if (m_current_state == AvState::Jump) {
|
||||||
return -2;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::lock_guard guard(m_event_handler_mutex);
|
std::lock_guard guard(m_event_handler_mutex);
|
||||||
|
|
||||||
auto event = m_event_queue.Pop();
|
auto event = m_event_queue.Pop();
|
||||||
if (!event.has_value()) {
|
if (!event.has_value()) {
|
||||||
return -1;
|
return;
|
||||||
}
|
}
|
||||||
switch (event->event) {
|
switch (event->event) {
|
||||||
case AvEventType::WarningId: {
|
case AvEventType::WarningId: {
|
||||||
|
@ -385,16 +368,14 @@ int AvPlayerState::ProcessEvent() {
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called inside CONTROLLER thread
|
// Called inside CONTROLLER thread
|
||||||
int AvPlayerState::UpdateBufferingState() {
|
void AvPlayerState::UpdateBufferingState() {
|
||||||
if (m_current_state == AvState::Buffering) {
|
if (m_current_state == AvState::Buffering) {
|
||||||
const auto has_frames = OnBufferingCheckEvent(10);
|
const auto has_frames = OnBufferingCheckEvent(10);
|
||||||
if (!has_frames.has_value()) {
|
if (!has_frames.has_value()) {
|
||||||
return -1;
|
return;
|
||||||
}
|
}
|
||||||
if (has_frames.value()) {
|
if (has_frames.value()) {
|
||||||
const auto state =
|
const auto state =
|
||||||
|
@ -405,14 +386,13 @@ int AvPlayerState::UpdateBufferingState() {
|
||||||
} else if (m_current_state == AvState::Play) {
|
} else if (m_current_state == AvState::Play) {
|
||||||
const auto has_frames = OnBufferingCheckEvent(0);
|
const auto has_frames = OnBufferingCheckEvent(0);
|
||||||
if (!has_frames.has_value()) {
|
if (!has_frames.has_value()) {
|
||||||
return -1;
|
return;
|
||||||
}
|
}
|
||||||
if (!has_frames.value()) {
|
if (!has_frames.value()) {
|
||||||
SetState(AvState::Buffering);
|
SetState(AvState::Buffering);
|
||||||
OnPlaybackStateChanged(AvState::Buffering);
|
OnPlaybackStateChanged(AvState::Buffering);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AvPlayerState::IsStateTransitionValid(AvState state) {
|
bool AvPlayerState::IsStateTransitionValid(AvState state) {
|
||||||
|
|
|
@ -10,6 +10,9 @@
|
||||||
#include "core/libraries/kernel/thread_management.h"
|
#include "core/libraries/kernel/thread_management.h"
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
#include <stop_token>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
namespace Libraries::AvPlayer {
|
namespace Libraries::AvPlayer {
|
||||||
|
|
||||||
|
@ -18,7 +21,7 @@ class AvDecoder;
|
||||||
|
|
||||||
class AvPlayerState : public AvPlayerStateCallback {
|
class AvPlayerState : public AvPlayerStateCallback {
|
||||||
public:
|
public:
|
||||||
AvPlayerState(const SceAvPlayerInitData& init_data, const ThreadPriorities& priorities);
|
AvPlayerState(const SceAvPlayerInitData& init_data);
|
||||||
~AvPlayerState();
|
~AvPlayerState();
|
||||||
|
|
||||||
s32 AddSource(std::string_view filename, SceAvPlayerSourceType source_type);
|
s32 AddSource(std::string_view filename, SceAvPlayerSourceType source_type);
|
||||||
|
@ -51,34 +54,32 @@ private:
|
||||||
void EmitEvent(SceAvPlayerEvents event_id, void* event_data = nullptr);
|
void EmitEvent(SceAvPlayerEvents event_id, void* event_data = nullptr);
|
||||||
bool SetState(AvState state);
|
bool SetState(AvState state);
|
||||||
|
|
||||||
static void* PS4_SYSV_ABI AvControllerThread(void* p_user_data);
|
void AvControllerThread(std::stop_token stop);
|
||||||
|
|
||||||
void AddSourceEvent();
|
void AddSourceEvent();
|
||||||
void WarningEvent(s32 id);
|
void WarningEvent(s32 id);
|
||||||
|
|
||||||
int StartControllerThread();
|
void StartControllerThread();
|
||||||
int ProcessEvent();
|
void ProcessEvent();
|
||||||
int UpdateBufferingState();
|
void UpdateBufferingState();
|
||||||
bool IsStateTransitionValid(AvState state);
|
bool IsStateTransitionValid(AvState state);
|
||||||
|
|
||||||
std::unique_ptr<AvPlayerSource> m_up_source;
|
std::unique_ptr<AvPlayerSource> m_up_source;
|
||||||
|
|
||||||
SceAvPlayerInitData m_init_data{};
|
SceAvPlayerInitData m_init_data{};
|
||||||
SceAvPlayerEventReplacement m_event_replacement{};
|
SceAvPlayerEventReplacement m_event_replacement{};
|
||||||
ThreadPriorities m_thread_priorities{};
|
|
||||||
bool m_auto_start{};
|
bool m_auto_start{};
|
||||||
u8 m_default_language[4]{};
|
u8 m_default_language[4]{};
|
||||||
|
|
||||||
std::atomic_bool m_quit;
|
|
||||||
std::atomic<AvState> m_current_state;
|
std::atomic<AvState> m_current_state;
|
||||||
std::atomic<AvState> m_previous_state;
|
std::atomic<AvState> m_previous_state;
|
||||||
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{};
|
||||||
|
|
||||||
PthreadMutex m_state_machine_mutex{};
|
std::mutex m_state_machine_mutex{};
|
||||||
PthreadMutex m_event_handler_mutex{};
|
std::mutex m_event_handler_mutex{};
|
||||||
ScePthread m_controller_thread{};
|
std::jthread m_controller_thread{};
|
||||||
AvPlayerQueue<AvPlayerEvent> m_event_queue{};
|
AvPlayerQueue<AvPlayerEvent> m_event_queue{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -158,24 +158,19 @@ void init_pthreads();
|
||||||
void pthreadInitSelfMainThread();
|
void pthreadInitSelfMainThread();
|
||||||
|
|
||||||
int PS4_SYSV_ABI scePthreadAttrInit(ScePthreadAttr* attr);
|
int PS4_SYSV_ABI scePthreadAttrInit(ScePthreadAttr* attr);
|
||||||
int PS4_SYSV_ABI scePthreadAttrDestroy(ScePthreadAttr* attr);
|
|
||||||
int PS4_SYSV_ABI scePthreadAttrSetdetachstate(ScePthreadAttr* attr, int detachstate);
|
int PS4_SYSV_ABI scePthreadAttrSetdetachstate(ScePthreadAttr* attr, int detachstate);
|
||||||
int PS4_SYSV_ABI scePthreadAttrSetinheritsched(ScePthreadAttr* attr, int inheritSched);
|
int PS4_SYSV_ABI scePthreadAttrSetinheritsched(ScePthreadAttr* attr, int inheritSched);
|
||||||
int PS4_SYSV_ABI scePthreadAttrSetschedparam(ScePthreadAttr* attr,
|
int PS4_SYSV_ABI scePthreadAttrSetschedparam(ScePthreadAttr* attr,
|
||||||
const SceKernelSchedParam* param);
|
const SceKernelSchedParam* param);
|
||||||
int PS4_SYSV_ABI scePthreadAttrSetschedpolicy(ScePthreadAttr* attr, int policy);
|
int PS4_SYSV_ABI scePthreadAttrSetschedpolicy(ScePthreadAttr* attr, int policy);
|
||||||
|
ScePthread PS4_SYSV_ABI scePthreadSelf();
|
||||||
int PS4_SYSV_ABI scePthreadAttrSetaffinity(ScePthreadAttr* pattr,
|
int PS4_SYSV_ABI scePthreadAttrSetaffinity(ScePthreadAttr* pattr,
|
||||||
const /*SceKernelCpumask*/ u64 mask);
|
const /*SceKernelCpumask*/ u64 mask);
|
||||||
int PS4_SYSV_ABI scePthreadAttrSetstacksize(ScePthreadAttr* attr, size_t stack_size);
|
|
||||||
|
|
||||||
ScePthread PS4_SYSV_ABI scePthreadSelf();
|
|
||||||
int PS4_SYSV_ABI scePthreadSetaffinity(ScePthread thread, const /*SceKernelCpumask*/ u64 mask);
|
int PS4_SYSV_ABI scePthreadSetaffinity(ScePthread thread, const /*SceKernelCpumask*/ u64 mask);
|
||||||
int PS4_SYSV_ABI scePthreadGetaffinity(ScePthread thread, /*SceKernelCpumask*/ u64* mask);
|
int PS4_SYSV_ABI scePthreadGetaffinity(ScePthread thread, /*SceKernelCpumask*/ u64* mask);
|
||||||
int PS4_SYSV_ABI scePthreadCreate(ScePthread* thread, const ScePthreadAttr* attr,
|
int PS4_SYSV_ABI scePthreadCreate(ScePthread* thread, const ScePthreadAttr* attr,
|
||||||
PthreadEntryFunc start_routine, void* arg, const char* name);
|
PthreadEntryFunc start_routine, void* arg, const char* name);
|
||||||
[[noreturn]] void PS4_SYSV_ABI scePthreadExit(void* value_ptr);
|
|
||||||
int PS4_SYSV_ABI scePthreadJoin(ScePthread thread, void** res);
|
|
||||||
int PS4_SYSV_ABI scePthreadGetprio(ScePthread thread, int* prio);
|
|
||||||
int PS4_SYSV_ABI scePthreadSetprio(ScePthread thread, int prio);
|
int PS4_SYSV_ABI scePthreadSetprio(ScePthread thread, int prio);
|
||||||
|
|
||||||
/***
|
/***
|
||||||
|
@ -183,14 +178,11 @@ int PS4_SYSV_ABI scePthreadSetprio(ScePthread thread, int prio);
|
||||||
*/
|
*/
|
||||||
int PS4_SYSV_ABI scePthreadMutexInit(ScePthreadMutex* mutex, const ScePthreadMutexattr* attr,
|
int PS4_SYSV_ABI scePthreadMutexInit(ScePthreadMutex* mutex, const ScePthreadMutexattr* attr,
|
||||||
const char* name);
|
const char* name);
|
||||||
int PS4_SYSV_ABI scePthreadMutexDestroy(ScePthreadMutex* mutex);
|
|
||||||
int PS4_SYSV_ABI scePthreadMutexLock(ScePthreadMutex* mutex);
|
|
||||||
int PS4_SYSV_ABI scePthreadMutexUnlock(ScePthreadMutex* mutex);
|
|
||||||
|
|
||||||
int PS4_SYSV_ABI scePthreadMutexattrDestroy(ScePthreadMutexattr* attr);
|
|
||||||
int PS4_SYSV_ABI scePthreadMutexattrInit(ScePthreadMutexattr* attr);
|
int PS4_SYSV_ABI scePthreadMutexattrInit(ScePthreadMutexattr* attr);
|
||||||
int PS4_SYSV_ABI scePthreadMutexattrSettype(ScePthreadMutexattr* attr, int type);
|
int PS4_SYSV_ABI scePthreadMutexattrSettype(ScePthreadMutexattr* attr, int type);
|
||||||
int PS4_SYSV_ABI scePthreadMutexattrSetprotocol(ScePthreadMutexattr* attr, int protocol);
|
int PS4_SYSV_ABI scePthreadMutexattrSetprotocol(ScePthreadMutexattr* attr, int protocol);
|
||||||
|
int PS4_SYSV_ABI scePthreadMutexLock(ScePthreadMutex* mutex);
|
||||||
|
int PS4_SYSV_ABI scePthreadMutexUnlock(ScePthreadMutex* mutex);
|
||||||
/****
|
/****
|
||||||
* Cond calls
|
* Cond calls
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -50,7 +50,6 @@ u64 PS4_SYSV_ABI sceKernelGetProcessTime();
|
||||||
u64 PS4_SYSV_ABI sceKernelGetProcessTimeCounter();
|
u64 PS4_SYSV_ABI sceKernelGetProcessTimeCounter();
|
||||||
u64 PS4_SYSV_ABI sceKernelGetProcessTimeCounterFrequency();
|
u64 PS4_SYSV_ABI sceKernelGetProcessTimeCounterFrequency();
|
||||||
u64 PS4_SYSV_ABI sceKernelReadTsc();
|
u64 PS4_SYSV_ABI sceKernelReadTsc();
|
||||||
int PS4_SYSV_ABI sceKernelUsleep(u32 microseconds);
|
|
||||||
|
|
||||||
void timeSymbolsRegister(Core::Loader::SymbolsResolver* sym);
|
void timeSymbolsRegister(Core::Loader::SymbolsResolver* sym);
|
||||||
|
|
||||||
|
|
|
@ -792,10 +792,11 @@ int PS4_SYSV_ABI sceSaveDataTransferringMount() {
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 PS4_SYSV_ABI sceSaveDataUmount(const OrbisSaveDataMountPoint* mountPoint) {
|
s32 PS4_SYSV_ABI sceSaveDataUmount(const OrbisSaveDataMountPoint* mountPoint) {
|
||||||
LOG_INFO(Lib_SaveData, "mountPoint = {}", std::string(mountPoint->data));
|
if (mountPoint->data == nullptr) {
|
||||||
if (std::string(mountPoint->data).empty()) {
|
LOG_WARNING(Lib_SaveData, "mountPoint = nullptr");
|
||||||
return ORBIS_SAVE_DATA_ERROR_NOT_MOUNTED;
|
return ORBIS_SAVE_DATA_ERROR_NOT_MOUNTED;
|
||||||
}
|
}
|
||||||
|
LOG_INFO(Lib_SaveData, "mountPoint = {}", mountPoint->data);
|
||||||
const auto& mount_dir = Common::FS::GetUserPath(Common::FS::PathType::SaveDataDir) /
|
const auto& mount_dir = Common::FS::GetUserPath(Common::FS::PathType::SaveDataDir) /
|
||||||
std::to_string(1) / game_serial / mountPoint->data;
|
std::to_string(1) / game_serial / mountPoint->data;
|
||||||
auto* mnt = Common::Singleton<Core::FileSys::MntPoints>::Instance();
|
auto* mnt = Common::Singleton<Core::FileSys::MntPoints>::Instance();
|
||||||
|
|
Loading…
Reference in New Issue