avplayer: implemented AddSourceEx, SetAvSyncMode, Pause and Resume (#2456)

* avplayer: code improvements

* avplayer: implemented pause/resume

* avplayer: implemented sync modes

* avplayer: issue warning on loopback

* avplayer: sync on video ts in default mode when audio ts is not available

* avplayer: removed waits for the frame in Get*Data, replaced cv with sleep

* avplayer: removed all waits from GetVideoData

* avplayer: fix warning propagation + small fixes

* Using texture memory for video frames, dropped video frame cache, syncing audio to video

* do not sync to audio when audio is not enabled

* removed logs, fixed sync

* reverted the removal of pre-allocated buffers
This commit is contained in:
Vladislav Mikhalin
2025-09-20 19:27:16 +03:00
committed by GitHub
parent eb18382396
commit 2b1c0b4f82
12 changed files with 452 additions and 373 deletions

View File

@@ -9,23 +9,22 @@
namespace Libraries::AvPlayer { namespace Libraries::AvPlayer {
s32 PS4_SYSV_ABI sceAvPlayerAddSource(SceAvPlayerHandle handle, const char* filename) { s32 PS4_SYSV_ABI sceAvPlayerAddSource(AvPlayerHandle handle, const char* filename) {
LOG_TRACE(Lib_AvPlayer, "filename = {}", filename); LOG_TRACE(Lib_AvPlayer, "filename = {}", filename);
if (handle == nullptr) { if (handle == nullptr) {
return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS;
} }
const auto res = handle->AddSource(filename); return handle->AddSource(filename);
LOG_TRACE(Lib_AvPlayer, "returning {}", res);
return res;
} }
s32 PS4_SYSV_ABI sceAvPlayerAddSourceEx(SceAvPlayerHandle handle, SceAvPlayerUriType uriType, s32 PS4_SYSV_ABI sceAvPlayerAddSourceEx(AvPlayerHandle handle, AvPlayerUriType uri_type,
SceAvPlayerSourceDetails* sourceDetails) { AvPlayerSourceDetails* source_details) {
LOG_ERROR(Lib_AvPlayer, "(STUBBED) called"); LOG_TRACE(Lib_AvPlayer, "called");
if (handle == nullptr) { if (handle == nullptr || uri_type != AvPlayerUriType::Source) {
return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS;
} }
return ORBIS_OK; const auto path = std::string_view(source_details->uri.name, source_details->uri.length);
return handle->AddSourceEx(path, source_details->source_type);
} }
int PS4_SYSV_ABI sceAvPlayerChangeStream() { int PS4_SYSV_ABI sceAvPlayerChangeStream() {
@@ -33,28 +32,24 @@ int PS4_SYSV_ABI sceAvPlayerChangeStream() {
return ORBIS_OK; return ORBIS_OK;
} }
s32 PS4_SYSV_ABI sceAvPlayerClose(SceAvPlayerHandle handle) { s32 PS4_SYSV_ABI sceAvPlayerClose(AvPlayerHandle handle) {
LOG_TRACE(Lib_AvPlayer, "called"); LOG_TRACE(Lib_AvPlayer, "called");
if (handle == nullptr) { if (handle == nullptr) {
LOG_TRACE(Lib_AvPlayer, "returning ORBIS_AVPLAYER_ERROR_INVALID_PARAMS");
return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS;
} }
delete handle; delete handle;
LOG_TRACE(Lib_AvPlayer, "returning ORBIS_OK");
return ORBIS_OK; return ORBIS_OK;
} }
u64 PS4_SYSV_ABI sceAvPlayerCurrentTime(SceAvPlayerHandle handle) { u64 PS4_SYSV_ABI sceAvPlayerCurrentTime(AvPlayerHandle handle) {
LOG_TRACE(Lib_AvPlayer, "called"); LOG_TRACE(Lib_AvPlayer, "called");
if (handle == nullptr) { if (handle == nullptr) {
return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS;
} }
const auto res = handle->CurrentTime(); return handle->CurrentTime();
LOG_TRACE(Lib_AvPlayer, "returning {}", res);
return res;
} }
s32 PS4_SYSV_ABI sceAvPlayerDisableStream(SceAvPlayerHandle handle, u32 stream_id) { s32 PS4_SYSV_ABI sceAvPlayerDisableStream(AvPlayerHandle handle, u32 stream_id) {
LOG_ERROR(Lib_AvPlayer, "(STUBBED) called"); LOG_ERROR(Lib_AvPlayer, "(STUBBED) called");
if (handle == nullptr) { if (handle == nullptr) {
return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS;
@@ -62,60 +57,49 @@ s32 PS4_SYSV_ABI sceAvPlayerDisableStream(SceAvPlayerHandle handle, u32 stream_i
return ORBIS_OK; return ORBIS_OK;
} }
s32 PS4_SYSV_ABI sceAvPlayerEnableStream(SceAvPlayerHandle handle, u32 stream_id) { s32 PS4_SYSV_ABI sceAvPlayerEnableStream(AvPlayerHandle handle, u32 stream_id) {
LOG_TRACE(Lib_AvPlayer, "stream_id = {}", stream_id); LOG_TRACE(Lib_AvPlayer, "stream_id = {}", stream_id);
if (handle == nullptr) { if (handle == nullptr) {
return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS;
} }
const auto res = handle->EnableStream(stream_id); return handle->EnableStream(stream_id);
LOG_TRACE(Lib_AvPlayer, "returning {}", res);
return res;
} }
bool PS4_SYSV_ABI sceAvPlayerGetAudioData(SceAvPlayerHandle handle, SceAvPlayerFrameInfo* p_info) { bool PS4_SYSV_ABI sceAvPlayerGetAudioData(AvPlayerHandle handle, AvPlayerFrameInfo* p_info) {
LOG_TRACE(Lib_AvPlayer, "called"); LOG_TRACE(Lib_AvPlayer, "called");
if (handle == nullptr || p_info == nullptr) { if (handle == nullptr || p_info == nullptr) {
return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; return false;
} }
const auto res = handle->GetAudioData(*p_info); return handle->GetAudioData(*p_info);
LOG_TRACE(Lib_AvPlayer, "returning {}", res);
return res;
} }
s32 PS4_SYSV_ABI sceAvPlayerGetStreamInfo(SceAvPlayerHandle handle, u32 stream_id, s32 PS4_SYSV_ABI sceAvPlayerGetStreamInfo(AvPlayerHandle handle, u32 stream_id,
SceAvPlayerStreamInfo* p_info) { AvPlayerStreamInfo* p_info) {
LOG_TRACE(Lib_AvPlayer, "stream_id = {}", stream_id); LOG_TRACE(Lib_AvPlayer, "stream_id = {}", stream_id);
if (handle == nullptr || p_info == nullptr) { if (handle == nullptr || p_info == nullptr) {
return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS;
} }
const auto res = handle->GetStreamInfo(stream_id, *p_info); return handle->GetStreamInfo(stream_id, *p_info);
LOG_TRACE(Lib_AvPlayer, "returning {}", res);
return res;
} }
bool PS4_SYSV_ABI sceAvPlayerGetVideoData(SceAvPlayerHandle handle, bool PS4_SYSV_ABI sceAvPlayerGetVideoData(AvPlayerHandle handle, AvPlayerFrameInfo* video_info) {
SceAvPlayerFrameInfo* video_info) {
LOG_TRACE(Lib_AvPlayer, "called"); LOG_TRACE(Lib_AvPlayer, "called");
if (handle == nullptr || video_info == nullptr) { if (handle == nullptr || video_info == nullptr) {
return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; return false;
} }
const auto res = handle->GetVideoData(*video_info); return handle->GetVideoData(*video_info);
LOG_TRACE(Lib_AvPlayer, "returning {}", res);
return res;
} }
bool PS4_SYSV_ABI sceAvPlayerGetVideoDataEx(SceAvPlayerHandle handle, bool PS4_SYSV_ABI sceAvPlayerGetVideoDataEx(AvPlayerHandle handle,
SceAvPlayerFrameInfoEx* video_info) { AvPlayerFrameInfoEx* video_info) {
LOG_TRACE(Lib_AvPlayer, "called"); LOG_TRACE(Lib_AvPlayer, "called");
if (handle == nullptr || video_info == nullptr) { if (handle == nullptr || video_info == nullptr) {
return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; return false;
} }
const auto res = handle->GetVideoData(*video_info); return handle->GetVideoData(*video_info);
LOG_TRACE(Lib_AvPlayer, "returning {}", res);
return res;
} }
SceAvPlayerHandle PS4_SYSV_ABI sceAvPlayerInit(SceAvPlayerInitData* data) { AvPlayerHandle PS4_SYSV_ABI sceAvPlayerInit(AvPlayerInitData* data) {
LOG_TRACE(Lib_AvPlayer, "called"); LOG_TRACE(Lib_AvPlayer, "called");
if (data == nullptr) { if (data == nullptr) {
return nullptr; return nullptr;
@@ -125,15 +109,14 @@ SceAvPlayerHandle PS4_SYSV_ABI sceAvPlayerInit(SceAvPlayerInitData* data) {
data->memory_replacement.allocate_texture == nullptr || data->memory_replacement.allocate_texture == nullptr ||
data->memory_replacement.deallocate == nullptr || data->memory_replacement.deallocate == nullptr ||
data->memory_replacement.deallocate_texture == nullptr) { data->memory_replacement.deallocate_texture == nullptr) {
LOG_ERROR(Lib_AvPlayer, "All allocators are required for AVPlayer Initialisation."); LOG_ERROR(Lib_AvPlayer, "All allocators are required for AvPlayer Initialisation.");
return nullptr; return nullptr;
} }
return new AvPlayer(*data); return new AvPlayer(*data);
} }
s32 PS4_SYSV_ABI sceAvPlayerInitEx(const SceAvPlayerInitDataEx* p_data, s32 PS4_SYSV_ABI sceAvPlayerInitEx(const AvPlayerInitDataEx* p_data, AvPlayerHandle* p_player) {
SceAvPlayerHandle* p_player) {
LOG_TRACE(Lib_AvPlayer, "called"); LOG_TRACE(Lib_AvPlayer, "called");
if (p_data == nullptr || p_player == nullptr) { if (p_data == nullptr || p_player == nullptr) {
return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS;
@@ -143,11 +126,11 @@ s32 PS4_SYSV_ABI sceAvPlayerInitEx(const SceAvPlayerInitDataEx* p_data,
p_data->memory_replacement.allocate_texture == nullptr || p_data->memory_replacement.allocate_texture == nullptr ||
p_data->memory_replacement.deallocate == nullptr || p_data->memory_replacement.deallocate == nullptr ||
p_data->memory_replacement.deallocate_texture == nullptr) { p_data->memory_replacement.deallocate_texture == nullptr) {
LOG_ERROR(Lib_AvPlayer, "All allocators are required for AVPlayer Initialisation."); LOG_ERROR(Lib_AvPlayer, "All allocators are required for AvPlayer Initialisation.");
return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS;
} }
SceAvPlayerInitData data = {}; AvPlayerInitData data = {};
data.memory_replacement = p_data->memory_replacement; data.memory_replacement = p_data->memory_replacement;
data.file_replacement = p_data->file_replacement; data.file_replacement = p_data->file_replacement;
data.event_replacement = p_data->event_replacement; data.event_replacement = p_data->event_replacement;
@@ -159,18 +142,15 @@ s32 PS4_SYSV_ABI sceAvPlayerInitEx(const SceAvPlayerInitDataEx* p_data,
return ORBIS_OK; return ORBIS_OK;
} }
bool PS4_SYSV_ABI sceAvPlayerIsActive(SceAvPlayerHandle handle) { bool PS4_SYSV_ABI sceAvPlayerIsActive(AvPlayerHandle handle) {
LOG_TRACE(Lib_AvPlayer, "called"); LOG_TRACE(Lib_AvPlayer, "called");
if (handle == nullptr) { if (handle == nullptr) {
LOG_TRACE(Lib_AvPlayer, "returning false");
return false; return false;
} }
const auto res = handle->IsActive(); return handle->IsActive();
LOG_TRACE(Lib_AvPlayer, "returning {}", res);
return res;
} }
s32 PS4_SYSV_ABI sceAvPlayerJumpToTime(SceAvPlayerHandle handle, uint64_t time) { s32 PS4_SYSV_ABI sceAvPlayerJumpToTime(AvPlayerHandle handle, uint64_t time) {
LOG_ERROR(Lib_AvPlayer, "(STUBBED) called, time (msec) = {}", time); LOG_ERROR(Lib_AvPlayer, "(STUBBED) called, time (msec) = {}", time);
if (handle == nullptr) { if (handle == nullptr) {
return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS;
@@ -178,22 +158,20 @@ s32 PS4_SYSV_ABI sceAvPlayerJumpToTime(SceAvPlayerHandle handle, uint64_t time)
return ORBIS_OK; return ORBIS_OK;
} }
s32 PS4_SYSV_ABI sceAvPlayerPause(SceAvPlayerHandle handle) { s32 PS4_SYSV_ABI sceAvPlayerPause(AvPlayerHandle 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;
} }
return ORBIS_OK; return handle->Pause();
} }
s32 PS4_SYSV_ABI sceAvPlayerPostInit(SceAvPlayerHandle handle, SceAvPlayerPostInitData* data) { s32 PS4_SYSV_ABI sceAvPlayerPostInit(AvPlayerHandle handle, AvPlayerPostInitData* data) {
LOG_TRACE(Lib_AvPlayer, "called"); LOG_TRACE(Lib_AvPlayer, "called");
if (handle == nullptr || data == nullptr) { if (handle == nullptr || data == nullptr) {
return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS;
} }
const auto res = handle->PostInit(*data); return handle->PostInit(*data);
LOG_TRACE(Lib_AvPlayer, "returning {}", res);
return res;
} }
s32 PS4_SYSV_ABI sceAvPlayerPrintf(const char* format, ...) { s32 PS4_SYSV_ABI sceAvPlayerPrintf(const char* format, ...) {
@@ -201,29 +179,28 @@ s32 PS4_SYSV_ABI sceAvPlayerPrintf(const char* format, ...) {
return ORBIS_OK; return ORBIS_OK;
} }
s32 PS4_SYSV_ABI sceAvPlayerResume(SceAvPlayerHandle handle) { s32 PS4_SYSV_ABI sceAvPlayerResume(AvPlayerHandle 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;
} }
return ORBIS_OK; return handle->Resume();
} }
s32 PS4_SYSV_ABI sceAvPlayerSetAvSyncMode(SceAvPlayerHandle handle, s32 PS4_SYSV_ABI sceAvPlayerSetAvSyncMode(AvPlayerHandle handle, AvPlayerAvSyncMode sync_mode) {
SceAvPlayerAvSyncMode sync_mode) { LOG_TRACE(Lib_AvPlayer, "called");
LOG_ERROR(Lib_AvPlayer, "(STUBBED) called");
if (handle == nullptr) { if (handle == nullptr) {
return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS;
} }
return ORBIS_OK; return handle->SetAvSyncMode(sync_mode);
} }
s32 PS4_SYSV_ABI sceAvPlayerSetLogCallback(SceAvPlayerLogCallback log_cb, void* user_data) { s32 PS4_SYSV_ABI sceAvPlayerSetLogCallback(AvPlayerLogCallback log_cb, void* user_data) {
LOG_ERROR(Lib_AvPlayer, "(STUBBED) called"); LOG_ERROR(Lib_AvPlayer, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
s32 PS4_SYSV_ABI sceAvPlayerSetLooping(SceAvPlayerHandle handle, bool loop_flag) { s32 PS4_SYSV_ABI sceAvPlayerSetLooping(AvPlayerHandle handle, bool loop_flag) {
LOG_TRACE(Lib_AvPlayer, "called, looping = {}", loop_flag); LOG_TRACE(Lib_AvPlayer, "called, looping = {}", loop_flag);
if (handle == nullptr) { if (handle == nullptr) {
return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS;
@@ -234,43 +211,36 @@ s32 PS4_SYSV_ABI sceAvPlayerSetLooping(SceAvPlayerHandle handle, bool loop_flag)
return ORBIS_OK; return ORBIS_OK;
} }
s32 PS4_SYSV_ABI sceAvPlayerSetTrickSpeed(SceAvPlayerHandle handle, s32 trick_speed) { s32 PS4_SYSV_ABI sceAvPlayerSetTrickSpeed(AvPlayerHandle handle, s32 trick_speed) {
LOG_ERROR(Lib_AvPlayer, "(STUBBED) called"); LOG_ERROR(Lib_AvPlayer, "(STUBBED) called speed = {}", trick_speed);
if (handle == nullptr) { if (handle == nullptr) {
return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS;
} }
return ORBIS_OK; return ORBIS_OK;
} }
s32 PS4_SYSV_ABI sceAvPlayerStart(SceAvPlayerHandle handle) { s32 PS4_SYSV_ABI sceAvPlayerStart(AvPlayerHandle handle) {
LOG_TRACE(Lib_AvPlayer, "called"); LOG_TRACE(Lib_AvPlayer, "called");
if (handle == nullptr) { if (handle == nullptr) {
return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS;
} }
const auto res = handle->Start(); return handle->Start();
LOG_TRACE(Lib_AvPlayer, "returning {}", res);
return res;
} }
s32 PS4_SYSV_ABI sceAvPlayerStop(SceAvPlayerHandle handle) { s32 PS4_SYSV_ABI sceAvPlayerStop(AvPlayerHandle handle) {
LOG_TRACE(Lib_AvPlayer, "called"); LOG_TRACE(Lib_AvPlayer, "called");
if (handle == nullptr) { if (handle == nullptr) {
LOG_TRACE(Lib_AvPlayer, "returning ORBIS_AVPLAYER_ERROR_INVALID_PARAMS");
return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS;
} }
const auto res = handle->Stop(); return handle->Stop();
LOG_TRACE(Lib_AvPlayer, "returning {}", res);
return res;
} }
s32 PS4_SYSV_ABI sceAvPlayerStreamCount(SceAvPlayerHandle handle) { s32 PS4_SYSV_ABI sceAvPlayerStreamCount(AvPlayerHandle handle) {
LOG_TRACE(Lib_AvPlayer, "called"); LOG_TRACE(Lib_AvPlayer, "called");
if (handle == nullptr) { if (handle == nullptr) {
return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS;
} }
const auto res = handle->GetStreamCount(); return handle->GetStreamCount();
LOG_TRACE(Lib_AvPlayer, "returning {}", res);
return res;
} }
s32 PS4_SYSV_ABI sceAvPlayerVprintf(const char* format, va_list args) { s32 PS4_SYSV_ABI sceAvPlayerVprintf(const char* format, va_list args) {

View File

@@ -16,38 +16,38 @@ namespace Libraries::AvPlayer {
class AvPlayer; class AvPlayer;
using SceAvPlayerHandle = AvPlayer*; using AvPlayerHandle = AvPlayer*;
enum class SceAvPlayerUriType : u32 { enum class AvPlayerUriType : u32 {
Source = 0, Source = 0,
}; };
struct SceAvPlayerUri { struct AvPlayerUri {
const char* name; const char* name;
u32 length; u32 length;
}; };
enum class SceAvPlayerSourceType { enum class AvPlayerSourceType {
Unknown = 0, Unknown = 0,
FileMp4 = 1, FileMp4 = 1,
Hls = 8, Hls = 8,
}; };
enum class SceAvPlayerStreamType : u32 { enum class AvPlayerStreamType : u32 {
Video, Video,
Audio, Audio,
TimedText, TimedText,
Unknown, Unknown,
}; };
struct SceAvPlayerSourceDetails { struct AvPlayerSourceDetails {
SceAvPlayerUri uri; AvPlayerUri uri;
u8 reserved1[64]; u8 reserved1[64];
SceAvPlayerSourceType source_type; AvPlayerSourceType source_type;
u8 reserved2[44]; u8 reserved2[44];
}; };
struct SceAvPlayerAudio { struct AvPlayerAudio {
u16 channel_count; u16 channel_count;
u8 reserved1[2]; u8 reserved1[2];
u32 sample_rate; u32 sample_rate;
@@ -55,50 +55,50 @@ struct SceAvPlayerAudio {
u8 language_code[4]; u8 language_code[4];
}; };
struct SceAvPlayerVideo { struct AvPlayerVideo {
u32 width; u32 width;
u32 height; u32 height;
f32 aspect_ratio; f32 aspect_ratio;
char language_code[4]; char language_code[4];
}; };
struct SceAvPlayerTextPosition { struct AvPlayerTextPosition {
u16 top; u16 top;
u16 left; u16 left;
u16 bottom; u16 bottom;
u16 right; u16 right;
}; };
struct SceAvPlayerTimedText { struct AvPlayerTimedText {
u8 language_code[4]; u8 language_code[4];
u16 text_size; u16 text_size;
u16 font_size; u16 font_size;
SceAvPlayerTextPosition position; AvPlayerTextPosition position;
}; };
union SceAvPlayerStreamDetails { union AvPlayerStreamDetails {
u8 reserved[16]; u8 reserved[16];
SceAvPlayerAudio audio; AvPlayerAudio audio;
SceAvPlayerVideo video; AvPlayerVideo video;
SceAvPlayerTimedText subs; AvPlayerTimedText subs;
}; };
struct SceAvPlayerFrameInfo { struct AvPlayerFrameInfo {
u8* pData; u8* p_data;
u8 reserved[4]; u8 reserved[4];
u64 timestamp; u64 timestamp;
SceAvPlayerStreamDetails details; AvPlayerStreamDetails details;
}; };
struct SceAvPlayerStreamInfo { struct AvPlayerStreamInfo {
SceAvPlayerStreamType type; AvPlayerStreamType type;
u8 reserved[4]; u8 reserved[4];
SceAvPlayerStreamDetails details; AvPlayerStreamDetails details;
u64 duration; u64 duration;
u64 start_time; u64 start_time;
}; };
struct SceAvPlayerAudioEx { struct AvPlayerAudioEx {
u16 channel_count; u16 channel_count;
u8 reserved[2]; u8 reserved[2];
u32 sample_rate; u32 sample_rate;
@@ -107,7 +107,7 @@ struct SceAvPlayerAudioEx {
u8 reserved1[64]; u8 reserved1[64];
}; };
struct SceAvPlayerVideoEx { struct AvPlayerVideoEx {
u32 width; u32 width;
u32 height; u32 height;
f32 aspect_ratio; f32 aspect_ratio;
@@ -124,53 +124,53 @@ struct SceAvPlayerVideoEx {
u8 reserved1[37]; u8 reserved1[37];
}; };
struct SceAvPlayerTimedTextEx { struct AvPlayerTimedTextEx {
u8 language_code[4]; u8 language_code[4];
u8 reserved[12]; u8 reserved[12];
u8 reserved1[64]; u8 reserved1[64];
}; };
union SceAvPlayerStreamDetailsEx { union AvPlayerStreamDetailsEx {
SceAvPlayerAudioEx audio; AvPlayerAudioEx audio;
SceAvPlayerVideoEx video; AvPlayerVideoEx video;
SceAvPlayerTimedTextEx subs; AvPlayerTimedTextEx subs;
u8 reserved1[80]; u8 reserved1[80];
}; };
struct SceAvPlayerFrameInfoEx { struct AvPlayerFrameInfoEx {
void* pData; void* p_data;
u8 reserved[4]; u8 reserved[4];
u64 timestamp; u64 timestamp;
SceAvPlayerStreamDetailsEx details; AvPlayerStreamDetailsEx details;
}; };
using SceAvPlayerAllocate = void* PS4_SYSV_ABI (*)(void* p, u32 align, u32 size); using AvPlayerAllocate = void* PS4_SYSV_ABI (*)(void* p, u32 align, u32 size);
using SceAvPlayerDeallocate = void PS4_SYSV_ABI (*)(void* p, void* mem); using AvPlayerDeallocate = void PS4_SYSV_ABI (*)(void* p, void* mem);
using SceAvPlayerAllocateTexture = void* PS4_SYSV_ABI (*)(void* p, u32 align, u32 size); using AvPlayerAllocateTexture = void* PS4_SYSV_ABI (*)(void* p, u32 align, u32 size);
using SceAvPlayerDeallocateTexture = void PS4_SYSV_ABI (*)(void* p, void* mem); using AvPlayerDeallocateTexture = void PS4_SYSV_ABI (*)(void* p, void* mem);
struct SceAvPlayerMemAllocator { struct AvPlayerMemAllocator {
void* object_ptr; void* object_ptr;
SceAvPlayerAllocate allocate; AvPlayerAllocate allocate;
SceAvPlayerDeallocate deallocate; AvPlayerDeallocate deallocate;
SceAvPlayerAllocateTexture allocate_texture; AvPlayerAllocateTexture allocate_texture;
SceAvPlayerDeallocateTexture deallocate_texture; AvPlayerDeallocateTexture deallocate_texture;
}; };
using SceAvPlayerOpenFile = s32 PS4_SYSV_ABI (*)(void* p, const char* name); using AvPlayerOpenFile = s32 PS4_SYSV_ABI (*)(void* p, const char* name);
using SceAvPlayerCloseFile = s32 PS4_SYSV_ABI (*)(void* p); using AvPlayerCloseFile = s32 PS4_SYSV_ABI (*)(void* p);
using SceAvPlayerReadOffsetFile = s32 PS4_SYSV_ABI (*)(void* p, u8* buf, u64 pos, u32 len); using AvPlayerReadOffsetFile = s32 PS4_SYSV_ABI (*)(void* p, u8* buf, u64 pos, u32 len);
using SceAvPlayerSizeFile = u64 PS4_SYSV_ABI (*)(void* p); using AvPlayerSizeFile = u64 PS4_SYSV_ABI (*)(void* p);
struct SceAvPlayerFileReplacement { struct AvPlayerFileReplacement {
void* object_ptr; void* object_ptr;
SceAvPlayerOpenFile open; AvPlayerOpenFile open;
SceAvPlayerCloseFile close; AvPlayerCloseFile close;
SceAvPlayerReadOffsetFile readOffset; AvPlayerReadOffsetFile read_offset;
SceAvPlayerSizeFile size; AvPlayerSizeFile size;
}; };
enum class SceAvPlayerEvents { enum class AvPlayerEvents {
StateStop = 0x01, StateStop = 0x01,
StateReady = 0x02, StateReady = 0x02,
StatePlay = 0x03, StatePlay = 0x03,
@@ -182,26 +182,26 @@ enum class SceAvPlayerEvents {
DrmError = 0x40, DrmError = 0x40,
}; };
using SceAvPlayerEventCallback = void PS4_SYSV_ABI (*)(void* p, SceAvPlayerEvents event, s32 src_id, using AvPlayerEventCallback = void PS4_SYSV_ABI (*)(void* p, AvPlayerEvents event, s32 src_id,
void* data); void* data);
struct SceAvPlayerEventReplacement { struct AvPlayerEventReplacement {
void* object_ptr; void* object_ptr;
SceAvPlayerEventCallback event_callback; AvPlayerEventCallback event_callback;
}; };
enum class SceAvPlayerDebuglevels { enum class AvPlayerDebuglevels {
None, None,
Info, Info,
Warnings, Warnings,
All, All,
}; };
struct SceAvPlayerInitData { struct AvPlayerInitData {
SceAvPlayerMemAllocator memory_replacement; AvPlayerMemAllocator memory_replacement;
SceAvPlayerFileReplacement file_replacement; AvPlayerFileReplacement file_replacement;
SceAvPlayerEventReplacement event_replacement; AvPlayerEventReplacement event_replacement;
SceAvPlayerDebuglevels debug_level; AvPlayerDebuglevels debug_level;
u32 base_priority; u32 base_priority;
s32 num_output_video_framebuffers; s32 num_output_video_framebuffers;
bool auto_start; bool auto_start;
@@ -209,13 +209,13 @@ struct SceAvPlayerInitData {
const char* default_language; const char* default_language;
}; };
struct SceAvPlayerInitDataEx { struct AvPlayerInitDataEx {
size_t this_size; size_t this_size;
SceAvPlayerMemAllocator memory_replacement; AvPlayerMemAllocator memory_replacement;
SceAvPlayerFileReplacement file_replacement; AvPlayerFileReplacement file_replacement;
SceAvPlayerEventReplacement event_replacement; AvPlayerEventReplacement event_replacement;
const char* default_language; const char* default_language;
SceAvPlayerDebuglevels debug_level; AvPlayerDebuglevels debug_level;
u32 audio_decoder_priority; u32 audio_decoder_priority;
u32 audio_decoder_affinity; u32 audio_decoder_affinity;
u32 video_decoder_priority; u32 video_decoder_priority;
@@ -233,25 +233,25 @@ struct SceAvPlayerInitDataEx {
u8 reserved[3]; u8 reserved[3];
}; };
enum class SceAvPlayerVideoDecoderType { enum class AvPlayerVideoDecoderType {
Default = 0, Default = 0,
Reserved1, Reserved1,
Software, Software,
Software2, Software2,
}; };
enum class SceAvPlayerAudioDecoderType { enum class AvPlayerAudioDecoderType {
Default = 0, Default = 0,
Reserved1, Reserved1,
Reserved2, Reserved2,
}; };
struct SceAvPlayerDecoderInit { struct AvPlayerDecoderInit {
union { union {
SceAvPlayerVideoDecoderType video_type; AvPlayerVideoDecoderType video_type;
SceAvPlayerAudioDecoderType audio_type; AvPlayerAudioDecoderType audio_type;
u8 reserved[4]; u8 reserved[4];
} decoderType; } decoder_type;
union { union {
struct { struct {
s32 cpu_affinity_mask; s32 cpu_affinity_mask;
@@ -261,34 +261,34 @@ struct SceAvPlayerDecoderInit {
u8 compute_queue_id; u8 compute_queue_id;
u8 enable_interlaced; u8 enable_interlaced;
u8 reserved[16]; u8 reserved[16];
} avcSw2; } avc_sw2;
struct { struct {
u8 audio_channel_order; u8 audio_channel_order;
u8 reserved[27]; u8 reserved[27];
} aac; } aac;
u8 reserved[28]; u8 reserved[28];
} decoderParams; } decoder_params;
}; };
struct SceAvPlayerHTTPCtx { struct AvPlayerHTTPCtx {
u32 http_context_id; u32 http_context_id;
u32 ssl_context_id; u32 ssl_context_id;
}; };
struct SceAvPlayerPostInitData { struct AvPlayerPostInitData {
u32 demux_video_buffer_size; u32 demux_video_buffer_size;
SceAvPlayerDecoderInit video_decoder_init; AvPlayerDecoderInit video_decoder_init;
SceAvPlayerDecoderInit audio_decoder_init; AvPlayerDecoderInit audio_decoder_init;
SceAvPlayerHTTPCtx http_context; AvPlayerHTTPCtx http_context;
u8 reserved[56]; u8 reserved[56];
}; };
enum class SceAvPlayerAvSyncMode { enum class AvPlayerAvSyncMode {
Default = 0, Default = 0,
None, None,
}; };
using SceAvPlayerLogCallback = int PS4_SYSV_ABI (*)(void* p, const char* format, va_list args); using AvPlayerLogCallback = int PS4_SYSV_ABI (*)(void* p, const char* format, va_list args);
void RegisterLib(Core::Loader::SymbolsResolver* sym); void RegisterLib(Core::Loader::SymbolsResolver* sym);

View File

@@ -13,9 +13,9 @@ static bool iequals(std::string_view l, std::string_view r) {
return std::ranges::equal(l, r, [](u8 a, u8 b) { return std::tolower(a) == std::tolower(b); }); return std::ranges::equal(l, r, [](u8 a, u8 b) { return std::tolower(a) == std::tolower(b); });
} }
SceAvPlayerSourceType GetSourceType(std::string_view path) { AvPlayerSourceType GetSourceType(std::string_view path) {
if (path.empty()) { if (path.empty()) {
return SceAvPlayerSourceType::Unknown; return AvPlayerSourceType::Unknown;
} }
std::string_view name = path; std::string_view name = path;
@@ -25,14 +25,14 @@ SceAvPlayerSourceType GetSourceType(std::string_view path) {
// -> schema://server.domain/path/to/file.ext/and/beyond // -> schema://server.domain/path/to/file.ext/and/beyond
name = path.substr(0, path.find_first_of("?#")); name = path.substr(0, path.find_first_of("?#"));
if (name.empty()) { if (name.empty()) {
return SceAvPlayerSourceType::Unknown; return AvPlayerSourceType::Unknown;
} }
} }
// schema://server.domain/path/to/file.ext/and/beyond -> .ext/and/beyond // schema://server.domain/path/to/file.ext/and/beyond -> .ext/and/beyond
auto ext = name.substr(name.rfind('.')); auto ext = name.substr(name.rfind('.'));
if (ext.empty()) { if (ext.empty()) {
return SceAvPlayerSourceType::Unknown; return AvPlayerSourceType::Unknown;
} }
// .ext/and/beyond -> .ext // .ext/and/beyond -> .ext
@@ -40,14 +40,14 @@ SceAvPlayerSourceType GetSourceType(std::string_view path) {
if (iequals(ext, ".mp4") || iequals(ext, ".m4v") || iequals(ext, ".m3d") || if (iequals(ext, ".mp4") || iequals(ext, ".m4v") || iequals(ext, ".m3d") ||
iequals(ext, ".m4a") || iequals(ext, ".mov")) { iequals(ext, ".m4a") || iequals(ext, ".mov")) {
return SceAvPlayerSourceType::FileMp4; return AvPlayerSourceType::FileMp4;
} }
if (iequals(ext, ".m3u8")) { if (iequals(ext, ".m3u8")) {
return SceAvPlayerSourceType::Hls; return AvPlayerSourceType::Hls;
} }
return SceAvPlayerSourceType::Unknown; return AvPlayerSourceType::Unknown;
} }
} // namespace Libraries::AvPlayer } // namespace Libraries::AvPlayer

View File

@@ -16,6 +16,7 @@
namespace Libraries::AvPlayer { namespace Libraries::AvPlayer {
enum class AvState { enum class AvState {
Unknown,
Initial, Initial,
AddingSource, AddingSource,
Ready, Ready,
@@ -64,6 +65,10 @@ public:
m_queue.emplace(std::forward<T>(value)); m_queue.emplace(std::forward<T>(value));
} }
T& Front() {
return m_queue.front();
}
std::optional<T> Pop() { std::optional<T> Pop() {
if (Size() == 0) { if (Size() == 0) {
return std::nullopt; return std::nullopt;
@@ -84,6 +89,6 @@ private:
std::queue<T> m_queue{}; std::queue<T> m_queue{};
}; };
SceAvPlayerSourceType GetSourceType(std::string_view path); AvPlayerSourceType GetSourceType(std::string_view path);
} // namespace Libraries::AvPlayer } // namespace Libraries::AvPlayer

View File

@@ -14,7 +14,7 @@ constexpr u32 AVPLAYER_AVIO_BUFFER_SIZE = 4096;
namespace Libraries::AvPlayer { namespace Libraries::AvPlayer {
AvPlayerFileStreamer::AvPlayerFileStreamer(const SceAvPlayerFileReplacement& file_replacement) AvPlayerFileStreamer::AvPlayerFileStreamer(const AvPlayerFileReplacement& file_replacement)
: m_file_replacement(file_replacement) {} : m_file_replacement(file_replacement) {}
AvPlayerFileStreamer::~AvPlayerFileStreamer() { AvPlayerFileStreamer::~AvPlayerFileStreamer() {
@@ -51,7 +51,7 @@ s32 AvPlayerFileStreamer::ReadPacket(void* opaque, u8* buffer, s32 size) {
if (self->m_position + size > self->m_file_size) { if (self->m_position + size > self->m_file_size) {
size = self->m_file_size - self->m_position; size = self->m_file_size - self->m_position;
} }
const auto read_offset = self->m_file_replacement.readOffset; const auto read_offset = self->m_file_replacement.read_offset;
const auto ptr = self->m_file_replacement.object_ptr; const auto ptr = self->m_file_replacement.object_ptr;
const auto bytes_read = read_offset(ptr, buffer, self->m_position, size); const auto bytes_read = read_offset(ptr, buffer, self->m_position, size);
if (bytes_read == 0 && size != 0) { if (bytes_read == 0 && size != 0) {

View File

@@ -13,7 +13,7 @@ namespace Libraries::AvPlayer {
class AvPlayerFileStreamer : public IDataStreamer { class AvPlayerFileStreamer : public IDataStreamer {
public: public:
AvPlayerFileStreamer(const SceAvPlayerFileReplacement& file_replacement); AvPlayerFileStreamer(const AvPlayerFileReplacement& file_replacement);
~AvPlayerFileStreamer(); ~AvPlayerFileStreamer();
bool Init(std::string_view path) override; bool Init(std::string_view path) override;
@@ -26,7 +26,7 @@ private:
static s32 ReadPacket(void* opaque, u8* buffer, s32 size); static s32 ReadPacket(void* opaque, u8* buffer, s32 size);
static s64 Seek(void* opaque, s64 buffer, int whence); static s64 Seek(void* opaque, s64 buffer, int whence);
SceAvPlayerFileReplacement m_file_replacement; AvPlayerFileReplacement m_file_replacement;
int m_fd = -1; int m_fd = -1;
u64 m_position{}; u64 m_position{};

View File

@@ -58,7 +58,7 @@ int PS4_SYSV_ABI AvPlayer::ReadOffsetFile(void* handle, u8* buffer, u64 position
auto const self = reinterpret_cast<AvPlayer*>(handle); auto const self = reinterpret_cast<AvPlayer*>(handle);
std::lock_guard guard(self->m_file_io_mutex); std::lock_guard guard(self->m_file_io_mutex);
const auto read_offset = self->m_init_data_original.file_replacement.readOffset; const auto read_offset = self->m_init_data_original.file_replacement.read_offset;
const auto ptr = self->m_init_data_original.file_replacement.object_ptr; const auto ptr = self->m_init_data_original.file_replacement.object_ptr;
return Core::ExecuteGuest(read_offset, ptr, buffer, position, length); return Core::ExecuteGuest(read_offset, ptr, buffer, position, length);
} }
@@ -72,38 +72,46 @@ u64 PS4_SYSV_ABI AvPlayer::SizeFile(void* handle) {
return Core::ExecuteGuest(size, ptr); return Core::ExecuteGuest(size, ptr);
} }
SceAvPlayerInitData AvPlayer::StubInitData(const SceAvPlayerInitData& data) { AvPlayerInitData AvPlayer::StubInitData(const AvPlayerInitData& data) {
SceAvPlayerInitData result = data; AvPlayerInitData result = data;
result.memory_replacement.object_ptr = this; result.memory_replacement.object_ptr = this;
result.memory_replacement.allocate = &AvPlayer::Allocate; result.memory_replacement.allocate = &AvPlayer::Allocate;
result.memory_replacement.deallocate = &AvPlayer::Deallocate; result.memory_replacement.deallocate = &AvPlayer::Deallocate;
result.memory_replacement.allocate_texture = &AvPlayer::AllocateTexture; result.memory_replacement.allocate_texture = &AvPlayer::AllocateTexture;
result.memory_replacement.deallocate_texture = &AvPlayer::DeallocateTexture; result.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.read_offset == nullptr || data.file_replacement.size == nullptr) {
result.file_replacement = {}; result.file_replacement = {};
} else { } else {
result.file_replacement.object_ptr = this; result.file_replacement.object_ptr = this;
result.file_replacement.open = &AvPlayer::OpenFile; result.file_replacement.open = &AvPlayer::OpenFile;
result.file_replacement.close = &AvPlayer::CloseFile; result.file_replacement.close = &AvPlayer::CloseFile;
result.file_replacement.readOffset = &AvPlayer::ReadOffsetFile; result.file_replacement.read_offset = &AvPlayer::ReadOffsetFile;
result.file_replacement.size = &AvPlayer::SizeFile; result.file_replacement.size = &AvPlayer::SizeFile;
} }
return result; return result;
} }
AvPlayer::AvPlayer(const SceAvPlayerInitData& data) AvPlayer::AvPlayer(const AvPlayerInitData& data)
: m_init_data(StubInitData(data)), m_init_data_original(data), : m_init_data(StubInitData(data)), m_init_data_original(data),
m_state(std::make_unique<AvPlayerState>(m_init_data)) {} m_state(std::make_unique<AvPlayerState>(m_init_data)) {}
s32 AvPlayer::PostInit(const SceAvPlayerPostInitData& data) { s32 AvPlayer::PostInit(const AvPlayerPostInitData& data) {
m_state->PostInit(data); m_state->PostInit(data);
return ORBIS_OK; return ORBIS_OK;
} }
s32 AvPlayer::AddSource(std::string_view path) { s32 AvPlayer::AddSource(std::string_view path) {
if (path.empty()) { return AddSourceEx(path, AvPlayerSourceType::Unknown);
return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; }
s32 AvPlayer::AddSourceEx(std::string_view path, AvPlayerSourceType source_type) {
if (source_type == AvPlayerSourceType::Unknown) {
source_type = GetSourceType(path);
}
if (source_type == AvPlayerSourceType::Hls) {
LOG_ERROR(Lib_AvPlayer, "HTTP Live Streaming is not implemented");
return ORBIS_AVPLAYER_ERROR_NOT_SUPPORTED;
} }
if (!m_state->AddSource(path, GetSourceType(path))) { if (!m_state->AddSource(path, GetSourceType(path))) {
return ORBIS_AVPLAYER_ERROR_OPERATION_FAILED; return ORBIS_AVPLAYER_ERROR_OPERATION_FAILED;
@@ -122,7 +130,7 @@ s32 AvPlayer::GetStreamCount() {
return res; return res;
} }
s32 AvPlayer::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info) { s32 AvPlayer::GetStreamInfo(u32 stream_index, AvPlayerStreamInfo& info) {
if (!m_state->GetStreamInfo(stream_index, info)) { if (!m_state->GetStreamInfo(stream_index, info)) {
return ORBIS_AVPLAYER_ERROR_OPERATION_FAILED; return ORBIS_AVPLAYER_ERROR_OPERATION_FAILED;
} }
@@ -140,27 +148,49 @@ s32 AvPlayer::EnableStream(u32 stream_index) {
} }
s32 AvPlayer::Start() { s32 AvPlayer::Start() {
if (!m_state->Start()) { if (m_state == nullptr || !m_state->Start()) {
return ORBIS_AVPLAYER_ERROR_OPERATION_FAILED; return ORBIS_AVPLAYER_ERROR_OPERATION_FAILED;
} }
return ORBIS_OK; return ORBIS_OK;
} }
bool AvPlayer::GetVideoData(SceAvPlayerFrameInfo& video_info) { s32 AvPlayer::Pause() {
if (m_state == nullptr || !m_state->Pause()) {
return ORBIS_AVPLAYER_ERROR_OPERATION_FAILED;
}
return ORBIS_OK;
}
s32 AvPlayer::Resume() {
if (m_state == nullptr || !m_state->Resume()) {
return ORBIS_AVPLAYER_ERROR_OPERATION_FAILED;
}
return ORBIS_OK;
}
s32 AvPlayer::SetAvSyncMode(AvPlayerAvSyncMode sync_mode) {
if (m_state == nullptr) {
return ORBIS_AVPLAYER_ERROR_OPERATION_FAILED;
}
m_state->SetAvSyncMode(sync_mode);
return ORBIS_OK;
}
bool AvPlayer::GetVideoData(AvPlayerFrameInfo& video_info) {
if (m_state == nullptr) { if (m_state == nullptr) {
return false; return false;
} }
return m_state->GetVideoData(video_info); return m_state->GetVideoData(video_info);
} }
bool AvPlayer::GetVideoData(SceAvPlayerFrameInfoEx& video_info) { bool AvPlayer::GetVideoData(AvPlayerFrameInfoEx& video_info) {
if (m_state == nullptr) { if (m_state == nullptr) {
return false; return false;
} }
return m_state->GetVideoData(video_info); return m_state->GetVideoData(video_info);
} }
bool AvPlayer::GetAudioData(SceAvPlayerFrameInfo& audio_info) { bool AvPlayer::GetAudioData(AvPlayerFrameInfo& audio_info) {
if (m_state == nullptr) { if (m_state == nullptr) {
return false; return false;
} }

View File

@@ -19,17 +19,21 @@ namespace Libraries::AvPlayer {
class AvPlayer { class AvPlayer {
public: public:
AvPlayer(const SceAvPlayerInitData& data); AvPlayer(const AvPlayerInitData& data);
s32 PostInit(const SceAvPlayerPostInitData& data); s32 PostInit(const AvPlayerPostInitData& data);
s32 AddSource(std::string_view filename); s32 AddSource(std::string_view filename);
s32 AddSourceEx(std::string_view path, AvPlayerSourceType source_type);
s32 GetStreamCount(); s32 GetStreamCount();
s32 GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info); s32 GetStreamInfo(u32 stream_index, AvPlayerStreamInfo& info);
s32 EnableStream(u32 stream_index); s32 EnableStream(u32 stream_index);
s32 Start(); s32 Start();
bool GetAudioData(SceAvPlayerFrameInfo& audio_info); s32 Pause();
bool GetVideoData(SceAvPlayerFrameInfo& video_info); s32 Resume();
bool GetVideoData(SceAvPlayerFrameInfoEx& video_info); s32 SetAvSyncMode(AvPlayerAvSyncMode sync_mode);
bool GetAudioData(AvPlayerFrameInfo& audio_info);
bool GetVideoData(AvPlayerFrameInfo& video_info);
bool GetVideoData(AvPlayerFrameInfoEx& video_info);
bool IsActive(); bool IsActive();
u64 CurrentTime(); u64 CurrentTime();
s32 Stop(); s32 Stop();
@@ -48,13 +52,12 @@ private:
static int PS4_SYSV_ABI ReadOffsetFile(void* handle, u8* buffer, u64 position, u32 length); static int PS4_SYSV_ABI ReadOffsetFile(void* handle, u8* buffer, u64 position, u32 length);
static u64 PS4_SYSV_ABI SizeFile(void* handle); static u64 PS4_SYSV_ABI SizeFile(void* handle);
SceAvPlayerInitData StubInitData(const SceAvPlayerInitData& data); AvPlayerInitData StubInitData(const AvPlayerInitData& data);
SceAvPlayerInitData m_init_data{}; AvPlayerInitData m_init_data{};
SceAvPlayerInitData m_init_data_original{}; AvPlayerInitData m_init_data_original{};
std::mutex m_file_io_mutex{}; std::mutex m_file_io_mutex{};
std::atomic_bool m_has_source{};
std::unique_ptr<AvPlayerState> m_state{}; std::unique_ptr<AvPlayerState> m_state{};
}; };

View File

@@ -5,6 +5,7 @@
#include "common/singleton.h" #include "common/singleton.h"
#include "common/thread.h" #include "common/thread.h"
#include "core/file_sys/fs.h" #include "core/file_sys/fs.h"
#include "core/libraries/avplayer/avplayer_error.h"
#include "core/libraries/avplayer/avplayer_file_streamer.h" #include "core/libraries/avplayer/avplayer_file_streamer.h"
#include "core/libraries/avplayer/avplayer_source.h" #include "core/libraries/avplayer/avplayer_source.h"
@@ -29,9 +30,9 @@ AvPlayerSource::~AvPlayerSource() {
Stop(); Stop();
} }
bool AvPlayerSource::Init(const SceAvPlayerInitData& init_data, std::string_view path) { bool AvPlayerSource::Init(const AvPlayerInitData& init_data, std::string_view path) {
m_memory_replacement = init_data.memory_replacement, m_memory_replacement = init_data.memory_replacement,
m_num_output_video_framebuffers = m_max_num_video_framebuffers =
std::min(std::max(2, init_data.num_output_video_framebuffers), 16); std::min(std::max(2, init_data.num_output_video_framebuffers), 16);
AVFormatContext* context = avformat_alloc_context(); AVFormatContext* context = avformat_alloc_context();
@@ -76,25 +77,21 @@ s32 AvPlayerSource::GetStreamCount() {
return m_avformat_context->nb_streams; return m_avformat_context->nb_streams;
} }
static SceAvPlayerStreamType CodecTypeToStreamType(AVMediaType codec_type) { static AvPlayerStreamType CodecTypeToStreamType(AVMediaType codec_type) {
switch (codec_type) { switch (codec_type) {
case AVMediaType::AVMEDIA_TYPE_VIDEO: case AVMediaType::AVMEDIA_TYPE_VIDEO:
return SceAvPlayerStreamType::Video; return AvPlayerStreamType::Video;
case AVMediaType::AVMEDIA_TYPE_AUDIO: case AVMediaType::AVMEDIA_TYPE_AUDIO:
return SceAvPlayerStreamType::Audio; return AvPlayerStreamType::Audio;
case AVMediaType::AVMEDIA_TYPE_SUBTITLE: case AVMediaType::AVMEDIA_TYPE_SUBTITLE:
return SceAvPlayerStreamType::TimedText; return AvPlayerStreamType::TimedText;
default: default:
LOG_ERROR(Lib_AvPlayer, "Unexpected AVMediaType {}", magic_enum::enum_name(codec_type)); LOG_ERROR(Lib_AvPlayer, "Unexpected AVMediaType {}", magic_enum::enum_name(codec_type));
return SceAvPlayerStreamType::Unknown; return AvPlayerStreamType::Unknown;
} }
} }
static f32 AVRationalToF32(const AVRational rational) { bool AvPlayerSource::GetStreamInfo(u32 stream_index, AvPlayerStreamInfo& info) {
return f32(rational.num) / rational.den;
}
bool AvPlayerSource::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info) {
info = {}; info = {};
if (m_avformat_context == nullptr || stream_index >= m_avformat_context->nb_streams) { if (m_avformat_context == nullptr || stream_index >= m_avformat_context->nb_streams) {
LOG_ERROR(Lib_AvPlayer, "Could not get stream {} info.", stream_index); LOG_ERROR(Lib_AvPlayer, "Could not get stream {} info.", stream_index);
@@ -115,7 +112,7 @@ bool AvPlayerSource::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info
LOG_WARNING(Lib_AvPlayer, "Stream {} language is unknown", stream_index); LOG_WARNING(Lib_AvPlayer, "Stream {} language is unknown", stream_index);
} }
switch (info.type) { switch (info.type) {
case SceAvPlayerStreamType::Video: { case AvPlayerStreamType::Video: {
LOG_INFO(Lib_AvPlayer, "Stream {} is a video stream.", stream_index); LOG_INFO(Lib_AvPlayer, "Stream {} is a video stream.", stream_index);
info.details.video.aspect_ratio = info.details.video.aspect_ratio =
f32(p_stream->codecpar->width) / p_stream->codecpar->height; f32(p_stream->codecpar->width) / p_stream->codecpar->height;
@@ -133,7 +130,7 @@ bool AvPlayerSource::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info
} }
break; break;
} }
case SceAvPlayerStreamType::Audio: { case AvPlayerStreamType::Audio: {
LOG_INFO(Lib_AvPlayer, "Stream {} is an audio stream.", stream_index); LOG_INFO(Lib_AvPlayer, "Stream {} is an audio stream.", stream_index);
info.details.audio.channel_count = p_stream->codecpar->ch_layout.nb_channels; info.details.audio.channel_count = p_stream->codecpar->ch_layout.nb_channels;
info.details.audio.sample_rate = p_stream->codecpar->sample_rate; info.details.audio.sample_rate = p_stream->codecpar->sample_rate;
@@ -144,7 +141,7 @@ bool AvPlayerSource::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info
} }
break; break;
} }
case SceAvPlayerStreamType::TimedText: { case AvPlayerStreamType::TimedText: {
LOG_WARNING(Lib_AvPlayer, "Stream {} is a timedtext stream.", stream_index); LOG_WARNING(Lib_AvPlayer, "Stream {} is a timedtext stream.", stream_index);
info.details.subs.font_size = 12; info.details.subs.font_size = 12;
info.details.subs.text_size = 12; info.details.subs.text_size = 12;
@@ -186,16 +183,6 @@ bool AvPlayerSource::EnableStream(u32 stream_index) {
LOG_ERROR(Lib_AvPlayer, "Could not open avcodec for video stream {}.", stream_index); LOG_ERROR(Lib_AvPlayer, "Could not open avcodec for video stream {}.", stream_index);
return false; return false;
} }
auto width = u32(m_video_codec_context->width);
auto height = u32(m_video_codec_context->height);
if (!m_use_vdec2) {
width = Common::AlignUp(width, 16);
height = Common::AlignUp(height, 16);
}
const auto size = (width * height * 3) / 2;
for (u64 index = 0; index < m_num_output_video_framebuffers; ++index) {
m_video_buffers.Push(FrameBuffer(m_memory_replacement, 0x100, size));
}
LOG_INFO(Lib_AvPlayer, "Video stream {} enabled", stream_index); LOG_INFO(Lib_AvPlayer, "Video stream {} enabled", stream_index);
break; break;
} }
@@ -212,12 +199,6 @@ bool AvPlayerSource::EnableStream(u32 stream_index) {
LOG_ERROR(Lib_AvPlayer, "Could not open avcodec for audio stream {}.", stream_index); LOG_ERROR(Lib_AvPlayer, "Could not open avcodec for audio stream {}.", stream_index);
return false; return false;
} }
const auto num_channels = m_audio_codec_context->ch_layout.nb_channels;
const auto align = num_channels * sizeof(u16);
const auto size = num_channels * sizeof(u16) * 1024;
for (u64 index = 0; index < 4; ++index) {
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);
break; break;
} }
@@ -244,6 +225,26 @@ bool AvPlayerSource::Start() {
LOG_ERROR(Lib_AvPlayer, "Could not start playback. NULL context."); LOG_ERROR(Lib_AvPlayer, "Could not start playback. NULL context.");
return false; return false;
} }
if (m_video_codec_context) {
auto width = u32(m_video_codec_context->width);
auto height = u32(m_video_codec_context->height);
if (!m_use_vdec2) {
width = Common::AlignUp(width, 16);
height = Common::AlignUp(height, 16);
}
const auto size = (width * height * 3) / 2;
for (u64 index = 0; index < m_max_num_video_framebuffers; ++index) {
m_video_buffers.Push(GuestBuffer(m_memory_replacement, 0x100, size, true));
}
}
if (m_audio_codec_context) {
const auto num_channels = m_audio_codec_context->ch_layout.nb_channels;
const auto align = num_channels * sizeof(u16);
const auto size = num_channels * sizeof(u16) * 1024;
for (u64 index = 0; index < 8; ++index) {
m_audio_buffers.Push(GuestBuffer(m_memory_replacement, 0x100, size, false));
}
}
m_demuxer_thread.Run([this](std::stop_token stop) { this->DemuxerThread(stop); }); m_demuxer_thread.Run([this](std::stop_token stop) { this->DemuxerThread(stop); });
m_video_decoder_thread.Run([this](std::stop_token stop) { this->VideoDecoderThread(stop); }); m_video_decoder_thread.Run([this](std::stop_token stop) { this->VideoDecoderThread(stop); });
m_audio_decoder_thread.Run([this](std::stop_token stop) { this->AudioDecoderThread(stop); }); m_audio_decoder_thread.Run([this](std::stop_token stop) { this->AudioDecoderThread(stop); });
@@ -263,123 +264,122 @@ bool AvPlayerSource::Stop() {
m_audio_decoder_thread.Stop(); m_audio_decoder_thread.Stop();
m_demuxer_thread.Stop(); m_demuxer_thread.Stop();
if (m_current_audio_frame.has_value()) { m_current_audio_frame.reset();
m_audio_buffers.Push(std::move(m_current_audio_frame.value())); m_current_video_frame.reset();
m_current_audio_frame.reset();
}
if (m_current_video_frame.has_value()) {
m_video_buffers.Push(std::move(m_current_video_frame.value()));
m_current_video_frame.reset();
}
m_stop_cv.Notify();
m_video_buffers.Clear();
m_audio_buffers.Clear();
m_audio_packets.Clear(); m_audio_packets.Clear();
m_video_packets.Clear(); m_video_packets.Clear();
m_audio_frames.Clear(); m_audio_frames.Clear();
m_video_frames.Clear(); m_video_frames.Clear();
return true; return true;
} }
bool AvPlayerSource::GetVideoData(SceAvPlayerFrameInfo& video_info) { void AvPlayerSource::Pause() {
if (!IsActive()) { m_pause_time = std::chrono::high_resolution_clock::now();
return false; m_is_paused = true;
} }
SceAvPlayerFrameInfoEx info{}; void AvPlayerSource::Resume() {
m_pause_duration += std::chrono::high_resolution_clock::now() - m_pause_time;
m_is_paused = false;
}
bool AvPlayerSource::GetVideoData(AvPlayerFrameInfo& video_info) {
AvPlayerFrameInfoEx info{};
if (!GetVideoData(info)) { if (!GetVideoData(info)) {
return false; return false;
} }
video_info = {}; video_info = {};
video_info.timestamp = u64(info.timestamp); video_info.timestamp = u64(info.timestamp);
video_info.pData = reinterpret_cast<u8*>(info.pData); video_info.p_data = reinterpret_cast<u8*>(info.p_data);
video_info.details.video.aspect_ratio = info.details.video.aspect_ratio; video_info.details.video.aspect_ratio = info.details.video.aspect_ratio;
video_info.details.video.width = info.details.video.width; video_info.details.video.width = info.details.video.width;
video_info.details.video.height = info.details.video.height; video_info.details.video.height = info.details.video.height;
return true; return true;
} }
bool AvPlayerSource::GetVideoData(SceAvPlayerFrameInfoEx& video_info) { bool AvPlayerSource::GetVideoData(AvPlayerFrameInfoEx& video_info) {
if (!IsActive()) { if (m_current_video_frame.has_value()) {
m_video_buffers.Push(std::move(m_current_video_frame->buffer));
m_current_video_frame.reset();
m_video_buffers_cv.Notify();
}
if (!IsActive() || m_is_paused) {
return false; return false;
} }
m_video_frames_cv.Wait([this] { return m_video_frames.Size() != 0 || m_is_eof; }); if (m_video_frames.Size() == 0) {
auto frame = m_video_frames.Pop();
if (!frame.has_value()) {
LOG_TRACE(Lib_AvPlayer, "Could get video frame. EOF reached.");
return false; return false;
} }
{ const auto& new_frame = m_video_frames.Front();
using namespace std::chrono; if (m_state.GetSyncMode() == AvPlayerAvSyncMode::Default) {
auto elapsed_time = if (m_audio_codec_context != nullptr) {
duration_cast<milliseconds>(high_resolution_clock::now() - m_start_time).count(); // Sync with the audio
if (elapsed_time < frame->info.timestamp) { auto avdiff = s64(new_frame.info.timestamp) - s64(m_last_audio_ts.value_or(0));
if (m_stop_cv.WaitFor(milliseconds(frame->info.timestamp - elapsed_time), if (avdiff > 69) {
[&] { return elapsed_time >= frame->info.timestamp; })) { // VIDEO_AHEAD, wait
return false;
}
// These will remain unimplemented for now:
// avdiff < -28 = VIDEO_BEHIND, ??? skip frames ???
// -2 < avdiff < 0 = WAIT_FOR_SYNC, ??? loop until synced ???
} else {
// Sync with the internal timer since audio is not available
const auto current_time = CurrentTime();
if (0 < current_time && current_time < new_frame.info.timestamp) {
return false; return false;
} }
} }
} }
// return the buffer to the queue auto frame = m_video_frames.Pop();
if (m_current_video_frame.has_value()) {
m_video_buffers.Push(std::move(m_current_video_frame.value()));
m_video_buffers_cv.Notify();
}
m_current_video_frame = std::move(frame->buffer);
video_info = frame->info; video_info = frame->info;
m_current_video_frame = std::move(frame);
return true; return true;
} }
bool AvPlayerSource::GetAudioData(SceAvPlayerFrameInfo& audio_info) { bool AvPlayerSource::GetAudioData(AvPlayerFrameInfo& audio_info) {
if (!IsActive()) {
return false;
}
m_audio_frames_cv.Wait([this] { return m_audio_frames.Size() != 0 || m_is_eof; });
auto frame = m_audio_frames.Pop();
if (!frame.has_value()) {
LOG_TRACE(Lib_AvPlayer, "Could get audio frame. EOF reached.");
return false;
}
{
using namespace std::chrono;
auto elapsed_time =
duration_cast<milliseconds>(high_resolution_clock::now() - m_start_time).count();
if (elapsed_time < frame->info.timestamp) {
if (m_stop_cv.WaitFor(milliseconds(frame->info.timestamp - elapsed_time),
[&] { return elapsed_time >= frame->info.timestamp; })) {
return false;
}
}
}
// return the buffer to the queue
if (m_current_audio_frame.has_value()) { if (m_current_audio_frame.has_value()) {
m_audio_buffers.Push(std::move(m_current_audio_frame.value())); // return the buffer to the queue
m_audio_buffers.Push(std::move(m_current_audio_frame->buffer));
m_current_audio_frame.reset();
m_audio_buffers_cv.Notify(); m_audio_buffers_cv.Notify();
} }
m_current_audio_frame = std::move(frame->buffer);
if (!IsActive() || m_is_paused) {
return false;
}
if (m_audio_frames.Size() == 0) {
return false;
}
auto frame = m_audio_frames.Pop();
m_last_audio_ts = frame->info.timestamp;
audio_info = {}; audio_info = {};
audio_info.timestamp = frame->info.timestamp; audio_info.timestamp = frame->info.timestamp;
audio_info.pData = reinterpret_cast<u8*>(frame->info.pData); audio_info.p_data = reinterpret_cast<u8*>(frame->info.p_data);
audio_info.details.audio.sample_rate = frame->info.details.audio.sample_rate; audio_info.details.audio.sample_rate = frame->info.details.audio.sample_rate;
audio_info.details.audio.size = frame->info.details.audio.size; audio_info.details.audio.size = frame->info.details.audio.size;
audio_info.details.audio.channel_count = frame->info.details.audio.channel_count; audio_info.details.audio.channel_count = frame->info.details.audio.channel_count;
m_current_audio_frame = std::move(frame);
return true; return true;
} }
u64 AvPlayerSource::CurrentTime() { u64 AvPlayerSource::CurrentTime() {
if (!IsActive()) { if (!IsActive() || !m_start_time.has_value()) {
return 0; return 0;
} }
using namespace std::chrono; using namespace std::chrono;
return duration_cast<milliseconds>(high_resolution_clock::now() - m_start_time).count(); return duration_cast<milliseconds>(high_resolution_clock::now() - m_start_time.value() -
m_pause_duration)
.count();
} }
bool AvPlayerSource::IsActive() { bool AvPlayerSource::IsActive() {
@@ -445,6 +445,7 @@ void AvPlayerSource::DemuxerThread(std::stop_token stop) {
if (res == AVERROR_EOF) { if (res == AVERROR_EOF) {
if (m_is_looping) { if (m_is_looping) {
LOG_INFO(Lib_AvPlayer, "EOF reached in demuxer. Looping the source..."); 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); avio_seek(m_avformat_context->pb, 0, SEEK_SET);
if (m_video_stream_index.has_value()) { if (m_video_stream_index.has_value()) {
const auto index = m_video_stream_index.value(); const auto index = m_video_stream_index.value();
@@ -548,7 +549,7 @@ static void CopyNV12Data(u8* dst, const AVFrame& src, bool use_vdec2) {
} }
} }
Frame AvPlayerSource::PrepareVideoFrame(FrameBuffer buffer, const AVFrame& frame) { Frame AvPlayerSource::PrepareVideoFrame(GuestBuffer buffer, const AVFrame& frame) {
ASSERT(frame.format == AV_PIX_FMT_NV12); ASSERT(frame.format == AV_PIX_FMT_NV12);
auto p_buffer = buffer.GetBuffer(); auto p_buffer = buffer.GetBuffer();
@@ -572,7 +573,7 @@ Frame AvPlayerSource::PrepareVideoFrame(FrameBuffer buffer, const AVFrame& frame
.buffer = std::move(buffer), .buffer = std::move(buffer),
.info = .info =
{ {
.pData = p_buffer, .p_data = p_buffer,
.timestamp = timestamp, .timestamp = timestamp,
.details = .details =
{ {
@@ -580,7 +581,7 @@ Frame AvPlayerSource::PrepareVideoFrame(FrameBuffer buffer, const AVFrame& frame
{ {
.width = width, .width = width,
.height = height, .height = height,
.aspect_ratio = AVRationalToF32(frame.sample_aspect_ratio), .aspect_ratio = (float)av_q2d(frame.sample_aspect_ratio),
.crop_left_offset = u32(frame.crop_left), .crop_left_offset = u32(frame.crop_left),
.crop_right_offset = u32(frame.crop_right + (width - frame.width)), .crop_right_offset = u32(frame.crop_right + (width - frame.width)),
.crop_top_offset = u32(frame.crop_top), .crop_top_offset = u32(frame.crop_top),
@@ -683,7 +684,7 @@ AvPlayerSource::AVFramePtr AvPlayerSource::ConvertAudioFrame(const AVFrame& fram
return pcm16_frame; return pcm16_frame;
} }
Frame AvPlayerSource::PrepareAudioFrame(FrameBuffer buffer, const AVFrame& frame) { Frame AvPlayerSource::PrepareAudioFrame(GuestBuffer buffer, const AVFrame& frame) {
ASSERT(frame.format == AV_SAMPLE_FMT_S16); ASSERT(frame.format == AV_SAMPLE_FMT_S16);
ASSERT(frame.nb_samples <= 1024); ASSERT(frame.nb_samples <= 1024);
@@ -702,7 +703,7 @@ Frame AvPlayerSource::PrepareAudioFrame(FrameBuffer buffer, const AVFrame& frame
.buffer = std::move(buffer), .buffer = std::move(buffer),
.info = .info =
{ {
.pData = p_buffer, .p_data = p_buffer,
.timestamp = timestamp, .timestamp = timestamp,
.details = .details =
{ {

View File

@@ -30,35 +30,45 @@ class AvPlayerStateCallback {
public: public:
virtual ~AvPlayerStateCallback() = default; virtual ~AvPlayerStateCallback() = default;
virtual AvPlayerAvSyncMode GetSyncMode() = 0;
virtual void OnWarning(u32 id) = 0; virtual void OnWarning(u32 id) = 0;
virtual void OnError() = 0; virtual void OnError() = 0;
virtual void OnEOF() = 0; virtual void OnEOF() = 0;
}; };
class FrameBuffer { class GuestBuffer {
public: public:
FrameBuffer(const SceAvPlayerMemAllocator& memory_replacement, u32 align, u32 size) noexcept GuestBuffer(const AvPlayerMemAllocator& memory_replacement, u32 align, u32 size,
bool is_texture) noexcept
: m_memory_replacement(memory_replacement), : m_memory_replacement(memory_replacement),
m_data(Allocate(memory_replacement, align, size)) { m_data(is_texture ? AllocateTexture(memory_replacement, align, size)
: Allocate(memory_replacement, align, size)),
m_is_texture(is_texture) {
ASSERT_MSG(m_data, "Could not allocate frame buffer."); ASSERT_MSG(m_data, "Could not allocate frame buffer.");
} }
~FrameBuffer() { ~GuestBuffer() {
if (m_data != nullptr) { if (m_data != nullptr) {
Deallocate(m_memory_replacement, m_data); if (m_is_texture) {
DeallocateTexture(m_memory_replacement, m_data);
} else {
Deallocate(m_memory_replacement, m_data);
}
m_data = {}; m_data = {};
} }
} }
FrameBuffer(const FrameBuffer&) noexcept = delete; GuestBuffer(const GuestBuffer&) noexcept = delete;
FrameBuffer& operator=(const FrameBuffer&) noexcept = delete; GuestBuffer& operator=(const GuestBuffer&) noexcept = delete;
FrameBuffer(FrameBuffer&& r) noexcept GuestBuffer(GuestBuffer&& r) noexcept
: m_memory_replacement(r.m_memory_replacement), m_data(r.m_data) { : m_memory_replacement(r.m_memory_replacement), m_data(r.m_data),
m_is_texture(r.m_is_texture) {
r.m_data = nullptr; r.m_data = nullptr;
}; };
FrameBuffer& operator=(FrameBuffer&& r) noexcept { GuestBuffer& operator=(GuestBuffer&& r) noexcept {
std::swap(m_data, r.m_data); std::swap(m_data, r.m_data);
return *this; return *this;
} }
@@ -68,22 +78,33 @@ public:
} }
private: private:
static u8* Allocate(const SceAvPlayerMemAllocator& memory_replacement, u32 align, u32 size) { static u8* Allocate(const AvPlayerMemAllocator& memory_replacement, u32 align, u32 size) {
return reinterpret_cast<u8*>( return reinterpret_cast<u8*>(
memory_replacement.allocate(memory_replacement.object_ptr, align, size)); memory_replacement.allocate(memory_replacement.object_ptr, align, size));
} }
static void Deallocate(const SceAvPlayerMemAllocator& memory_replacement, void* ptr) { static void Deallocate(const AvPlayerMemAllocator& memory_replacement, void* ptr) {
memory_replacement.deallocate(memory_replacement.object_ptr, ptr); memory_replacement.deallocate(memory_replacement.object_ptr, ptr);
} }
const SceAvPlayerMemAllocator& m_memory_replacement; static u8* AllocateTexture(const AvPlayerMemAllocator& memory_replacement, u32 align,
u32 size) {
return reinterpret_cast<u8*>(
memory_replacement.allocate_texture(memory_replacement.object_ptr, align, size));
}
static void DeallocateTexture(const AvPlayerMemAllocator& memory_replacement, void* ptr) {
memory_replacement.deallocate_texture(memory_replacement.object_ptr, ptr);
}
const AvPlayerMemAllocator& m_memory_replacement;
u8* m_data = nullptr; u8* m_data = nullptr;
bool m_is_texture = false;
}; };
struct Frame { struct Frame {
FrameBuffer buffer; GuestBuffer buffer;
SceAvPlayerFrameInfoEx info; AvPlayerFrameInfoEx info;
}; };
class EventCV { class EventCV {
@@ -121,18 +142,20 @@ public:
AvPlayerSource(AvPlayerStateCallback& state, bool use_vdec2); AvPlayerSource(AvPlayerStateCallback& state, bool use_vdec2);
~AvPlayerSource(); ~AvPlayerSource();
bool Init(const SceAvPlayerInitData& init_data, std::string_view path); bool Init(const AvPlayerInitData& init_data, std::string_view path);
bool FindStreamInfo(); bool FindStreamInfo();
s32 GetStreamCount(); s32 GetStreamCount();
bool GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info); bool GetStreamInfo(u32 stream_index, AvPlayerStreamInfo& info);
bool EnableStream(u32 stream_index); bool EnableStream(u32 stream_index);
void SetLooping(bool is_looping); void SetLooping(bool is_looping);
std::optional<bool> HasFrames(u32 num_frames); std::optional<bool> HasFrames(u32 num_frames);
bool Start(); bool Start();
bool Stop(); bool Stop();
bool GetAudioData(SceAvPlayerFrameInfo& audio_info); void Pause();
bool GetVideoData(SceAvPlayerFrameInfo& video_info); void Resume();
bool GetVideoData(SceAvPlayerFrameInfoEx& video_info); bool GetAudioData(AvPlayerFrameInfo& audio_info);
bool GetVideoData(AvPlayerFrameInfo& video_info);
bool GetVideoData(AvPlayerFrameInfoEx& video_info);
u64 CurrentTime(); u64 CurrentTime();
bool IsActive(); bool IsActive();
@@ -160,22 +183,23 @@ private:
AVFramePtr ConvertAudioFrame(const AVFrame& frame); AVFramePtr ConvertAudioFrame(const AVFrame& frame);
AVFramePtr ConvertVideoFrame(const AVFrame& frame); AVFramePtr ConvertVideoFrame(const AVFrame& frame);
Frame PrepareAudioFrame(FrameBuffer buffer, const AVFrame& frame); Frame PrepareAudioFrame(GuestBuffer buffer, const AVFrame& frame);
Frame PrepareVideoFrame(FrameBuffer buffer, const AVFrame& frame); Frame PrepareVideoFrame(GuestBuffer buffer, const AVFrame& frame);
AvPlayerStateCallback& m_state; AvPlayerStateCallback& m_state;
bool m_use_vdec2 = false; bool m_use_vdec2 = false;
SceAvPlayerMemAllocator m_memory_replacement{}; AvPlayerMemAllocator m_memory_replacement{};
u32 m_num_output_video_framebuffers{}; u32 m_max_num_video_framebuffers{};
std::atomic_bool m_is_looping = false; std::atomic_bool m_is_looping = false;
std::atomic_bool m_is_paused = false;
std::atomic_bool m_is_eof = false; std::atomic_bool m_is_eof = false;
std::unique_ptr<IDataStreamer> m_up_data_streamer; std::unique_ptr<IDataStreamer> m_up_data_streamer;
AvPlayerQueue<FrameBuffer> m_audio_buffers; AvPlayerQueue<GuestBuffer> m_audio_buffers;
AvPlayerQueue<FrameBuffer> m_video_buffers; AvPlayerQueue<GuestBuffer> m_video_buffers;
AvPlayerQueue<AVPacketPtr> m_audio_packets; AvPlayerQueue<AVPacketPtr> m_audio_packets;
AvPlayerQueue<AVPacketPtr> m_video_packets; AvPlayerQueue<AVPacketPtr> m_video_packets;
@@ -183,8 +207,8 @@ private:
AvPlayerQueue<Frame> m_audio_frames; AvPlayerQueue<Frame> m_audio_frames;
AvPlayerQueue<Frame> m_video_frames; AvPlayerQueue<Frame> m_video_frames;
std::optional<FrameBuffer> m_current_video_frame; std::optional<Frame> m_current_video_frame;
std::optional<FrameBuffer> m_current_audio_frame; std::optional<Frame> m_current_audio_frame;
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{};
@@ -197,8 +221,6 @@ private:
EventCV m_video_frames_cv{}; EventCV m_video_frames_cv{};
EventCV m_video_buffers_cv{}; EventCV m_video_buffers_cv{};
EventCV m_stop_cv{};
std::mutex m_state_mutex{}; std::mutex m_state_mutex{};
Kernel::Thread m_demuxer_thread{}; Kernel::Thread m_demuxer_thread{};
Kernel::Thread m_video_decoder_thread{}; Kernel::Thread m_video_decoder_thread{};
@@ -210,7 +232,10 @@ private:
SWRContextPtr m_swr_context{nullptr, &ReleaseSWRContext}; SWRContextPtr m_swr_context{nullptr, &ReleaseSWRContext};
SWSContextPtr m_sws_context{nullptr, &ReleaseSWSContext}; SWSContextPtr m_sws_context{nullptr, &ReleaseSWSContext};
std::chrono::high_resolution_clock::time_point m_start_time{}; std::optional<u64> m_last_audio_ts{};
std::optional<std::chrono::high_resolution_clock::time_point> m_start_time{};
std::chrono::high_resolution_clock::time_point m_pause_time{};
std::chrono::high_resolution_clock::duration m_pause_duration{};
}; };
} // namespace Libraries::AvPlayer } // namespace Libraries::AvPlayer

View File

@@ -12,11 +12,11 @@
namespace Libraries::AvPlayer { namespace Libraries::AvPlayer {
void PS4_SYSV_ABI AvPlayerState::AutoPlayEventCallback(void* opaque, SceAvPlayerEvents event_id, void PS4_SYSV_ABI AvPlayerState::AutoPlayEventCallback(void* opaque, AvPlayerEvents event_id,
s32 source_id, void* event_data) { s32 source_id, void* event_data) {
auto const self = reinterpret_cast<AvPlayerState*>(opaque); auto const self = reinterpret_cast<AvPlayerState*>(opaque);
if (event_id == SceAvPlayerEvents::StateReady) { if (event_id == AvPlayerEvents::StateReady) {
s32 video_stream_index = -1; s32 video_stream_index = -1;
s32 audio_stream_index = -1; s32 audio_stream_index = -1;
s32 timedtext_stream_index = -1; s32 timedtext_stream_index = -1;
@@ -30,7 +30,7 @@ void PS4_SYSV_ABI AvPlayerState::AutoPlayEventCallback(void* opaque, SceAvPlayer
return; return;
} }
for (u32 stream_index = 0; stream_index < stream_count; ++stream_index) { for (u32 stream_index = 0; stream_index < stream_count; ++stream_index) {
SceAvPlayerStreamInfo info{}; AvPlayerStreamInfo info{};
if (!self->GetStreamInfo(stream_index, info)) { if (!self->GetStreamInfo(stream_index, info)) {
self->Stop(); self->Stop();
return; return;
@@ -38,7 +38,7 @@ void PS4_SYSV_ABI AvPlayerState::AutoPlayEventCallback(void* opaque, SceAvPlayer
const std::string_view default_language{self->m_default_language}; const std::string_view default_language{self->m_default_language};
switch (info.type) { switch (info.type) {
case SceAvPlayerStreamType::Video: case AvPlayerStreamType::Video:
if (video_stream_index == -1) { if (video_stream_index == -1) {
video_stream_index = stream_index; video_stream_index = stream_index;
} }
@@ -47,7 +47,7 @@ void PS4_SYSV_ABI AvPlayerState::AutoPlayEventCallback(void* opaque, SceAvPlayer
video_stream_index = stream_index; video_stream_index = stream_index;
} }
break; break;
case SceAvPlayerStreamType::Audio: case AvPlayerStreamType::Audio:
if (audio_stream_index == -1) { if (audio_stream_index == -1) {
audio_stream_index = stream_index; audio_stream_index = stream_index;
} }
@@ -56,7 +56,7 @@ void PS4_SYSV_ABI AvPlayerState::AutoPlayEventCallback(void* opaque, SceAvPlayer
audio_stream_index = stream_index; audio_stream_index = stream_index;
} }
break; break;
case SceAvPlayerStreamType::TimedText: case AvPlayerStreamType::TimedText:
if (default_language.empty()) { if (default_language.empty()) {
timedtext_stream_index = stream_index; timedtext_stream_index = stream_index;
break; break;
@@ -86,7 +86,7 @@ void PS4_SYSV_ABI AvPlayerState::AutoPlayEventCallback(void* opaque, SceAvPlayer
DefaultEventCallback(opaque, event_id, 0, event_data); DefaultEventCallback(opaque, event_id, 0, event_data);
} }
void AvPlayerState::DefaultEventCallback(void* opaque, SceAvPlayerEvents event_id, s32 source_id, void AvPlayerState::DefaultEventCallback(void* opaque, AvPlayerEvents event_id, s32 source_id,
void* event_data) { void* event_data) {
auto const self = reinterpret_cast<AvPlayerState*>(opaque); auto const self = reinterpret_cast<AvPlayerState*>(opaque);
const auto callback = self->m_event_replacement.event_callback; const auto callback = self->m_event_replacement.event_callback;
@@ -97,7 +97,7 @@ void AvPlayerState::DefaultEventCallback(void* opaque, SceAvPlayerEvents event_i
} }
// Called inside GAME thread // Called inside GAME thread
AvPlayerState::AvPlayerState(const SceAvPlayerInitData& init_data) AvPlayerState::AvPlayerState(const AvPlayerInitData& init_data)
: m_init_data(init_data), m_event_replacement(init_data.event_replacement) { : m_init_data(init_data), m_event_replacement(init_data.event_replacement) {
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;
@@ -122,12 +122,12 @@ AvPlayerState::~AvPlayerState() {
m_event_queue.Clear(); m_event_queue.Clear();
} }
void AvPlayerState::PostInit(const SceAvPlayerPostInitData& post_init_data) { void AvPlayerState::PostInit(const AvPlayerPostInitData& post_init_data) {
m_post_init_data = post_init_data; m_post_init_data = post_init_data;
} }
// Called inside GAME thread // Called inside GAME thread
bool AvPlayerState::AddSource(std::string_view path, SceAvPlayerSourceType source_type) { bool AvPlayerState::AddSource(std::string_view path, AvPlayerSourceType source_type) {
if (path.empty()) { if (path.empty()) {
LOG_ERROR(Lib_AvPlayer, "File path is empty."); LOG_ERROR(Lib_AvPlayer, "File path is empty.");
return false; return false;
@@ -141,8 +141,8 @@ bool AvPlayerState::AddSource(std::string_view path, SceAvPlayerSourceType sourc
} }
m_up_source = std::make_unique<AvPlayerSource>( m_up_source = std::make_unique<AvPlayerSource>(
*this, m_post_init_data.video_decoder_init.decoderType.video_type == *this, m_post_init_data.video_decoder_init.decoder_type.video_type ==
SceAvPlayerVideoDecoderType::Software2); AvPlayerVideoDecoderType::Software2);
if (!m_up_source->Init(m_init_data, path)) { if (!m_up_source->Init(m_init_data, path)) {
SetState(AvState::Error); SetState(AvState::Error);
m_up_source.reset(); m_up_source.reset();
@@ -164,7 +164,7 @@ s32 AvPlayerState::GetStreamCount() {
} }
// Called inside GAME thread // Called inside GAME thread
bool AvPlayerState::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info) { bool AvPlayerState::GetStreamInfo(u32 stream_index, AvPlayerStreamInfo& info) {
std::shared_lock lock(m_source_mutex); std::shared_lock lock(m_source_mutex);
if (m_up_source == nullptr) { if (m_up_source == nullptr) {
LOG_ERROR(Lib_AvPlayer, "Could not get stream {} info. No source.", stream_index); LOG_ERROR(Lib_AvPlayer, "Could not get stream {} info. No source.", stream_index);
@@ -185,6 +185,42 @@ bool AvPlayerState::Start() {
return true; 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::SetAvSyncMode(AvPlayerAvSyncMode sync_mode) {
m_sync_mode = sync_mode;
}
void AvPlayerState::AvControllerThread(std::stop_token stop) { void AvPlayerState::AvControllerThread(std::stop_token stop) {
using std::chrono::milliseconds; using std::chrono::milliseconds;
Common::SetCurrentThreadName("shadPS4:AvController"); Common::SetCurrentThreadName("shadPS4:AvController");
@@ -247,7 +283,7 @@ bool AvPlayerState::Stop() {
return true; return true;
} }
bool AvPlayerState::GetVideoData(SceAvPlayerFrameInfo& video_info) { bool AvPlayerState::GetVideoData(AvPlayerFrameInfo& video_info) {
std::shared_lock lock(m_source_mutex); std::shared_lock lock(m_source_mutex);
if (m_up_source == nullptr) { if (m_up_source == nullptr) {
return false; return false;
@@ -255,7 +291,7 @@ bool AvPlayerState::GetVideoData(SceAvPlayerFrameInfo& video_info) {
return m_up_source->GetVideoData(video_info); return m_up_source->GetVideoData(video_info);
} }
bool AvPlayerState::GetVideoData(SceAvPlayerFrameInfoEx& video_info) { bool AvPlayerState::GetVideoData(AvPlayerFrameInfoEx& video_info) {
std::shared_lock lock(m_source_mutex); std::shared_lock lock(m_source_mutex);
if (m_up_source == nullptr) { if (m_up_source == nullptr) {
return false; return false;
@@ -263,7 +299,7 @@ bool AvPlayerState::GetVideoData(SceAvPlayerFrameInfoEx& video_info) {
return m_up_source->GetVideoData(video_info); return m_up_source->GetVideoData(video_info);
} }
bool AvPlayerState::GetAudioData(SceAvPlayerFrameInfo& audio_info) { bool AvPlayerState::GetAudioData(AvPlayerFrameInfo& audio_info) {
std::shared_lock lock(m_source_mutex); std::shared_lock lock(m_source_mutex);
if (m_up_source == nullptr) { if (m_up_source == nullptr) {
return false; return false;
@@ -305,6 +341,10 @@ void AvPlayerState::OnWarning(u32 id) {
WarningEvent(id); WarningEvent(id);
} }
AvPlayerAvSyncMode AvPlayerState::GetSyncMode() {
return m_sync_mode;
}
void AvPlayerState::OnError() { void AvPlayerState::OnError() {
SetState(AvState::Error); SetState(AvState::Error);
OnPlaybackStateChanged(AvState::Error); OnPlaybackStateChanged(AvState::Error);
@@ -318,23 +358,23 @@ void AvPlayerState::OnEOF() {
void AvPlayerState::OnPlaybackStateChanged(AvState state) { void AvPlayerState::OnPlaybackStateChanged(AvState state) {
switch (state) { switch (state) {
case AvState::Ready: { case AvState::Ready: {
EmitEvent(SceAvPlayerEvents::StateReady); EmitEvent(AvPlayerEvents::StateReady);
break; break;
} }
case AvState::Play: { case AvState::Play: {
EmitEvent(SceAvPlayerEvents::StatePlay); EmitEvent(AvPlayerEvents::StatePlay);
break; break;
} }
case AvState::Stop: { case AvState::Stop: {
EmitEvent(SceAvPlayerEvents::StateStop); EmitEvent(AvPlayerEvents::StateStop);
break; break;
} }
case AvState::Pause: { case AvState::Pause: {
EmitEvent(SceAvPlayerEvents::StatePause); EmitEvent(AvPlayerEvents::StatePause);
break; break;
} }
case AvState::Buffering: { case AvState::Buffering: {
EmitEvent(SceAvPlayerEvents::StateBuffering); EmitEvent(AvPlayerEvents::StateBuffering);
break; break;
} }
default: default:
@@ -366,7 +406,7 @@ std::optional<bool> AvPlayerState::OnBufferingCheckEvent(u32 num_frames) {
} }
// Called inside CONTROLLER thread // Called inside CONTROLLER thread
void AvPlayerState::EmitEvent(SceAvPlayerEvents event_id, void* event_data) { void AvPlayerState::EmitEvent(AvPlayerEvents event_id, void* event_data) {
LOG_INFO(Lib_AvPlayer, "Sending event to the game: id = {}", magic_enum::enum_name(event_id)); LOG_INFO(Lib_AvPlayer, "Sending event to the game: id = {}", magic_enum::enum_name(event_id));
const auto callback = m_init_data.event_replacement.event_callback; const auto callback = m_init_data.event_replacement.event_callback;
if (callback) { if (callback) {
@@ -389,7 +429,7 @@ void AvPlayerState::ProcessEvent() {
} }
switch (event->event) { switch (event->event) {
case AvEventType::WarningId: { case AvEventType::WarningId: {
OnWarning(event->payload.error); EmitEvent(AvPlayerEvents::WarningId, &event->payload.error);
break; break;
} }
case AvEventType::RevertState: { case AvEventType::RevertState: {

View File

@@ -18,31 +18,35 @@ class AvDecoder;
class AvPlayerState : public AvPlayerStateCallback { class AvPlayerState : public AvPlayerStateCallback {
public: public:
AvPlayerState(const SceAvPlayerInitData& init_data); AvPlayerState(const AvPlayerInitData& init_data);
~AvPlayerState(); ~AvPlayerState();
void PostInit(const SceAvPlayerPostInitData& post_init_data); void PostInit(const AvPlayerPostInitData& post_init_data);
bool AddSource(std::string_view filename, SceAvPlayerSourceType source_type); bool AddSource(std::string_view filename, AvPlayerSourceType source_type);
s32 GetStreamCount(); s32 GetStreamCount();
bool GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info); bool GetStreamInfo(u32 stream_index, AvPlayerStreamInfo& info);
bool EnableStream(u32 stream_index); bool EnableStream(u32 stream_index);
bool Start(); bool Start();
bool Stop(); bool Stop();
bool GetAudioData(SceAvPlayerFrameInfo& audio_info); bool Pause();
bool GetVideoData(SceAvPlayerFrameInfo& video_info); bool Resume();
bool GetVideoData(SceAvPlayerFrameInfoEx& video_info); void SetAvSyncMode(AvPlayerAvSyncMode sync_mode);
bool GetAudioData(AvPlayerFrameInfo& audio_info);
bool GetVideoData(AvPlayerFrameInfo& video_info);
bool GetVideoData(AvPlayerFrameInfoEx& video_info);
bool IsActive(); bool IsActive();
u64 CurrentTime(); u64 CurrentTime();
bool SetLooping(bool is_looping); bool SetLooping(bool is_looping);
private: private:
// Event Replacement // Event Replacement
static void PS4_SYSV_ABI AutoPlayEventCallback(void* handle, SceAvPlayerEvents event_id, static void PS4_SYSV_ABI AutoPlayEventCallback(void* handle, AvPlayerEvents event_id,
s32 source_id, void* event_data); s32 source_id, void* event_data);
static void PS4_SYSV_ABI DefaultEventCallback(void* handle, SceAvPlayerEvents event_id, static void PS4_SYSV_ABI DefaultEventCallback(void* handle, AvPlayerEvents event_id,
s32 source_id, void* event_data); s32 source_id, void* event_data);
AvPlayerAvSyncMode GetSyncMode() override;
void OnWarning(u32 id) override; void OnWarning(u32 id) override;
void OnError() override; void OnError() override;
void OnEOF() override; void OnEOF() override;
@@ -50,7 +54,7 @@ private:
void OnPlaybackStateChanged(AvState state); void OnPlaybackStateChanged(AvState state);
std::optional<bool> OnBufferingCheckEvent(u32 num_frames); std::optional<bool> OnBufferingCheckEvent(u32 num_frames);
void EmitEvent(SceAvPlayerEvents event_id, void* event_data = nullptr); void EmitEvent(AvPlayerEvents event_id, void* event_data = nullptr);
bool SetState(AvState state); bool SetState(AvState state);
void AvControllerThread(std::stop_token stop); void AvControllerThread(std::stop_token stop);
@@ -65,11 +69,12 @@ private:
std::unique_ptr<AvPlayerSource> m_up_source; std::unique_ptr<AvPlayerSource> m_up_source;
SceAvPlayerInitData m_init_data{}; AvPlayerInitData m_init_data{};
SceAvPlayerPostInitData m_post_init_data{}; AvPlayerPostInitData m_post_init_data{};
SceAvPlayerEventReplacement m_event_replacement{}; AvPlayerEventReplacement m_event_replacement{};
bool m_auto_start{}; bool m_auto_start{};
char m_default_language[4]{}; char m_default_language[4]{};
AvPlayerAvSyncMode m_sync_mode = AvPlayerAvSyncMode::Default;
std::atomic<AvState> m_current_state; std::atomic<AvState> m_current_state;
std::atomic<AvState> m_previous_state; std::atomic<AvState> m_previous_state;