
#include "stdafx.h"
#include "cusb2.h"
#include <tlhelp32.h>
#include <io.h>
#include <conio.h>

#include "BonTsEngine/EventHandler.h"


//#define DEBUG_FW
//#define TS_BACK
#define S_MARGIN	(-30)		// 録画開始マージン
#define SE_MARGIN	(-40)		// 停止要求マージン
#define E_MARGIN	(+30)		// 録画終了マージン
#define APPVER		"sea(009_4)"

#ifdef DEBUG_FW
u8 fw_iic[1024*8];
#else
u8 fw_iic[]=
#include "fw.inc"
#endif

static char fw_id_str[] = "FX2_KAI4";
#define FW_ID	(fw_id_str)

#define CMD_EP6IN_START		0x50	//
#define	CMD_EP6IN_STOP		0x51	//
#define	CMD_EP2OUT_START	0x52	//
#define	CMD_EP2OUT_STOP		0x53	//
#define	CMD_PORT_CFG		0x54	//addr_mask, out_pins
#define	CMD_REG_READ		0x55	//addr	(return 1byte)
#define	CMD_REG_WRITE		0x56	//addr, value
#define	CMD_PORT_READ		0x57	//(return 1byte)
#define	CMD_PORT_WRITE		0x58	//value
#define	CMD_IFCONFIG		0x59	//value
#define	CMD_MODE_IDLE		0x5a
#define CMD_EP4IN_START		0x5b	//
#define	CMD_EP4IN_STOP		0x5c	//
#define	CMD_IR_CODE			0x5d	//val_l, val_h (0x0000:RBUF capture 0xffff:BWUF output)
#define CMD_IR_WBUF			0x5e	//ofs(0 or 64 or 128 or 192), len(max 64), data, ....
#define	CMD_IR_RBUF			0x5f	//ofs(0 or 64 or 128 or 192) (return 64byte)

#define BUF_LEN	(1024*64)
#define	QUE_NUM	16

#define	PIO_START	0x20
#define	PIO_IR_OUT	0x10
#define	PIO_IR_IN	0x08
#ifdef TS_BACK
#define PIO_TS_BACK	0x04
#endif

cusb2 *usb;
cusb2_tcb *tcb1;
cusb2_tcb *tcb2;

char *tsfname = NULL;	// TS ファイル名

LONG cmd_len=0,ret_len=0;
u8	cmd[64];
u8  ret[64];
CCyUSBEndPoint *inep;
CCyUSBEndPoint *outep;

#define BCAS_PRE_TIME	0
#define BCAS_POST_TIME	0

ULONG buffer_margin;	// TSストリームバッファマージン
ULONG buffer_size;		// TSストリームバッファサイズ

time_t start_time = 0;
time_t end_time = 0;
bool bcas = false;
bool udp = false;
bool http = false;
bool force_poff = false;
bool force_pon = false;
bool file_name_no_change = false;
bool ch_select = false;
bool ffmpeg = false;
bool vlc = false;
bool demux = false;
bool bdiag = false;
bool view_only = false;
FILE *wfp;
FILE *wfp2;
u16 ir_base = 0x00;
s32 ir_wait_setting;

WORD server_port;

// 電源制御
bool pon = false;
bool poff = false;

// 指定サービス記録
bool bSel = false;
int  service = 0;
char szCh[10];

// 未受信チェック
bool bRecvChk = false;
HANDLE hRecvChkTh = NULL;
// 未受信時にチャンネル制御をやり直すのでここに移動
u16 ir_cmd[32];
s32 ir_cmd_num = 0;

// B25,NULLパケット除去用
bool bB25 = false;
CEventHandler* pEventHandler;


// 別cap_stsの停止処理
HANDLE hStopEvent = NULL;
HANDLE hCmdReqEvent = NULL;


bool bStopEventRecv = false;
bool bStop = FALSE;
char logfilename[256];
char inifilename[256];
char appname[256];
char execname[256];
char eventname[256];
char ViewerOpt[256];
char enc_param[256];

// 別cap_stsからのコマンド受け付け
HANDLE hCmdThread = NULL;
bool bCmdThreadStop = false;
char cmdMapName[256];
char cmdReqEventName[256];
char cmdResEventName[256];
typedef struct {
	BYTE	cmd;
	int		add;
	time_t	end_time;
	char szCh[10];
} SHARED_DATA;
#define CMD_ADD_MIN		1
#define RES_ADD_MIN		101
#define CMD_GET_TIME	2
#define RES_GET_TIME	102
#define CMD_CHG_CN		3
#define RES_CHG_CN		103

#define VLC_INI_FILE	inifilename//".\\setting_vlc.ini"

//////////////////////////////////////////////////////////////////////////////
// ログ処理
#include <time.h>


void log_out(const char* szFormat, ...)
{
	va_list Args;
	TCHAR szTempStr[4096];
	TCHAR time_buf[128];
	static BOOL return_find=TRUE;

	va_start(Args , szFormat);
	vsprintf(szTempStr, szFormat, Args);
	va_end(Args);

	printf(szTempStr);

	if(return_find){

		struct tm *date;
		time_t now;
		int year, month, day;
		int hour, minute, second;

		/* 現在の日時を取得 */
		time(&now);
		date = localtime(&now);

		year = date->tm_year + 1900;
		month = date->tm_mon + 1;
		day = date->tm_mday;

		hour = date->tm_hour;
		minute = date->tm_min;
		second = date->tm_sec;

		sprintf(time_buf,"%04d/%02d/%02d %02d:%02d:%02d ",year,month,day,hour,minute,second);
	} else {
		time_buf[0] = NULL;
	}
	FILE *fout;
	if(!fopen_s(&fout, logfilename, "a+t")){
		time_t now = time(NULL);
		fwrite(time_buf,strlen(time_buf),1,fout);
		fwrite(szTempStr,strlen(szTempStr),1,fout);
		fclose(fout);
	}
	if(szTempStr[0] == NULL) return;
	return_find = FALSE;
	for(int i = (int)strlen(szTempStr)-1 ; i >= 0 ; i --){
		if(szTempStr[i] == '\n'){
			return_find = TRUE;
			break;
		}
	} 
}

//////////////////////////////////////////////////////////////////////////////
// CUSB側からのコールバック処理

bool write_func(u8 *buf, u32 len){

	time_t now;

	time( &now );							//秒単位で時刻を取得

	// 終了要求が来たら終了させる。
	if(WaitForSingleObject(hStopEvent,0) == WAIT_OBJECT_0) {
		ResetEvent(hStopEvent);
		// ただし初回受信時の場合は自分が出したイベントである可能性が高いので無視する
		if(bRecvChk) {
			printf("\n");
			log_out("終了要求を受けたので終了します。\n");
			bStopEventRecv = true;
			end_time = now;			// bsc出力を停止させるため
		}
	}
	bRecvChk = true;

	if(now < start_time && !bStopEventRecv) return true;		//BCASを先に記録させる

	// デコーダグラフに入力
	BOOL ret = pEventHandler->OnTsData(buf, len);
	if(ret == FALSE){
		log_out("致命的なエラーが発生しました。停止します。\n");
		bStopEventRecv = true;
		end_time = now;			// bsc出力を停止させるため
		return false;
	}

	if(now < end_time && !bStopEventRecv) return true;			//終了時間？

	cmd[cmd_len++]=CMD_EP6IN_STOP;
	if(bcas == false) 
		cmd[cmd_len++]=CMD_MODE_IDLE;
	usb->xfer(outep, cmd, cmd_len);
	cmd_len=0;
	return false;
}



bool write_bcas(u8 *buf, u32 len){
	time_t now;

	time( &now );			//秒単位で時刻を取得

//	fwrite(buf,1,len,wfp2);

	pEventHandler->OnBcasData(buf, len);

	if(now < end_time + BCAS_POST_TIME) return true;

	cmd[cmd_len++]=CMD_EP4IN_STOP;
	cmd[cmd_len++]=CMD_MODE_IDLE;
	usb->xfer(outep, cmd, cmd_len);
	cmd_len=0;
	return false;
}

//////////////////////////////////////////////////////////////////////////////
//リモコンコード送信処理

void ir_cmdtx(u16 *ircmd, s32 n){
	s32 i;
	s32 ir_wait = ir_wait_setting;
	for(i=0; i<n; i++){
		cmd[cmd_len++]=CMD_IR_CODE;
		cmd[cmd_len++]=(u8)ircmd[i];
		cmd[cmd_len++]=(u8)(ircmd[i] >> 8);
		usb->xfer(outep, cmd, cmd_len);
		cmd_len=0;
		if(ircmd[i] == 0x04d8) 
			ir_wait = 300;	//3digit
		Sleep(ir_wait);
		//IR出力停止
		cmd[cmd_len++]=CMD_IR_CODE;
		cmd[cmd_len++]=0;
		cmd[cmd_len++]=0;
		usb->xfer(outep, cmd, cmd_len);
		cmd_len=0;
		Sleep(100);
	}
}

u16 uniden_remocon(u8 *rbuf, s32 len){
	s32 i,j;
	u16 code = 0;

	//Interval timeをさがす
	for(i = j = 0; i < len - 2*16; i++){
		if(rbuf[len-1-i] == 0xff){
			if(++j > 3) break;
		}
		else{
			j = 0;
		}
	}
	if(j > 3){
		//Header(1) == 0 (2300us)をさがす
		for(;i < len - 2*16; i++) if((rbuf[len-1-i] & 1) == 0) break;
		if((i == len - 2*16) || (rbuf[len-1-i] < 200/2))
			return 0;
		//Header(2) == 1 (1100us)をさがす
		i++;
		if(rbuf[len-1-i] < 80/2)
			return 0;
		//Header(3) == 0 (600us)をさがす
		i++;
		if(rbuf[len-1-i] > 80/2)
			return 0;
		i++;
		for(j = 0; j < 16; j++){
			code = code >> 1;
			if(rbuf[len-1-i] > 100/2)
				code |= 0x8000;
			i+=2;
		}
		return(code);
	}
	return(0);
}

//////////////////////////////////////////////////////////////////////////////
//開始後PONチェック

// 開始から2秒たってもデータが受信されない場合は電源ONとチャンネル制御を行う
void recvchkthread_proc(void) {
//	Sleep(4000);
	if(bRecvChk)
		return;
	log_out("データが受信されないので電源ON制御を行います。\n");
	u16 cmd[16];
	s32 cmd_num = 0;

	cmd[cmd_num++] = 0x048b + ir_base;								//電源ON
	memcpy(&cmd[cmd_num],	ir_cmd , ir_cmd_num*sizeof(ir_cmd[0]));
	cmd_num += ir_cmd_num;
	ir_cmdtx(cmd, cmd_num);
	//チャンネル設定
	if(ir_cmd_num != 0)
		ir_cmdtx(ir_cmd, ir_cmd_num);

	if(bB25)
		pEventHandler->Reset();
}

//////////////////////////////////////////////////////////////////////////////
//コマンド受付スレッド処理
void ChSelect(char* szCh,u16* ir,s32& num);

void cmdThread_proc(void)
{
	if(_kbhit()){
		if(_getch() == 'q'){
			SetEvent(hStopEvent);
		}
	}

	if(WaitForSingleObject(hCmdReqEvent,0) == WAIT_OBJECT_0) {
		ResetEvent(hCmdReqEvent);
		HANDLE hCmdResEvent = CreateEvent(NULL,FALSE,FALSE,cmdResEventName);

		HANDLE hMap; /* ファイルマッピングオブジェクトのハンドル */
		SHARED_DATA	 *pData; /* 共有するデータ */

		/* ファイルマッピングオブジェクトの作成 */
		hMap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE,cmdMapName);

		if(hMap != NULL) {
			/* ビューの作成 */
			pData = (SHARED_DATA *)MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(SHARED_DATA));
			if(pData->cmd == CMD_ADD_MIN) {
				pData->cmd = RES_ADD_MIN;
				end_time += (pData->add * 60);
				pData->end_time = end_time;
				struct tm* stm = localtime(&end_time);
				log_out("%d 分延長し %02d:%02d:%02d まで記録します。\n",pData->add,stm->tm_hour,stm->tm_min,stm->tm_sec);
			}
			else if(pData->cmd == CMD_GET_TIME) {
				pData->cmd = RES_GET_TIME;
				pData->end_time = end_time;
			}
			UnmapViewOfFile(pData);
			CloseHandle(hMap);
		}
		SetEvent(hCmdResEvent);
		CloseHandle(hCmdResEvent);
	}
}

//////////////////////////////////////////////////////////////////////////////
//VLC起動関連

STARTUPINFO viewer_si;
PROCESS_INFORMATION viewer_pi;

// ウィンドウハンドルを取得しアプリケーションを終了させる。
BOOL CALLBACK EnumWindowsProc(HWND hWnd, LPARAM lParam)
{
    // CreateProcess()で取得したPROCESS_INFORMATION構造体のポインタを取得
    PROCESS_INFORMATION* pi = (PROCESS_INFORMATION*)lParam;

    // ウインドウを作成したプロセスIDを取得。
    DWORD lpdwProcessId = 0;
    ::GetWindowThreadProcessId(hWnd, &lpdwProcessId);

    // CreateProcessで起動したアプリのプロセスIDとメインウィンドウを
    // 作成したプロセスIDが同じ場合、起動したアプリを終了させる。
    if(pi->dwProcessId == lpdwProcessId)
    {
        ::SendMessage(hWnd, WM_CLOSE, 0, 0);
        return TRUE;
    }
    return TRUE;
}

	
bool OpenViewer(LPSTR vlc)
{
	// VLCの起動。
	::ZeroMemory(&viewer_si, sizeof(STARTUPINFO));
	viewer_si.cb           = sizeof(STARTUPINFO);
	viewer_si.dwFlags      = STARTF_USESHOWWINDOW;
	viewer_si.wShowWindow  = SW_SHOWNORMAL;
	::CreateProcess(NULL, vlc, NULL, NULL, FALSE, 0, NULL, NULL, &viewer_si, &viewer_pi);

	return TRUE;
}

void CloseVlc(void)
{
	// コールバック関数の呼び出し。
	EnumWindows(EnumWindowsProc, (LPARAM)&viewer_pi);

	// 終了するまで待機し、もし5秒待っても終わらない時は強制終了させる。
	if(::WaitForSingleObject(viewer_pi.hProcess, 5000) == WAIT_TIMEOUT){
		TerminateProcess(viewer_pi.hProcess, 0);
	}
}

//////////////////////////////////////////////////////////////////////////////
//FFMPEG起動関連

STARTUPINFO ffmpeg_si;
PROCESS_INFORMATION ffmpeg_pi;

bool OpenFFmpeg(LPSTR vlc)
{
	// ffmpegの起動。
	::ZeroMemory(&ffmpeg_si, sizeof(STARTUPINFO));
	ffmpeg_si.cb           = sizeof(STARTUPINFO);
	ffmpeg_si.dwFlags      = STARTF_USESTDHANDLES;
	ffmpeg_si.wShowWindow  = SW_SHOWNORMAL;

	ffmpeg_si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
	ffmpeg_si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
	ffmpeg_si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
	
	int ret = ::CreateProcess(NULL, vlc, NULL, NULL, FALSE, /*DETACHED_PROCESS*/0, NULL, NULL, &ffmpeg_si, &ffmpeg_pi);

	return TRUE;
}

void CloseFFmpeg(void)
{
	if(::WaitForSingleObject(ffmpeg_pi.hProcess, 0) == WAIT_TIMEOUT){
		log_out("ffmpegの終了を待っています。\n");
		::WaitForSingleObject(ffmpeg_pi.hProcess, 15*1000);
		TerminateProcess(ffmpeg_pi.hProcess, 0);
		log_out("ffmpeg終了。\n");
	}
}

//////////////////////////////////////////////////////////////////////////////
//スタンバイ処理

int term_commit;

#define STANDBY_DLG_CAPTION		_T("standby notice")

BOOL CALLBACK EnumWindowsProcMessageBox(HWND hWnd, LPARAM lParam)
{
	char className[256];

	GetWindowText(hWnd, className, 256);

	if(strcmp(className,STANDBY_DLG_CAPTION) == 0){

       ::SendMessage(hWnd, WM_CLOSE, 0, 0);
	}
        return TRUE;
}


DWORD WINAPI MonitorThread(LPVOID lpParam)
{
	
	term_commit = MessageBoxA(NULL,"あと10秒後にStandByに移行します。",STANDBY_DLG_CAPTION,MB_OKCANCEL|MB_ICONWARNING|MB_TOPMOST);

	return TRUE;
}

void DoSuspend(void)
{

	char buff[256];
	GetPrivateProfileString("POWER","STANDBY","OFF",buff,sizeof(buff),VLC_INI_FILE);

	if(strcmpi(buff, "ON") == 0){		// StandBy制御を行う
		DWORD dwThreadId;

		HANDLE hThread = CreateThread(NULL,0,MonitorThread,0,0,&dwThreadId); 

		int i;

		term_commit = -1;
		for(i = 0 ; i < 20 ; i ++){
			if(term_commit != -1) break;
			Sleep(500);
		}

		EnumWindows(EnumWindowsProcMessageBox, NULL);

		if(term_commit == IDCANCEL && i != 20){
			return;
		}

		BOOL sus,force;
		GetPrivateProfileString("POWER","fSuspend","1",buff,sizeof(buff),VLC_INI_FILE);
		sus = atoi(buff);
		GetPrivateProfileString("POWER","fForce","0",buff,sizeof(buff),VLC_INI_FILE);
		force = atoi(buff);

		HANDLE hToken;
		TOKEN_PRIVILEGES tknPrvlgs;

		OpenProcessToken(GetCurrentProcess(),
						 TOKEN_ADJUST_PRIVILEGES, &hToken);
		LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME,
							 &(tknPrvlgs.Privileges[0].Luid));
		tknPrvlgs.PrivilegeCount = 1;
		tknPrvlgs.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
		AdjustTokenPrivileges(hToken, FALSE, &tknPrvlgs, 0, NULL, NULL);
		SetSystemPowerState(sus,force);
	}
}

//////////////////////////////////////////////////////////////////////////////
//録画終了後に独自コマンドを実行

string& replace(string& str, const string sb, const string sa)
{
	string::size_type n, nb = 0;
	
	while ((n = str.find(sb,nb)) != string::npos)
	{
		str.replace(n,sb.size(),sa);
		nb = n + sa.size();
	}
	
	return str;
}

void DoAfterCommand(LPCTSTR tsname,LPCTSTR vlcparm)
{
	char buff[256],buff2[16];
	int i,j;
	string arg;

	char drv[256],path[256],name[256],ext[256];
	_splitpath(tsname,drv,path,name,ext);

	for(j = 0 ; j < 2 ; j ++){
		for(i = 1 ; ; i ++){
			sprintf(buff2,"cmd%d",i);
			if(j == 0){
				GetPrivateProfileString("COMMAND_AFTER",buff2,"",buff,sizeof(buff),VLC_INI_FILE);
				if(buff[0]==NULL) break;
			} else {
				if(vlcparm[0] == NULL) break;
				
				char buff3[256];
				sprintf(buff3,"COMMAND_AFTER_%s",vlcparm);
				GetPrivateProfileString(buff3,buff2,"",buff,sizeof(buff),VLC_INI_FILE);				
				if(buff[0]==NULL) break;
			}
			arg = buff;
			replace(arg,string("%FILE%"),string(tsname));
			replace(arg,string("%DRV%"),string(drv));
			replace(arg,string("%PATH%"),string(path));
			replace(arg,string("%NAME%"),string(name));
			replace(arg,string("%EXT%"),string(ext));

			if(buff[0] == NULL) break;

			::ZeroMemory(&viewer_si, sizeof(STARTUPINFO));
			viewer_si.cb           = sizeof(STARTUPINFO);
			viewer_si.dwFlags      = STARTF_USESHOWWINDOW;
			viewer_si.wShowWindow  = SW_SHOWNORMAL;
			log_out("cmd : %s",arg.c_str());
			::CreateProcess(NULL, (LPSTR)arg.c_str(), NULL, NULL, FALSE, 0, NULL, NULL, &viewer_si, &viewer_pi);
			::WaitForSingleObject(viewer_pi.hProcess, INFINITE);
		}
	}

}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//強制的に、チューナーをPOFFにする。

void ForcePoff(u8 fx2id)
{
	usb = new cusb2(NULL);
	int retry_cnt = 0;
RETRY:
	if(usb->fwload(fx2id, fw_iic,(u8 *)FW_ID/* NULL */))
	{
		u16 ir_post_cmd[16];
		s32 ir_post_cmd_num = 0;

		inep = usb->get_endpoint(0x81);
		outep = usb->get_endpoint(0x01);

		cmd[cmd_len++]=CMD_PORT_CFG;
		cmd[cmd_len++]=0x00;					//no addr(addr_mask)
		cmd[cmd_len++]=PIO_START|PIO_IR_OUT;	//START,IR_OUT(out_pins)
		cmd[cmd_len++]=0x72;

		usb->xfer(outep, cmd, cmd_len);
		cmd_len=0;

		ir_post_cmd[ir_post_cmd_num++] = 0x048c;					//電源OFF
		ir_cmdtx(ir_post_cmd, ir_post_cmd_num);
	} else {
		if(retry_cnt++ < 6){
			Sleep(3000);
			goto RETRY;
		}
	}

	delete usb;
}

void ForcePon(u8 fx2id)
{
	usb = new cusb2(NULL);
	int retry_cnt = 0;
RETRY:

	if(usb->fwload(fx2id, fw_iic,(u8 *)FW_ID/* NULL */))
	{
		u16 ir_cmd[16];
		s32 ir_cmd_num = 0;

		inep = usb->get_endpoint(0x81);
		outep = usb->get_endpoint(0x01);
		
		//FX2 I/Oポート設定　＆　MAX2初期パラメータ設定 ＆ MAX2リセット
		cmd[cmd_len++]=CMD_PORT_CFG;
		cmd[cmd_len++]=0x00;					//no addr(addr_mask)
		cmd[cmd_len++]=PIO_START|PIO_IR_OUT;	//START,IR_OUT(out_pins)

		usb->xfer(outep, cmd, cmd_len);
		cmd_len=0;

		ir_cmd[ir_cmd_num++] = 0x048b + ir_base;								//電源ON

		ir_cmdtx(ir_cmd, ir_cmd_num);
	} else {
		if(retry_cnt++ < 6){
			Sleep(3000);
			goto RETRY;
		}
	}

	delete usb;
}

void ForceIrtx(u8 fx2id , u16* ir , s32 num)
{
	usb = new cusb2(NULL);
	int retry_cnt = 0;
RETRY:

	if(usb->fwload(fx2id, fw_iic,(u8 *)FW_ID/* NULL */))
	{
		inep = usb->get_endpoint(0x81);
		outep = usb->get_endpoint(0x01);
		
		ir_cmdtx(ir, num);

	} else {
		if(retry_cnt++ < 6){
			Sleep(3000);
			goto RETRY;
		}
	}

	delete usb;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//起動前に、他のcap_stsがいないかを確認する。

void ChkAnotherCapsts(void)
{
	// タスクリストを参照し、自分以外にcap_stsがいないか調べる
	BOOL bFast = TRUE;
	BOOL bEventSend = TRUE;
	DWORD hMyProcessId = GetCurrentProcessId();
	int mode = 0;
	while(1) {
		HANDLE	hProcessSnap = NULL;
		PROCESSENTRY32   pe32;
		BOOL    bRet = FALSE;
		ZeroMemory ( &pe32, sizeof (pe32) );

		hProcessSnap = CreateToolhelp32Snapshot ( TH32CS_SNAPPROCESS, 0);
		if ( INVALID_HANDLE_VALUE == hProcessSnap ) {
			break;
		}
	
		// 開始予定時刻SE_MARGIN秒前になっても停止しない場合は前のcap_staを停止させる
		if((start_time+SE_MARGIN) <= time(NULL) || bStop)
			mode++;

		BOOL bExec = FALSE;
		pe32.dwSize = sizeof ( PROCESSENTRY32 );
		bRet = Process32First ( hProcessSnap, &pe32 );
		if ( bRet ) {
			do {
				// cap_sts.exeを発見
				if(strcmpi(pe32.szExeFile,execname) == 0) {
					// 自分では無い
					if(pe32.th32ProcessID != hMyProcessId) {
						bExec = TRUE;
						if(mode == 1 && bEventSend) {
							// 先ずはEventを使って終了要求 複数起動時でもSetEventは一回のみ
							hStopEvent = OpenEvent(EVENT_MODIFY_STATE,FALSE,eventname);
							SetEvent(hStopEvent);
							log_out("実行中の別%sへ終了要求を送信しました。\n",appname);
							CloseHandle(hStopEvent);
							bEventSend = FALSE;
						}
#if 0
						else if(mode == 3) {
							// それでも終了しない場合は強制終了
							HANDLE process_handle;
							process_handle = OpenProcess(PROCESS_TERMINATE, FALSE, pe32.th32ProcessID);
							if( process_handle ) {
								TerminateProcess(process_handle, 0);
								log_out("実行中の別%sを強制停止しました。\n",appname);
								CloseHandle(process_handle);
							}
						}
#endif
					}
				}
			} while ( Process32Next ( hProcessSnap, &pe32 ) );
		}
		CloseHandle ( hProcessSnap );

		if(!bExec || mode > 4)
			break;

		if(bFast && !bStop && bEventSend) {
			log_out("別の%sが動作中です。終了するのを待ちます。\n",appname);
			bFast = FALSE;
		}
		Sleep(1000);
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//B25関係初期化、終了処理

void B25Init(void)
{
	pEventHandler = new CEventHandler();

	if(server_port){
		pEventHandler->SetServerPort(server_port);
	}

	{	// demux出力時の音声チャンネル数決定
		char buff[_MAX_PATH*2];
		GetPrivateProfileString("FFMPEG_SETTING",enc_param,"",buff,sizeof(buff),VLC_INI_FILE);
		if(strstr(buff, "-ac 6")){	// 5.1chエンコード指定の場合
			pEventHandler->SetSoundMethod(3);		// 強制5.1ch出力指定
		} else {
			pEventHandler->SetSoundMethod(0);		// 通常出力指定
		}
	}

	pEventHandler->Init((bSel ? OPT_SEL : 0) | (bB25 ? OPT_B25 : 0) | (udp ? OPT_UDP : 0) | 
						(demux ? OPT_DEMUX : 0) | (bdiag ? OPT_BDIAG : 0) | (ffmpeg ? OPT_FFMPEG : 0) | 
						(http ? OPT_HTTP : 0)
						,
						tsfname,
						BUF_LEN,
						buffer_margin,
						buffer_size,
						service,
						GetPrivateProfileInt("ETC","UseOuterReader",0,VLC_INI_FILE)
						);
}

void B25Exit(void)
{
	pEventHandler->Exit();
	pEventHandler = NULL;
}



void OpenDecoder(void)
{
	if (ffmpeg){		// ffmpeg連携機能。
		http = true;

		char buff[_MAX_PATH*2];
		string arg;
		GetPrivateProfileString("FFMPEG_SETTING",enc_param,"",buff,sizeof(buff),VLC_INI_FILE);
		arg += buff;
		arg += " -y ";
		if(buff[0] == NULL){
			log_out("ffmpeg パラメータ %s がありません。\n",enc_param);
			return;
		}
		//拡張子チェック。aviとかmpgとかじゃないと、ffmpegがエラーはいて終了してしまう。
		int i;
		for(i = (int)strlen(tsfname) - 1 ; (i > 0 && tsfname[i] != '.') ; i --);
		if(i == 0){
			i = (int)strlen(tsfname) - 1;
		}
		if(strstr(buff, "-vcodec copy") || strstr(buff, "-f dvd") || strstr(buff, "-f vcd") || strstr(buff, "-f vob")){
			strcpy(&tsfname[i+1],"mpg");
		} else if(strstr(buff, "-f mp4") || strstr(buff, "-f psp") || strstr(buff, "-f ipod")){
			strcpy(&tsfname[i+1],"mp4");
		} else {
			strcpy(&tsfname[i+1],"avi");
		}

		arg += "\"";
		arg += tsfname;
		arg += "\"";

		log_out("ffmpeg Param:%s\n",arg.c_str());
//		arg = " -i \"http://localhost:1234/video.m2v\" -i \"http://localhost:1234/audio.aac\" " + arg;
//		arg = " -i \"http://localhost:1234/video.m2v\" -i \"http://localhost:1234/audio.wav\" " + arg;
		arg = " -f mpeg2video -i \"http://localhost:1234/video.m2v\" -f wav -i \"http://localhost:1234/audio.wav\" " + arg;
//		arg = " -i \"http://localhost:1234/audio.wav\" " + arg;
		GetPrivateProfileString("FFMPEG_SETTING","PATH","ffmpeg.exe",buff,sizeof(buff),VLC_INI_FILE);
		{
			FILE *fchk;
			if(fopen_s(&fchk,buff,"rb")) {
				log_out("ffmpeg.exe(%s)が見つかりません。\n",buff);
				return;
			}
			else {
				fclose(fchk);
			}
		}

		arg = buff + arg;
		OpenFFmpeg((LPSTR)arg.c_str());

	} else if(vlc) {	// vlc連携機能
		
		char buff[1024];
		GetPrivateProfileString("VLC_SETTING","VLC_PATH","C:\\Program Files\\VideoLAN\\VLC\\vlc.exe",buff,sizeof(buff),VLC_INI_FILE);
		string arg = buff;
		arg += " ";
		GetPrivateProfileString("VLC_SETTING",enc_param,"",buff,sizeof(buff),VLC_INI_FILE);
		if(buff[0] == NULL){
			log_out("VLC_PARAM : %s が見つからないため、udp://@で起動します。\n",enc_param);
			strcpy(buff,"udp://@");
		} else if(strcmpi(buff,"bontest") == 0){
			GetPrivateProfileString("VLC_SETTING","BONTEST_PATH","C:\\Program Files\\VideoLAN\\VLC\\vlc.exe",buff,sizeof(buff),VLC_INI_FILE);
			arg = buff;
			buff[0] = NULL;
		}
		arg += buff;
				
		if(buff[0] != '-'){
			replace(arg,string("%FILE%"),string(tsfname));
			log_out("VLC Open:%s\n",arg.c_str());
			OpenViewer((LPSTR)arg.c_str());
		}
	} else if (view_only){	// 視聴オプション
		string opt;
		opt = ViewerOpt;
		if(http){
			char buf[10];
			string url;
			itoa(server_port,buf,sizeof(buf));
			url = " http://localhost:";
			url += buf;
			url += "/video.ts";
			replace(opt,string("%URL%"),string(url.c_str()));
		}
		OpenViewer((LPSTR)opt.c_str());
	}

}


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//ライブラリ
int Is2ByteChar(unsigned char c)
{
    int esc = 0; /* 最終バイトの状態 */
    int rtn = 0; /* 返却値 */
	
	if (esc == 0) { /* ESC以外 */
		if ((c >= 0x20) && (c <= 0x7E)) {
			/* ASCII */
		} else if ((c <= 0x06) || (c == 0x7f) || (c == 0xff)) {
			rtn = 1; /* Binary */
		} else if (c == 0x1B) {
			esc = 1; /* ESC (1B) */
			rtn = 1; /* Not ASCII */
		} else if ((c >= 0x07) && (c <= 0x0d)) {
			/* ctrl */
		} else if ((c >= 0x0e) && (c <= 0x1F)) {
			rtn = 0; /* Binary */
		} else if (((c >= 0x80) && (c <= 0x8D))
			|| ((c >= 0x90) && (c <= 0xA0))) {
			rtn = 1; /* SJIS */
		} else if (c == 0xFE) {
			rtn = 1; /* EUC */
		} else {
			rtn = 0; /* Not ASCII */
		}
	} else if (esc == 1) { /* ESC (1B) */
		if (c == 0x24) {
			esc = 1; /* ESC (1B 24) */
		} else if (c == 0x28) {
			esc = 1; /* ESC (1B 28) */
		} else {
			esc = 0;
		}
	} else if (esc == 2) { /* ESC (1B 24) */
		esc = 0;
		if ((c == 0x40) || (c == 0x42)) {
			rtn = 1; /* JIS */
		}
		
	} else { /* ESC (1B 28) */
		esc = 0;
		if ((c == 0x42) || (c == 0x49) || (c == 0x4A)) {
			rtn = 1; /* JIS */
		}
	}
	
    return(rtn);
}


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//チャンネルセレクト

void ChSelect(char* szCh,u16* ir,s32& num)
{
	_mbslwr((unsigned char *)szCh);
	//チャンネル
	int n = 2;
	log_out("チャンネル[");
	if(szCh[1] == 'b'){
		//BS
		buffer_margin = GetPrivateProfileInt("TS_BUFFER","BS_MARGIN",512,VLC_INI_FILE) * 1024;
		log_out("BS");
		ir[num++] = 0x04e2 + ir_base;					//BS
		if((szCh[3]==0)||(szCh[4]==0)){
			if(szCh[3] != 0){//10,11,12ch
				n = 4;
				log_out("%s", szCh+2);
				if(szCh[3] == '0')
					ir[num++] = 0x0480 + ir_base;						//10ch
				else
					ir[num++] = 0x04d1 + ir_base + (szCh[3] - '1');	//11,12ch
			}
			else{
				n = 2;
			}
		}
		else{
			ir[num++] = 0x04d8 + ir_base;					//3-digit
			service = atoi(&szCh[2]);
		}
	}
	else if(szCh[1] == 'c'){
		//CS
		buffer_margin = GetPrivateProfileInt("TS_BUFFER","CS_MARGIN",1024,VLC_INI_FILE) * 1024;
		log_out("CS");
		ir[num++] = 0x04e3 + ir_base;					//CS
		if((szCh[3]==0)||(szCh[4]==0)){
			if(szCh[3] != 0){//10,11,12ch
				n = 4;
				log_out("%s", szCh+2);
				if(szCh[3] == '0')
					ir[num++] = 0x0480 + ir_base;						//10ch
				else
					ir[num++] = 0x04d1 + ir_base + (szCh[3] - '1');	//11,12ch
			}
			else{
				n = 2;
			}
		}
		else{
			ir[num++] = 0x04d8 + ir_base;					//3-digit
			service = atoi(&szCh[2]);
		}
	}
	else{
		//地デジ
		buffer_margin = GetPrivateProfileInt("TS_BUFFER","CHIDEZI_MARGIN",512,VLC_INI_FILE) * 1024;
		log_out("地デジ");
		ir[num++] = 0x04d6 + ir_base;					//地上
		ir[num++] = 0;
		if(szCh[2] != 0){//10,11,12ch
			n = 3;
			log_out("%s", szCh+1);
			if(szCh[2] == '0')
				ir[num++] = 0x0480 + ir_base;						//10ch
			else
				ir[num++] = 0x04d1 + ir_base + (szCh[2] - '1');	//11,12ch
		}
		else{
			n = 1;
		}
	}
	while(szCh[n] != 0){
		log_out("%c",szCh[n]);
		ir[num++] = 0x0480 + ir_base + (szCh[n] - '0');	//num
		n++;
	}
	log_out("]\n");
}


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//メイン処理

using namespace std;
#include <string>

static const char *def_tsfname = "test.ts2";

int _tmain(int argc, _TCHAR* argv[])
{
	s32 i;
	s32 sleep=0;
	time_t now;
	char tsfname_buf[256];
	u8 fx2id = 0;
	u16 ir_post_cmd[16];
	s32 ir_post_cmd_num = 0;

	buffer_size = 1024*1024*4;
	buffer_margin = 1024*1024*1;

	server_port = 1234;

	inep = NULL;
	outep = NULL;


#ifdef DEBUG_FW
	FILE *fp;
	fopen_s(&fp, "fw\\fifo.iic","rb");
	//fopen_s(&fp, "fw\\fifo_sdcc.iic","rb");
	fread(fw_iic, 1, sizeof(fw_iic), fp);
#endif
	u32 cap_mb = 10;

	// ログ用のファイル名を作成
	char szModulePath[256],drv[10],path[256],ext[256];
	enc_param[0] = NULL;
	GetModuleFileName(NULL,szModulePath,256);
	_splitpath(szModulePath,drv,path,appname,ext);
	_makepath(logfilename,drv,path,appname,".log");
	_makepath(inifilename,drv,path,"cap_sts_sea",".ini");
	_makepath(execname,NULL,NULL,appname,ext);
	sprintf(eventname,"%s_stopeventname",appname);
	sprintf(cmdMapName,"%s_cmdMapFileName",appname);
	sprintf(cmdReqEventName,"%s_cmdReqEventName",appname);
	sprintf(cmdResEventName,"%s_cmdResEventName",appname);


	// iniファイルがあるかをチェック
	FILE *fchk;
	if(fopen_s(&fchk,VLC_INI_FILE,"r") == 0) {
		fclose(fchk);
	} else {
		log_out("注意：ini ファイル(%s)がありません。\n",VLC_INI_FILE);
		log_out("　　　デフォルト設定で起動します。\n");
	}

	ir_wait_setting = GetPrivateProfileInt("ETC","IrWait",600,VLC_INI_FILE);

	// パラメータを順不同にするため一時退避
	char szCh[10];
	memset(szCh,0,10);

	time( &now );			//秒単位で時刻を取得
	if(argc == 1){
		printf("＜使い方＞ %s\n",APPVER);
		printf("%s [ファイル名] [@チャンネル] [時間] [B] [I数値]\n",appname);
		printf("  ファイル名：拡張子省略時は\".ts2\"。ファイル名省略時は\"test.ts2\"。\n");
		printf("              ファイル名後部に、\"_年月日_時分秒_@チャンネル\"が付きます。\n");
		printf("  P         ：開始時に電源ON制御を行う。\n");
		printf("  POFF      ：終了時に電源OFF制御を行う(外部からの停止時は行わない。\n");
		printf("  R数値(1-3)：チューナーのリモコン番号を指示。（チャンネルの前に指定）\n");
		printf("  チャンネル：地デジは\"@1\"〜\"@12\"。BSは\"@B3\"、CSは\"@C2\"、3桁指定も可。\n");
		printf("  時間      ：数値\"30\"で記録時間(分)。\"19:00-19:30\"で時刻指定。\n");
		printf("  B or B25  ：内蔵のB-CASリーダを使い、B25処理を行います。\n");
		printf("  I数値     ：対象のカメレオンFX2のIDを指定。省略時は0。\n");
		printf("  SEL       ：地デジではデータ放送の削除を行います。\n");
		printf("              BS/CSでCHの3桁指定時は、指定チャンネルのサービスのみを記録\n");
		printf("  STOP      ：現在動作中の%sに終了要求を出します(録画等は行いません)\n",appname);
		printf("  ADD 分    ：現在動作中の%sの終了時間を指定分延長します\n",appname);
		printf("  view      ：cap_sts_sea.iniに登録されたプレイヤーで視聴を行います。\n");
		printf("  vlc:[parm]：vlc経由で録画します。\n");
		printf("  ffmpeg:[parm]：ffmpeg経由で録画します。\n");
		printf("\n＜使用例＞\n");
		printf("「cap_sts ニュース @B101 30」 BS101で\"ニュース.ts2\"で作成。記録時間は30分。\n");
		printf("「cap_sts 19:00-19:30 B」 現チャンネル19:00-19:30を\"test.ts2\"\n");
		exit(-1);
	}
	else{
		// タスク実行時用にログに残す
		struct tm *ntm = localtime(&now);
		log_out("%04d/%02d/%02d %02d:%02d:%02d %s開始 %s\n",ntm->tm_year+1900,ntm->tm_mon+1,ntm->tm_mday,ntm->tm_hour,ntm->tm_min,ntm->tm_sec,appname,APPVER);
		for(i=0; i<argc; i++)
			log_out("%s ",argv[i]);
		log_out("\n");
		for(i=1; i<argc; i++){
			BOOL time_check = TRUE;
			DWORD str_ptr;
			for(str_ptr = 0;argv[i][str_ptr] != NULL ; str_ptr++) {
				if(
					!(
					(argv[i][str_ptr] >= '0') && (argv[i][str_ptr]) <= '9' ||
					(argv[i][str_ptr] == ':') || 
					(argv[i][str_ptr] == '-')
					)
				){
					time_check = FALSE;
					break;
				}
			}
			if(time_check) {
				//時間
				if(strchr(argv[i], ':')!=NULL){//nn:nn指定
					struct tm *nt;
					s32 st_h,st_m,en_h,en_m;
					nt = localtime( &now );	//時刻を tm 構造体形式に変換
					if(sscanf(argv[i], "%d:%d-%d:%d", &st_h, &st_m, &en_h, &en_m) == 4){//正常処理
						if((st_h * 60 + st_m) < (nt->tm_hour * 60 + nt->tm_min)){
							//翌日分なので24時間追加
							st_h += 24;
							en_h += 24;
						}
						if((st_h * 60 + st_m) > (en_h * 60+ en_m))
							//終了時刻が日付をまたぐので
							en_h += 24;
						//何秒後？
						sleep = (st_h * 60*60 + st_m * 60) - (nt->tm_hour * 60*60 + nt->tm_min * 60 + nt->tm_sec);
						end_time = now + sleep + ((en_h * 60*60 + en_m * 60) - (st_h * 60*60 + st_m * 60));
						int min = (en_h * 60 + en_m) - (st_h * 60 + st_m);
						st_m = sleep - (sleep/(60*60))*(60*60);
						log_out("%2.2d:%2.2d:%2.2d 秒後から%2.2d:%2.2d 分間記録します。\n", sleep/(60*60), st_m/60 , sleep%60, min/60, min%60);
					}
				}
				else{
					char *s;
					int min = strtol(argv[i], &s, 10);
					end_time = now + min*60;
					log_out("現在時刻から%2.2d:%2.2d 分間記録します。\n", min/60, min%60);
				}
			}
			else if(argv[i][0] == '@'){
				// チャンネル指定
				strcpy(szCh,argv[i]);
			}
			else if(strcmpi(argv[i], "b") == 0){
				//BCASログ指定
				bcas = true;
				bB25 = true;		// B25も有効にする。
			}
			else if((argv[i][0] == 'r' || argv[i][0] == 'R') && isdigit(argv[i][1]) && (argv[i][2]==0) ){
				char *s;
				ir_base = (u8)strtol(argv[i]+1, &s, 10);
				log_out("リモコン番号を %d に設定しました。\n", ir_base);
				ir_base = (ir_base - 1) * 0x80;
			}
			else if(argv[i][0] == 'v' && argv[i][1] == 'l' && argv[i][2] == 'c' && argv[i][3] == ':'){
				//Network出力
				udp = true;
				vlc = true;
				bcas = true;
				bB25 = true;		// B25も有効にする。
				strcpy(enc_param,&argv[i][4]);
			}
			else if(strcmpi(argv[i], "b25") == 0){
				//B25指定
				bcas = true;		// 内蔵B-CASも有効にする。
				bB25 = true;
			}
			else if(strcmpi(argv[i],"fpoff") == 0){
				force_poff = true;
			}
			else if(strcmpi(argv[i],"fpon") == 0){
				force_pon = true;
			}
			else if(strcmpi(argv[i],"fn") == 0){
				file_name_no_change = true;
			}
			else if(strcmpi(argv[i],"view") == 0){
				bcas = true;		// 内蔵B-CASも有効にする。
				bB25 = true;
				bSel = true;
				view_only = true;
				ir_wait_setting=GetPrivateProfileInt("ETC","IrWait_View",600,VLC_INI_FILE);
			}
			else if(strcmpi(argv[i], "p") == 0){
				//開始時PON指定
				ir_cmd[ir_cmd_num++] = 0x048b + ir_base;								//電源ON
			}
			else if((argv[i][0] == 'r')&&(isdigit(argv[i][1]))){
				char *s;
				ir_base = (u8)strtol(argv[i]+1, &s, 10);
				log_out("リモコン番号を %d に設定しました。\n", ir_base);
				ir_base = (ir_base - 1) * 0x80;
			}
			else if(strcmpi(argv[i], "poff") == 0){
				ir_post_cmd[ir_post_cmd_num++] = 0x048c + ir_base;					//電源OFF
				poff = true;
			}
			else if(strcmpi(argv[i], "stop") == 0){
				//終了要求指定
				bStop = true;
			}
			else if(strcmpi(argv[i], "sel") == 0){
				//サービス選択
				bSel = true;
			}
			else if(strcmpi(argv[i], "bdiag") == 0){
				//B-CAS ストリームマージン確認モード
				//pEcm->SetDiag(TRUE);
				bdiag = TRUE;
			}
			else if(strcmpi(argv[i], "add") == 0){
				//コマンド送信
				HANDLE hMap = CreateFileMapping(
					(HANDLE)-1,
					NULL,
					PAGE_READWRITE,
					0, sizeof(SHARED_DATA), 
					cmdMapName);

				SHARED_DATA* pData = (SHARED_DATA *)MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(SHARED_DATA));

				if(i+1 < argc) {
					pData->cmd = CMD_ADD_MIN;
					pData->add = atoi(argv[i+1]);
				}
				else {
					pData->cmd = CMD_GET_TIME;
				}

				UnmapViewOfFile(pData);
				
				HANDLE hCmdReqEvent = CreateEvent(NULL,FALSE,FALSE,cmdReqEventName);
				HANDLE hCmdResEvent = CreateEvent(NULL,FALSE,FALSE,cmdResEventName);
				SetEvent(hCmdReqEvent);
				CloseHandle(hCmdReqEvent);
				WaitForSingleObject(hCmdResEvent,1000);
				ResetEvent(hCmdResEvent);
				CloseHandle(hCmdResEvent);
				pData = (SHARED_DATA *)MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(SHARED_DATA));
				if(pData->cmd == RES_ADD_MIN) {
					struct tm* stm = localtime(&pData->end_time);
					log_out("%d 分延長し %02d:%02d:%02d まで記録します。\n",pData->add,stm->tm_hour,stm->tm_min,stm->tm_sec);
				}
				else if(pData->cmd == RES_GET_TIME) {
					struct tm* stm = localtime(&pData->end_time);
					log_out("%02d:%02d:%02d まで記録します。\n",stm->tm_hour,stm->tm_min,stm->tm_sec);
				}
				UnmapViewOfFile(pData);
				CloseHandle(hMap);
				return 0;
			}
			else if((argv[i][0] == 'i' || argv[i][0] == 'I')&&(isdigit(argv[i][1]))&&(argv[i][2]==0)){
				//カメFX2　ID指定
				char *s;
				fx2id = (u8)strtol(argv[i]+1, &s, 10);
				log_out("ターゲットのカメレオンUSB FX2のIDを %d に設定しました。\n", fx2id);
			}
			else if(argv[i][0] == 'c' && argv[i][1] == 'h' && argv[i][2] == ':'){
				//チャンネル制御のみ
				strcpy(szCh,&argv[i][3]);
				ch_select = true;
				ir_wait_setting=GetPrivateProfileInt("ETC","IrWait_View",600,VLC_INI_FILE);
			}
			else if(argv[i][0] == 'f' && argv[i][1] == 'f' && argv[i][2] == 'm' && argv[i][3] == 'p' && argv[i][4] == 'e' && argv[i][5] == 'g' && argv[i][6] == ':'){
				bcas = true;		// 内蔵B-CASも有効にする。
				bB25 = true;
				ffmpeg = true;
				strcpy(enc_param,&argv[i][7]);
			}
			else if(strcmpi(argv[i], "demux") == 0){
				bcas = true;		// 内蔵B-CASも有効にする。
				bB25 = true;
				demux = true;
			}
			else{
				//ファイル名
				tsfname = tsfname_buf;
				strcpy(tsfname, argv[i]);
				if(strchr(tsfname, '.') == NULL){
					strcat(tsfname, ".ts2");
				}
			}
		}
	}

	if(szCh[0] != 0x00 && !ch_select) {
		ChSelect(szCh,ir_cmd,ir_cmd_num);
	}

	//// サービスNoが0(未設定)の場合はSELフラグをクリアする。
	//if(service == 0)
	//	bSel = false;

	start_time = now + sleep;
	// 30秒前から録画開始するように変更
	if(sleep != 0){
		sleep = sleep+S_MARGIN;
		if(sleep < 0) sleep = 0;
	}
	if(bcas){
		//BCAS記録あり
		if(sleep != 0){
			sleep -= BCAS_PRE_TIME;
			if(sleep < 0) sleep = 0;
		}
		else{
			start_time += BCAS_PRE_TIME;
		}
	}
	if(end_time == 0 && !bStop) {
		end_time = start_time + 20;	//デフォルト記録時間は20秒
		log_out("%d 秒後から%d 秒間記録します。\n", (int)(start_time - now), (int)(end_time - start_time));
	}
	else {
		// 30秒余計に録画します。
		end_time = end_time+E_MARGIN;
	}

	time_t echktime = time(NULL);

	if(!ch_select){
		ChkAnotherCapsts();		// 他にcap_stsがいないか確認する。を関数化。いたら強制停止。
	}

	// ch:***で、チャンネル切替のみ指定時
	if(ch_select){
		u16 irbuf_ptr[16];
		s32 irbuf_num = 0;
		memset(irbuf_ptr,NULL,sizeof(irbuf_ptr));
		ChSelect(szCh,irbuf_ptr,irbuf_num);
		ForceIrtx(fx2id,irbuf_ptr,irbuf_num);
	}
	// パラメータに"stop"が指定されていた場合はここで終了
	if(bStop || ch_select){
		if(force_poff){
			ForcePoff(fx2id);
		} else if (force_pon){
			ForcePon(fx2id);
		}
		return 0;
	}

	// 他cap_stsからの終了要求を受けるためのイベントを作成
	hStopEvent = CreateEvent(NULL,FALSE,FALSE,eventname);

	if(tsfname == NULL) tsfname = (char *)def_tsfname;
	
	struct tm *stm = localtime(&start_time);
	{	// 出力ファイル名設定
		
		bool bFileChange = false;
		char drv[256],path[256],name[256],name2[256],ext[256],srcName[256],szEx[10];
		_splitpath(tsfname,drv,path,name,ext);
		int ptr;
		for(ptr = 0 ; name[ptr] != NULL ; ptr++){
			if(Is2ByteChar(name[ptr])){
				ptr++;
			} else {
				if(/* tsfname[ptr] == '\\' || */ name[ptr] == '/' || name[ptr] == ':' || name[ptr] == '?' ||		// 禁則文字使っていた場合
					name[ptr] == '*' || name[ptr] == '"' || name[ptr] == '<' || name[ptr] == '>' || name[ptr] == '|'){
						name[ptr] = '_';
				}
			}
		}
		if(file_name_no_change==false){
			sprintf(name2,"%s_%04d%02d%02d_%02d%02d%02d%s%s",name,stm->tm_year+1900,stm->tm_mon+1,stm->tm_mday,stm->tm_hour,stm->tm_min,stm->tm_sec,szCh[0] == 0x00 ? "":"_",szCh);
			strcpy(name,name2);
			for(int i = 0;i < 100;i++) {
				strcpy(name2,name);
				if(i == 0)
					szEx[0] = 0x00;
				else
					sprintf(szEx,"(%d)",i);
				strcat(name2,szEx);
				_makepath(srcName,drv,path,name2,ext);
				if(_access(srcName,0) != 0) {
					tsfname = srcName;
					if(i != 0)
						bFileChange = TRUE;
					break;
				}
			}
		}
		// ファイルが出力可能かチェックする
		bool bFileError = false;
#if 0
		FILE *fchk;
		if(fopen_s(&fchk,tsfname,"wb")) {
			bFileError = true;
		}
		else {
			int dmysize = fwrite("dummy",1,5,fchk);
			if(dmysize != 5)
				bFileError = true;
			fclose(fchk);
		}
#endif
		if(bFileError) {
			struct tm *stm = localtime(&start_time);
			sprintf(name,"%s_%04d%02d%02d_%02d%02d%02d%s%s",appname,stm->tm_year+1900,stm->tm_mon+1,stm->tm_mday,stm->tm_hour,stm->tm_min,stm->tm_sec,szCh[0] == 0x00 ? "":"_",szCh);
			_makepath(srcName,drv,path,name,ext);
			tsfname = srcName;
			bFileChange = true;
		}

		if(bFileChange)
			log_out("出力ファイル名を%sに変更します。\n",tsfname);
	}

	buffer_size = GetPrivateProfileInt("TS_BUFFER","MAX",4,VLC_INI_FILE) * 1024 * 1024;

	log_out("TS Buffer Max : %d MB(margin : %d kB)\n",buffer_size/(1024*1024),buffer_margin/(1024));
	int retry_cnt = 0;

	if (view_only){
		char param[128],param2[128],buff_prtcl[30];
		GetPrivateProfileString("VIEWER","SELECT","VLC",param,sizeof(param),VLC_INI_FILE);
		GetPrivateProfileString("VIEWER",param,"C:\\Program Files\\VideoLAN\\VLC\\vlc.exe udp://@",ViewerOpt,sizeof(ViewerOpt),VLC_INI_FILE);		
		sprintf(param2,"%s_PRTCL",param);
		GetPrivateProfileString("VIEWER",param2,"UDP:1234",buff_prtcl,sizeof(buff_prtcl),VLC_INI_FILE);
		size_t i;
		for(i = strlen(buff_prtcl) - 1 ; (i && buff_prtcl[i] != ':') ; i --);
		if(i){
			buff_prtcl[i] = NULL;
			server_port = atoi(&buff_prtcl[i+1]);
		}

		if(strcmpi(buff_prtcl, "udp") == 0){
			udp = true;
		} else if(strcmpi(buff_prtcl, "http") == 0){
			http = true;
		} else {
			log_out("viewerのプロトコル %s が理解できません。\n",param);
		}
	}

//↑オプション解釈完了
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//↓実行

	hCmdReqEvent = CreateEvent(NULL,FALSE,FALSE,cmdReqEventName);	

	usb = new cusb2(NULL);

	B25Init();

RETRY:
	if(usb->fwload(fx2id, fw_iic,(u8 *)FW_ID/* NULL */))
	{
		inep = usb->get_endpoint(0x81);
		outep = usb->get_endpoint(0x01);

		//FX2 I/Oポート設定　＆　MAX2初期パラメータ設定 ＆ MAX2リセット
		cmd[cmd_len++]=CMD_PORT_CFG;
		cmd[cmd_len++]=0x00;					//no addr(addr_mask)
#ifdef TS_BACK
		cmd[cmd_len++]=PIO_START|PIO_IR_OUT|PIO_TS_BACK;	//out_pins  
#else
		cmd[cmd_len++]=PIO_START|PIO_IR_OUT;	//START,IR_OUT(out_pins)
#endif
//		cmd[cmd_len++]=CMD_MODE_IDLE;
//		cmd[cmd_len++]=CMD_IFCONFIG;
//		cmd[cmd_len++]=0xE3;			//slave FIFO, IFCLK=48MHz, IFCLK出力,  同期(Sync)モード
		cmd[cmd_len++]=0x70;
		cmd[cmd_len++]=0x71;
		usb->xfer(outep, cmd, cmd_len);
		cmd_len=0;

		//チャンネル設定
		// 他cap_sts終了を待った分をsleepからマイナスする。
		time_t echktime2 = time(NULL);
		if(sleep > (s32)(echktime2-echktime)){
			BOOL ir_trust = GetPrivateProfileInt("ETC","IrTrust",1,VLC_INI_FILE);
			log_out("[記録開始時間まで待機します]\n");
			for(;;){
				echktime2 = time(NULL);
				if(sleep <= (s32)(echktime2-echktime)) break;
				if(ir_trust && ir_cmd_num && (sleep - (s32)(echktime2-echktime)) <= 30){	// Sleep30秒前で、リモコンコードがある場合
					ir_cmdtx(ir_cmd, ir_cmd_num);	// ただ待つのはもったいないため、リモコンコードを再度送信して、CH切り替えの信頼性を高める。
				} else {
					Sleep(1000);
				}
			}
		}

		if(ir_cmd_num != 0){
			ir_cmdtx(ir_cmd, ir_cmd_num);
		}

		OpenDecoder();
		if(http){
			BOOL ret = pEventHandler->CheckServerConnection();			// httpの場合、ひとつ以上のサーバーコネクションを確認する。
			if(ret == FALSE){
				log_out("Http接続のサーバーコネクションタイムアウト\n");
				goto END;
			}
		}

		time_t stime = time(NULL);
		log_out("記録を開始します。\n");
/*
		// bcasファイル作成時はbcs記録スタートから15秒後にts記録が始まるようにする。
		if(bcas) {
			start_time = stime + GetPrivateProfileInt("TS_BUFFER","TS_START_MARGIN",5,VLC_INI_FILE);
		}
		else 
			start_time = stime;		// bcs無しのときはすぐにts記録が始まるようにする。
*/
		start_time = stime;		// とにかく早く出画したいので、すぐ始める。ハンドシェイクはBonTsEngine側で。

		tcb1 = usb->start_thread(0x86, BUF_LEN, QUE_NUM, write_func);

		if(bcas){
			tcb2 = usb->start_thread(0x84, 512, 1, write_bcas);
		}

		if(!bB25){
			cmd[cmd_len++]=CMD_EP6IN_START;
		}
		usb->xfer(outep, cmd, cmd_len);
		cmd_len=0;

		log_out("カメレオンUSB FX2 -> PC 記録中\n");
		printf("Press 'q' to quit.\n");
//		hRecvChkTh = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)recvchkthread_proc, NULL, 0, NULL);
//		hCmdThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)cmdThread_proc, NULL, 0, NULL);


		DWORD wait_loop_cnt = 0;
		DWORD PacketNum_First,Scrambling_First,Error_First,BitErr_First;
		DWORD Scrambling_b = 0,Error_b = 0,BitErr_b = 0;
		DWORD Scrambling_n = 0,Error_n = 0,BitErr_n = 0;
		BOOL err_view = FALSE;

		#define POL_INTERVAL	500
		
		while(WaitForSingleObject(tcb1->th,POL_INTERVAL)==WAIT_TIMEOUT){		// 終了待ちLoop
			wait_loop_cnt++;
			cmdThread_proc();

			if(wait_loop_cnt == (4000/POL_INTERVAL)){		// 4秒後
				recvchkthread_proc();
			}
			if(wait_loop_cnt < (7000/POL_INTERVAL)){		// 7秒後
				PacketNum_First = pEventHandler->GetInputPacketNum();
				Scrambling_First = pEventHandler->GetScramblingPacketNum();		// 始め7秒間のエラーはカウントしない(seaの処理上、最初のScramblingが避けられない)
				Error_First = pEventHandler->GetErrorPacketNum();
				BitErr_First = pEventHandler->GetErrorIndicatorPacketNum();
			} else if(!ffmpeg){
				time_t est_time = time(NULL) - stime;
				int hour = (int)(est_time / (60*60));
				int min = (int)((est_time - hour*(60*60)) / 60);
				int sec = (int)(est_time - hour*(60*60) - min*60);

				Scrambling_n = pEventHandler->GetScramblingPacketNum() - Scrambling_First;
				Error_n = pEventHandler->GetErrorPacketNum() - Error_First;
				BitErr_n = pEventHandler->GetErrorIndicatorPacketNum() - BitErr_First;

				if(Scrambling_b != Scrambling_n || Error_b != Error_n || BitErr_b != BitErr_n){	// エラー数に変化があった場合
					if(err_view == FALSE){
						err_view = TRUE;
					}
					log_out("\rtime %02d:%02d:%02d  Packet=%u , Scrambling=%u , Drop=%u , BitError=%u\n"
																			, hour , min , sec
																			, pEventHandler->GetInputPacketNum()
																			, Scrambling_n
																			, Error_n
																			, BitErr_n
																			);
				} else {
					printf("\rtime %02d:%02d:%02d  Packet=%u , Scrambling=%u , Drop=%u , BitError=%u"
																			, hour , min , sec
																			, pEventHandler->GetInputPacketNum()
																			, Scrambling_n
																			, Error_n
																			, BitErr_n
																			);
					err_view = FALSE;
				}
				Scrambling_b = Scrambling_n; 
				Error_b = Error_n; 
				BitErr_b = BitErr_n;
			}
		}

		usb->delete_thread(tcb1);
		if(bcas){
			usb->delete_thread(tcb2);
		}

		time_t etime = time(NULL);
		struct tm* etm = localtime(&etime);

		printf("\n");
		log_out("Result : Packet=%u , Scrambling=%u , Drop=%u , BitError=%u\n" 
																	, pEventHandler->GetInputPacketNum()
																	, pEventHandler->GetScramblingPacketNum() - Scrambling_First
																	, pEventHandler->GetErrorPacketNum() - Error_First
																	, pEventHandler->GetErrorIndicatorPacketNum() - BitErr_First
																	);
		log_out("記録を終了しました。\n");

		CloseHandle(hRecvChkTh);
		bCmdThreadStop = true;
		WaitForSingleObject(hCmdThread,1000);
		CloseHandle(hCmdThread);

		//電源OFF用 他cap_stsから終了要求をうけた時はpoffを行わない
		if(ir_post_cmd_num != 0 && !bStopEventRecv){
			ir_cmdtx(ir_post_cmd, ir_post_cmd_num);

			cmd[cmd_len++]=0x72;
			usb->xfer(outep, cmd, cmd_len);
			cmd_len=0;
//			Sleep(500);
		}
	}
	else{
		log_out("ターゲットのカメレオンUSB FX2が認識できません。\n");
		if(retry_cnt++ < 6){
			Sleep(3000);
			goto RETRY;
		}
	}

END:
	if(vlc || view_only){
		CloseVlc();
	}

	CloseHandle(hStopEvent);
	CloseHandle(hCmdReqEvent);

	B25Exit();

	if(ffmpeg){
		CloseFFmpeg();
	}

	delete usb;

	if(tsfname){
		DoAfterCommand(tsfname,enc_param);
	}
	//電源OFF用 他cap_stsから終了要求をうけた時はpoffを行わない
	if(ir_post_cmd_num != 0 && !bStopEventRecv){
		DoSuspend();
	}

	return 0;
}

