myRtspClient通过简单修改JRTPLIB的官方例程作为其RTP传输层实现。因为JRTPLIB使用的是CMAKE编译工具,这就是为什么编译myRtspClient时需要预装CMAKE。

该部分所有代码均集中在myRtpSession.cpp中,接下来将对其进行分析。

一、获取RTP数据

此处GetMyRTPData获取数据的方式主要是轮询,即每隔USLEEP_UNIT个微秒轮询一次直到获取到一包数据或超时,超时时间为timeout_ms,单位是微秒。

GetMyRTPPacket的逻辑与之相同,但是会比前者多获取RTP消息头,对于myRtspClient的用户来说,这同样也就是RtspClient::GetMediaPacket和RtspClient::GetMediaData的区别(逻辑相同,GetMyRTPPacket的代码就不再此附上了)。

RtspClient::GetMediaPacket和RtspClient::GetMediaData最终获取RTP数据就是通过调用这两个函数完成的。

 uint8_t * MyRTPSession::GetMyRTPData(uint8_t * data_buf, size_t * size, unsigned long timeout_ms)
{
if(!data_buf) {
fprintf(stderr, "%s: Invalide argument('data_buf==NULL')", __func__);
return NULL;
} if(!size) {
fprintf(stderr, "%s: Invalide argument('size==NULL')", __func__);
return NULL;
} unsigned long UsleepTimes = (timeout_ms + USLEEP_UNIT - ) / USLEEP_UNIT; // floor the 'timeout_ms / USLEEP_UNIT' do {
#ifndef RTP_SUPPORT_THREAD
int status = Poll();
if(!IsError(status)) return NULL;
#endif BeginDataAccess(); // check incoming packets
if (!GotoFirstSourceWithData()) {
EndDataAccess();
usleep(USLEEP_UNIT);
UsleepTimes--;
continue;
// return NULL;
}
RTPPacket *pack; if(!(pack = GetNextPacket()))
{
EndDataAccess();
usleep(USLEEP_UNIT);
UsleepTimes--;
continue;
// return NULL;
} size_t PacketSize = ;
uint8_t * Packet = NULL;
Packet = pack->GetPayloadData();
PacketSize = pack->GetPayloadLength();
// printf("data length: %lu\n", PacketSize); *size = PacketSize;
memcpy(data_buf, Packet, PacketSize); // we don't longer need the packet, so
// we'll delete it
DeletePacket(pack);
EndDataAccess();
UsleepTimes = ; // Got the data. So not need to sleep any more.
} while(UsleepTimes > ); return data_buf;
}

二、会话建立、结束等接口

此处的MyRTP_SetUp的作用是建立会话,它会确定RTP/RTCP的UDP端口,建立通信socket。每当RTSP的SETUP命令设置成功后,都会调用此函数。
 int MyRTPSession::MyRTP_SetUp(MediaSession * media_session)
{
if(!media_session) {
fprintf(stderr, "%s: Invalid media session\n", __func__);
return RTP_ERROR;
}
if( == media_session->TimeRate) {
fprintf(stderr, "%s: Invalid MediaSession::TimeRate\n", __func__);
return RTP_ERROR;
}
if( == media_session->RTPPort) {
fprintf(stderr, "%s: Invalid MediaSession::RTPPort\n", __func__);
return RTP_ERROR;
} int status; // Now, we'll create a RTP session, set the destination
// and poll for incoming data. RTPUDPv4TransmissionParams transparams;
RTPSessionParams sessparams; // IMPORTANT: The local timestamp unit MUST be set, otherwise
// RTCP Sender Report info will be calculated wrong
// In this case, we'll be just use 8000 samples per second.
sessparams.SetOwnTimestampUnit(1.0/media_session->TimeRate); sessparams.SetAcceptOwnPackets(true);
transparams.SetPortbase(media_session->RTPPort);
status = Create(sessparams,&transparams);
return IsError(status);
}

客户端通过MyRTP_Teardown发起销毁会话,每当RTSP的TEARDOWN命令设置成功后,都会调用此函数。

 void MyRTPSession::MyRTP_Teardown(MediaSession * media_session, struct timeval * tval)
{
struct timeval Timeout; if(!tval) {
Timeout.tv_sec = ;
Timeout.tv_usec = ;
} else {
Timeout.tv_sec = tval->tv_sec;
Timeout.tv_usec = tval->tv_usec;
} media_session->RTPPort = ;
BYEDestroy(RTPTime(Timeout.tv_sec, Timeout.tv_usec), , );
}

客户端通过OnBYEPacket被动销毁会话,当服务器向客户端发送BYE的RTP数据包时(比如当一段媒体流播放完的时候),该函数就会被调用。其中DestroiedClbk是myRtspClient提供给用户的回调接口。用户可以通过调用RtspClient::SetAudioByeFromServerClbk/RtspClient::SetVideoByeFromServerClbk来设置该函数。(逻辑相同,OnRemoveSource的代码就不再此附上了)。

 void MyRTPSession::OnBYEPacket(RTPSourceData *dat)
{
if (dat->IsOwnSSRC())
return; uint32_t ip;
uint16_t port; if (dat->GetRTPDataAddress() != )
{
const RTPIPv4Address *addr = (const RTPIPv4Address *)(dat->GetRTPDataAddress());
ip = addr->GetIP();
port = addr->GetPort();
}
else if (dat->GetRTCPDataAddress() != )
{
const RTPIPv4Address *addr = (const RTPIPv4Address *)(dat->GetRTCPDataAddress());
ip = addr->GetIP();
port = addr->GetPort()-;
}
else
return; RTPIPv4Address dest(ip,port);
DeleteDestination(dest); struct in_addr inaddr;
inaddr.s_addr = htonl(ip);
std::cout << "Deleting destination " << std::string(inet_ntoa(inaddr)) << ":" << port << std::endl;
if(DestroiedClbk) {
DestroiedClbk();
}
}

每当新加入一个RTP数据源,OnNewSource就会被调用

 void MyRTPSession::OnNewSource(RTPSourceData *dat)
{
if (dat->IsOwnSSRC())
return; uint32_t ip;
uint16_t port; if (dat->GetRTPDataAddress() != )
{
const RTPIPv4Address *addr = (const RTPIPv4Address *)(dat->GetRTPDataAddress());
ip = addr->GetIP();
port = addr->GetPort();
}
else if (dat->GetRTCPDataAddress() != )
{
const RTPIPv4Address *addr = (const RTPIPv4Address *)(dat->GetRTCPDataAddress());
ip = addr->GetIP();
port = addr->GetPort()-;
}
else
return; RTPIPv4Address dest(ip,port);
AddDestination(dest); struct in_addr inaddr;
inaddr.s_addr = htonl(ip);
std::cout << "Adding destination " << std::string(inet_ntoa(inaddr)) << ":" << port << std::endl;
}

上一篇                回目录

最新文章

  1. myeclipse导入项目出现jquery错误(有红叉)
  2. HTML5-列表的使用
  3. char导致的验证异常
  4. mvn archetype:create报错解决办法
  5. .Net Attribute特性
  6. CentOS下下删除大量文件
  7. DTCMS规格统一赋值
  8. java.lang和java.lang.annotation中实现Annotation的类小结
  9. 播放视频的框架Vitamio的使用问题
  10. 兼容不同浏览器的 CSS Hack 写法
  11. My Function Lib
  12. XML解析之SAX
  13. 【Python】 关于import和package结构
  14. ansj构造最短路径
  15. 使用vue-cli搭建项目
  16. 一本通1619【例 1】Prime Distance
  17. ext4文件系统的delalloc选项造成单次写延迟增加的分析
  18. 向Windows内核驱动传递用户层定义的事件Event,并响应内核层的通知
  19. 温习DL之一:梯度的概念
  20. 巧妙利用JQuery和Servlet来实现跨域请求

热门文章

  1. day_6.10 tcp三次握手 四次挥手
  2. 二进制安装mysql
  3. HDOJ HDU 1850 Being a Good Boy in Spring Festival
  4. 汇编语言里 eax, ebx, ecx, edx, esi, edi, ebp, esp
  5. vins-mono源码解读
  6. mysql 语句 字段 和结构主键外键的增删改
  7. [No0000135]程序员修炼之道 Tips
  8. [No0000124]WPF 扩展控件Behavior的几种方式
  9. Javascript:一些基本语法
  10. Linear transformations. 线性变换与矩阵的关系