//New3DS-only, see also: http://3dbrew.org/wiki/MVD_Services
+///These values are the data returned as "result-codes" by MVDSTD.
+#define MVD_STATUS_OK 0x17000
+#define MVD_STATUS_BUSY 0x17002
+
/// Processing mode.
typedef enum {
MVDMODE_COLORFORMATCONV, ///< Converting color formats.
/// Output format.
typedef enum {
- MVD_OUTPUT_RGB565 = 0x00040002 ///< RGB565
+ MVD_OUTPUT_BGR565 = 0x00040002, ///< BGR565
+ MVD_OUTPUT_RGB565 = 0x00040004 ///< RGB565
} MVDSTD_OutputFormat;
/// Processing configuration.
u32 outheight1; ///< Second output height.
u32 physaddr_outdata0; ///< Physical address of output data.
u32 physaddr_outdata1_colorconv; ///< Physical address of color conversion output data.
- u32 unk_x6c[0xb0>>2]; ///< Unknown.
+ u32 unk_x6c[0xa4>>2]; ///< Unknown.
+ u32 output_width_override; ///< Used for aligning the output width when larger than the output width. Overrides the output width when smaller than the output width.
+ u32 output_height_override; ///< Same as output_width_override except for the output height.
+ u32 unk_x118;
} MVDSTD_Config;
/**
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_colorconv);
/**
- * @brief Processes a frame.
+ * @brief Run color-format-conversion.
+ * @param config Pointer to the configuration to use.
+ */
+Result mvdstdConvertImage(MVDSTD_Config* config);
+
+/**
+ * @brief Processes a video frame(specifically a NAL-unit).
* @param config Pointer to the configuration to use.
- * @param h264_vaddr_inframe Input H264 frame.
- * @param h264_inframesize Size of the input frame.
- * @param h264_frameid ID of the input frame.
+ * @parameter_set Must be true for the initial NAL-unit parameter sets("Sequence Parameter Set" and "Picture Parameter Set"), false otherwise.
+ * @param inbuf_vaddr Input NAL-unit starting with the 3-byte "00 00 01" prefix. Must be located in linearmem.
+ * @param size Size of the input buffer.
*/
-Result mvdstdProcessFrame(MVDSTD_Config*config, u32 *h264_vaddr_inframe, u32 h264_inframesize, u32 h264_frameid);
+Result mvdstdProcessVideoFrame(MVDSTD_Config* config, bool parameter_set, void* inbuf_vaddr, size_t size);
/**
* @brief Sets the current configuration of MVDSTD.
static u32 *mvdstd_workbuf;
static size_t mvdstd_workbufsize;
+static u32 mvdstd_videoproc_frameid;
+
static Result MVDSTD_Initialize(u32* buf, u32 bufsize)
{
u32* cmdbuf = getThreadCommandBuffer();
return cmdbuf[1];
}
+static Result MVDSTD_cmd5(s8 unk0, s8 unk1, s8 unk2, u32 unk3)
+{
+ u32* cmdbuf = getThreadCommandBuffer();
+ cmdbuf[0] = IPC_MakeHeader(0x5,4,0); // 0x50100
+ cmdbuf[1] = unk0;
+ cmdbuf[2] = unk1;
+ cmdbuf[3] = unk2;
+ cmdbuf[4] = unk3;
+
+ Result ret=0;
+ if(R_FAILED(ret=svcSendSyncRequest(mvdstdHandle)))return ret;
+
+ return cmdbuf[1];
+}
+
+static Result MVDSTD_cmd7(void)
+{
+ u32* cmdbuf = getThreadCommandBuffer();
+ cmdbuf[0] = IPC_MakeHeader(0x7,0,0); // 0x70000
+
+ Result ret=0;
+ if(R_FAILED(ret=svcSendSyncRequest(mvdstdHandle)))return ret;
+
+ return cmdbuf[1];
+}
+
+static Result MVDSTD_ProcessNALUnit(u32 vaddr_buf, u32 physaddr_buf, u32 size, u32 frameid)
+{
+ u32* cmdbuf = getThreadCommandBuffer();
+ cmdbuf[0] = IPC_MakeHeader(0x8,5,2); // 0x80142
+ cmdbuf[1] = vaddr_buf;
+ cmdbuf[2] = physaddr_buf;
+ cmdbuf[3] = size;
+ cmdbuf[4] = frameid;
+ cmdbuf[5] = 0;//Unknown
+ cmdbuf[6] = IPC_Desc_SharedHandles(1);
+ cmdbuf[7] = CUR_PROCESS_HANDLE;
+
+ Result ret=0;
+ if(R_FAILED(ret=svcSendSyncRequest(mvdstdHandle)))return ret;
+
+ return cmdbuf[1];
+}
+
+static Result MVDSTD_ControlFrameRendering(s8 type)
+{
+ u32* cmdbuf = getThreadCommandBuffer();
+ cmdbuf[0] = IPC_MakeHeader(0x9,1,2); // 0x90042
+ cmdbuf[1] = type;
+ cmdbuf[2] = IPC_Desc_SharedHandles(1);
+ cmdbuf[3] = CUR_PROCESS_HANDLE;
+
+ Result ret=0;
+ if(R_FAILED(ret=svcSendSyncRequest(mvdstdHandle)))return ret;
+
+ return cmdbuf[1];
+}
+
static Result MVDSTD_cmd18(void)
{
u32* cmdbuf = getThreadCommandBuffer();
return cmdbuf[1];
}
+static Result MVDSTD_cmd1b(u8 unk)
+{
+ u32* cmdbuf = getThreadCommandBuffer();
+ cmdbuf[0] = IPC_MakeHeader(0x1B,1,0); // 0x1B0040
+ cmdbuf[1] = unk;
+
+ Result ret=0;
+ if(R_FAILED(ret=svcSendSyncRequest(mvdstdHandle)))return ret;
+
+ return cmdbuf[1];
+}
+
+static Result MVDSTD_cmd1c(void)
+{
+ u32* cmdbuf = getThreadCommandBuffer();
+ cmdbuf[0] = IPC_MakeHeader(0x1C,0,0); // 0x1C0000
+
+ Result ret=0;
+ if(R_FAILED(ret=svcSendSyncRequest(mvdstdHandle)))return ret;
+
+ return cmdbuf[1];
+}
+
Result MVDSTD_SetConfig(MVDSTD_Config* config)
{
Result ret=0;
mvdstd_input_type = input_type;
mvdstd_output_type = output_type;
+ mvdstd_videoproc_frameid = 0;
+
if(mvdstd_mode==MVDMODE_COLORFORMATCONV)mvdstd_workbufsize = 1;
- if(mvdstd_mode!=MVDMODE_COLORFORMATCONV)return -2;//Video processing / H.264 isn't supported atm.
if (AtomicPostIncrement(&mvdstdRefCount)) return 0;
if(R_FAILED(ret=srvGetServiceHandle(&mvdstdHandle, "mvd:STD"))) goto cleanup0;
mvdstd_workbuf = linearAlloc(mvdstd_workbufsize);
- if(mvdstd_workbuf==NULL) goto cleanup1;
+ if(mvdstd_workbuf==NULL)
+ {
+ ret = -1;
+ goto cleanup1;
+ }
ret = MVDSTD_Initialize((u32*) osConvertOldLINEARMemToNew(mvdstd_workbuf), mvdstd_workbufsize);
if(R_FAILED(ret)) goto cleanup2;
+ if(mvdstd_mode==MVDMODE_VIDEOPROCESSING)
+ {
+ ret = MVDSTD_cmd5(0, 0, 0, 0);
+ if(ret!=MVD_STATUS_OK) goto cleanup3;
+ }
+
ret = MVDSTD_cmd18();
- if(R_FAILED(ret)) goto cleanup3;
+ if(ret!=MVD_STATUS_OK) goto cleanup3;
- return ret;
+ if(mvdstd_mode==MVDMODE_VIDEOPROCESSING)
+ {
+ ret = MVDSTD_cmd1b(1);
+ if(ret!=MVD_STATUS_OK) goto cleanup3;
+ }
+
+ return 0;
cleanup3:
+ ret = MVD_STATUS_BUSY;
+ while(ret==MVD_STATUS_BUSY)ret = MVDSTD_ControlFrameRendering(1);
+
+ if(mvdstd_mode==MVDMODE_VIDEOPROCESSING)MVDSTD_cmd1c();
+
+ MVDSTD_cmd19();
+
+ if(mvdstd_mode==MVDMODE_VIDEOPROCESSING)MVDSTD_cmd7();
+
MVDSTD_Shutdown();
cleanup2:
linearFree(mvdstd_workbuf);
void mvdstdExit(void)
{
+ Result ret=0;
+
if (AtomicDecrement(&mvdstdRefCount)) return;
- if(mvdstd_mode==MVDMODE_COLORFORMATCONV)
- {
- MVDSTD_cmd19();
- }
+ ret = MVD_STATUS_BUSY;
+ while(ret==MVD_STATUS_BUSY)ret = MVDSTD_ControlFrameRendering(1);
+
+ if(mvdstd_mode==MVDMODE_VIDEOPROCESSING)MVDSTD_cmd1c();
+
+ MVDSTD_cmd19();
+
+ if(mvdstd_mode==MVDMODE_VIDEOPROCESSING)MVDSTD_cmd7();
MVDSTD_Shutdown();
config->unk_x6c[(0x94-0x6c)>>2] = 0x204;
config->unk_x6c[(0xa8-0x6c)>>2] = 0x1;
config->unk_x6c[(0x104-0x6c)>>2] = 0x1;
- config->unk_x6c[(0x110-0x6c)>>2] = 0x200;
- config->unk_x6c[(0x114-0x6c)>>2] = 0x100;
+ config->output_width_override = 0x200;
+ config->output_height_override = 0x100;
}
-Result mvdstdProcessFrame(MVDSTD_Config*config, u32 *h264_vaddr_inframe, u32 h264_inframesize, u32 h264_frameid)
+Result mvdstdConvertImage(MVDSTD_Config* config)
{
Result ret;
- if(mvdstdRefCount==0)return 0;
+ if(mvdstdRefCount==0)return -3;
if(config==NULL)return -1;
if(mvdstd_mode!=MVDMODE_COLORFORMATCONV)return -2;
ret = MVDSTD_SetConfig(config);
- if(R_FAILED(ret))return ret;
+ if(ret!=MVD_STATUS_OK)return ret;
return MVDSTD_cmd1a();
}
+Result mvdstdProcessVideoFrame(MVDSTD_Config* config, bool parameter_set, void* inbuf_vaddr, size_t size)
+{
+ Result ret;
+
+ if(mvdstdRefCount==0)return -3;
+ if(config==NULL)return -1;
+ if(mvdstd_mode!=MVDMODE_VIDEOPROCESSING)return -2;
+
+ ret = MVDSTD_ProcessNALUnit((u32)inbuf_vaddr, (u32)osConvertVirtToPhys(inbuf_vaddr), size, mvdstd_videoproc_frameid);
+ mvdstd_videoproc_frameid++;
+ if(mvdstd_videoproc_frameid>=0x12)mvdstd_videoproc_frameid = 0;
+ //if(ret!=MVD_STATUS_OK)return ret;
+
+ if(parameter_set)return ret;
+
+ ret = MVDSTD_SetConfig(config);
+ if(ret!=MVD_STATUS_OK)return ret;
+
+ ret = MVD_STATUS_BUSY;
+ while(ret==MVD_STATUS_BUSY)ret = MVDSTD_ControlFrameRendering(0);
+
+ return ret;
+}
+