diff --git a/CMakeLists.txt b/CMakeLists.txt index 9d3ea35c3..04bd6a331 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -348,6 +348,13 @@ set(FIBER_LIB src/core/libraries/fiber/fiber.cpp src/core/libraries/fiber/fiber.h ) +set(VDEC_LIB src/core/libraries/videodec/videodec2_impl.cpp + src/core/libraries/videodec/videodec2_impl.h + src/core/libraries/videodec/videodec2.cpp + src/core/libraries/videodec/videodec2.h + src/core/libraries/videodec/videodec2_avc.h +) + set(NP_LIBS src/core/libraries/np_manager/np_manager.cpp src/core/libraries/np_manager/np_manager.h src/core/libraries/np_score/np_score.cpp @@ -503,6 +510,7 @@ set(CORE src/core/aerolib/stubs.cpp ${MISC_LIBS} ${IME_LIB} ${FIBER_LIB} + ${VDEC_LIB} ${DEV_TOOLS} src/core/debug_state.cpp src/core/debug_state.h diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp index 051bbd79e..a76c472a7 100644 --- a/src/common/logging/filter.cpp +++ b/src/common/logging/filter.cpp @@ -119,6 +119,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) { SUB(Lib, Remoteplay) \ SUB(Lib, SharePlay) \ SUB(Lib, Fiber) \ + SUB(Lib, Vdec2) \ CLS(Frontend) \ CLS(Render) \ SUB(Render, Vulkan) \ diff --git a/src/common/logging/types.h b/src/common/logging/types.h index 39319d0dc..fd7dcfd1a 100644 --- a/src/common/logging/types.h +++ b/src/common/logging/types.h @@ -86,6 +86,7 @@ enum class Class : u8 { Lib_Remoteplay, ///< The LibSceRemotePlay implementation Lib_SharePlay, ///< The LibSceSharePlay implemenation Lib_Fiber, ///< The LibSceFiber implementation. + Lib_Vdec2, ///< The LibSceVideodec2 implementation. Frontend, ///< Emulator UI Render, ///< Video Core Render_Vulkan, ///< Vulkan backend diff --git a/src/core/libraries/error_codes.h b/src/core/libraries/error_codes.h index c3efe0e97..66ad5f8b6 100644 --- a/src/core/libraries/error_codes.h +++ b/src/core/libraries/error_codes.h @@ -552,4 +552,42 @@ constexpr int ORBIS_IME_ERROR_INVALID_PARAM = 0x80BC0030; constexpr int ORBIS_IME_ERROR_INVALID_ADDRESS = 0x80BC0031; constexpr int ORBIS_IME_ERROR_INVALID_RESERVED = 0x80BC0032; constexpr int ORBIS_IME_ERROR_INVALID_TIMING = 0x80BC0033; -constexpr int ORBIS_IME_ERROR_INTERNAL = 0x80BC00FF; \ No newline at end of file +constexpr int ORBIS_IME_ERROR_INTERNAL = 0x80BC00FF; + +// Videodec2 library +constexpr int ORBIS_VIDEODEC2_ERROR_API_FAIL = 0x811D0100; +constexpr int ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE = 0x811D0101; +constexpr int ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER = 0x811D0102; +constexpr int ORBIS_VIDEODEC2_ERROR_DECODER_INSTANCE = 0x811D0103; +constexpr int ORBIS_VIDEODEC2_ERROR_MEMORY_SIZE = 0x811D0104; +constexpr int ORBIS_VIDEODEC2_ERROR_MEMORY_POINTER = 0x811D0105; +constexpr int ORBIS_VIDEODEC2_ERROR_FRAME_BUFFER_SIZE = 0x811D0106; +constexpr int ORBIS_VIDEODEC2_ERROR_FRAME_BUFFER_POINTER = 0x811D0107; +constexpr int ORBIS_VIDEODEC2_ERROR_FRAME_BUFFER_ALIGNMENT = 0x811D0108; +constexpr int ORBIS_VIDEODEC2_ERROR_NOT_ONION_MEMORY = 0x811D0109; +constexpr int ORBIS_VIDEODEC2_ERROR_NOT_GARLIC_MEMORY = 0x811D010A; +constexpr int ORBIS_VIDEODEC2_ERROR_NOT_DIRECT_MEMORY = 0x811D010B; +constexpr int ORBIS_VIDEODEC2_ERROR_MEMORY_INFO = 0x811D010C; +constexpr int ORBIS_VIDEODEC2_ERROR_ACCESS_UNIT_SIZE = 0x811D010D; +constexpr int ORBIS_VIDEODEC2_ERROR_ACCESS_UNIT_POINTER = 0x811D010E; +constexpr int ORBIS_VIDEODEC2_ERROR_OUTPUT_INFO = 0x811D010F; +constexpr int ORBIS_VIDEODEC2_ERROR_COMPUTE_QUEUE = 0x811D0110; +constexpr int ORBIS_VIDEODEC2_ERROR_FATAL_STATE = 0x811D0111; +constexpr int ORBIS_VIDEODEC2_ERROR_PRESET_VALUE = 0x811D0112; +constexpr int ORBIS_VIDEODEC2_ERROR_CONFIG_INFO = 0x811D0200; +constexpr int ORBIS_VIDEODEC2_ERROR_COMPUTE_PIPE_ID = 0x811D0201; +constexpr int ORBIS_VIDEODEC2_ERROR_COMPUTE_QUEUE_ID = 0x811D0202; +constexpr int ORBIS_VIDEODEC2_ERROR_RESOURCE_TYPE = 0x811D0203; +constexpr int ORBIS_VIDEODEC2_ERROR_CODEC_TYPE = 0x811D0204; +constexpr int ORBIS_VIDEODEC2_ERROR_PROFILE_LEVEL = 0x811D0205; +constexpr int ORBIS_VIDEODEC2_ERROR_PIPELINE_DEPTH = 0x811D0206; +constexpr int ORBIS_VIDEODEC2_ERROR_AFFINITY_MASK = 0x811D0207; +constexpr int ORBIS_VIDEODEC2_ERROR_THREAD_PRIORITY = 0x811D0208; +constexpr int ORBIS_VIDEODEC2_ERROR_DPB_FRAME_COUNT = 0x811D0209; +constexpr int ORBIS_VIDEODEC2_ERROR_FRAME_WIDTH_HEIGHT = 0x811D020A; +constexpr int ORBIS_VIDEODEC2_ERROR_EXTRA_CONFIG_INFO = 0x811D020B; +constexpr int ORBIS_VIDEODEC2_ERROR_NEW_SEQUENCE = 0x811D0300; +constexpr int ORBIS_VIDEODEC2_ERROR_ACCESS_UNIT = 0x811D0301; +constexpr int ORBIS_VIDEODEC2_ERROR_OVERSIZE_DECODE = 0x811D0302; +constexpr int ORBIS_VIDEODEC2_ERROR_INVALID_SEQUENCE = 0x811D0303; +constexpr int ORBIS_VIDEODEC2_ERROR_FATAL_STREAM = 0x811D0304; \ No newline at end of file diff --git a/src/core/libraries/fiber/fiber.cpp b/src/core/libraries/fiber/fiber.cpp index bd1575dda..a0bfd6850 100644 --- a/src/core/libraries/fiber/fiber.cpp +++ b/src/core/libraries/fiber/fiber.cpp @@ -15,7 +15,7 @@ namespace Libraries::Fiber { -constexpr static u64 kFiberSignature = 0x054ad954; +static constexpr u64 kFiberSignature = 0x054ad954; thread_local SceFiber* gCurrentFiber = nullptr; thread_local void* gFiberThread = nullptr; diff --git a/src/core/libraries/libs.cpp b/src/core/libraries/libs.cpp index 10f425c13..b0365435b 100644 --- a/src/core/libraries/libs.cpp +++ b/src/core/libraries/libs.cpp @@ -41,6 +41,7 @@ #include "core/libraries/system/systemservice.h" #include "core/libraries/system/userservice.h" #include "core/libraries/usbd/usbd.h" +#include "core/libraries/videodec/videodec2.h" #include "core/libraries/videoout/video_out.h" namespace Libraries { @@ -80,6 +81,7 @@ void InitHLELibs(Core::Loader::SymbolsResolver* sym) { Libraries::ErrorDialog::RegisterlibSceErrorDialog(sym); Libraries::ImeDialog::RegisterlibSceImeDialog(sym); Libraries::AvPlayer::RegisterlibSceAvPlayer(sym); + Libraries::Vdec2::RegisterlibSceVdec2(sym); Libraries::Audio3d::RegisterlibSceAudio3d(sym); Libraries::Ime::RegisterlibSceIme(sym); Libraries::GameLiveStreaming::RegisterlibSceGameLiveStreaming(sym); diff --git a/src/core/libraries/videodec/videodec2.cpp b/src/core/libraries/videodec/videodec2.cpp new file mode 100644 index 000000000..fe2b82bea --- /dev/null +++ b/src/core/libraries/videodec/videodec2.cpp @@ -0,0 +1,200 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "videodec2.h" +#include "videodec2_impl.h" + +#include "common/assert.h" +#include "common/logging/log.h" +#include "core/libraries/error_codes.h" +#include "core/libraries/libs.h" + +namespace Libraries::Vdec2 { + +static constexpr u64 kMinimumMemorySize = 32_MB; ///> Fake minimum memory size for querying + +s32 PS4_SYSV_ABI +sceVideodec2QueryComputeMemoryInfo(OrbisVideodec2ComputeMemoryInfo* computeMemInfo) { + LOG_INFO(Lib_Vdec2, "called"); + + if (!computeMemInfo) { + return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER; + } + if (computeMemInfo->thisSize != sizeof(OrbisVideodec2ComputeMemoryInfo)) { + return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE; + } + + computeMemInfo->cpuGpuMemory = nullptr; + computeMemInfo->cpuGpuMemorySize = kMinimumMemorySize; + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI +sceVideodec2AllocateComputeQueue(const OrbisVideodec2ComputeConfigInfo* computeCfgInfo, + const OrbisVideodec2ComputeMemoryInfo* computeMemInfo, + OrbisVideodec2ComputeQueue* computeQueue) { + LOG_INFO(Lib_Vdec2, "called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVideodec2ReleaseComputeQueue(OrbisVideodec2ComputeQueue computeQueue) { + LOG_INFO(Lib_Vdec2, "called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI +sceVideodec2QueryDecoderMemoryInfo(const OrbisVideodec2DecoderConfigInfo* decoderCfgInfo, + OrbisVideodec2DecoderMemoryInfo* decoderMemInfo) { + LOG_INFO(Lib_Vdec2, "called"); + + if (!decoderCfgInfo || !decoderMemInfo) { + return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER; + } + if (decoderCfgInfo->thisSize != sizeof(OrbisVideodec2DecoderConfigInfo) || + decoderMemInfo->thisSize != sizeof(OrbisVideodec2DecoderMemoryInfo)) { + return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE; + } + + decoderMemInfo->cpuMemory = nullptr; + decoderMemInfo->gpuMemory = nullptr; + decoderMemInfo->cpuGpuMemory = nullptr; + + decoderMemInfo->cpuGpuMemorySize = kMinimumMemorySize; + decoderMemInfo->cpuMemorySize = kMinimumMemorySize; + decoderMemInfo->gpuMemorySize = kMinimumMemorySize; + + decoderMemInfo->maxFrameBufferSize = kMinimumMemorySize; + decoderMemInfo->frameBufferAlignment = 0x100; + + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVideodec2CreateDecoder(const OrbisVideodec2DecoderConfigInfo* decoderCfgInfo, + const OrbisVideodec2DecoderMemoryInfo* decoderMemInfo, + OrbisVideodec2Decoder* decoder) { + LOG_INFO(Lib_Vdec2, "called"); + + if (!decoderCfgInfo || !decoderMemInfo || !decoder) { + return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER; + } + if (decoderCfgInfo->thisSize != sizeof(OrbisVideodec2DecoderConfigInfo) || + decoderMemInfo->thisSize != sizeof(OrbisVideodec2DecoderMemoryInfo)) { + return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE; + } + + *decoder = new VdecDecoder(*decoderCfgInfo, *decoderMemInfo); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVideodec2DeleteDecoder(OrbisVideodec2Decoder decoder) { + LOG_INFO(Lib_Vdec2, "called"); + + if (!decoder) { + return ORBIS_VIDEODEC2_ERROR_DECODER_INSTANCE; + } + + delete decoder; + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVideodec2Decode(OrbisVideodec2Decoder decoder, + const OrbisVideodec2InputData* inputData, + OrbisVideodec2FrameBuffer* frameBuffer, + OrbisVideodec2OutputInfo* outputInfo) { + LOG_TRACE(Lib_Vdec2, "called"); + + if (!decoder) { + return ORBIS_VIDEODEC2_ERROR_DECODER_INSTANCE; + } + if (!inputData || !frameBuffer || !outputInfo) { + return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER; + } + if (inputData->thisSize != sizeof(OrbisVideodec2InputData) || + frameBuffer->thisSize != sizeof(OrbisVideodec2FrameBuffer)) { + return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE; + } + + return decoder->Decode(*inputData, *frameBuffer, *outputInfo); +} + +s32 PS4_SYSV_ABI sceVideodec2Flush(OrbisVideodec2Decoder decoder, + OrbisVideodec2FrameBuffer* frameBuffer, + OrbisVideodec2OutputInfo* outputInfo) { + LOG_INFO(Lib_Vdec2, "called"); + + if (!decoder) { + return ORBIS_VIDEODEC2_ERROR_DECODER_INSTANCE; + } + if (!frameBuffer || !outputInfo) { + return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER; + } + if (frameBuffer->thisSize != sizeof(OrbisVideodec2FrameBuffer) || + outputInfo->thisSize != sizeof(OrbisVideodec2OutputInfo)) { + return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE; + } + + return decoder->Flush(*frameBuffer, *outputInfo); +} + +s32 PS4_SYSV_ABI sceVideodec2Reset(OrbisVideodec2Decoder decoder) { + LOG_INFO(Lib_Vdec2, "called"); + + if (!decoder) { + return ORBIS_VIDEODEC2_ERROR_DECODER_INSTANCE; + } + + return decoder->Reset(); +} + +s32 PS4_SYSV_ABI sceVideodec2GetPictureInfo(const OrbisVideodec2OutputInfo* outputInfo, + void* p1stPictureInfoOut, void* p2ndPictureInfoOut) { + LOG_TRACE(Lib_Vdec2, "called"); + + if (!outputInfo) { + return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER; + } + if (outputInfo->thisSize != sizeof(OrbisVideodec2OutputInfo)) { + return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE; + } + if (outputInfo->pictureCount == 0 || gPictureInfos.empty()) { + return ORBIS_OK; + } + + if (p1stPictureInfoOut) { + OrbisVideodec2AvcPictureInfo* picInfo = + static_cast(p1stPictureInfoOut); + if (picInfo->thisSize != sizeof(OrbisVideodec2AvcPictureInfo)) { + return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE; + } + *picInfo = gPictureInfos.back(); + } + + if (outputInfo->pictureCount > 1) { + UNREACHABLE(); + } + + return ORBIS_OK; +} + +void RegisterlibSceVdec2(Core::Loader::SymbolsResolver* sym) { + LIB_FUNCTION("RnDibcGCPKw", "libSceVideodec2", 1, "libSceVideodec2", 1, 1, + sceVideodec2QueryComputeMemoryInfo); + LIB_FUNCTION("eD+X2SmxUt4", "libSceVideodec2", 1, "libSceVideodec2", 1, 1, + sceVideodec2AllocateComputeQueue); + LIB_FUNCTION("UvtA3FAiF4Y", "libSceVideodec2", 1, "libSceVideodec2", 1, 1, + sceVideodec2ReleaseComputeQueue); + + LIB_FUNCTION("qqMCwlULR+E", "libSceVideodec2", 1, "libSceVideodec2", 1, 1, + sceVideodec2QueryDecoderMemoryInfo); + LIB_FUNCTION("CNNRoRYd8XI", "libSceVideodec2", 1, "libSceVideodec2", 1, 1, + sceVideodec2CreateDecoder); + LIB_FUNCTION("jwImxXRGSKA", "libSceVideodec2", 1, "libSceVideodec2", 1, 1, + sceVideodec2DeleteDecoder); + LIB_FUNCTION("852F5+q6+iM", "libSceVideodec2", 1, "libSceVideodec2", 1, 1, sceVideodec2Decode); + LIB_FUNCTION("l1hXwscLuCY", "libSceVideodec2", 1, "libSceVideodec2", 1, 1, sceVideodec2Flush); + LIB_FUNCTION("wJXikG6QFN8", "libSceVideodec2", 1, "libSceVideodec2", 1, 1, sceVideodec2Reset); + LIB_FUNCTION("NtXRa3dRzU0", "libSceVideodec2", 1, "libSceVideodec2", 1, 1, + sceVideodec2GetPictureInfo); +} + +} // namespace Libraries::Vdec2 \ No newline at end of file diff --git a/src/core/libraries/videodec/videodec2.h b/src/core/libraries/videodec/videodec2.h new file mode 100644 index 000000000..9b230cc54 --- /dev/null +++ b/src/core/libraries/videodec/videodec2.h @@ -0,0 +1,139 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/types.h" + +#include "videodec2_avc.h" + +namespace Core::Loader { +class SymbolsResolver; +} +namespace Libraries::Vdec2 { + +class VdecDecoder; + +using OrbisVideodec2Decoder = VdecDecoder*; +typedef void* OrbisVideodec2ComputeQueue; + +struct OrbisVideodec2DecoderConfigInfo { + u64 thisSize; + u32 resourceType; + u32 codecType; + u32 profile; + u32 maxLevel; + s32 maxFrameWidth; + s32 maxFrameHeight; + s32 maxDpbFrameCount; + u32 decodePipelineDepth; + OrbisVideodec2ComputeQueue computeQueue; + u64 cpuAffinityMask; + s32 cpuThreadPriority; + bool optimizeProgressiveVideo; + bool checkMemoryType; + u8 reserved0; + u8 reserved1; + void* extraConfigInfo; +}; +static_assert(sizeof(OrbisVideodec2DecoderConfigInfo) == 0x48); + +struct OrbisVideodec2DecoderMemoryInfo { + u64 thisSize; + u64 cpuMemorySize; + void* cpuMemory; + u64 gpuMemorySize; + void* gpuMemory; + u64 cpuGpuMemorySize; + void* cpuGpuMemory; + u64 maxFrameBufferSize; + u32 frameBufferAlignment; + u32 reserved0; +}; +static_assert(sizeof(OrbisVideodec2DecoderMemoryInfo) == 0x48); + +struct OrbisVideodec2InputData { + u64 thisSize; + void* auData; + u64 auSize; + u64 ptsData; + u64 dtsData; + u64 attachedData; +}; +static_assert(sizeof(OrbisVideodec2InputData) == 0x30); + +struct OrbisVideodec2OutputInfo { + u64 thisSize; + bool isValid; + bool isErrorFrame; + u8 pictureCount; + u32 codecType; + u32 frameWidth; + u32 framePitch; + u32 frameHeight; + void* frameBuffer; + u64 frameBufferSize; +}; +static_assert(sizeof(OrbisVideodec2OutputInfo) == 0x30); + +struct OrbisVideodec2FrameBuffer { + u64 thisSize; + void* frameBuffer; + u64 frameBufferSize; + bool isAccepted; +}; +static_assert(sizeof(OrbisVideodec2FrameBuffer) == 0x20); + +struct OrbisVideodec2ComputeMemoryInfo { + u64 thisSize; + u64 cpuGpuMemorySize; + void* cpuGpuMemory; +}; +static_assert(sizeof(OrbisVideodec2ComputeMemoryInfo) == 0x18); + +struct OrbisVideodec2ComputeConfigInfo { + u64 thisSize; + u16 computePipeId; + u16 computeQueueId; + bool checkMemoryType; + u8 reserved0; + u16 reserved1; +}; +static_assert(sizeof(OrbisVideodec2ComputeConfigInfo) == 0x10); + +s32 PS4_SYSV_ABI +sceVideodec2QueryComputeMemoryInfo(OrbisVideodec2ComputeMemoryInfo* computeMemInfo); + +s32 PS4_SYSV_ABI +sceVideodec2AllocateComputeQueue(const OrbisVideodec2ComputeConfigInfo* computeCfgInfo, + const OrbisVideodec2ComputeMemoryInfo* computeMemInfo, + OrbisVideodec2ComputeQueue* computeQueue); + +s32 PS4_SYSV_ABI sceVideodec2ReleaseComputeQueue(OrbisVideodec2ComputeQueue computeQueue); + +s32 PS4_SYSV_ABI +sceVideodec2QueryDecoderMemoryInfo(const OrbisVideodec2DecoderConfigInfo* decoderCfgInfo, + OrbisVideodec2DecoderMemoryInfo* decoderMemInfo); + +s32 PS4_SYSV_ABI sceVideodec2CreateDecoder(const OrbisVideodec2DecoderConfigInfo* decoderCfgInfo, + const OrbisVideodec2DecoderMemoryInfo* decoderMemInfo, + OrbisVideodec2Decoder* decoder); + +s32 PS4_SYSV_ABI sceVideodec2DeleteDecoder(OrbisVideodec2Decoder decoder); + +s32 PS4_SYSV_ABI sceVideodec2Decode(OrbisVideodec2Decoder decoder, + const OrbisVideodec2InputData* inputData, + OrbisVideodec2FrameBuffer* frameBuffer, + OrbisVideodec2OutputInfo* outputInfo); + +s32 PS4_SYSV_ABI sceVideodec2Flush(OrbisVideodec2Decoder decoder, + OrbisVideodec2FrameBuffer* frameBuffer, + OrbisVideodec2OutputInfo* outputInfo); + +s32 PS4_SYSV_ABI sceVideodec2Reset(OrbisVideodec2Decoder decoder); + +s32 PS4_SYSV_ABI sceVideodec2GetPictureInfo(const OrbisVideodec2OutputInfo* outputInfo, + void* p1stPictureInfo, void* p2ndPictureInfo); + +void RegisterlibSceVdec2(Core::Loader::SymbolsResolver* sym); +} // namespace Libraries::Vdec2 \ No newline at end of file diff --git a/src/core/libraries/videodec/videodec2_avc.h b/src/core/libraries/videodec/videodec2_avc.h new file mode 100644 index 000000000..4109b5fd2 --- /dev/null +++ b/src/core/libraries/videodec/videodec2_avc.h @@ -0,0 +1,60 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/types.h" + +namespace Libraries::Vdec2 { + +struct OrbisVideodec2AvcPictureInfo { + u64 thisSize; + + bool isValid; + + u64 ptsData; + u64 dtsData; + u64 attachedData; + + u8 idrPictureflag; + + u8 profile_idc; + u8 level_idc; + u32 pic_width_in_mbs_minus1; + u32 pic_height_in_map_units_minus1; + u8 frame_mbs_only_flag; + + u8 frame_cropping_flag; + u32 frameCropLeftOffset; + u32 frameCropRightOffset; + u32 frameCropTopOffset; + u32 frameCropBottomOffset; + + u8 aspect_ratio_info_present_flag; + u8 aspect_ratio_idc; + u16 sar_width; + u16 sar_height; + + u8 video_signal_type_present_flag; + u8 video_format; + u8 video_full_range_flag; + u8 colour_description_present_flag; + u8 colour_primaries; + u8 transfer_characteristics; + u8 matrix_coefficients; + + u8 timing_info_present_flag; + u32 num_units_in_tick; + u32 time_scale; + u8 fixed_frame_rate_flag; + + u8 bitstream_restriction_flag; + u8 max_dec_frame_buffering; + + u8 pic_struct_present_flag; + u8 pic_struct; + u8 field_pic_flag; + u8 bottom_field_flag; +}; + +} // namespace Libraries::Vdec2 \ No newline at end of file diff --git a/src/core/libraries/videodec/videodec2_impl.cpp b/src/core/libraries/videodec/videodec2_impl.cpp new file mode 100644 index 000000000..021965e3d --- /dev/null +++ b/src/core/libraries/videodec/videodec2_impl.cpp @@ -0,0 +1,228 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "videodec2_impl.h" + +#include "common/alignment.h" +#include "common/assert.h" +#include "common/logging/log.h" +#include "core/libraries/error_codes.h" + +namespace Libraries::Vdec2 { + +std::vector gPictureInfos; + +static inline void CopyNV12Data(u8* dst, const AVFrame& src) { + std::memcpy(dst, src.data[0], src.width * src.height); + std::memcpy(dst + (src.width * src.height), src.data[1], (src.width * src.height) / 2); +} + +VdecDecoder::VdecDecoder(const OrbisVideodec2DecoderConfigInfo& configInfo, + const OrbisVideodec2DecoderMemoryInfo& memoryInfo) { + ASSERT(configInfo.codecType == 1); /* AVC */ + + const AVCodec* codec = avcodec_find_decoder(AV_CODEC_ID_H264); + ASSERT(codec); + + mCodecContext = avcodec_alloc_context3(codec); + ASSERT(mCodecContext); + mCodecContext->width = configInfo.maxFrameWidth; + mCodecContext->height = configInfo.maxFrameHeight; + + avcodec_open2(mCodecContext, codec, nullptr); +} + +VdecDecoder::~VdecDecoder() { + avcodec_free_context(&mCodecContext); + sws_freeContext(mSwsContext); + + gPictureInfos.clear(); +} + +s32 VdecDecoder::Decode(const OrbisVideodec2InputData& inputData, + OrbisVideodec2FrameBuffer& frameBuffer, + OrbisVideodec2OutputInfo& outputInfo) { + frameBuffer.isAccepted = false; + outputInfo.thisSize = sizeof(OrbisVideodec2OutputInfo); + outputInfo.isValid = false; + outputInfo.isErrorFrame = true; + outputInfo.pictureCount = 0; + + if (!inputData.auData) { + return ORBIS_VIDEODEC2_ERROR_ACCESS_UNIT_POINTER; + } + if (inputData.auSize == 0) { + return ORBIS_VIDEODEC2_ERROR_ACCESS_UNIT_SIZE; + } + + AVPacket* packet = av_packet_alloc(); + if (!packet) { + LOG_ERROR(Lib_Vdec2, "Failed to allocate packet"); + return ORBIS_VIDEODEC2_ERROR_API_FAIL; + } + + packet->data = (u8*)inputData.auData; + packet->size = inputData.auSize; + packet->pts = inputData.ptsData; + packet->dts = inputData.dtsData; + + int ret = avcodec_send_packet(mCodecContext, packet); + if (ret < 0) { + LOG_ERROR(Lib_Vdec2, "Error sending packet to decoder: {}", ret); + av_packet_free(&packet); + return ORBIS_VIDEODEC2_ERROR_API_FAIL; + } + + AVFrame* frame = av_frame_alloc(); + if (frame == nullptr) { + LOG_ERROR(Lib_Vdec2, "Failed to allocate frame"); + av_packet_free(&packet); + return ORBIS_VIDEODEC2_ERROR_API_FAIL; + } + + while (true) { + ret = avcodec_receive_frame(mCodecContext, frame); + if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { + break; + } else if (ret < 0) { + LOG_ERROR(Lib_Vdec2, "Error receiving frame from decoder: {}", ret); + av_packet_free(&packet); + av_frame_free(&frame); + return ORBIS_VIDEODEC2_ERROR_API_FAIL; + } + + if (frame->format != AV_PIX_FMT_NV12) { + AVFrame* nv12_frame = ConvertNV12Frame(*frame); + ASSERT(nv12_frame); + av_frame_free(&frame); + frame = nv12_frame; + } + + CopyNV12Data((u8*)frameBuffer.frameBuffer, *frame); + frameBuffer.isAccepted = true; + + outputInfo.codecType = 1; // FIXME: Hardcoded to AVC + outputInfo.frameWidth = frame->width; + outputInfo.frameHeight = frame->height; + outputInfo.framePitch = frame->linesize[0]; + outputInfo.frameBufferSize = frameBuffer.frameBufferSize; + outputInfo.frameBuffer = frameBuffer.frameBuffer; + + outputInfo.isValid = true; + outputInfo.isErrorFrame = false; + outputInfo.pictureCount = 1; // TODO: 2 pictures for interlaced video + + if (outputInfo.isValid) { + OrbisVideodec2AvcPictureInfo pictureInfo = {}; + + pictureInfo.thisSize = sizeof(OrbisVideodec2AvcPictureInfo); + pictureInfo.isValid = true; + + pictureInfo.ptsData = inputData.ptsData; + pictureInfo.dtsData = inputData.dtsData; + pictureInfo.attachedData = inputData.attachedData; + + pictureInfo.frameCropLeftOffset = frame->crop_left; + pictureInfo.frameCropRightOffset = frame->crop_right; + pictureInfo.frameCropTopOffset = frame->crop_top; + pictureInfo.frameCropBottomOffset = frame->crop_bottom; + + gPictureInfos.push_back(pictureInfo); + } + } + + av_packet_free(&packet); + av_frame_free(&frame); + return ORBIS_OK; +} + +s32 VdecDecoder::Flush(OrbisVideodec2FrameBuffer& frameBuffer, + OrbisVideodec2OutputInfo& outputInfo) { + frameBuffer.isAccepted = false; + outputInfo.thisSize = sizeof(OrbisVideodec2OutputInfo); + outputInfo.isValid = false; + outputInfo.isErrorFrame = true; + outputInfo.pictureCount = 0; + + AVFrame* frame = av_frame_alloc(); + if (!frame) { + LOG_ERROR(Lib_Vdec2, "Failed to allocate frame"); + return ORBIS_VIDEODEC2_ERROR_API_FAIL; + } + + while (true) { + int ret = avcodec_receive_frame(mCodecContext, frame); + if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { + break; + } else if (ret < 0) { + LOG_ERROR(Lib_Vdec2, "Error receiving frame from decoder: {}", ret); + av_frame_free(&frame); + return ORBIS_VIDEODEC2_ERROR_API_FAIL; + } + + if (frame->format != AV_PIX_FMT_NV12) { + AVFrame* nv12_frame = ConvertNV12Frame(*frame); + ASSERT(nv12_frame); + av_frame_free(&frame); + frame = nv12_frame; + } + + CopyNV12Data((u8*)frameBuffer.frameBuffer, *frame); + frameBuffer.isAccepted = true; + + outputInfo.codecType = 1; // FIXME: Hardcoded to AVC + outputInfo.frameWidth = frame->width; + outputInfo.frameHeight = frame->height; + outputInfo.framePitch = frame->linesize[0]; + outputInfo.frameBufferSize = frameBuffer.frameBufferSize; + outputInfo.frameBuffer = frameBuffer.frameBuffer; + + outputInfo.isValid = true; + outputInfo.isErrorFrame = false; + outputInfo.pictureCount = 1; // TODO: 2 pictures for interlaced video + + // FIXME: Should we add picture info here too? + } + + av_frame_free(&frame); + return ORBIS_OK; +} + +s32 VdecDecoder::Reset() { + avcodec_flush_buffers(mCodecContext); + gPictureInfos.clear(); + return ORBIS_OK; +} + +AVFrame* VdecDecoder::ConvertNV12Frame(AVFrame& frame) { + AVFrame* nv12_frame = av_frame_alloc(); + nv12_frame->pts = frame.pts; + nv12_frame->pkt_dts = frame.pkt_dts < 0 ? 0 : frame.pkt_dts; + nv12_frame->format = AV_PIX_FMT_NV12; + nv12_frame->width = frame.width; + nv12_frame->height = frame.height; + nv12_frame->sample_aspect_ratio = frame.sample_aspect_ratio; + nv12_frame->crop_top = frame.crop_top; + nv12_frame->crop_bottom = frame.crop_bottom; + nv12_frame->crop_left = frame.crop_left; + nv12_frame->crop_right = frame.crop_right; + + av_frame_get_buffer(nv12_frame, 0); + + if (mSwsContext == nullptr) { + mSwsContext = sws_getContext(frame.width, frame.height, AVPixelFormat(frame.format), + nv12_frame->width, nv12_frame->height, AV_PIX_FMT_NV12, + SWS_FAST_BILINEAR, nullptr, nullptr, nullptr); + } + + const auto res = sws_scale(mSwsContext, frame.data, frame.linesize, 0, frame.height, + nv12_frame->data, nv12_frame->linesize); + if (res < 0) { + LOG_ERROR(Lib_Vdec2, "Could not convert to NV12: {}", av_err2str(res)); + return nullptr; + } + + return nv12_frame; +} + +} // namespace Libraries::Vdec2 \ No newline at end of file diff --git a/src/core/libraries/videodec/videodec2_impl.h b/src/core/libraries/videodec/videodec2_impl.h new file mode 100644 index 000000000..1bcece6e1 --- /dev/null +++ b/src/core/libraries/videodec/videodec2_impl.h @@ -0,0 +1,39 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +#include "videodec2.h" + +extern "C" { +#include +#include +#include +} + +namespace Libraries::Vdec2 { + +extern std::vector gPictureInfos; + +class VdecDecoder { +public: + VdecDecoder(const OrbisVideodec2DecoderConfigInfo& configInfo, + const OrbisVideodec2DecoderMemoryInfo& memoryInfo); + ~VdecDecoder(); + + s32 Decode(const OrbisVideodec2InputData& inputData, OrbisVideodec2FrameBuffer& frameBuffer, + OrbisVideodec2OutputInfo& outputInfo); + s32 Flush(OrbisVideodec2FrameBuffer& frameBuffer, OrbisVideodec2OutputInfo& outputInfo); + s32 Reset(); + +private: + AVFrame* ConvertNV12Frame(AVFrame& frame); + +private: + AVCodecContext* mCodecContext = nullptr; + SwsContext* mSwsContext = nullptr; +}; + +} // namespace Libraries::Vdec2 \ No newline at end of file diff --git a/src/core/libraries/videoout/video_out.cpp b/src/core/libraries/videoout/video_out.cpp index 631f77732..31b8a21ca 100644 --- a/src/core/libraries/videoout/video_out.cpp +++ b/src/core/libraries/videoout/video_out.cpp @@ -106,13 +106,13 @@ s32 PS4_SYSV_ABI sceVideoOutRegisterBuffers(s32 handle, s32 startIndex, void* co } s32 PS4_SYSV_ABI sceVideoOutSetFlipRate(s32 handle, s32 rate) { - LOG_INFO(Lib_VideoOut, "called"); + LOG_TRACE(Lib_VideoOut, "called"); driver->GetPort(handle)->flip_rate = rate; return ORBIS_OK; } s32 PS4_SYSV_ABI sceVideoOutIsFlipPending(s32 handle) { - LOG_INFO(Lib_VideoOut, "called"); + LOG_TRACE(Lib_VideoOut, "called"); auto* port = driver->GetPort(handle); std::unique_lock lock{port->port_mutex}; s32 pending = port->flip_status.flipPendingNum;