一個基於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\n", 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\n", __func__);
 5         return RTP_ERROR;
 6     }
 7     if(0 == media_session->TimeRate) {
 8         fprintf(stderr, "%s: Invalid MediaSession::TimeRate\n", __func__);
 9         return RTP_ERROR;
10     }
11     if(0 == media_session->RTPPort) {
12         fprintf(stderr, "%s: Invalid MediaSession::RTPPort\n", __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 }

 

 

上一篇                 回目錄 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM