AvPlayer: Handle Initialization errors

This commit is contained in:
Vladislav Mikhalin 2024-08-27 09:32:42 +03:00
parent af4356bfe1
commit 2d354a095a
8 changed files with 95 additions and 66 deletions

View File

@ -7,6 +7,8 @@
#include "common/types.h" #include "common/types.h"
#include <string_view>
struct AVIOContext; struct AVIOContext;
namespace Libraries::AvPlayer { namespace Libraries::AvPlayer {
@ -14,6 +16,7 @@ namespace Libraries::AvPlayer {
class IDataStreamer { class IDataStreamer {
public: public:
virtual ~IDataStreamer() = default; virtual ~IDataStreamer() = default;
virtual bool Init(std::string_view path) = 0;
virtual AVIOContext* GetContext() = 0; virtual AVIOContext* GetContext() = 0;
}; };

View File

@ -18,19 +18,8 @@ extern "C" {
namespace Libraries::AvPlayer { namespace Libraries::AvPlayer {
AvPlayerFileStreamer::AvPlayerFileStreamer(const SceAvPlayerFileReplacement& file_replacement, AvPlayerFileStreamer::AvPlayerFileStreamer(const SceAvPlayerFileReplacement& file_replacement)
std::string_view path) : m_file_replacement(file_replacement) {}
: m_file_replacement(file_replacement) {
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) {
@ -43,6 +32,21 @@ AvPlayerFileStreamer::~AvPlayerFileStreamer() {
} }
} }
bool 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 false;
}
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);
return true;
}
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) {

View File

@ -15,9 +15,11 @@ namespace Libraries::AvPlayer {
class AvPlayerFileStreamer : public IDataStreamer { class AvPlayerFileStreamer : public IDataStreamer {
public: public:
AvPlayerFileStreamer(const SceAvPlayerFileReplacement& file_replacement, std::string_view path); AvPlayerFileStreamer(const SceAvPlayerFileReplacement& file_replacement);
~AvPlayerFileStreamer(); ~AvPlayerFileStreamer();
bool Init(std::string_view path) override;
AVIOContext* GetContext() override { AVIOContext* GetContext() override {
return m_avio_context; return m_avio_context;
} }

View File

@ -110,7 +110,7 @@ s32 AvPlayer::AddSource(std::string_view path) {
if (path.empty()) { if (path.empty()) {
return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS;
} }
if (AVPLAYER_IS_ERROR(m_state->AddSource(path, GetSourceType(path)))) { if (!m_state->AddSource(path, GetSourceType(path))) {
return ORBIS_AVPLAYER_ERROR_OPERATION_FAILED; return ORBIS_AVPLAYER_ERROR_OPERATION_FAILED;
} }
return ORBIS_OK; return ORBIS_OK;
@ -128,7 +128,7 @@ s32 AvPlayer::GetStreamCount() {
} }
s32 AvPlayer::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info) { s32 AvPlayer::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info) {
if (AVPLAYER_IS_ERROR(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;
} }
return ORBIS_OK; return ORBIS_OK;
@ -145,7 +145,10 @@ s32 AvPlayer::EnableStream(u32 stream_index) {
} }
s32 AvPlayer::Start() { s32 AvPlayer::Start() {
return m_state->Start(); if (!m_state->Start()) {
return ORBIS_AVPLAYER_ERROR_OPERATION_FAILED;
}
return ORBIS_OK;
} }
bool AvPlayer::GetVideoData(SceAvPlayerFrameInfo& video_info) { bool AvPlayer::GetVideoData(SceAvPlayerFrameInfo& video_info) {

View File

@ -24,31 +24,39 @@ namespace Libraries::AvPlayer {
using namespace Kernel; using namespace Kernel;
AvPlayerSource::AvPlayerSource(AvPlayerStateCallback& state, std::string_view path, AvPlayerSource::AvPlayerSource(AvPlayerStateCallback& state) : m_state(state) {}
const SceAvPlayerInitData& init_data,
SceAvPlayerSourceType source_type)
: m_state(state), m_memory_replacement(init_data.memory_replacement),
m_num_output_video_framebuffers(
std::min(std::max(2, init_data.num_output_video_framebuffers), 16)) {
AVFormatContext* context = avformat_alloc_context();
if (init_data.file_replacement.open != nullptr) {
m_up_data_streamer =
std::make_unique<AvPlayerFileStreamer>(init_data.file_replacement, path);
context->pb = m_up_data_streamer->GetContext();
ASSERT(!AVPLAYER_IS_ERROR(avformat_open_input(&context, nullptr, nullptr, nullptr)));
} else {
const auto mnt = Common::Singleton<Core::FileSys::MntPoints>::Instance();
const auto filepath = mnt->GetHostPath(path);
ASSERT(!AVPLAYER_IS_ERROR(
avformat_open_input(&context, filepath.string().c_str(), nullptr, nullptr)));
}
m_avformat_context = AVFormatContextPtr(context, &ReleaseAVFormatContext);
}
AvPlayerSource::~AvPlayerSource() { AvPlayerSource::~AvPlayerSource() {
Stop(); Stop();
} }
bool AvPlayerSource::Init(const SceAvPlayerInitData& init_data, std::string_view path) {
m_memory_replacement = init_data.memory_replacement,
m_num_output_video_framebuffers =
std::min(std::max(2, init_data.num_output_video_framebuffers), 16);
AVFormatContext* context = avformat_alloc_context();
if (init_data.file_replacement.open != nullptr) {
m_up_data_streamer = std::make_unique<AvPlayerFileStreamer>(init_data.file_replacement);
if (!m_up_data_streamer->Init(path)) {
return false;
}
context->pb = m_up_data_streamer->GetContext();
if (AVPLAYER_IS_ERROR(avformat_open_input(&context, nullptr, nullptr, nullptr))) {
return false;
}
} else {
const auto mnt = Common::Singleton<Core::FileSys::MntPoints>::Instance();
const auto filepath = mnt->GetHostPath(path);
if (AVPLAYER_IS_ERROR(
avformat_open_input(&context, filepath.string().c_str(), nullptr, nullptr))) {
return false;
}
}
m_avformat_context = AVFormatContextPtr(context, &ReleaseAVFormatContext);
return true;
}
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."); LOG_ERROR(Lib_AvPlayer, "Could not find stream info. NULL context.");
@ -87,16 +95,16 @@ static f32 AVRationalToF32(const AVRational rational) {
return f32(rational.num) / rational.den; return f32(rational.num) / rational.den;
} }
s32 AvPlayerSource::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info) { 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);
return -1; return false;
} }
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); LOG_ERROR(Lib_AvPlayer, "Could not get stream {} info. NULL stream.", stream_index);
return -1; return false;
} }
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;
@ -140,9 +148,9 @@ s32 AvPlayerSource::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info)
break; break;
default: default:
LOG_ERROR(Lib_AvPlayer, "Stream {} type is unknown: {}.", stream_index, info.type); LOG_ERROR(Lib_AvPlayer, "Stream {} type is unknown: {}.", stream_index, info.type);
return -1; return false;
} }
return 0; return true;
} }
bool AvPlayerSource::EnableStream(u32 stream_index) { bool AvPlayerSource::EnableStream(u32 stream_index) {
@ -215,12 +223,12 @@ std::optional<bool> AvPlayerSource::HasFrames(u32 num_frames) {
return m_video_packets.Size() > num_frames || m_is_eof; return m_video_packets.Size() > num_frames || m_is_eof;
} }
s32 AvPlayerSource::Start() { bool AvPlayerSource::Start() {
std::unique_lock lock(m_state_mutex); std::unique_lock lock(m_state_mutex);
if (m_audio_codec_context == nullptr && m_video_codec_context == nullptr) { if (m_audio_codec_context == nullptr && m_video_codec_context == nullptr) {
LOG_ERROR(Lib_AvPlayer, "Could not start playback. NULL context."); LOG_ERROR(Lib_AvPlayer, "Could not start playback. NULL context.");
return -1; return false;
} }
m_demuxer_thread = std::jthread([this](std::stop_token stop) { this->DemuxerThread(stop); }); m_demuxer_thread = std::jthread([this](std::stop_token stop) { this->DemuxerThread(stop); });
m_video_decoder_thread = m_video_decoder_thread =
@ -228,7 +236,7 @@ s32 AvPlayerSource::Start() {
m_audio_decoder_thread = m_audio_decoder_thread =
std::jthread([this](std::stop_token stop) { this->AudioDecoderThread(stop); }); std::jthread([this](std::stop_token stop) { this->AudioDecoderThread(stop); });
m_start_time = std::chrono::high_resolution_clock::now(); m_start_time = std::chrono::high_resolution_clock::now();
return 0; return true;
} }
bool AvPlayerSource::Stop() { bool AvPlayerSource::Stop() {

View File

@ -120,17 +120,17 @@ private:
class AvPlayerSource { class AvPlayerSource {
public: public:
AvPlayerSource(AvPlayerStateCallback& state, std::string_view path, AvPlayerSource(AvPlayerStateCallback& state);
const SceAvPlayerInitData& init_data, SceAvPlayerSourceType source_type);
~AvPlayerSource(); ~AvPlayerSource();
bool Init(const SceAvPlayerInitData& init_data, std::string_view path);
bool FindStreamInfo(); bool FindStreamInfo();
s32 GetStreamCount(); s32 GetStreamCount();
s32 GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info); bool GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& 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);
s32 Start(); bool Start();
bool Stop(); bool Stop();
bool GetAudioData(SceAvPlayerFrameInfo& audio_info); bool GetAudioData(SceAvPlayerFrameInfo& audio_info);
bool GetVideoData(SceAvPlayerFrameInfo& video_info); bool GetVideoData(SceAvPlayerFrameInfo& video_info);

View File

@ -24,6 +24,7 @@ void PS4_SYSV_ABI AvPlayerState::AutoPlayEventCallback(void* opaque, s32 event_i
s32 timedtext_stream_index = -1; s32 timedtext_stream_index = -1;
const s32 stream_count = self->GetStreamCount(); const s32 stream_count = self->GetStreamCount();
if (AVPLAYER_IS_ERROR(stream_count)) { if (AVPLAYER_IS_ERROR(stream_count)) {
self->Stop();
return; return;
} }
if (stream_count == 0) { if (stream_count == 0) {
@ -32,7 +33,10 @@ void PS4_SYSV_ABI AvPlayerState::AutoPlayEventCallback(void* opaque, s32 event_i
} }
for (u32 stream_index = 0; stream_index < stream_count; ++stream_index) { for (u32 stream_index = 0; stream_index < stream_count; ++stream_index) {
SceAvPlayerStreamInfo info{}; SceAvPlayerStreamInfo info{};
self->GetStreamInfo(stream_index, info); if (!self->GetStreamInfo(stream_index, info)) {
self->Stop();
return;
}
const std::string_view default_language( const std::string_view default_language(
reinterpret_cast<char*>(self->m_default_language)); reinterpret_cast<char*>(self->m_default_language));
@ -116,23 +120,28 @@ AvPlayerState::~AvPlayerState() {
} }
// Called inside GAME thread // Called inside GAME thread
s32 AvPlayerState::AddSource(std::string_view path, SceAvPlayerSourceType source_type) { bool AvPlayerState::AddSource(std::string_view path, SceAvPlayerSourceType 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 -1; return false;
} }
{ {
std::unique_lock lock(m_source_mutex); std::unique_lock lock(m_source_mutex);
if (m_up_source != nullptr) { if (m_up_source != nullptr) {
LOG_ERROR(Lib_AvPlayer, "Only one source is supported."); LOG_ERROR(Lib_AvPlayer, "Only one source is supported.");
return -1; return false;
} }
m_up_source = std::make_unique<AvPlayerSource>(*this, path, m_init_data, source_type); m_up_source = std::make_unique<AvPlayerSource>(*this);
if (!m_up_source->Init(m_init_data, path)) {
SetState(AvState::Error);
m_up_source.reset();
return false;
}
} }
AddSourceEvent(); AddSourceEvent();
return 0; return true;
} }
// Called inside GAME thread // Called inside GAME thread
@ -146,25 +155,25 @@ s32 AvPlayerState::GetStreamCount() {
} }
// Called inside GAME thread // Called inside GAME thread
s32 AvPlayerState::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info) { bool AvPlayerState::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& 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);
return -1; return false;
} }
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() { bool AvPlayerState::Start() {
std::shared_lock lock(m_source_mutex); std::shared_lock lock(m_source_mutex);
if (m_up_source == nullptr || m_up_source->Start() < 0) { if (m_up_source == nullptr || !m_up_source->Start()) {
LOG_ERROR(Lib_AvPlayer, "Could not start playback."); LOG_ERROR(Lib_AvPlayer, "Could not start playback.");
return -1; return false;
} }
SetState(AvState::Play); SetState(AvState::Play);
OnPlaybackStateChanged(AvState::Play); OnPlaybackStateChanged(AvState::Play);
return 0; return true;
} }
void AvPlayerState::AvControllerThread(std::stop_token stop) { void AvPlayerState::AvControllerThread(std::stop_token stop) {
@ -219,10 +228,10 @@ bool AvPlayerState::Stop() {
if (m_up_source == nullptr || m_current_state == AvState::Stop) { if (m_up_source == nullptr || m_current_state == AvState::Stop) {
return false; return false;
} }
if (!SetState(AvState::Stop)) { if (!m_up_source->Stop()) {
return false; return false;
} }
if (!m_up_source->Stop()) { if (!SetState(AvState::Stop)) {
return false; return false;
} }
OnPlaybackStateChanged(AvState::Stop); OnPlaybackStateChanged(AvState::Stop);

View File

@ -24,11 +24,11 @@ public:
AvPlayerState(const SceAvPlayerInitData& init_data); AvPlayerState(const SceAvPlayerInitData& init_data);
~AvPlayerState(); ~AvPlayerState();
s32 AddSource(std::string_view filename, SceAvPlayerSourceType source_type); bool AddSource(std::string_view filename, SceAvPlayerSourceType source_type);
s32 GetStreamCount(); s32 GetStreamCount();
s32 GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info); bool GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info);
bool EnableStream(u32 stream_index); bool EnableStream(u32 stream_index);
s32 Start(); bool Start();
bool Stop(); bool Stop();
bool GetAudioData(SceAvPlayerFrameInfo& audio_info); bool GetAudioData(SceAvPlayerFrameInfo& audio_info);
bool GetVideoData(SceAvPlayerFrameInfo& video_info); bool GetVideoData(SceAvPlayerFrameInfo& video_info);