Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions libctru/include/3ds/services/mvd.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,29 @@
/// Default input size for mvdstdInit(). This is what the New3DS Internet Browser uses, from the MVDSTD:CalculateWorkBufSize output.
#define MVD_DEFAULT_WORKBUF_SIZE 0x9006C8

#define MVD_CALC_WITH_LEVEL_FLAG_NONE 0x00 //Nothing.
#define MVD_CALC_WITH_LEVEL_FLAG_ENABLE_CALC 0x01 //Enable calculation with level.
#define MVD_CALC_WITH_LEVEL_FLAG_ENABLE_EXTRA_OP 0x02 //Enable extra op after base calculation (see : https://www.3dbrew.org/wiki/MVDSTD:CalculateWorkBufSize.
#define MVD_CALC_WITH_LEVEL_FLAG_UNK 0x04 //Unknown.

#define MVD_H264_LEVEL_1_0 0x00 //H.264 level 1.0.
#define MVD_H264_LEVEL_1_0B 0x01 //H.264 level 1.0b.
#define MVD_H264_LEVEL_1_1 0x02 //H.264 level 1.1.
#define MVD_H264_LEVEL_1_2 0x03 //H.264 level 1.2.
#define MVD_H264_LEVEL_1_3 0x04 //H.264 level 1.3.
#define MVD_H264_LEVEL_2_0 0x05 //H.264 level 2.0.
#define MVD_H264_LEVEL_2_1 0x06 //H.264 level 2.1.
#define MVD_H264_LEVEL_2_2 0x07 //H.264 level 2.2.
#define MVD_H264_LEVEL_3_0 0x08 //H.264 level 3.0.
#define MVD_H264_LEVEL_3_1 0x09 //H.264 level 3.1.
#define MVD_H264_LEVEL_3_2 0x0A //H.264 level 3.2.
#define MVD_H264_LEVEL_4_0 0x0B //H.264 level 4.0.
#define MVD_H264_LEVEL_4_1 0x0C //H.264 level 4.1.
#define MVD_H264_LEVEL_4_2 0x0D //H.264 level 4.2.
#define MVD_H264_LEVEL_5_0 0x0E //H.264 level 5.0.
#define MVD_H264_LEVEL_5_1 0x0F //H.264 level 5.1.
#define MVD_H264_LEVEL_5_2 0x10 //H.264 level 5.2.

/// Processing mode.
typedef enum {
MVDMODE_COLORFORMATCONV, ///< Converting color formats.
Expand Down Expand Up @@ -96,6 +119,39 @@ typedef struct {
u8 cmd1b_inval;
} MVDSTD_InitStruct;

typedef struct
{
u8 enable; //Whether use this calculation method.
u8 flag; //Flag for calculation (MVD_CALC_WITH_LEVEL_FLAG_XXXXXX).
u8 double_size; //If set, size calculation result is doubled.
u8 level; //H.264 (AVC) level (MVD_H264_LEVEL_1_0 ~ MVD_H264_LEVEL_5_2).
} MVDSTD_WithLevel;

typedef struct
{
u8 enable; //Whether use this calculation method.
u8 ref_frames; //Number of reference frames.
} MVDSTD_WithNumOfRefFrames;

/// H.264 buffer calculation configuration.
/// See here for detailed explanations : https://www.3dbrew.org/wiki/MVDSTD:CalculateWorkBufSize.
typedef struct {
u8 unused_0x00; //Unused.
MVDSTD_WithLevel level; //Calc buffer size with H.264 level.
MVDSTD_WithNumOfRefFrames ref_frames_a; //Calc buffer size with num of reference frames and resolution.
MVDSTD_WithNumOfRefFrames ref_frames_b; //Calc buffer size with num of reference frames and resolution.
u8 unused_0x09[3]; //Unused.
u32 unk_0x0c; //Unknown.
u32 unk_0x10; //Unknown.
u32 unk_0x14; //Unknown.
u32 unk_0x18; //Unknown.
u32 unk_0x1c; //Unknown.
u32 unk_0x20; //Unknown.
u32 unk_0x24; //Unknown.
u32 width; //Video width.
u32 height; //Video height.
} MVDSTD_CalculateWorkBufSizeConfig;

/**
* @brief Initializes MVDSTD.
* @param mode Mode to initialize MVDSTD to.
Expand All @@ -109,6 +165,13 @@ Result mvdstdInit(MVDSTD_Mode mode, MVDSTD_InputFormat input_type, MVDSTD_Output
/// Shuts down MVDSTD.
void mvdstdExit(void);

/**
* @brief Calculate working buffer size for H.264 decoding.
* @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.
* @param size_out Calculated buffer size in bytes.
*/
Result mvdstdCalculateBufferSize(const MVDSTD_CalculateWorkBufSizeConfig* config, u32* size_out);

/**
* @brief Generates a default MVDSTD configuration.
* @param config Pointer to output the generated config to.
Expand Down
52 changes: 50 additions & 2 deletions libctru/source/services/mvd.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
#include <3ds/services/mvd.h>
#include <3ds/ipc.h>

Handle mvdstdHandle;
#define CALC_BUF_SIZE_SAFETY_MARGIN (u16)(4096)

Handle mvdstdHandle = 0;
static int mvdstdRefCount;
static MVDSTD_Mode mvdstd_mode;
static MVDSTD_InputFormat mvdstd_input_type;
Expand Down Expand Up @@ -50,6 +52,20 @@ static Result MVDSTD_Shutdown(void)
return cmdbuf[1];
}

static Result MVDSTD_CalculateWorkBufSize(const MVDSTD_CalculateWorkBufSizeConfig* config, u32* size_out)
{
Result ret=0;
u32* cmdbuf = getThreadCommandBuffer();

cmdbuf[0] = IPC_MakeHeader(0x3,12,0); // 0x30300
memcpy(&cmdbuf[1], config, sizeof(MVDSTD_OutputBuffersEntryList));

if(R_FAILED(ret=svcSendSyncRequest(mvdstdHandle)))return ret;
if(size_out) *size_out = cmdbuf[2];

return cmdbuf[1];
}

static Result MVDSTD_cmd5(s8 unk0, s8 unk1, s8 unk2, u32 unk3)
{
u32* cmdbuf = getThreadCommandBuffer();
Expand Down Expand Up @@ -219,7 +235,9 @@ Result mvdstdInit(MVDSTD_Mode mode, MVDSTD_InputFormat input_type, MVDSTD_Output
{
Result ret=0, ret2=0;

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

svcCloseHandle(mvdstdHandle);
mvdstdHandle = 0;

linearFree(mvdstd_workbuf);
}

Result mvdstdCalculateBufferSize(const MVDSTD_CalculateWorkBufSizeConfig* config, u32* size_out)
{
bool must_close_handle = false;
Result ret = 0;

if(!config || !size_out || config->level.level > 0x10)
return -1;

//If we don't have mvdstd handle, get it.
if(mvdstdHandle == 0)
{
if(R_FAILED(ret=srvGetServiceHandle(&mvdstdHandle, "mvd:STD")))
return ret;

must_close_handle = true;
}

ret = MVDSTD_CalculateWorkBufSize(config, size_out);

//Release handle if we must do so.
if(must_close_handle)
{
svcCloseHandle(mvdstdHandle);
mvdstdHandle = 0;
}

return ret;
}

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)
{
memset(config, 0, sizeof(MVDSTD_Config));
Expand Down