diff --git a/src/audio_core/sdl_audio.cpp b/src/audio_core/sdl_audio.cpp index 141d338e..aac67d8c 100644 --- a/src/audio_core/sdl_audio.cpp +++ b/src/audio_core/sdl_audio.cpp @@ -100,7 +100,7 @@ s32 SDLAudio::AudioOutOutput(s32 handle, const void* ptr) { int result = SDL_PutAudioStreamData(port.stream, ptr, port.samples_num * port.sample_size * port.channels_num); // TODO find a correct value 8192 is estimated - while (SDL_GetAudioStreamAvailable(port.stream) > 8192) { + while (SDL_GetAudioStreamAvailable(port.stream) > 65536) { SDL_Delay(0); } diff --git a/src/core/libraries/avplayer/avplayer.cpp b/src/core/libraries/avplayer/avplayer.cpp index 41f8d076..e8e99bca 100644 --- a/src/core/libraries/avplayer/avplayer.cpp +++ b/src/core/libraries/avplayer/avplayer.cpp @@ -9,7 +9,9 @@ #include "core/libraries/kernel/thread_management.h" #include "core/libraries/libs.h" -#include +#include // std::max, std::min + +#include // va_list 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.maxPriority = priorities.http_streaming_priority; - const auto player = new AvPlayer(); - player->Init(*data, priorities); - return player; + return new AvPlayer(*data, priorities); } 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; - const auto player = new AvPlayer(); - player->Init(data, priorities); - *p_player = player; + *p_player = new AvPlayer(data, priorities); return ORBIS_OK; } @@ -290,13 +288,13 @@ s32 PS4_SYSV_ABI sceAvPlayerSetAvSyncMode(SceAvPlayerHandle handle, 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"); return ORBIS_OK; } 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) { return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; } diff --git a/src/core/libraries/avplayer/avplayer.h b/src/core/libraries/avplayer/avplayer.h index f5589441..e88419c1 100644 --- a/src/core/libraries/avplayer/avplayer.h +++ b/src/core/libraries/avplayer/avplayer.h @@ -5,6 +5,8 @@ #include "common/types.h" +#include // size_t + namespace Core::Loader { class SymbolsResolver; } diff --git a/src/core/libraries/avplayer/avplayer_file_streamer.cpp b/src/core/libraries/avplayer/avplayer_file_streamer.cpp index 1c54c454..43055fd5 100644 --- a/src/core/libraries/avplayer/avplayer_file_streamer.cpp +++ b/src/core/libraries/avplayer/avplayer_file_streamer.cpp @@ -12,23 +12,30 @@ extern "C" { #include } +#include // std::max, std::min + #define AVPLAYER_AVIO_BUFFER_SIZE 4096 namespace Libraries::AvPlayer { -AvPlayerFileStreamer::AvPlayerFileStreamer(SceAvPlayerFileReplacement& file_replacement, +AvPlayerFileStreamer::AvPlayerFileStreamer(const SceAvPlayerFileReplacement& file_replacement, std::string_view path) : 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(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() { if (m_avio_context != nullptr) { 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) { const auto close = m_file_replacement.close; 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(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) { const auto self = reinterpret_cast(opaque); 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 ptr = self->m_file_replacement.object_ptr; 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; } self->m_position += bytes_read; diff --git a/src/core/libraries/avplayer/avplayer_file_streamer.h b/src/core/libraries/avplayer/avplayer_file_streamer.h index 9f1442f9..658ce8c1 100644 --- a/src/core/libraries/avplayer/avplayer_file_streamer.h +++ b/src/core/libraries/avplayer/avplayer_file_streamer.h @@ -15,7 +15,7 @@ namespace Libraries::AvPlayer { class AvPlayerFileStreamer : public IDataStreamer { public: - AvPlayerFileStreamer(SceAvPlayerFileReplacement& file_replacement, std::string_view path); + AvPlayerFileStreamer(const SceAvPlayerFileReplacement& file_replacement, std::string_view path); ~AvPlayerFileStreamer(); AVIOContext* GetContext() override { @@ -23,8 +23,6 @@ public: } private: - s32 Init(std::string_view path); - static s32 ReadPacket(void* opaque, u8* buffer, s32 size); static s64 Seek(void* opaque, s64 buffer, int whence); @@ -33,7 +31,6 @@ private: int m_fd = -1; u64 m_position{}; u64 m_file_size{}; - u8* m_avio_buffer{}; AVIOContext* m_avio_context{}; }; diff --git a/src/core/libraries/avplayer/avplayer_impl.cpp b/src/core/libraries/avplayer/avplayer_impl.cpp index 58793da7..f08356bd 100644 --- a/src/core/libraries/avplayer/avplayer_impl.cpp +++ b/src/core/libraries/avplayer/avplayer_impl.cpp @@ -77,11 +77,9 @@ u64 PS4_SYSV_ABI AvPlayer::SizeFile(void* handle) { return size(ptr); } -AvPlayer::AvPlayer() : m_file_io_mutex(PTHREAD_MUTEX_ERRORCHECK, "SceAvPlayerFileIOLock") {} - -void AvPlayer::Init(const SceAvPlayerInitData& data, const ThreadPriorities& priorities) { - m_init_data = data; - m_init_data_original = data; +AvPlayer::AvPlayer(const SceAvPlayerInitData& data, const ThreadPriorities& priorities) + : m_file_io_mutex(PTHREAD_MUTEX_ERRORCHECK, "AvPlayer_FileIO"), m_init_data(data), + m_init_data_original(data) { m_init_data.memory_replacement.object_ptr = this; m_init_data.memory_replacement.allocate = &AvPlayer::Allocate; @@ -120,6 +118,9 @@ s32 AvPlayer::AddSource(std::string_view path) { } s32 AvPlayer::GetStreamCount() { + if (m_state == nullptr) { + return ORBIS_AVPLAYER_ERROR_OPERATION_FAILED; + } return m_state->GetStreamCount(); } @@ -130,11 +131,14 @@ s32 AvPlayer::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info) { return ORBIS_OK; } -s32 AvPlayer::EnableStream(u32 stream_id) { +s32 AvPlayer::EnableStream(u32 stream_index) { if (m_state == nullptr) { 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() { diff --git a/src/core/libraries/avplayer/avplayer_impl.h b/src/core/libraries/avplayer/avplayer_impl.h index fe3abcd1..98f959e6 100644 --- a/src/core/libraries/avplayer/avplayer_impl.h +++ b/src/core/libraries/avplayer/avplayer_impl.h @@ -33,15 +33,13 @@ public: static int PS4_SYSV_ABI ReadOffsetFile(void* handle, u8* buffer, u64 position, u32 length); static u64 PS4_SYSV_ABI SizeFile(void* handle); - AvPlayer(); - - void Init(const SceAvPlayerInitData& data, const ThreadPriorities& priorities); + AvPlayer(const SceAvPlayerInitData& data, const ThreadPriorities& priorities); s32 PostInit(const SceAvPlayerPostInitData& data); s32 AddSource(std::string_view filename); s32 GetStreamCount(); s32 GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info); - s32 EnableStream(u32 stream_id); + s32 EnableStream(u32 stream_index); s32 Start(); bool GetAudioData(SceAvPlayerFrameInfo& audio_info); bool GetVideoData(SceAvPlayerFrameInfo& video_info); diff --git a/src/core/libraries/avplayer/avplayer_source.cpp b/src/core/libraries/avplayer/avplayer_source.cpp index c1dc6d57..c3d3dc55 100644 --- a/src/core/libraries/avplayer/avplayer_source.cpp +++ b/src/core/libraries/avplayer/avplayer_source.cpp @@ -23,67 +23,47 @@ namespace Libraries::AvPlayer { using namespace Kernel; -AvPlayerSource::AvPlayerSource(AvPlayerStateCallback& state) : m_state(state) {} - -AvPlayerSource::~AvPlayerSource() { - if (!m_video_frame_storage.empty()) { - m_memory_replacement.deallocate(m_memory_replacement.object_ptr, - m_video_frame_storage.data()); - } - if (!m_audio_frame_storage.empty()) { - m_memory_replacement.deallocate(m_memory_replacement.object_ptr, - m_audio_frame_storage.data()); - } - 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(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; - } +AvPlayerSource::AvPlayerSource(AvPlayerStateCallback& state, std::string_view path, + const SceAvPlayerInitData& init_data, ThreadPriorities& priorities, + SceAvPlayerSourceType source_type) + : m_state(state), m_priorities(priorities), m_memory_replacement(init_data.memory_replacement), + m_num_output_video_framebuffers(init_data.num_output_video_framebuffers) { + AVFormatContext* context = avformat_alloc_context(); + if (init_data.file_replacement.open != nullptr) { + m_up_data_streamer = + std::make_unique(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::Instance(); const auto filepath = mnt->GetHostPath(path); - if (AVPLAYER_IS_ERROR(avformat_open_input(&m_avformat_context, filepath.string().c_str(), - nullptr, nullptr))) { - return -1; - } + ASSERT(!AVPLAYER_IS_ERROR( + avformat_open_input(&context, filepath.string().c_str(), nullptr, nullptr))); } + m_avformat_context = AVFormatContextPtr(context, &ReleaseAVFormatContext); +} - return 0; +AvPlayerSource::~AvPlayerSource() { + Stop(); } bool AvPlayerSource::FindStreamInfo() { if (m_avformat_context == nullptr) { + LOG_ERROR(Lib_AvPlayer, "Could not find stream info. NULL context."); return false; } if (m_avformat_context->nb_streams > 0) { 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() { if (m_avformat_context == nullptr) { + LOG_ERROR(Lib_AvPlayer, "Could not get stream count. NULL context."); 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; } @@ -108,70 +88,68 @@ static f32 AVRationalToF32(AVRational rational) { s32 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; } 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; } info.type = CodecTypeToStreamType(p_stream->codecpar->codec_type); info.start_time = p_stream->start_time; info.duration = p_stream->duration; const auto p_lang_node = av_dict_get(p_stream->metadata, "language", nullptr, 0); - if (p_lang_node == nullptr) { - return -1; + if (p_lang_node != nullptr) { + 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) { 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.width = p_stream->codecpar->width; info.details.video.height = p_stream->codecpar->height; - std::memcpy(info.details.video.language_code, p_lang_node->value, - std::min(strlen(p_lang_node->value), 3ull)); + if (p_lang_node != nullptr) { + std::memcpy(info.details.video.language_code, p_lang_node->value, + std::min(strlen(p_lang_node->value), 3ull)); + } break; 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.sample_rate = p_stream->codecpar->sample_rate; info.details.audio.size = 0; // sceAvPlayerGetStreamInfo() is expected to set this to 0 - std::memcpy(info.details.audio.language_code, p_lang_node->value, - std::min(strlen(p_lang_node->value), 3ull)); + if (p_lang_node != nullptr) { + std::memcpy(info.details.audio.language_code, p_lang_node->value, + std::min(strlen(p_lang_node->value), 3ull)); + } break; case SCE_AVPLAYER_TIMEDTEXT: LOG_WARNING(Lib_AvPlayer, "Stream {} is a timedtext stream.", stream_index); info.details.subs.font_size = 12; info.details.subs.text_size = 12; - std::memcpy(info.details.subs.language_code, p_lang_node->value, - std::min(strlen(p_lang_node->value), 3ull)); + if (p_lang_node != nullptr) { + std::memcpy(info.details.subs.language_code, p_lang_node->value, + std::min(strlen(p_lang_node->value), 3ull)); + } break; default: + LOG_ERROR(Lib_AvPlayer, "Stream {} type is unknown: {}.", stream_index, info.type); return -1; } return 0; } -static AVPixelFormat GetPreferredVideoFormat(AVCodecContext* s, const AVPixelFormat* fmt) { - 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) { +bool AvPlayerSource::EnableStream(u32 stream_index) { 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 decoder = avcodec_find_decoder(stream->codecpar->codec_id); if (decoder == nullptr) { - return -1; + return false; } switch (stream->codecpar->codec_type) { case AVMediaType::AVMEDIA_TYPE_VIDEO: { @@ -179,10 +157,18 @@ s32 AvPlayerSource::EnableStream(u32 stream_index) { m_video_codec_context = AVCodecContextPtr(avcodec_alloc_context3(decoder), &ReleaseAVCodecContext); 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) { - 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); break; @@ -192,10 +178,19 @@ s32 AvPlayerSource::EnableStream(u32 stream_index) { m_audio_codec_context = AVCodecContextPtr(avcodec_alloc_context3(decoder), &ReleaseAVCodecContext); 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) { - 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); break; @@ -205,35 +200,7 @@ s32 AvPlayerSource::EnableStream(u32 stream_index) { magic_enum::enum_name(stream->codecpar->codec_type), stream_index); break; } - return 0; -} - -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( - m_memory_replacement.allocate(m_memory_replacement.object_ptr, frame->width, size)); - m_video_frame_storage = std::span(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( - m_memory_replacement.allocate(m_memory_replacement.object_ptr, 0x40, size)); - m_audio_frame_storage = std::span(ptr, size); - } - return m_audio_frame_storage.data(); + return true; } void AvPlayerSource::SetLooping(bool is_looping) { @@ -241,11 +208,12 @@ void AvPlayerSource::SetLooping(bool is_looping) { } std::optional 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() { if (m_audio_codec_context == nullptr && m_video_codec_context == nullptr) { + LOG_ERROR(Lib_AvPlayer, "Could not start playback. NULL context."); return -1; } { @@ -258,6 +226,7 @@ s32 AvPlayerSource::Start() { }; m_demuxer_thread = CreateThread(&DemuxerThread, demuxer_params); if (m_demuxer_thread == nullptr) { + LOG_ERROR(Lib_AvPlayer, "Could not create DEMUXER thread."); return -1; } } @@ -271,6 +240,7 @@ s32 AvPlayerSource::Start() { }; m_video_decoder_thread = CreateThread(&VideoDecoderThread, video_decoder_params); if (m_video_decoder_thread == nullptr) { + LOG_ERROR(Lib_AvPlayer, "Could not create VIDEO DECODER thread."); return -1; } } @@ -284,6 +254,7 @@ s32 AvPlayerSource::Start() { }; m_audio_decoder_thread = CreateThread(&AudioDecoderThread, audio_decoder_params); if (m_audio_decoder_thread == nullptr) { + LOG_ERROR(Lib_AvPlayer, "Could not create AUDIO DECODER thread."); return -1; } } @@ -292,6 +263,11 @@ s32 AvPlayerSource::Start() { } bool AvPlayerSource::Stop() { + if (m_is_stop) { + LOG_WARNING(Lib_AvPlayer, "Could not stop playback: already stopped."); + return false; + } + m_is_stop = true; void* res = nullptr; @@ -304,6 +280,18 @@ bool AvPlayerSource::Stop() { if (m_demuxer_thread != nullptr) { 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; } @@ -333,44 +321,27 @@ bool AvPlayerSource::GetVideoData(SceAvPlayerFrameInfoEx& video_info) { auto frame = m_video_frames.Pop(); if (!frame.has_value()) { + LOG_WARNING(Lib_AvPlayer, "Could get video frame: no frames."); 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; auto elapsed_time = duration_cast(high_resolution_clock::now() - m_start_time).count(); - while (elapsed_time < timestamp) { - sceKernelUsleep((timestamp - elapsed_time) * 1000); + while (elapsed_time < frame->info.timestamp) { + sceKernelUsleep((frame->info.timestamp - elapsed_time) * 1000); elapsed_time = duration_cast(high_resolution_clock::now() - m_start_time).count(); } } - auto buffer = GetVideoBuffer(up_frame.get()); - - CopyNV12Data(buffer, *up_frame); - - video_info = {}; - video_info.timestamp = timestamp; - 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 the buffer to the queue + if (m_current_video_frame.has_value()) { + m_video_buffers.Push(std::move(m_current_video_frame.value())); + } + m_current_video_frame = std::move(frame->buffer); + video_info = frame->info; return true; } @@ -381,45 +352,38 @@ bool AvPlayerSource::GetAudioData(SceAvPlayerFrameInfo& audio_info) { auto frame = m_audio_frames.Pop(); if (!frame.has_value()) { + LOG_WARNING(Lib_AvPlayer, "Could get audio frame: no frames."); 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; auto elapsed_time = duration_cast(high_resolution_clock::now() - m_start_time).count(); - while (elapsed_time < timestamp) { - sceKernelUsleep((timestamp - elapsed_time) * 1000); + while (elapsed_time < frame->info.timestamp) { + sceKernelUsleep((frame->info.timestamp - elapsed_time) * 1000); elapsed_time = duration_cast(high_resolution_clock::now() - m_start_time).count(); } } - auto buffer = GetAudioBuffer(up_frame.get()); - const auto size = up_frame->ch_layout.nb_channels * up_frame->nb_samples * sizeof(u16); - std::memcpy(buffer, up_frame->data[0], size); + // return the buffer to the queue + if (m_current_audio_frame.has_value()) { + m_audio_buffers.Push(std::move(m_current_audio_frame.value())); + } + m_current_audio_frame = std::move(frame->buffer); audio_info = {}; - audio_info.timestamp = timestamp; - audio_info.pData = m_audio_frame_storage.data(); - audio_info.details.audio.size = u32(m_audio_frame_storage.size()); - audio_info.details.audio.channel_count = up_frame->ch_layout.nb_channels; + audio_info.timestamp = frame->info.timestamp; + audio_info.pData = reinterpret_cast(frame->info.pData); + audio_info.details.audio.size = frame->info.details.audio.size; + audio_info.details.audio.channel_count = frame->info.details.audio.channel_count; return true; } u64 AvPlayerSource::CurrentTime() { - // using namespace std::chrono; - // return duration_cast(high_resolution_clock::now() - m_start_time).count(); - return m_last_video_timestamp; + using namespace std::chrono; + return duration_cast(high_resolution_clock::now() - m_start_time).count(); } 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) { - LOG_TRACE(Lib_AvPlayer, "Demuxer Thread started"); const auto self = reinterpret_cast(opaque); 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; } + LOG_INFO(Lib_AvPlayer, "Demuxer Thread started"); while (!self->m_is_stop) { if (self->m_video_packets.Size() > 60) { @@ -470,10 +441,10 @@ void* AvPlayerSource::DemuxerThread(void* opaque) { continue; } 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 == AVERROR_EOF) { - LOG_TRACE(Lib_AvPlayer, "EOF reached in demuxer"); + LOG_INFO(Lib_AvPlayer, "EOF reached in demuxer"); break; } else { LOG_ERROR(Lib_AvPlayer, "Could not read AV frame: error = {}", res); @@ -500,7 +471,7 @@ void* AvPlayerSource::DemuxerThread(void* opaque) { } self->m_state.OnEOF(); - LOG_TRACE(Lib_AvPlayer, "Demuxer Thread exited normaly"); + LOG_INFO(Lib_AvPlayer, "Demuxer Thread exited normaly"); scePthreadExit(0); } @@ -531,12 +502,47 @@ AvPlayerSource::AVFramePtr AvPlayerSource::ConvertVideoFrame(const AVFrame& fram 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) { - LOG_TRACE(Lib_AvPlayer, "Video Decoder Thread started"); + LOG_INFO(Lib_AvPlayer, "Video Decoder Thread started"); const auto self = reinterpret_cast(opaque); 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); continue; } @@ -544,6 +550,7 @@ void* AvPlayerSource::VideoDecoderThread(void* opaque) { if (!packet.has_value()) { continue; } + auto res = avcodec_send_packet(self->m_video_codec_context.get(), packet->get()); if (res < 0 && res != AVERROR(EAGAIN)) { self->m_state.OnError(); @@ -552,11 +559,15 @@ void* AvPlayerSource::VideoDecoderThread(void* opaque) { scePthreadExit(nullptr); } 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); res = avcodec_receive_frame(self->m_video_codec_context.get(), up_frame.get()); if (res < 0) { if (res == AVERROR_EOF) { - LOG_TRACE(Lib_AvPlayer, "EOF reached in video decoder"); + LOG_INFO(Lib_AvPlayer, "EOF reached in video decoder"); scePthreadExit(nullptr); } else if (res != AVERROR(EAGAIN)) { LOG_ERROR(Lib_AvPlayer, @@ -566,18 +577,26 @@ void* AvPlayerSource::VideoDecoderThread(void* opaque) { scePthreadExit(nullptr); } } else { - LOG_TRACE(Lib_AvPlayer, "Producing Video Frame. Num Frames: {}", - self->m_video_frames.Size()); - if (up_frame->format != AV_PIX_FMT_NV12) { - self->m_video_frames.Push(self->ConvertVideoFrame(*up_frame)); - } else { - self->m_video_frames.Push(std::move(up_frame)); + auto buffer = self->m_video_buffers.Pop(); + if (!buffer.has_value()) { + // Video buffers queue was cleared. This means that player was stopped. + break; } + 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); } @@ -607,12 +626,45 @@ AvPlayerSource::AVFramePtr AvPlayerSource::ConvertAudioFrame(const AVFrame& fram 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) { - LOG_TRACE(Lib_AvPlayer, "Audio Decoder Thread started"); + LOG_INFO(Lib_AvPlayer, "Audio Decoder Thread started"); const auto self = reinterpret_cast(opaque); 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); continue; } @@ -628,11 +680,15 @@ void* AvPlayerSource::AudioDecoderThread(void* opaque) { scePthreadExit(nullptr); } 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); res = avcodec_receive_frame(self->m_audio_codec_context.get(), up_frame.get()); if (res < 0) { if (res == AVERROR_EOF) { - LOG_TRACE(Lib_AvPlayer, "EOF reached in audio decoder"); + LOG_INFO(Lib_AvPlayer, "EOF reached in audio decoder"); scePthreadExit(nullptr); } else if (res != AVERROR(EAGAIN)) { self->m_state.OnError(); @@ -642,16 +698,26 @@ void* AvPlayerSource::AudioDecoderThread(void* opaque) { scePthreadExit(nullptr); } } else { - if (up_frame->format != AV_SAMPLE_FMT_S16) { - self->m_audio_frames.Push(self->ConvertAudioFrame(*up_frame)); - } else { - self->m_audio_frames.Push(std::move(up_frame)); + auto buffer = self->m_audio_buffers.Pop(); + if (!buffer.has_value()) { + // Audio buffers queue was cleared. This means that player was stopped. + 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); } diff --git a/src/core/libraries/avplayer/avplayer_source.h b/src/core/libraries/avplayer/avplayer_source.h index 81f77a62..67ad44d8 100644 --- a/src/core/libraries/avplayer/avplayer_source.h +++ b/src/core/libraries/avplayer/avplayer_source.h @@ -38,11 +38,13 @@ class FrameBuffer { public: FrameBuffer(const SceAvPlayerMemAllocator& memory_replacement, u32 align, u32 size) noexcept : 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() { - if (!m_data.empty()) { - Deallocate(m_memory_replacement, m_data.data()); + if (m_data != nullptr) { + Deallocate(m_memory_replacement, m_data); m_data = {}; } } @@ -52,17 +54,16 @@ public: FrameBuffer(FrameBuffer&& r) noexcept : m_memory_replacement(r.m_memory_replacement), m_data(r.m_data) { - r.m_data = {}; + r.m_data = nullptr; }; FrameBuffer& operator=(FrameBuffer&& r) noexcept { - m_memory_replacement = r.m_memory_replacement; std::swap(m_data, r.m_data); return *this; } u8* GetBuffer() const noexcept { - return m_data.data(); + return m_data; } private: @@ -75,23 +76,26 @@ private: memory_replacement.deallocate(memory_replacement.object_ptr, ptr); } - SceAvPlayerMemAllocator m_memory_replacement; - std::span m_data; + const SceAvPlayerMemAllocator& m_memory_replacement; + u8* m_data = nullptr; +}; + +struct Frame { + FrameBuffer buffer; + SceAvPlayerFrameInfoEx info; }; class AvPlayerSource { public: - AvPlayerSource(AvPlayerStateCallback& state); + AvPlayerSource(AvPlayerStateCallback& state, std::string_view path, + const SceAvPlayerInitData& init_data, ThreadPriorities& priorities, + SceAvPlayerSourceType source_type); ~AvPlayerSource(); - s32 Init(std::string_view path, SceAvPlayerMemAllocator& memory_replacement, - SceAvPlayerFileReplacement& file_replacement, ThreadPriorities& priorities, - SceAvPlayerSourceType source_type); - bool FindStreamInfo(); s32 GetStreamCount(); s32 GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info); - s32 EnableStream(u32 stream_index); + bool EnableStream(u32 stream_index); void SetLooping(bool is_looping); std::optional HasFrames(u32 num_frames); s32 Start(); @@ -114,52 +118,55 @@ private: static void ReleaseAVCodecContext(AVCodecContext* context); static void ReleaseSWRContext(SwrContext* context); static void ReleaseSWSContext(SwsContext* context); + static void ReleaseAVFormatContext(AVFormatContext* context); using AVPacketPtr = std::unique_ptr; using AVFramePtr = std::unique_ptr; using AVCodecContextPtr = std::unique_ptr; using SWRContextPtr = std::unique_ptr; using SWSContextPtr = std::unique_ptr; - - u8* GetVideoBuffer(AVFrame* frame); - u8* GetAudioBuffer(AVFrame* frame); + using AVFormatContextPtr = std::unique_ptr; AVFramePtr ConvertAudioFrame(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; - ThreadPriorities m_priorities; - SceAvPlayerMemAllocator m_memory_replacement; + ThreadPriorities m_priorities{}; + SceAvPlayerMemAllocator m_memory_replacement{}; + u64 m_num_output_video_framebuffers{}; std::atomic_bool m_is_looping = false; std::atomic_bool m_is_eof = false; std::atomic_bool m_is_stop = false; + std::unique_ptr m_up_data_streamer; - AVFormatContext* m_avformat_context{}; + AvPlayerQueue m_audio_buffers; + AvPlayerQueue m_video_buffers; AvPlayerQueue m_audio_packets; AvPlayerQueue m_video_packets; - AvPlayerQueue m_audio_frames; - AvPlayerQueue m_video_frames; + AvPlayerQueue m_audio_frames; + AvPlayerQueue m_video_frames; - std::span m_video_frame_storage; - std::span m_audio_frame_storage; + std::optional m_current_video_frame; + std::optional m_current_audio_frame; - AVCodecContextPtr m_video_codec_context{nullptr, &ReleaseAVCodecContext}; - AVCodecContextPtr m_audio_codec_context{nullptr, &ReleaseAVCodecContext}; - - std::optional m_video_stream_index{}; - std::optional m_audio_stream_index{}; + std::optional m_video_stream_index{}; + std::optional m_audio_stream_index{}; ScePthread m_demuxer_thread{}; ScePthread m_video_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}; SWSContextPtr m_sws_context{nullptr, &ReleaseSWSContext}; diff --git a/src/core/libraries/avplayer/avplayer_state.cpp b/src/core/libraries/avplayer/avplayer_state.cpp index 3048819e..e7aa4571 100644 --- a/src/core/libraries/avplayer/avplayer_state.cpp +++ b/src/core/libraries/avplayer/avplayer_state.cpp @@ -39,38 +39,29 @@ void PS4_SYSV_ABI AvPlayerState::AutoPlayEventCallback(void* opaque, s32 event_i switch (info.type) { case SCE_AVPLAYER_VIDEO: if (video_stream_index == -1) { - if (default_language.empty()) { - video_stream_index = stream_index; - break; - } - if (default_language == - reinterpret_cast(info.details.video.language_code)) { - video_stream_index = stream_index; - } + video_stream_index = stream_index; + } + if (!default_language.empty() && + default_language == reinterpret_cast(info.details.video.language_code)) { + video_stream_index = stream_index; } break; case SCE_AVPLAYER_AUDIO: if (audio_stream_index == -1) { - if (default_language.empty()) { - audio_stream_index = stream_index; - break; - } - if (default_language == - reinterpret_cast(info.details.video.language_code)) { - audio_stream_index = stream_index; - } + audio_stream_index = stream_index; + } + if (!default_language.empty() && + default_language == reinterpret_cast(info.details.video.language_code)) { + audio_stream_index = stream_index; } break; case SCE_AVPLAYER_TIMEDTEXT: - if (timedtext_stream_index == -1) { - if (default_language.empty()) { - timedtext_stream_index = stream_index; - break; - } - if (default_language == - reinterpret_cast(info.details.video.language_code)) { - timedtext_stream_index = stream_index; - } + if (default_language.empty()) { + timedtext_stream_index = stream_index; + break; + } + if (default_language == reinterpret_cast(info.details.video.language_code)) { + timedtext_stream_index = stream_index; } break; } @@ -89,8 +80,9 @@ void PS4_SYSV_ABI AvPlayerState::AutoPlayEventCallback(void* opaque, s32 event_i return; } - const auto callback = self->m_user_event_replacement.event_callback; - const auto ptr = self->m_user_event_replacement.object_ptr; + // Pass other events to the game + const auto callback = self->m_event_replacement.event_callback; + const auto ptr = self->m_event_replacement.object_ptr; if (callback != nullptr) { 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 AvPlayerState::AvPlayerState(const SceAvPlayerInitData& init_data, const ThreadPriorities& priorities) - : m_event_handler_mutex(PTHREAD_MUTEX_ERRORCHECK, "SceAvPlayerEventHandler"), - m_state_machine_mutex(PTHREAD_MUTEX_ERRORCHECK, "SceAvPlayerStateMachine") { - if (init_data.event_replacement.event_callback == nullptr || init_data.auto_start) { + : m_init_data(init_data), m_event_replacement(init_data.event_replacement), + m_thread_priorities(priorities), + m_event_handler_mutex(PTHREAD_MUTEX_ERRORCHECK, "AvPlayer_EventHandler"), + m_state_machine_mutex(PTHREAD_MUTEX_ERRORCHECK, "AvPlayer_StateMachine") { + if (m_event_replacement.event_callback == nullptr || init_data.auto_start) { m_auto_start = true; - m_event_replacement.event_callback = &AvPlayerState::AutoPlayEventCallback; - m_event_replacement.object_ptr = this; - } else { - m_event_replacement = init_data.event_replacement; + m_init_data.event_replacement.event_callback = &AvPlayerState::AutoPlayEventCallback; + m_init_data.event_replacement.object_ptr = this; } - 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) { 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() { - m_event_queue.Clear(); if (m_up_source && m_current_state == AvState::Play) { 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 s32 AvPlayerState::AddSource(std::string_view path, SceAvPlayerSourceType source_type) { if (path.empty()) { + LOG_ERROR(Lib_AvPlayer, "File path is empty."); return -1; } - if (m_up_source) { + if (m_up_source != nullptr) { + LOG_ERROR(Lib_AvPlayer, "Only one source is supported."); return -1; } - m_up_source = std::make_unique(*this); - if (AVPLAYER_IS_ERROR(m_up_source->Init(path, m_memory_replacement, m_file_replacement, - m_thread_priorities, source_type))) { - return -1; - } + m_up_source = std::make_unique(*this, path, m_init_data, m_thread_priorities, + source_type); AddSourceEvent(); - return 0; } // Called inside GAME thread 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(); } // Called inside GAME thread 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); } // Called inside GAME thread 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 m_up_source->Start(); + SetState(AvState::Play); + OnPlaybackStateChanged(AvState::Play); + return 0; } void* AvPlayerState::AvControllerThread(void* p_user_data) { AvPlayerState* self = reinterpret_cast(p_user_data); - while (self->m_quit.load() == 0) { + while (!self->m_quit) { if (self->m_event_queue.Size() != 0) { self->ProcessEvent(); 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 int AvPlayerState::StartControllerThread() { m_quit.store(0); @@ -214,25 +210,28 @@ int AvPlayerState::StartControllerThread() { }; m_controller_thread = CreateThread(&AvControllerThread, params); if (m_controller_thread == nullptr) { + LOG_ERROR(Lib_AvPlayer, "Could not create CONTROLLER thread."); return -1; } return 0; } // Called inside GAME thread -s32 AvPlayerState::EnableStream(u32 stream_id) { +bool AvPlayerState::EnableStream(u32 stream_index) { 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 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; } - SetState(AvState::Stop); OnPlaybackStateChanged(AvState::Stop); return m_up_source->Stop(); } @@ -268,13 +267,16 @@ bool AvPlayerState::IsActive() { u64 AvPlayerState::CurrentTime() { if (m_up_source == nullptr) { + LOG_ERROR(Lib_AvPlayer, "Could not get current time. No source."); return 0; } return m_up_source->CurrentTime(); } +// May be called from different threads void AvPlayerState::OnWarning(u32 id) { - EmitEvent(SCE_AVPLAYER_WARNING_ID, &id); + // Forward to CONTROLLER thread + WarningEvent(id); } void AvPlayerState::OnError() { @@ -282,9 +284,7 @@ void AvPlayerState::OnError() { OnPlaybackStateChanged(AvState::Error); } -void AvPlayerState::OnEOF() { - Stop(); -} +void AvPlayerState::OnEOF() {} // Called inside CONTROLLER thread void AvPlayerState::OnPlaybackStateChanged(AvState state) { @@ -319,6 +319,8 @@ bool AvPlayerState::SetState(AvState state) { std::lock_guard guard(m_state_machine_mutex); 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; } m_previous_state.store(m_current_state); @@ -337,16 +339,16 @@ std::optional AvPlayerState::OnBufferingCheckEvent(u32 num_frames) { // Called inside CONTROLLER thread 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)); - const auto callback = m_event_replacement.event_callback; + const auto callback = m_init_data.event_replacement.event_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); } } // Called inside CONTROLLER thread int AvPlayerState::ProcessEvent() { - if (m_current_state.load() == AvState::Jump) { + if (m_current_state == AvState::Jump) { return -2; } @@ -357,6 +359,10 @@ int AvPlayerState::ProcessEvent() { return -1; } switch (event->event) { + case AvEventType::WarningId: { + OnWarning(event->payload.error); + break; + } case AvEventType::RevertState: { SetState(m_previous_state.load()); break; diff --git a/src/core/libraries/avplayer/avplayer_state.h b/src/core/libraries/avplayer/avplayer_state.h index fc822884..cfddab09 100644 --- a/src/core/libraries/avplayer/avplayer_state.h +++ b/src/core/libraries/avplayer/avplayer_state.h @@ -24,7 +24,7 @@ public: s32 AddSource(std::string_view filename, SceAvPlayerSourceType source_type); s32 GetStreamCount(); s32 GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info); - s32 EnableStream(u32 stream_id); + bool EnableStream(u32 stream_index); s32 Start(); bool Stop(); bool GetAudioData(SceAvPlayerFrameInfo& audio_info); @@ -54,6 +54,8 @@ private: static void* PS4_SYSV_ABI AvControllerThread(void* p_user_data); void AddSourceEvent(); + void WarningEvent(s32 id); + int StartControllerThread(); int ProcessEvent(); int UpdateBufferingState(); @@ -61,15 +63,13 @@ private: std::unique_ptr m_up_source; - SceAvPlayerMemAllocator m_memory_replacement{}; - SceAvPlayerFileReplacement m_file_replacement{}; + SceAvPlayerInitData m_init_data{}; SceAvPlayerEventReplacement m_event_replacement{}; - SceAvPlayerEventReplacement m_user_event_replacement{}; ThreadPriorities m_thread_priorities{}; bool m_auto_start{}; u8 m_default_language[4]{}; - std::atomic_int32_t m_quit; + std::atomic_bool m_quit; std::atomic m_current_state; std::atomic m_previous_state; u32 m_thread_priority;