vdec1 implementation

This commit is contained in:
georgemoralis 2024-11-05 07:05:34 +02:00
parent be69f7a004
commit 5be1e27911
7 changed files with 346 additions and 10 deletions

View file

@ -368,6 +368,8 @@ set(VDEC_LIB src/core/libraries/videodec/videodec2_impl.cpp
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

View file

@ -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;
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;

View file

@ -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

View file

@ -6,13 +6,29 @@
#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_ERROR(Lib_Videodec, "(STUBBED) called");
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;
}
@ -20,20 +36,48 @@ int PS4_SYSV_ABI sceVideodecDecode(OrbisVideodecCtrl* pCtrlIn,
const OrbisVideodecInputData* pInputDataIn,
OrbisVideodecFrameBuffer* pFrameBufferInOut,
OrbisVideodecPictureInfo* pPictureInfoOut) {
LOG_ERROR(Lib_Videodec, "(STUBBED) called");
return ORBIS_OK;
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_ERROR(Lib_Videodec, "(STUBBED) called");
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_ERROR(Lib_Videodec, "(STUBBED) called");
return ORBIS_OK;
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() {
@ -43,12 +87,33 @@ int PS4_SYSV_ABI sceVideodecMapMemory() {
int PS4_SYSV_ABI sceVideodecQueryResourceInfo(const OrbisVideodecConfigInfo* pCfgInfoIn,
OrbisVideodecResourceInfo* pRsrcInfoOut) {
LOG_ERROR(Lib_Videodec, "(STUBBED) called");
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_ERROR(Lib_Videodec, "(STUBBED) called");
LOG_INFO(Lib_Videodec, "(STUBBED) called");
VdecDecoder* decoder = (VdecDecoder*)pCtrlIn->handle;
decoder->Reset();
return ORBIS_OK;
}

View file

@ -34,7 +34,7 @@ struct OrbisVideodecResourceInfo {
struct OrbisVideodecCtrl {
u64 thisSize;
u64 handle;
void* handle;
u64 version;
};

View file

@ -0,0 +1,201 @@
// 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 <string>
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) {
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 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;
}
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 = 1;
pPictureInfoOut.frameWidth = frame->width;
pPictureInfoOut.frameHeight = frame->height;
pPictureInfoOut.framePitch = frame->linesize[0];
pPictureInfoOut.isValid = true;
pPictureInfoOut.isErrorPic = false;
}
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;
}
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 = 1;
pPictureInfoOut.frameWidth = frame->width;
pPictureInfoOut.frameHeight = frame->height;
pPictureInfoOut.framePitch = frame->linesize[0];
pPictureInfoOut.isValid = true;
pPictureInfoOut.isErrorPic = false;
}
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

View file

@ -0,0 +1,41 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <vector>
#include "videodec.h"
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
}
namespace Libraries::Videodec {
#if 0
extern std::vector<OrbisVideodec2AvcPictureInfo> gPictureInfos;
#endif
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::Vdec2