/*
 * @history		2019_12_27  [SDK3.0] タスクをTA_FPU属性付で起動していない不具合を修正 (uC3版) (#2698)
 * @history     2020_07_22  [SDK3.1] GVDが占有するメモリ領域を指定するAPIを追加 (#2965)
 */
#include <stdint.h>

/* uC3 */
#include "kernel.h"
#include "AG903_UC3.h"
#include "ffsys.h"

/* ライブラリ */
#include "bmu/bmumgr.h"
#include "dsp/dspmgr.h"
#include "timr/timrmgr.h"
#include "sys/sscprm.h"

#include "sample_common.h"
#include "gvd_external.h"

static gvdInitializeParam GVDInitParam;				/* GVD初期化パラメータ */
static AG903_DSPMgrWinAttribute *dsp_attr = NULL;	/* ウィンドウアトリビュート */
static uint32_t gWinCallCnt = 0;					/* ウィンドウ管理用コール回数カウンタ */

static uint32_t	gStreamSize[GVD_MAX_STREAM];		/* ストリームサイズ */
static uint32_t	gInBufSize[GVD_MAX_STREAM];			/* 入力バッファ1個当たりのサイズ */
static uint32_t	gInBufPutcount[GVD_MAX_STREAM];		/* 入力バッファ供給数 */
static int32_t		gInBufOffset[GVD_MAX_STREAM];	/* ストリームオフセット位置 */
static bool		gFlagEndOfStream[GVD_MAX_STREAM];	/* ストリーム終端到達フラグ */
static FILE	*fp[GVD_MAX_STREAM] = {NULL};			/* ストリーム入力用 */

extern AG903_DSPMgrHandle	*dsp_handle;			/* 表示ハンドラ */
extern AG903_TIMRMgrHandle	*tim_handle;			/* タイマハンドラ */
extern ER_ID		gWindowTaskID;					/* ウィンドウ制御タスクID */
extern ER_ID		gAsyncTaskID;					/* 非同期処理タスクID */
extern ER_ID		gSemID_BufSync;					/* フレームバッファ用セマフォ */
extern gvdContext	*gGVDctx[GVD_MAX_STREAM];		/* GVDコンテキスト */
extern GVDBuffers	gGVDBufs;						/* GVD用バッファ構造体 */
extern GVDFlags	gFlags[GVD_MAX_STREAM];				/* GVD用フラグ構造体 */

/* ストリーム関連設定値 */
extern char		STREAM_NAME[GVD_MAX_STREAM][GVD_MAX_FILENAME_LEN];
extern uint32_t	STREAM_WIDTH[GVD_MAX_STREAM];
extern uint32_t	STREAM_HEIGHT[GVD_MAX_STREAM];
extern uint32_t	STREAM_REFS[GVD_MAX_STREAM];
/* ウィンドウ関連設定値 */
extern uint32_t	WINDOW_WIDTH[GVD_MAX_STREAM];
extern uint32_t	WINDOW_HEIGHT[GVD_MAX_STREAM];

/* 処理関数 */
static int32_t GVD_setup_module(void);
static int32_t GVD_play_stream(void);
static int32_t GVD_close_module(void);
static void GVD_help();

/* ウィンドウ処理関数 */
static void UpdateWindowAddr(int win_id, uintptr_t addr);
static void AppendBufferInfo(gvdContext *ctx, gvdOutBufInfo buf, gvdTimeStamp ts);
static void WindowUpdate();
static void BufferReset(int window);

/* GVDコールバック関数 */
static int32_t cbGVDDiscardInputBuffer(void *param_gvd, void *param_user, gvdError error);
static int32_t cbGVDDecodeDone(void *param_gvd, void *param_user, gvdError error);
static int32_t cbGVDConnectDone(void *param_gvd, void *param_user, gvdError error);
static int32_t cbGVDDisConnectDone(void *param_gvd, void *param_user, gvdError error);
static int32_t cbGVDPlayAccepted(void *param_gvd, void *param_user, gvdError error);
static int32_t cbGVDForwardAccepted(void *param_gvd, void *param_user, gvdError error);
static int32_t cbGVDDecodeStreamEnd(void *param_gvd, void *param_user, gvdError error);
static int32_t cbGVDPauseDone(void *param_gvd, void *param_user, gvdError error);

/* バッファ管理用構造体 */
static DecodedBufferInfo gBufList[GVD_MAX_STREAM][GVD_OUTBUF_COUNT] = {{{0}}}; /* 書き込み済みバッファ情報リスト */
static uint32_t gBufListNextAdd[GVD_MAX_STREAM] = {0}; /* 書き込み済み出力バッファ管理用 */
static uint32_t gBufListCount[GVD_MAX_STREAM]   = {0}; /* 書き込み済みバッファの個数 */
static uint32_t gBufListCurrent[GVD_MAX_STREAM] = {0}; /* 現在使用中のバッファ番号 */
static uint32_t gBufListNext[GVD_MAX_STREAM]    = {0}; /* 次回使用するバッファ番号 */
static uint32_t gBufListPrev[GVD_MAX_STREAM]    = {0}; /* 次回使用するバッファ番号 */

static gvdOutBufInfo gOutbufinfo[GVD_MAX_STREAM][GVD_OUTBUF_COUNT]; /* 出力バッファ情報 */

static volatile uint32_t gCurrentFrame[GVD_MAX_STREAM] = {0}; /* 現在表示中のフレームID */


/* 非同期処理タスク
 * これ以外のタスクで余った時間にコールされる
 * デコーダの制御も行うため、コールされる頻度が落ちるとデコード性能に影響します */
extern void async_tsk(VP_INT);
static const T_CTSK ctsk_async = {
	TA_HLNG | TA_ACT | TA_FPU, (VP_INT)0, (FP)async_tsk, 9, 0x800, 0, "Async"
};

/* ウィンドウ制御タスク
 * フレームバッファの管理とフレーム表示時間の制御 */
void window_tsk_ext(VP_INT);
static const T_CTSK ctsk_window = {
    TA_HLNG | TA_ACT | TA_FPU, (VP_INT)0, (FP)window_tsk_ext, 3, 0x400, 0, "Window"
};

/* 外部入力バッファサンプルメインルーチン */
int32_t GVD_trickplay_external(void)
{
	int32_t rc = AG903_ENONE;
	int32_t idx;
	for(idx=0; idx<GVD_MAX_STREAM; idx++) {
		gFlags[idx].quit = false;
	}

	rc = GVD_setup_module();

	if(rc == AG903_ENONE) { rc = GVD_play_stream(); }


	return rc;
}

/* GVD初期化 */
static int32_t GVD_setup_module(void)
{
	int32_t err;
	int32_t idx;

	GVDInitParam.func_notify_fatalerror = (gvdCbFunc)(GVD_cbGVDFatalerror);
	GVDInitParam.param_notify_fatalerror = NULL;
	GVDInitParam.vramaddr_mask = 0x00000000;
	GVDInitParam.bmuaddr_mask = 0x00000000;
	GVDInitParam.stream_buf_size = GVD_INBUF_SIZE;
	GVDInitParam.stream_buf_num = GVD_INBUF_COUNT;
	GVDInitParam.stream_num = GVD_MAX_STREAM;
	err = GVD_InitInitializeParam(&GVDInitParam);
	if (err != GVD_ERR_SUCCESS) {
	    return -AG903_EINVAL;
	}

	gGVDBufs.work.size = GVD_GetRequiredMemorySize();
	gGVDBufs.work.addr = sys_memalign(gGVDBufs.work.size, 16<<10);

	err = GVD_SetWorkMemory(gGVDBufs.work.addr,
							gGVDBufs.work.size);
	if (err != GVD_ERR_SUCCESS) {
	    return -AG903_EINVAL;
	}

	GVD_SetFatalErrorAddress((void *)0xFE007000);
	GVD_SetOccupyMemoryAddress((void *)0x83FF0000);

	/* GVD用ヒープメモリは2KB以上のアラインが必須 */
	gGVDBufs.heap.size = GVD_HEAP_SIZE;
	gGVDBufs.heap.addr = sys_memalign(gGVDBufs.heap.size, 16<<10);

	err = GVD_Initialize();
	if (err != GVD_ERR_SUCCESS) {
	    return -AG903_EINVAL;
	}

	/* コールバックの設定 */
	gvdCbFuncs cbFuncs;
	{
		int32_t i;
		int32_t size = (int32_t)sizeof(gvdCbFuncs);
		for(i=0; i<size; i++)
		{
			*((uint8_t *)(&cbFuncs) + i) = 0x00;
		}
	}
	cbFuncs.discard_input_buffer =	(gvdCbFunc)cbGVDDiscardInputBuffer;
	cbFuncs.commit_output_buffer =	(gvdCbFunc)cbGVDDecodeDone;
	cbFuncs.connect_done		=	(gvdCbFunc)cbGVDConnectDone;
	cbFuncs.disconnect_done		=	(gvdCbFunc)cbGVDDisConnectDone;
	cbFuncs.play_accepted		=	(gvdCbFunc)cbGVDPlayAccepted;
	cbFuncs.forward_accepted	=	(gvdCbFunc)cbGVDForwardAccepted;
	cbFuncs.decode_stream_end	=	(gvdCbFunc)cbGVDDecodeStreamEnd;
	cbFuncs.pause_done 			=	(gvdCbFunc)cbGVDPauseDone;



	/* コールバックパラメータの設定 */
	uint32_t	total = 0;
	uint32_t	heapsize[2];
	uint8_t		*addr = gGVDBufs.heap.addr;
	for (idx=0; idx<(int32_t)GVDInitParam.stream_num; idx++)
	{

		gvdCbParams cbParams;
		{
			int32_t i;
			int32_t size = (int32_t)sizeof(gvdCbParams);
			for(i=0; i<size; i++)
			{
				*((uint8_t *)(&cbParams) + i) = 0x00;
			}
		}
		cbParams.decode_stream_end = (void *)&gGVDctx[idx];
		cbParams.commit_output_buffer = (void *)&gGVDctx[idx];
		cbParams.rewind_done = (void *)&gGVDctx[idx];
		cbParams.discard_input_buffer = (void *)&gGVDctx[idx];

		/* コンテキストの取得 */
		err = GVD_CreateContext(&gGVDctx[idx]);
		if(err) GVD_ErrPrintf("ERROR: GVD_CreateContext():L%d\r\n", __LINE__);

		/* コールバックの登録 */
		err = GVD_SetCallbackFunctions(gGVDctx[idx], &cbFuncs);
		if(err) GVD_ErrPrintf("ERROR: GVD_SetCallbackFunctions():L%d\r\n", __LINE__);
		err = GVD_SetCallbackParams(gGVDctx[idx], &cbParams);
		if(err) GVD_ErrPrintf("ERROR: GVD_SetCallbackParams():L%d\r\n", __LINE__);

		/* 出力先にVRAMを指定 */
		err = GVD_SelectOutputModule(gGVDctx[idx], GVD_OUTMOD_VRAM);
		if(err) GVD_ErrPrintf("ERROR: GVD_SelectOutputModule():L%d\r\n", __LINE__);

		/* デコーダヒープの割り当て */
		heapsize[idx] = GVD_ALIGNn(2048UL, (uint32_t)(2.5
													  * GVD_ALIGNn(128UL, STREAM_WIDTH[idx])
													  * GVD_ALIGNn(16UL, STREAM_HEIGHT[idx])
													  *(STREAM_REFS[idx]+1)));
		total += heapsize[idx];

		if(total > GVD_HEAP_SIZE) {
			GVD_ErrPrintf("Error: Not enough GVD heap memory.\r\n");
			return -AG903_ENOMEM;
		}
		err = GVD_SetOutputHeap(gGVDctx[idx], addr, heapsize[idx]);
		addr += heapsize[idx];
	}

	/* 出力バッファのサイズ算出 */
	uint32_t stream_stride[GVD_MAX_STREAM];
	for(idx=0; idx<GVD_MAX_STREAM; idx++)
	{
		if(STREAM_HEIGHT[idx] % 16 == 0)
		{
			stream_stride[idx] =
				STREAM_WIDTH[idx] * STREAM_HEIGHT[idx] * 2;
		}
		else
		{

			stream_stride[idx] =
				STREAM_WIDTH[idx]
				* (STREAM_HEIGHT[idx] + (16 - STREAM_HEIGHT[idx] % 16))
				* 2;
		}
	}
	int32_t cnt;
	/* 出力バッファを確保（GVD_OUTBUF_COUNT[枚/ストリーム]） */
	for(idx=0; idx<GVD_MAX_STREAM; idx++)
	{
		for(cnt=0; cnt<GVD_OUTBUF_COUNT; cnt++)
		{
			/*  */
			gOutbufinfo[idx][cnt].buffer = sys_memalign(stream_stride[idx], 1024);
			gOutbufinfo[idx][cnt].size_buffer = stream_stride[idx];
			gOutbufinfo[idx][cnt].height = STREAM_HEIGHT[idx];
			gOutbufinfo[idx][cnt].width = STREAM_WIDTH[idx];
			if(gOutbufinfo[idx][cnt].buffer == NULL)
			{
				GVD_ErrPrintf("ERROR: [Outputbuf] allocate faild.\r\n");
				return -AG903_ENOMEM;
			}
		}
	}

	/* 初回表示フレームバッファの初期化 */
	for(idx=0; idx<GVD_MAX_STREAM; idx++)
	{
		uint32_t cnt;
		uint32_t *p = (uint32_t *)gOutbufinfo[idx][GVD_OUTBUF_COUNT-1].buffer;
		for(cnt=0; cnt<stream_stride[idx]>>2; cnt++)
		{
			*(p + cnt) = 0x00000000;
		}

		/* 初回表示バッファを表示待ちキューに追加 */
		{
			gvdTimeStamp ts = {0};
			BufferReset(idx);
			AppendBufferInfo(gGVDctx[idx], gOutbufinfo[idx][GVD_OUTBUF_COUNT-1], ts);
		}
	}

	/* 入出力バッファ数を設定 */
	for(idx=0; idx<GVD_MAX_STREAM; idx++)
	{
	    if(GVD_ERR_SUCCESS != GVD_SetMaxNumInputBuffers(gGVDctx[idx], GVD_INBUF_COUNT))
			GVD_ErrPrintf("ERROR: GVD_SetMaxNumInputBuffers(): L%d\r\n", __LINE__);
	    if(GVD_ERR_SUCCESS != GVD_SetMaxNumOutputBuffers(gGVDctx[idx], GVD_OUTBUF_COUNT))
			GVD_ErrPrintf("ERROR: GVD_SetMaxNumOutputBuffers(): L%d\r\n", __LINE__);
	}

	/* ウィンドウを有効にする */
	{
		err = AG903_DSPMgrGetAttribute(dsp_handle, 0, &dsp_attr);
			for(idx=0; idx<GVD_MAX_STREAM; idx++)
				dsp_attr[idx].conf.valid = true;
		}

	return err;
}

/* 動画ストリームの再生 */
static int32_t GVD_play_stream(void)
{
	int32_t idx, cnt;
	int32_t err = 0;

	/* 非同期処理開始 */
	gAsyncTaskID = acre_tsk((T_CTSK*)&ctsk_async);

	/* 表示更新処理開始 */
	gWindowTaskID = acre_tsk((T_CTSK*)&ctsk_window);

	/* デコーダ接続処理 */
	for(idx=0; idx<GVD_MAX_STREAM; idx++)
	{
		/* 入力バッファ管理変数の初期化 */
		gInBufPutcount[idx] = 0;

		err = GVD_Connect(gGVDctx[idx], &gGVDctx[idx]);
	    if (err != AG903_ENONE)
	    {
			GVD_ErrPrintf("ERROR: GVD_Connect failed\n");
			return err;
	    }
		while(gFlags[idx].connect == false){ sys_dlytsk(10); };

		/* 出力バッファを供給 */
		for(cnt=0; cnt<GVD_OUTBUF_COUNT-1; cnt++)
		{
			err = GVD_PutOutputBuffer(gGVDctx[idx], &gOutbufinfo[idx][cnt], &gGVDctx[idx]);
			if(err)
				GVD_ErrPrintf("ERROR: GVD_PutOutputBuffer : %d\n", err);
		}
	}

	/* 動画ストリームのオープン */
	for (idx=0; idx<GVD_MAX_STREAM; idx++)
	{
		fp[idx] = fopen(STREAM_NAME[idx], "r");
		if(fp[idx] == NULL) {
			GVD_ErrPrintf("Error: Stream open failed.\r\n");
			return -AG903_ENOENT;
		}
		struct stat stat;
		fstat(fp[idx], &stat);
		gStreamSize[idx] = stat.st_size;
		gInBufOffset[idx] = 0;

		/* 初回入力バッファ供給 */
		int32_t cnt;
		gInBufSize[idx] = (GVD_INBUF_LIMIT_SIZE[idx]/GVD_INBUF_COUNT/GVD_MAX_STREAM);
		for(cnt=0; cnt<GVD_INBUF_COUNT; cnt++)
		{
			uint32_t rsize;
			gvdInBufInfo inbuf = {0};

			inbuf.size_buffer = gInBufSize[idx];
			inbuf.buffer = sys_memalign(inbuf.size_buffer, GVD_INBUF_ALIGNMENT);
			if(inbuf.buffer == NULL) {
				/* 入力バッファが供給できない */
				int32_t lp;
				GVD_ErrPrintf("Error: Input buffer alloc failed.\r\n");
				/* 全コンテキストの終了フラグを立てる */
				for(lp=0; lp<GVD_MAX_STREAM; lp++) {
					gFlags[lp].quit = true;
				}
				goto label_finalize;
			}
			//inbuf.offset_data = gInBufOffset[idx];
			inbuf.offset_data = 0;

			rsize = fread(inbuf.buffer, 1, inbuf.size_buffer, fp[idx]);
			inbuf.size_data = rsize;
			if(gInBufOffset[idx] == 0)
				inbuf.flags |= GVD_BUFFLAG_START;
			if(gInBufOffset[idx]+rsize >= gStreamSize[idx])
				inbuf.flags |= GVD_BUFFLAG_END;
			gInBufOffset[idx] += rsize;


			err = GVD_PutInputBuffer(gGVDctx[idx], &inbuf, &gGVDctx[idx]);
			gInBufPutcount[idx]++;
		}
	}

	/* 再生 */
	for (idx=0; idx<GVD_MAX_STREAM; idx++)
	{
	    GVD_Play(gGVDctx[idx], &gGVDctx[idx]);
	}

	GVD_help();
	while(1)
	{
		char input[3];
		uint32_t cmd, spd;
		int32_t idx;

		if(GVD_isAllQuit()) break;

		GVD_Printf("Command:" );
		COM_GetNum_Wait(input, 3);	/* コマンド入力要求 */
        cmd = ASCtoBIN(input[0]);	/* 入力コマンド取得 */

		switch(cmd) {
			case 0:
				GVD_help();
				break;
			case 1:
				GVD_Pause(gGVDctx[1], &gGVDctx[1]);
				break;
			case 2:
				GVD_Play(gGVDctx[1], &gGVDctx[1]);
				break;
			case 3:
				GVD_Printf("--- Input forward speed(skip frames). (1h to Fh): ");
				COM_GetNum_Wait(input, 3);	/* 早送り速度入力要求 */
				spd = ASCtoBIN(input[0]);	/* 入力速度取得 */
				GVD_Forward(gGVDctx[1], spd, &gGVDctx[1]);
				break;
			case 4:
				for(idx=0; idx<(int32_t)GVDInitParam.stream_num; idx++) {
					if(!gFlags[idx].paused) {
						GVD_Pause(gGVDctx[idx], &gGVDctx[idx]);
						while(1){
							if(gFlags[idx].paused) break;
							sys_dlytsk(100);
						}
					}
					gFlags[idx].quit = true;
				}
		}
	}

label_finalize:

	sys_dlytsk(500); /* 他タスク終了猶予時間 */
	err = GVD_close_module();

	return err;
}

/* 操作説明 */
static void GVD_help()
{
	GVD_Printf("Command: %X:HELP\r\n",		0);
	GVD_Printf("       : %X:PAUSE\r\n",		1);
	GVD_Printf("       : %X:PLAY\r\n",		2);
	GVD_Printf("       : %X:FORWARD\r\n",	3);
	GVD_Printf("       : %X:EXIT\r\n",		4);
}

/* GVDの終了 */
static int32_t GVD_close_module(void)
{
	int32_t rc = AG903_ENONE;
	rc = AG903_DSPMgrSetAttribute(dsp_handle, 0);


	int32_t idx, cnt;

	for(idx=0; idx<(int32_t)GVDInitParam.stream_num; idx++)
	{
		if(fp[idx] != NULL) {
			if(fclose(fp[idx]) == 0)
				fp[idx] = NULL;
		}

		rc = GVD_Disconnect(gGVDctx[idx], &gGVDctx[idx]);
		if(rc) return rc;
	    rc = GVD_DestroyContext(gGVDctx[idx]);
	    if(rc) return rc;

		/* 出力バッファ解放 */
		for(cnt=0; cnt<GVD_OUTBUF_COUNT; cnt++) {
			sys_free(gOutbufinfo[idx][cnt].buffer);
			gOutbufinfo[idx][cnt].buffer = NULL;
			gOutbufinfo[idx][cnt].width = 0;
			gOutbufinfo[idx][cnt].height = 0;
			gOutbufinfo[idx][cnt].size_buffer = 0;
		}
	}
	/* デコーダヒープ解放 */
	sys_free(gGVDBufs.heap.addr);
	gGVDBufs.heap.addr = NULL;
	/* ワークバッファ解放 */
	sys_free(gGVDBufs.work.addr);
	gGVDBufs.work.addr = NULL;

	/* タスク削除 */
	if(gWindowTaskID >= 0) {
		del_tsk(gWindowTaskID);
		gWindowTaskID = -1;
	}
	if(gAsyncTaskID >= 0) {
		del_tsk(gAsyncTaskID);
		gAsyncTaskID = -1;
	}

	rc = GVD_Finalize();
	return rc;
}

/* ウィンドウ制御タスク */
void window_tsk_ext(VP_INT exinf)
{
	((void)exinf);
	while(!GVD_isAllQuit())
	{
	    ER_ID rc = wai_sem(gSemID_BufSync);
		if(rc == E_OK) {
			WindowUpdate();
	    	sig_sem(gSemID_BufSync);
		}
	    slp_tsk();
	}

	GVD_Printf("Window task done.\r\n");
	ext_tsk();
}

/* 表示するフレームバッファアドレスの更新 */
static void UpdateWindowAddr(int win_id, uintptr_t addr)
{
	dsp_attr[win_id].framebuffer_base = (uint32_t)addr;
}

/* デコード済みバッファの追加 */
static void AppendBufferInfo(gvdContext *ctx, gvdOutBufInfo buf, gvdTimeStamp ts)
{
	if(E_OK != wai_sem(gSemID_BufSync))
		GVD_ErrPrintf("ERROR: wai_sem(): L%d\r\n", __LINE__);

	DecodedBufferInfo dbi = {ctx, buf, ts};
	uint32_t wID = GVD_getCID(ctx);
	if(wID > GVD_MAX_STREAM) return;

	gBufList[wID][gBufListNextAdd[wID]] = dbi;
	gBufListNextAdd[wID]++;
	if(gBufListNextAdd[wID] == GVD_OUTBUF_COUNT)
	    gBufListNextAdd[wID] = 0;
	gBufListCount[wID]++;

	if(E_OK != sig_sem(gSemID_BufSync))
		GVD_ErrPrintf("ERROR: sig_sem(): L%d\r\n", __LINE__);
}

/* 表示の更新 */
static void WindowUpdate()
{
	int32_t w = gWinCallCnt == 0 ? 0 : 1;
	if(gWinCallCnt == 0) gWinCallCnt = 1;
	else gWinCallCnt = 0;

	if(gBufListCount[w] > 0)
	{

		/* 表示フレームの更新 */
		UpdateWindowAddr(GVD_getCID(gBufList[w][gBufListCurrent[w]].ctx),
						 (uintptr_t)gBufList[w][gBufListCurrent[w]].buf.buffer);
		gCurrentFrame[w] = gBufList[w][gBufListCurrent[w]].ts.frame_id;

		/* 使い終えた出力バッファの再供給 */
		if(gBufListPrev[w] != gBufListCurrent[w])
		{
			GVD_PutOutputBuffer(gBufList[w][gBufListPrev[w]].ctx,
								&gBufList[w][gBufListPrev[w]].buf,
								NULL);
			gBufList[w][gBufListPrev[w]].buf.buffer = NULL;
		}

		gBufListPrev[w] = gBufListCurrent[w];
		gBufListCurrent[w] = gBufListNext[w];
		gBufListNext[w]++;
		if(gBufListNext[w] == GVD_OUTBUF_COUNT)
			gBufListNext[w] = 0;

		gBufListCount[w]--;

	}
	return;
}

/* バッファ管理変数の初期化 */
static void BufferReset(int window)
{
	gBufListNextAdd[window]	= GVD_OUTBUF_COUNT-1;
	gBufListCount[window]	= 0;
	gBufListCurrent[window]	= GVD_OUTBUF_COUNT-1;
	gBufListNext[window]	= 0;
	gBufListPrev[window]	= GVD_OUTBUF_COUNT-1;
}

/* GVDの入力バッファ破棄コールバック（入力バッファの返却） */
static int32_t cbGVDDiscardInputBuffer(void *param_gvd, void *param_user, gvdError error)
{
	((void)(error));
	gvdDiscardBufInfo	*DscdBufinfo	= (gvdDiscardBufInfo *)param_gvd;
	gvdContext			*ctx			= *(gvdContext **)param_user;
	uint32_t			cid				= GVD_getCID(ctx);
	gvdInBufInfo		inbuf			= {0};

	sys_free(DscdBufinfo->buffer);
	gInBufPutcount[cid]--;

	if(gFlags[cid].quit == false) {
		uint32_t rsize;
		inbuf.size_buffer = gInBufSize[cid];
		inbuf.buffer = sys_memalign(inbuf.size_buffer, GVD_INBUF_ALIGNMENT);
		if(inbuf.buffer == NULL) {
			GVD_ErrPrintf("Error: Input buffer alloc failed.\r\n");
		}
		else {
			rsize = fread(inbuf.buffer, 1, inbuf.size_buffer, fp[cid]);
			inbuf.size_data = rsize;
			inbuf.offset_data = 0;
			if(rsize > 0) {
				if(gInBufOffset[cid] == 0)
					inbuf.flags |= GVD_BUFFLAG_START;
				if(gInBufOffset[cid]+rsize == gStreamSize[cid])
					inbuf.flags |= GVD_BUFFLAG_END;
				gInBufOffset[cid] += rsize;
			}
			else {
				if(!gFlagEndOfStream[cid]) gFlagEndOfStream[cid] = true;
				else return GVD_ERR_SUCCESS;
			}
			GVD_PutInputBuffer(gGVDctx[cid], &inbuf, &gGVDctx[cid]);
			gInBufPutcount[cid]++;
		}
	}

	return 0;
}

/* GVDのデコード完了コールバック（出力バッファの返却） */
static int32_t cbGVDDecodeDone(void *param_gvd, void *param_user, gvdError error)
{
	((void)(error));
	gvdOutBufInfo	*outbuf		= NULL;
	gvdContext		*ctx		= *(gvdContext **)param_user;
	gvdFrameInfo	*frameInfo	= (gvdFrameInfo *)param_gvd;
	gvdTimeStamp	ts			= {frameInfo->timestamp, frameInfo->frame_count, 0};

	/* 返却されたgvdFrameInfoと同じアドレスを持つgvdOutBufInfoを検索する */
	int32_t idx;
	for (idx=0; idx<GVD_OUTBUF_COUNT; idx++)
	{
		if(gOutbufinfo[GVD_getCID(ctx)][idx].buffer == frameInfo->buffer)
	    {
			outbuf = &gOutbufinfo[GVD_getCID(ctx)][idx];
			break;
		}
	}

	/* デコード済みバッファを追加 */
	AppendBufferInfo(ctx, *outbuf, ts);

	return 0;
}

/* GVDのデコーダ初期化完了コールバック */
static int32_t cbGVDConnectDone(void *param_gvd, void *param_user, gvdError error)
{
	((void)(param_gvd));
	((void)(error));
	gvdContext *ctx = *(gvdContext **)param_user;
	uint32_t cid = GVD_getCID(ctx);
	gFlags[cid].paused = true;
	gFlags[cid].quit = false;
	gFlags[cid].playdone = false;
	gFlags[cid].connect = true;
	return 0;
}

/* GVDのデコーダ終了処理完了コールバック */
static int32_t cbGVDDisConnectDone(void *param_gvd, void *param_user, gvdError error)
{
	((void)(param_gvd));
	((void)(error));
	gvdContext *ctx = *(gvdContext **)param_user;
	uint32_t cid = GVD_getCID(ctx);
	gFlags[cid].quit = true;
	gFlags[cid].connect = false;
	return 0;
}

/* GVDの再生要求受付コールバック */
static int32_t cbGVDPlayAccepted(void *param_gvd, void *param_user, gvdError error)
{
	((void)(param_gvd));
	((void)(error));
	gvdContext *ctx = *(gvdContext **)param_user;
	uint32_t cid = GVD_getCID(ctx);
	gFlags[cid].paused = false;
	gFlags[cid].playdone = false;
	return 0;
}

/* GVDの早送り要求受付コールバック */
static int32_t cbGVDForwardAccepted(void *param_gvd, void *param_user, gvdError error)
{
	((void)(param_gvd));
	((void)(error));
	gvdContext *ctx = *(gvdContext **)param_user;
	uint32_t cid = GVD_getCID(ctx);
	gFlags[cid].paused = false;
	gFlags[cid].playdone = false;
	return 0;
}

/* GVDのストリーム終端到達コールバック */
static int32_t cbGVDDecodeStreamEnd(void *param_gvd, void *param_user, gvdError error)
{
	((void)(param_gvd));
	((void)(error));
	gvdContext *ctx = *(gvdContext **)param_user;
	uint32_t cid = GVD_getCID(ctx);
	gFlags[cid].paused = true;
	gFlags[cid].playdone = true;

	/* ストリーム先頭へシーク */
	fseek(fp[cid], 0, SEEK_SET);
	gInBufOffset[cid] = 0;

	/* ループ再生 */
	GVD_Play(gGVDctx[cid], &gGVDctx[cid]);

	/* 入力バッファ供給 */
	while(gInBufPutcount[cid] < GVD_INBUF_COUNT)
	{
		uint32_t rsize;
		gvdInBufInfo inbuf = {0};

		inbuf.size_buffer = gInBufSize[cid];
		inbuf.buffer = sys_memalign(inbuf.size_buffer, GVD_INBUF_ALIGNMENT);
		if(inbuf.buffer == NULL) {
			/* 入力バッファが供給できない */
			int32_t lp;
			GVD_ErrPrintf("Error: Input buffer alloc failed.\r\n");
			/* 全コンテキストの終了フラグを立てる */
			for(lp=0; lp<GVD_MAX_STREAM; lp++) {
				gFlags[lp].quit = true;
			}
			return GVD_ERR_RESOURCE_NOT_ENOUGH;
		}

		rsize = fread(inbuf.buffer, 1, inbuf.size_buffer, fp[cid]);
		inbuf.size_data = rsize;
		inbuf.offset_data = 0;
		if(rsize > 0) {
			if(gInBufOffset[cid] == 0)
				inbuf.flags |= GVD_BUFFLAG_START;
			if(gInBufOffset[cid]+rsize == gStreamSize[cid])
				inbuf.flags |= GVD_BUFFLAG_END;
			gInBufOffset[cid] += rsize;
		}
		else {
			if(!gFlagEndOfStream[cid]) gFlagEndOfStream[cid] = true;
			else return GVD_ERR_SUCCESS;
		}
		GVD_PutInputBuffer(gGVDctx[cid], &inbuf, &gGVDctx[cid]);
		gInBufPutcount[cid]++;	}

	return 0;
}

/* GVDの一時停止完了コールバック */
static int32_t cbGVDPauseDone(void *param_gvd, void *param_user, gvdError error)
{
	((void)(param_gvd));
	((void)(error));
	gvdContext *ctx = *(gvdContext **)param_user;
	uint32_t cid = GVD_getCID(ctx);
	gFlags[cid].paused = true;
	return 0;
}
