From 29023aa39dd5802fcd705ecdc21ae0d0633c937a Mon Sep 17 00:00:00 2001 From: Core 2 Extreme <45873899+Core-2-Extreme@users.noreply.github.com> Date: Thu, 3 Apr 2025 21:17:34 +0900 Subject: [PATCH 1/6] Added mvdstdCalculateBufferSize() --- libctru/include/3ds/services/mvd.h | 40 +++++++++++++++++++++ libctru/source/services/mvd.c | 57 ++++++++++++++++++++++++++++-- 2 files changed, 95 insertions(+), 2 deletions(-) diff --git a/libctru/include/3ds/services/mvd.h b/libctru/include/3ds/services/mvd.h index 4ccf5c94e..7749af63e 100644 --- a/libctru/include/3ds/services/mvd.h +++ b/libctru/include/3ds/services/mvd.h @@ -96,6 +96,39 @@ typedef struct { u8 cmd1b_inval; } MVDSTD_InitStruct; +typedef struct +{ + u8 enable; //Whether use this calculation method. + u8 flag; //Flag for calculation. + u8 double_size; //If set, size calculation result is doubled. + u8 level; //H.264 (AVC) level. +} 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 +142,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, see here for detailed explanations : https://www.3dbrew.org/wiki/MVDSTD:CalculateWorkBufSize. + * @param size_out Calculated buffer size in bytes. + */ +Result mvdstdCalculateBufferSize(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..03ddcbb4d 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(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,45 @@ void mvdstdExit(void) MVDSTD_Shutdown(); svcCloseHandle(mvdstdHandle); + mvdstdHandle = 0; linearFree(mvdstd_workbuf); } +Result mvdstdCalculateBufferSize(MVDSTD_CalculateWorkBufSizeConfig* config, u32* size_out) +{ + bool must_close_handle = false; + Result ret = 0; + + if(!config || !size_out) + 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; + } + + //Attempting to use more than 0x10 for H.264 level will likely cause out-of-bound access. + //https://www.3dbrew.org/wiki/MVDSTD:CalculateWorkBufSize + if(config->level.level > 0x10) + config->level.level = 0x10; + + 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)); From e02889d889532780d6817f8ac99f3a934553ec72 Mon Sep 17 00:00:00 2001 From: Core 2 Extreme <45873899+Core-2-Extreme@users.noreply.github.com> Date: Thu, 3 Apr 2025 21:39:43 +0900 Subject: [PATCH 2/6] Added flags and H.264 level definitions. --- libctru/include/3ds/services/mvd.h | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/libctru/include/3ds/services/mvd.h b/libctru/include/3ds/services/mvd.h index 7749af63e..1ec505ea0 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 (u8)(0x00) //Nothing. +#define MVD_CALC_WITH_LEVEL_FLAG_ENABLE_CALC (u8)(0x01) //Enable calculation with level. +#define MVD_CALC_WITH_LEVEL_FLAG_ENABLE_EXTRA_OP (u8)(0x02) //Enable extra op after base calculation (see : https://www.3dbrew.org/wiki/MVDSTD:CalculateWorkBufSize). +#define MVD_CALC_WITH_LEVEL_FLAG_UNK (u8)(0x04) //Unknown. + +#define MVD_H264_LEVEL_1_0 (u8)(0x00) //H.264 level 1.0. +#define MVD_H264_LEVEL_1_0B (u8)(0x01) //H.264 level 1.0b. +#define MVD_H264_LEVEL_1_1 (u8)(0x02) //H.264 level 1.1. +#define MVD_H264_LEVEL_1_2 (u8)(0x03) //H.264 level 1.2. +#define MVD_H264_LEVEL_1_3 (u8)(0x04) //H.264 level 1.3. +#define MVD_H264_LEVEL_2_0 (u8)(0x05) //H.264 level 2.0. +#define MVD_H264_LEVEL_2_1 (u8)(0x06) //H.264 level 2.1. +#define MVD_H264_LEVEL_2_2 (u8)(0x07) //H.264 level 2.2. +#define MVD_H264_LEVEL_3_0 (u8)(0x08) //H.264 level 3.0. +#define MVD_H264_LEVEL_3_1 (u8)(0x09) //H.264 level 3.1. +#define MVD_H264_LEVEL_3_2 (u8)(0x0A) //H.264 level 3.2. +#define MVD_H264_LEVEL_4_0 (u8)(0x0B) //H.264 level 4.0. +#define MVD_H264_LEVEL_4_1 (u8)(0x0C) //H.264 level 4.1. +#define MVD_H264_LEVEL_4_2 (u8)(0x0D) //H.264 level 4.2. +#define MVD_H264_LEVEL_5_0 (u8)(0x0E) //H.264 level 5.0. +#define MVD_H264_LEVEL_5_1 (u8)(0x0F) //H.264 level 5.1. +#define MVD_H264_LEVEL_5_2 (u8)(0x10) //H.264 level 5.2. + /// Processing mode. typedef enum { MVDMODE_COLORFORMATCONV, ///< Converting color formats. @@ -99,9 +122,9 @@ typedef struct { typedef struct { u8 enable; //Whether use this calculation method. - u8 flag; //Flag for calculation. + 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. + u8 level; //H.264 (AVC) level (MVD_H264_LEVEL_XXXXXX). } MVDSTD_WithLevel; typedef struct From 68f1b87cdcb55f5f5b6e77c2d43f06b635b74684 Mon Sep 17 00:00:00 2001 From: Core 2 Extreme <45873899+Core-2-Extreme@users.noreply.github.com> Date: Thu, 8 May 2025 20:49:32 +0900 Subject: [PATCH 3/6] Changed to return an error when level > 0x10 --- libctru/include/3ds/services/mvd.h | 2 +- libctru/source/services/mvd.c | 7 +------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/libctru/include/3ds/services/mvd.h b/libctru/include/3ds/services/mvd.h index 1ec505ea0..c9e2efa6e 100644 --- a/libctru/include/3ds/services/mvd.h +++ b/libctru/include/3ds/services/mvd.h @@ -124,7 +124,7 @@ 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_XXXXXX). + u8 level; //H.264 (AVC) level (MVD_H264_LEVEL_1_0 ~ MVD_H264_LEVEL_5_2). } MVDSTD_WithLevel; typedef struct diff --git a/libctru/source/services/mvd.c b/libctru/source/services/mvd.c index 03ddcbb4d..cde862135 100644 --- a/libctru/source/services/mvd.c +++ b/libctru/source/services/mvd.c @@ -332,7 +332,7 @@ Result mvdstdCalculateBufferSize(MVDSTD_CalculateWorkBufSizeConfig* config, u32* bool must_close_handle = false; Result ret = 0; - if(!config || !size_out) + if(!config || !size_out || config->level.level > 0x10) return -1; //If we don't have mvdstd handle, get it. @@ -344,11 +344,6 @@ Result mvdstdCalculateBufferSize(MVDSTD_CalculateWorkBufSizeConfig* config, u32* must_close_handle = true; } - //Attempting to use more than 0x10 for H.264 level will likely cause out-of-bound access. - //https://www.3dbrew.org/wiki/MVDSTD:CalculateWorkBufSize - if(config->level.level > 0x10) - config->level.level = 0x10; - ret = MVDSTD_CalculateWorkBufSize(config, size_out); //Release handle if we must do so. From d2ec79d3c3e197c8300b15b97f7442ef27349686 Mon Sep 17 00:00:00 2001 From: Core 2 Extreme <45873899+Core-2-Extreme@users.noreply.github.com> Date: Thu, 8 May 2025 20:50:00 +0900 Subject: [PATCH 4/6] Replaced (u8)(xxx) with xxx --- libctru/include/3ds/services/mvd.h | 44 +++++++++++++++--------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/libctru/include/3ds/services/mvd.h b/libctru/include/3ds/services/mvd.h index c9e2efa6e..ec15362de 100644 --- a/libctru/include/3ds/services/mvd.h +++ b/libctru/include/3ds/services/mvd.h @@ -20,28 +20,28 @@ /// 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 (u8)(0x00) //Nothing. -#define MVD_CALC_WITH_LEVEL_FLAG_ENABLE_CALC (u8)(0x01) //Enable calculation with level. -#define MVD_CALC_WITH_LEVEL_FLAG_ENABLE_EXTRA_OP (u8)(0x02) //Enable extra op after base calculation (see : https://www.3dbrew.org/wiki/MVDSTD:CalculateWorkBufSize). -#define MVD_CALC_WITH_LEVEL_FLAG_UNK (u8)(0x04) //Unknown. - -#define MVD_H264_LEVEL_1_0 (u8)(0x00) //H.264 level 1.0. -#define MVD_H264_LEVEL_1_0B (u8)(0x01) //H.264 level 1.0b. -#define MVD_H264_LEVEL_1_1 (u8)(0x02) //H.264 level 1.1. -#define MVD_H264_LEVEL_1_2 (u8)(0x03) //H.264 level 1.2. -#define MVD_H264_LEVEL_1_3 (u8)(0x04) //H.264 level 1.3. -#define MVD_H264_LEVEL_2_0 (u8)(0x05) //H.264 level 2.0. -#define MVD_H264_LEVEL_2_1 (u8)(0x06) //H.264 level 2.1. -#define MVD_H264_LEVEL_2_2 (u8)(0x07) //H.264 level 2.2. -#define MVD_H264_LEVEL_3_0 (u8)(0x08) //H.264 level 3.0. -#define MVD_H264_LEVEL_3_1 (u8)(0x09) //H.264 level 3.1. -#define MVD_H264_LEVEL_3_2 (u8)(0x0A) //H.264 level 3.2. -#define MVD_H264_LEVEL_4_0 (u8)(0x0B) //H.264 level 4.0. -#define MVD_H264_LEVEL_4_1 (u8)(0x0C) //H.264 level 4.1. -#define MVD_H264_LEVEL_4_2 (u8)(0x0D) //H.264 level 4.2. -#define MVD_H264_LEVEL_5_0 (u8)(0x0E) //H.264 level 5.0. -#define MVD_H264_LEVEL_5_1 (u8)(0x0F) //H.264 level 5.1. -#define MVD_H264_LEVEL_5_2 (u8)(0x10) //H.264 level 5.2. +#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 { From d7b0595ac59111c15dc12409c4f227a36845c527 Mon Sep 17 00:00:00 2001 From: Core 2 Extreme <45873899+Core-2-Extreme@users.noreply.github.com> Date: Thu, 8 May 2025 20:50:31 +0900 Subject: [PATCH 5/6] Added const --- libctru/include/3ds/services/mvd.h | 2 +- libctru/source/services/mvd.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libctru/include/3ds/services/mvd.h b/libctru/include/3ds/services/mvd.h index ec15362de..7d0172dc0 100644 --- a/libctru/include/3ds/services/mvd.h +++ b/libctru/include/3ds/services/mvd.h @@ -170,7 +170,7 @@ void mvdstdExit(void); * @param config Calculation config, see here for detailed explanations : https://www.3dbrew.org/wiki/MVDSTD:CalculateWorkBufSize. * @param size_out Calculated buffer size in bytes. */ -Result mvdstdCalculateBufferSize(MVDSTD_CalculateWorkBufSizeConfig* config, u32* size_out); +Result mvdstdCalculateBufferSize(const MVDSTD_CalculateWorkBufSizeConfig* config, u32* size_out); /** * @brief Generates a default MVDSTD configuration. diff --git a/libctru/source/services/mvd.c b/libctru/source/services/mvd.c index cde862135..d060efa16 100644 --- a/libctru/source/services/mvd.c +++ b/libctru/source/services/mvd.c @@ -52,7 +52,7 @@ static Result MVDSTD_Shutdown(void) return cmdbuf[1]; } -static Result MVDSTD_CalculateWorkBufSize(MVDSTD_CalculateWorkBufSizeConfig* config, u32* size_out) +static Result MVDSTD_CalculateWorkBufSize(const MVDSTD_CalculateWorkBufSizeConfig* config, u32* size_out) { Result ret=0; u32* cmdbuf = getThreadCommandBuffer(); @@ -327,7 +327,7 @@ void mvdstdExit(void) linearFree(mvdstd_workbuf); } -Result mvdstdCalculateBufferSize(MVDSTD_CalculateWorkBufSizeConfig* config, u32* size_out) +Result mvdstdCalculateBufferSize(const MVDSTD_CalculateWorkBufSizeConfig* config, u32* size_out) { bool must_close_handle = false; Result ret = 0; From f285b500d79e5cf9d363cb4649ed4ea31c9e8397 Mon Sep 17 00:00:00 2001 From: Core 2 Extreme <45873899+Core-2-Extreme@users.noreply.github.com> Date: Thu, 8 May 2025 20:58:19 +0900 Subject: [PATCH 6/6] Added doc for H.264 level --- libctru/include/3ds/services/mvd.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libctru/include/3ds/services/mvd.h b/libctru/include/3ds/services/mvd.h index 7d0172dc0..0dcb81648 100644 --- a/libctru/include/3ds/services/mvd.h +++ b/libctru/include/3ds/services/mvd.h @@ -167,7 +167,7 @@ void mvdstdExit(void); /** * @brief Calculate working buffer size for H.264 decoding. - * @param config Calculation config, see here for detailed explanations : https://www.3dbrew.org/wiki/MVDSTD:CalculateWorkBufSize. + * @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);