zoukankan      html  css  js  c++  java
  • 一个基于JRTPLIB的轻量级RTSP客户端(myRTSPClient)——实现篇:(十)使用JRTPLIB传输RTP数据

    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数据就是通过调用这两个函数完成的。

     1 uint8_t * MyRTPSession::GetMyRTPData(uint8_t * data_buf, size_t * size, unsigned long timeout_ms)
     2 {
     3     if(!data_buf) {
     4         fprintf(stderr, "%s: Invalide argument('data_buf==NULL')", __func__);
     5         return NULL;
     6     }
     7 
     8     if(!size) {
     9         fprintf(stderr, "%s: Invalide argument('size==NULL')", __func__);
    10         return NULL;
    11     }
    12 
    13     unsigned long UsleepTimes = (timeout_ms + USLEEP_UNIT - 1) / USLEEP_UNIT; // floor the 'timeout_ms / USLEEP_UNIT'
    14 
    15     do {
    16 #ifndef RTP_SUPPORT_THREAD
    17         int status = Poll();
    18         if(!IsError(status)) return NULL;
    19 #endif 
    20 
    21         BeginDataAccess();
    22 
    23         // check incoming packets
    24         if (!GotoFirstSourceWithData()) {
    25             EndDataAccess();
    26             usleep(USLEEP_UNIT);
    27             UsleepTimes--;
    28             continue;
    29             // return NULL;
    30         }
    31         RTPPacket *pack;
    32 
    33         if(!(pack = GetNextPacket()))
    34         {
    35             EndDataAccess();
    36             usleep(USLEEP_UNIT);
    37             UsleepTimes--;
    38             continue;
    39             // return NULL;
    40         }
    41 
    42         size_t PacketSize = 0;
    43         uint8_t * Packet = NULL;
    44         Packet = pack->GetPayloadData();
    45         PacketSize = pack->GetPayloadLength();
    46         // printf("data length: %lu
    ", PacketSize);
    47 
    48         *size = PacketSize;
    49         memcpy(data_buf, Packet, PacketSize);
    50 
    51         // we don't longer need the packet, so
    52         // we'll delete it
    53         DeletePacket(pack);
    54         EndDataAccess();
    55         UsleepTimes = 0; // Got the data. So not need to sleep any more.
    56     } while(UsleepTimes > 0);
    57 
    58     return data_buf;
    59 }

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

    此处的MyRTP_SetUp的作用是建立会话,它会确定RTP/RTCP的UDP端口,建立通信socket。每当RTSP的SETUP命令设置成功后,都会调用此函数。
     1 int MyRTPSession::MyRTP_SetUp(MediaSession * media_session)
     2 {
     3     if(!media_session) {
     4         fprintf(stderr, "%s: Invalid media session
    ", __func__);
     5         return RTP_ERROR;
     6     }
     7     if(0 == media_session->TimeRate) {
     8         fprintf(stderr, "%s: Invalid MediaSession::TimeRate
    ", __func__);
     9         return RTP_ERROR;
    10     }
    11     if(0 == media_session->RTPPort) {
    12         fprintf(stderr, "%s: Invalid MediaSession::RTPPort
    ", __func__);
    13         return RTP_ERROR;
    14     }
    15 
    16     int status;
    17 
    18     // Now, we'll create a RTP session, set the destination
    19     // and poll for incoming data.
    20 
    21     RTPUDPv4TransmissionParams transparams;
    22     RTPSessionParams sessparams;
    23 
    24     // IMPORTANT: The local timestamp unit MUST be set, otherwise
    25     //            RTCP Sender Report info will be calculated wrong
    26     // In this case, we'll be just use 8000 samples per second.
    27     sessparams.SetOwnTimestampUnit(1.0/media_session->TimeRate);         
    28 
    29     sessparams.SetAcceptOwnPackets(true);
    30     transparams.SetPortbase(media_session->RTPPort);
    31     status = Create(sessparams,&transparams);  
    32     return IsError(status);
    33 }

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

     1 void MyRTPSession::MyRTP_Teardown(MediaSession * media_session, struct timeval * tval)
     2 {
     3     struct timeval Timeout;
     4 
     5     if(!tval) {
     6         Timeout.tv_sec = 1; 
     7         Timeout.tv_usec = 0; 
     8     } else {
     9         Timeout.tv_sec = tval->tv_sec;
    10         Timeout.tv_usec = tval->tv_usec;
    11     }
    12 
    13     media_session->RTPPort = 0;
    14     BYEDestroy(RTPTime(Timeout.tv_sec, Timeout.tv_usec), 0, 0);
    15 }

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

     1 void MyRTPSession::OnBYEPacket(RTPSourceData *dat)
     2 {
     3     if (dat->IsOwnSSRC())
     4         return;
     5 
     6     uint32_t ip;
     7     uint16_t port;
     8 
     9     if (dat->GetRTPDataAddress() != 0)
    10     {
    11         const RTPIPv4Address *addr = (const RTPIPv4Address *)(dat->GetRTPDataAddress());
    12         ip = addr->GetIP();
    13         port = addr->GetPort();
    14     }
    15     else if (dat->GetRTCPDataAddress() != 0)
    16     {
    17         const RTPIPv4Address *addr = (const RTPIPv4Address *)(dat->GetRTCPDataAddress());
    18         ip = addr->GetIP();
    19         port = addr->GetPort()-1;
    20     }
    21     else
    22         return;
    23 
    24     RTPIPv4Address dest(ip,port);
    25     DeleteDestination(dest);
    26 
    27     struct in_addr inaddr;
    28     inaddr.s_addr = htonl(ip);
    29     std::cout << "Deleting destination " << std::string(inet_ntoa(inaddr)) << ":" << port << std::endl;
    30     if(DestroiedClbk) {
    31         DestroiedClbk();
    32     } 
    33 }

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

     1 void MyRTPSession::OnNewSource(RTPSourceData *dat)
     2 {
     3     if (dat->IsOwnSSRC())
     4         return;
     5 
     6     uint32_t ip;
     7     uint16_t port;
     8 
     9     if (dat->GetRTPDataAddress() != 0)
    10     {
    11         const RTPIPv4Address *addr = (const RTPIPv4Address *)(dat->GetRTPDataAddress());
    12         ip = addr->GetIP();
    13         port = addr->GetPort();
    14     }
    15     else if (dat->GetRTCPDataAddress() != 0)
    16     {
    17         const RTPIPv4Address *addr = (const RTPIPv4Address *)(dat->GetRTCPDataAddress());
    18         ip = addr->GetIP();
    19         port = addr->GetPort()-1;
    20     }
    21     else
    22         return;
    23 
    24     RTPIPv4Address dest(ip,port);
    25     AddDestination(dest);
    26 
    27     struct in_addr inaddr;
    28     inaddr.s_addr = htonl(ip);
    29     std::cout << "Adding destination " << std::string(inet_ntoa(inaddr)) << ":" << port << std::endl;
    30 }

    上一篇                 回目录 

  • 相关阅读:
    springboot启动时不加载数据库
    ElasticSearch常用的查询操作
    Windows10 搭建ElasticSearch集群服务
    windows10安装ElasticSearch7.5遇到两个警告解决方法
    MybatisPlus自动生成代码配置
    初识RabbitMQ ------基本概念
    深拷贝与浅拷贝的区别
    Java8中 LocalDateTime与Date互相转换
    Spring中常用的工具类StringUtils、DateUtils、CollectionUtils等
    SpringBoot定时任务 @Scheduled cron 表达式说明
  • 原文地址:https://www.cnblogs.com/ansersion/p/8991983.html
Copyright © 2011-2022 走看看