/*
 * This program was created by AXELL CORPORATION.
 * Copyright (C) 2017-2023 AXELL CORPORATION, all rights reserved.
 *************************************************************************************
 * @brief		JPG encode code
 * @file		encode.c
 * @auther		AXELL
 * @date
 * @description
 * @note
 * @history		2019_12_27  [SDK3.0] サンプルのメッセージ出力をASCIIコードのみに変更 (#2728)
 * @history		2023_03_31  [SDK3.5] JPGで特定サイズの画像をエンコードすると終了しなくなる不具合を修正 (#4515)
 */
#include "jpg_sample.h"
#include "via/viamgr.h"
#include "pgp/pgpmgr.h"
#include "jpg/jpg.h"

/* データ */
static AG903_JPGHooks	gHook[AG903_JPG_HOOK_MAX];
static AG903_PgpMgrPipelineHandle	*gPipelineHdl;
static AG903_ViaMgrInputHandle		*gViaHdl;
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_encode_file(uint32_t filetype);
static int32_t jpg_encode_capture(void);
static int32_t jpg_encode_repeat(uint32_t num);
static void	jpg_select_format(uint8_t *format);
static void	jpg_set_qscale(uint8_t *scale);
static int32_t jpg_read_yuv(char* name, uint8_t* buf, uint32_t bufsize, uint32_t* width, uint32_t* height);
static int32_t jpg_read_bmp(char* name, uint8_t* buf, uint32_t bufsize, uint32_t* dsize, uint32_t* width, uint32_t* height);
static int32_t jpg_write_jpg(char* name, uint8_t* buf, uint32_t size);
static int32_t jpg_bmp_to_yuv(uint8_t *bmp, uint8_t *yuv, uint32_t bmpsz, uint32_t bufsiz, uint32_t *yuvsz);
static int32_t jpg_setup_capture(void);
static int32_t jpg_start_capture(void);
static int32_t jpg_term_capture(void);
static void jpg_set_callback(AG903_JPGHandle* handle);
static void jpg_del_callback(AG903_JPGHandle* handle);
static void jpg_init_clbk(AG903_JPGHandle *handle, uint32_t param);
static void jpg_end_clbk(AG903_JPGHandle *handle, uint32_t param);
static void jpg_err_clbk(AG903_JPGHandle *handle, uint32_t param);

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

	CLRSEQ(JPG_FLAG_ALL);

	switch (mode) {
		case 0:	/* Encode(YUV to JPEG) */
			jpg_encode_file(JPG_ENCFILE_YUV);
			break;
		case 1:	/* Encode(RGB to JPEG) */
			jpg_encode_file(JPG_ENCFILE_RGB);
			break;
		case 2:	/* Encode(CAPTURE to JPEG) */
			jpg_encode_capture();
			break;
		case 3:	/* Encode(YUV to JPEG Repeat) */
			jpg_encode_repeat(JPG_ENC_REPEAT_NUM);
			break;
			
		default:
			break;
	}

	return;
}

/**
 * @brief	エンコード処理 (File入力)
 * @param	filetype[in] ファイル種別
 * @return	AG903_ENONE		正常終了
 * @return	AG903_ENONE以外	エラーコード
 * @note	none
 */
static int32_t jpg_encode_file(uint32_t filetype)
{
	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*	rgbbuf = NULL;
	uint8_t		format = AG903_JPG_CCOMP_YUV422;

	/* 品質値入力 & 回路初期化 */
	uint8_t scale;
	jpg_set_qscale(&scale);
	AG903_JPGSetQScale(scale);
	AG903_JPGInit();

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

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

	do {
		/* ファイルRead */
		if(JPG_ENCFILE_YUV == filetype) {
			ret = jpg_read_yuv((char*)JPG_INPUT_YUV_PATH, inbuf, JPG_YUVBUF_SIZE, &width, &height);
		}
		else {
			ret = jpg_read_bmp((char*)JPG_INPUT_RGB_PATH, rgbbuf, JPG_RGBBUF_SIZE, &size, &width, &height);
			if(AG903_ENONE == ret) {
				ret = jpg_bmp_to_yuv(rgbbuf, inbuf, size, JPG_YUVBUF_SIZE, &size);
			}
		}
		if(AG903_ENONE != ret) {
			JPG_ErrPrintf("ERROR: File read");
			break;
		}

		/* コーデック初期化 */
		ini_param.operation	  = AG903_JPG_ENDEC_ENCODE;
		ini_param.color_cmpnt = format;
		ini_param.EOImarker   = true;
		ini_param.RSTmarker   = true;
		ini_param.SOImarker   = true;
		ini_param.marker  	  = true;
		ini_param.SOSstop 	  = false;
		ini_param.interval    = 8;
		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     = width;
		set_param.v_size     = height;
		set_param.in_buf     = (void*)(inbuf);
		set_param.out_buf    = (void*)(outbuf);
		set_param.stride     = width;
		if (set_param.stride < set_param.h_size) {
			JPG_ErrPrintf("ERROR: Stride size");
			break;
		}
		if (set_param.stride % 8) {
			JPG_ErrPrintf("ERROR: Stride size");
			break;
		}
		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;
		}

		/* ファイルWrite */
		ret = AG903_JPGGetStreamSize(handle, &size);
		if(AG903_ENONE != ret) {
			JPG_ErrPrintf("ERROR: Stream size get");
			break;
		}
		ret = jpg_write_jpg((char*)JPG_OUTPUT_ENC_PATH, outbuf, size);
		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);
	if(JPG_ENCFILE_RGB == filetype) {
		sys_free(rgbbuf);
	}

	return ret;
}

/**
 * @brief	エンコード処理 (Capture入力)
 * @return	AG903_ENONE		正常終了
 * @return	AG903_ENONE以外	エラーコード
 * @note	none
 */
static int32_t jpg_encode_capture(void)
{
	AG903_JPGHandle* 	handle;
	AG903_JPGCtrlParam	ini_param;
	AG903_JPGCodecParam	set_param;
	int32_t		ret;
	uint32_t	size;
	uint8_t*	outbuf = NULL;
	uint8_t		format = AG903_JPG_CCOMP_YUV422;

	/* 品質値入力 & 回路初期化 */
	uint8_t scale;
	jpg_set_qscale(&scale);
	AG903_JPGSetQScale(scale);
	AG903_JPGInit();

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

	ret = Board_SetupCvbs(TEST_CVBS_MODE);
	if(AG903_ENONE != ret) {
		JPG_ErrPrintf("ERROR: Video input setup");
		return ret;
	}

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

	do {
		/* パイプライン初期化 */
		ret = jpg_setup_capture();
		if(AG903_ENONE != ret) {
			JPG_ErrPrintf("ERROR: Capture setup");
			break;
		}

		/* VIAのパラメータ設定後安定すまで約1秒待ってビデオ入力を判定する */
		sys_dlytsk(1000);
		if (!AG903_ViaMgrIsInputValid(gViaHdl)) {
			JPG_ErrPrintf("ERROR: No video signal");
			break;
		}

		/* コーデック初期化 */
		ini_param.operation	  = AG903_JPG_ENDEC_ENCODE;
		ini_param.color_cmpnt = format;
		ini_param.EOImarker   = true;
		ini_param.RSTmarker   = true;
		ini_param.SOImarker   = true;
		ini_param.marker  	  = true;
		ini_param.SOSstop 	  = false;
		ini_param.interval    = 8;
		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    = true;
		set_param.buf_flow   = false;
		set_param.flame_skip = false;
		set_param.h_size     = TEST_CVBS_WIDTH;
		set_param.v_size     = TEST_CVBS_HEIGHT;
/*		set_param.in_buf     = キャプチャ回路から取り込み */
		set_param.out_buf    = (void*)outbuf;
/*		set_param.stride     = キャプチャ回路から取り込み */
		ret = AG903_JPGStartCodec(handle, &set_param);
		if(AG903_ENONE != ret) {
			JPG_ErrPrintf("ERROR: Codec start");
			break;;
		}

		JPG_Printf(" Press enter key to start capture: ");
	    JPG_wait();	/* CRLF入力待ち	*/
		/* パイプライン開始 */
		ret = jpg_start_capture();
		if(AG903_ENONE != ret) {
			JPG_ErrPrintf("ERROR: Capture 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;
		}

		/* パイプライン停止 */
		ret = jpg_term_capture();
		if(AG903_ENONE != ret) {
			JPG_ErrPrintf("ERROR: Capture term");
			break;
		}

		/* ファイルWrite */
		ret = AG903_JPGGetStreamSize(handle, &size);
		if(AG903_ENONE != ret) {
			JPG_ErrPrintf("ERROR: Stream size get");
			break;
		}
		ret = jpg_write_jpg((char*)JPG_OUTPUT_CAP_PATH, outbuf, size);
		if(AG903_ENONE != ret) {
			JPG_ErrPrintf("ERROR: File write");
			break;
		}
	}while(0);

	jpg_del_callback(handle);

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

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

	return ret;
}

/**
 * @brief	エンコード処理 (繰り返し)
 * @param	num [in] 実行回数
 * @return	AG903_ENONE		正常終了
 * @return	AG903_ENONE以外	エラーコード
 * @note	none
 */
static int32_t jpg_encode_repeat(uint32_t num)
{
	AG903_JPGHandle* 	handle;
	AG903_JPGCtrlParam	ini_param;
	AG903_JPGCodecParam	set_param;
	int32_t		ret = AG903_ENONE;
	uint32_t	loop;
	uint32_t	width, height;
	uint8_t*	inbuf  = NULL;
	uint8_t*	outbuf = NULL;
	uint8_t		format = AG903_JPG_CCOMP_YUV422;

	/* 品質値入力 & 回路初期化 */
	uint8_t scale;
	jpg_set_qscale(&scale);
	AG903_JPGSetQScale(scale);
	AG903_JPGInit();

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

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

	do {
		/* ファイルRead */
		ret = jpg_read_yuv((char*)JPG_INPUT_YUV_PATH, inbuf, JPG_YUVBUF_SIZE, &width, &height);
		if(AG903_ENONE != ret) {
			JPG_ErrPrintf("ERROR: File read");
			break;
		}

		/* コーデック初期化 */
		ini_param.operation	  = AG903_JPG_ENDEC_ENCODE;
		ini_param.color_cmpnt = format;
		ini_param.EOImarker   = true;
		ini_param.RSTmarker   = true;
		ini_param.SOImarker   = true;
		ini_param.marker  	  = true;
		ini_param.SOSstop 	  = false;
		ini_param.interval    = 8;
		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     = width;
		set_param.v_size     = height;
		set_param.in_buf     = (void*)(inbuf);
		set_param.out_buf    = (void*)(outbuf);
		set_param.stride     = width;
		for (loop=0; loop<num; loop++) {
			JPG_Printf(" # Encode %d\r\n", loop);
			CLRSEQ(JPG_FLAG_END_CODEC);
			ret = AG903_JPGStartCodec(handle, &set_param);
			if(AG903_ENONE != ret) {
				break;
			}
			WAITSEQ(JPG_FLAG_END_CODEC|JPG_FLAG_ERR_CODEC, 0);
			if(JPG_FLAG_ERR_CODEC & GETSEQ()) {
				break;
			}
		}
		if(AG903_ENONE != ret) {
			break;
		}

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

	jpg_del_callback(handle);

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

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

/**
 * @brief	フォーマット選択
 * @param	format [out] 選択フォーマット
 * @return	none
 * @note	none
 */
static void	jpg_select_format(uint8_t *format)
{
	char  chara[4];
	int8_t	num;

	while(1){
		JPG_Printf(" select encode format\r\n");
		JPG_Printf(" 1:YUV420  2:YUV422\r\n");
		JPG_Printf(" ");
		COM_GetNum_Wait(chara, 3);  /* 3 = 1(format No.)+CRLF */
		num = ASCtoBIN(chara[0]);
		if(1==num) {
			(*format) = AG903_JPG_CCOMP_YUV420;
			break;
		}
		else if(2==num) {
			(*format) = AG903_JPG_CCOMP_YUV422;
			break;
		}
		else {
			JPG_Printf(" Invalid\r\n");
		}
	}

	return;
}

/**
 * @brief	品質値入力
 * @param	qscale [out] 品質値
 * @return	none
 * @note	none
 */
static void	jpg_set_qscale(uint8_t *scale)
{
	char  chara[5];
	int8_t	num;

	while(1){
		JPG_Printf(" Input QScale [000-100]\r\n");
		JPG_Printf(" ");
		COM_GetNum_Wait(chara, 5);  /* 5 = 3(qscale)+CRLF */
		num = 0;
		if((chara[0]>='0') && (chara[0]<='1')){
			num += (ASCtoBIN(chara[0]) * 100);
			if((chara[1]>='0') && (chara[1]<='9')){
				num += (ASCtoBIN(chara[1]) * 10);
				if((chara[2]>='0') && (chara[2]<='9')){
					num += ASCtoBIN(chara[2]);
				}else{
					num = -1;
				}
			}else{
				num = -1;
			}
		}else{
			num = -1;
		}
		if((num == -1) || (num > 100)){
			JPG_Printf(" Invalid\r\n");
		}else{
			*scale = (uint8_t)num;
			break;
		}
	}

	return;
}

/**
 * @brief	YUVファイルリード
 * @param	name	[in]  ファイル名
 * @param	buf		[out] リードデータ
 * @param	bufsize	[in]  バッファサイズ
 * @param	width	[out] 幅
 * @param	height	[out] 高さ
 * @return	AG903_ENONE		正常終了
 * @return	AG903_ENONE以外	エラーコード
 * @note	none
 */
static int32_t jpg_read_yuv(char* name, uint8_t* buf, uint32_t bufsize, 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 > bufsize) {
		return -AG903_EFAULT;
	}

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

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

	return AG903_ENONE;
}

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

	ret = jpg_ReadBitmap(name, buf, bufsize, dsize, width, height);
	if(AG903_ENONE != ret) {
		return -AG903_EFAULT;
	}
	
	JPG_Printf(" Read RGB data.\r\n pixel width :%8d\r\n", (unsigned int)(*width));
	JPG_Printf(" pixel height:%8d\r\n data  length:%8d\r\n",(unsigned int)(*height), (unsigned int)(*dsize));

	return AG903_ENONE;
}

/**
 * @brief	JPGファイルライト
 * @param	name	[in] ファイル名
 * @param	buf		[in] ライトデータ
 * @param	size	[in] ライトサイズ
 * @return	AG903_ENONE		正常終了
 * @return	AG903_ENONE以外	エラーコード
 * @note	none
 */
static int32_t jpg_write_jpg(char* name, uint8_t* buf, uint32_t size)
{
	int32_t		ret;
	uint32_t	strsz;

	strsz = (*(uint32_t*)buf) << 2;	/* 先頭の1ワードはストリームサイズ(ワード単位) */
	if(strsz != size) {
		return -AG903_EFAULT;
	}

	buf += sizeof(int32_t);
	ret = jpg_OpenWrite(name, buf, size);
	if(AG903_ENONE != ret) {
		return -AG903_EFAULT;
	}

	JPG_Printf(" Write JPEG file.\r\n");
	
	return AG903_ENONE;
}

typedef struct {
	unsigned char r;
	unsigned char g;
	unsigned char b;
}	RGB;
/**
 * @brief	ビットマップYUV変換
 * @param	bmp    [in]  ビットマップデータ
 * @param	yuv    [out] YUVデータ
 * @param	bmpsz  [in]  ビットマップデータサイズ
 * @param	bufsiz [in]  バッファサイズ
 * @param	yuvsz  [out] YUVデータサイズ
 * @return	AG903_ENONE   正常終了
 * @return	-AG903_EFAULT バッファ不足
 * @note
*/
static int32_t jpg_bmp_to_yuv(uint8_t *bmp, uint8_t *yuv, uint32_t bmpsz, uint32_t bufsiz, uint32_t *yuvsz)
{
	int32_t		rc = AG903_ENONE;
	RGB			rgb[2];
	int16_t		y0, y1, cb, cr;
	uint32_t	ptr;
	uint32_t	ii;

	ptr  = 0;

	/*--- 2ピクセル同時処理 ---*/

	for (ii = 0;ii < bmpsz;) {
		/*--- 色成分取得 & スワップ(BGR to RGB) ---*/

		rgb[0].b = bmp[ii++];													/* 1ピクセル目BGR				*/
		rgb[0].g = bmp[ii++];													/*								*/
		rgb[0].r = bmp[ii++];													/*								*/
		rgb[1].b = bmp[ii++];													/* 2ピクセル目BGR				*/
		rgb[1].g = bmp[ii++];													/*								*/
		rgb[1].r = bmp[ii++];													/*								*/

		/***********************************************************************************************************
		 * ITU-R BT.601/ITU-R BT.709に基づいた計算式
		 * - RGB to YUV ------------------------------------------------------------------------------------------
		 * Y  =  0.299R + 0.587G + 0.114B
		 * Cr = -0.169R - 0.331G + 0.500B + 128
		 * Cb =  0.500R - 0.419G - 0.081B + 128
		 * -------------------------------------------------------------------------------------------------------
		 * R = 1.000Y                   + 1.402(Cr - 128)
		 * G = 1.000Y - 0.344(Cb - 128) - 0.714(Cr - 128)
		 * B = 1.000Y + 1.772(Cb - 128)
		 * -------------------------------------------------------------------------------------------------------
		 * ●CbCr(PbPr)のデータ範囲を0〜255（フルレンジ）とする為+128しているので留意すること
		 * ●8bit・256階調のデータの場合、RGBデータでは0〜255までの全階調（フルレンジ）を用いるのが一般的だが、
		 *   YUVデータではY＝16〜235、U、V＝16〜240の限定された階調（リミテッドレンジ）を一般的に用いる
		 *   その為、データ変換の際はフルレンジ・リミテッドレンジを考慮しないと映像の黒浮きなどが起きてしまう
		 ***********************************************************************************************************/

		/*--- 輝度成分の計算 ---*/

		y0 = (299 * rgb[0].r + 587 * rgb[0].g + 114 * rgb[0].b) / 1000;
		y1 = (299 * rgb[1].r + 587 * rgb[1].g + 114 * rgb[1].b) / 1000;

		/*--- 色差成分の計算 ---*/

		cb = (((500 * rgb[0].b) - (169 * rgb[0].r) - (331 * rgb[0].g))
			+ ((500 * rgb[1].b) - (169 * rgb[1].r) - (331 * rgb[1].g))) / 2 / 1000 + 128;
		cr = (((500 * rgb[0].r) - (419 * rgb[0].g) - ( 81 * rgb[0].b))
			+ ((500 * rgb[1].r) - (419 * rgb[1].g) - ( 81 * rgb[1].b))) / 2 / 1000 + 128;

		/*--- 計算結果を Cb Y0 Cr Y1 の順番に格納(メーカー毎にフォーマットが異なる部分) ---*/

		if (y0 > 255) { y0 = 255; }
		if (y1 > 255) { y1 = 255; }
		if (cb > 255) { cb = 255; }
		if (cr > 255) { cr = 255; }

		if (y0 < 0) { y0 = 0; }
		if (y1 < 0) { y1 = 0; }
		if (cb < 0) { cb = 0; }
		if (cr < 0) { cr = 0; }

		if(bufsiz < (ptr+4)) {
			rc = -AG903_EFAULT;
			break;
		}
		yuv[ptr++] = (uint8_t)cb;
		yuv[ptr++] = (uint8_t)y0;
		yuv[ptr++] = (uint8_t)cr;
		yuv[ptr++] = (uint8_t)y1;
	}

	(*yuvsz) = ptr;

	return rc;
}










/**
 * @brief	パイプライン初期化
 * @param	none
 * @return	AG903_ENONE		正常終了
 * @return	AG903_ENONE以外	エラーコード
 * @note	none
 */
static int32_t jpg_setup_capture(void)
{
	int32_t	rc = AG903_ENONE;
	int32_t	pipe;
	int32_t	via_port[1] = { 0, };

	gPipelineHdl = NULL;
	gViaHdl      = NULL;

	/* パイプラインハンドル、アナログビデオ入力ハンドルの取得 */
	if (rc == AG903_ENONE) { rc = AG903_PgpMgrGetPipelineHandle(1, &gPipelineHdl);                                                   }
	if (rc == AG903_ENONE) { rc = AG903_ViaMgrGetInputHandle(via_port, 1, &gViaHdl);                                                 }
	/* アナログビデオ入力の設定 */
	if (rc == AG903_ENONE) { rc = AG903_ViaMgrSetInputParameter(gViaHdl, AG903_VIA_MGR_FORMAT_NTSC_BT, AG903_VIA_MGR_MODE_COLOR);    }
	{/* パイプラインのパラメータ設定(パイプライン割り当て前に設定すること */
		if (rc == AG903_ENONE) { rc = AG903_PgpMgrSetParamInputMode(gPipelineHdl, AG903_PGP_MGR_INPUT_MD_VSYNC);                     }/* キャプチャ入力動作モード			*/
		if (rc == AG903_ENONE) { rc = AG903_PgpMgrSetParamOutputFormat(gPipelineHdl, AG903_PGP_MGR_OUTPUT_FMT_YCBCR444_TO_YCBCR422); }/* 出力フォーマット					*/
		if (rc == AG903_ENONE) { rc = AG903_PgpMgrSetParamInputFormat(gPipelineHdl, AG903_PGP_MGR_INPUT_FMT_YCBCR422_W_IP);          }/* 入力フォーマット					*/
		if (rc == AG903_ENONE) { rc = AG903_PgpMgrSetParamInputSize(gPipelineHdl, TEST_CVBS_HEIGHT/2, TEST_CVBS_WIDTH);              }/* キャプチャサイズ					*/
		if (rc == AG903_ENONE) { rc = AG903_PgpMgrSetParamInputPosition(gPipelineHdl, AG903_CAPTURE_VPOS_NTSC_BT, AG903_CAPTURE_HPOS_NTSC_BT); }/* キャプチャ開始座標		*/
		if (rc == AG903_ENONE) { rc = AG903_PgpMgrSetInputPort(gPipelineHdl, gViaHdl);                                               }/* パイプラインの入力ポートへ接続	*/
	}

	if (rc == AG903_ENONE) {
		pipe = AG903_PgpMgrAssignPipeline(gPipelineHdl);																			/* パイプライン割り当て				*/
		if (pipe >= 0) {
			if (rc == AG903_ENONE) { rc = AG903_PgpMgrSetJPEG(gPipelineHdl);  }
			if (rc == AG903_ENONE) { rc = AG903_PgpMgrEnableJPEG(true);       }
			gPipelineHdl->frame_out_enable = false;
		}
	}

	return rc;
}

/**
 * @brief	パイプライン開始
 * @param	none
 * @return	AG903_ENONE		正常終了
 * @return	AG903_ENONE以外	エラーコード
 * @note	none
 */
static int32_t jpg_start_capture(void)
{
	int32_t	rc;

	rc = AG903_PgpMgrExecPipeline(gPipelineHdl, AG903_PGP_MGR_CMD_EXEC);

	return rc;
}

/**
 * @brief	パイプライン終了
 * @param	none
 * @return	AG903_ENONE		正常終了
 * @return	AG903_ENONE以外	エラーコード
 * @note	none
 */
static int32_t jpg_term_capture(void)
{
	int32_t		rc;
	uint32_t	status;

	rc = AG903_PgpMgrExecPipeline(gPipelineHdl, AG903_PGP_MGR_CMD_IDLE);
	if(AG903_ENONE == rc) {
		/* パイプラインがIDLEになるまで待ち合わせる */
		do {
			rc = AG903_PgpMgrGetPipelineStatus(gPipelineHdl, NULL, NULL, NULL, NULL, &status);
			if(AG903_ENONE != rc) {
				break;
			}
		}while(status != AG903_PGP_MGR_STATE_IDLE);
	}
	if (rc == AG903_ENONE) { rc = AG903_PgpMgrEnableJPEG(false);  }
	if (rc == AG903_ENONE) { rc = AG903_PgpMgrReleasePipelineHandle(gPipelineHdl);  }
	if (rc == AG903_ENONE) { rc = AG903_ViaMgrReleaseInputHandle(gViaHdl);  }

	return rc;
}

/**
 * @brief	コールバック設定
 * @param	handle [in] JPGハンドル
 * @return	none
 * @note	none
 */
static void jpg_set_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;
	}

	gHook[AG903_JPG_HOOK_INITF].clbk  = jpg_init_clbk;
	gHook[AG903_JPG_HOOK_INITF].param = 0;
	gHook[AG903_JPG_HOOK_CMP  ].clbk  = jpg_end_clbk;
	gHook[AG903_JPG_HOOK_CMP  ].param = 0;
	gHook[AG903_JPG_HOOK_NG   ].clbk  = jpg_err_clbk;
	gHook[AG903_JPG_HOOK_NG   ].param = 0;
	gHook[AG903_JPG_HOOK_ESTE ].clbk  = jpg_err_clbk;
	gHook[AG903_JPG_HOOK_ESTE ].param = 0;
	gHook[AG903_JPG_HOOK_CAPE ].clbk  = jpg_err_clbk;
	gHook[AG903_JPG_HOOK_CAPE ].param = 0;
	gHook[AG903_JPG_HOOK_IMGE ].clbk  = jpg_err_clbk;
	gHook[AG903_JPG_HOOK_IMGE ].param = 0;
	gHook[AG903_JPG_HOOK_STRE ].clbk  = jpg_err_clbk;
	gHook[AG903_JPG_HOOK_STRE ].param = 0;
	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;
}

/** コーデック初期化完了コールバック */
static void jpg_init_clbk(AG903_JPGHandle *handle, uint32_t param)
{
	((void)handle);
	((void)param);

	SETSEQ(JPG_FLAG_INIT_CODEC);
}

/** コーデックデータ転送完了コールバック */
static void jpg_end_clbk(AG903_JPGHandle *handle, uint32_t param)
{
	((void)handle);
	((void)param);

	SETSEQ(JPG_FLAG_END_CODEC);
}

/** コーデックエラーコールバック */
static void jpg_err_clbk(AG903_JPGHandle *handle, uint32_t param)
{
	((void)handle);
	((void)param);

	SETSEQ(JPG_FLAG_ERR_CODEC);
}
