www.pudn.com > H264RtpSource.rar > AllProtocol.cpp


#include "StdAfx.h" 
#include ".\allprotocol.h" 
 
#include "typedef.h" 
#include "MySafeDefine.h" 
#include "Queue.h" 
#include "rtp_type.h" 
 
 
//心跳时间 
#define TIME_PULSE			20 
 
#define RTP_PAYLOAD_VIDEO_H264	36		//h.264视频码流payload type 
#define RTP_PAYLOAD_AUDIO_AAC	37		//aac音频码流payload type 
 
#define RTP_HEADER_SIZE 12 
 
#define H264_Get_NalType(c) ((c)&0x1f) 
 
#define PLAY_ADD_AUDIO(flag)	(flag = flag|0x02) 
#define PLAY_ADD_VIDEO(flag)	(flag = flag&0xfb) 
 
#define PLAY_DEL_AUDIO(flag)	(flag = flag&0xfd) 
#define PLAY_DEL_VIDEO(flag)	(flag = flag|0x04) 
 
#define GET_AUDIO_FLAG(value)	((value & 0x02)>>1) 
#define GET_VIDEO_FLAG(value)	((value & 0x04)>>2) 
 
//客户端连接状态 
const int LINK_STATUS_STOP	    =0; 
const int LINK_STATUS_LINKING	=1; 
const int LINK_STATUS_LINKED	=2; 
 
typedef struct _RECVBUF 
{ 
	int OperatorCode; 
	char Buf[1500]; 
}RECVBUF; 
 
CAllProtocol::CAllProtocol(void) 
:m_SocketTCP(INVALID_SOCKET) 
,m_SocketRTP(INVALID_SOCKET) 
,m_RemotePort(3320) 
,m_LocalPort(8000) 
,m_RemoteIp("192.168.0.130") 
,m_LocalIp(0) 
,m_hGetDataThreadId(NULL) 
,bFinishThread(TRUE) 
,m_iLinkStatus(LINK_STATUS_STOP) 
,tmLastSendPulse(0) 
,bConnectRestart(FALSE) 
,m_VideoQueue(NULL) 
,SPS_GET(FALSE) 
,VideoFrameNum(0) 
,m_bFirstFrame(TRUE) 
{ 
	//网络通讯初始化 
	WORD wVersionRequested; 
	WSADATA wsadata; 
	int iRetCode; 
 
	wVersionRequested = MAKEWORD(1,1); 
	iRetCode = WSAStartup(wVersionRequested,&wsadata); 
	if (iRetCode!=0) 
	{ 
		WSACleanup(); 
	} 
	m_VideoQueue = new CGoldQueue(); 
	initFIFO(); 
	//得到本机的IP地址,因为RTP服务端需要 
	//取得本地的IP地址 
	char strHost[80]; 
	gethostname(strHost,80); 
	hostent *pAddressInfo = gethostbyname(strHost); 
	m_LocalIp = (*(struct in_addr *)pAddressInfo->h_addr_list[0]).S_un.S_addr; 
} 
 
CAllProtocol::~CAllProtocol(void) 
{ 
	//在结束这个实例的时候,我们要关闭SOCKET 
	SAFE_CLOSESOCKET(m_SocketRTP); 
	SAFE_CLOSESOCKET(m_SocketTCP); 
	if (m_VideoQueue!=NULL) 
	{ 
		SAFE_DELETE(m_VideoQueue); 
	} 
	WSACleanup(); 
} 
 
//TCP连接服务器 
//采用非阻塞通讯,这样连接服务器效率更高 
// 
int CAllProtocol::ConnectTCP() 
{ 
	//初始化一个SOCKET 
	m_SocketTCP = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); 
	if (m_SocketTCP == INVALID_SOCKET)return -1; 
 
	//设置SOCKET选项 
	BOOL  ReuseAddress = 1; 
	//设置一个ip地址可以被多个套接字使用 
	if (setsockopt(m_SocketTCP,SOL_SOCKET,SO_REUSEADDR,(char *)&ReuseAddress,sizeof(ReuseAddress)) == SOCKET_ERROR)return -1; 
	int opt = 1; 
	if (setsockopt(m_SocketTCP,IPPROTO_TCP,TCP_NODELAY,(char *)&opt,sizeof(opt)) == SOCKET_ERROR)return -1; 
	//绑定通讯地址 
	sockaddr_in sockAddr; 
	sockAddr.sin_family = AF_INET; 
	sockAddr.sin_port = htons(m_RemotePort); 
	sockAddr.sin_addr.S_un.S_addr = inet_addr(m_RemoteIp); 
 
	//设置非阻塞 
	unsigned long	nonBlocked = 1; 
	if (ioctlsocket(m_SocketTCP,FIONBIO ,&nonBlocked) == SOCKET_ERROR)return -1; 
	if(connect(m_SocketTCP,(sockaddr *)&sockAddr,sizeof(sockAddr))== SOCKET_ERROR) 
	{ 
		if(WSAGetLastError() != WSAEWOULDBLOCK)return -1; 
	} 
 
	//////////////////////////////////////////////////////////////////////////////// 
	int reval = -1; 
	struct timeval WaitVal; 
	fd_set WriteSet; 
	fd_set ExceptSet; 
	time_t TimeNow; 
	time_t TimeOut; 
 
	time(&TimeNow); 
	TimeOut = TimeNow + 1; 
	do 
	{ 
		//清空fd_set与所有文件句柄的联系 
		FD_ZERO(&WriteSet); 
		FD_ZERO(&ExceptSet); 
 
		//建立sock句柄与fd_set的联系 
		FD_SET(m_SocketTCP, &WriteSet); 
		FD_SET(m_SocketTCP, &ExceptSet); 
 
		WaitVal.tv_sec = 0; 
		WaitVal.tv_usec = 1000 * 200L; 
 
		//select函数常常用在用一个进程监听多个服务器端socket。 
		reval = select((int)m_SocketTCP + 1, NULL, &WriteSet, &ExceptSet,&WaitVal);  
		if (reval == SOCKET_ERROR) 
		{ 
			return -1; 
		} 
		else if ( reval == 0) 
		{ 
			time(&TimeNow); 
		}else if ( reval > 0) 
		{ 
			m_iLinkStatus = LINK_STATUS_LINKING; 
			return 1; 
		} 
	} while (TimeNow < TimeOut); 
 
	//这个地方采用非组塞通讯,可以判断服务器是存在的,但还没有确定连上; 
	//所以我们把连接的状态设置成为正在连接 
	return -1; 
} 
 
//RTP接收数据的初始化 
int CAllProtocol::ConnectRTP() 
{ 
	//初始化一个SOCKET 
	m_SocketRTP = socket(AF_INET,SOCK_DGRAM,0); 
	if (m_SocketRTP==INVALID_SOCKET)return -1; 
 
	BOOL			ReuseAddress = 1; 
	int				nBufSize = 1500*50; 
	 
	unsigned long	nonBlocked = 1; 
	//设置SOCKET为,非阻塞通讯 
	if (ioctlsocket(m_SocketRTP,FIONBIO ,&nonBlocked) == SOCKET_ERROR)return -1; 
	if (setsockopt(m_SocketRTP,SOL_SOCKET,SO_REUSEADDR,(char *)&ReuseAddress,sizeof(ReuseAddress)) == SOCKET_ERROR)return -1; 
	if (setsockopt(m_SocketRTP,SOL_SOCKET,SO_RCVBUF,(char *)&nBufSize,sizeof(nBufSize)) == SOCKET_ERROR)return -1; 
	 
	sockaddr_in		sockAddr; 
	ZeroMemory(&sockAddr,sizeof(sockAddr)); 
	sockAddr.sin_family      = AF_INET; 
	sockAddr.sin_port	     = htons(m_LocalPort); 
	sockAddr.sin_addr.s_addr = INADDR_ANY; 
	if (bind(m_SocketRTP,(sockaddr *)&sockAddr,sizeof(sockAddr)) == SOCKET_ERROR)return -1; 
	return 1; 
} 
 
//发送TCP数据,当这个函数返回SOCKET_ERROR的时候,我们让函数重新连接。 
int CAllProtocol::SendTcpData(SOCKET Sock, const char* Data, int Length) 
{ 
	int lens        = Length; 
	int sentLen     = 0; 
	const char *buf = Data; 
	int nRetVal     = 0; 
	fd_set fdsWrite; 
	timeval tv; 
	//如果这个SOCKET都不存在,那么赶快返回 
	if (Sock==SOCKET_ERROR)return SOCKET_ERROR; 
 
	while(lens > 0) 
	{ 
		FD_ZERO(&fdsWrite); 
		FD_SET(Sock,&fdsWrite); 
		tv.tv_sec = 0; 
		tv.tv_usec = 300; 
		nRetVal = select(0,NULL,&fdsWrite,NULL,&tv); 
		if(nRetVal == SOCKET_ERROR) 
		{ 
			return SOCKET_ERROR; 
		} 
		else if (nRetVal > 0) 
		{ 
			if(FD_ISSET(Sock,&fdsWrite)) 
			{ 
				nRetVal = send(Sock , buf + sentLen ,lens ,0); 
				if(nRetVal != SOCKET_ERROR) 
				{       
					lens -= nRetVal; 
					sentLen += nRetVal; 
				} 
				else 
				{ 
				    return SOCKET_ERROR; 
				} 
			} 
		} 
	} 
	return Length; 
} 
 
//开启线程 
int CAllProtocol::OpenThread() 
{ 
	//如果线程标志没有为false,则退出创建线程 
	if (!bFinishThread)return 0; 
	//线程结束标志为真,但是线程句柄又不NULL, 
	//此时我们要Close这个句柄 
	if (m_hGetDataThreadId!=NULL)CloseThread(); 
	//现在开始可以创建线程了,并让线程挂起 
	m_hGetDataThreadId = ::CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ThreadGetDataEntry,this,CREATE_SUSPENDED,NULL); 
	if(m_hGetDataThreadId) 
	{ 
		bFinishThread = FALSE; 
		::SetThreadPriority(m_hGetDataThreadId,THREAD_PRIORITY_ABOVE_NORMAL); 
		::ResumeThread(m_hGetDataThreadId);	//线程重新开始 
	}else{ 
		return -1; 
	} 
	return 1; 
} 
 
//关闭线程 
BOOL CAllProtocol::CloseThread() 
{ 
	//等待线程结束 
	bFinishThread = TRUE;	//线程标志为真,准备结束线程 
	WaitForSingleObject(m_hGetDataThreadId,INFINITE); 
	if (m_hGetDataThreadId!=NULL) 
	{ 
		CloseHandle(m_hGetDataThreadId); 
		m_hGetDataThreadId = NULL; 
	} 
	return TRUE; 
} 
 
//线程函数; 
DWORD CAllProtocol::ThreadGetDataEntry(LPVOID pv) 
{ 
	(reinterpret_cast(pv))->ThreadGetDataProc(); 
	return 0; 
} 
 
//线程功能模块实现过程函数 
//在这个线程中,我们要考虑到以下这几种情况: 
//1、服务器还正在连接中;2、要重新建立连接;3、要退出线程; 
void CAllProtocol::ThreadGetDataProc(void) 
{ 
	//我们采用for循环可能效率高一点 
	//等待和服务器建立连接 
	for (;;) 
	{ 
		//说明要退出线程,我们只要退出for循环就可以 
		//没有什么要做,相应的处理,因为在别的地方都做了 
		if (bFinishThread == TRUE)break; 
		//线程启动了,但是我们必须注意,可能现在服务端现在还没有连接上 
		//还没有和服务器建立连接的意思 
		if (m_iLinkStatus == LINK_STATUS_STOP) 
		{ 
			Sleep(10); 
			continue; 
		}else{ 
			break; 
		} 
	} 
 
	//采集数据 
	for(;;) 
	{ 
		//说明要退出线程,我们只要退出for循环就可以, 
		//没有什么要做,相应的处理,因为在别的地方都做了 
		if (bFinishThread == TRUE)break; 
		//这个地方说明和服务器建立连接可能出现了问题 
		//需要重新连接 
		if (bConnectRestart) 
		{ 
			//如果重新连接不成功,那么我们继续重新连接 
			if (!RestartConnnect())continue; 
		} 
 
		//这里跟TCP没什么关系,最重要的是RTP数据的采集 
		int ifds = 0; 
		fd_set fdsRead/*,fdsWrite*/; 
		timeval tv; 
		 
		FD_ZERO(&fdsRead); 
		//FD_ZERO(&fdsWrite); 
		//如果SOCKET是坏的,让他继续循环; 
		//出现这种情况的原因可能是:a,用户关闭了SOCKET链接; 
		if (m_SocketRTP == INVALID_SOCKET)continue;  //不要再往下,执行了; 
		FD_SET(m_SocketRTP,&fdsRead); 
		ifds++; 
 
		tv.tv_sec = 0; 
		tv.tv_usec = 100000; 
		//只管读数据 
		int iRetCode = select(ifds,&fdsRead,NULL,NULL,&tv); 
		if (iRetCode == -1) 
		{ 
			iRetCode = WSAGetLastError(); 
			continue; 
		}else{ 
			if ((FD_ISSET(m_SocketRTP,&fdsRead))) 
			{ 
				//有数据可以采集了 
				RecvRtpPacket(); 
			}			 
		} 
 
		//发送心跳 
		time_t tmNow = time(NULL); 
	  /*if (m_iLinkStatus == LINK_STATUS_LINKED) 
		{*/ 
			if (tmNow -tmLastSendPulse >= TIME_PULSE) 
			{ 
				//如果发送心跳没有成功,那么我们重新开始建立连接 
				if (SendPulse()) 
				{ 
					tmLastSendPulse = tmNow; 
				}else 
				{ 
					//需要重新连接 
					//设置连接状态 
					m_iLinkStatus = LINK_STATUS_STOP; 
					//设置重连标志 
					bConnectRestart = TRUE; 
				} 
			} 
		/*}*/ 
	}//结束 for 
} 
 
//发送客户端心跳 
int CAllProtocol::SendPulse() 
{ 
	//如果TCP没有连接好,那么就不要发送心跳了 
	if (m_iLinkStatus == LINK_STATUS_STOP)return SOCKET_ERROR; 
	 
	//发送心跳可能有点问题,如果出现服务端连接,20,30秒后,退出 
	//那么要看看是不是服务端出现了问题 
	PACKET_TYPE	Packet; 
 
	ZeroMemory(&Packet,sizeof(Packet)); 
	Packet.ProtocolType = 0x33; 
	Packet.PacketType = TYPE_REQUEST; 
	int iRetVal = SendTcpData(m_SocketTCP, (const char*)&Packet, PACKET_HEAD); 
	if(iRetVal == SOCKET_ERROR)return SOCKET_ERROR; 
	return 1; 
} 
 
//和重新服务器建立连接的函数 
BOOL CAllProtocol::RestartConnnect() 
{ 
	//这里我们和服务器连接一次就算了,如果不成功返回FALSE 
	int iRetCode = -1; 
 
	//要想重新建立连接,先把一些东西初始化 
	SAFE_CLOSESOCKET(m_SocketTCP); 
	SAFE_CLOSESOCKET(m_SocketRTP); 
	iRetCode = ConnectTCP(); 
	if (!iRetCode)return FALSE; 
	iRetCode = ConnectRTP(); 
	if (!iRetCode)return FALSE; 
	 
	//告诉服务端,可以发送视频数据 
    
 
	//连接上了,就不要在重新连接了 
	bConnectRestart = FALSE; 
	return TRUE; 
} 
 
//接收RTP数据 
//输入参数: 
//输出参数: 
void CAllProtocol::RecvRtpPacket() 
{ 
	rtp_header_t* rtp; 
	unsigned char RecvBuf[2004]; 
 
	//如果线程要结束,那么赶快退出这个函数 
	if (bFinishThread)return; 
	int iRecvLen = recvfrom(m_SocketRTP,(char *)RecvBuf,sizeof(RecvBuf),0,NULL,NULL); 
	if (iRecvLen == SOCKET_ERROR) 
	{ 
		int nError = WSAGetLastError(); 
		SAFE_CLOSESOCKET(m_SocketRTP); 
		//重新建立RTP连接 
		ConnectRTP(); 
		return; 
	} 
	//判断是不是视频数据 
	rtp = (rtp_header_t*)RecvBuf; 
	//说明我们接收到得是视频数据 
	if (rtp->paytype == RTP_PAYLOAD_VIDEO_H264) 
	{ 
		if ( !SPS_GET )	//如果没找到第一个i帧,就嫁出  
		{ 
			if (7 != H264_Get_NalType(RecvBuf[20]))return; 
			SPS_GET = 1; 
		} 
		///////////////////////////////////////////////////////////////////////////////////// 
		//下面这段代码需要改进,这里很有可能会引起错误:比如图象失真。 
		//把数据写入FIFO 
		mu_write_pes_fifo(RecvBuf,iRecvLen); 
		if (rtp->markbit)VideoFrameNum++;    //找到一帧数据 
		if (VideoFrameNum>=1) 
		{ 
			//把数据处理出来 
			DealVideo(); 
		} 
	} 
} 
 
//连接好,告诉服务端可以发送视频数据或音频数据,但是我们这个只有视频数据 
BOOL CAllProtocol::USRStartPlay(unsigned char StartFlag) 
{ 
	//如果最基本的TCP连接没有,那么退出这个函数 
	if (m_iLinkStatus == LINK_STATUS_STOP)return FALSE; 
	 
	int iRetCode = -1; 
	PLAY_REQUEST play_request; 
	memset(&play_request, 0, sizeof(PLAY_REQUEST)); 
	play_request.IP =/* inet_addr("192.168.0.16");*/m_LocalIp; 
	play_request.Port = htons(m_LocalPort); 
 
	if(1 == GET_AUDIO_FLAG(StartFlag)) 
	{ 
		PLAY_ADD_AUDIO(play_request.StartOrEnd); 
	} 
	else 
	{ 
		PLAY_DEL_AUDIO(play_request.StartOrEnd); 
	} 
 
	if( 0 == GET_VIDEO_FLAG(StartFlag) ) 
	{ 
		PLAY_ADD_VIDEO(play_request.StartOrEnd); 
	} 
	else 
	{ 
		PLAY_DEL_VIDEO(play_request.StartOrEnd); 
	} 
 
	//当网络断开重新连接后,这个标志可以告诉服务端需要发送的是视频还是音视频数据 
	//但是这个filter中,没有用到 
	//m_StartFlag = play_request.StartOrEnd; 
 
	//开始发送数据 
	iRetCode = SendTcpPacket(m_SocketTCP,(char*)&play_request,sizeof(play_request),OP0041); 
	if (iRetCode<0)return FALSE; 
	//数据发送成功,说明连接已经建立好了 
	if (m_iLinkStatus == LINK_STATUS_LINKING)m_iLinkStatus = LINK_STATUS_LINKED; 
	//接收服务端的应答数据,确定服务端已经响应了,我们的请求 
	//这个数据只有数据头 
   PACKET_TYPE TempPacket; 
	ZeroMemory(&TempPacket,sizeof(PACKET_TYPE)); 
	iRetCode = RecvTcpData(m_SocketTCP,(char*)&TempPacket,PACKET_HEAD); 
	if (iRetCode<=0) 
	{ 
		return FALSE; 
	} 
	return TRUE; 
} 
 
//发送数据包, 
int CAllProtocol::SendTcpPacket(SOCKET sock, char* usrbuf, int len, unsigned short OperatorType) 
{ 
	PACKET_TYPE TempPacket; 
 
	memset(&TempPacket, 0, sizeof(PACKET_TYPE)); 
	TempPacket.PacketType = 0x33; 
	TempPacket.OperationCode = OperatorType; 
	TempPacket.DataLength = len; 
 
	if(len > 0) 
	{ 
		memcpy(TempPacket.DataBuffer, usrbuf, len); 
	} 
	return SendTcpData(sock,(char*)&TempPacket,len+PACKET_HEAD); 
} 
 
 
//接收一定长度的TCP数据:如果成功返回接收的长度;没有数据返回0;SOCKET出错返回-1; 
//SOCKET出错的时候,本应该重新连接,但考虑到,我们统一在发送心跳不成功的时候重连, 
//所以这个地方我们就不做重新连接了。 
int CAllProtocol::RecvTcpData(SOCKET Sock,char* Data,int Length) 
{ 
	if (Sock==INVALID_SOCKET)return -1; 
 
	int iRetCode; 
	fd_set fdsRead; 
	time_t tmNow; 
	time_t tmOut; 
	struct timeval WaitVal; 
	char RevData[2000]; 
 
	//得到当前时间 
	time(&tmNow); 
	//异常时间 
	tmOut = tmNow + 2; // 整个接收过程2秒 
	do  
	{ 
		int ifds =0; 
		FD_ZERO(&fdsRead); 
		FD_SET(Sock,&fdsRead); 
		ifds++; 
		WaitVal.tv_sec = 0;	//秒 
		WaitVal.tv_usec = 1000 * 200L; //毫秒  
		iRetCode = select(ifds,&fdsRead,NULL,NULL,&WaitVal); //超时返回:0;有数据:>0;SOCKET出错:<0; 
		if (iRetCode==0) 
		{ 
			time(&tmNow); 
			continue; 
		} 
		if ((iRetCode==INVALID_SOCKET)||((iRetCode > 0) &&(!FD_ISSET(Sock, &fdsRead)))) 
		{ 
			return -1; 
		}else{ 
			//开始接受数据 
			ZeroMemory(RevData,2000); 
			iRetCode = recv(Sock,RevData,Length,0); 
			if (iRetCode == SOCKET_ERROR)return -1; 
			memcpy(Data,RevData,iRetCode); 
			break; 
		} 
	} while(tmNow0;SOCKET出错:<0; 
		if (iRetCode>0) 
		{ 
			iRetCode = recv(Sock,tmpbuf,1500,0); 
			if (iRetCode<=0)break; 
		}else{ 
			break; 
		} 
	} 
} 
 
////FIFO队列,这是一个比较经得起考验的FIFO 
//网络中接收到的原始数据 
void CAllProtocol::initFIFO() 
{ 
	RtpPacketFifo.Read = 0; 
	RtpPacketFifo.Write = 0; 
	RtpPacketFifo.DatCount = 0; 
} 
 
 
 
BOOL CAllProtocol::mu_write_pes_fifo(unsigned char *dat,int len) 
{ 
	//unsigned int EmptyLen; 
	unsigned int WriteByteCount=0; 
 
	if(RtpPacketFifo.DatCount < 20) 
	{ 
		memcpy(RtpPacketFifo.Buf[RtpPacketFifo.Write],dat,/*2000*/len); 
		RtpPacketFifo.iDataLen[RtpPacketFifo.Write] = len; 
		RtpPacketFifo.Write++; 
		RtpPacketFifo.Write %= (20);						 //小技巧 
		RtpPacketFifo.DatCount++; 
	}else 
	{ 
	/*	Sleep(30);*/ 
		return FALSE; 
	} 
	return TRUE; 
} 
 
BOOL CAllProtocol::mu_read_pes_fifo(unsigned char **Buf,int &len) 
{ 
	if (RtpPacketFifo.DatCount>0) 
	{ 
		*Buf = RtpPacketFifo.Buf[RtpPacketFifo.Read]; 
		len  = RtpPacketFifo.iDataLen[RtpPacketFifo.Read]; 
		RtpPacketFifo.Read++; 
		RtpPacketFifo.Read %= (20); //小技巧 
		RtpPacketFifo.DatCount--; 
		return TRUE; 
	} 
	return FALSE; 
} 
////FIFO队列 结束 
 
//纯粹的视频处理函数 
void CAllProtocol::DealVideo() 
{ 
	rtp_header_t* rtp; 
	unsigned char *pBuf = NULL; 
	unsigned char* p; 
	int time_stamp; 
	int FrameLen; 
	int headlen = RTP_HEADER_SIZE+sizeof(Payload_Mode_a); 
 
	pBuf = new unsigned char[1024*1024]; 
	p = pBuf; 
	for (;;) 
	{ 
		unsigned char *pData = NULL; 
		int lenData; 
		//读取数据 
		if (!mu_read_pes_fifo(&pData,lenData)) 
		{ 
			VideoFrameNum =0; 
			//没有读到数据,那么退出并释放内存 
			SAFE_DELETE(pBuf); 
			return; 
		} 
 
		rtp = (rtp_header_t*)pData; 
		memcpy( p, pData+headlen, lenData-headlen ); 
		p += (lenData-headlen); 
		if (rtp->markbit) 
		{ 
			time_stamp = rtp->timestamp; 
			FrameLen = int(p-pBuf); 
			break; 
		} 
	} 
	VideoFrameNum--;  //减掉一个视频数据 
	//如果队列里面的数据量大于15了,此时我们最好对掉这帧数据 
	if (m_VideoQueue->GetCount()<10) 
	{ 
		//已取到一帧, 加入帧队列 
		CGoldNode *tempNode = new CGoldNode( pBuf, FrameLen ,time_stamp); 
		m_VideoQueue->PutNode(tempNode); 
	} 
	//删除分配的内存 
	SAFE_DELETE(pBuf); 
} 
 
//读取一帧H264数据 
//输入参数:无 
//输出参数: 
//		pNode:H264帧数据 
//返回值: 
//		0:帧缓冲空,无法取得数据 
//		>0:取得帧数据 
CGoldNode* CAllProtocol::GetFrame() 
{ 
	CGoldNode* pNode; 
	//安全一点 
	if (m_VideoQueue==NULL)return NULL; 
	//判断是否是第一帧数据 
	if(m_bFirstFrame) 
	{ 
		//如果是看数据个数是否大于3; 
		if(m_VideoQueue->GetCount() < 2) 
		{ 
			return NULL; 
		} 
		m_bFirstFrame = FALSE;	 
	} 
	else 
	{ 
		// There is no frame in the receive queue  
		if (m_VideoQueue->GetCount() == 0) return NULL; 
	} 
#ifdef _DEBUG 
	char db_buf[100]; 
	sprintf(db_buf, "队列中的数据帧数:%d\n", m_VideoQueue->GetCount());		 
	//OutputDebugString(db_buf); 
#endif 
	//如果队列中视频数据的帧数大于5,我们可以考虑把视频数据丢掉 
	DWORD RetValue = 0; 
	//得到一帧数据 
	if (m_VideoQueue->GetNode(&pNode)) 
	{ 
		return pNode; 
	}else{ 
		return NULL; 
	} 
} 
 
//关闭和服务端的链接 
void CAllProtocol::CloseConnect(void) 
{ 
	SAFE_CLOSESOCKET(m_SocketTCP); 
	SAFE_CLOSESOCKET(m_SocketRTP); 
} 
 
//调用这个函数设置客户端的服务器IP地址和端口号 
void CAllProtocol::SetServerAddrr(char* RemoteIp,unsigned short RemotePort) 
{ 
	m_RemoteIp = RemoteIp; 
	m_RemotePort = RemotePort; 
} 
 
//接口: 用户通过这个函数设置RTP数据采集的端口号 
void CAllProtocol::SetRTPPort(unsigned short LocalPort) 
{ 
	m_LocalPort = LocalPort; 
} 
//接口: 用户调用这个函数,开始链接服务器 
BOOL CAllProtocol::USRStart(char *IPaddr, int port) 
{ 
	int iRetCode = -1; 
	//设置服务器地址 
	SetServerAddrr(IPaddr,port); 
	iRetCode = ConnectTCP(); 
	if (iRetCode<0)return FALSE; 
	//USRStartPlay(0); 
	iRetCode = ConnectRTP(); 
	if (iRetCode<0)return FALSE; 
	return TRUE; 
} 
 
//接口: 断开和服务端的链接 
BOOL CAllProtocol::USRStop() 
{ 
	CloseConnect(); 
	return TRUE; 
} 
 
BOOL CAllProtocol::GetPicture(PICTURE *pic) 
{ 
	//首先判断TCP SOCKET是否还存在 
	if (m_SocketTCP == INVALID_SOCKET)return FALSE; 
	ClearTcpDataBuf(m_SocketTCP); 
 
	int iRet; 
	PACKET_TYPE TempPacket; 
 
	memset(&TempPacket, 0, sizeof(PACKET_TYPE)); 
	TempPacket.PacketType = 0x33; 
	TempPacket.OperationCode = OP0047;		//查询图象参数 
	iRet = SendTcpData(m_SocketTCP,(char*)&TempPacket,PACKET_HEAD); 
	if (iRet != PACKET_HEAD)return FALSE;	//发送数据不成功 
	//等待返回的数据 
	PICTURE_LOOK_RESPONSE look_response; 
	RECVBUF RecvBuf; 
	ZeroMemory(&look_response,sizeof(PICTURE_LOOK_RESPONSE)); 
	ZeroMemory(&RecvBuf,sizeof(RECVBUF)); 
	ZeroMemory(&TempPacket,sizeof(PACKET_TYPE)); 
	//接收数据头 
	iRet = RecvTcpData(m_SocketTCP,(char*)&TempPacket, PACKET_HEAD); 
	if (iRet != PACKET_HEAD)return FALSE;	//接收头失败 
	if (TempPacket.OperationCode != OP0047)return FALSE; 
	//接收数据 
	iRet = RecvTcpData(m_SocketTCP,RecvBuf.Buf,TempPacket.DataLength); 
	if (iRet>0) 
	{ 
		/*if (RecvBuf.OperatorCode == OP0047) 
		{*/ 
			memcpy(&look_response, RecvBuf.Buf, sizeof(PICTURE_LOOK_RESPONSE)); 
			memcpy(pic, &look_response.Picture, sizeof(PICTURE)); 
			return TRUE; 
		/*}*/ 
	} 
	return FALSE; 
} 
 
BOOL CAllProtocol::SetPicture(PICTURE *pic) 
{ 
	//首先判断TCP SOCKET是否还存在 
	if (m_SocketTCP == INVALID_SOCKET)return FALSE; 
	ClearTcpDataBuf(m_SocketTCP); 
 
	int iRet; 
	int SenLen; 
	PICTURE_DATA_REQUEST PicReq; 
	PACKET_TYPE TempPacket; 
	 
	SenLen = sizeof(PICTURE_DATA_REQUEST); 
	memset(&PicReq, 0, sizeof(PICTURE_DATA_REQUEST)); 
	memcpy(&PicReq.Picture, pic, sizeof(PICTURE)); 
	memset(&TempPacket, 0, sizeof(PACKET_TYPE)); 
 
	TempPacket.PacketType = 0x33; 
	TempPacket.OperationCode = OP0046;		//设置图象参数 
	TempPacket.DataLength = sizeof(PICTURE_DATA_REQUEST); 
	memcpy(TempPacket.DataBuffer, &PicReq, sizeof(PICTURE_DATA_REQUEST)); 
	iRet = SendTcpData(m_SocketTCP,(char*)&TempPacket,PACKET_HEAD+SenLen); 
	if (iRet != (PACKET_HEAD+SenLen))return FALSE;	//发送数据不成功 
 
	PICTURE_DATA_RESPONSE PicResp; 
	RECVBUF RecvBuf; 
	ZeroMemory(&PicResp,sizeof(PICTURE_DATA_RESPONSE)); 
	ZeroMemory(&RecvBuf,sizeof(RECVBUF)); 
 
	iRet = RecvTcpData(m_SocketTCP,(char*)&RecvBuf,sizeof(RECVBUF)); 
	if (iRet>0) 
	{ 
		/*if (RecvBuf.OperatorCode == OP0047) 
		{*/ 
		//memcpy(&PicResp, RecvBuf.Buf, sizeof(PICTURE_DATA_RESPONSE)); 
		return TRUE; 
		/*}*/ 
	} 
	return FALSE; 
} 
 
int CAllProtocol::ControlPlatform(unsigned char Action, int Step) 
{ 
	//首先判断TCP SOCKET是否还存在 
	//if (m_SocketTCP == INVALID_SOCKET)return FALSE; 
	//ClearTcpDataBuf(m_SocketTCP); 
 
	//int iRet; 
	//PLATFORM_REQUEST plat_request; 
	//memset(&plat_request, 0, sizeof(PLATFORM_REQUEST)); 
	//plat_request.Platform.Direction = Action; 
 return 1; 
}