mvd/source/main.c
#include <stdio.h>
#include <string.h>
#include <3ds.h>
u8* inaddr;
u8* outaddr;
void mvd_colorconvert()
{
Result ret;
FILE *f = NULL;
u8* bufAdr = gfxGetFramebuffer(GFX_BOTTOM, GFX_LEFT, NULL, NULL);
u8* gfxtopadr = gfxGetFramebuffer(GFX_TOP, GFX_LEFT, NULL, NULL);
MVDSTD_Config config;
printf("mvd color-format-conversion example.\n");
f = fopen("sdmc:/mvd_indata.bin", "r");
if(f)
{
fread(inaddr, 1, 0x46500, f);
fclose(f);
}
else
{
memcpy(inaddr, bufAdr, 320*240*3);
}
memset(gfxtopadr, 0, 0x46500);
GSPGPU_FlushDataCache(inaddr, 0x46500);
GSPGPU_FlushDataCache(gfxtopadr, 0x46500);
ret = mvdstdInit(MVDMODE_COLORFORMATCONV, MVD_INPUT_YUYV422, MVD_OUTPUT_BGR565, 0, NULL);
printf("mvdstdInit(): 0x%08x\n", (unsigned int)ret);
if(ret==0)
{
mvdstdGenerateDefaultConfig(&config, 320, 240, 320, 240, (u32*)inaddr, (u32*)outaddr, (u32*)&outaddr[0x12c00]);
ret = mvdstdConvertImage(&config);
printf("mvdstdConvertImage(): 0x%08x\n", (unsigned int)ret);
}
f = fopen("sdmc:/mvd_outdata.bin", "w");
if(f)
{
fwrite(outaddr, 1, 0x100000, f);
fclose(f);
}
memcpy(gfxtopadr, outaddr, 0x46500);
mvdstdExit();
gfxFlushBuffers();
gfxSwapBuffersGpu();
gspWaitForVBlank();
}
void mvd_video()
{
Result ret;
size_t video_size, nalunitsize;
u32 video_pos=0;
u32 cur_nalunit_pos=0, prev_nalunit_pos=0;
u32 nalcount=0;
u8 *video;
u32 flagval=0;
FILE *f = NULL;
u8* gfxtopadr=NULL;
MVDSTD_Config config;
u32 prefix_offset;
u8 prefix[4] = {0x00, 0x00, 0x00, 0x01};
printf("Loading video...\n");
//This loads the entire video into memory, normally you'd use a library to stream it.
f = fopen("romfs:/video.h264", "r");
if(f==NULL)
{
printf("Faile to open the video in romfs.\n");
return;
}
video = &inaddr[0x100000];
video_size = fread(video, 1, 0xF00000, f);
fclose(f);
if(video_size==0 || video_size>=0xF00000)
{
printf("Failed to read video / video is too large.\n");
return;
}
ret = mvdstdInit(MVDMODE_VIDEOPROCESSING, MVD_INPUT_H264, MVD_OUTPUT_BGR565, MVD_DEFAULT_WORKBUF_SIZE, NULL);
printf("mvdstdInit(): 0x%08x\n", (unsigned int)ret);
if(ret!=0)return;
printf("Processing 0x%08x-byte video...\n", (unsigned int)video_size);
mvdstdGenerateDefaultConfig(&config, 240, 400, 240, 400, NULL, (u32*)outaddr, (u32*)outaddr);//Normally you'd set the input dimensions here to dimensions loaded from the actual video.
//Normally you'd use a library to load each NAL-unit, this example just parses the data manually.
while(video_pos < video_size+1)
{
cur_nalunit_pos = video_pos;
video_pos++;
prefix_offset = 1;
if(cur_nalunit_pos<video_size)
{
/*
{
if(memcmp(&video[cur_nalunit_pos], &prefix[1], 3)==0 && cur_nalunit_pos==0x2dd)
{
prefix_offset = 0;
}
}
if(prefix_offset)*/
//else
{
if(memcmp(&video[cur_nalunit_pos], prefix, 4))
{
continue;
}
else
{
video_pos++;
}
}
}
if(nalcount && prev_nalunit_pos!=cur_nalunit_pos)
{
nalunitsize = cur_nalunit_pos - prev_nalunit_pos - prefix_offset;
if(nalunitsize > 0x100000)
{
printf("The NAL-unit at offset 0x%08x is too large.\n", (unsigned int)nalunitsize);
break;
}
memcpy(inaddr, &video[prev_nalunit_pos+prefix_offset], nalunitsize);
GSPGPU_FlushDataCache(inaddr, nalunitsize);
MVDSTD_ProcessNALUnitOut tmpout;//Normally you don't really need to use this.
//printf("Processing NAL-unit at offset 0x%08x size 0x%08x...\n", (unsigned int)prev_nalunit_pos, (unsigned int)nalunitsize);
ret = mvdstdProcessVideoFrame(inaddr, nalunitsize, flagval, &tmpout);
if(!MVD_CHECKNALUPROC_SUCCESS(ret))
{
printf("mvdstdProcessVideoFrame() at NAL-unit offset 0x%08x size 0x%08x returned: 0x%08x. remaining_size=0x%08x.\n", (unsigned int)prev_nalunit_pos, (unsigned int)nalunitsize, (unsigned int)ret, (unsigned int)tmpout.remaining_size);
break;
}
if(ret!=MVD_STATUS_PARAMSET && ret!=MVD_STATUS_INCOMPLETEPROCESSING)
{
gfxtopadr = gfxGetFramebuffer(GFX_TOP, GFX_LEFT, NULL, NULL);
config.physaddr_outdata0 = osConvertVirtToPhys(gfxtopadr);
//This sets the MVD output to the framebuffer directly. This is to avoid doing the video->framebuffer image rotation on the ARM11.
//Normally you'd use a seperate MVD output buffer, then transfer that to the framebuffer(such as with GPU rendering).
ret = mvdstdRenderVideoFrame(&config, true);
if(ret!=MVD_STATUS_OK)
{
printf("mvdstdRenderVideoFrame() at NAL-unit offset 0x%08x returned: 0x%08x\n", (unsigned int)prev_nalunit_pos, (unsigned int)ret);
break;
}
hidScanInput();
if(hidKeysDown() & KEY_B)break;
//Enable/disable the flag passed to mvdstdProcessVideoFrame().
if(hidKeysDown() & KEY_DOWN)
{
flagval-= 0x1;
printf("0x%08x\n", (unsigned int)flagval);
}
if(hidKeysDown() & KEY_UP)
{
flagval+= 0x1;
printf("0x%08x\n", (unsigned int)flagval);
}
//gspWaitForVBlank();//Can't use this under this example without a major slowdown. This is due to this example not doing any buffering for the frames.
gfxSwapBuffersGpu();
}
}
nalcount++;
prev_nalunit_pos = cur_nalunit_pos;
}
mvdstdExit();
}
int main()
{
Result ret=0;
int draw=1;
int ready=0;
int type=0;
gfxInit(GSP_RGB565_OES,GSP_BGR8_OES,false);
consoleInit(GFX_BOTTOM, NULL);
ret = romfsInit();
if(R_FAILED(ret))
{
printf("romfsInit() failed: 0x%08x\n", (unsigned int)ret);
ready = -1;
}
if(ready==0)
{
inaddr = linearMemAlign(0x1000000, 0x40);
outaddr = linearMemAlign(0x100000, 0x40);
if(!(inaddr && outaddr))
{
ready = -2;
printf("Failed to allocate memory.\n");
}
}
// Main loop
while (aptMainLoop())
{
gspWaitForVBlank();
hidScanInput();
if(draw && type==0)
{
consoleClear();
draw = 0;
if(ready==0)printf("mvd example\n");
printf("Press START to exit.\n");
if(ready==0)
{
printf("Press A for color-format-conversion.\n");
printf("Press B for video(no sound).\n");
}
}
u32 kDown = hidKeysDown();
if(type)
{
if(kDown & KEY_A)
{
type = 0;
continue;
}
}
if(type)continue;
if (kDown & KEY_START)
break; // break in order to return to hbmenu
if(ready==0)
{
type = 0;
if (kDown & KEY_A)type = 1;
if (kDown & KEY_B)type = 2;
if(type)
{
memset(inaddr, 0, 0x100000);
memset(outaddr, 0, 0x100000);
if(type==1)mvd_colorconvert();
if(type==2)mvd_video();
draw = 1;
printf("Press A to continue.\n");
}
}
}
if(inaddr)linearFree(inaddr);
if(outaddr)linearFree(outaddr);
if(ready!=-1)romfsExit();
gfxExit();
return 0;
}
3ds.h
Central 3DS header.
aptMainLoop
bool aptMainLoop(void)
Main function which handles sleep mode and HOME/power buttons - call this at the beginning of every f...
consoleInit
PrintConsole * consoleInit(gfxScreen_t screen, PrintConsole *console)
Initialise the console.
consoleClear
void consoleClear(void)
Clears the screen by using iprintf("\x1b[2J");.
gfxInit
void gfxInit(GSPGPU_FramebufferFormat topFormat, GSPGPU_FramebufferFormat bottomFormat, bool vrambuffers)
Initializes the LCD framebuffers.
gfxSwapBuffersGpu
void gfxSwapBuffersGpu(void)
Same as gfxSwapBuffers (formerly different).
gfxGetFramebuffer
u8 * gfxGetFramebuffer(gfxScreen_t screen, gfx3dSide_t side, u16 *width, u16 *height)
Retrieves the framebuffer of the specified screen to which graphics should be rendered.
GFX_BOTTOM
@ GFX_BOTTOM
Bottom screen.
Definition: gfx.h:27
GFX_TOP
@ GFX_TOP
Top screen.
Definition: gfx.h:26
gfxExit
void gfxExit(void)
Deinitializes and frees the LCD framebuffers.
GFX_LEFT
@ GFX_LEFT
Left eye framebuffer.
Definition: gfx.h:37
gfxFlushBuffers
void gfxFlushBuffers(void)
Flushes the data cache for the current framebuffers.
GSPGPU_FlushDataCache
Result GSPGPU_FlushDataCache(const void *adr, u32 size)
Flushes memory from the data cache.
gspWaitForVBlank
#define gspWaitForVBlank()
Waits for VBlank.
Definition: gspgpu.h:151
GSP_BGR8_OES
@ GSP_BGR8_OES
BGR8. (3 bytes)
Definition: gspgpu.h:30
GSP_RGB565_OES
@ GSP_RGB565_OES
RGB565. (2 bytes)
Definition: gspgpu.h:31
KEY_UP
@ KEY_UP
D-Pad Up or Circle Pad Up.
Definition: hid.h:37
KEY_B
@ KEY_B
B.
Definition: hid.h:13
KEY_START
@ KEY_START
Start.
Definition: hid.h:15
KEY_A
@ KEY_A
A.
Definition: hid.h:12
KEY_DOWN
@ KEY_DOWN
D-Pad Down or Circle Pad Down.
Definition: hid.h:38
hidKeysDown
u32 hidKeysDown(void)
Returns a bitmask of newly pressed buttons, this frame.
hidScanInput
void hidScanInput(void)
Scans HID for input data.
linearMemAlign
void * linearMemAlign(size_t size, size_t alignment)
Allocates a buffer aligned to the given size.
linearFree
void linearFree(void *mem)
Frees a buffer.
mvdstdExit
void mvdstdExit(void)
Shuts down MVDSTD.
MVDMODE_VIDEOPROCESSING
@ MVDMODE_VIDEOPROCESSING
Processing video.
Definition: mvd.h:26
MVDMODE_COLORFORMATCONV
@ MVDMODE_COLORFORMATCONV
Converting color formats.
Definition: mvd.h:25
MVD_DEFAULT_WORKBUF_SIZE
#define MVD_DEFAULT_WORKBUF_SIZE
Default input size for mvdstdInit(). This is what the New3DS Internet Browser uses,...
Definition: mvd.h:21
mvdstdRenderVideoFrame
Result mvdstdRenderVideoFrame(MVDSTD_Config *config, bool wait)
Renders the video frame.
MVD_STATUS_OK
#define MVD_STATUS_OK
These values are the data returned as "result-codes" by MVDSTD.
Definition: mvd.h:10
MVD_CHECKNALUPROC_SUCCESS
#define MVD_CHECKNALUPROC_SUCCESS(x)
This can be used to check whether mvdstdProcessVideoFrame() was successful.
Definition: mvd.h:18
mvdstdGenerateDefaultConfig
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)
Generates a default MVDSTD configuration.
mvdstdInit
Result mvdstdInit(MVDSTD_Mode mode, MVDSTD_InputFormat input_type, MVDSTD_OutputFormat output_type, u32 size, MVDSTD_InitStruct *initstruct)
Initializes MVDSTD.
MVD_INPUT_H264
@ MVD_INPUT_H264
H264.
Definition: mvd.h:32
MVD_INPUT_YUYV422
@ MVD_INPUT_YUYV422
YUYV422.
Definition: mvd.h:31
MVD_OUTPUT_BGR565
@ MVD_OUTPUT_BGR565
BGR565.
Definition: mvd.h:38
mvdstdConvertImage
Result mvdstdConvertImage(MVDSTD_Config *config)
Run color-format-conversion.
mvdstdProcessVideoFrame
Result mvdstdProcessVideoFrame(void *inbuf_vaddr, size_t size, u32 flag, MVDSTD_ProcessNALUnitOut *out)
Processes a video frame(specifically a NAL-unit).
osConvertVirtToPhys
u32 osConvertVirtToPhys(const void *vaddr)
Converts an address from virtual (process) memory to physical memory.
R_FAILED
#define R_FAILED(res)
Checks whether a result code indicates failure.
Definition: result.h:11
romfsInit
static Result romfsInit(void)
Wrapper for romfsMountSelf with the default "romfs" device name.
Definition: romfs.h:84
romfsExit
static Result romfsExit(void)
Wrapper for romfsUnmount with the default "romfs" device name.
Definition: romfs.h:90
MVDSTD_Config
Processing configuration.
Definition: mvd.h:43
MVDSTD_Config::physaddr_outdata0
u32 physaddr_outdata0
Physical address of output data.
Definition: mvd.h:64
MVDSTD_ProcessNALUnitOut
Definition: mvd.h:75
u8
uint8_t u8
would be nice if newlib had this already
Definition: types.h:21
Result
s32 Result
Function result.
Definition: types.h:42
u32
uint32_t u32
32-bit unsigned integer
Definition: types.h:23