diff --git a/libctru/include/3ds/services/mvd.h b/libctru/include/3ds/services/mvd.h index 4ccf5c94e..0dcb81648 100644 --- a/libctru/include/3ds/services/mvd.h +++ b/libctru/include/3ds/services/mvd.h @@ -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. @@ -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. @@ -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. diff --git a/libctru/source/services/mvd.c b/libctru/source/services/mvd.c index 85989d79e..d060efa16 100644 --- a/libctru/source/services/mvd.c +++ b/libctru/source/services/mvd.c @@ -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; @@ -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(); @@ -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; @@ -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));