diff --git a/CMakeLists.txt b/CMakeLists.txt index 036f74309f..4396633525 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -366,6 +366,10 @@ set(VDEC_LIB src/core/libraries/videodec/videodec2_impl.cpp src/core/libraries/videodec/videodec2.cpp src/core/libraries/videodec/videodec2.h src/core/libraries/videodec/videodec2_avc.h + src/core/libraries/videodec/videodec.cpp + src/core/libraries/videodec/videodec.h + src/core/libraries/videodec/videodec_impl.cpp + src/core/libraries/videodec/videodec_impl.h ) set(NP_LIBS src/core/libraries/np_manager/np_manager.cpp diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp index a76c472a77..5ca594bf7b 100644 --- a/src/common/logging/filter.cpp +++ b/src/common/logging/filter.cpp @@ -120,6 +120,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) { SUB(Lib, SharePlay) \ SUB(Lib, Fiber) \ SUB(Lib, Vdec2) \ + SUB(Lib, Videodec) \ CLS(Frontend) \ CLS(Render) \ SUB(Render, Vulkan) \ diff --git a/src/common/logging/types.h b/src/common/logging/types.h index fd7dcfd1aa..2821729d40 100644 --- a/src/common/logging/types.h +++ b/src/common/logging/types.h @@ -87,6 +87,7 @@ enum class Class : u8 { Lib_SharePlay, ///< The LibSceSharePlay implemenation Lib_Fiber, ///< The LibSceFiber implementation. Lib_Vdec2, ///< The LibSceVideodec2 implementation. + Lib_Videodec, ///< The LibSceVideodec 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 66ad5f8b66..6bbdc30807 100644 --- a/src/core/libraries/error_codes.h +++ b/src/core/libraries/error_codes.h @@ -590,4 +590,29 @@ 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 +constexpr int ORBIS_VIDEODEC2_ERROR_FATAL_STREAM = 0x811D0304; + +// Videodec library + +constexpr int ORBIS_VIDEODEC_ERROR_API_FAIL = 0x80C10000; +constexpr int ORBIS_VIDEODEC_ERROR_CODEC_TYPE = 0x80C10001; +constexpr int ORBIS_VIDEODEC_ERROR_STRUCT_SIZE = 0x80C10002; +constexpr int ORBIS_VIDEODEC_ERROR_HANDLE = 0x80C10003; +constexpr int ORBIS_VIDEODEC_ERROR_CPU_MEMORY_SIZE = 0x80C10004; +constexpr int ORBIS_VIDEODEC_ERROR_CPU_MEMORY_POINTER = 0x80C10005; +constexpr int ORBIS_VIDEODEC_ERROR_CPU_GPU_MEMORY_SIZE = 0x80C10006; +constexpr int ORBIS_VIDEODEC_ERROR_CPU_GPU_MEMORY_POINTER = 0x80C10007; +constexpr int ORBIS_VIDEODEC_ERROR_SHADER_CONTEXT_POINTER = 0x80C10008; +constexpr int ORBIS_VIDEODEC_ERROR_AU_SIZE = 0x80C10009; +constexpr int ORBIS_VIDEODEC_ERROR_AU_POINTER = 0x80C1000A; +constexpr int ORBIS_VIDEODEC_ERROR_FRAME_BUFFER_SIZE = 0x80C1000B; +constexpr int ORBIS_VIDEODEC_ERROR_FRAME_BUFFER_POINTER = 0x80C1000C; +constexpr int ORBIS_VIDEODEC_ERROR_FRAME_BUFFER_ALIGNMENT = 0x80C1000D; +constexpr int ORBIS_VIDEODEC_ERROR_CONFIG_INFO = 0x80C1000E; +constexpr int ORBIS_VIDEODEC_ERROR_ARGUMENT_POINTER = 0x80C1000F; +constexpr int ORBIS_VIDEODEC_ERROR_NEW_SEQUENCE = 0x80C10010; +constexpr int ORBIS_VIDEODEC_ERROR_DECODE_AU = 0x80C10011; +constexpr int ORBIS_VIDEODEC_ERROR_MISMATCH_SPEC = 0x80C10012; +constexpr int ORBIS_VIDEODEC_ERROR_INVALID_SEQUENCE = 0x80C10013; +constexpr int ORBIS_VIDEODEC_ERROR_FATAL_STREAM = 0x80C10014; +constexpr int ORBIS_VIDEODEC_ERROR_FATAL_STATE = 0x80C10015; diff --git a/src/core/libraries/libs.cpp b/src/core/libraries/libs.cpp index b0365435b3..23d751622c 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/videodec.h" #include "core/libraries/videodec/videodec2.h" #include "core/libraries/videoout/video_out.h" @@ -87,6 +88,7 @@ void InitHLELibs(Core::Loader::SymbolsResolver* sym) { Libraries::GameLiveStreaming::RegisterlibSceGameLiveStreaming(sym); Libraries::SharePlay::RegisterlibSceSharePlay(sym); Libraries::Remoteplay::RegisterlibSceRemoteplay(sym); + Libraries::Videodec::RegisterlibSceVideodec(sym); } } // namespace Libraries diff --git a/src/core/libraries/videodec/videodec.cpp b/src/core/libraries/videodec/videodec.cpp new file mode 100644 index 0000000000..96ece3c5c3 --- /dev/null +++ b/src/core/libraries/videodec/videodec.cpp @@ -0,0 +1,137 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "videodec.h" + +#include "common/logging/log.h" +#include "core/libraries/error_codes.h" +#include "core/libraries/libs.h" +#include "videodec_impl.h" + +namespace Libraries::Videodec { + +static constexpr u64 kMinimumMemorySize = 32_MB; ///> Fake minimum memory size for querying + +int PS4_SYSV_ABI sceVideodecCreateDecoder(const OrbisVideodecConfigInfo* pCfgInfoIn, + const OrbisVideodecResourceInfo* pRsrcInfoIn, + OrbisVideodecCtrl* pCtrlOut) { + LOG_INFO(Lib_Videodec, "called"); + + if (!pCfgInfoIn || !pRsrcInfoIn || !pCtrlOut) { + return ORBIS_VIDEODEC_ERROR_ARGUMENT_POINTER; + } + if (pCfgInfoIn->thisSize != sizeof(OrbisVideodecConfigInfo) || + pRsrcInfoIn->thisSize != sizeof(OrbisVideodecResourceInfo)) { + return ORBIS_VIDEODEC_ERROR_STRUCT_SIZE; + } + + VdecDecoder* decoder = new VdecDecoder(*pCfgInfoIn, *pRsrcInfoIn); + pCtrlOut->thisSize = sizeof(OrbisVideodecCtrl); + pCtrlOut->handle = decoder; + pCtrlOut->version = 1; //??? + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceVideodecDecode(OrbisVideodecCtrl* pCtrlIn, + const OrbisVideodecInputData* pInputDataIn, + OrbisVideodecFrameBuffer* pFrameBufferInOut, + OrbisVideodecPictureInfo* pPictureInfoOut) { + LOG_INFO(Lib_Videodec, "called"); + if (!pCtrlIn || !pInputDataIn || !pPictureInfoOut) { + return ORBIS_VIDEODEC_ERROR_ARGUMENT_POINTER; + } + if (pCtrlIn->thisSize != sizeof(OrbisVideodecCtrl) || + pFrameBufferInOut->thisSize != sizeof(OrbisVideodecFrameBuffer)) { + return ORBIS_VIDEODEC_ERROR_STRUCT_SIZE; + } + + VdecDecoder* decoder = (VdecDecoder*)pCtrlIn->handle; + if (!decoder) { + return ORBIS_VIDEODEC_ERROR_HANDLE; + } + return decoder->Decode(*pInputDataIn, *pFrameBufferInOut, *pPictureInfoOut); +} + +int PS4_SYSV_ABI sceVideodecDeleteDecoder(OrbisVideodecCtrl* pCtrlIn) { + LOG_INFO(Lib_Videodec, "(STUBBED) called"); + + VdecDecoder* decoder = (VdecDecoder*)pCtrlIn->handle; + if (!decoder) { + return ORBIS_VIDEODEC_ERROR_HANDLE; + } + delete decoder; + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceVideodecFlush(OrbisVideodecCtrl* pCtrlIn, + OrbisVideodecFrameBuffer* pFrameBufferInOut, + OrbisVideodecPictureInfo* pPictureInfoOut) { + LOG_INFO(Lib_Videodec, "called"); + + if (!pFrameBufferInOut || !pPictureInfoOut) { + return ORBIS_VIDEODEC_ERROR_ARGUMENT_POINTER; + } + if (pFrameBufferInOut->thisSize != sizeof(OrbisVideodecFrameBuffer) || + pPictureInfoOut->thisSize != sizeof(OrbisVideodecPictureInfo)) { + return ORBIS_VIDEODEC_ERROR_STRUCT_SIZE; + } + + VdecDecoder* decoder = (VdecDecoder*)pCtrlIn->handle; + if (!decoder) { + return ORBIS_VIDEODEC_ERROR_HANDLE; + } + return decoder->Flush(*pFrameBufferInOut, *pPictureInfoOut); +} + +int PS4_SYSV_ABI sceVideodecMapMemory() { + LOG_ERROR(Lib_Videodec, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceVideodecQueryResourceInfo(const OrbisVideodecConfigInfo* pCfgInfoIn, + OrbisVideodecResourceInfo* pRsrcInfoOut) { + LOG_INFO(Lib_Videodec, "called"); + + if (!pCfgInfoIn || !pRsrcInfoOut) { + return ORBIS_VIDEODEC_ERROR_ARGUMENT_POINTER; + } + if (pCfgInfoIn->thisSize != sizeof(OrbisVideodecConfigInfo) || + pRsrcInfoOut->thisSize != sizeof(OrbisVideodecResourceInfo)) { + return ORBIS_VIDEODEC_ERROR_STRUCT_SIZE; + } + + pRsrcInfoOut->thisSize = sizeof(OrbisVideodecResourceInfo); + pRsrcInfoOut->pCpuMemory = nullptr; + pRsrcInfoOut->pCpuGpuMemory = nullptr; + + pRsrcInfoOut->cpuGpuMemorySize = kMinimumMemorySize; + pRsrcInfoOut->cpuMemorySize = kMinimumMemorySize; + + pRsrcInfoOut->maxFrameBufferSize = kMinimumMemorySize; + pRsrcInfoOut->frameBufferAlignment = 0x100; + + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceVideodecReset(OrbisVideodecCtrl* pCtrlIn) { + LOG_INFO(Lib_Videodec, "(STUBBED) called"); + + VdecDecoder* decoder = (VdecDecoder*)pCtrlIn->handle; + decoder->Reset(); + return ORBIS_OK; +} + +void RegisterlibSceVideodec(Core::Loader::SymbolsResolver* sym) { + LIB_FUNCTION("qkgRiwHyheU", "libSceVideodec", 1, "libSceVideodec", 1, 1, + sceVideodecCreateDecoder); + LIB_FUNCTION("q0W5GJMovMs", "libSceVideodec", 1, "libSceVideodec", 1, 1, sceVideodecDecode); + LIB_FUNCTION("U0kpGF1cl90", "libSceVideodec", 1, "libSceVideodec", 1, 1, + sceVideodecDeleteDecoder); + LIB_FUNCTION("jeigLlKdp5I", "libSceVideodec", 1, "libSceVideodec", 1, 1, sceVideodecFlush); + LIB_FUNCTION("kg+lH0V61hM", "libSceVideodec", 1, "libSceVideodec", 1, 1, sceVideodecMapMemory); + LIB_FUNCTION("leCAscipfFY", "libSceVideodec", 1, "libSceVideodec", 1, 1, + sceVideodecQueryResourceInfo); + LIB_FUNCTION("f8AgDv-1X8A", "libSceVideodec", 1, "libSceVideodec", 1, 1, sceVideodecReset); +}; + +} // namespace Libraries::Videodec \ No newline at end of file diff --git a/src/core/libraries/videodec/videodec.h b/src/core/libraries/videodec/videodec.h new file mode 100644 index 0000000000..45c5a69243 --- /dev/null +++ b/src/core/libraries/videodec/videodec.h @@ -0,0 +1,108 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/types.h" + +namespace Core::Loader { +class SymbolsResolver; +} + +namespace Libraries::Videodec { + +struct OrbisVideodecConfigInfo { + u64 thisSize; + u32 codecType; + u32 profile; + u32 maxLevel; + s32 maxFrameWidth; + s32 maxFrameHeight; + s32 maxDpbFrameCount; + u64 videodecFlags; +}; + +struct OrbisVideodecResourceInfo { + u64 thisSize; + u64 cpuMemorySize; + void* pCpuMemory; + u64 cpuGpuMemorySize; + void* pCpuGpuMemory; + u64 maxFrameBufferSize; + u32 frameBufferAlignment; +}; + +struct OrbisVideodecCtrl { + u64 thisSize; + void* handle; + u64 version; +}; + +struct OrbisVideodecFrameBuffer { + u64 thisSize; + void* pFrameBuffer; + u64 frameBufferSize; +}; + +struct OrbisVideodecAvcInfo { + u32 numUnitsInTick; + u32 timeScale; + u8 fixedFrameRateFlag; + u8 aspectRatioIdc; + u16 sarWidth; + u16 sarHeight; + u8 colourPrimaries; + u8 transferCharacteristics; + u8 matrixCoefficients; + u8 videoFullRangeFlag; + u32 frameCropLeftOffset; + u32 frameCropRightOffset; + u32 frameCropTopOffset; + u32 frameCropBottomOffset; +}; + +union OrbisVideodecCodecInfo { + u8 reserved[64]; + OrbisVideodecAvcInfo avc; +}; + +struct OrbisVideodecPictureInfo { + u64 thisSize; + u32 isValid; + u32 codecType; + u32 frameWidth; + u32 framePitch; + u32 frameHeight; + u32 isErrorPic; + u64 ptsData; + u64 attachedData; + OrbisVideodecCodecInfo codec; +}; + +struct OrbisVideodecInputData { + u64 thisSize; + void* pAuData; + u64 auSize; + u64 ptsData; + u64 dtsData; + u64 attachedData; +}; + +int PS4_SYSV_ABI sceVideodecCreateDecoder(const OrbisVideodecConfigInfo* pCfgInfoIn, + const OrbisVideodecResourceInfo* pRsrcInfoIn, + OrbisVideodecCtrl* pCtrlOut); +int PS4_SYSV_ABI sceVideodecDecode(OrbisVideodecCtrl* pCtrlIn, + const OrbisVideodecInputData* pInputDataIn, + OrbisVideodecFrameBuffer* pFrameBufferInOut, + OrbisVideodecPictureInfo* pPictureInfoOut); +int PS4_SYSV_ABI sceVideodecDeleteDecoder(OrbisVideodecCtrl* pCtrlIn); +int PS4_SYSV_ABI sceVideodecFlush(OrbisVideodecCtrl* pCtrlIn, + OrbisVideodecFrameBuffer* pFrameBufferInOut, + OrbisVideodecPictureInfo* pPictureInfoOut); +int PS4_SYSV_ABI sceVideodecMapMemory(); +int PS4_SYSV_ABI sceVideodecQueryResourceInfo(const OrbisVideodecConfigInfo* pCfgInfoIn, + OrbisVideodecResourceInfo* pRsrcInfoOut); +int PS4_SYSV_ABI sceVideodecReset(OrbisVideodecCtrl* pCtrlIn); + +void RegisterlibSceVideodec(Core::Loader::SymbolsResolver* sym); +} // namespace Libraries::Videodec \ No newline at end of file diff --git a/src/core/libraries/videodec/videodec_impl.cpp b/src/core/libraries/videodec/videodec_impl.cpp new file mode 100644 index 0000000000..a244c4a611 --- /dev/null +++ b/src/core/libraries/videodec/videodec_impl.cpp @@ -0,0 +1,222 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "videodec_impl.h" + +#include "common/alignment.h" +#include "common/assert.h" +#include "common/logging/log.h" +#include "core/libraries/error_codes.h" + +// The av_err2str macro in libavutil/error.h does not play nice with C++ +#ifdef av_err2str +#undef av_err2str +#include +av_always_inline std::string av_err2string(int errnum) { + char errbuf[AV_ERROR_MAX_STRING_SIZE]; + return av_make_error_string(errbuf, AV_ERROR_MAX_STRING_SIZE, errnum); +} +#define av_err2str(err) av_err2string(err).c_str() +#endif // av_err2str + +namespace Libraries::Videodec { + +static inline void CopyNV12Data(u8* dst, const AVFrame& src) { + u32 width = Common::AlignUp((u32)src.width, 16); + u32 height = Common::AlignUp((u32)src.height, 16); + std::memcpy(dst, src.data[0], src.width * src.height); + std::memcpy(dst + src.width * height, src.data[1], (src.width * src.height) / 2); +} + +VdecDecoder::VdecDecoder(const OrbisVideodecConfigInfo& pCfgInfoIn, + const OrbisVideodecResourceInfo& pRsrcInfoIn) { + + const AVCodec* codec = avcodec_find_decoder(AV_CODEC_ID_H264); + ASSERT(codec); + + mCodecContext = avcodec_alloc_context3(codec); + ASSERT(mCodecContext); + mCodecContext->width = pCfgInfoIn.maxFrameWidth; + mCodecContext->height = pCfgInfoIn.maxFrameHeight; + + avcodec_open2(mCodecContext, codec, nullptr); +} + +VdecDecoder::~VdecDecoder() { + avcodec_free_context(&mCodecContext); + sws_freeContext(mSwsContext); +} + +s32 VdecDecoder::Decode(const OrbisVideodecInputData& pInputDataIn, + OrbisVideodecFrameBuffer& pFrameBufferInOut, + OrbisVideodecPictureInfo& pPictureInfoOut) { + pPictureInfoOut.thisSize = sizeof(OrbisVideodecPictureInfo); + pPictureInfoOut.isValid = false; + pPictureInfoOut.isErrorPic = true; + + if (!pInputDataIn.pAuData) { + return ORBIS_VIDEODEC_ERROR_AU_POINTER; + } + if (pInputDataIn.auSize == 0) { + return ORBIS_VIDEODEC_ERROR_AU_SIZE; + } + + AVPacket* packet = av_packet_alloc(); + if (!packet) { + LOG_ERROR(Lib_Videodec, "Failed to allocate packet"); + return ORBIS_VIDEODEC_ERROR_API_FAIL; + } + + packet->data = (u8*)pInputDataIn.pAuData; + packet->size = pInputDataIn.auSize; + packet->pts = pInputDataIn.ptsData; + packet->dts = pInputDataIn.dtsData; + + int ret = avcodec_send_packet(mCodecContext, packet); + if (ret < 0) { + LOG_ERROR(Lib_Videodec, "Error sending packet to decoder: {}", ret); + av_packet_free(&packet); + return ORBIS_VIDEODEC_ERROR_API_FAIL; + } + + AVFrame* frame = av_frame_alloc(); + if (frame == nullptr) { + LOG_ERROR(Lib_Videodec, "Failed to allocate frame"); + av_packet_free(&packet); + return ORBIS_VIDEODEC_ERROR_API_FAIL; + } + int frameCount = 0; + while (true) { + ret = avcodec_receive_frame(mCodecContext, frame); + if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { + break; + } else if (ret < 0) { + LOG_ERROR(Lib_Videodec, "Error receiving frame from decoder: {}", ret); + av_packet_free(&packet); + av_frame_free(&frame); + return ORBIS_VIDEODEC_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*)pFrameBufferInOut.pFrameBuffer, *frame); + + pPictureInfoOut.codecType = 0; + pPictureInfoOut.frameWidth = Common::AlignUp((u32)frame->width, 16); + pPictureInfoOut.frameHeight = Common::AlignUp((u32)frame->height, 16); + pPictureInfoOut.framePitch = frame->linesize[0]; + + pPictureInfoOut.isValid = true; + pPictureInfoOut.isErrorPic = false; + frameCount++; + if (frameCount > 1) { + LOG_WARNING(Lib_Videodec, "We have more than 1 frame"); + } + } + + av_packet_free(&packet); + av_frame_free(&frame); + return ORBIS_OK; +} + +s32 VdecDecoder::Flush(OrbisVideodecFrameBuffer& pFrameBufferInOut, + OrbisVideodecPictureInfo& pPictureInfoOut) { + pPictureInfoOut.thisSize = sizeof(pPictureInfoOut); + pPictureInfoOut.isValid = false; + pPictureInfoOut.isErrorPic = true; + + AVFrame* frame = av_frame_alloc(); + if (!frame) { + LOG_ERROR(Lib_Videodec, "Failed to allocate frame"); + return ORBIS_VIDEODEC_ERROR_API_FAIL; + } + + int frameCount = 0; + while (true) { + int ret = avcodec_receive_frame(mCodecContext, frame); + if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { + break; + } else if (ret < 0) { + LOG_ERROR(Lib_Videodec, "Error receiving frame from decoder: {}", ret); + av_frame_free(&frame); + return ORBIS_VIDEODEC_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*)pFrameBufferInOut.pFrameBuffer, *frame); + + pPictureInfoOut.codecType = 0; + pPictureInfoOut.frameWidth = Common::AlignUp((u32)frame->width, 16); + pPictureInfoOut.frameHeight = Common::AlignUp((u32)frame->height, 16); + pPictureInfoOut.framePitch = frame->linesize[0]; + + pPictureInfoOut.isValid = true; + pPictureInfoOut.isErrorPic = false; + + u32 width = Common::AlignUp((u32)frame->width, 16); + u32 height = Common::AlignUp((u32)frame->height, 16); + pPictureInfoOut.codec.avc.frameCropLeftOffset = u32(frame->crop_left); + pPictureInfoOut.codec.avc.frameCropRightOffset = + u32(frame->crop_right + (width - frame->width)); + pPictureInfoOut.codec.avc.frameCropTopOffset = u32(frame->crop_top); + pPictureInfoOut.codec.avc.frameCropBottomOffset = + u32(frame->crop_bottom + (height - frame->height)); + // TODO maybe more avc? + + if (frameCount > 1) { + LOG_WARNING(Lib_Videodec, "We have more than 1 frame"); + } + } + + av_frame_free(&frame); + return ORBIS_OK; +} + +s32 VdecDecoder::Reset() { + avcodec_flush_buffers(mCodecContext); + 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_Videodec, "Could not convert to NV12: {}", av_err2str(res)); + return nullptr; + } + + return nv12_frame; +} + +} // namespace Libraries::Videodec diff --git a/src/core/libraries/videodec/videodec_impl.h b/src/core/libraries/videodec/videodec_impl.h new file mode 100644 index 0000000000..3d48db608c --- /dev/null +++ b/src/core/libraries/videodec/videodec_impl.h @@ -0,0 +1,38 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +#include "videodec.h" + +extern "C" { +#include +#include +#include +} + +namespace Libraries::Videodec { + +class VdecDecoder { +public: + VdecDecoder(const OrbisVideodecConfigInfo& pCfgInfoIn, + const OrbisVideodecResourceInfo& pRsrcInfoIn); + ~VdecDecoder(); + s32 Decode(const OrbisVideodecInputData& pInputDataIn, + OrbisVideodecFrameBuffer& pFrameBufferInOut, + OrbisVideodecPictureInfo& pPictureInfoOut); + s32 Flush(OrbisVideodecFrameBuffer& pFrameBufferInOut, + OrbisVideodecPictureInfo& pPictureInfoOut); + s32 Reset(); + +private: + AVFrame* ConvertNV12Frame(AVFrame& frame); + +private: + AVCodecContext* mCodecContext = nullptr; + SwsContext* mSwsContext = nullptr; +}; + +} // namespace Libraries::Videodec \ No newline at end of file