一個基於JRTPLIB的輕量級RTSP客戶端(myRTSPClient)——實現篇:(十)使用JRTPLIB傳輸RTP數據

myRtspClient經過簡單修改JRTPLIB的官方例程做爲其RTP傳輸層實現。由於JRTPLIB使用的是CMAKE編譯工具,這就是爲何編譯myRtspClient時須要預裝CMAKE。html

該部分全部代碼均集中在myRtpSession.cpp中,接下來將對其進行分析。服務器

 

1、獲取RTP數據session

此處GetMyRTPData獲取數據的方式主要是輪詢,即每隔USLEEP_UNIT個微秒輪詢一次直到獲取到一包數據或超時,超時時間爲timeout_ms,單位是微秒。socket

GetMyRTPPacket的邏輯與之相同,可是會比前者多獲取RTP消息頭,對於myRtspClient的用戶來講,這一樣也就是RtspClient::GetMediaPacket和RtspClient::GetMediaData的區別(邏輯相同,GetMyRTPPacket的代碼就再也不此附上了)。ide

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 }

 

2、會話創建、結束等接口工具

此處的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命令設置成功後,都會調用此函數。ui

 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的代碼就再也不此附上了)。this

 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就會被調用url

 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 }

 

 

上一篇                 回目錄 

相關文章
相關標籤/搜索