上一篇咱們介紹了RTP協議的一些基本知識,下面咱們介紹如何使用jrtplib這個庫傳輸H264編碼。app
JRTP傳輸: 好了,下面是我寫的使用JRTP進行發送H264數據包的例子,具體解釋能夠看註釋。發送端也能夠接收接收端發送過來的RTCP數據包。 #define MAX_RTP_PKT_LENGTH 1360 #define H264 96 bool CheckError(int rtperr); class CRTPSender : public RTPSession { public: CRTPSender(void); ~CRTPSender(void); protected: void OnAPPPacket(RTCPAPPPacket *apppacket,const RTPTime &receivetime,const RTPAddress *senderaddress); void OnBYEPacket(RTPSourceData *srcdat); void OnBYETimeout(RTPSourceData *srcdat); public: void SendH264Nalu(unsigned char* m_h264Buf,int buflen); void SetParamsForSendingH264(); }; bool CheckError(int rtperr) { if (rtperr < 0) { std::cout<<"ERROR: "<<RTPGetErrorString(rtperr)<<std::endl; return false; } return true; } CRTPSender::CRTPSender(void) { } CRTPSender::~CRTPSender(void) { } void CRTPSender::OnAPPPacket(RTCPAPPPacket *apppacket,const RTPTime &receivetime,const RTPAddress *senderaddress) {//收到RTCP APP數據 std::cout<<"Got RTCP packet from: "<<senderaddress<<std::endl; std::cout<<"Got RTCP subtype: "<<apppacket->GetSubType()<<std::endl; std::cout<<"Got RTCP data: "<<(char *)apppacket->GetAPPData()<<std::endl; return ; } void CRTPSender::SendH264Nalu(unsigned char* m_h264Buf,int buflen) { unsigned char *pSendbuf; //發送數據指針 pSendbuf = m_h264Buf; //去除前導碼0x000001 或者0x00000001 //if( 0x01 == m_h264Buf[2] ) //{ // pSendbuf = &m_h264Buf[3]; // buflen -= 3; //} //else //{ // pSendbuf = &m_h264Buf[4]; // buflen -= 4; //} char sendbuf[1430]; //發送的數據緩衝 memset(sendbuf,0,1430); int status; printf("send packet length %d \n",buflen); if ( buflen <= MAX_RTP_PKT_LENGTH ) { memcpy(sendbuf,pSendbuf,buflen); status = this->SendPacket((void *)sendbuf,buflen); CheckError(status); } else if(buflen > MAX_RTP_PKT_LENGTH) { //設置標誌位Mark爲0 this->SetDefaultMark(false); //printf("buflen = %d\n",buflen); //獲得該須要用多少長度爲MAX_RTP_PKT_LENGTH字節的RTP包來發送 int k=0,l=0; k = buflen / MAX_RTP_PKT_LENGTH; l = buflen % MAX_RTP_PKT_LENGTH; int t=0;//用指示當前發送的是第幾個分片RTP包 char nalHeader = pSendbuf[0]; // NALU 頭ª¡¤ while( t < k || ( t==k && l>0 ) ) { if( (0 == t ) || ( t<k && 0!=t ) )//第一包到最後包的前一包 { /*sendbuf[0] = (nalHeader & 0x60)|28; sendbuf[1] = (nalHeader & 0x1f); if ( 0 == t ) { sendbuf[1] |= 0x80; } memcpy(sendbuf+2,&pSendbuf[t*MAX_RTP_PKT_LENGTH],MAX_RTP_PKT_LENGTH); status = this->SendPacket((void *)sendbuf,MAX_RTP_PKT_LENGTH+2);*/ memcpy(sendbuf,&pSendbuf[t*MAX_RTP_PKT_LENGTH],MAX_RTP_PKT_LENGTH); status = this->SendPacket((void *)sendbuf,MAX_RTP_PKT_LENGTH); CheckError(status); t++; } //最後一包 else if( ( k==t && l>0 ) || ( t== (k-1) && l==0 )) { //設置標誌位Mark爲1 this->SetDefaultMark(true); int iSendLen; if ( l > 0) { iSendLen = buflen - t*MAX_RTP_PKT_LENGTH; } else iSendLen = MAX_RTP_PKT_LENGTH; //sendbuf[0] = (nalHeader & 0x60)|28; //sendbuf[1] = (nalHeader & 0x1f); //sendbuf[1] |= 0x40; //memcpy(sendbuf+2,&pSendbuf[t*MAX_RTP_PKT_LENGTH],iSendLen); //status = this->SendPacket((void *)sendbuf,iSendLen+2); memcpy(sendbuf,&pSendbuf[t*MAX_RTP_PKT_LENGTH],iSendLen); status = this->SendPacket((void *)sendbuf,iSendLen); CheckError(status); t++; } } } } void CRTPSender::SetParamsForSendingH264() { this->SetDefaultPayloadType(H264);//設置傳輸類型 this->SetDefaultMark(true); //設置位 this->SetTimestampUnit(1.0/9000.0); //設置採樣間隔 this->SetDefaultTimestampIncrement(3600);//設置時間戳增長間隔 } void CRTPSender::OnBYEPacket(RTPSourceData *srcdat) { } void CRTPSender::OnBYETimeout(RTPSourceData *srcdat) { } Main.cpp 在上一篇博客中的編碼以後進行傳輸 #define SSRC 100 #define DEST_IP_STR "192.168.1.252" #define DEST_PORT 1234 #define BASE_PORT 2222 int iNal = 0; x264_nal_t* pNals = NULL; void SetRTPParams(CRTPSender& sess,uint32_t destip,uint16_t destport,uint16_t baseport) { int status; //RTP+RTCP庫初始化SOCKET環境 RTPUDPv4TransmissionParams transparams; RTPSessionParams sessparams; sessparams.SetOwnTimestampUnit(1.0/9000.0); //時間戳單位 sessparams.SetAcceptOwnPackets(true); //接收本身發送的數據包 sessparams.SetUsePredefinedSSRC(true); //設置使用預先定義的SSRC sessparams.SetPredefinedSSRC(SSRC); //定義SSRC transparams.SetPortbase(baseport); status = sess.Create(sessparams,&transparams); CheckError(status); destip = ntohl(destip); RTPIPv4Address addr(destip,destport); status = sess.AddDestination(addr); CheckError(status); //爲發送H264包設置參數 //sess.SetParamsForSendingH264(); } bool InitSocket() { int Error; WORD VersionRequested; WSADATA WsaData; VersionRequested=MAKEWORD(2,2); Error=WSAStartup(VersionRequested,&WsaData); //啓動WinSock2 if(Error!=0) { printf("Error:Start WinSock failed!\n"); return false; } else { if(LOBYTE(WsaData.wVersion)!=2||HIBYTE(WsaData.wHighVersion)!=2) { printf("Error:The version is WinSock2!\n"); WSACleanup(); return false; } } return true; } void CloseSocket(CRTPSender sess) { //發送一個BYE包離開會話最多等待秒鐘超時則不發送 sess.BYEDestroy(RTPTime(3,0),0,0); WSACleanup(); } int main(int argc, char** argv) { InitSocket(); CRTPSender sender; string destip_str = "127.0.0.1"; uint32_t dest_ip = inet_addr(destip_str.c_str()); SetRTPParams(sender,dest_ip,DEST_PORT,BASE_PORT); sender.SetParamsForSendingH264(); //…x264設置參數等步驟,具體參見上篇博客 for(int i = 0; i < nFrames ; i++ ) { //讀取一幀 read_frame_y4m(pPicIn,(hnd_t*)y4m_hnd,i); if( i ==0 ) pPicIn->i_pts = i; else pPicIn->i_pts = i - 1; //編碼 int frame_size = x264_encoder_encode(pX264Handle,&pNals,&iNal,pPicIn,pPicOut); if(frame_size >0) { for (int i = 0; i < iNal; ++i) {//將編碼數據寫入文件t //fwrite(pNals[i].p_payload, 1, pNals[i].i_payload, pFile); //發送編碼文件 sender.SendH264Nalu(pNals[i].p_payload,pNals[i].i_payload); RTPTime::Wait(RTPTime(1,0)); } } } CloseSocket(sender); //一些清理工做… }