Skip to content

Commit 8ca109d

Browse files
Implemented mvdstdCalculateBufferSize() (#569)
1 parent 8d04877 commit 8ca109d

2 files changed

Lines changed: 113 additions & 2 deletions

File tree

  • libctru

libctru/include/3ds/services/mvd.h

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,29 @@
2020
/// Default input size for mvdstdInit(). This is what the New3DS Internet Browser uses, from the MVDSTD:CalculateWorkBufSize output.
2121
#define MVD_DEFAULT_WORKBUF_SIZE 0x9006C8
2222

23+
#define MVD_CALC_WITH_LEVEL_FLAG_NONE 0x00 //Nothing.
24+
#define MVD_CALC_WITH_LEVEL_FLAG_ENABLE_CALC 0x01 //Enable calculation with level.
25+
#define MVD_CALC_WITH_LEVEL_FLAG_ENABLE_EXTRA_OP 0x02 //Enable extra op after base calculation (see : https://www.3dbrew.org/wiki/MVDSTD:CalculateWorkBufSize.
26+
#define MVD_CALC_WITH_LEVEL_FLAG_UNK 0x04 //Unknown.
27+
28+
#define MVD_H264_LEVEL_1_0 0x00 //H.264 level 1.0.
29+
#define MVD_H264_LEVEL_1_0B 0x01 //H.264 level 1.0b.
30+
#define MVD_H264_LEVEL_1_1 0x02 //H.264 level 1.1.
31+
#define MVD_H264_LEVEL_1_2 0x03 //H.264 level 1.2.
32+
#define MVD_H264_LEVEL_1_3 0x04 //H.264 level 1.3.
33+
#define MVD_H264_LEVEL_2_0 0x05 //H.264 level 2.0.
34+
#define MVD_H264_LEVEL_2_1 0x06 //H.264 level 2.1.
35+
#define MVD_H264_LEVEL_2_2 0x07 //H.264 level 2.2.
36+
#define MVD_H264_LEVEL_3_0 0x08 //H.264 level 3.0.
37+
#define MVD_H264_LEVEL_3_1 0x09 //H.264 level 3.1.
38+
#define MVD_H264_LEVEL_3_2 0x0A //H.264 level 3.2.
39+
#define MVD_H264_LEVEL_4_0 0x0B //H.264 level 4.0.
40+
#define MVD_H264_LEVEL_4_1 0x0C //H.264 level 4.1.
41+
#define MVD_H264_LEVEL_4_2 0x0D //H.264 level 4.2.
42+
#define MVD_H264_LEVEL_5_0 0x0E //H.264 level 5.0.
43+
#define MVD_H264_LEVEL_5_1 0x0F //H.264 level 5.1.
44+
#define MVD_H264_LEVEL_5_2 0x10 //H.264 level 5.2.
45+
2346
/// Processing mode.
2447
typedef enum {
2548
MVDMODE_COLORFORMATCONV, ///< Converting color formats.
@@ -96,6 +119,39 @@ typedef struct {
96119
u8 cmd1b_inval;
97120
} MVDSTD_InitStruct;
98121

122+
typedef struct
123+
{
124+
u8 enable; //Whether use this calculation method.
125+
u8 flag; //Flag for calculation (MVD_CALC_WITH_LEVEL_FLAG_XXXXXX).
126+
u8 double_size; //If set, size calculation result is doubled.
127+
u8 level; //H.264 (AVC) level (MVD_H264_LEVEL_1_0 ~ MVD_H264_LEVEL_5_2).
128+
} MVDSTD_WithLevel;
129+
130+
typedef struct
131+
{
132+
u8 enable; //Whether use this calculation method.
133+
u8 ref_frames; //Number of reference frames.
134+
} MVDSTD_WithNumOfRefFrames;
135+
136+
/// H.264 buffer calculation configuration.
137+
/// See here for detailed explanations : https://www.3dbrew.org/wiki/MVDSTD:CalculateWorkBufSize.
138+
typedef struct {
139+
u8 unused_0x00; //Unused.
140+
MVDSTD_WithLevel level; //Calc buffer size with H.264 level.
141+
MVDSTD_WithNumOfRefFrames ref_frames_a; //Calc buffer size with num of reference frames and resolution.
142+
MVDSTD_WithNumOfRefFrames ref_frames_b; //Calc buffer size with num of reference frames and resolution.
143+
u8 unused_0x09[3]; //Unused.
144+
u32 unk_0x0c; //Unknown.
145+
u32 unk_0x10; //Unknown.
146+
u32 unk_0x14; //Unknown.
147+
u32 unk_0x18; //Unknown.
148+
u32 unk_0x1c; //Unknown.
149+
u32 unk_0x20; //Unknown.
150+
u32 unk_0x24; //Unknown.
151+
u32 width; //Video width.
152+
u32 height; //Video height.
153+
} MVDSTD_CalculateWorkBufSizeConfig;
154+
99155
/**
100156
* @brief Initializes MVDSTD.
101157
* @param mode Mode to initialize MVDSTD to.
@@ -109,6 +165,13 @@ Result mvdstdInit(MVDSTD_Mode mode, MVDSTD_InputFormat input_type, MVDSTD_Output
109165
/// Shuts down MVDSTD.
110166
void mvdstdExit(void);
111167

168+
/**
169+
* @brief Calculate working buffer size for H.264 decoding.
170+
* @param config Calculation config, config->level.level must NOT exceed MVD_H264_LEVEL_5_2. See here for more explanations : https://www.3dbrew.org/wiki/MVDSTD:CalculateWorkBufSize.
171+
* @param size_out Calculated buffer size in bytes.
172+
*/
173+
Result mvdstdCalculateBufferSize(const MVDSTD_CalculateWorkBufSizeConfig* config, u32* size_out);
174+
112175
/**
113176
* @brief Generates a default MVDSTD configuration.
114177
* @param config Pointer to output the generated config to.

libctru/source/services/mvd.c

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@
1414
#include <3ds/services/mvd.h>
1515
#include <3ds/ipc.h>
1616

17-
Handle mvdstdHandle;
17+
#define CALC_BUF_SIZE_SAFETY_MARGIN (u16)(4096)
18+
19+
Handle mvdstdHandle = 0;
1820
static int mvdstdRefCount;
1921
static MVDSTD_Mode mvdstd_mode;
2022
static MVDSTD_InputFormat mvdstd_input_type;
@@ -50,6 +52,20 @@ static Result MVDSTD_Shutdown(void)
5052
return cmdbuf[1];
5153
}
5254

55+
static Result MVDSTD_CalculateWorkBufSize(const MVDSTD_CalculateWorkBufSizeConfig* config, u32* size_out)
56+
{
57+
Result ret=0;
58+
u32* cmdbuf = getThreadCommandBuffer();
59+
60+
cmdbuf[0] = IPC_MakeHeader(0x3,12,0); // 0x30300
61+
memcpy(&cmdbuf[1], config, sizeof(MVDSTD_OutputBuffersEntryList));
62+
63+
if(R_FAILED(ret=svcSendSyncRequest(mvdstdHandle)))return ret;
64+
if(size_out) *size_out = cmdbuf[2];
65+
66+
return cmdbuf[1];
67+
}
68+
5369
static Result MVDSTD_cmd5(s8 unk0, s8 unk1, s8 unk2, u32 unk3)
5470
{
5571
u32* cmdbuf = getThreadCommandBuffer();
@@ -219,7 +235,9 @@ Result mvdstdInit(MVDSTD_Mode mode, MVDSTD_InputFormat input_type, MVDSTD_Output
219235
{
220236
Result ret=0, ret2=0;
221237

222-
mvdstd_workbufsize = size;
238+
//It seems there is some buffer overflow (some hundreds bytes according to test)
239+
//when maximum number of reference frames are used : https://www.3dbrew.org/wiki/MVDSTD:CalculateWorkBufSize
240+
mvdstd_workbufsize = (size + CALC_BUF_SIZE_SAFETY_MARGIN);
223241
mvdstd_mode = mode;
224242
mvdstd_input_type = input_type;
225243
mvdstd_output_type = output_type;
@@ -304,10 +322,40 @@ void mvdstdExit(void)
304322
MVDSTD_Shutdown();
305323

306324
svcCloseHandle(mvdstdHandle);
325+
mvdstdHandle = 0;
307326

308327
linearFree(mvdstd_workbuf);
309328
}
310329

330+
Result mvdstdCalculateBufferSize(const MVDSTD_CalculateWorkBufSizeConfig* config, u32* size_out)
331+
{
332+
bool must_close_handle = false;
333+
Result ret = 0;
334+
335+
if(!config || !size_out || config->level.level > 0x10)
336+
return -1;
337+
338+
//If we don't have mvdstd handle, get it.
339+
if(mvdstdHandle == 0)
340+
{
341+
if(R_FAILED(ret=srvGetServiceHandle(&mvdstdHandle, "mvd:STD")))
342+
return ret;
343+
344+
must_close_handle = true;
345+
}
346+
347+
ret = MVDSTD_CalculateWorkBufSize(config, size_out);
348+
349+
//Release handle if we must do so.
350+
if(must_close_handle)
351+
{
352+
svcCloseHandle(mvdstdHandle);
353+
mvdstdHandle = 0;
354+
}
355+
356+
return ret;
357+
}
358+
311359
void mvdstdGenerateDefaultConfig(MVDSTD_Config*config, u32 input_width, u32 input_height, u32 output_width, u32 output_height, u32 *vaddr_colorconv_indata, u32 *vaddr_outdata0, u32 *vaddr_outdata1)
312360
{
313361
memset(config, 0, sizeof(MVDSTD_Config));

0 commit comments

Comments
 (0)