mirror of
https://github.com/ekeeke/Genesis-Plus-GX.git
synced 2024-12-28 12:11:50 +01:00
[GCN/Wii]
*fixed audio/video desync that could occur after loading a new game *improved general synchronization of frame execution *modified ASNDLIB shutdown to prevent potential memory leak with DSP tasks *changed some global variables to use int type by default for optimization *some code cleanup
This commit is contained in:
parent
f44d38b537
commit
bad0696190
@ -34,10 +34,10 @@
|
|||||||
u8 soundbuffer[2][3840] ATTRIBUTE_ALIGN(32);
|
u8 soundbuffer[2][3840] ATTRIBUTE_ALIGN(32);
|
||||||
|
|
||||||
/* Current work soundbuffer */
|
/* Current work soundbuffer */
|
||||||
u8 mixbuffer;
|
u32 mixbuffer;
|
||||||
|
|
||||||
/* audio DMA status */
|
/* audio DMA status */
|
||||||
static u8 audioStarted = 0;
|
static u32 audioStarted = 0;
|
||||||
|
|
||||||
/* Background music */
|
/* Background music */
|
||||||
static u8 *Bg_music_ogg = NULL;
|
static u8 *Bg_music_ogg = NULL;
|
||||||
@ -56,9 +56,9 @@ static void ai_callback(void)
|
|||||||
/* AUDIO engine initialization */
|
/* AUDIO engine initialization */
|
||||||
void gx_audio_Init(void)
|
void gx_audio_Init(void)
|
||||||
{
|
{
|
||||||
/* Initialize AUDIO hardware */
|
/* Initialize AUDIO processing library (ASNDLIB) */
|
||||||
/* Default samplerate is 48kHZ */
|
/* AUDIO & DSP hardware are initialized */
|
||||||
/* Default DMA callback is programmed */
|
/* Default samplerate is set to 48kHz */
|
||||||
ASND_Init();
|
ASND_Init();
|
||||||
|
|
||||||
/* Load background music from FAT device */
|
/* Load background music from FAT device */
|
||||||
@ -83,7 +83,8 @@ void gx_audio_Shutdown(void)
|
|||||||
StopOgg();
|
StopOgg();
|
||||||
ASND_Pause(1);
|
ASND_Pause(1);
|
||||||
ASND_End();
|
ASND_End();
|
||||||
if (Bg_music_ogg) free(Bg_music_ogg);
|
if (Bg_music_ogg)
|
||||||
|
free(Bg_music_ogg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/***
|
/***
|
||||||
@ -92,8 +93,11 @@ void gx_audio_Shutdown(void)
|
|||||||
This function retrieves samples for the frame then set the next DMA parameters
|
This function retrieves samples for the frame then set the next DMA parameters
|
||||||
Parameters will be taken in account only when current DMA operation is over
|
Parameters will be taken in account only when current DMA operation is over
|
||||||
***/
|
***/
|
||||||
void gx_audio_Update(int size)
|
void gx_audio_Update(void)
|
||||||
{
|
{
|
||||||
|
/* retrieve audio samples */
|
||||||
|
int size = audio_update() * 4;
|
||||||
|
|
||||||
/* set next DMA soundbuffer */
|
/* set next DMA soundbuffer */
|
||||||
s16 *sb = (s16 *)(soundbuffer[mixbuffer]);
|
s16 *sb = (s16 *)(soundbuffer[mixbuffer]);
|
||||||
mixbuffer ^= 1;
|
mixbuffer ^= 1;
|
||||||
@ -101,11 +105,21 @@ void gx_audio_Update(int size)
|
|||||||
AUDIO_InitDMA((u32) sb, size);
|
AUDIO_InitDMA((u32) sb, size);
|
||||||
|
|
||||||
/* Start Audio DMA */
|
/* Start Audio DMA */
|
||||||
/* this is only called once, DMA being automatically restarted when previous one is over */
|
/* this is only called once to kick-off DMA from external memory to audio interface */
|
||||||
/* If DMA settings are not updated at that time, the same soundbuffer will be played again */
|
/* DMA operation is automatically restarted when all samples have been sent. */
|
||||||
|
/* If DMA settings are not updated at that time, previous sound buffer will be used. */
|
||||||
|
/* Therefore we need to make sure frame emulation is completed before current DMA is */
|
||||||
|
/* completed, either by synchronizing frame emulation with DMA start or by syncing it */
|
||||||
|
/* with Vertical Interrupt and outputing a suitable number of samples per frame. */
|
||||||
|
/* In 60hz mode, VSYNC period is actually 16715 ms which is 802.32 samples at 48kHz. */
|
||||||
if (!audioStarted)
|
if (!audioStarted)
|
||||||
{
|
{
|
||||||
audioStarted = 1;
|
audioStarted = 1;
|
||||||
|
|
||||||
|
/* when not using 60hz mode, frame emulation is synchronized with Audio Interface DMA */
|
||||||
|
if (gc_pal | vdp_pal)
|
||||||
|
AUDIO_RegisterDMACallback(ai_callback);
|
||||||
|
|
||||||
AUDIO_StartDMA();
|
AUDIO_StartDMA();
|
||||||
if (frameticker > 1)
|
if (frameticker > 1)
|
||||||
frameticker = 1;
|
frameticker = 1;
|
||||||
@ -120,20 +134,20 @@ void gx_audio_Update(int size)
|
|||||||
***/
|
***/
|
||||||
void gx_audio_Start(void)
|
void gx_audio_Start(void)
|
||||||
{
|
{
|
||||||
/* shutdown menu audio */
|
/* shutdown background music */
|
||||||
PauseOgg(1);
|
PauseOgg(1);
|
||||||
StopOgg();
|
StopOgg();
|
||||||
|
|
||||||
|
/* shutdown menu audio processing */
|
||||||
ASND_Pause(1);
|
ASND_Pause(1);
|
||||||
ASND_End();
|
AUDIO_StopDMA();
|
||||||
|
AUDIO_RegisterDMACallback(NULL);
|
||||||
|
DSP_Halt();
|
||||||
|
|
||||||
|
/* reset emulation audio processing */
|
||||||
|
memset(soundbuffer, 0, 2 * 3840);
|
||||||
audioStarted = 0;
|
audioStarted = 0;
|
||||||
mixbuffer = 0;
|
mixbuffer = 0;
|
||||||
|
|
||||||
/* reset sound buffers */
|
|
||||||
memset(soundbuffer, 0, 2 * 3840);
|
|
||||||
|
|
||||||
/* By default, use audio DMA to synchronize frame emulation */
|
|
||||||
if (gc_pal | vdp_pal)
|
|
||||||
AUDIO_RegisterDMACallback(ai_callback);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/***
|
/***
|
||||||
@ -145,13 +159,16 @@ void gx_audio_Start(void)
|
|||||||
***/
|
***/
|
||||||
void gx_audio_Stop(void)
|
void gx_audio_Stop(void)
|
||||||
{
|
{
|
||||||
/* restart menu audio (this will automatically stops current audio DMA) */
|
/* restart menu audio processing */
|
||||||
|
DSP_Unhalt();
|
||||||
ASND_Init();
|
ASND_Init();
|
||||||
ASND_Pause(0);
|
ASND_Pause(0);
|
||||||
|
|
||||||
|
/* play background music */
|
||||||
if (Bg_music_ogg && !Shutdown)
|
if (Bg_music_ogg && !Shutdown)
|
||||||
{
|
{
|
||||||
PauseOgg(0);
|
PauseOgg(0);
|
||||||
PlayOgg((char *)Bg_music_ogg, Bg_music_ogg_size, 0, OGG_INFINITE_TIME);
|
PlayOgg((char *)Bg_music_ogg, Bg_music_ogg_size, 0, OGG_INFINITE_TIME);
|
||||||
SetVolumeOgg(((int)config.bgm_volume * 255) / 100);
|
SetVolumeOgg(((int)config.bgm_volume * 255) / 100);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,12 +26,12 @@
|
|||||||
#define _GC_AUDIO_H_
|
#define _GC_AUDIO_H_
|
||||||
|
|
||||||
extern u8 soundbuffer[2][3840];
|
extern u8 soundbuffer[2][3840];
|
||||||
extern u8 mixbuffer;
|
extern u32 mixbuffer;
|
||||||
|
|
||||||
extern void gx_audio_Init(void);
|
extern void gx_audio_Init(void);
|
||||||
extern void gx_audio_Shutdown(void);
|
extern void gx_audio_Shutdown(void);
|
||||||
extern void gx_audio_Start(void);
|
extern void gx_audio_Start(void);
|
||||||
extern void gx_audio_Stop(void);
|
extern void gx_audio_Stop(void);
|
||||||
extern void gx_audio_Update(int size);
|
extern void gx_audio_Update(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -37,7 +37,7 @@
|
|||||||
#define HELD_SPEED 4
|
#define HELD_SPEED 4
|
||||||
|
|
||||||
/* Menu request flag */
|
/* Menu request flag */
|
||||||
u8 ConfigRequested = 0;
|
u32 ConfigRequested = 0;
|
||||||
|
|
||||||
/* Configurable Genesis keys */
|
/* Configurable Genesis keys */
|
||||||
#define KEY_BUTTONA 0
|
#define KEY_BUTTONA 0
|
||||||
|
@ -49,6 +49,6 @@ extern void gx_input_Config(u8 num, u8 type, u8 max_keys);
|
|||||||
extern void gx_input_UpdateEmu(void);
|
extern void gx_input_UpdateEmu(void);
|
||||||
extern void gx_input_UpdateMenu(u32 cnt);
|
extern void gx_input_UpdateMenu(u32 cnt);
|
||||||
|
|
||||||
extern u8 ConfigRequested;
|
extern u32 ConfigRequested;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -48,14 +48,14 @@ extern const u8 Crosshair_p1_png[];
|
|||||||
extern const u8 Crosshair_p2_png[];
|
extern const u8 Crosshair_p2_png[];
|
||||||
|
|
||||||
/*** VI ***/
|
/*** VI ***/
|
||||||
unsigned int *xfb[2]; /* External Framebuffers */
|
u32 *xfb[2]; /* External Framebuffers */
|
||||||
int whichfb = 0; /* Current Framebuffer */
|
u32 whichfb = 0; /* Current Framebuffer */
|
||||||
GXRModeObj *vmode; /* Default Video Mode */
|
GXRModeObj *vmode; /* Default Video Mode */
|
||||||
u8 *texturemem; /* Texture Data */
|
u8 *texturemem; /* Texture Data */
|
||||||
u8 *screenshot; /* Texture Data */
|
u8 *screenshot; /* Texture Data */
|
||||||
|
|
||||||
/* 50/60hz flag */
|
/*** 50/60hz flag ***/
|
||||||
u8 gc_pal = 0;
|
u32 gc_pal = 0;
|
||||||
|
|
||||||
/*** NTSC Filters ***/
|
/*** NTSC Filters ***/
|
||||||
sms_ntsc_t sms_ntsc;
|
sms_ntsc_t sms_ntsc;
|
||||||
@ -1282,14 +1282,13 @@ void gx_video_Start(void)
|
|||||||
gc_pal = 0;
|
gc_pal = 0;
|
||||||
|
|
||||||
/* VSYNC callbacks */
|
/* VSYNC callbacks */
|
||||||
/* in 60hz mode we use VSYNC to synchronize frame emulation */
|
/* in 60hz mode, frame emulation is synchronized with Video Interrupt */
|
||||||
if (!gc_pal && !vdp_pal)
|
if (!gc_pal && !vdp_pal)
|
||||||
VIDEO_SetPreRetraceCallback(vi_callback);
|
VIDEO_SetPreRetraceCallback(vi_callback);
|
||||||
VIDEO_SetPostRetraceCallback(NULL);
|
VIDEO_SetPostRetraceCallback(NULL);
|
||||||
VIDEO_Flush();
|
VIDEO_Flush();
|
||||||
VIDEO_WaitVSync();
|
|
||||||
|
|
||||||
/* interlaced/progressive mode */
|
/* interlaced/progressive Video mode */
|
||||||
if (config.render == 2)
|
if (config.render == 2)
|
||||||
{
|
{
|
||||||
tvmodes[2]->viTVMode = VI_TVMODE_NTSC_PROG;
|
tvmodes[2]->viTVMode = VI_TVMODE_NTSC_PROG;
|
||||||
@ -1306,7 +1305,8 @@ void gx_video_Start(void)
|
|||||||
{
|
{
|
||||||
bitmap.viewport.x = (reg[12] & 1) ? 16 : 12;
|
bitmap.viewport.x = (reg[12] & 1) ? 16 : 12;
|
||||||
bitmap.viewport.y = (reg[1] & 8) ? 0 : 8;
|
bitmap.viewport.y = (reg[1] & 8) ? 0 : 8;
|
||||||
if (vdp_pal) bitmap.viewport.y += 24;
|
if (vdp_pal)
|
||||||
|
bitmap.viewport.y += 24;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -1314,6 +1314,10 @@ void gx_video_Start(void)
|
|||||||
bitmap.viewport.y = 0;
|
bitmap.viewport.y = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* reinitialize video size */
|
||||||
|
vwidth = bitmap.viewport.w + (2 * bitmap.viewport.x);
|
||||||
|
vheight = bitmap.viewport.h + (2 * bitmap.viewport.y);
|
||||||
|
|
||||||
/* software NTSC filters */
|
/* software NTSC filters */
|
||||||
if (config.ntsc == 1)
|
if (config.ntsc == 1)
|
||||||
{
|
{
|
||||||
@ -1348,6 +1352,9 @@ void gx_video_Start(void)
|
|||||||
|
|
||||||
/* reset GX rendering */
|
/* reset GX rendering */
|
||||||
gxResetRendering(0);
|
gxResetRendering(0);
|
||||||
|
|
||||||
|
/* resynchronize emulation with VSYNC*/
|
||||||
|
VIDEO_WaitVSync();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* GX render update */
|
/* GX render update */
|
||||||
@ -1365,11 +1372,14 @@ void gx_video_Update(void)
|
|||||||
|
|
||||||
/* if width has been changed, do no render this frame */
|
/* if width has been changed, do no render this frame */
|
||||||
/* this fixes texture glitches when changing width middle-frame */
|
/* this fixes texture glitches when changing width middle-frame */
|
||||||
if (vwidth != old_vwidth) return;
|
if (vwidth != old_vwidth)
|
||||||
|
return;
|
||||||
|
|
||||||
/* special cases */
|
/* special cases */
|
||||||
if (config.render && interlaced) vheight = vheight << 1;
|
if (config.render && interlaced)
|
||||||
if (config.ntsc) vwidth = (reg[12]&1) ? MD_NTSC_OUT_WIDTH(vwidth) : SMS_NTSC_OUT_WIDTH(vwidth);
|
vheight = vheight << 1;
|
||||||
|
if (config.ntsc)
|
||||||
|
vwidth = (reg[12]&1) ? MD_NTSC_OUT_WIDTH(vwidth) : SMS_NTSC_OUT_WIDTH(vwidth);
|
||||||
|
|
||||||
/* texels size must be multiple of 4 */
|
/* texels size must be multiple of 4 */
|
||||||
vwidth = (vwidth >> 2) << 2;
|
vwidth = (vwidth >> 2) << 2;
|
||||||
@ -1380,14 +1390,17 @@ void gx_video_Update(void)
|
|||||||
GX_InitTexObj(&texobj, texturemem, vwidth, vheight, GX_TF_RGB565, GX_CLAMP, GX_CLAMP, GX_FALSE);
|
GX_InitTexObj(&texobj, texturemem, vwidth, vheight, GX_TF_RGB565, GX_CLAMP, GX_CLAMP, GX_FALSE);
|
||||||
|
|
||||||
/* configure texture filtering */
|
/* configure texture filtering */
|
||||||
if (!config.bilinear) GX_InitTexObjLOD(&texobj,GX_NEAR,GX_NEAR_MIP_NEAR,0.0,10.0,0.0,GX_FALSE,GX_FALSE,GX_ANISO_1);
|
if (!config.bilinear)
|
||||||
|
GX_InitTexObjLOD(&texobj,GX_NEAR,GX_NEAR_MIP_NEAR,0.0,10.0,0.0,GX_FALSE,GX_FALSE,GX_ANISO_1);
|
||||||
|
|
||||||
/* load texture object */
|
/* load texture object */
|
||||||
GX_LoadTexObj(&texobj, GX_TEXMAP0);
|
GX_LoadTexObj(&texobj, GX_TEXMAP0);
|
||||||
|
|
||||||
/* reset TV mode */
|
/* reset TV mode */
|
||||||
if (config.render) rmode = tvmodes[gc_pal*3 + 2];
|
if (config.render)
|
||||||
else rmode = tvmodes[gc_pal*3 + interlaced];
|
rmode = tvmodes[gc_pal*3 + 2];
|
||||||
|
else
|
||||||
|
rmode = tvmodes[gc_pal*3 + interlaced];
|
||||||
|
|
||||||
/* reset aspect ratio */
|
/* reset aspect ratio */
|
||||||
gxResetScaler(vwidth,vheight);
|
gxResetScaler(vwidth,vheight);
|
||||||
@ -1410,8 +1423,10 @@ void gx_video_Update(void)
|
|||||||
draw_square();
|
draw_square();
|
||||||
|
|
||||||
/* LightGun marks */
|
/* LightGun marks */
|
||||||
if (crosshair[0]) gxDrawCrosshair(crosshair[0], input.analog[0][0],input.analog[0][1]);
|
if (crosshair[0])
|
||||||
if (crosshair[1]) gxDrawCrosshair(crosshair[1], input.analog[1][0],input.analog[1][1]);
|
gxDrawCrosshair(crosshair[0], input.analog[0][0],input.analog[0][1]);
|
||||||
|
if (crosshair[1])
|
||||||
|
gxDrawCrosshair(crosshair[1], input.analog[1][0],input.analog[1][1]);
|
||||||
|
|
||||||
/* swap XFB */
|
/* swap XFB */
|
||||||
whichfb ^= 1;
|
whichfb ^= 1;
|
||||||
@ -1427,9 +1442,12 @@ void gx_video_Update(void)
|
|||||||
{
|
{
|
||||||
/* field synchronizations */
|
/* field synchronizations */
|
||||||
VIDEO_WaitVSync();
|
VIDEO_WaitVSync();
|
||||||
if (rmode->viTVMode & VI_NON_INTERLACE) VIDEO_WaitVSync();
|
if (rmode->viTVMode & VI_NON_INTERLACE)
|
||||||
else while (VIDEO_GetNextField() != odd_frame) VIDEO_WaitVSync();
|
VIDEO_WaitVSync();
|
||||||
if (frameticker > 1) frameticker = 1;
|
else while (VIDEO_GetNextField() != odd_frame)
|
||||||
|
VIDEO_WaitVSync();
|
||||||
|
if (frameticker > 1)
|
||||||
|
frameticker = 1;
|
||||||
bitmap.viewport.changed = 0;
|
bitmap.viewport.changed = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,11 +40,11 @@ typedef struct
|
|||||||
} gx_texture;
|
} gx_texture;
|
||||||
|
|
||||||
/* Global variables */
|
/* Global variables */
|
||||||
extern unsigned int *xfb[2];
|
extern u32 *xfb[2];
|
||||||
extern int whichfb;
|
extern u32 whichfb;
|
||||||
extern GXRModeObj *vmode;
|
extern GXRModeObj *vmode;
|
||||||
extern u8 *texturemem;
|
extern u8 *texturemem;
|
||||||
extern u8 gc_pal;
|
extern u32 gc_pal;
|
||||||
|
|
||||||
|
|
||||||
/* GX rendering */
|
/* GX rendering */
|
||||||
|
@ -30,12 +30,13 @@
|
|||||||
#include "dvd.h"
|
#include "dvd.h"
|
||||||
|
|
||||||
#include <fat.h>
|
#include <fat.h>
|
||||||
|
#include <ogc/cast.h>
|
||||||
|
|
||||||
#ifdef HW_RVL
|
#ifdef HW_RVL
|
||||||
#include <wiiuse/wpad.h>
|
#include <wiiuse/wpad.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
u8 Shutdown = 0;
|
u32 Shutdown = 0;
|
||||||
|
|
||||||
#ifdef HW_RVL
|
#ifdef HW_RVL
|
||||||
|
|
||||||
@ -163,19 +164,18 @@ void shutdown(void)
|
|||||||
* M A I N
|
* M A I N
|
||||||
*
|
*
|
||||||
***************************************************************************/
|
***************************************************************************/
|
||||||
u8 fat_enabled = 0;
|
u32 fat_enabled = 0;
|
||||||
u32 frameticker = 0;
|
u32 frameticker = 0;
|
||||||
|
|
||||||
int main (int argc, char *argv[])
|
int main (int argc, char *argv[])
|
||||||
{
|
{
|
||||||
|
CAST_Init();
|
||||||
|
|
||||||
#ifdef HW_RVL
|
#ifdef HW_RVL
|
||||||
/* initialize DVDX */
|
/* initialize DVDX */
|
||||||
DI_Close();
|
|
||||||
DI_Init();
|
DI_Init();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int size;
|
|
||||||
|
|
||||||
/* initialize hardware */
|
/* initialize hardware */
|
||||||
gx_video_Init();
|
gx_video_Init();
|
||||||
gx_input_Init();
|
gx_input_Init();
|
||||||
@ -255,44 +255,40 @@ int main (int argc, char *argv[])
|
|||||||
if (ConfigRequested)
|
if (ConfigRequested)
|
||||||
{
|
{
|
||||||
/* stop video & audio */
|
/* stop video & audio */
|
||||||
gx_video_Stop();
|
|
||||||
gx_audio_Stop();
|
gx_audio_Stop();
|
||||||
|
gx_video_Stop();
|
||||||
|
|
||||||
/* show menu */
|
/* show menu */
|
||||||
MainMenu ();
|
MainMenu ();
|
||||||
ConfigRequested = 0;
|
ConfigRequested = 0;
|
||||||
|
|
||||||
/* start video & audio */
|
/* start video & audio */
|
||||||
/* always restart video first because it setup gc_pal */
|
|
||||||
gx_video_Start();
|
|
||||||
gx_audio_Start();
|
gx_audio_Start();
|
||||||
|
gx_video_Start();
|
||||||
/* reset framesync */
|
|
||||||
frameticker = 1;
|
frameticker = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (frameticker > 1)
|
if (frameticker > 1)
|
||||||
{
|
{
|
||||||
/* skip frame */
|
/* skip frame */
|
||||||
frameticker-=2;
|
system_frame(1);
|
||||||
system_frame (1);
|
--frameticker;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* framesync */
|
while (frameticker < 1)
|
||||||
while (frameticker < 1) usleep(1);
|
usleep(10);
|
||||||
frameticker--;
|
|
||||||
|
|
||||||
/* render frame */
|
/* render frame */
|
||||||
system_frame (0);
|
system_frame(0);
|
||||||
|
--frameticker;
|
||||||
|
|
||||||
|
/* update video */
|
||||||
|
gx_video_Update();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* retrieve audio samples */
|
/* update audio */
|
||||||
size = audio_update();
|
gx_audio_Update();
|
||||||
|
|
||||||
/* update video & audio */
|
|
||||||
gx_video_Update();
|
|
||||||
gx_audio_Update(size * 4);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -49,6 +49,6 @@ extern void legal();
|
|||||||
extern void reloadrom (int size, char *name);
|
extern void reloadrom (int size, char *name);
|
||||||
extern void shutdown();
|
extern void shutdown();
|
||||||
extern u32 frameticker;
|
extern u32 frameticker;
|
||||||
extern u8 Shutdown;
|
extern u32 Shutdown;
|
||||||
|
|
||||||
#endif /* _OSD_H_ */
|
#endif /* _OSD_H_ */
|
||||||
|
Loading…
Reference in New Issue
Block a user