视频的采集显示以及编码解码都实现了,下一步就要考虑把数据发出去,由其他客户端播放(设计是先做网络传输,一切都正常后再做音频,虽然叫音视频聊天,其实网络才是最重要的部分)。这里使用udp进行发送和接收。
考虑到音视频聊天,需要支持ios,android等多平台,所以尽可能采用公用方法,下面是简单的一个封装:
/////////////////////
//udpsock.h:
class cudpsock
{
public:
cudpsock();
virtual ~cudpsock();
int open(int nport);
int close();
int send( const char* pbuf, size_t nlen, uint32_t ip, uint16_t port ) const;
int receive(char* pbuf, size_t nlen, uint32_t& ip, uint16_t& port) const;
int m_handle;
};
////////////////////////
//
udpsock.cpp
cudpsock::cudpsock()
{
m_handle = invalid_socket;
}
cudpsock::~cudpsock()
{
}
int cudpsock::open(int nport)
{
if(m_handle != invalid_socket)
{
close();
}
m_handle = socket(af_inet, sock_dgram, 0) ;
if (m_handle == invalid_socket )
{
return -1;
}
sockaddr_in addr;
memset(&addr, 0, sizeof(sockaddr_in));
addr.sin_family = af_inet;
addr.sin_port = htons (nport);
addr.sin_addr.s_addr = htonl( inaddr_any );
//addr.sin_addr.s_addr = inet_addr (host);
int iresult = bind(m_handle, (struct sockaddr*) &addr, sizeof( addr ));
if( iresult == socket_error )
{
int err=getlasterror();
close();
}
return iresult;
}
int cudpsock::close()
{
int iresult = 0;
if ( m_handle != invalid_socket )
{
#ifndef win32
close(m_handle);
#else
closesocket(m_handle);
#endif
m_handle = invalid_socket;
}
return 0;
}
int cudpsock::send( const char* pbuf, size_t nlen, uint32_t ip, uint16_t port ) const
{
if (m_handle == invalid_socket)
return 0;
sockaddr_in addr;
memset(&addr, 0, sizeof(sockaddr_in));
addr.sin_port = htons((u_short)port);
addr.sin_addr.s_addr = ip;
addr.sin_family = af_inet;
int nsendlen = sendto(m_handle, pbuf, nlen, 0, (sockaddr*)&addr, sizeof(sockaddr_in));
if (nsendlen < 0)
{
int nerr = getlasterror();
char log[1024] = {'\0'};
//sprintf(log, "send error = %d \n", nerr);
//outputdebugstring(log);
}
return nsendlen;
}
int cudpsock::receive(char* pbuf, size_t nlen, uint32_t& ip, uint16_t& port) const
{
if (m_handle == invalid_socket)
return 0;
sockaddr_in sockaddr;
memset(&sockaddr, 0, sizeof(sockaddr));
int nsockaddrlen = sizeof(sockaddr);
int ireceived = recvfrom( m_handle, pbuf, nlen, 0, (sockaddr*)&sockaddr, &nsockaddrlen );
port= ntohs(sockaddr.sin_port);
ip = sockaddr.sin_addr.s_addr;
return ireceived;
}
在windows下使用,需要注意加上wsastartup。
这个class类似casyncsocket,不过功能更简单,只处理udp。还是那句话,我们的目的是用最简单的方法实现一个基本的但足够用的音视频功能。虽然有用更好的更优的方案,但本着够用的原则。
前两部分的代码在:
udp这部分还只是基本功能,需要在这上面做成两层再提交,一个是分包重组,另一个是可靠udp。
分包重组是因为udp数据包有最大限制,本项目里限定最大包为1400.假设一个视频帧有10k,可能需要分成8包。收到后再重组。但是,因为udp是不可靠的,也是无序的,有可能中间丢掉部分,也可能后发先至,所以分包好做,但重组需要花点时间。至于可靠udp,如果不考虑效率的话,收到一包,回复一个确认就行了,非常简单。
下文开始讲分包重组和可靠udp。