Skip to content

Commit 992caf6

Browse files
committed
vdec1 implementation
1 parent 8744dac commit 992caf6

File tree

7 files changed

+346
-10
lines changed

7 files changed

+346
-10
lines changed

CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,8 @@ set(VDEC_LIB src/core/libraries/videodec/videodec2_impl.cpp
355355
src/core/libraries/videodec/videodec2_avc.h
356356
src/core/libraries/videodec/videodec.cpp
357357
src/core/libraries/videodec/videodec.h
358+
src/core/libraries/videodec/videodec_impl.cpp
359+
src/core/libraries/videodec/videodec_impl.h
358360
)
359361

360362
set(NP_LIBS src/core/libraries/np_manager/np_manager.cpp

src/core/libraries/error_codes.h

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -590,4 +590,29 @@ constexpr int ORBIS_VIDEODEC2_ERROR_NEW_SEQUENCE = 0x811D0300;
590590
constexpr int ORBIS_VIDEODEC2_ERROR_ACCESS_UNIT = 0x811D0301;
591591
constexpr int ORBIS_VIDEODEC2_ERROR_OVERSIZE_DECODE = 0x811D0302;
592592
constexpr int ORBIS_VIDEODEC2_ERROR_INVALID_SEQUENCE = 0x811D0303;
593-
constexpr int ORBIS_VIDEODEC2_ERROR_FATAL_STREAM = 0x811D0304;
593+
constexpr int ORBIS_VIDEODEC2_ERROR_FATAL_STREAM = 0x811D0304;
594+
595+
// Videodec library
596+
597+
constexpr int ORBIS_VIDEODEC_ERROR_API_FAIL = 0x80C10000;
598+
constexpr int ORBIS_VIDEODEC_ERROR_CODEC_TYPE = 0x80C10001;
599+
constexpr int ORBIS_VIDEODEC_ERROR_STRUCT_SIZE = 0x80C10002;
600+
constexpr int ORBIS_VIDEODEC_ERROR_HANDLE = 0x80C10003;
601+
constexpr int ORBIS_VIDEODEC_ERROR_CPU_MEMORY_SIZE = 0x80C10004;
602+
constexpr int ORBIS_VIDEODEC_ERROR_CPU_MEMORY_POINTER = 0x80C10005;
603+
constexpr int ORBIS_VIDEODEC_ERROR_CPU_GPU_MEMORY_SIZE = 0x80C10006;
604+
constexpr int ORBIS_VIDEODEC_ERROR_CPU_GPU_MEMORY_POINTER = 0x80C10007;
605+
constexpr int ORBIS_VIDEODEC_ERROR_SHADER_CONTEXT_POINTER = 0x80C10008;
606+
constexpr int ORBIS_VIDEODEC_ERROR_AU_SIZE = 0x80C10009;
607+
constexpr int ORBIS_VIDEODEC_ERROR_AU_POINTER = 0x80C1000A;
608+
constexpr int ORBIS_VIDEODEC_ERROR_FRAME_BUFFER_SIZE = 0x80C1000B;
609+
constexpr int ORBIS_VIDEODEC_ERROR_FRAME_BUFFER_POINTER = 0x80C1000C;
610+
constexpr int ORBIS_VIDEODEC_ERROR_FRAME_BUFFER_ALIGNMENT = 0x80C1000D;
611+
constexpr int ORBIS_VIDEODEC_ERROR_CONFIG_INFO = 0x80C1000E;
612+
constexpr int ORBIS_VIDEODEC_ERROR_ARGUMENT_POINTER = 0x80C1000F;
613+
constexpr int ORBIS_VIDEODEC_ERROR_NEW_SEQUENCE = 0x80C10010;
614+
constexpr int ORBIS_VIDEODEC_ERROR_DECODE_AU = 0x80C10011;
615+
constexpr int ORBIS_VIDEODEC_ERROR_MISMATCH_SPEC = 0x80C10012;
616+
constexpr int ORBIS_VIDEODEC_ERROR_INVALID_SEQUENCE = 0x80C10013;
617+
constexpr int ORBIS_VIDEODEC_ERROR_FATAL_STREAM = 0x80C10014;
618+
constexpr int ORBIS_VIDEODEC_ERROR_FATAL_STATE = 0x80C10015;

src/core/libraries/libs.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
#include "core/libraries/system/systemservice.h"
4242
#include "core/libraries/system/userservice.h"
4343
#include "core/libraries/usbd/usbd.h"
44+
#include "core/libraries/videodec/videodec.h"
4445
#include "core/libraries/videodec/videodec2.h"
4546
#include "core/libraries/videoout/video_out.h"
4647

@@ -87,6 +88,7 @@ void InitHLELibs(Core::Loader::SymbolsResolver* sym) {
8788
Libraries::GameLiveStreaming::RegisterlibSceGameLiveStreaming(sym);
8889
Libraries::SharePlay::RegisterlibSceSharePlay(sym);
8990
Libraries::Remoteplay::RegisterlibSceRemoteplay(sym);
91+
Libraries::Videodec::RegisterlibSceVideodec(sym);
9092
}
9193

9294
} // namespace Libraries

src/core/libraries/videodec/videodec.cpp

Lines changed: 73 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,34 +6,78 @@
66
#include "common/logging/log.h"
77
#include "core/libraries/error_codes.h"
88
#include "core/libraries/libs.h"
9+
#include "videodec_impl.h"
910

1011
namespace Libraries::Videodec {
1112

13+
static constexpr u64 kMinimumMemorySize = 32_MB; ///> Fake minimum memory size for querying
14+
1215
int PS4_SYSV_ABI sceVideodecCreateDecoder(const OrbisVideodecConfigInfo* pCfgInfoIn,
1316
const OrbisVideodecResourceInfo* pRsrcInfoIn,
1417
OrbisVideodecCtrl* pCtrlOut) {
15-
LOG_ERROR(Lib_Videodec, "(STUBBED) called");
18+
LOG_INFO(Lib_Videodec, "called");
19+
20+
if (!pCfgInfoIn || !pRsrcInfoIn || !pCtrlOut) {
21+
return ORBIS_VIDEODEC_ERROR_ARGUMENT_POINTER;
22+
}
23+
if (pCfgInfoIn->thisSize != sizeof(OrbisVideodecConfigInfo) ||
24+
pRsrcInfoIn->thisSize != sizeof(OrbisVideodecResourceInfo)) {
25+
return ORBIS_VIDEODEC_ERROR_STRUCT_SIZE;
26+
}
27+
28+
VdecDecoder* decoder = new VdecDecoder(*pCfgInfoIn, *pRsrcInfoIn);
29+
pCtrlOut->thisSize = sizeof(OrbisVideodecCtrl);
30+
pCtrlOut->handle = decoder;
31+
pCtrlOut->version = 1; //???
1632
return ORBIS_OK;
1733
}
1834

1935
int PS4_SYSV_ABI sceVideodecDecode(OrbisVideodecCtrl* pCtrlIn,
2036
const OrbisVideodecInputData* pInputDataIn,
2137
OrbisVideodecFrameBuffer* pFrameBufferInOut,
2238
OrbisVideodecPictureInfo* pPictureInfoOut) {
23-
LOG_ERROR(Lib_Videodec, "(STUBBED) called");
24-
return ORBIS_OK;
39+
LOG_INFO(Lib_Videodec, "called");
40+
if (!pCtrlIn || !pInputDataIn || !pPictureInfoOut) {
41+
return ORBIS_VIDEODEC_ERROR_ARGUMENT_POINTER;
42+
}
43+
if (pCtrlIn->thisSize != sizeof(OrbisVideodecCtrl) ||
44+
pFrameBufferInOut->thisSize != sizeof(OrbisVideodecFrameBuffer)) {
45+
return ORBIS_VIDEODEC_ERROR_STRUCT_SIZE;
46+
}
47+
VdecDecoder* decoder = (VdecDecoder*)pCtrlIn->handle;
48+
if (!decoder) {
49+
return ORBIS_VIDEODEC_ERROR_HANDLE;
50+
}
51+
return decoder->Decode(*pInputDataIn, *pFrameBufferInOut, *pPictureInfoOut);
2552
}
2653

2754
int PS4_SYSV_ABI sceVideodecDeleteDecoder(OrbisVideodecCtrl* pCtrlIn) {
28-
LOG_ERROR(Lib_Videodec, "(STUBBED) called");
55+
LOG_INFO(Lib_Videodec, "(STUBBED) called");
56+
VdecDecoder* decoder = (VdecDecoder*)pCtrlIn->handle;
57+
if (!decoder) {
58+
return ORBIS_VIDEODEC_ERROR_HANDLE;
59+
}
60+
delete decoder;
2961
return ORBIS_OK;
3062
}
3163

3264
int PS4_SYSV_ABI sceVideodecFlush(OrbisVideodecCtrl* pCtrlIn,
3365
OrbisVideodecFrameBuffer* pFrameBufferInOut,
3466
OrbisVideodecPictureInfo* pPictureInfoOut) {
35-
LOG_ERROR(Lib_Videodec, "(STUBBED) called");
36-
return ORBIS_OK;
67+
LOG_INFO(Lib_Videodec, "called");
68+
69+
if (!pFrameBufferInOut || !pPictureInfoOut) {
70+
return ORBIS_VIDEODEC_ERROR_ARGUMENT_POINTER;
71+
}
72+
if (pFrameBufferInOut->thisSize != sizeof(OrbisVideodecFrameBuffer) ||
73+
pPictureInfoOut->thisSize != sizeof(OrbisVideodecPictureInfo)) {
74+
return ORBIS_VIDEODEC_ERROR_STRUCT_SIZE;
75+
}
76+
VdecDecoder* decoder = (VdecDecoder*)pCtrlIn->handle;
77+
if (!decoder) {
78+
return ORBIS_VIDEODEC_ERROR_HANDLE;
79+
}
80+
return decoder->Flush(*pFrameBufferInOut, *pPictureInfoOut);
3781
}
3882

3983
int PS4_SYSV_ABI sceVideodecMapMemory() {
@@ -43,12 +87,33 @@ int PS4_SYSV_ABI sceVideodecMapMemory() {
4387

4488
int PS4_SYSV_ABI sceVideodecQueryResourceInfo(const OrbisVideodecConfigInfo* pCfgInfoIn,
4589
OrbisVideodecResourceInfo* pRsrcInfoOut) {
46-
LOG_ERROR(Lib_Videodec, "(STUBBED) called");
90+
LOG_INFO(Lib_Videodec, "called");
91+
92+
if (!pCfgInfoIn || !pRsrcInfoOut) {
93+
return ORBIS_VIDEODEC_ERROR_ARGUMENT_POINTER;
94+
}
95+
if (pCfgInfoIn->thisSize != sizeof(OrbisVideodecConfigInfo) ||
96+
pRsrcInfoOut->thisSize != sizeof(OrbisVideodecResourceInfo)) {
97+
return ORBIS_VIDEODEC_ERROR_STRUCT_SIZE;
98+
}
99+
100+
pRsrcInfoOut->thisSize = sizeof(OrbisVideodecResourceInfo);
101+
pRsrcInfoOut->pCpuMemory = nullptr;
102+
pRsrcInfoOut->pCpuGpuMemory = nullptr;
103+
104+
pRsrcInfoOut->cpuGpuMemorySize = kMinimumMemorySize;
105+
pRsrcInfoOut->cpuMemorySize = kMinimumMemorySize;
106+
107+
pRsrcInfoOut->maxFrameBufferSize = kMinimumMemorySize;
108+
pRsrcInfoOut->frameBufferAlignment = 0x100;
109+
47110
return ORBIS_OK;
48111
}
49112

50113
int PS4_SYSV_ABI sceVideodecReset(OrbisVideodecCtrl* pCtrlIn) {
51-
LOG_ERROR(Lib_Videodec, "(STUBBED) called");
114+
LOG_INFO(Lib_Videodec, "(STUBBED) called");
115+
VdecDecoder* decoder = (VdecDecoder*)pCtrlIn->handle;
116+
decoder->Reset();
52117
return ORBIS_OK;
53118
}
54119

src/core/libraries/videodec/videodec.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ struct OrbisVideodecResourceInfo {
3434

3535
struct OrbisVideodecCtrl {
3636
u64 thisSize;
37-
u64 handle;
37+
void* handle;
3838
u64 version;
3939
};
4040

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
2+
// SPDX-License-Identifier: GPL-2.0-or-later
3+
4+
#include "videodec_impl.h"
5+
6+
#include "common/alignment.h"
7+
#include "common/assert.h"
8+
#include "common/logging/log.h"
9+
#include "core/libraries/error_codes.h"
10+
11+
// The av_err2str macro in libavutil/error.h does not play nice with C++
12+
#ifdef av_err2str
13+
#undef av_err2str
14+
#include <string>
15+
av_always_inline std::string av_err2string(int errnum) {
16+
char errbuf[AV_ERROR_MAX_STRING_SIZE];
17+
return av_make_error_string(errbuf, AV_ERROR_MAX_STRING_SIZE, errnum);
18+
}
19+
#define av_err2str(err) av_err2string(err).c_str()
20+
#endif // av_err2str
21+
22+
namespace Libraries::Videodec {
23+
24+
static inline void CopyNV12Data(u8* dst, const AVFrame& src) {
25+
std::memcpy(dst, src.data[0], src.width * src.height);
26+
std::memcpy(dst + (src.width * src.height), src.data[1], (src.width * src.height) / 2);
27+
}
28+
29+
VdecDecoder::VdecDecoder(const OrbisVideodecConfigInfo& pCfgInfoIn,
30+
const OrbisVideodecResourceInfo& pRsrcInfoIn) {
31+
32+
const AVCodec* codec = avcodec_find_decoder(AV_CODEC_ID_H264);
33+
ASSERT(codec);
34+
35+
mCodecContext = avcodec_alloc_context3(codec);
36+
ASSERT(mCodecContext);
37+
mCodecContext->width = pCfgInfoIn.maxFrameWidth;
38+
mCodecContext->height = pCfgInfoIn.maxFrameHeight;
39+
40+
avcodec_open2(mCodecContext, codec, nullptr);
41+
}
42+
43+
VdecDecoder::~VdecDecoder() {
44+
avcodec_free_context(&mCodecContext);
45+
sws_freeContext(mSwsContext);
46+
}
47+
48+
s32 VdecDecoder::Decode(const OrbisVideodecInputData& pInputDataIn,
49+
OrbisVideodecFrameBuffer& pFrameBufferInOut,
50+
OrbisVideodecPictureInfo& pPictureInfoOut) {
51+
pPictureInfoOut.thisSize = sizeof(OrbisVideodecPictureInfo);
52+
pPictureInfoOut.isValid = false;
53+
pPictureInfoOut.isErrorPic = true;
54+
55+
if (!pInputDataIn.pAuData) {
56+
return ORBIS_VIDEODEC_ERROR_AU_POINTER;
57+
}
58+
if (pInputDataIn.auSize == 0) {
59+
return ORBIS_VIDEODEC_ERROR_AU_SIZE;
60+
}
61+
62+
AVPacket* packet = av_packet_alloc();
63+
if (!packet) {
64+
LOG_ERROR(Lib_Videodec, "Failed to allocate packet");
65+
return ORBIS_VIDEODEC_ERROR_API_FAIL;
66+
}
67+
68+
packet->data = (u8*)pInputDataIn.pAuData;
69+
packet->size = pInputDataIn.auSize;
70+
packet->pts = pInputDataIn.ptsData;
71+
packet->dts = pInputDataIn.dtsData;
72+
73+
int ret = avcodec_send_packet(mCodecContext, packet);
74+
if (ret < 0) {
75+
LOG_ERROR(Lib_Videodec, "Error sending packet to decoder: {}", ret);
76+
av_packet_free(&packet);
77+
return ORBIS_VIDEODEC_ERROR_API_FAIL;
78+
}
79+
80+
AVFrame* frame = av_frame_alloc();
81+
if (frame == nullptr) {
82+
LOG_ERROR(Lib_Videodec, "Failed to allocate frame");
83+
av_packet_free(&packet);
84+
return ORBIS_VIDEODEC_ERROR_API_FAIL;
85+
}
86+
87+
while (true) {
88+
ret = avcodec_receive_frame(mCodecContext, frame);
89+
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
90+
break;
91+
} else if (ret < 0) {
92+
LOG_ERROR(Lib_Videodec, "Error receiving frame from decoder: {}", ret);
93+
av_packet_free(&packet);
94+
av_frame_free(&frame);
95+
return ORBIS_VIDEODEC_ERROR_API_FAIL;
96+
}
97+
98+
if (frame->format != AV_PIX_FMT_NV12) {
99+
AVFrame* nv12_frame = ConvertNV12Frame(*frame);
100+
ASSERT(nv12_frame);
101+
av_frame_free(&frame);
102+
frame = nv12_frame;
103+
}
104+
105+
CopyNV12Data((u8*)pFrameBufferInOut.pFrameBuffer, *frame);
106+
107+
pPictureInfoOut.codecType = 1;
108+
pPictureInfoOut.frameWidth = frame->width;
109+
pPictureInfoOut.frameHeight = frame->height;
110+
pPictureInfoOut.framePitch = frame->linesize[0];
111+
112+
pPictureInfoOut.isValid = true;
113+
pPictureInfoOut.isErrorPic = false;
114+
}
115+
116+
av_packet_free(&packet);
117+
av_frame_free(&frame);
118+
return ORBIS_OK;
119+
}
120+
121+
s32 VdecDecoder::Flush(OrbisVideodecFrameBuffer& pFrameBufferInOut,
122+
OrbisVideodecPictureInfo& pPictureInfoOut) {
123+
pPictureInfoOut.thisSize = sizeof(pPictureInfoOut);
124+
pPictureInfoOut.isValid = false;
125+
pPictureInfoOut.isErrorPic = true;
126+
127+
AVFrame* frame = av_frame_alloc();
128+
if (!frame) {
129+
LOG_ERROR(Lib_Videodec, "Failed to allocate frame");
130+
return ORBIS_VIDEODEC_ERROR_API_FAIL;
131+
}
132+
133+
while (true) {
134+
int ret = avcodec_receive_frame(mCodecContext, frame);
135+
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
136+
break;
137+
} else if (ret < 0) {
138+
LOG_ERROR(Lib_Videodec, "Error receiving frame from decoder: {}", ret);
139+
av_frame_free(&frame);
140+
return ORBIS_VIDEODEC_ERROR_API_FAIL;
141+
}
142+
143+
if (frame->format != AV_PIX_FMT_NV12) {
144+
AVFrame* nv12_frame = ConvertNV12Frame(*frame);
145+
ASSERT(nv12_frame);
146+
av_frame_free(&frame);
147+
frame = nv12_frame;
148+
}
149+
150+
CopyNV12Data((u8*)pFrameBufferInOut.pFrameBuffer, *frame);
151+
152+
pPictureInfoOut.codecType = 1;
153+
pPictureInfoOut.frameWidth = frame->width;
154+
pPictureInfoOut.frameHeight = frame->height;
155+
pPictureInfoOut.framePitch = frame->linesize[0];
156+
157+
pPictureInfoOut.isValid = true;
158+
pPictureInfoOut.isErrorPic = false;
159+
}
160+
161+
av_frame_free(&frame);
162+
return ORBIS_OK;
163+
}
164+
165+
s32 VdecDecoder::Reset() {
166+
avcodec_flush_buffers(mCodecContext);
167+
return ORBIS_OK;
168+
}
169+
170+
AVFrame* VdecDecoder::ConvertNV12Frame(AVFrame& frame) {
171+
AVFrame* nv12_frame = av_frame_alloc();
172+
nv12_frame->pts = frame.pts;
173+
nv12_frame->pkt_dts = frame.pkt_dts < 0 ? 0 : frame.pkt_dts;
174+
nv12_frame->format = AV_PIX_FMT_NV12;
175+
nv12_frame->width = frame.width;
176+
nv12_frame->height = frame.height;
177+
nv12_frame->sample_aspect_ratio = frame.sample_aspect_ratio;
178+
nv12_frame->crop_top = frame.crop_top;
179+
nv12_frame->crop_bottom = frame.crop_bottom;
180+
nv12_frame->crop_left = frame.crop_left;
181+
nv12_frame->crop_right = frame.crop_right;
182+
183+
av_frame_get_buffer(nv12_frame, 0);
184+
185+
if (mSwsContext == nullptr) {
186+
mSwsContext = sws_getContext(frame.width, frame.height, AVPixelFormat(frame.format),
187+
nv12_frame->width, nv12_frame->height, AV_PIX_FMT_NV12,
188+
SWS_FAST_BILINEAR, nullptr, nullptr, nullptr);
189+
}
190+
191+
const auto res = sws_scale(mSwsContext, frame.data, frame.linesize, 0, frame.height,
192+
nv12_frame->data, nv12_frame->linesize);
193+
if (res < 0) {
194+
LOG_ERROR(Lib_Videodec, "Could not convert to NV12: {}", av_err2str(res));
195+
return nullptr;
196+
}
197+
198+
return nv12_frame;
199+
}
200+
201+
} // namespace Libraries::Videodec

0 commit comments

Comments
 (0)