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 <string_view>
struct AVIOContext;
namespace Libraries::AvPlayer {
@ -14,6 +16,7 @@ namespace Libraries::AvPlayer {
class IDataStreamer {
public:
virtual ~IDataStreamer() = default;
virtual bool Init(std::string_view path) = 0;
virtual AVIOContext* GetContext() = 0;
};

View File

@ -18,19 +18,8 @@ extern "C" {
namespace Libraries::AvPlayer {
AvPlayerFileStreamer::AvPlayerFileStreamer(const SceAvPlayerFileReplacement& file_replacement,
std::string_view path)
: 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(const SceAvPlayerFileReplacement& file_replacement)
: m_file_replacement(file_replacement) {}
AvPlayerFileStreamer::~AvPlayerFileStreamer() {
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) {
const auto self = reinterpret_cast<AvPlayerFileStreamer*>(opaque);
if (self->m_position >= self->m_file_size) {

View File

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

View File

@ -110,7 +110,7 @@ s32 AvPlayer::AddSource(std::string_view path) {
if (path.empty()) {
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_OK;
@ -128,7 +128,7 @@ s32 AvPlayer::GetStreamCount() {
}
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_OK;
@ -145,7 +145,10 @@ s32 AvPlayer::EnableStream(u32 stream_index) {
}
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) {

View File

@ -24,31 +24,39 @@ namespace Libraries::AvPlayer {
using namespace Kernel;
AvPlayerSource::AvPlayerSource(AvPlayerStateCallback& state, std::string_view path,
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(AvPlayerStateCallback& state) : m_state(state) {}
AvPlayerSource::~AvPlayerSource() {
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() {
if (m_avformat_context == nullptr) {
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;
}
s32 AvPlayerSource::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info) {
bool AvPlayerSource::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info) {
info = {};
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 false;
}
const auto p_stream = m_avformat_context->streams[stream_index];
if (p_stream == nullptr || p_stream->codecpar == nullptr) {
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.start_time = p_stream->start_time;
@ -140,9 +148,9 @@ s32 AvPlayerSource::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info)
break;
default:
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) {
@ -215,12 +223,12 @@ std::optional<bool> AvPlayerSource::HasFrames(u32 num_frames) {
return m_video_packets.Size() > num_frames || m_is_eof;
}
s32 AvPlayerSource::Start() {
bool AvPlayerSource::Start() {
std::unique_lock lock(m_state_mutex);
if (m_audio_codec_context == nullptr && m_video_codec_context == nullptr) {
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_video_decoder_thread =
@ -228,7 +236,7 @@ s32 AvPlayerSource::Start() {
m_audio_decoder_thread =
std::jthread([this](std::stop_token stop) { this->AudioDecoderThread(stop); });
m_start_time = std::chrono::high_resolution_clock::now();
return 0;
return true;
}
bool AvPlayerSource::Stop() {

View File

@ -120,17 +120,17 @@ private:
class AvPlayerSource {
public:
AvPlayerSource(AvPlayerStateCallback& state, std::string_view path,
const SceAvPlayerInitData& init_data, SceAvPlayerSourceType source_type);
AvPlayerSource(AvPlayerStateCallback& state);
~AvPlayerSource();
bool Init(const SceAvPlayerInitData& init_data, std::string_view path);
bool FindStreamInfo();
s32 GetStreamCount();
s32 GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info);
bool GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info);
bool EnableStream(u32 stream_index);
void SetLooping(bool is_looping);
std::optional<bool> HasFrames(u32 num_frames);
s32 Start();
bool Start();
bool Stop();
bool GetAudioData(SceAvPlayerFrameInfo& audio_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;
const s32 stream_count = self->GetStreamCount();
if (AVPLAYER_IS_ERROR(stream_count)) {
self->Stop();
return;
}
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) {
SceAvPlayerStreamInfo info{};
self->GetStreamInfo(stream_index, info);
if (!self->GetStreamInfo(stream_index, info)) {
self->Stop();
return;
}
const std::string_view default_language(
reinterpret_cast<char*>(self->m_default_language));
@ -116,23 +120,28 @@ AvPlayerState::~AvPlayerState() {
}
// 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()) {
LOG_ERROR(Lib_AvPlayer, "File path is empty.");
return -1;
return false;
}
{
std::unique_lock lock(m_source_mutex);
if (m_up_source != nullptr) {
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();
return 0;
return true;
}
// Called inside GAME thread
@ -146,25 +155,25 @@ s32 AvPlayerState::GetStreamCount() {
}
// 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);
if (m_up_source == nullptr) {
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);
}
// Called inside GAME thread
s32 AvPlayerState::Start() {
bool AvPlayerState::Start() {
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.");
return -1;
return false;
}
SetState(AvState::Play);
OnPlaybackStateChanged(AvState::Play);
return 0;
return true;
}
void AvPlayerState::AvControllerThread(std::stop_token stop) {
@ -219,10 +228,10 @@ bool AvPlayerState::Stop() {
if (m_up_source == nullptr || m_current_state == AvState::Stop) {
return false;
}
if (!SetState(AvState::Stop)) {
if (!m_up_source->Stop()) {
return false;
}
if (!m_up_source->Stop()) {
if (!SetState(AvState::Stop)) {
return false;
}
OnPlaybackStateChanged(AvState::Stop);

View File

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