/*
 * This program was created by AXELL CORPORATION.
 * Copyright (C) 2017-2019 AXELL CORPORATION, all rights reserved.
 *************************************************************************************
 * @brief		JPG decode code
 * @file		decode.c
 * @auther		AXELL
 * @date
 * @description
 * @note
 * @history		2019_12_27  [SDK3.0] サンプルのメッセージ出力をASCIIコードのみに変更 (#2728)
*/
#include "jpg_sample.h"
#include "int/intmgr.h"
#include "dsp/dspmgr.h"
#include "jpg/jpg.h"
#include "bmu/bmumgr.h"
#include "sys/spcprm.h"
#include "sys/sscprm.h"
#include "../dsp/dsp_common.h"

/* Param */
#define	VOD_WIDTH				(1280)					/* 解像度・横幅			*/
#define	VOD_HEIGHT				(1024)					/* 解像度・縦幅			*/
#define VOD_SSAMP_NONSUPPORT 0
#define VOD_SSAMP_422        1
#define VOD_SSAMP_420        2

/* データ */
static AG903_JPGHooks		gHook[AG903_JPG_HOOK_MAX];
static AG903_DSPMgrHandle		*gDspHdl;
static AG903_BMUMgrHandle		*gBmuHdl;
static int32_t					gIntHdlID;
static uint32_t					gJpgFlag=0;

/* Macro */
#define	GETSEQ()			(gJpgFlag)
#define	SETSEQ(SEQ)			(GETSEQ() |= (SEQ))
#define	CLRSEQ(SEQ)			(GETSEQ() &= ~(SEQ))
#define	WAITSEQ(SEQ, T)		do { sys_dlytsk(T); } while (0 == (gJpgFlag & (SEQ)))

/* プロトタイプ */
static int32_t jpg_create_YUVfile(void);
static int32_t jpg_display_image(void);
static int32_t jpg_decode_repeat(void);
static int32_t jpg_read_jpg(char* name, uint8_t* buf, uint32_t bufsize, uint32_t* size, uint32_t* width, uint32_t* height);
static int32_t jpg_read_mov(char* name, uint8_t* buf, uint32_t bufsize, uint32_t* size);
static int32_t jpg_write_yuv(char* name, uint8_t* buf, uint32_t width, uint32_t height);
static int32_t jpg_is_decodable(uint8_t* buf, uint32_t size);
static void* jpg_search_marker(uint8_t* buf, uint8_t marker, uint32_t size);
static int32_t jpg_get_pixcel_size(uint8_t *buf, uint32_t size, uint32_t *width, uint32_t *height);
static int32_t jpg_setup_bmu(void* buf, uint32_t bufsize);
static int32_t jpg_getaddr_bmu(void* srcaddr, void* snkaddr);
static int32_t jpg_term_bmu(void);
static int32_t jpg_setup_dsp(uint8_t* wbase, uint8_t* buf);
static int32_t jpg_enable_dsp(void);
static int32_t jpg_term_dsp(void);
static int32_t jpg_setup_dsp_display_sync(void);
static int32_t jpg_setup_dsp_option(void);
static int32_t jpg_setup_dsp_window(uint8_t* wbase, uint8_t* buf);
static int32_t jpg_set_dsp_window(uint32_t width, uint32_t height);
static void jpg_set_callback(AG903_JPGHandle* handle);
static void jpg_del_callback(AG903_JPGHandle* handle);
static void jpg_clbk(AG903_JPGHandle *handle, uint32_t param);

extern AG903_SPCPrmPllnParam	DSP_SAMPLE_PLL[];
extern AG903_DSPMgrSyncParam	DSP_SAMPLE_SYNCPARAM[];

/**
 * @brief	デコード処理メイン
 * @param	mode  [in] 処理モード
 * @param	param [in] 処理別パラメータ
 * @return	none
 * @note	none
*/
void decode_main(uint8_t mode, uint32_t param)
{
	((void)param);

	CLRSEQ(JPG_FLAG_ALL);

	switch (mode) {
		case 0:	/* Decode(JPEG to YUV) */
			jpg_create_YUVfile();
			break;
		case 1:	/* Decode(Display image) */
			jpg_display_image();
			break;
		case 2:	/* Decode(JPEG to YUV Repeat & Display image) */
			jpg_decode_repeat();
			break;
		default:
			break;
	}

	return;
}

/**
 * @brief	デコード処理 (YUVファイル作成)
 * @return	AG903_ENONE		正常終了
 * @return	AG903_ENONE以外	エラーコード
 * @note	none
 */
static int32_t jpg_create_YUVfile(void)
{
	AG903_JPGHandle* 	handle;
	AG903_JPGCtrlParam	ini_param;
	AG903_JPGCodecParam	set_param;
	int32_t		ret = AG903_ENONE;
	uint32_t	width, height, size;
	uint8_t*	inbuf  = NULL;
	uint8_t*	outbuf = NULL;
	uint8_t		ccomp;


	ret = AG903_JPGGetHandle(&handle);
	if(AG903_ENONE != ret) {
		JPG_ErrPrintf("ERROR: Handle get");
		return -AG903_EFAULT;
	}
	jpg_set_callback(handle);

	/* メモリ確保 */
	inbuf  = (uint8_t *)sys_memalign(JPG_JPGBUF_SIZE, 4);
	outbuf = (uint8_t *)sys_memalign(JPG_YUVBUF_SIZE, 4);
	if((NULL == inbuf) || (NULL == outbuf)) {
		JPG_ErrPrintf("ERROR: Memory alloc");
		return -AG903_EFAULT;
	}

	do {
		/* JPEGファイルRead */
		ret = jpg_read_jpg((char*)JPG_INPUT_DEC_PATH, inbuf, JPG_JPGBUF_SIZE, &size, &width, &height);
		if(AG903_ENONE != ret) {
			JPG_ErrPrintf("ERROR: File read");
			break;
		}

		/* ヘッダの情報によるデコード可否チェック */
		ret = jpg_is_decodable(inbuf, size);
		if(AG903_ENONE != ret) {
			JPG_ErrPrintf("ERROR: Non-support JPEG format");
			break;
		}

		/* コーデック初期化 */
		ini_param.operation = AG903_JPG_ENDEC_DECODE;
		ini_param.interval  = 8;
		ini_param.SOSstop   = false;
		ret =  AG903_JPGInitCodec(handle, &ini_param);
		if(AG903_ENONE != ret) {
			JPG_ErrPrintf("ERROR: Codec init");
			break;
		}
		WAITSEQ(JPG_FLAG_INIT_CODEC|JPG_FLAG_ERR_CODEC, 0);
		if(JPG_FLAG_ERR_CODEC & GETSEQ()) {
			JPG_ErrPrintf("ERROR: Codec init wait");
			break;
		}

		/* コーデック処理 */
		set_param.capture    = false;
		set_param.buf_flow   = false;
		set_param.flame_skip = false;
		set_param.h_size     = 0;
		set_param.v_size     = 0;
		set_param.in_buf     = (void*)(inbuf);
		set_param.out_buf    = (void*)(outbuf);
		set_param.strm_size  = size;
		set_param.stride     = width;
		ret = AG903_JPGStartCodec(handle, &set_param);
		if(AG903_ENONE != ret) {
			JPG_ErrPrintf("ERROR: Codec start");
			break;
		}
		WAITSEQ(JPG_FLAG_END_CODEC|JPG_FLAG_ERR_CODEC, 0);
		if(JPG_FLAG_ERR_CODEC & GETSEQ()) {
			JPG_ErrPrintf("ERROR: Codec end wait");
			break;
		}

		/* コーデック動作終了 */
		ret = AG903_JPGEndCodec(handle);
		if(AG903_ENONE != ret) {
			JPG_ErrPrintf("ERROR: Codec end");
			break;
		}

		/* JPGデコード情報取得 */
		ret = AG903_JPGGetColorComponent(handle, &ccomp);
		if(AG903_ENONE != ret) {
			JPG_ErrPrintf("ERROR: Color component get");
			break;
		}
		JPG_Printf(" Format : %s\r\n", (ccomp == 0)?"YUV420":"YUV422");

		/* YUVファイルWrite */
		ret = jpg_write_yuv((char*)JPG_OUTPUT_YUV_PATH, outbuf, width, height);
		if(AG903_ENONE != ret) {
			JPG_ErrPrintf("ERROR: File write");
			break;
		}
	}while(0);

	jpg_del_callback(handle);

	/* ハンドル解放 */
	AG903_JPGReleaseHandle(handle);

	/* メモリ解放 */
	sys_free(inbuf);
	sys_free(outbuf);

	return ret;
}

/**
 * @brief	デコード処理 (画面表示)
 * @return	AG903_ENONE		正常終了
 * @return	AG903_ENONE以外	エラーコード
 * @note	none
 */
static int32_t jpg_display_image(void)
{
	AG903_JPGHandle* 	handle;
	AG903_JPGCtrlParam	ini_param;
	AG903_JPGCodecParam	set_param;
	int32_t		ret = AG903_ENONE;
	uint32_t	width, height, size;
	uint8_t*	inbuf  = NULL;
	uint8_t*	outbuf = NULL;
	uint8_t*	wbase  = NULL;
	uint8_t		ccomp;

	ret = AG903_JPGGetHandle(&handle);
	if(AG903_ENONE != ret) {
		JPG_ErrPrintf("ERROR: Handle get");
		return -AG903_EFAULT;
	}
	jpg_set_callback(handle);

	/* メモリ確保 */
	inbuf  = (uint8_t *)sys_memalign(JPG_JPGBUF_SIZE, 4);
	outbuf = (uint8_t *)sys_memalign(JPG_YUVBUF_SIZE, 4);
	wbase  = (uint8_t *)sys_memalign(JPG_WINATTR_SIZE, 8);
	if((NULL == inbuf) || (NULL == outbuf) || (NULL == wbase)) {
		JPG_ErrPrintf("ERROR: Memory alloc");
		return -AG903_EFAULT;
	}

	do {
		/* JPEGファイルRead */
		ret = jpg_read_jpg((char*)JPG_INPUT_DEC_PATH, inbuf, JPG_JPGBUF_SIZE, &size, &width, &height);
		if(AG903_ENONE != ret) {
			JPG_ErrPrintf("ERROR: File read");
			break;
		}

		/* ヘッダの情報によるデコード可否チェック */
		ret = jpg_is_decodable(inbuf, size);
		if(AG903_ENONE != ret) {
			JPG_ErrPrintf("ERROR: Non-support JPEG format");
			break;
		}

		/* 表示設定 */
		ret = jpg_setup_dsp(wbase, outbuf);
		if(AG903_ENONE != ret) {
			JPG_ErrPrintf("ERROR: Display setup");
			break;
		}

		/* コーデック初期化 */
		ini_param.operation = AG903_JPG_ENDEC_DECODE;
		ini_param.interval  = 8;
		ini_param.SOSstop   = false;
		ret =  AG903_JPGInitCodec(handle, &ini_param);
		if(AG903_ENONE != ret) {
			JPG_ErrPrintf("ERROR: Codec init");
			break;
		}
		WAITSEQ(JPG_FLAG_INIT_CODEC|JPG_FLAG_ERR_CODEC, 0);
		if(JPG_FLAG_ERR_CODEC & GETSEQ()) {
			JPG_ErrPrintf("ERROR: Codec init wait");
			break;
		}

		/* コーデック処理 */
		set_param.capture    = false;
		set_param.buf_flow   = false;
		set_param.flame_skip = false;
		set_param.h_size     = 0;
		set_param.v_size     = 0;
		set_param.in_buf     = (void*)(inbuf);
		set_param.out_buf    = (void*)(outbuf);
		set_param.strm_size  = size;
		set_param.stride     = width;
		ret = AG903_JPGStartCodec(handle, &set_param);
		if(AG903_ENONE != ret) {
			JPG_ErrPrintf("ERROR: Codec start");
			break;
		}
		WAITSEQ(JPG_FLAG_END_CODEC|JPG_FLAG_ERR_CODEC, 0);
		if(JPG_FLAG_ERR_CODEC & GETSEQ()) {
			JPG_ErrPrintf("ERROR: Codec end wait");
			break;
		}

		/* コーデック動作終了 */
		ret = AG903_JPGEndCodec(handle);
		if(AG903_ENONE != ret) {
			JPG_ErrPrintf("ERROR: Codec end");
			break;
		}

		/* JPGデコード情報取得 */
		ret = AG903_JPGGetColorComponent(handle, &ccomp);
		if(AG903_ENONE != ret) {
			JPG_ErrPrintf("ERROR: Color component get");
			break;
		}
		JPG_Printf(" Format : %s\r\n", (ccomp == 0)?"YUV420":"YUV422");

		/* ウインドウ設定 */
		ret = jpg_set_dsp_window(width, height);
		if(AG903_ENONE != ret) {
			JPG_ErrPrintf("ERROR: Display window set");
			break;
		}

		/* 表示ON */
		ret = jpg_enable_dsp();
		if(AG903_ENONE != ret) {
			JPG_ErrPrintf("ERROR: Display enable");
			break;
		}

		JPG_Printf(" Press enter key to exit: ");
		JPG_wait();	/* CRLF入力待ち */

		/* 表示終了 */
		ret = jpg_term_dsp();
		if(AG903_ENONE != ret) {
			JPG_ErrPrintf("ERROR: Display term");
			break;
		}

	}while(0);

	jpg_del_callback(handle);

	/* ハンドル解放 */
	AG903_JPGReleaseHandle(handle);

	/* メモリ解放 */
	sys_free(inbuf);
	sys_free(outbuf);
	sys_free(wbase);

	return ret;
}

/**
 * @brief	デコード処理 (繰り返し)
 * @return	AG903_ENONE		正常終了
 * @return	AG903_ENONE以外	エラーコード
 * @note	none
 */
static int32_t jpg_decode_repeat(void)
{
	AG903_JPGHandle* 	handle;
	AG903_JPGCtrlParam	ini_param;
	AG903_JPGCodecParam	set_param;
	int32_t		ret = AG903_ENONE;
	uint32_t	width, height, size, fsize, jsize;
	uintptr_t	srcaddr, snkaddr;
	uint8_t*	inbuf   = NULL;
	uint8_t*	bmubuf  = NULL;
	uint8_t*	workbuf = NULL;
	uint8_t*	wbase   = NULL;
	uint8_t*	straddr = NULL;
	uint8_t*	endaddr = NULL;
	uint8_t*	bufptr  = NULL;

	ret = AG903_JPGGetHandle(&handle);
	if(AG903_ENONE != ret) {
		JPG_ErrPrintf("ERROR: Handle get");
		return -AG903_EFAULT;
	}
	jpg_set_callback(handle);

	/* メモリ確保 */
	inbuf  = (uint8_t *)sys_memalign(JPG_JPGBUF_SIZE, 4);
	bmubuf = (uint8_t *)sys_memalign(JPG_BMUBUF_SIZE, 4096);
	workbuf= (uint8_t *)sys_memalign(JPG_WORKBUF_SIZE, 32);
	wbase  = (uint8_t *)sys_memalign(JPG_WINATTR_SIZE, 8);
	if((NULL == inbuf) || (NULL == bmubuf) || (NULL == wbase)) {
		JPG_ErrPrintf("ERROR: Memory alloc");
		return -AG903_EFAULT;
	}
	sys_memset(bmubuf, 0, JPG_YUVBUF_SIZE);

	do {
		/* ファイルRead */
		ret = jpg_read_mov((char*)JPG_INPUT_MOV_PATH, workbuf, JPG_WORKBUF_SIZE, &fsize);
		if(AG903_ENONE != ret) {
			JPG_ErrPrintf("ERROR: File read");
			break;
		}

		/* BMU設定 */
		ret = jpg_setup_bmu((void*)bmubuf, JPG_BMUBUF_SIZE/JPG_BMUFIFO_NUM);
		if(AG903_ENONE != ret) {
			JPG_ErrPrintf("ERROR: BMU setup");
			break;
		}
		ret = jpg_getaddr_bmu(&srcaddr, &snkaddr);
		if(AG903_ENONE != ret) {
			JPG_ErrPrintf("ERROR: BMU get address");
			break;
		}

		/* 表示設定 */
		ret = jpg_setup_dsp(wbase, (uint8_t*)snkaddr);
		if(AG903_ENONE != ret) {
			JPG_ErrPrintf("ERROR: Display setup");
			break;
		}

		/* コーデック初期化 */
		ini_param.operation = AG903_JPG_ENDEC_DECODE;
		ini_param.interval  = 8;
		ini_param.SOSstop   = false;
		ret =  AG903_JPGInitCodec(handle, &ini_param);
		if(AG903_ENONE != ret) {
			JPG_ErrPrintf("ERROR: Codec init");
			break;
		}
		WAITSEQ(JPG_FLAG_INIT_CODEC|JPG_FLAG_ERR_CODEC, 0);
		if(JPG_FLAG_ERR_CODEC & GETSEQ()) {
			JPG_ErrPrintf("ERROR: Codec init wait");
			break;
		}

		/* 表示ON */
		ret = jpg_enable_dsp();
		if(AG903_ENONE != ret) {
			JPG_ErrPrintf("ERROR: Display enable");
			break;
		}

		bufptr = workbuf;
		for(size=0; size<fsize; ) {
			/* JPEG切出し */
			straddr = (uint8_t*)jpg_search_marker(bufptr, 0xD8, fsize-size);	/* SOI */
			endaddr = (uint8_t*)jpg_search_marker(bufptr, 0xD9, fsize-size);	/* EOI */
			if((NULL == straddr) || (NULL == endaddr)){
				break;
			}
            endaddr += 2;
			bufptr = endaddr;
			jsize  = endaddr - straddr;
			size  += jsize;
			sys_memcpy(inbuf, straddr, jsize);

			/* Pixcelサイズ取得 */
			ret = jpg_get_pixcel_size(inbuf, jsize, &width, &height);
			if(AG903_ENONE != ret) {
				return -AG903_EFAULT;
			}
			inbuf[jsize+0] = 0xFF;	/* 4バイトのダミーデータ付加 */
			inbuf[jsize+1] = 0xFF;
			inbuf[jsize+2] = 0xFF;
			inbuf[jsize+3] = 0xFF;
			jsize += 4;

			/* コーデック処理 */
			set_param.capture    = false;
			set_param.buf_flow   = true;
			set_param.flame_skip = false;
			set_param.h_size     = 0;
			set_param.v_size     = 0;
			set_param.in_buf     = (void*)(inbuf);
			set_param.out_buf    = (void*)(srcaddr);
			set_param.strm_size  = jsize;
			set_param.stride     = width;
			ret = AG903_JPGStartCodec(handle, &set_param);
			if(AG903_ENONE != ret) {
				JPG_ErrPrintf("ERROR: Codec start");
				break;
			}
			WAITSEQ(JPG_FLAG_END_CODEC|JPG_FLAG_ERR_CODEC, 0);
			if(JPG_FLAG_ERR_CODEC & GETSEQ()) {
				JPG_ErrPrintf("ERROR: Codec end wait");
				break;
			}
			CLRSEQ(JPG_FLAG_END_CODEC);

			/* ウインドウ設定 */
			ret = jpg_set_dsp_window(width, height);
			if(AG903_ENONE != ret) {
				JPG_ErrPrintf("ERROR: Display window set");
				break;
			}
		}
		if(AG903_ENONE != ret) {
			break;
		}

		/* コーデック動作終了 */
		ret = AG903_JPGEndCodec(handle);
		if(AG903_ENONE != ret) {
			JPG_ErrPrintf("ERROR: Codec end");
			break;
		}

		/* BMU終了 */
		jpg_term_bmu();
		if(AG903_ENONE != ret) {
			JPG_ErrPrintf("ERROR: BMU term");
			break;
		}

		/* 表示終了 */
		ret = jpg_term_dsp();
		if(AG903_ENONE != ret) {
			JPG_ErrPrintf("ERROR: Display term");
			break;
		}

	}while(0);

	jpg_del_callback(handle);

	/* ハンドル解放 */
	AG903_JPGReleaseHandle(handle);

	/* メモリ解放 */
	sys_free(inbuf);
	sys_free(bmubuf);
	sys_free(workbuf);
	sys_free(wbase);

	return ret;
}

/**
 * @brief	JPEGファイルリード
 * @param	name	[in]  ファイル名
 * @param	buf		[out] リードデータ
 * @param	bufsize	[in]  バッファサイズ名
 * @param	size	[out] データサイズ
 * @param	width	[out] 幅
 * @param	height	[out] 高さ
 * @return	AG903_ENONE		正常終了
 * @return	AG903_ENONE以外	エラーコード
 * @note	none
 */
static int32_t jpg_read_jpg(char* name, uint8_t* buf, uint32_t bufsize, uint32_t* size, uint32_t* width, uint32_t* height)
{
	int32_t		ret;
	uint32_t	fsize;

	/* ファイルサイズ取得 */
	ret = jpg_GetFileSize(name, &fsize);
	if(AG903_ENONE != ret) {
		return -AG903_EFAULT;
	}
	if(fsize+4 > bufsize) {
		return -AG903_EFAULT;
	}

	/* ファイルリード */
	ret = jpg_OpenRead(name, buf, fsize);
	if(AG903_ENONE != ret) {
		return -AG903_EFAULT;
	}
	buf[fsize+0] = 0xFF;	/* 4バイトのダミーデータ付加 */
	buf[fsize+1] = 0xFF;
	buf[fsize+2] = 0xFF;
	buf[fsize+3] = 0xFF;

	/* Pixcelサイズ取得 */
	ret = jpg_get_pixcel_size(buf, fsize, width, height);
	if(AG903_ENONE != ret) {
		return -AG903_EFAULT;
	}
	(*size) = fsize+4;

	JPG_Printf(" Read JPEG data.\r\n width  :%8d\r\n", (unsigned int)(*width));
	JPG_Printf(" height :%8d\r\n length :%8d\r\n",(unsigned int)(*height), (unsigned int)(*size));

	return AG903_ENONE;
}

/**
 * @brief	MOVファイルリード
 * @param	name	[in]  ファイル名
 * @param	buf		[out] リードデータ
 * @param	bufsize	[in]  バッファサイズ
 * @param	size	[out] ファイルサイズ
 * @return	AG903_ENONE		正常終了
 * @return	AG903_ENONE以外	エラーコード
 * @note	none
 */
static int32_t jpg_read_mov(char* name, uint8_t* buf, uint32_t bufsize, uint32_t* size)
{
	int32_t		ret;
	uint32_t	fsize;

	/* ファイルサイズ取得 */
	ret = jpg_GetFileSize(name, &fsize);
	if(AG903_ENONE != ret) {
		return -AG903_EFAULT;
	}
	if(fsize > bufsize) {
		return -AG903_EFAULT;
	}

	/* ファイルリード */
	ret = jpg_OpenRead(name, buf, fsize);
	if(AG903_ENONE != ret) {
		return -AG903_EFAULT;
	}
	(*size) = fsize;

	JPG_Printf(" Read MOV file.\r\n size  :%8d\r\n", (unsigned int)(*size));

	return AG903_ENONE;
}

/**
 * @brief	YUVファイルライト
 * @param	name	[in] ファイル名
 * @param	buf		[in] ライトデータ
 * @param	width	[in] 幅
 * @param	height	[in] 高さ
 * @return	AG903_ENONE		正常終了
 * @return	AG903_ENONE以外	エラーコード
 * @note	none
 */
static int32_t jpg_write_yuv(char* name, uint8_t* buf, uint32_t width, uint32_t height)
{
	int32_t		rc;
	uint32_t	wsize;

	wsize = (width*height)<<1;

	rc = jpg_WriteYUV(name, buf, wsize, width, height);
	if(AG903_ENONE != rc) {
		return -AG903_EFAULT;
	}

	JPG_Printf(" Write YUV file.\r\n");

	return AG903_ENONE;
}

/*
 * @brief	JPEGヘッダのデコード
 * @param	buf		[in] JPEGデータ
 * @param   width   [out] 画像の幅
 * @param   height  [out] 画像の高さ
 * @param   subsamp [out] 彩度のサブサンプリング方式
 * @return	AG903_ENONE		正常終了
 * @return	AG903_ENONE以外	未対応のヘッダ
 * @note	フレームがSOF0の場合のみ正常にデコード可能です。
 *          正常にデコードできなかったとき引数で受け渡される戻り値は不定です。
*/
static int32_t jpg_decompress_header(uint8_t* buf, uint32_t size, uint32_t *width, uint32_t *height, uint32_t *subsamp)
{
	uint32_t ii;
	for (ii = 0; ii+1 < size; ii++) {
		/* SOF0を探す */
		if (buf[ii]==0xFF && buf[ii+1]==0xC0) {

			/* SOF0セグメント長を確認 */
			int lf = buf[ii+2]<<8 | buf[ii+3];
			if (lf != 17) {
				return -AG903_EFAULT;
			}

			/* 画像サイズを取得 */
			int xf = buf[ii+7]<<8 | buf[ii+8]; /* Xサイズ */
			int yf = buf[ii+5]<<8 | buf[ii+6]; /* Yサイズ */
			if (height != NULL) {
				*height = xf;
			}
			if (width != NULL) {
				*width = yf;
			}

			/* 色成分数を確認 */
			int nf = buf[ii+9];
			if (nf != 3) {
				return -AG903_EFAULT; /* チェック未対応の格納方式 */
			}

			/* 色成分のサブサンプリングを取得 */
			int ycn = buf[ii+10];
			int yhv = buf[ii+11];
			int ucn = buf[ii+13];
			int uhv = buf[ii+14];
			int vcn = buf[ii+16];
			int vhv = buf[ii+17];
			if (ycn==0x01 && yhv==0x21) {
				if ((ucn==0x02 && uhv==0x11) && (vcn==0x03 && vhv==0x11)) {
					*subsamp = VOD_SSAMP_422;
				} else {
					*subsamp = VOD_SSAMP_NONSUPPORT;
					return -AG903_EFAULT;
				}
			} else if (ycn==0x01 && yhv==0x22) {
				if ((ucn==0x02 && uhv==0x11) && (vcn==0x03 && vhv==0x11)) {
					*subsamp = VOD_SSAMP_420;
				} else {
					*subsamp = VOD_SSAMP_NONSUPPORT;
					return -AG903_EFAULT;
				}
			} else {
				*subsamp = VOD_SSAMP_NONSUPPORT;
				return -AG903_EFAULT;
			}

			return AG903_ENONE;
		}
	}

	return -AG903_EFAULT;
}


/*
 * @brief	JPEGデータのデコード可否確認
 * @param	buf		[in] JPEGデータ
 * @param   width   [out] 画像の幅
 * @param   height  [out] 画像の高さ
 * @param   subsamp [out] 彩度のサブサンプリング方式
 * @return	AG903_ENONE		正常終了
 * @return	AG903_ENONE以外	未対応のヘッダ
 * @note	none
*/
static int32_t jpg_is_decodable(uint8_t* buf, uint32_t size)
{
	uint32_t width   = 0;
	uint32_t height  = 0;
	uint32_t subsamp = 0;
	int32_t ret;

	ret = jpg_decompress_header(buf, size, &width, &height, &subsamp);
	if (ret != AG903_ENONE) {
		return -AG903_EFAULT;
	}

	if (subsamp!=VOD_SSAMP_422 && subsamp!=VOD_SSAMP_420) {
		return -AG903_EFAULT;
	}

	if (subsamp==VOD_SSAMP_422 && height>4096) {
		return -AG903_EFAULT;
	}

	if (subsamp==VOD_SSAMP_420 && height>8192) {
		return -AG903_EFAULT;
	}

	if (width<8 || height<8) {
		return -AG903_EFAULT;
	}

	return AG903_ENONE;
}


/**
 * @brief	マーカー検索
 * @param	buf		[in] JPEGデータ
 * @param	marker	[in] 検索マーカー
 * @param	size	[in] 検索サイズ
 * @return	pbuf	対象マーカーのアドレス
 * @return	NULL	対象マーカー無し
 * @note	none
 */
static void* jpg_search_marker(uint8_t* buf, uint8_t marker, uint32_t size)
{
	uint8_t*	pbuf;

	for(pbuf=buf; (uint32_t)(pbuf-buf)<size; ) {

		if(0xff != pbuf[0]) {	/* マーカ識別子(0xFF)検索 */
			pbuf++;
			continue;
		}
		pbuf++;

		if (marker == pbuf[0]) {
			pbuf--;
			break;	/* 発見 */
		}

		if((0xE0 == (pbuf[0]&0xF0)) ||	/* JFIF識別子 */
		   (0xC0 == pbuf[0])        ||	/* SOF0 */
		   (0xC2 == pbuf[0])        ||	/* SOF2 */
		   (0xDB == pbuf[0])        ||	/* DQT */
		   (0xC4 == pbuf[0])        ||	/* DHT */
		   (0xDA == pbuf[0])) {			/* SOS */
			pbuf += (pbuf[1]<<8) + pbuf[2];
			continue;
		}
		pbuf++;
	}
	if((uint32_t)(pbuf-buf)>=size) {
		pbuf = NULL;
	}

	return (void*)pbuf;
}

/**
 * @brief	JPEGピクセルサイズ取得
 * @param	buf    [in]  JPEGデータ
 * @param	size   [in]  JPEGデータサイズ
 * @param	width  [out] ビットマップデータサイズ
 * @param	height [out] バッファサイズ
 * @return	AG903_ENONE   正常終了
 * @return	-AG903_EFAULT JPEGデータ異常
 * @note
*/
static int32_t jpg_get_pixcel_size(uint8_t *buf, uint32_t size, uint32_t *width, uint32_t *height)
{
	int32_t		rc = AG903_ENONE;
	uint8_t*	pbuf;

	(*width)  = 0;
	(*height) = 0;

	for(pbuf=buf; (uint32_t)(pbuf-buf)<size; ) {

		if(0xff != (pbuf[0])) {	/* マーカ識別子(0xFF)検索 */
			pbuf++;
			continue;
		}
		pbuf++;

		if((0xE0 == ((pbuf[0])&0xF0)) ||	/* JFIF識別子 */
		   (0xDB == (pbuf[0]))        ||	/* DQT */
		   (0xC4 == (pbuf[0]))        ||	/* DHT */
		   (0xDA == (pbuf[0]))) {			/* SOS */
			pbuf += (pbuf[1]<<8) + pbuf[2];
			continue;
		}

		if((0xC0 == pbuf[0]) ||	/* SOF0 */
		   (0xC2 == pbuf[0])) {	/* SOF2 */
		   break;
		}

	}
	if((uint32_t)(pbuf-buf) < size) {
		(*height) = (pbuf[4] << 8) + pbuf[5];	/* 高さ */
		if((*height) % 16) {
			(*height) += 16 - (*height % 16);
		}
		(*width) = (pbuf[6] << 8) + pbuf[7];	/* 幅 */
		if((*width) % 16) {
			(*width) += 16 - (*width % 16);
		}
	}
	else {
		rc = -AG903_EFAULT;
	}

	return rc;
}

/**
 * @brief	BMU設定
 * @param	buf		[in] BMUバッファベースアドレス
 * @param	bufsize	[in] BMUバッファサイズ
 * @return	AG903_ENONE		正常終了
 * @return	AG903_ENONE以外	エラーコード
 * @note	none
 */
static int32_t jpg_setup_bmu(void* buf, uint32_t bufsize)
{
	int32_t		rc = AG903_ENONE;

	if (rc == AG903_ENONE) { rc = AG903_BMUMgrGetHandle(&gBmuHdl); }
	if (rc == AG903_ENONE) { rc = AG903_BMUMgrSetMode(gBmuHdl, AG903_BMU_SINK_WAIT_DISABLE, AG903_BMU_BUF_MGR_MODE1); }
	if (rc == AG903_ENONE) { rc = AG903_BMUMgrSetSrcModule(gBmuHdl, AG903_BMU_SRC_JPG0); }
	if (rc == AG903_ENONE) { rc = AG903_BMUMgrAddSinkModule(gBmuHdl, AG903_BMU_SINK_DSP0); }
	if (rc == AG903_ENONE) { rc = AG903_BMUMgrSetBufferConfig(gBmuHdl, buf, bufsize, JPG_BMUFIFO_NUM); }
	if (rc == AG903_ENONE) { rc = AG903_BMUMgrEnable(gBmuHdl); }

	return rc;
}

/**
 * @brief	BMUアドレス取得
 * @param	srcaddr	[out] ソースバッファアドレス
 * @param	snkaddr	[out] シンクバッファアドレス
 * @return	AG903_ENONE		正常終了
 * @return	AG903_ENONE以外	エラーコード
 * @note	none
 */
static int32_t jpg_getaddr_bmu(void* srcaddr, void* snkaddr)
{
	int32_t		rc = AG903_ENONE;

	if (rc == AG903_ENONE) { rc = AG903_BMUMgrGetBMUSrcAddress(gBmuHdl, (uint32_t*)srcaddr);  }
	if (rc == AG903_ENONE) { rc = AG903_BMUMgrGetBMUSinkAddress(gBmuHdl, (uint32_t*)snkaddr); }

	return rc;
}

/**
 * @brief	BMU終了処理
 * @param	none
 * @return	AG903_ENONE		正常終了
 * @return	AG903_ENONE以外	エラーコード
 * @note	none
 */
static int32_t jpg_term_bmu(void)
{
	int32_t		rc = AG903_ENONE;
	AG903_BMUMgrStatus	bmustat;

	while (1) {
		if (rc != AG903_ENONE) { break; }
		rc = AG903_BMUMgrGetStatus(gBmuHdl, &bmustat);
		if (bmustat.is_empty != 0) { break; }
	}

	if (rc == AG903_ENONE) { rc = AG903_BMUMgrReleaseHandle(gBmuHdl); }

	return rc;
}

/**
 * @brief	表示設定
 * @param	wbase	[in] ウインドウベースアドレス
 * @param	buf		[in] フレームバッファアドレス
 * @return	AG903_ENONE		正常終了
 * @return	AG903_ENONE以外	エラーコード
 * @note	none
 */
static int32_t jpg_setup_dsp(uint8_t* wbase, uint8_t* buf)
{
	int32_t	rc = AG903_ENONE;

	if (rc == AG903_ENONE) { rc = AG903_DSPMgrInit(); }
	if (rc == AG903_ENONE) { rc = AG903_DSPMgrGetHandle(0, &gDspHdl); }
	if (rc == AG903_ENONE) { rc = jpg_setup_dsp_display_sync(); }
	if (rc == AG903_ENONE) { rc = jpg_setup_dsp_option(); }
	if (rc == AG903_ENONE) { rc = jpg_setup_dsp_window(wbase, buf); }

	return rc;
}

/**
 * @brief	表示ON
 * @param	none
 * @return	AG903_ENONE		正常終了
 * @return	AG903_ENONE以外	エラーコード
 * @note	none
 */
static int32_t jpg_enable_dsp(void)
{
	int32_t	rc = AG903_ENONE;

	if (rc == AG903_ENONE) { rc = AG903_DSPMgrEnable(gDspHdl); }
	Board_SelectDviTX();

	return rc;
}

/**
 * @brief	表示終了
 * @param	none
 * @return	AG903_ENONE		正常終了
 * @return	AG903_ENONE以外	エラーコード
 * @note	none
 */
static int32_t jpg_term_dsp(void)
{
	int32_t	rc = AG903_ENONE;

	if (rc == AG903_ENONE) { rc = AG903_DSPMgrDisable(gDspHdl); }

	while(AG903_DSPMgrCheckStopped(gDspHdl));

	if (gIntHdlID) {
		if (rc == AG903_ENONE) { rc = AG903_INTMgrDisableInt(AG903_IRQ16_DSP0 + 0); }
		if (rc == AG903_ENONE) { rc = AG903_INTMgrDeleteHandler(gIntHdlID); }
	}

	if (rc == AG903_ENONE) { rc = AG903_DSPMgrReleaseHandle(gDspHdl); }
	gDspHdl = NULL;

	return rc;
}

/**
 * @brief	画面出力設定
 * @param	none
 * @return	AG903_ENONE		正常終了
 * @return	AG903_ENONE以外	エラーコード
 * @note	none
 */
static int32_t jpg_setup_dsp_display_sync(void)
{
	int32_t						rc  = AG903_ENONE;
	AG903_DSPMgrCtrlParam		ctrl = {0};

	/* PLL */
	AG903_SPCPrmPllnParam pll = {0};
	AG903_SPCPrmSetPll1Ctrl(&pll);
	sys_delayus(50);
	AG903_SPCPrmSetPll1Ctrl(&DSP_SAMPLE_PLL[DSP_SAMPLE_RES_SXGA]);
	sys_delayus(50);

	/* 表示クロックソース:PLL1 */
	AG903_SPCPrmClkSel clksrc;
	AG903_SPCPrmGetClkSrc(&clksrc);
	clksrc.dt0 = AG903_SPC_DT0_CLKSRC_PLL1;
	AG903_SPCPrmSetClkSrc(&clksrc);

	AG903_DSPMgrCMOSParam		cmos = {0};

	/*--- CMOS出力パラメータ ---*/

	cmos.rgbde_polarity       = AG903_DSP_POLARITY_POSI;						/* RGBDEの極性					*/
	cmos.vsync_polarity       = AG903_DSP_POLARITY_NEGA;						/* VSYNCの極性(ModeLineから設定)*/
	cmos.hsync_polarity       = AG903_DSP_POLARITY_NEGA;						/* HSYNCの極性(ModeLineから設定)*/
	cmos.field_polarity       = AG903_DSP_POLARITY_NEGA;						/* フィールド信号の極性			*/
	cmos.colordetect_polarity = AG903_DSP_POLARITY_NEGA;						/* 色検出信号の極性				*/
	cmos.rgbde_en             = true;											/* RGBDEの有効・無効			*/
	cmos.vsync_en             = true;											/* VSYNCの有効・無効			*/
	cmos.hsync_en             = true;											/* HSYNCの有効・無効			*/
	cmos.field_en             = false;											/* フィールド信号の有効・無効	*/
	cmos.colordetect_en       = false;											/* 色検出信号の有効・無効		*/
	cmos.pixeldata_en         = true;											/* ピクセルデータの有効・無効	*/
	cmos.dotclk_polarity      = AG903_DSP_POLARITY_NEGA;						/* ドットクロックの極性			*/
	cmos.code_en              = false;											/* データにSAV、EAVコード埋めこみ有無	*/
	cmos.yuv_mode             = 0;												/* RGBからYUV変換方法			*/

	/*--- 表示回路パラメータ ---*/

	ctrl.ip_sel        = AG903_DSP_VMODE_NONINTERLACE;							/* ビデオモード					*/
	ctrl.hrz_framesize = VOD_WIDTH;
	ctrl.vt_framesize  = VOD_HEIGHT;
    ctrl.syncparam     = &DSP_SAMPLE_SYNCPARAM[DSP_SAMPLE_RES_SXGA];

	rc = AG903_DSPMgrSetPortSel(gDspHdl, AG903_VOD0_PORTSEL_LVCMOS24);

	if (rc == AG903_ENONE) {
		rc = AG903_DSPMgrSetCMOSParam(gDspHdl, &ctrl, &cmos);
	}

	return rc;
}

/**
 * @brief	オプション設定
 * @param	none
 * @return	AG903_ENONE		正常終了
 * @return	AG903_ENONE以外	エラーコード
 * @note	none
 */
static int32_t jpg_setup_dsp_option(void)
{
	int32_t						rc  = AG903_ENONE;
	AG903_DSPMgrExSyncParam		exsync;

	/*--- 外部同期パラメータ ---*/
	if (rc == AG903_ENONE) {
		exsync.sync_sel = 0;
		rc = AG903_DSPMgrSetExSyncParam(gDspHdl, &exsync);
	}

	/*--- 色補正パラメータ ---*/
	if (rc == AG903_ENONE) {
		rc = AG903_DSPMgrSetLutParam(gDspHdl, false, NULL);
	}

	/*--- 減色パラメータ ---*/
	if (rc == AG903_ENONE) {
		rc = AG903_DSPMgrSetDithParam(gDspHdl, false, NULL);
	}

	/*--- 色検出パラメータ ---*/
	if (rc == AG903_ENONE) {
		rc = AG903_DSPMgrSetCDParam(gDspHdl, NULL);
	}

	return rc;
}

/**
 * @brief	ウィンドウ初期設定
 * @param	wbase [in] ウインドウベースアドレス
 * @param	buf   [in] フレームバッファアドレス
 * @return	AG903_ENONE		正常終了
 * @return	AG903_ENONE以外	エラーコード
 * @note	none
 */
static int32_t jpg_setup_dsp_window(uint8_t* wbase, uint8_t* buf)
{
	int32_t						rc  = AG903_ENONE;
	AG903_DSPMgrWindowParam		win;
	AG903_DSPMgrWinAttribute	*attr;

	/*--- ウィンドウパラメータ ---*/
	win.update_timing      = AG903_DSP_ATTR_END_OF_VSYNC;	/* ウィンドウアトリビュート読出しタイミング	*/
	win.background         = 0xFF000000;					/* 画面背景						*/
	win.num_config         = 1;								/* 2画面合計16ウィンドウ		*/
	win.num_attr           = 1;								/* 1ウィンドウ=1アトリビュート	*/
	win.window_attr_base   = (uint32_t)wbase;				/* ウィンドウアトリビュートベースアドレス	*/
	win.window_attr_update = true;							/* ウィンドウアトリビュート読出し設定	*/
	win.palette_update     = false;							/* パレット読み出さない			*/
	if (rc == AG903_ENONE) { rc = AG903_DSPMgrSetWindowParam(gDspHdl, &win); }

	/*--- ウィンドウアトリビュート初期化 ---*/
	if (rc == AG903_ENONE) { rc = AG903_DSPMgrGetAttribute(gDspHdl, 0, &attr); }
	if (rc == AG903_ENONE) {
		AG903_DSPMgrWinAttribute zattr = {0};
		zattr.framebuffer_base        = (uint32_t)buf;
		zattr.conf.valid              = true;
		zattr.conf.default_alpha      = 0xFF;
		zattr.conf.framebuffer_format = AG903_DSP_FFMT_YUV422_BT601_FULL;
		*attr = zattr;
		rc = AG903_DSPMgrSetAttribute(gDspHdl, 0);
	}

	return rc;
}

/**
 * @brief	ウィンドウ設定
 * @param	width [in] 幅
 * @param	height[in] 高さ
 * @return	AG903_ENONE		正常終了
 * @return	AG903_ENONE以外	エラーコード
 * @note	none
 */
static int32_t jpg_set_dsp_window(uint32_t width, uint32_t height)
{
	AG903_DSPMgrWinAttribute	*attr;
	int32_t		rc  = AG903_ENONE;
	uint32_t	hrzsize;

	if (height % 16) { height += 16 - (height % 16); }
	if (width % 16) { width += 16 - (width % 16); }
	hrzsize/*byte*/ = (width/*px*/ * 16/*bit*/) >> 3;	/* AG903_DSP_FFMT_YUV422_BT601_FULLの場合	*/

	if (rc == AG903_ENONE) { rc = AG903_DSPMgrGetAttribute(gDspHdl, 0, &attr); }
	if (rc == AG903_ENONE) {
		attr->destination_width  = width;	/* 表示画面上・横幅		*/
		attr->destination_height = height;	/* 表示画面上・縦幅		*/
		attr->source_width       = width;	/* VRAM上・データ横幅	*/
		attr->source_height      = height;	/* VRAM上・データ縦幅	*/
		attr->hrz_size           = hrzsize;	/* VRAM上・フレームバッファ横幅	*/
		rc = AG903_DSPMgrSetAttribute(gDspHdl, 0);
	}

	return rc;
}

/**
 * @brief	コールバック設定
 * @param	handle [in] JPGハンドル
 * @return	none
 * @note	none
 */
static void jpg_set_callback(AG903_JPGHandle* handle)
{
	int32_t	loop;

	for (loop = 0;loop < AG903_JPG_HOOK_MAX;loop++) {
		gHook[loop].param = loop;
		gHook[loop].clbk  = (AG903_JPGClbk)jpg_clbk;
	}
	AG903_JPGSetHooks(handle, &gHook[0]);

	return;
}

/**
 * @brief	コールバック削除
 * @param	handle [in] JPGハンドル
 * @return	none
 * @note	none
 */
static void jpg_del_callback(AG903_JPGHandle* handle)
{
	int32_t	loop;

	for (loop = AG903_JPG_HOOK_CMP;loop < AG903_JPG_HOOK_MAX;loop++) {
		gHook[loop].param = 0;
		gHook[loop].clbk  = (AG903_JPGClbk)NULL;
	}
	AG903_JPGSetHooks(handle, &gHook[0]);

	return;
}

/**
 * @brief	コールバック関数
 * @param	handle [in] JPGハンドル
 * @param	param [in] 汎用パラメータ
 * @return	none
 * @note	(注意) 処理中のエラーは対応するコールバック関数を登録しなければ検出されません。
 */
static void jpg_clbk(AG903_JPGHandle *handle, uint32_t param)
{
	((void)handle);

	switch (param) {
	case AG903_JPG_HOOK_INITF:	// 12:CODEC初期化完了
		SETSEQ(JPG_FLAG_INIT_CODEC);
		break;
	case AG903_JPG_HOOK_CMP:	// 0:バス転送完了
		SETSEQ(JPG_FLAG_END_CODEC);
		break;
	case AG903_JPG_HOOK_STRE:	// 1:ストリームデータ転送エラー
	case AG903_JPG_HOOK_IMGE:	// 2:画像データ転送エラー
	case AG903_JPG_HOOK_CAPE:	// 3:キャプチャーオーバーフロー
	case AG903_JPG_HOOK_ESTE:	// 4:エンコード開始エラー
	case AG903_JPG_HOOK_NG:		// 9:コーデックエラー
		SETSEQ(JPG_FLAG_ERR_CODEC);
		break;
	case AG903_JPG_HOOK_EOI:	// 5:EOI検出
	case AG903_JPG_HOOK_SOI:	// 6:SOI/Pictureヘッダ検出
	case AG903_JPG_HOOK_EOP:	// 7:CODEC完了
	case AG903_JPG_HOOK_SOP:	// 8:CODEC開始
	case AG903_JPG_HOOK_SOS:	// 10:SOS検出
	case AG903_JPG_HOOK_DCTRI:	// 11:DCT読み込み
	case AG903_JPG_HOOK_SRSTF:	// 13:CODECソフトリセット完了
	default:
		break;
	}
}
