使用jrtplib打包发送h264数据,关使用vlc|mplayer播放
生活随笔
收集整理的這篇文章主要介紹了
使用jrtplib打包发送h264数据,关使用vlc|mplayer播放
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
前段時間寫了個測試程序,使用jrtplib,把h264打成標準的rtp數據包,并能通過vlc,mpalyer等播放器播放出來。這中間主要的難點是解析h264數據中的nal單元,并把nal單元打成rtp包,之后的事情都是jrtplib庫做了。然后就可以通過播放器播放出來了。
使用播放器播放時使用以下命令:
mplayer player.sdp
或者
用vlc打開player.sdp
對于rtp打包不懂的同學,可以仔細看下這個包里面的文檔和一個rtp打包的代碼,這是我上傳的資源。
http://download.csdn.net/detail/xyyangkun/6990313
主要的代碼貼出來:
/** test_jrtp.cpp** Created on: 2014-2-19* Author: xy*/#include "rtpsession.h" #include "rtpsessionparams.h" #include "rtpudpv4transmitter.h" #include "rtpipv4address.h" #include "rtptimeutilities.h" #include "rtppacket.h" #include <stdlib.h> #include <iostream> #include "h264.h" #define SSRC 100#define DEST_IP_STR "127.0.0.1" #define DEST_PORT 9000 #define BASE_PORT 2222using namespace jrtplib;int main(int argc, char** argv) {RTPSession session;RTPSessionParams sessionparams;sessionparams.SetOwnTimestampUnit(1.0/90000.0);RTPUDPv4TransmissionParams transparams;transparams.SetPortbase(8000);int status = session.Create(sessionparams,&transparams);if (status < 0){std::cerr << RTPGetErrorString(status) << std::endl;exit(-1);}uint8_t localip[]={127,0,0,1};RTPIPv4Address addr(localip,9000);status = session.AddDestination(addr);if (status < 0){std::cerr << RTPGetErrorString(status) << std::endl;exit(-1);}session.SetDefaultPayloadType(96);session.SetDefaultMark(false);session.SetDefaultTimestampIncrement(90000.0 /25.0);RTPTime delay(0.040);RTPTime starttime = RTPTime::CurrentTime();NALU_HEADER *nalu_hdr;FU_INDICATOR *fu_ind;FU_HEADER *fu_hdr;char sendbuf[1500];char* nalu_payload;unsigned int timestamp_increse=0,ts_current=0;#define ddd//OpenBitstreamFile("agnt.264");//打開264文件,并將文件指針賦給bits,在此修改文件名實現打開別的264文件。OpenBitstreamFile("1.264");//打開264文件,并將文件指針賦給bits,在此修改文件名實現打開別的264文件。//OpenBitstreamFile("test.264");//打開264文件,并將文件指針賦給bits,在此修改文件名實現打開別的264文件。//OpenBitstreamFile("slamtv60.264");//打開264文件,并將文件指針賦給bits,在此修改文件名實現打開別的264文件。//OpenBitstreamFile("avc.h264");//打開264文件,并將文件指針賦給bits,在此修改文件名實現打開別的264文件。NALU_t *n;n = AllocNALU(8000000);//為結構體nalu_t及其成員buf分配空間。返回值為指向nalu_t存儲空間的指針bool start=false;while(!feof(bits)){int size=GetAnnexbNALU(n);//每執行一次,文件的指針指向本次找到的NALU的末尾,下一個位置即為下個NALU的起始碼0x000001if(size<4){printf("get nul error!\n");continue;}dump(n);//輸出NALU長度和TYPEif(!start){if(n->nal_unit_type==5||n->nal_unit_type==6||n->nal_unit_type==7||n->nal_unit_type==7){printf("begin\n");start=true;}}//將編碼數據寫入文件t//fwrite(pNals[i].p_payload, 1, pNals[i].i_payload, pFile);//發送編碼文件 #if 1// 當一個NALU小于MAX_RTP_PKT_LENGTH字節的時候,采用一個單RTP包發送if(n->len<=MAX_RTP_PKT_LENGTH){//printf("ddd0\n");//session.SetDefaultMark(false);//設置NALU HEADER,并將這個HEADER填入sendbuf[12]nalu_hdr =(NALU_HEADER*)&sendbuf[0]; //將sendbuf[12]的地址賦給nalu_hdr,之后對nalu_hdr的寫入就將寫入sendbuf中;nalu_hdr->F=n->forbidden_bit;nalu_hdr->NRI=n->nal_reference_idc>>5;//有效數據在n->nal_reference_idc的第6,7位,需要右移5位才能將其值賦給nalu_hdr->NRI。nalu_hdr->TYPE=n->nal_unit_type;nalu_payload=&sendbuf[1];//同理將sendbuf[13]賦給nalu_payloadmemcpy(nalu_payload,n->buf+1,n->len-1);//去掉nalu頭的nalu剩余內容寫入sendbuf[13]開始的字符串。ts_current=ts_current+timestamp_increse;//status = session.SendPacket((void *)sendbuf,n->len);if(n->nal_unit_type==1 || n->nal_unit_type==5){status = session.SendPacket((void *)sendbuf,n->len,96,true,3600);}else{status = session.SendPacket((void *)sendbuf,n->len,96,true,0);\//如果是6,7類型的包,不應該延時;之前有停頓,原因這在這continue;}//發送RTP格式數據包并指定負載類型為96if (status < 0){std::cerr << RTPGetErrorString(status) << std::endl;exit(-1);}}else if(n->len>MAX_RTP_PKT_LENGTH){//得到該nalu需要用多少長度為MAX_RTP_PKT_LENGTH字節的RTP包來發送int k=0,l=0;k=n->len/MAX_RTP_PKT_LENGTH;//需要k個MAX_RTP_PKT_LENGTH字節的RTP包l=n->len%MAX_RTP_PKT_LENGTH;//最后一個RTP包的需要裝載的字節數int t=0;//用于指示當前發送的是第幾個分片RTP包ts_current=ts_current+timestamp_increse;while(t<=k){if(!t)//發送一個需要分片的NALU的第一個分片,置FU HEADER的S位{//printf("dddd1");memset(sendbuf,0,1500);//session.SetDefaultMark(false);//設置FU INDICATOR,并將這個HEADER填入sendbuf[12]fu_ind =(FU_INDICATOR*)&sendbuf[0]; //將sendbuf[12]的地址賦給fu_ind,之后對fu_ind的寫入就將寫入sendbuf中;fu_ind->F=n->forbidden_bit;fu_ind->NRI=n->nal_reference_idc>>5;fu_ind->TYPE=28;//設置FU HEADER,并將這個HEADER填入sendbuf[13]fu_hdr =(FU_HEADER*)&sendbuf[1];fu_hdr->E=0;fu_hdr->R=0;fu_hdr->S=1;fu_hdr->TYPE=n->nal_unit_type;nalu_payload=&sendbuf[2];//同理將sendbuf[14]賦給nalu_payloadmemcpy(nalu_payload,n->buf+1,MAX_RTP_PKT_LENGTH);//去掉NALU頭//status = session.SendPacket((void *)sendbuf,MAX_RTP_PKT_LENGTH+2);status = session.SendPacket((void *)sendbuf,MAX_RTP_PKT_LENGTH+2,96,false,0);if (status < 0){std::cerr << RTPGetErrorString(status) << std::endl;exit(-1);}t++;}//發送一個需要分片的NALU的非第一個分片,清零FU HEADER的S位,如果該分片是該NALU的最后一個分片,置FU HEADER的E位else if(k==t)//發送的是最后一個分片,注意最后一個分片的長度可能超過MAX_RTP_PKT_LENGTH字節(當l>1386時)。{//printf("dddd3\n");memset(sendbuf,0,1500);//session.SetDefaultMark(true);//設置FU INDICATOR,并將這個HEADER填入sendbuf[12]fu_ind =(FU_INDICATOR*)&sendbuf[0]; //將sendbuf[12]的地址賦給fu_ind,之后對fu_ind的寫入就將寫入sendbuf中;fu_ind->F=n->forbidden_bit;fu_ind->NRI=n->nal_reference_idc>>5;fu_ind->TYPE=28;//設置FU HEADER,并將這個HEADER填入sendbuf[13]fu_hdr =(FU_HEADER*)&sendbuf[1];fu_hdr->R=0;fu_hdr->S=0;fu_hdr->TYPE=n->nal_unit_type;fu_hdr->E=1;nalu_payload=&sendbuf[2];//同理將sendbuf[14]賦給nalu_payloadmemcpy(nalu_payload,n->buf+t*MAX_RTP_PKT_LENGTH+1,l-1);//將nalu最后剩余的l-1(去掉了一個字節的NALU頭)字節內容寫入sendbuf[14]開始的字符串。//status = session.SendPacket((void *)sendbuf,l+1);status = session.SendPacket((void *)sendbuf,l+1,96,true,3600);if (status < 0){std::cerr << RTPGetErrorString(status) << std::endl;exit(-1);}t++;// Sleep(100);}else if(t<k&&0!=t){//printf("dddd2");memset(sendbuf,0,1500);//session.SetDefaultMark(false);//設置FU INDICATOR,并將這個HEADER填入sendbuf[12]fu_ind =(FU_INDICATOR*)&sendbuf[0]; //將sendbuf[12]的地址賦給fu_ind,之后對fu_ind的寫入就將寫入sendbuf中;fu_ind->F=n->forbidden_bit;fu_ind->NRI=n->nal_reference_idc>>5;fu_ind->TYPE=28;//設置FU HEADER,并將這個HEADER填入sendbuf[13]fu_hdr =(FU_HEADER*)&sendbuf[1];//fu_hdr->E=0;fu_hdr->R=0;fu_hdr->S=0;fu_hdr->E=0;fu_hdr->TYPE=n->nal_unit_type;nalu_payload=&sendbuf[2];//同理將sendbuf[14]的地址賦給nalu_payloadmemcpy(nalu_payload,n->buf+t*MAX_RTP_PKT_LENGTH+1,MAX_RTP_PKT_LENGTH);//去掉起始前綴的nalu剩余內容寫入sendbuf[14]開始的字符串。//status = session.SendPacket((void *)sendbuf,MAX_RTP_PKT_LENGTH+2);status = session.SendPacket((void *)sendbuf,MAX_RTP_PKT_LENGTH+2,96,false,0);if (status < 0){std::cerr << RTPGetErrorString(status) << std::endl;exit(-1);}t++;}}}#endif#if 0session.BeginDataAccess();if (session.GotoFirstSource()){do{RTPPacket *packet;while ((packet = session.GetNextPacket()) != 0){std::cout << "Got packet with "<< "extended sequence number "<< packet->GetExtendedSequenceNumber()<< " from SSRC " << packet->GetSSRC()<< std::endl;session.DeletePacket(packet);}} while (session.GotoNextSource());}session.EndDataAccess(); #endifRTPTime::Wait(delay);RTPTime t = RTPTime::CurrentTime();t -= starttime;if (t > RTPTime(60.0))break;}printf("over\n");delay = RTPTime(10.0);session.BYEDestroy(delay,"Time's up",9);//一些清理工作… }
這些代碼等所用到提到的全部東西都上傳到了我的github中,大家可以到這找到我的完整的工程。
https://github.com/xyyangkun/test_jrtp.git
總結
以上是生活随笔為你收集整理的使用jrtplib打包发送h264数据,关使用vlc|mplayer播放的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 怎么用vnc访问自己内网电脑,同时又是同
- 下一篇: 编译libav(ffmpeg)库