//NetTalk
/*------------------------------------------------------------------------------*\
 =============================
   模块名称: AVIOMgr.cpp
 =============================
 
 [版权]
 
   2000-2002  115软件工厂  版权所有
                                              
\*------------------------------------------------------------------------------*/

#include "WndX.h"
#include <vfw.h>

#include "AVIOMgr.h"
#include "UDPSocket.h"
#include "g729a.h"
#include "AudioPlay.h"
#include <stdio.h>
/*------------------------------------------------------------------------------*/
CAVIOMgr* pMgrInst=0;


/*------------------------------------------------------------------------------*/
//声音输入线程，包括声音的压缩
DWORD WINAPI CAVIOMgr::AudioInThreadProc(LPVOID lpParameter)
{
	CAVIOMgr* pMgr=(CAVIOMgr*)lpParameter;
	MSG msg;
	while(GetMessage(&msg,0,0,0))
	{
		switch(msg.message)
		{
		case WIM_DATA:
			{
				WAVEHDR* pWH=(WAVEHDR*)msg.lParam;
				waveInUnprepareHeader((HWAVEIN)msg.wParam,pWH,sizeof(WAVEHDR));
							
				if(pWH->dwBytesRecorded!=SIZE_AUDIO_FRAME)
					break;//it's not full recorded,i think the wave recorder has ben
				//stopped,discard it,and don't do anything!
				//
				CopyMemory(pMgr->m_AudioLocal,pWH->lpData,SIZE_AUDIO_FRAME);
				pMgr->OnEncodeAudioData(pMgr->m_AudioLocal,SIZE_AUDIO_FRAME);
				
				waveInPrepareHeader((HWAVEIN)msg.wParam,pWH,sizeof(WAVEHDR));
				waveInAddBuffer((HWAVEIN)msg.wParam,pWH,sizeof(WAVEHDR));
			}
			break;
		}
	}	
	return msg.wParam;
}

/*------------------------------------------------------------------------------*/
//音频输出线程，包括音频解码
DWORD WINAPI CAVIOMgr::AudioOutThreadProc(LPVOID lpParameter)
{
	
	CAVIOMgr* pMgr=(CAVIOMgr*)lpParameter;
	MSG msg;
	while(GetMessage(&msg,0,0,0))
	{
		switch(msg.message)
		{
		case IOM_AUDIO:
			pMgr->OnDecodeAudioData((PACK_AUDIO*)msg.lParam,(int)msg.wParam);
			break;
		case WOM_DONE:
			{
				WAVEHDR* pwh=(WAVEHDR*)msg.lParam;
				waveOutUnprepareHeader((HWAVEOUT)msg.wParam,pwh,sizeof(WAVEHDR));
				pMgr->m_iAudioBuf--;
				delete []pwh->lpData;//删除Play调用时分配的内存
				delete pwh;
			}
			break;
		}
	}
	return msg.wParam;
}

/*------------------------------------------------------------------------------*/
//视频流回调函数
LRESULT CALLBACK CAVIOMgr::VideoStreamCallbackProc(HWND hWnd, LPVIDEOHDR lpVHdr)
{
	
	CAVIOMgr* pMgr=(CAVIOMgr*)capGetUserData(hWnd);
	if(pMgr)
	{
		//压缩视频数据
		pMgr->OnEncodeVideoData((char*)lpVHdr->lpData,lpVHdr->dwBytesUsed);
		
	}	
	return TRUE;
}

/*------------------------------------------------------------------------------*/
//Socket线程，负责接收数据
DWORD WINAPI CAVIOMgr::SockThreadProc(LPVOID lpParameter)
{
	CAVIOMgr* pMgr=(CAVIOMgr*)lpParameter;

	char buf[4096];
	int iLen=0;
	while(1)
	{
		iLen=pMgr->m_Socket.RecvFrom(buf,4096,(sockaddr*)&pMgr->m_sockaddr);
		if(iLen>0)
		{
			
			switch(*((short*)buf))//check the flag
			{
			case FLAG_CMD:
				{
				//命令
					pMgr->OnCommand((PACK_CMD*)buf,iLen);
				}
				break;
			case FLAG_AUDIO:
				{
					//音频数据
					if(pMgr->m_ds.bAudioOut&&
						pMgr->m_ds.bAudioCodec&&
						pMgr->m_hAudioOut&&
						((PACK_AUDIO*)buf)->session==pMgr->m_session)
					{
						char* p=new char[iLen];
						if(p)
						{
							CopyMemory(p,buf,iLen);
							if(!PostThreadMessage(pMgr->m_dwAudioOutId,IOM_AUDIO,iLen,(LPARAM)p))
								delete []p;
						}
					}
				}
				break;
			case FLAG_VIDEO:
				{
					//视频数据
					if(pMgr->m_ds.bVideoOut&&
						pMgr->m_ds.bVideoCodec&&
						((PACK_VIDEO*)buf)->session==pMgr->m_session)
					{
						
						pMgr->OnDecodeVideoData((PACK_VIDEO*)buf,iLen);			
						
					}
				}
				
				break;
			}
		}
		else
		{
			if(!pMgr->m_Socket.IsSocket())
				break;
		}//the socket should be closed,that is m_Socket have been
		//destroyed,so break the loop and end the thread
	}
	return 0;
}

/*------------------------------------------------------------------------------*/

CAVIOMgr::CAVIOMgr()
{
	pMgrInst=this;	

	m_hSockThread=NULL;

	m_hAudioOut=0;
	m_hAudioIn=0;

	m_idCmd=0;

	m_bEnable=TRUE;
	m_bEnableBandAdjust=TRUE;
	
}

/*------------------------------------------------------------------------------*/
CAVIOMgr::~CAVIOMgr()
{
	Destroy();
	pMgrInst=0;
}

/*------------------------------------------------------------------------------*/
//初始化SOCKET,指定PORT
BOOL CAVIOMgr::InitSocket(UINT nPort)
{
	
	DestroySocket();	
	
	BOOL bRet=FALSE;	
	if(!m_Socket.Create(nPort))
		goto RET;
	//创建socket线程(socket为block方式)
	m_hSockThread=CreateThread(0,0,SockThreadProc,(LPVOID)this,0,&m_dwSockThreadId);
	if(!m_hSockThread)	
		goto RET;
	
	
	bRet=TRUE;
RET:
	if(!bRet)
		DestroySocket();
		
	return bRet;
}

/*------------------------------------------------------------------------------*/

BOOL CAVIOMgr::DestroySocket()
{
	m_Socket.Destroy();
	//
	
	if(m_hSockThread)
	{
		BOOL b=FALSE;
		DWORD ExitCode;
		int Timeout = 50; 
		while(Timeout)//等待线程结束，如果到一定时间还没结束，就强制结束
		{			 //因为Socket已经destroy了，所以socket线程会返回
			GetExitCodeThread(m_hSockThread, &ExitCode);
			
			if (ExitCode != STILL_ACTIVE)
			{
				b=TRUE;
				// Thread has ended.
				break;
			}
			else
			{
				Sleep(10);
			}
			
			--Timeout;
		}
		if(!b)//time out ,terminate it
			TerminateThread(m_hSockThread,0);
				
	}
	m_hSockThread=NULL;
	return TRUE;
}

/*------------------------------------------------------------------------------*/
//设定目标
void CAVIOMgr::SetDst(char *ip, unsigned short port)
{
	m_dst.sin_family=AF_INET;
	//将计算机名或IP转化为网络地址
	m_dst.sin_addr.s_addr=CUDPSocket::Name2Inet(ip);
	m_dst.sin_port=htons(port);
	
}

/*------------------------------------------------------------------------------*/
//初始化音频输入，当初始化完成，录音就开始了
BOOL CAVIOMgr::InitAudioRec()
{
	DestroyAudioRec();
	BOOL bRet=FALSE;
	//创建录音线程
	m_hAudioIn=CreateThread(0,0,AudioInThreadProc,this,0,&m_dwAudioInId);
	if(!m_hAudioIn)
		goto RET;
	if(!m_AudioRec.Create(0,m_dwAudioInId,(DWORD)this,CALLBACK_THREAD,SIZE_AUDIO_FRAME))
		goto RET;
	//开始录音
	if(!m_AudioRec.Start())
		goto RET;
	
	bRet=TRUE;
RET:
	if(!bRet)
	{
		//如果失败，就向主窗口发送出错消息
		PostMessage(m_hwndMainWnd,IOM_NOTIFY,MAKEWPARAM(IOMN_ERR,IOME_AUDIO_IN),(LPARAM)this);
		DestroyAudioRec();
	}
	
	return bRet;

}

/*------------------------------------------------------------------------------*/
//该死的waveIn函数，结束录音时老是出现死锁（XP下还是会出现这个问题，也不知道怎么解决，苦啊）
BOOL CAVIOMgr::DestroyAudioRec()
{
	
	m_AudioRec.Stop();
    m_AudioRec.Destroy();
	if(m_hAudioIn)
	{
		int t=50;
		DWORD ExitCode;
		BOOL bEnd=FALSE;
		//向录音线程发送退出消息，并等待线程结束
		PostThreadMessage(m_dwAudioInId,WM_QUIT,0,0);
		while(t)
		{
			
			GetExitCodeThread(m_hAudioIn,&ExitCode);
			if(ExitCode!= STILL_ACTIVE)
			{
				bEnd=TRUE;
				break;
			}
			else
				Sleep(10);
			t--;
		}
		if(!bEnd)
			TerminateThread(m_hAudioIn,0);
		m_hAudioIn=0;
		
	}	
		
	return TRUE;

}

/*------------------------------------------------------------------------------*/
//初始化播放设备
BOOL CAVIOMgr::InitAudioPlay()
{
	BOOL bRet=FALSE;
	DestroyAudioPlay();
	m_iAudioBuf=0;
	m_hAudioOut=CreateThread(0,0,AudioOutThreadProc,this,0,&m_dwAudioOutId);
	if(!m_hAudioOut)
		goto RET;
	if(!m_AudioPlay.Create(0,m_dwAudioOutId,(DWORD)this,CALLBACK_THREAD))
		goto RET;
	bRet=TRUE;
	
	
RET:
	if(!bRet)
	{
		//向主窗口发送出错消息
		PostMessage(m_hwndMainWnd,IOM_NOTIFY,MAKEWPARAM(IOMN_ERR,IOME_AUDIO_OUT),(LPARAM)this);
		DestroyAudioPlay();
	}
	
	return bRet;
}

/*------------------------------------------------------------------------------*/
//
BOOL CAVIOMgr::DestroyAudioPlay()
{

	m_AudioPlay.Destroy();
	if(m_hAudioOut)
	{
		int t=50;
		DWORD ExitCode;
		BOOL bEnd=FALSE;
		PostThreadMessage(m_dwAudioOutId,WM_QUIT,0,0);
		while(t)
		{
			
			GetExitCodeThread(m_hAudioOut,&ExitCode);
			if(ExitCode!= STILL_ACTIVE)
			{
				bEnd=TRUE;
				break;
			}
			else
				Sleep(10);
			t--;
		}
		if(!bEnd)
			TerminateThread(m_hAudioOut,0);
		m_hAudioOut=0;
		
	}
	return TRUE;
}

/*------------------------------------------------------------------------------*/
//音频解码，并将音频数据发送到想要数据的窗口
void CAVIOMgr::OnDecodeAudioData(PACK_AUDIO *ppa, int len)
{
	
	if(m_CodecMgr.DecodeAudioData((char*)ppa->data,SIZE_AUDIO_PACKED,m_AudioRemote,0))
	{	
		//为了避免延迟过长，当累积的缓冲超过六块时抛弃即将加入的缓冲
		if(m_iAudioBuf<6)
		{
			m_iAudioBuf++;
			m_AudioPlay.Play(m_AudioRemote,SIZE_AUDIO_FRAME);
		}
		if(m_hwndRemoteAudioRcv)
			SendMessage(m_hwndRemoteAudioRcv,IOM_AUDIO,1,(LPARAM)this);
	}
	
	m_uDataRcv+=sizeof(PACK_AUDIO);
	delete []ppa;

}

/*------------------------------------------------------------------------------*/
//压缩音频原始数据并发送出去
void CAVIOMgr::OnEncodeAudioData(char *pa, int len)
{
	
	m_AudioPack.flag=FLAG_AUDIO;
	m_AudioPack.session=m_session;
	
	if(m_CodecMgr.EncodeAudioData(pa,len,(char*)m_AudioPack.data,0))
	{			
		m_Socket.SendTo((char*)&m_AudioPack,sizeof(PACK_AUDIO),(sockaddr*)&m_dst);
		m_uDataSend+=sizeof(PACK_AUDIO);
	}
	
	if(m_hwndLocalAudioRcv)
		SendMessage(m_hwndLocalAudioRcv,IOM_AUDIO,0,(LPARAM)this);
		
}

/*------------------------------------------------------------------------------*/
//视频解码
void CAVIOMgr::OnDecodeVideoData(PACK_VIDEO *ppv, int len)
{	
	
	if(m_CodecMgr.DecodeVideoData(((char*)ppv)+sizeof(PACK_VIDEO),ppv->data_size,m_VideoRemote,0,0))
	{
		m_nFrameCount++;
		
		m_nCurVid=ppv->id;
		//将视频数据发送到视频显示窗口
		if(m_hwndRemoteVideoRcv)
			SendMessage(m_hwndRemoteVideoRcv,IOM_VIDEO,1,(LPARAM)this);
		
	}
	//统计收到的数据
	m_uDataRcv+=sizeof(PACK_VIDEO)+ppv->data_size;
}

/*------------------------------------------------------------------------------*/
//视频压缩并发送
void CAVIOMgr::OnEncodeVideoData(char *pv, int len)
{
	
	int rlen;
	
	if(m_bVideoSend)
	{			
		if(m_CodecMgr.EncodeVideoData(pv,len,m_VideoPack+sizeof(PACK_VIDEO),&rlen,0))
		{
			
			((PACK_VIDEO*)m_VideoPack)->data_size=(unsigned short)rlen;
			((PACK_VIDEO*)m_VideoPack)->id=m_idVideo++;
			((PACK_VIDEO*)m_VideoPack)->session=m_session;
			m_Socket.SendTo(m_VideoPack,rlen+sizeof(PACK_VIDEO),(sockaddr*)&m_dst);
			//统计发送的数据
			m_uDataSend+=rlen+sizeof(PACK_VIDEO);
		}
	}
	m_VideoLocal=pv;
	if(m_hwndLocalVideoRcv)
	{
		//将视频数据发送到视频显示窗口
		SendMessage(m_hwndLocalVideoRcv,IOM_VIDEO,0,(LPARAM)this);
	}

	
}

/*------------------------------------------------------------------------------*/
//初始化，创建SOCKET，并监听
BOOL CAVIOMgr::Init(UINT nPort)
{
	Destroy();
		
	m_hwndLocalVideoRcv=0;
	m_hwndLocalAudioRcv=0;
	m_hwndRemoteVideoRcv=0;
	m_hwndRemoteAudioRcv=0;
	m_hwndMainWnd=0;
	m_bVideoSend=TRUE;
	
	m_iStatus=STA_FREE;
	BOOL bRet=FALSE;
	m_uDataRcv=0;
	m_uDataSend=0;
	if(!InitSocket(nPort))
		goto RET;
	//创建消息接收窗口，主要用于超时重发机制，因为要设定很多不同ID的定时器
	m_MsgRcvWnd.Create(0,0,0,0,CRectX(0,0,0,0),0,0);
	bRet=TRUE;
RET:
	return bRet;
}

/*------------------------------------------------------------------------------*/
//初始化视频捕捉设备
BOOL CAVIOMgr::InitCap()//
{
	HWND hCap;
	
	m_ViCap.Destroy();
	BOOL bRet=FALSE;
	if(!m_ViCap.Init())
		goto RET;
	//得到驱动数
	if(!m_ViCap.GetDriverNum())
		goto RET;
	//连接到驱动
	if(!m_ViCap.ConnectToDriver(0))
		goto RET;
	hCap=m_ViCap.GetCapWindow();
	//设置视频格式
	if(!capSetVideoFormat(hCap,&m_CodecMgr.m_BmpU,sizeof(BITMAPINFO)))
		goto RET;
	//设置视频流回调函数
	if(!capSetCallbackOnVideoStream(hCap,VideoStreamCallbackProc))
		goto RET;
	//将user数据设为AVIOMgr对象指针
	if(!capSetUserData(hCap,(DWORD)this))
		goto RET;
	//开始视频流
	if(!capCaptureSequenceNoFile(hCap))
		goto RET;
	
	bRet=TRUE;
RET:
	if(!bRet)
	{
		PostMessage(m_hwndMainWnd,IOM_NOTIFY,MAKEWPARAM(IOMN_ERR,IOME_VIDEO_IN),(LPARAM)this);
		m_ViCap.Destroy();
	}
	return bRet;
}

/*------------------------------------------------------------------------------*/
BOOL CAVIOMgr::DestroyCap()
{
	
	m_ViCap.Destroy();
	return TRUE;
}

/*------------------------------------------------------------------------------*/
void CAVIOMgr::Destroy()
{
	EndTalk();
	DestroySocket();
	DestroyAudioRec();
	DestroyCap();
	DestroyAudioPlay();
	DestroyAudioCodec();
	DestroyVideoCodec();
}

/*------------------------------------------------------------------------------*/
//
BOOL CAVIOMgr::InitAudioCodec()
{
	
	BOOL bRet=FALSE;
	
	if(!m_CodecMgr.InitCodecA())
		goto RET;
	//启动丢包率定时器	
	SetTimer(m_hwndMainWnd,100,1000,DropRateCounter);
	bRet=TRUE;
RET:
	if(!bRet)
	{
		PostMessage(m_hwndMainWnd,IOM_NOTIFY,MAKEWPARAM(IOMN_ERR,IOME_AUDIO_CODEC),(LPARAM)this);
		DestroyAudioCodec();
	}
	return bRet;
}

/*------------------------------------------------------------------------------*/
//
void CAVIOMgr::DestroyAudioCodec()
{	
	//关闭丢包率定时器
	KillTimer(m_hwndMainWnd,100);
	m_CodecMgr.DestroyCodecA();
}

/*------------------------------------------------------------------------------*/
//初始化视频编码解码器
BOOL CAVIOMgr::InitVideoCodec()
{
	m_idVideo=0;
	m_nFps=0;
	m_nFrameCount=0;
	m_nLastFrameCount=0;
	m_nDropRate=0;
	m_nCurVid=0;
	m_nLastVid=0;
	BOOL bRet=FALSE;
	//
	if(!m_CodecMgr.InitCodecV())
		goto RET;
	
	((PACK_VIDEO*)m_VideoPack)->flag=FLAG_VIDEO;

		
	bRet=TRUE;
RET:
	if(!bRet)
	{
		PostMessage(m_hwndMainWnd,IOM_NOTIFY,MAKEWPARAM(IOMN_ERR,IOME_VIDEO_CODEC),(LPARAM)this);
		DestroyVideoCodec();
	}
	return bRet;
}

/*------------------------------------------------------------------------------*/
void CAVIOMgr::DestroyVideoCodec()
{
	
	
		
	m_CodecMgr.DestroyCodecV();
}


/*------------------------------------------------------------------------------*/
void CAVIOMgr::GetDeviceSupport(DEVICE_SUPPORT &ds)
{

}

/*------------------------------------------------------------------------------*/
//呼叫某个IP
BOOL CAVIOMgr::Call(char *ip, unsigned short port)
{
	BOOL bRet=FALSE;
	if(m_iStatus!=STA_FREE)
		goto RET;
	SetDst(ip,port);
	PACK_CMD pc;
	m_session=rand()&0xff;
	pc.flag=FLAG_CMD;
	pc.cmd=CMD_CALL;
	pc.type=0;
	pc.data_size_extra=0;
	pc.ext=VER_AVIO&0xff;//AVIO 的版本
	if(!SendCmd(&pc,sizeof(pc),m_dst,TRUE))
		goto RET;
	
	m_iStatus=STA_BUSY;
	bRet=TRUE;
	m_uDataSend=0;
	m_uDataRcv=0;
	
RET:
	return bRet;
}

/*------------------------------------------------------------------------------*/
//结束通话
BOOL CAVIOMgr::EndTalk()
{
	BOOL bRet=FALSE;
	if(m_iStatus==STA_FREE)
		goto RET;
	if(m_iStatus==STA_BUSY)
	{
		m_iStatus=STA_FREE;
	
		PACK_CMD pc;
		pc.flag=FLAG_CMD;
		
		pc.cmd=CMD_END;
		pc.data_size_extra=0;
		pc.type=0;
		SendCmd(&pc,sizeof(pc),m_dst,FALSE);
		ZeroMemory(&m_dst,sizeof(m_dst));
		DestroyVideoCodec();
		DestroyAudioCodec();
		DestroyAudioPlay();
		DestroyAudioRec();
		DestroyCap();
		
		
	}
	
	bRet=TRUE;
RET:
	return bRet;
}

/*------------------------------------------------------------------------------*/
//接受请求
BOOL CAVIOMgr::Accept()
{
	BOOL bRet=FALSE;
	if(m_iStatus!=STA_BUSY)
		goto RET;
	PACK_CMD pc;
	pc.flag=FLAG_CMD;
	pc.cmd=CMD_ACCEPT;
	
	pc.data_size_extra=0;
	pc.type=0;
	SendCmd(&pc,sizeof(pc),m_dst,TRUE);
		
	ZeroMemory(&m_ds,sizeof(m_ds));
	m_ds.bVideoOut=true;//always ok
	if(InitVideoCodec())//why this must ahead InitAudioCodec??i don't know why
		m_ds.bVideoCodec=true;
	if(InitAudioCodec())
		m_ds.bAudioCodec=true;
	if(InitAudioPlay())
		m_ds.bAudioOut=true;
	if(InitAudioRec())
		m_ds.bAudioIn=true;
	
	if(m_ds.bVideoCodec&&InitCap())
		m_ds.bVideoIn=true;
	
	if(!m_ds.bAudioCodec&&!m_ds.bVideoCodec)
	{
		DestroyCap();
		DestroyAudioRec();
		DestroyAudioPlay();
		DestroyAudioCodec();
		DestroyVideoCodec();
		
		m_iStatus=STA_FREE;
	}
	bRet=TRUE;
RET:
	return bRet;

}

/*------------------------------------------------------------------------------*/
//拒绝请求
BOOL CAVIOMgr::Refuse()
{
	BOOL bRet=FALSE;
	if(m_iStatus!=STA_BUSY)
		goto RET;
	PACK_CMD pc;
	pc.flag=FLAG_CMD;
	pc.cmd=CMD_REFUSE;
	
	pc.ext=R_USER;
	pc.data_size_extra=0;
	pc.type=0;
	m_iStatus=STA_FREE;

	SendCmd(&pc,sizeof(pc),m_dst,TRUE);	
	
	bRet=TRUE;
RET:
	return bRet;
}

/*------------------------------------------------------------------------------*/
//解释发送来的命令
void CAVIOMgr::OnCommand(PACK_CMD *ppc, int len)
{
	switch(ppc->cmd)
	{
	case CMD_CALL:
		{
			if(ppc->type==0)
			{
				if(m_bEnable)
				{
					//如果是空闲的，就保存地址，设置状态为工作状态
					//并向主窗口发送呼叫通知
					if(m_iStatus==STA_FREE)
					{
						m_dst=m_sockaddr;
						m_iStatus=STA_BUSY;
						m_session=ppc->session;
						PostMessage(m_hwndMainWnd,IOM_NOTIFY,MAKEWPARAM(IOMN_CALL,VER_AVIO),(LPARAM)this);
						//返回请求确认
						PACK_CMD pc;
						pc.flag=FLAG_CMD;
						pc.data_size_extra=0;
						
						pc.cmd=CMD_CALL;
						pc.ext=VER_AVIO&0xff;// AVIO的版本
						pc.type=1;
						pc.id=ppc->id;
						pc.session=m_session;
						m_Socket.SendTo((char*)&pc,sizeof(pc),(sockaddr*)&m_sockaddr);
						
						
					}
					else
					if(m_iStatus==STA_BUSY&&m_session!=ppc->session)
					{					
						/*//有另一台机请求通话，拒绝！
						
						PACK_CMD pc;
						pc.flag=FLAG_CMD;
						pc.cmd=CMD_REFUSE;
						pc.data_size_extra=0;
						pc.ext=R_BUSY;
						pc.type=0;
						pc.id=ppc->id;
						pc.session=ppc->session;
						m_Socket.SendTo((char*)&pc,sizeof(pc),(sockaddr*)&m_sockaddr);*/
					}
				}
			}
			//回应
			else
			{
				if(ppc->session==m_session)
				{
					KillTimer(m_MsgRcvWnd,ppc->id);
					m_CmdQueue.Remove(ppc->id);
					PostMessage(m_hwndMainWnd,IOM_NOTIFY,IOMN_CONNECTED,(LPARAM)this);
				}
			}
		}break;
	case CMD_END:
		{
			if(ppc->session==m_session)
			{
				if(ppc->type==0)
				{
					
					if(m_iStatus==STA_BUSY)
					{
						PostMessage(m_hwndMainWnd,IOM_NOTIFY,MAKEWPARAM(IOMN_END,E_USER),(LPARAM)this);
					}
					PACK_CMD pc;
					pc.flag=FLAG_CMD;
					pc.data_size_extra=0;
					pc.cmd=CMD_END;
					pc.type=1;
					pc.id=ppc->id;
					pc.session=m_session;
					m_Socket.SendTo((char*)&pc,sizeof(pc),(sockaddr*)&m_sockaddr);
					
				}
				else
				{
					KillTimer(m_MsgRcvWnd,ppc->id);
					m_CmdQueue.Remove(ppc->id);
				}
			}
			
		}
		break;
	case CMD_REFUSE:
		{
			if(ppc->session==m_session)
			{
				
				if(ppc->type==0)
				{
					
					if(m_iStatus==STA_BUSY)
					{
						m_iStatus=STA_FREE;
						PostMessage(m_hwndMainWnd,IOM_NOTIFY,MAKEWPARAM(IOMN_REFUSE,ppc->ext),(LPARAM)this);
					}
					PACK_CMD pc;
					pc.flag=FLAG_CMD;
					pc.data_size_extra=0;
					pc.cmd=CMD_REFUSE;
					pc.type=1;
					pc.id=ppc->id;
					pc.session=m_session;				
					m_Socket.SendTo((char*)&pc,sizeof(pc),(sockaddr*)&m_sockaddr);
					
					
				}
				else
				{
					KillTimer(m_MsgRcvWnd,ppc->id);
					m_CmdQueue.Remove(ppc->id);
				}
			}
		}
		break;
	case CMD_ACCEPT:
		{
			if(ppc->session==m_session)
			{
				if(ppc->type==0)
				{
					
					if(m_iStatus==STA_BUSY)
					{
						PostMessage(m_hwndMainWnd,IOM_NOTIFY,MAKEWPARAM(IOMN_ACCEPT,VER_AVIO),(LPARAM)this);
					}
					PACK_CMD pc;
					pc.flag=FLAG_CMD;
					pc.data_size_extra=0;
					pc.cmd=CMD_ACCEPT;
					pc.type=1;
					pc.id=ppc->id;
					pc.session=m_session;
					m_Socket.SendTo((char*)&pc,sizeof(pc),(sockaddr*)&m_sockaddr);
					
				}
				else
				{
					KillTimer(m_MsgRcvWnd,ppc->id);
					m_CmdQueue.Remove(ppc->id);
				}
			}
			
		}
		break;
	case CMD_TXT:
		{
			if(ppc->type==0)
			{
				//bug exist
				SendMessage(m_hwndMainWnd,IOM_NOTIFY,MAKEWPARAM(IOMN_TXT,0),(LPARAM)(((char*)ppc)+sizeof(PACK_CMD)));
				PACK_CMD pc;
				pc.flag=FLAG_CMD;
				pc.data_size_extra=0;
				pc.cmd=CMD_TXT;
				pc.type=1;
				pc.id=ppc->id;
				m_Socket.SendTo((char*)&pc,sizeof(pc),(sockaddr*)&m_sockaddr);
			}
			else
			{
				
				KillTimer(m_MsgRcvWnd,ppc->id);
				UINT len;char resend;sockaddr_in addr;
				char* p=m_CmdQueue.GetBuf(ppc->id,len,resend,addr);
				if(p)
				{
					SendMessage(m_hwndMainWnd,IOM_NOTIFY,MAKEWPARAM(IOMN_TXT,1),(LPARAM)(p+sizeof(PACK_CMD)));
				}
				m_CmdQueue.Remove(ppc->id);
			}
		}
		break;

	case CMD_DROPRATE:
		{
			//根据丢包率来调整视频质量
			if(ppc->type==0&&ppc->session&&m_bEnableBandAdjust)
			{
				SetVideoQuality(100-ppc->ext);
				SendMessage(m_hwndMainWnd,IOM_NOTIFY,MAKEWPARAM(IOMN_VQSET,100-ppc->ext),(LPARAM)this);
			}
		}break;
		
	}

}

/*------------------------------------------------------------------------------*/
//以下是设置对外消息接口
//设置主窗口
void CAVIOMgr::SetMainWnd(HWND hwnd)
{
	m_hwndMainWnd=hwnd;
}

void CAVIOMgr::SetLocalVideoRcvWnd(HWND hwnd)
{
	m_hwndLocalVideoRcv=hwnd;
}

void CAVIOMgr::SetLocalAudioRcvWnd(HWND hwnd)
{
	m_hwndLocalAudioRcv=hwnd;
}

void CAVIOMgr::SetRemoteVideoRcvWnd(HWND hwnd)
{
	m_hwndRemoteVideoRcv=hwnd;
}

void CAVIOMgr::SetRemoteAudioRcvWnd(HWND hwnd)
{
	m_hwndRemoteAudioRcv=hwnd;
}

/*------------------------------------------------------------------------------*/
//得到当前状态
int CAVIOMgr::GetStatus()
{
	return m_iStatus;
}

/*------------------------------------------------------------------------------*/
//调用以下函数要谨慎
//只有在收到相应消息时才能调用
/////////////////////////
///
char* CAVIOMgr::GetLocalVideo(UINT &len)
{
	len=m_CodecMgr.m_BmpU.bmiHeader.biSizeImage;
	return m_VideoLocal;
}

char* CAVIOMgr::GetLocalAudio(UINT &len)
{
	len=SIZE_AUDIO_FRAME;
	return m_AudioLocal;
}

char* CAVIOMgr::GetRemoteVideo(UINT &len)
{
	len=m_CodecMgr.m_BmpU.bmiHeader.biSizeImage;
	return m_VideoRemote;
}

char* CAVIOMgr::GetRemoteAudio(UINT &len)
{
	len=SIZE_AUDIO_FRAME;
	return m_AudioRemote;
}

/*------------------------------------------------------------------------------*/
//得到原始视频格式
BITMAPINFO* CAVIOMgr::GetBitampInfo()
{
	return &m_CodecMgr.m_BmpU;
}

/*------------------------------------------------------------------------------*/
//得到数据发送量
UINT CAVIOMgr::GetDataSend()
{
	return m_uDataSend;
}
/*------------------------------------------------------------------------------*/
//得到数据接收量
UINT CAVIOMgr::GetDataRcv()
{
	return m_uDataRcv;
}
/*------------------------------------------------------------------------------*/
//设置是否传输视频
void CAVIOMgr::VideoSend(BOOL b)
{
	m_bVideoSend=b;
	
}
/*------------------------------------------------------------------------------*/
//得到对方地址
sockaddr_in CAVIOMgr::GetRemoteAddr()
{
	return m_dst;
}
/*------------------------------------------------------------------------------*/
//设置视频质量
void CAVIOMgr::SetVideoQuality(UINT q)
{
	m_CodecMgr.m_cv.lQ=q*100;
}
/*------------------------------------------------------------------------------*/
//预览
BOOL CAVIOMgr::EnablePreview(BOOL b)
{
	if(m_iStatus==STA_FREE&&b)
	{
		return InitCap();			
	}
	else
	if(m_iStatus==STA_FREE&&!b)
	{
		DestroyCap();
		
	}
	return TRUE;
}

/*------------------------------------------------------------------------------*/
//当对方接收请求后，会受到Accept的消息，这时必须调用此函数，来打开各种设备
void CAVIOMgr::OnAccept()
{
	if(m_iStatus==STA_FREE) return;
	ZeroMemory(&m_ds,sizeof(m_ds));

	m_ds.bVideoOut=true;//always ok
	if(InitVideoCodec())
		m_ds.bVideoCodec=true;
	if(InitAudioCodec())
		m_ds.bAudioCodec=true;
	if(InitAudioPlay())
		m_ds.bAudioOut=true;
	if(InitAudioRec())
		m_ds.bAudioIn=true;
	
	if(m_ds.bVideoCodec&&InitCap())
		m_ds.bVideoIn=true;
	
	if(!m_ds.bAudioCodec&&!m_ds.bVideoCodec)
	{
		DestroyCap();
		DestroyAudioRec();
		DestroyAudioPlay();
		DestroyAudioCodec();
		DestroyVideoCodec();
		
		m_iStatus=STA_FREE;
	}


	
}

/*------------------------------------------------------------------------------*/
//得到帧数
UINT CAVIOMgr::GetFrameCount()
{
	return m_nFrameCount;
}

/*------------------------------------------------------------------------------*/
//发送命令（比较可靠）
BOOL CAVIOMgr::SendCmd(PACK_CMD *pCmd, UINT nLen,sockaddr_in &addr, BOOL bFlag)
{
	if(!pCmd) return FALSE;
	pCmd->id=m_idCmd++;
	pCmd->session=m_session;
	
	int l=m_Socket.SendTo((char*)pCmd,nLen,(sockaddr*)&addr);
	if(l>0)
	{
		if(bFlag)
		{
			m_CmdQueue.Add(pCmd->id,(char*)pCmd,nLen,addr);//把命令加入命令表
			SetTimer(m_MsgRcvWnd,pCmd->id,800,CmdTimeOutProc);
		}
		return TRUE;
	}
	else
		return FALSE;
}

/*------------------------------------------------------------------------------*/
//命令超时重发
void CALLBACK CAVIOMgr::CmdTimeOutProc(HWND hwnd,UINT uMsg,UINT idEvent,DWORD dwTime)
{
	if(pMgrInst)
	{
		UINT len;char resend;sockaddr_in addr;
		char* p=pMgrInst->m_CmdQueue.GetBuf(idEvent,len,resend,addr);//找出相应ID的命令(已经超时,需要重发)
		if(p)
		{
			if(resend<5)
				pMgrInst->m_Socket.SendTo(p,len,(sockaddr*)&addr);
			else//当重发次数超过4次时,宣布失败
			{
				KillTimer(hwnd,idEvent);//
				
				if(pMgrInst)
				{
					PACK_CMD* pCmd=(PACK_CMD*)p;
					switch(pCmd->cmd)
					{
					case CMD_CALL:
						{
							PostMessage(pMgrInst->m_hwndMainWnd,IOM_NOTIFY,MAKEWPARAM(IOMN_REFUSE,R_TIMEOUT),(LPARAM)pMgrInst);
							pMgrInst->m_iStatus=STA_FREE;
							
						}
						break;
					case CMD_REFUSE:
						break;
					case CMD_ACCEPT:
						break;
					case CMD_END:
						break;
					case CMD_TXT:
						break;
					}
				}
				pMgrInst->m_CmdQueue.Remove(idEvent);//把命令从表中移除
			}
		}
		else
			KillTimer(hwnd,idEvent);
	}
}

/*------------------------------------------------------------------------------*/
//丢包率计算(包括帧频率计算)
void CALLBACK CAVIOMgr::DropRateCounter(HWND hwnd,UINT uMsg,UINT idEvent,DWORD dwTime)
{
	//计算丢包率
	if(pMgrInst)
	{
		UINT delt;
		if(pMgrInst->m_nCurVid<pMgrInst->m_nLastVid)
			delt=pMgrInst->m_nCurVid+256-pMgrInst->m_nLastVid;
		else
			delt=pMgrInst->m_nCurVid-pMgrInst->m_nLastVid;
		if(delt>0)
			pMgrInst->m_nDropRate=100-(pMgrInst->m_nFrameCount-pMgrInst->m_nLastFrameCount)*100/delt;
		pMgrInst->m_nLastVid=pMgrInst->m_nCurVid;
		//发送丢包率
		PACK_CMD pc;
		pc.flag=FLAG_CMD;
		pc.cmd=CMD_DROPRATE;
		pc.session=pMgrInst->m_session;
		pc.type=0;
		pc.data_size_extra=0;
		pc.ext=pMgrInst->m_nDropRate;
		pMgrInst->m_Socket.SendTo((char*)&pc,sizeof(PACK_CMD),(SOCKADDR*)&pMgrInst->m_dst);		
		//计算帧频率
		pMgrInst->m_nFps=(pMgrInst->m_nFrameCount-pMgrInst->m_nLastFrameCount);
		
		
		pMgrInst->m_nLastFrameCount=pMgrInst->m_nFrameCount;
	}

}

/*------------------------------------------------------------------------------*/
UINT CAVIOMgr::GetFps()
{
	return m_nFps;
}

/*------------------------------------------------------------------------------*/
//发送文本信息
BOOL CAVIOMgr::SendTxt(char *ip, unsigned short port, char *txt)
{
	BOOL bRet=FALSE;
	
	PACK_CMD *ppc=(PACK_CMD*)new char[sizeof(PACK_CMD)+strlen(txt)+1];
	strcpy(((char*)ppc)+sizeof(PACK_CMD),txt);
	ppc->flag=FLAG_CMD;
	ppc->cmd=CMD_TXT;
	ppc->type=0;
	ppc->data_size_extra=strlen(txt)+1;
	//将ip和port转化
	sockaddr_in addr;
	addr.sin_family=AF_INET;
	addr.sin_addr.s_addr=CUDPSocket::Name2Inet(ip);
	addr.sin_port=htons(port);
	//
	if(!SendCmd(ppc,sizeof(PACK_CMD)+ppc->data_size_extra,addr,TRUE))
		goto RET;
	
	bRet=TRUE;
		
RET:
	return bRet;
}

/*------------------------------------------------------------------------------*/
//得到前一次发来数据的地址
sockaddr_in& CAVIOMgr::GetCurrentAddr()
{
	return m_sockaddr;
}

/*------------------------------------------------------------------------------*/
//
BOOL CAVIOMgr::Enable(BOOL bEnable)
{
	BOOL t=m_bEnable;
	m_bEnable=bEnable;
	return t;
}

/*------------------------------------------------------------------------------*/
//是否自动调整图象质量
BOOL CAVIOMgr::EnableBandAdjust(BOOL bEnable)
{
	BOOL t=m_bEnableBandAdjust;
	m_bEnableBandAdjust=bEnable;
	return t;
}
