Added some logs, fixed some crashes, fixed align.
This commit is contained in:
parent
b5c69189e5
commit
e33ff10212
|
@ -100,7 +100,7 @@ s32 SDLAudio::AudioOutOutput(s32 handle, const void* ptr) {
|
||||||
int result = SDL_PutAudioStreamData(port.stream, ptr,
|
int result = SDL_PutAudioStreamData(port.stream, ptr,
|
||||||
port.samples_num * port.sample_size * port.channels_num);
|
port.samples_num * port.sample_size * port.channels_num);
|
||||||
// TODO find a correct value 8192 is estimated
|
// TODO find a correct value 8192 is estimated
|
||||||
while (SDL_GetAudioStreamAvailable(port.stream) > 8192) {
|
while (SDL_GetAudioStreamAvailable(port.stream) > 65536) {
|
||||||
SDL_Delay(0);
|
SDL_Delay(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,9 @@
|
||||||
#include "core/libraries/kernel/thread_management.h"
|
#include "core/libraries/kernel/thread_management.h"
|
||||||
#include "core/libraries/libs.h"
|
#include "core/libraries/libs.h"
|
||||||
|
|
||||||
#include <mutex>
|
#include <algorithm> // std::max, std::min
|
||||||
|
|
||||||
|
#include <stdarg.h> // va_list
|
||||||
|
|
||||||
namespace Libraries::AvPlayer {
|
namespace Libraries::AvPlayer {
|
||||||
|
|
||||||
|
@ -148,9 +150,7 @@ SceAvPlayerHandle PS4_SYSV_ABI sceAvPlayerInit(SceAvPlayerInitData* data) {
|
||||||
// priorities.file_streaming_priority = GetPriority(priorities.http_streaming_priority, 15);
|
// priorities.file_streaming_priority = GetPriority(priorities.http_streaming_priority, 15);
|
||||||
// priorities.maxPriority = priorities.http_streaming_priority;
|
// priorities.maxPriority = priorities.http_streaming_priority;
|
||||||
|
|
||||||
const auto player = new AvPlayer();
|
return new AvPlayer(*data, priorities);
|
||||||
player->Init(*data, priorities);
|
|
||||||
return player;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 PS4_SYSV_ABI sceAvPlayerInitEx(const SceAvPlayerInitDataEx* p_data,
|
s32 PS4_SYSV_ABI sceAvPlayerInitEx(const SceAvPlayerInitDataEx* p_data,
|
||||||
|
@ -225,9 +225,7 @@ s32 PS4_SYSV_ABI sceAvPlayerInitEx(const SceAvPlayerInitDataEx* p_data,
|
||||||
// }
|
// }
|
||||||
// priorities.http_streaming_affinity = p_data->http_streaming_affinity;
|
// priorities.http_streaming_affinity = p_data->http_streaming_affinity;
|
||||||
|
|
||||||
const auto player = new AvPlayer();
|
*p_player = new AvPlayer(data, priorities);
|
||||||
player->Init(data, priorities);
|
|
||||||
*p_player = player;
|
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -290,13 +288,13 @@ s32 PS4_SYSV_ABI sceAvPlayerSetAvSyncMode(SceAvPlayerHandle handle,
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 PS4_SYSV_ABI sceAvPlayerSetLogCallback(SceAvPlayerLogCallback logCb, void* user_data) {
|
s32 PS4_SYSV_ABI sceAvPlayerSetLogCallback(SceAvPlayerLogCallback 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(SceAvPlayerHandle handle, bool loop_flag) {
|
||||||
LOG_ERROR(Lib_AvPlayer, "(STUBBED) called");
|
LOG_ERROR(Lib_AvPlayer, "(STUBBED) called, looping = {}", loop_flag);
|
||||||
if (handle == nullptr) {
|
if (handle == nullptr) {
|
||||||
return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS;
|
return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
|
|
||||||
#include "common/types.h"
|
#include "common/types.h"
|
||||||
|
|
||||||
|
#include <stddef.h> // size_t
|
||||||
|
|
||||||
namespace Core::Loader {
|
namespace Core::Loader {
|
||||||
class SymbolsResolver;
|
class SymbolsResolver;
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,23 +12,30 @@ extern "C" {
|
||||||
#include <libavformat/avio.h>
|
#include <libavformat/avio.h>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#include <algorithm> // std::max, std::min
|
||||||
|
|
||||||
#define AVPLAYER_AVIO_BUFFER_SIZE 4096
|
#define AVPLAYER_AVIO_BUFFER_SIZE 4096
|
||||||
|
|
||||||
namespace Libraries::AvPlayer {
|
namespace Libraries::AvPlayer {
|
||||||
|
|
||||||
AvPlayerFileStreamer::AvPlayerFileStreamer(SceAvPlayerFileReplacement& file_replacement,
|
AvPlayerFileStreamer::AvPlayerFileStreamer(const SceAvPlayerFileReplacement& file_replacement,
|
||||||
std::string_view path)
|
std::string_view path)
|
||||||
: m_file_replacement(file_replacement) {
|
: m_file_replacement(file_replacement) {
|
||||||
Init(path);
|
const auto ptr = m_file_replacement.object_ptr;
|
||||||
|
m_fd = m_file_replacement.open(ptr, path.data());
|
||||||
|
ASSERT(m_fd >= 0);
|
||||||
|
m_file_size = m_file_replacement.size(ptr);
|
||||||
|
// avio_buffer is deallocated in `avio_context_free`
|
||||||
|
const auto avio_buffer = reinterpret_cast<u8*>(av_malloc(AVPLAYER_AVIO_BUFFER_SIZE));
|
||||||
|
m_avio_context =
|
||||||
|
avio_alloc_context(avio_buffer, AVPLAYER_AVIO_BUFFER_SIZE, 0, this,
|
||||||
|
&AvPlayerFileStreamer::ReadPacket, nullptr, &AvPlayerFileStreamer::Seek);
|
||||||
}
|
}
|
||||||
|
|
||||||
AvPlayerFileStreamer::~AvPlayerFileStreamer() {
|
AvPlayerFileStreamer::~AvPlayerFileStreamer() {
|
||||||
if (m_avio_context != nullptr) {
|
if (m_avio_context != nullptr) {
|
||||||
avio_context_free(&m_avio_context);
|
avio_context_free(&m_avio_context);
|
||||||
}
|
}
|
||||||
if (m_avio_buffer != nullptr) {
|
|
||||||
av_free(m_avio_buffer);
|
|
||||||
}
|
|
||||||
if (m_file_replacement.close != nullptr && m_fd >= 0) {
|
if (m_file_replacement.close != nullptr && m_fd >= 0) {
|
||||||
const auto close = m_file_replacement.close;
|
const auto close = m_file_replacement.close;
|
||||||
const auto ptr = m_file_replacement.object_ptr;
|
const auto ptr = m_file_replacement.object_ptr;
|
||||||
|
@ -36,20 +43,6 @@ AvPlayerFileStreamer::~AvPlayerFileStreamer() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 AvPlayerFileStreamer::Init(std::string_view path) {
|
|
||||||
const auto ptr = m_file_replacement.object_ptr;
|
|
||||||
m_fd = m_file_replacement.open(ptr, path.data());
|
|
||||||
if (m_fd < 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
m_file_size = m_file_replacement.size(ptr);
|
|
||||||
m_avio_buffer = reinterpret_cast<u8*>(av_malloc(AVPLAYER_AVIO_BUFFER_SIZE));
|
|
||||||
m_avio_context =
|
|
||||||
avio_alloc_context(m_avio_buffer, AVPLAYER_AVIO_BUFFER_SIZE, 0, this,
|
|
||||||
&AvPlayerFileStreamer::ReadPacket, nullptr, &AvPlayerFileStreamer::Seek);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
s32 AvPlayerFileStreamer::ReadPacket(void* opaque, u8* buffer, s32 size) {
|
s32 AvPlayerFileStreamer::ReadPacket(void* opaque, u8* buffer, s32 size) {
|
||||||
const auto self = reinterpret_cast<AvPlayerFileStreamer*>(opaque);
|
const auto self = reinterpret_cast<AvPlayerFileStreamer*>(opaque);
|
||||||
if (self->m_position >= self->m_file_size) {
|
if (self->m_position >= self->m_file_size) {
|
||||||
|
@ -61,7 +54,7 @@ s32 AvPlayerFileStreamer::ReadPacket(void* opaque, u8* buffer, s32 size) {
|
||||||
const auto read_offset = self->m_file_replacement.readOffset;
|
const auto read_offset = self->m_file_replacement.readOffset;
|
||||||
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 (size != 0 && bytes_read == 0) {
|
if (bytes_read == 0 && size != 0) {
|
||||||
return AVERROR_EOF;
|
return AVERROR_EOF;
|
||||||
}
|
}
|
||||||
self->m_position += bytes_read;
|
self->m_position += bytes_read;
|
||||||
|
|
|
@ -15,7 +15,7 @@ namespace Libraries::AvPlayer {
|
||||||
|
|
||||||
class AvPlayerFileStreamer : public IDataStreamer {
|
class AvPlayerFileStreamer : public IDataStreamer {
|
||||||
public:
|
public:
|
||||||
AvPlayerFileStreamer(SceAvPlayerFileReplacement& file_replacement, std::string_view path);
|
AvPlayerFileStreamer(const SceAvPlayerFileReplacement& file_replacement, std::string_view path);
|
||||||
~AvPlayerFileStreamer();
|
~AvPlayerFileStreamer();
|
||||||
|
|
||||||
AVIOContext* GetContext() override {
|
AVIOContext* GetContext() override {
|
||||||
|
@ -23,8 +23,6 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
s32 Init(std::string_view path);
|
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
|
@ -33,7 +31,6 @@ private:
|
||||||
int m_fd = -1;
|
int m_fd = -1;
|
||||||
u64 m_position{};
|
u64 m_position{};
|
||||||
u64 m_file_size{};
|
u64 m_file_size{};
|
||||||
u8* m_avio_buffer{};
|
|
||||||
AVIOContext* m_avio_context{};
|
AVIOContext* m_avio_context{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -77,11 +77,9 @@ u64 PS4_SYSV_ABI AvPlayer::SizeFile(void* handle) {
|
||||||
return size(ptr);
|
return size(ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
AvPlayer::AvPlayer() : m_file_io_mutex(PTHREAD_MUTEX_ERRORCHECK, "SceAvPlayerFileIOLock") {}
|
AvPlayer::AvPlayer(const SceAvPlayerInitData& data, const ThreadPriorities& priorities)
|
||||||
|
: m_file_io_mutex(PTHREAD_MUTEX_ERRORCHECK, "AvPlayer_FileIO"), m_init_data(data),
|
||||||
void AvPlayer::Init(const SceAvPlayerInitData& data, const ThreadPriorities& priorities) {
|
m_init_data_original(data) {
|
||||||
m_init_data = data;
|
|
||||||
m_init_data_original = data;
|
|
||||||
|
|
||||||
m_init_data.memory_replacement.object_ptr = this;
|
m_init_data.memory_replacement.object_ptr = this;
|
||||||
m_init_data.memory_replacement.allocate = &AvPlayer::Allocate;
|
m_init_data.memory_replacement.allocate = &AvPlayer::Allocate;
|
||||||
|
@ -120,6 +118,9 @@ s32 AvPlayer::AddSource(std::string_view path) {
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 AvPlayer::GetStreamCount() {
|
s32 AvPlayer::GetStreamCount() {
|
||||||
|
if (m_state == nullptr) {
|
||||||
|
return ORBIS_AVPLAYER_ERROR_OPERATION_FAILED;
|
||||||
|
}
|
||||||
return m_state->GetStreamCount();
|
return m_state->GetStreamCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,11 +131,14 @@ s32 AvPlayer::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info) {
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 AvPlayer::EnableStream(u32 stream_id) {
|
s32 AvPlayer::EnableStream(u32 stream_index) {
|
||||||
if (m_state == nullptr) {
|
if (m_state == nullptr) {
|
||||||
return ORBIS_AVPLAYER_ERROR_OPERATION_FAILED;
|
return ORBIS_AVPLAYER_ERROR_OPERATION_FAILED;
|
||||||
}
|
}
|
||||||
return m_state->EnableStream(stream_id);
|
if (!m_state->EnableStream(stream_index)) {
|
||||||
|
return ORBIS_AVPLAYER_ERROR_OPERATION_FAILED;
|
||||||
|
}
|
||||||
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 AvPlayer::Start() {
|
s32 AvPlayer::Start() {
|
||||||
|
|
|
@ -33,15 +33,13 @@ public:
|
||||||
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);
|
||||||
|
|
||||||
AvPlayer();
|
AvPlayer(const SceAvPlayerInitData& data, const ThreadPriorities& priorities);
|
||||||
|
|
||||||
void Init(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);
|
||||||
s32 GetStreamCount();
|
s32 GetStreamCount();
|
||||||
s32 GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info);
|
s32 GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info);
|
||||||
s32 EnableStream(u32 stream_id);
|
s32 EnableStream(u32 stream_index);
|
||||||
s32 Start();
|
s32 Start();
|
||||||
bool GetAudioData(SceAvPlayerFrameInfo& audio_info);
|
bool GetAudioData(SceAvPlayerFrameInfo& audio_info);
|
||||||
bool GetVideoData(SceAvPlayerFrameInfo& video_info);
|
bool GetVideoData(SceAvPlayerFrameInfo& video_info);
|
||||||
|
|
|
@ -23,67 +23,47 @@ namespace Libraries::AvPlayer {
|
||||||
|
|
||||||
using namespace Kernel;
|
using namespace Kernel;
|
||||||
|
|
||||||
AvPlayerSource::AvPlayerSource(AvPlayerStateCallback& state) : m_state(state) {}
|
AvPlayerSource::AvPlayerSource(AvPlayerStateCallback& state, std::string_view path,
|
||||||
|
const SceAvPlayerInitData& init_data, ThreadPriorities& priorities,
|
||||||
AvPlayerSource::~AvPlayerSource() {
|
SceAvPlayerSourceType source_type)
|
||||||
if (!m_video_frame_storage.empty()) {
|
: m_state(state), m_priorities(priorities), m_memory_replacement(init_data.memory_replacement),
|
||||||
m_memory_replacement.deallocate(m_memory_replacement.object_ptr,
|
m_num_output_video_framebuffers(init_data.num_output_video_framebuffers) {
|
||||||
m_video_frame_storage.data());
|
AVFormatContext* context = avformat_alloc_context();
|
||||||
}
|
if (init_data.file_replacement.open != nullptr) {
|
||||||
if (!m_audio_frame_storage.empty()) {
|
m_up_data_streamer =
|
||||||
m_memory_replacement.deallocate(m_memory_replacement.object_ptr,
|
std::make_unique<AvPlayerFileStreamer>(init_data.file_replacement, path);
|
||||||
m_audio_frame_storage.data());
|
context->pb = m_up_data_streamer->GetContext();
|
||||||
}
|
ASSERT(!AVPLAYER_IS_ERROR(avformat_open_input(&context, nullptr, nullptr, nullptr)));
|
||||||
Stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
s32 AvPlayerSource::Init(std::string_view path, SceAvPlayerMemAllocator& memory_replacement,
|
|
||||||
SceAvPlayerFileReplacement& file_replacement, ThreadPriorities& priorities,
|
|
||||||
SceAvPlayerSourceType source_type) {
|
|
||||||
if (m_avformat_context != nullptr) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_priorities = priorities;
|
|
||||||
m_memory_replacement = memory_replacement;
|
|
||||||
|
|
||||||
m_avformat_context = avformat_alloc_context();
|
|
||||||
if (m_avformat_context == nullptr) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (file_replacement.open != nullptr) {
|
|
||||||
m_up_data_streamer = std::make_unique<AvPlayerFileStreamer>(file_replacement, path);
|
|
||||||
m_avformat_context->pb = m_up_data_streamer->GetContext();
|
|
||||||
if (avformat_open_input(&m_avformat_context, nullptr, nullptr, nullptr) < 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
const auto mnt = Common::Singleton<Core::FileSys::MntPoints>::Instance();
|
const auto mnt = Common::Singleton<Core::FileSys::MntPoints>::Instance();
|
||||||
const auto filepath = mnt->GetHostPath(path);
|
const auto filepath = mnt->GetHostPath(path);
|
||||||
if (AVPLAYER_IS_ERROR(avformat_open_input(&m_avformat_context, filepath.string().c_str(),
|
ASSERT(!AVPLAYER_IS_ERROR(
|
||||||
nullptr, nullptr))) {
|
avformat_open_input(&context, filepath.string().c_str(), nullptr, nullptr)));
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
m_avformat_context = AVFormatContextPtr(context, &ReleaseAVFormatContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
AvPlayerSource::~AvPlayerSource() {
|
||||||
|
Stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AvPlayerSource::FindStreamInfo() {
|
bool AvPlayerSource::FindStreamInfo() {
|
||||||
if (m_avformat_context == nullptr) {
|
if (m_avformat_context == nullptr) {
|
||||||
|
LOG_ERROR(Lib_AvPlayer, "Could not find stream info. NULL context.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (m_avformat_context->nb_streams > 0) {
|
if (m_avformat_context->nb_streams > 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return avformat_find_stream_info(m_avformat_context, nullptr) == 0;
|
return avformat_find_stream_info(m_avformat_context.get(), nullptr) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 AvPlayerSource::GetStreamCount() {
|
s32 AvPlayerSource::GetStreamCount() {
|
||||||
if (m_avformat_context == nullptr) {
|
if (m_avformat_context == nullptr) {
|
||||||
|
LOG_ERROR(Lib_AvPlayer, "Could not get stream count. NULL context.");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
LOG_DEBUG(Lib_AvPlayer, "num streams: {}", m_avformat_context->nb_streams);
|
LOG_INFO(Lib_AvPlayer, "Stream Count: {}", m_avformat_context->nb_streams);
|
||||||
return m_avformat_context->nb_streams;
|
return m_avformat_context->nb_streams;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,70 +88,68 @@ static f32 AVRationalToF32(AVRational rational) {
|
||||||
s32 AvPlayerSource::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info) {
|
s32 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);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
const auto p_stream = m_avformat_context->streams[stream_index];
|
const auto p_stream = m_avformat_context->streams[stream_index];
|
||||||
if (p_stream == nullptr || p_stream->codecpar == nullptr) {
|
if (p_stream == nullptr || p_stream->codecpar == nullptr) {
|
||||||
|
LOG_ERROR(Lib_AvPlayer, "Could not get stream {} info. NULL stream.", stream_index);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
info.type = CodecTypeToStreamType(p_stream->codecpar->codec_type);
|
info.type = CodecTypeToStreamType(p_stream->codecpar->codec_type);
|
||||||
info.start_time = p_stream->start_time;
|
info.start_time = p_stream->start_time;
|
||||||
info.duration = p_stream->duration;
|
info.duration = p_stream->duration;
|
||||||
const auto p_lang_node = av_dict_get(p_stream->metadata, "language", nullptr, 0);
|
const auto p_lang_node = av_dict_get(p_stream->metadata, "language", nullptr, 0);
|
||||||
if (p_lang_node == nullptr) {
|
if (p_lang_node != nullptr) {
|
||||||
return -1;
|
LOG_INFO(Lib_AvPlayer, "Stream {} language = {}", stream_index, p_lang_node->value);
|
||||||
|
} else {
|
||||||
|
LOG_WARNING(Lib_AvPlayer, "Stream {} language is unknown", stream_index);
|
||||||
}
|
}
|
||||||
LOG_DEBUG(Lib_AvPlayer, "Stream {} language = {}", stream_index, p_lang_node->value);
|
|
||||||
switch (info.type) {
|
switch (info.type) {
|
||||||
case SCE_AVPLAYER_VIDEO:
|
case SCE_AVPLAYER_VIDEO:
|
||||||
LOG_DEBUG(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 = AVRationalToF32(p_stream->codecpar->sample_aspect_ratio);
|
info.details.video.aspect_ratio = AVRationalToF32(p_stream->codecpar->sample_aspect_ratio);
|
||||||
info.details.video.width = p_stream->codecpar->width;
|
info.details.video.width = p_stream->codecpar->width;
|
||||||
info.details.video.height = p_stream->codecpar->height;
|
info.details.video.height = p_stream->codecpar->height;
|
||||||
|
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), 3ull));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case SCE_AVPLAYER_AUDIO:
|
case SCE_AVPLAYER_AUDIO:
|
||||||
LOG_DEBUG(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;
|
||||||
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) {
|
||||||
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), 3ull));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case SCE_AVPLAYER_TIMEDTEXT:
|
case SCE_AVPLAYER_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;
|
||||||
|
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), 3ull));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
LOG_ERROR(Lib_AvPlayer, "Stream {} type is unknown: {}.", stream_index, info.type);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static AVPixelFormat GetPreferredVideoFormat(AVCodecContext* s, const AVPixelFormat* fmt) {
|
bool AvPlayerSource::EnableStream(u32 stream_index) {
|
||||||
auto curr = fmt;
|
|
||||||
while (*curr != AV_PIX_FMT_NONE) {
|
|
||||||
LOG_TRACE(Lib_AvPlayer, "Supported format: {}", magic_enum::enum_name(*fmt));
|
|
||||||
if (*curr == AV_PIX_FMT_NV12) {
|
|
||||||
return AV_PIX_FMT_NV12;
|
|
||||||
}
|
|
||||||
++curr;
|
|
||||||
}
|
|
||||||
return AV_PIX_FMT_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
s32 AvPlayerSource::EnableStream(u32 stream_index) {
|
|
||||||
if (m_avformat_context == nullptr || stream_index >= m_avformat_context->nb_streams) {
|
if (m_avformat_context == nullptr || stream_index >= m_avformat_context->nb_streams) {
|
||||||
return -1;
|
return false;
|
||||||
}
|
}
|
||||||
const auto stream = m_avformat_context->streams[stream_index];
|
const auto stream = m_avformat_context->streams[stream_index];
|
||||||
const auto decoder = avcodec_find_decoder(stream->codecpar->codec_id);
|
const auto decoder = avcodec_find_decoder(stream->codecpar->codec_id);
|
||||||
if (decoder == nullptr) {
|
if (decoder == nullptr) {
|
||||||
return -1;
|
return false;
|
||||||
}
|
}
|
||||||
switch (stream->codecpar->codec_type) {
|
switch (stream->codecpar->codec_type) {
|
||||||
case AVMediaType::AVMEDIA_TYPE_VIDEO: {
|
case AVMediaType::AVMEDIA_TYPE_VIDEO: {
|
||||||
|
@ -179,10 +157,18 @@ s32 AvPlayerSource::EnableStream(u32 stream_index) {
|
||||||
m_video_codec_context =
|
m_video_codec_context =
|
||||||
AVCodecContextPtr(avcodec_alloc_context3(decoder), &ReleaseAVCodecContext);
|
AVCodecContextPtr(avcodec_alloc_context3(decoder), &ReleaseAVCodecContext);
|
||||||
if (avcodec_parameters_to_context(m_video_codec_context.get(), stream->codecpar) < 0) {
|
if (avcodec_parameters_to_context(m_video_codec_context.get(), stream->codecpar) < 0) {
|
||||||
return -1;
|
LOG_ERROR(Lib_AvPlayer, "Could not copy stream {} avcodec parameters to context.",
|
||||||
|
stream_index);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
if (avcodec_open2(m_video_codec_context.get(), decoder, nullptr) < 0) {
|
if (avcodec_open2(m_video_codec_context.get(), decoder, nullptr) < 0) {
|
||||||
return -1;
|
LOG_ERROR(Lib_AvPlayer, "Could not open avcodec for video stream {}.", stream_index);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const auto width = m_video_codec_context->width;
|
||||||
|
const auto size = (width * m_video_codec_context->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;
|
||||||
|
@ -192,10 +178,19 @@ s32 AvPlayerSource::EnableStream(u32 stream_index) {
|
||||||
m_audio_codec_context =
|
m_audio_codec_context =
|
||||||
AVCodecContextPtr(avcodec_alloc_context3(decoder), &ReleaseAVCodecContext);
|
AVCodecContextPtr(avcodec_alloc_context3(decoder), &ReleaseAVCodecContext);
|
||||||
if (avcodec_parameters_to_context(m_audio_codec_context.get(), stream->codecpar) < 0) {
|
if (avcodec_parameters_to_context(m_audio_codec_context.get(), stream->codecpar) < 0) {
|
||||||
return -1;
|
LOG_ERROR(Lib_AvPlayer, "Could not copy stream {} avcodec parameters to context.",
|
||||||
|
stream_index);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
if (avcodec_open2(m_audio_codec_context.get(), decoder, nullptr) < 0) {
|
if (avcodec_open2(m_audio_codec_context.get(), decoder, nullptr) < 0) {
|
||||||
return -1;
|
LOG_ERROR(Lib_AvPlayer, "Could not open avcodec for audio stream {}.", stream_index);
|
||||||
|
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 < 2; ++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;
|
||||||
|
@ -205,35 +200,7 @@ s32 AvPlayerSource::EnableStream(u32 stream_index) {
|
||||||
magic_enum::enum_name(stream->codecpar->codec_type), stream_index);
|
magic_enum::enum_name(stream->codecpar->codec_type), stream_index);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return 0;
|
return true;
|
||||||
}
|
|
||||||
|
|
||||||
u8* AvPlayerSource::GetVideoBuffer(AVFrame* frame) {
|
|
||||||
const auto size = (frame->width * frame->height * 3) / 2;
|
|
||||||
if (m_video_frame_storage.size() < size) {
|
|
||||||
if (!m_video_frame_storage.empty()) {
|
|
||||||
m_memory_replacement.deallocate(m_memory_replacement.object_ptr,
|
|
||||||
m_video_frame_storage.data());
|
|
||||||
}
|
|
||||||
const auto ptr = reinterpret_cast<u8*>(
|
|
||||||
m_memory_replacement.allocate(m_memory_replacement.object_ptr, frame->width, size));
|
|
||||||
m_video_frame_storage = std::span<u8>(ptr, size);
|
|
||||||
}
|
|
||||||
return m_video_frame_storage.data();
|
|
||||||
}
|
|
||||||
|
|
||||||
u8* AvPlayerSource::GetAudioBuffer(AVFrame* frame) {
|
|
||||||
const auto size = frame->ch_layout.nb_channels * frame->nb_samples * sizeof(u16);
|
|
||||||
if (m_audio_frame_storage.size() < size) {
|
|
||||||
if (!m_audio_frame_storage.empty()) {
|
|
||||||
m_memory_replacement.deallocate(m_memory_replacement.object_ptr,
|
|
||||||
m_audio_frame_storage.data());
|
|
||||||
}
|
|
||||||
const auto ptr = reinterpret_cast<u8*>(
|
|
||||||
m_memory_replacement.allocate(m_memory_replacement.object_ptr, 0x40, size));
|
|
||||||
m_audio_frame_storage = std::span<u8>(ptr, size);
|
|
||||||
}
|
|
||||||
return m_audio_frame_storage.data();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AvPlayerSource::SetLooping(bool is_looping) {
|
void AvPlayerSource::SetLooping(bool is_looping) {
|
||||||
|
@ -241,11 +208,12 @@ void AvPlayerSource::SetLooping(bool is_looping) {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<bool> AvPlayerSource::HasFrames(u32 num_frames) {
|
std::optional<bool> AvPlayerSource::HasFrames(u32 num_frames) {
|
||||||
return m_video_frames.Size() > num_frames;
|
return m_video_frames.Size() > num_frames || m_is_eof;
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 AvPlayerSource::Start() {
|
s32 AvPlayerSource::Start() {
|
||||||
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.");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
|
@ -258,6 +226,7 @@ s32 AvPlayerSource::Start() {
|
||||||
};
|
};
|
||||||
m_demuxer_thread = CreateThread(&DemuxerThread, demuxer_params);
|
m_demuxer_thread = CreateThread(&DemuxerThread, demuxer_params);
|
||||||
if (m_demuxer_thread == nullptr) {
|
if (m_demuxer_thread == nullptr) {
|
||||||
|
LOG_ERROR(Lib_AvPlayer, "Could not create DEMUXER thread.");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -271,6 +240,7 @@ s32 AvPlayerSource::Start() {
|
||||||
};
|
};
|
||||||
m_video_decoder_thread = CreateThread(&VideoDecoderThread, video_decoder_params);
|
m_video_decoder_thread = CreateThread(&VideoDecoderThread, video_decoder_params);
|
||||||
if (m_video_decoder_thread == nullptr) {
|
if (m_video_decoder_thread == nullptr) {
|
||||||
|
LOG_ERROR(Lib_AvPlayer, "Could not create VIDEO DECODER thread.");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -284,6 +254,7 @@ s32 AvPlayerSource::Start() {
|
||||||
};
|
};
|
||||||
m_audio_decoder_thread = CreateThread(&AudioDecoderThread, audio_decoder_params);
|
m_audio_decoder_thread = CreateThread(&AudioDecoderThread, audio_decoder_params);
|
||||||
if (m_audio_decoder_thread == nullptr) {
|
if (m_audio_decoder_thread == nullptr) {
|
||||||
|
LOG_ERROR(Lib_AvPlayer, "Could not create AUDIO DECODER thread.");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -292,6 +263,11 @@ s32 AvPlayerSource::Start() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AvPlayerSource::Stop() {
|
bool AvPlayerSource::Stop() {
|
||||||
|
if (m_is_stop) {
|
||||||
|
LOG_WARNING(Lib_AvPlayer, "Could not stop playback: already stopped.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
m_is_stop = true;
|
m_is_stop = true;
|
||||||
|
|
||||||
void* res = nullptr;
|
void* res = nullptr;
|
||||||
|
@ -304,6 +280,18 @@ bool AvPlayerSource::Stop() {
|
||||||
if (m_demuxer_thread != nullptr) {
|
if (m_demuxer_thread != nullptr) {
|
||||||
scePthreadJoin(m_demuxer_thread, &res);
|
scePthreadJoin(m_demuxer_thread, &res);
|
||||||
}
|
}
|
||||||
|
m_audio_packets.Clear();
|
||||||
|
m_video_packets.Clear();
|
||||||
|
m_audio_frames.Clear();
|
||||||
|
m_video_frames.Clear();
|
||||||
|
if (m_current_audio_frame.has_value()) {
|
||||||
|
m_audio_buffers.Push(std::move(m_current_audio_frame.value()));
|
||||||
|
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();
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -333,44 +321,27 @@ bool AvPlayerSource::GetVideoData(SceAvPlayerFrameInfoEx& video_info) {
|
||||||
|
|
||||||
auto frame = m_video_frames.Pop();
|
auto frame = m_video_frames.Pop();
|
||||||
if (!frame.has_value()) {
|
if (!frame.has_value()) {
|
||||||
|
LOG_WARNING(Lib_AvPlayer, "Could get video frame: no frames.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& up_frame = *frame;
|
|
||||||
|
|
||||||
const auto pkt_dts = u64(up_frame->pkt_dts) * 1000;
|
|
||||||
const auto stream = m_avformat_context->streams[m_video_stream_index.value()];
|
|
||||||
const auto time_base = stream->time_base;
|
|
||||||
const auto den = time_base.den;
|
|
||||||
const auto num = time_base.num;
|
|
||||||
const auto timestamp = (num != 0 && den > 1) ? (pkt_dts * num) / den : pkt_dts;
|
|
||||||
|
|
||||||
{
|
{
|
||||||
using namespace std::chrono;
|
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 < timestamp) {
|
while (elapsed_time < frame->info.timestamp) {
|
||||||
sceKernelUsleep((timestamp - elapsed_time) * 1000);
|
sceKernelUsleep((frame->info.timestamp - elapsed_time) * 1000);
|
||||||
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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto buffer = GetVideoBuffer(up_frame.get());
|
// return the buffer to the queue
|
||||||
|
if (m_current_video_frame.has_value()) {
|
||||||
CopyNV12Data(buffer, *up_frame);
|
m_video_buffers.Push(std::move(m_current_video_frame.value()));
|
||||||
|
}
|
||||||
video_info = {};
|
m_current_video_frame = std::move(frame->buffer);
|
||||||
video_info.timestamp = timestamp;
|
video_info = frame->info;
|
||||||
video_info.pData = buffer;
|
|
||||||
video_info.details.video.width = up_frame->width;
|
|
||||||
video_info.details.video.height = up_frame->height;
|
|
||||||
video_info.details.video.aspect_ratio = AVRationalToF32(up_frame->sample_aspect_ratio);
|
|
||||||
video_info.details.video.pitch = up_frame->linesize[0];
|
|
||||||
video_info.details.video.luma_bit_depth = 8;
|
|
||||||
video_info.details.video.chroma_bit_depth = 8;
|
|
||||||
|
|
||||||
m_last_video_timestamp = timestamp;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -381,45 +352,38 @@ bool AvPlayerSource::GetAudioData(SceAvPlayerFrameInfo& audio_info) {
|
||||||
|
|
||||||
auto frame = m_audio_frames.Pop();
|
auto frame = m_audio_frames.Pop();
|
||||||
if (!frame.has_value()) {
|
if (!frame.has_value()) {
|
||||||
|
LOG_WARNING(Lib_AvPlayer, "Could get audio frame: no frames.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& up_frame = *frame;
|
|
||||||
|
|
||||||
const auto pkt_dts = u64(up_frame->pkt_dts) * 1000;
|
|
||||||
const auto stream = m_avformat_context->streams[m_audio_stream_index.value()];
|
|
||||||
const auto time_base = stream->time_base;
|
|
||||||
const auto den = time_base.den;
|
|
||||||
const auto num = time_base.num;
|
|
||||||
const auto timestamp = (num != 0 && den > 1) ? (pkt_dts * num) / den : pkt_dts;
|
|
||||||
|
|
||||||
{
|
{
|
||||||
using namespace std::chrono;
|
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 < timestamp) {
|
while (elapsed_time < frame->info.timestamp) {
|
||||||
sceKernelUsleep((timestamp - elapsed_time) * 1000);
|
sceKernelUsleep((frame->info.timestamp - elapsed_time) * 1000);
|
||||||
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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto buffer = GetAudioBuffer(up_frame.get());
|
// return the buffer to the queue
|
||||||
const auto size = up_frame->ch_layout.nb_channels * up_frame->nb_samples * sizeof(u16);
|
if (m_current_audio_frame.has_value()) {
|
||||||
std::memcpy(buffer, up_frame->data[0], size);
|
m_audio_buffers.Push(std::move(m_current_audio_frame.value()));
|
||||||
|
}
|
||||||
|
m_current_audio_frame = std::move(frame->buffer);
|
||||||
|
|
||||||
audio_info = {};
|
audio_info = {};
|
||||||
audio_info.timestamp = timestamp;
|
audio_info.timestamp = frame->info.timestamp;
|
||||||
audio_info.pData = m_audio_frame_storage.data();
|
audio_info.pData = reinterpret_cast<u8*>(frame->info.pData);
|
||||||
audio_info.details.audio.size = u32(m_audio_frame_storage.size());
|
audio_info.details.audio.size = frame->info.details.audio.size;
|
||||||
audio_info.details.audio.channel_count = up_frame->ch_layout.nb_channels;
|
audio_info.details.audio.channel_count = frame->info.details.audio.channel_count;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 AvPlayerSource::CurrentTime() {
|
u64 AvPlayerSource::CurrentTime() {
|
||||||
// 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).count();
|
||||||
return m_last_video_timestamp;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AvPlayerSource::IsActive() {
|
bool AvPlayerSource::IsActive() {
|
||||||
|
@ -457,12 +421,19 @@ void AvPlayerSource::ReleaseSWSContext(SwsContext* context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AvPlayerSource::ReleaseAVFormatContext(AVFormatContext* context) {
|
||||||
|
if (context != nullptr) {
|
||||||
|
avformat_close_input(&context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void* AvPlayerSource::DemuxerThread(void* opaque) {
|
void* AvPlayerSource::DemuxerThread(void* opaque) {
|
||||||
LOG_TRACE(Lib_AvPlayer, "Demuxer Thread started");
|
|
||||||
const auto self = reinterpret_cast<AvPlayerSource*>(opaque);
|
const auto self = reinterpret_cast<AvPlayerSource*>(opaque);
|
||||||
if (!self->m_audio_stream_index.has_value() && !self->m_video_stream_index.has_value()) {
|
if (!self->m_audio_stream_index.has_value() && !self->m_video_stream_index.has_value()) {
|
||||||
|
LOG_WARNING(Lib_AvPlayer, "Could not start DEMUXER thread. No streams enabled.");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
LOG_INFO(Lib_AvPlayer, "Demuxer Thread started");
|
||||||
|
|
||||||
while (!self->m_is_stop) {
|
while (!self->m_is_stop) {
|
||||||
if (self->m_video_packets.Size() > 60) {
|
if (self->m_video_packets.Size() > 60) {
|
||||||
|
@ -470,10 +441,10 @@ void* AvPlayerSource::DemuxerThread(void* opaque) {
|
||||||
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, up_packet.get());
|
const auto res = av_read_frame(self->m_avformat_context.get(), up_packet.get());
|
||||||
if (res < 0) {
|
if (res < 0) {
|
||||||
if (res == AVERROR_EOF) {
|
if (res == AVERROR_EOF) {
|
||||||
LOG_TRACE(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);
|
||||||
|
@ -500,7 +471,7 @@ void* AvPlayerSource::DemuxerThread(void* opaque) {
|
||||||
}
|
}
|
||||||
self->m_state.OnEOF();
|
self->m_state.OnEOF();
|
||||||
|
|
||||||
LOG_TRACE(Lib_AvPlayer, "Demuxer Thread exited normaly");
|
LOG_INFO(Lib_AvPlayer, "Demuxer Thread exited normaly");
|
||||||
scePthreadExit(0);
|
scePthreadExit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -531,12 +502,47 @@ AvPlayerSource::AVFramePtr AvPlayerSource::ConvertVideoFrame(const AVFrame& fram
|
||||||
return nv12_frame;
|
return nv12_frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Frame AvPlayerSource::PrepareVideoFrame(FrameBuffer buffer, const AVFrame& frame) {
|
||||||
|
ASSERT(frame.format == AV_PIX_FMT_NV12);
|
||||||
|
|
||||||
|
auto p_buffer = buffer.GetBuffer();
|
||||||
|
CopyNV12Data(p_buffer, frame);
|
||||||
|
|
||||||
|
const auto pkt_dts = u64(frame.pkt_dts) * 1000;
|
||||||
|
const auto stream = m_avformat_context->streams[m_video_stream_index.value()];
|
||||||
|
const auto time_base = stream->time_base;
|
||||||
|
const auto den = time_base.den;
|
||||||
|
const auto num = time_base.num;
|
||||||
|
const auto timestamp = (num != 0 && den > 1) ? (pkt_dts * num) / den : pkt_dts;
|
||||||
|
|
||||||
|
return Frame{
|
||||||
|
.buffer = std::move(buffer),
|
||||||
|
.info =
|
||||||
|
{
|
||||||
|
.pData = p_buffer,
|
||||||
|
.timestamp = timestamp,
|
||||||
|
.details =
|
||||||
|
{
|
||||||
|
.video =
|
||||||
|
{
|
||||||
|
.width = u32(frame.width),
|
||||||
|
.height = u32(frame.height),
|
||||||
|
.aspect_ratio = AVRationalToF32(frame.sample_aspect_ratio),
|
||||||
|
.pitch = u32(frame.linesize[0]),
|
||||||
|
.luma_bit_depth = 8,
|
||||||
|
.chroma_bit_depth = 8,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
void* AvPlayerSource::VideoDecoderThread(void* opaque) {
|
void* AvPlayerSource::VideoDecoderThread(void* opaque) {
|
||||||
LOG_TRACE(Lib_AvPlayer, "Video Decoder Thread started");
|
LOG_INFO(Lib_AvPlayer, "Video Decoder Thread started");
|
||||||
const auto self = reinterpret_cast<AvPlayerSource*>(opaque);
|
const auto self = reinterpret_cast<AvPlayerSource*>(opaque);
|
||||||
|
|
||||||
while ((!self->m_is_eof || self->m_video_packets.Size() != 0) && !self->m_is_stop) {
|
while ((!self->m_is_eof || self->m_video_packets.Size() != 0) && !self->m_is_stop) {
|
||||||
if (self->m_video_frames.Size() > 60 || self->m_video_packets.Size() == 0) {
|
if (self->m_video_packets.Size() == 0) {
|
||||||
sceKernelUsleep(5000);
|
sceKernelUsleep(5000);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -544,6 +550,7 @@ void* AvPlayerSource::VideoDecoderThread(void* opaque) {
|
||||||
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(self->m_video_codec_context.get(), packet->get());
|
||||||
if (res < 0 && res != AVERROR(EAGAIN)) {
|
if (res < 0 && res != AVERROR(EAGAIN)) {
|
||||||
self->m_state.OnError();
|
self->m_state.OnError();
|
||||||
|
@ -552,11 +559,15 @@ void* AvPlayerSource::VideoDecoderThread(void* opaque) {
|
||||||
scePthreadExit(nullptr);
|
scePthreadExit(nullptr);
|
||||||
}
|
}
|
||||||
while (res >= 0) {
|
while (res >= 0) {
|
||||||
|
if (self->m_video_buffers.Size() == 0 && !self->m_is_stop) {
|
||||||
|
sceKernelUsleep(5000);
|
||||||
|
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(self->m_video_codec_context.get(), up_frame.get());
|
||||||
if (res < 0) {
|
if (res < 0) {
|
||||||
if (res == AVERROR_EOF) {
|
if (res == AVERROR_EOF) {
|
||||||
LOG_TRACE(Lib_AvPlayer, "EOF reached in video decoder");
|
LOG_INFO(Lib_AvPlayer, "EOF reached in video decoder");
|
||||||
scePthreadExit(nullptr);
|
scePthreadExit(nullptr);
|
||||||
} else if (res != AVERROR(EAGAIN)) {
|
} else if (res != AVERROR(EAGAIN)) {
|
||||||
LOG_ERROR(Lib_AvPlayer,
|
LOG_ERROR(Lib_AvPlayer,
|
||||||
|
@ -566,18 +577,26 @@ void* AvPlayerSource::VideoDecoderThread(void* opaque) {
|
||||||
scePthreadExit(nullptr);
|
scePthreadExit(nullptr);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
LOG_TRACE(Lib_AvPlayer, "Producing Video Frame. Num Frames: {}",
|
auto buffer = self->m_video_buffers.Pop();
|
||||||
self->m_video_frames.Size());
|
if (!buffer.has_value()) {
|
||||||
if (up_frame->format != AV_PIX_FMT_NV12) {
|
// Video buffers queue was cleared. This means that player was stopped.
|
||||||
self->m_video_frames.Push(self->ConvertVideoFrame(*up_frame));
|
break;
|
||||||
} else {
|
|
||||||
self->m_video_frames.Push(std::move(up_frame));
|
|
||||||
}
|
}
|
||||||
|
if (up_frame->format != AV_PIX_FMT_NV12) {
|
||||||
|
const auto nv12_frame = self->ConvertVideoFrame(*up_frame);
|
||||||
|
self->m_video_frames.Push(
|
||||||
|
self->PrepareVideoFrame(std::move(buffer.value()), *nv12_frame));
|
||||||
|
} else {
|
||||||
|
self->m_video_frames.Push(
|
||||||
|
self->PrepareVideoFrame(std::move(buffer.value()), *up_frame));
|
||||||
|
}
|
||||||
|
LOG_TRACE(Lib_AvPlayer, "Produced Video Frame. Num Frames: {}",
|
||||||
|
self->m_video_frames.Size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_TRACE(Lib_AvPlayer, "Video Decoder Thread exited normaly");
|
LOG_INFO(Lib_AvPlayer, "Video Decoder Thread exited normaly");
|
||||||
scePthreadExit(nullptr);
|
scePthreadExit(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -607,12 +626,45 @@ AvPlayerSource::AVFramePtr AvPlayerSource::ConvertAudioFrame(const AVFrame& fram
|
||||||
return pcm16_frame;
|
return pcm16_frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Frame AvPlayerSource::PrepareAudioFrame(FrameBuffer buffer, const AVFrame& frame) {
|
||||||
|
ASSERT(frame.format == AV_SAMPLE_FMT_S16);
|
||||||
|
ASSERT(frame.nb_samples <= 1024);
|
||||||
|
|
||||||
|
auto p_buffer = buffer.GetBuffer();
|
||||||
|
const auto size = frame.ch_layout.nb_channels * frame.nb_samples * sizeof(u16);
|
||||||
|
std::memcpy(p_buffer, frame.data[0], size);
|
||||||
|
|
||||||
|
const auto pkt_dts = u64(frame.pkt_dts) * 1000;
|
||||||
|
const auto stream = m_avformat_context->streams[m_audio_stream_index.value()];
|
||||||
|
const auto time_base = stream->time_base;
|
||||||
|
const auto den = time_base.den;
|
||||||
|
const auto num = time_base.num;
|
||||||
|
const auto timestamp = (num != 0 && den > 1) ? (pkt_dts * num) / den : pkt_dts;
|
||||||
|
|
||||||
|
return Frame{
|
||||||
|
.buffer = std::move(buffer),
|
||||||
|
.info =
|
||||||
|
{
|
||||||
|
.pData = p_buffer,
|
||||||
|
.timestamp = timestamp,
|
||||||
|
.details =
|
||||||
|
{
|
||||||
|
.audio =
|
||||||
|
{
|
||||||
|
.channel_count = u16(frame.ch_layout.nb_channels),
|
||||||
|
.size = u32(size),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
void* AvPlayerSource::AudioDecoderThread(void* opaque) {
|
void* AvPlayerSource::AudioDecoderThread(void* opaque) {
|
||||||
LOG_TRACE(Lib_AvPlayer, "Audio Decoder Thread started");
|
LOG_INFO(Lib_AvPlayer, "Audio Decoder Thread started");
|
||||||
const auto self = reinterpret_cast<AvPlayerSource*>(opaque);
|
const auto self = reinterpret_cast<AvPlayerSource*>(opaque);
|
||||||
|
|
||||||
while ((!self->m_is_eof || self->m_audio_packets.Size() != 0) && !self->m_is_stop) {
|
while ((!self->m_is_eof || self->m_audio_packets.Size() != 0) && !self->m_is_stop) {
|
||||||
if (self->m_audio_frames.Size() > 60 || self->m_audio_packets.Size() == 0) {
|
if (self->m_audio_packets.Size() == 0) {
|
||||||
sceKernelUsleep(5000);
|
sceKernelUsleep(5000);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -628,11 +680,15 @@ void* AvPlayerSource::AudioDecoderThread(void* opaque) {
|
||||||
scePthreadExit(nullptr);
|
scePthreadExit(nullptr);
|
||||||
}
|
}
|
||||||
while (res >= 0) {
|
while (res >= 0) {
|
||||||
|
if (self->m_audio_buffers.Size() == 0 && !self->m_is_stop) {
|
||||||
|
sceKernelUsleep(5000);
|
||||||
|
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(self->m_audio_codec_context.get(), up_frame.get());
|
||||||
if (res < 0) {
|
if (res < 0) {
|
||||||
if (res == AVERROR_EOF) {
|
if (res == AVERROR_EOF) {
|
||||||
LOG_TRACE(Lib_AvPlayer, "EOF reached in audio decoder");
|
LOG_INFO(Lib_AvPlayer, "EOF reached in audio decoder");
|
||||||
scePthreadExit(nullptr);
|
scePthreadExit(nullptr);
|
||||||
} else if (res != AVERROR(EAGAIN)) {
|
} else if (res != AVERROR(EAGAIN)) {
|
||||||
self->m_state.OnError();
|
self->m_state.OnError();
|
||||||
|
@ -642,16 +698,26 @@ void* AvPlayerSource::AudioDecoderThread(void* opaque) {
|
||||||
scePthreadExit(nullptr);
|
scePthreadExit(nullptr);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (up_frame->format != AV_SAMPLE_FMT_S16) {
|
auto buffer = self->m_audio_buffers.Pop();
|
||||||
self->m_audio_frames.Push(self->ConvertAudioFrame(*up_frame));
|
if (!buffer.has_value()) {
|
||||||
} else {
|
// Audio buffers queue was cleared. This means that player was stopped.
|
||||||
self->m_audio_frames.Push(std::move(up_frame));
|
break;
|
||||||
}
|
}
|
||||||
|
if (up_frame->format != AV_SAMPLE_FMT_S16) {
|
||||||
|
const auto pcm16_frame = self->ConvertAudioFrame(*up_frame);
|
||||||
|
self->m_audio_frames.Push(
|
||||||
|
self->PrepareAudioFrame(std::move(buffer.value()), *pcm16_frame));
|
||||||
|
} else {
|
||||||
|
self->m_audio_frames.Push(
|
||||||
|
self->PrepareAudioFrame(std::move(buffer.value()), *up_frame));
|
||||||
|
}
|
||||||
|
LOG_TRACE(Lib_AvPlayer, "Produced Audio Frame. Num Frames: {}",
|
||||||
|
self->m_audio_frames.Size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_TRACE(Lib_AvPlayer, "Audio Decoder Thread exited normaly");
|
LOG_INFO(Lib_AvPlayer, "Audio Decoder Thread exited normaly");
|
||||||
scePthreadExit(nullptr);
|
scePthreadExit(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,11 +38,13 @@ class FrameBuffer {
|
||||||
public:
|
public:
|
||||||
FrameBuffer(const SceAvPlayerMemAllocator& memory_replacement, u32 align, u32 size) noexcept
|
FrameBuffer(const SceAvPlayerMemAllocator& memory_replacement, u32 align, u32 size) noexcept
|
||||||
: m_memory_replacement(memory_replacement),
|
: m_memory_replacement(memory_replacement),
|
||||||
m_data(Allocate(memory_replacement, align, size), size) {}
|
m_data(Allocate(memory_replacement, align, size)) {
|
||||||
|
ASSERT_MSG(m_data, "Could not allocated frame buffer.");
|
||||||
|
}
|
||||||
|
|
||||||
~FrameBuffer() {
|
~FrameBuffer() {
|
||||||
if (!m_data.empty()) {
|
if (m_data != nullptr) {
|
||||||
Deallocate(m_memory_replacement, m_data.data());
|
Deallocate(m_memory_replacement, m_data);
|
||||||
m_data = {};
|
m_data = {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,17 +54,16 @@ public:
|
||||||
|
|
||||||
FrameBuffer(FrameBuffer&& r) noexcept
|
FrameBuffer(FrameBuffer&& 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) {
|
||||||
r.m_data = {};
|
r.m_data = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
FrameBuffer& operator=(FrameBuffer&& r) noexcept {
|
FrameBuffer& operator=(FrameBuffer&& r) noexcept {
|
||||||
m_memory_replacement = r.m_memory_replacement;
|
|
||||||
std::swap(m_data, r.m_data);
|
std::swap(m_data, r.m_data);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
u8* GetBuffer() const noexcept {
|
u8* GetBuffer() const noexcept {
|
||||||
return m_data.data();
|
return m_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -75,23 +76,26 @@ private:
|
||||||
memory_replacement.deallocate(memory_replacement.object_ptr, ptr);
|
memory_replacement.deallocate(memory_replacement.object_ptr, ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
SceAvPlayerMemAllocator m_memory_replacement;
|
const SceAvPlayerMemAllocator& m_memory_replacement;
|
||||||
std::span<u8> m_data;
|
u8* m_data = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Frame {
|
||||||
|
FrameBuffer buffer;
|
||||||
|
SceAvPlayerFrameInfoEx info;
|
||||||
};
|
};
|
||||||
|
|
||||||
class AvPlayerSource {
|
class AvPlayerSource {
|
||||||
public:
|
public:
|
||||||
AvPlayerSource(AvPlayerStateCallback& state);
|
AvPlayerSource(AvPlayerStateCallback& state, std::string_view path,
|
||||||
~AvPlayerSource();
|
const SceAvPlayerInitData& init_data, ThreadPriorities& priorities,
|
||||||
|
|
||||||
s32 Init(std::string_view path, SceAvPlayerMemAllocator& memory_replacement,
|
|
||||||
SceAvPlayerFileReplacement& file_replacement, ThreadPriorities& priorities,
|
|
||||||
SceAvPlayerSourceType source_type);
|
SceAvPlayerSourceType source_type);
|
||||||
|
~AvPlayerSource();
|
||||||
|
|
||||||
bool FindStreamInfo();
|
bool FindStreamInfo();
|
||||||
s32 GetStreamCount();
|
s32 GetStreamCount();
|
||||||
s32 GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info);
|
s32 GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info);
|
||||||
s32 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);
|
||||||
s32 Start();
|
s32 Start();
|
||||||
|
@ -114,52 +118,55 @@ private:
|
||||||
static void ReleaseAVCodecContext(AVCodecContext* context);
|
static void ReleaseAVCodecContext(AVCodecContext* context);
|
||||||
static void ReleaseSWRContext(SwrContext* context);
|
static void ReleaseSWRContext(SwrContext* context);
|
||||||
static void ReleaseSWSContext(SwsContext* context);
|
static void ReleaseSWSContext(SwsContext* context);
|
||||||
|
static void ReleaseAVFormatContext(AVFormatContext* context);
|
||||||
|
|
||||||
using AVPacketPtr = std::unique_ptr<AVPacket, decltype(&ReleaseAVPacket)>;
|
using AVPacketPtr = std::unique_ptr<AVPacket, decltype(&ReleaseAVPacket)>;
|
||||||
using AVFramePtr = std::unique_ptr<AVFrame, decltype(&ReleaseAVFrame)>;
|
using AVFramePtr = std::unique_ptr<AVFrame, decltype(&ReleaseAVFrame)>;
|
||||||
using AVCodecContextPtr = std::unique_ptr<AVCodecContext, decltype(&ReleaseAVCodecContext)>;
|
using AVCodecContextPtr = std::unique_ptr<AVCodecContext, decltype(&ReleaseAVCodecContext)>;
|
||||||
using SWRContextPtr = std::unique_ptr<SwrContext, decltype(&ReleaseSWRContext)>;
|
using SWRContextPtr = std::unique_ptr<SwrContext, decltype(&ReleaseSWRContext)>;
|
||||||
using SWSContextPtr = std::unique_ptr<SwsContext, decltype(&ReleaseSWSContext)>;
|
using SWSContextPtr = std::unique_ptr<SwsContext, decltype(&ReleaseSWSContext)>;
|
||||||
|
using AVFormatContextPtr = std::unique_ptr<AVFormatContext, decltype(&ReleaseAVFormatContext)>;
|
||||||
u8* GetVideoBuffer(AVFrame* frame);
|
|
||||||
u8* GetAudioBuffer(AVFrame* frame);
|
|
||||||
|
|
||||||
AVFramePtr ConvertAudioFrame(const AVFrame& frame);
|
AVFramePtr ConvertAudioFrame(const AVFrame& frame);
|
||||||
AVFramePtr ConvertVideoFrame(const AVFrame& frame);
|
AVFramePtr ConvertVideoFrame(const AVFrame& frame);
|
||||||
|
|
||||||
u64 m_last_video_timestamp{};
|
Frame PrepareAudioFrame(FrameBuffer buffer, const AVFrame& frame);
|
||||||
|
Frame PrepareVideoFrame(FrameBuffer buffer, const AVFrame& frame);
|
||||||
|
|
||||||
AvPlayerStateCallback& m_state;
|
AvPlayerStateCallback& m_state;
|
||||||
|
|
||||||
ThreadPriorities m_priorities;
|
ThreadPriorities m_priorities{};
|
||||||
SceAvPlayerMemAllocator m_memory_replacement;
|
SceAvPlayerMemAllocator m_memory_replacement{};
|
||||||
|
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::atomic_bool m_is_stop = false;
|
||||||
|
|
||||||
std::unique_ptr<IDataStreamer> m_up_data_streamer;
|
std::unique_ptr<IDataStreamer> m_up_data_streamer;
|
||||||
|
|
||||||
AVFormatContext* m_avformat_context{};
|
AvPlayerQueue<FrameBuffer> m_audio_buffers;
|
||||||
|
AvPlayerQueue<FrameBuffer> m_video_buffers;
|
||||||
|
|
||||||
AvPlayerQueue<AVPacketPtr> m_audio_packets;
|
AvPlayerQueue<AVPacketPtr> m_audio_packets;
|
||||||
AvPlayerQueue<AVPacketPtr> m_video_packets;
|
AvPlayerQueue<AVPacketPtr> m_video_packets;
|
||||||
|
|
||||||
AvPlayerQueue<AVFramePtr> m_audio_frames;
|
AvPlayerQueue<Frame> m_audio_frames;
|
||||||
AvPlayerQueue<AVFramePtr> m_video_frames;
|
AvPlayerQueue<Frame> m_video_frames;
|
||||||
|
|
||||||
std::span<u8> m_video_frame_storage;
|
std::optional<FrameBuffer> m_current_video_frame;
|
||||||
std::span<u8> m_audio_frame_storage;
|
std::optional<FrameBuffer> m_current_audio_frame;
|
||||||
|
|
||||||
AVCodecContextPtr m_video_codec_context{nullptr, &ReleaseAVCodecContext};
|
std::optional<s32> m_video_stream_index{};
|
||||||
AVCodecContextPtr m_audio_codec_context{nullptr, &ReleaseAVCodecContext};
|
std::optional<s32> m_audio_stream_index{};
|
||||||
|
|
||||||
std::optional<int> m_video_stream_index{};
|
|
||||||
std::optional<int> m_audio_stream_index{};
|
|
||||||
|
|
||||||
ScePthread m_demuxer_thread{};
|
ScePthread m_demuxer_thread{};
|
||||||
ScePthread m_video_decoder_thread{};
|
ScePthread m_video_decoder_thread{};
|
||||||
ScePthread m_audio_decoder_thread{};
|
ScePthread m_audio_decoder_thread{};
|
||||||
|
|
||||||
|
AVFormatContextPtr m_avformat_context{nullptr, &ReleaseAVFormatContext};
|
||||||
|
AVCodecContextPtr m_video_codec_context{nullptr, &ReleaseAVCodecContext};
|
||||||
|
AVCodecContextPtr m_audio_codec_context{nullptr, &ReleaseAVCodecContext};
|
||||||
SWRContextPtr m_swr_context{nullptr, &ReleaseSWRContext};
|
SWRContextPtr m_swr_context{nullptr, &ReleaseSWRContext};
|
||||||
SWSContextPtr m_sws_context{nullptr, &ReleaseSWSContext};
|
SWSContextPtr m_sws_context{nullptr, &ReleaseSWSContext};
|
||||||
|
|
||||||
|
|
|
@ -39,39 +39,30 @@ void PS4_SYSV_ABI AvPlayerState::AutoPlayEventCallback(void* opaque, s32 event_i
|
||||||
switch (info.type) {
|
switch (info.type) {
|
||||||
case SCE_AVPLAYER_VIDEO:
|
case SCE_AVPLAYER_VIDEO:
|
||||||
if (video_stream_index == -1) {
|
if (video_stream_index == -1) {
|
||||||
if (default_language.empty()) {
|
|
||||||
video_stream_index = stream_index;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (default_language ==
|
|
||||||
reinterpret_cast<char*>(info.details.video.language_code)) {
|
|
||||||
video_stream_index = stream_index;
|
video_stream_index = stream_index;
|
||||||
}
|
}
|
||||||
|
if (!default_language.empty() &&
|
||||||
|
default_language == reinterpret_cast<char*>(info.details.video.language_code)) {
|
||||||
|
video_stream_index = stream_index;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SCE_AVPLAYER_AUDIO:
|
case SCE_AVPLAYER_AUDIO:
|
||||||
if (audio_stream_index == -1) {
|
if (audio_stream_index == -1) {
|
||||||
if (default_language.empty()) {
|
|
||||||
audio_stream_index = stream_index;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (default_language ==
|
|
||||||
reinterpret_cast<char*>(info.details.video.language_code)) {
|
|
||||||
audio_stream_index = stream_index;
|
audio_stream_index = stream_index;
|
||||||
}
|
}
|
||||||
|
if (!default_language.empty() &&
|
||||||
|
default_language == reinterpret_cast<char*>(info.details.video.language_code)) {
|
||||||
|
audio_stream_index = stream_index;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SCE_AVPLAYER_TIMEDTEXT:
|
case SCE_AVPLAYER_TIMEDTEXT:
|
||||||
if (timedtext_stream_index == -1) {
|
|
||||||
if (default_language.empty()) {
|
if (default_language.empty()) {
|
||||||
timedtext_stream_index = stream_index;
|
timedtext_stream_index = stream_index;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (default_language ==
|
if (default_language == reinterpret_cast<char*>(info.details.video.language_code)) {
|
||||||
reinterpret_cast<char*>(info.details.video.language_code)) {
|
|
||||||
timedtext_stream_index = stream_index;
|
timedtext_stream_index = stream_index;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -89,8 +80,9 @@ void PS4_SYSV_ABI AvPlayerState::AutoPlayEventCallback(void* opaque, s32 event_i
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto callback = self->m_user_event_replacement.event_callback;
|
// Pass other events to the game
|
||||||
const auto ptr = self->m_user_event_replacement.object_ptr;
|
const auto callback = self->m_event_replacement.event_callback;
|
||||||
|
const auto ptr = self->m_event_replacement.object_ptr;
|
||||||
if (callback != nullptr) {
|
if (callback != nullptr) {
|
||||||
callback(ptr, event_id, 0, event_data);
|
callback(ptr, event_id, 0, event_data);
|
||||||
}
|
}
|
||||||
|
@ -99,34 +91,15 @@ 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)
|
const ThreadPriorities& priorities)
|
||||||
: m_event_handler_mutex(PTHREAD_MUTEX_ERRORCHECK, "SceAvPlayerEventHandler"),
|
: m_init_data(init_data), m_event_replacement(init_data.event_replacement),
|
||||||
m_state_machine_mutex(PTHREAD_MUTEX_ERRORCHECK, "SceAvPlayerStateMachine") {
|
m_thread_priorities(priorities),
|
||||||
if (init_data.event_replacement.event_callback == nullptr || init_data.auto_start) {
|
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) {
|
||||||
m_auto_start = true;
|
m_auto_start = true;
|
||||||
m_event_replacement.event_callback = &AvPlayerState::AutoPlayEventCallback;
|
m_init_data.event_replacement.event_callback = &AvPlayerState::AutoPlayEventCallback;
|
||||||
m_event_replacement.object_ptr = this;
|
m_init_data.event_replacement.object_ptr = this;
|
||||||
} else {
|
|
||||||
m_event_replacement = init_data.event_replacement;
|
|
||||||
}
|
}
|
||||||
m_user_event_replacement = init_data.event_replacement;
|
|
||||||
|
|
||||||
const auto& memory_replacement = init_data.memory_replacement;
|
|
||||||
if (memory_replacement.allocate != nullptr && memory_replacement.deallocate != nullptr &&
|
|
||||||
memory_replacement.allocate_texture != nullptr &&
|
|
||||||
memory_replacement.deallocate_texture != nullptr) {
|
|
||||||
m_memory_replacement = memory_replacement;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (init_data.event_replacement.event_callback != nullptr) {
|
|
||||||
m_event_replacement = init_data.event_replacement;
|
|
||||||
m_auto_start = init_data.auto_start;
|
|
||||||
} else {
|
|
||||||
m_auto_start = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_file_replacement = init_data.file_replacement;
|
|
||||||
m_thread_priorities = priorities;
|
|
||||||
|
|
||||||
if (init_data.default_language != nullptr) {
|
if (init_data.default_language != nullptr) {
|
||||||
std::memcpy(m_default_language, init_data.default_language, sizeof(m_default_language));
|
std::memcpy(m_default_language, init_data.default_language, sizeof(m_default_language));
|
||||||
}
|
}
|
||||||
|
@ -135,54 +108,67 @@ AvPlayerState::AvPlayerState(const SceAvPlayerInitData& init_data,
|
||||||
}
|
}
|
||||||
|
|
||||||
AvPlayerState::~AvPlayerState() {
|
AvPlayerState::~AvPlayerState() {
|
||||||
m_event_queue.Clear();
|
|
||||||
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();
|
||||||
OnPlaybackStateChanged(AvState::Stop);
|
|
||||||
}
|
}
|
||||||
|
m_quit = true;
|
||||||
|
if (m_controller_thread != nullptr) {
|
||||||
|
void* res = nullptr;
|
||||||
|
scePthreadJoin(m_controller_thread, &res);
|
||||||
|
}
|
||||||
|
m_event_queue.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called inside GAME thread
|
// Called inside GAME thread
|
||||||
s32 AvPlayerState::AddSource(std::string_view path, SceAvPlayerSourceType source_type) {
|
s32 AvPlayerState::AddSource(std::string_view path, SceAvPlayerSourceType source_type) {
|
||||||
if (path.empty()) {
|
if (path.empty()) {
|
||||||
|
LOG_ERROR(Lib_AvPlayer, "File path is empty.");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_up_source) {
|
if (m_up_source != nullptr) {
|
||||||
|
LOG_ERROR(Lib_AvPlayer, "Only one source is supported.");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_up_source = std::make_unique<AvPlayerSource>(*this);
|
m_up_source = std::make_unique<AvPlayerSource>(*this, path, m_init_data, m_thread_priorities,
|
||||||
if (AVPLAYER_IS_ERROR(m_up_source->Init(path, m_memory_replacement, m_file_replacement,
|
source_type);
|
||||||
m_thread_priorities, source_type))) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
AddSourceEvent();
|
AddSourceEvent();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called inside GAME thread
|
// Called inside GAME thread
|
||||||
s32 AvPlayerState::GetStreamCount() {
|
s32 AvPlayerState::GetStreamCount() {
|
||||||
|
if (m_up_source == nullptr) {
|
||||||
|
LOG_ERROR(Lib_AvPlayer, "Could not get stream count. No source.");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
return m_up_source->GetStreamCount();
|
return m_up_source->GetStreamCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called inside GAME thread
|
// Called inside GAME thread
|
||||||
s32 AvPlayerState::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info) {
|
s32 AvPlayerState::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info) {
|
||||||
|
if (m_up_source == nullptr) {
|
||||||
|
LOG_ERROR(Lib_AvPlayer, "Could not get stream {} info. No source.", stream_index);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
return m_up_source->GetStreamInfo(stream_index, info);
|
return m_up_source->GetStreamInfo(stream_index, info);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called inside GAME thread
|
// Called inside GAME thread
|
||||||
s32 AvPlayerState::Start() {
|
s32 AvPlayerState::Start() {
|
||||||
if (m_up_source == nullptr) {
|
if (m_up_source == nullptr || m_up_source->Start() < 0) {
|
||||||
|
LOG_ERROR(Lib_AvPlayer, "Could not start playback.");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
return m_up_source->Start();
|
SetState(AvState::Play);
|
||||||
|
OnPlaybackStateChanged(AvState::Play);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void* AvPlayerState::AvControllerThread(void* p_user_data) {
|
void* AvPlayerState::AvControllerThread(void* p_user_data) {
|
||||||
AvPlayerState* self = reinterpret_cast<AvPlayerState*>(p_user_data);
|
AvPlayerState* self = reinterpret_cast<AvPlayerState*>(p_user_data);
|
||||||
while (self->m_quit.load() == 0) {
|
while (!self->m_quit) {
|
||||||
if (self->m_event_queue.Size() != 0) {
|
if (self->m_event_queue.Size() != 0) {
|
||||||
self->ProcessEvent();
|
self->ProcessEvent();
|
||||||
continue;
|
continue;
|
||||||
|
@ -201,6 +187,16 @@ void AvPlayerState::AddSourceEvent() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AvPlayerState::WarningEvent(s32 id) {
|
||||||
|
m_event_queue.Push(AvPlayerEvent{
|
||||||
|
.event = AvEventType::WarningId,
|
||||||
|
.payload =
|
||||||
|
{
|
||||||
|
.error = id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Called inside GAME thread
|
// Called inside GAME thread
|
||||||
int AvPlayerState::StartControllerThread() {
|
int AvPlayerState::StartControllerThread() {
|
||||||
m_quit.store(0);
|
m_quit.store(0);
|
||||||
|
@ -214,25 +210,28 @@ int AvPlayerState::StartControllerThread() {
|
||||||
};
|
};
|
||||||
m_controller_thread = CreateThread(&AvControllerThread, params);
|
m_controller_thread = CreateThread(&AvControllerThread, params);
|
||||||
if (m_controller_thread == nullptr) {
|
if (m_controller_thread == nullptr) {
|
||||||
|
LOG_ERROR(Lib_AvPlayer, "Could not create CONTROLLER thread.");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called inside GAME thread
|
// Called inside GAME thread
|
||||||
s32 AvPlayerState::EnableStream(u32 stream_id) {
|
bool AvPlayerState::EnableStream(u32 stream_index) {
|
||||||
if (m_up_source == nullptr) {
|
if (m_up_source == nullptr) {
|
||||||
return -1;
|
return false;
|
||||||
}
|
}
|
||||||
return m_up_source->EnableStream(stream_id);
|
return m_up_source->EnableStream(stream_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called inside GAME thread
|
// Called inside GAME thread
|
||||||
bool AvPlayerState::Stop() {
|
bool AvPlayerState::Stop() {
|
||||||
if (m_up_source == nullptr) {
|
if (m_up_source == nullptr || m_current_state == AvState::Stop) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!SetState(AvState::Stop)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
SetState(AvState::Stop);
|
|
||||||
OnPlaybackStateChanged(AvState::Stop);
|
OnPlaybackStateChanged(AvState::Stop);
|
||||||
return m_up_source->Stop();
|
return m_up_source->Stop();
|
||||||
}
|
}
|
||||||
|
@ -268,13 +267,16 @@ bool AvPlayerState::IsActive() {
|
||||||
|
|
||||||
u64 AvPlayerState::CurrentTime() {
|
u64 AvPlayerState::CurrentTime() {
|
||||||
if (m_up_source == nullptr) {
|
if (m_up_source == nullptr) {
|
||||||
|
LOG_ERROR(Lib_AvPlayer, "Could not get current time. No source.");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return m_up_source->CurrentTime();
|
return m_up_source->CurrentTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// May be called from different threads
|
||||||
void AvPlayerState::OnWarning(u32 id) {
|
void AvPlayerState::OnWarning(u32 id) {
|
||||||
EmitEvent(SCE_AVPLAYER_WARNING_ID, &id);
|
// Forward to CONTROLLER thread
|
||||||
|
WarningEvent(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AvPlayerState::OnError() {
|
void AvPlayerState::OnError() {
|
||||||
|
@ -282,9 +284,7 @@ void AvPlayerState::OnError() {
|
||||||
OnPlaybackStateChanged(AvState::Error);
|
OnPlaybackStateChanged(AvState::Error);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AvPlayerState::OnEOF() {
|
void AvPlayerState::OnEOF() {}
|
||||||
Stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Called inside CONTROLLER thread
|
// Called inside CONTROLLER thread
|
||||||
void AvPlayerState::OnPlaybackStateChanged(AvState state) {
|
void AvPlayerState::OnPlaybackStateChanged(AvState state) {
|
||||||
|
@ -319,6 +319,8 @@ bool AvPlayerState::SetState(AvState state) {
|
||||||
std::lock_guard guard(m_state_machine_mutex);
|
std::lock_guard guard(m_state_machine_mutex);
|
||||||
|
|
||||||
if (!IsStateTransitionValid(state)) {
|
if (!IsStateTransitionValid(state)) {
|
||||||
|
LOG_ERROR(Lib_AvPlayer, "Invalid state transition: {} -> {}",
|
||||||
|
magic_enum::enum_name(m_current_state.load()), magic_enum::enum_name(state));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
m_previous_state.store(m_current_state);
|
m_previous_state.store(m_current_state);
|
||||||
|
@ -337,16 +339,16 @@ 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(SceAvPlayerEvents 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_event_replacement.event_callback;
|
const auto callback = m_init_data.event_replacement.event_callback;
|
||||||
if (callback) {
|
if (callback) {
|
||||||
const auto ptr = m_event_replacement.object_ptr;
|
const auto ptr = m_init_data.event_replacement.object_ptr;
|
||||||
callback(ptr, event_id, 0, event_data);
|
callback(ptr, event_id, 0, event_data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called inside CONTROLLER thread
|
// Called inside CONTROLLER thread
|
||||||
int AvPlayerState::ProcessEvent() {
|
int AvPlayerState::ProcessEvent() {
|
||||||
if (m_current_state.load() == AvState::Jump) {
|
if (m_current_state == AvState::Jump) {
|
||||||
return -2;
|
return -2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -357,6 +359,10 @@ int AvPlayerState::ProcessEvent() {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
switch (event->event) {
|
switch (event->event) {
|
||||||
|
case AvEventType::WarningId: {
|
||||||
|
OnWarning(event->payload.error);
|
||||||
|
break;
|
||||||
|
}
|
||||||
case AvEventType::RevertState: {
|
case AvEventType::RevertState: {
|
||||||
SetState(m_previous_state.load());
|
SetState(m_previous_state.load());
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -24,7 +24,7 @@ public:
|
||||||
s32 AddSource(std::string_view filename, SceAvPlayerSourceType source_type);
|
s32 AddSource(std::string_view filename, SceAvPlayerSourceType source_type);
|
||||||
s32 GetStreamCount();
|
s32 GetStreamCount();
|
||||||
s32 GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info);
|
s32 GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info);
|
||||||
s32 EnableStream(u32 stream_id);
|
bool EnableStream(u32 stream_index);
|
||||||
s32 Start();
|
s32 Start();
|
||||||
bool Stop();
|
bool Stop();
|
||||||
bool GetAudioData(SceAvPlayerFrameInfo& audio_info);
|
bool GetAudioData(SceAvPlayerFrameInfo& audio_info);
|
||||||
|
@ -54,6 +54,8 @@ private:
|
||||||
static void* PS4_SYSV_ABI AvControllerThread(void* p_user_data);
|
static void* PS4_SYSV_ABI AvControllerThread(void* p_user_data);
|
||||||
|
|
||||||
void AddSourceEvent();
|
void AddSourceEvent();
|
||||||
|
void WarningEvent(s32 id);
|
||||||
|
|
||||||
int StartControllerThread();
|
int StartControllerThread();
|
||||||
int ProcessEvent();
|
int ProcessEvent();
|
||||||
int UpdateBufferingState();
|
int UpdateBufferingState();
|
||||||
|
@ -61,15 +63,13 @@ private:
|
||||||
|
|
||||||
std::unique_ptr<AvPlayerSource> m_up_source;
|
std::unique_ptr<AvPlayerSource> m_up_source;
|
||||||
|
|
||||||
SceAvPlayerMemAllocator m_memory_replacement{};
|
SceAvPlayerInitData m_init_data{};
|
||||||
SceAvPlayerFileReplacement m_file_replacement{};
|
|
||||||
SceAvPlayerEventReplacement m_event_replacement{};
|
SceAvPlayerEventReplacement m_event_replacement{};
|
||||||
SceAvPlayerEventReplacement m_user_event_replacement{};
|
|
||||||
ThreadPriorities m_thread_priorities{};
|
ThreadPriorities m_thread_priorities{};
|
||||||
bool m_auto_start{};
|
bool m_auto_start{};
|
||||||
u8 m_default_language[4]{};
|
u8 m_default_language[4]{};
|
||||||
|
|
||||||
std::atomic_int32_t m_quit;
|
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;
|
||||||
|
|
Loading…
Reference in New Issue