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 }