直接進入正題,經過JPEG壓縮後的資料時通過RTP/RTCP協議傳輸到網路上去的,本課題使用的是Jrtplib的RTP/RTCP協議棧,首先在網上擷取Jrtplib包的源碼,解壓縮配置編譯安裝,沒有bugs就ok了
在源碼包裡面有好幾個examples,都可以借鑒。在設定Server端的時候,與TCP/IP協議不一樣,首先在初始化開啟的Session的時候,設定一個baseport連接埠,同時設定Client端的ip和port,然後再根據視頻採樣的頻率設定時間戳記,具體的設定函數都可以在examples中找到並且能很好的複用。
這裡講一下發送和接收的代碼,發送和接收都是通過線程來實現的:
發送:
ret = Send_rtppacket((unsigned char*)videoIn.ptframe[frameout],sizeof(struct frame_t)+headerframe->size);
在發送線程函數中直接發送ptframe[]指標指向的資料,資料大小為sizeof(struct frame_t)+headerframe->size,包含了該frame的資料,以及對該frame參數描述的資料結構。
int Send_rtppacket(unsigned char* framepointer,int framelength)
{
int done = 0;
int flage;
int sendbyte = 0;
int n;
do{
if(framelength > PacketMaxsize) //設定packetmaxsize: 1400 ,oversize情況下就要分割傳輸
flage = 0;
else flage =1;
if(flage = 1)
{
n = session.SendPacket(framepointer,framelength,26,1,1000); //發送函數 第四個參數決定是否是該frame最後
小於1400的資料
done = 1; //如果是 標示完成
sendbyte = framelength;
}else{
n = session.SendPacket(framepointer,PacketMaxsize,26,0,1000);
framepointer = framepointer + PacketMaxsize ; //update發送指標
framelength = framelength - PacketMaxsize ;
sendbyte = sendbyte + PacketMaxsize;
}
if(n<0)
{ return -1;}
RTPTime::Wait (delay);
}while(!done);
return sendbyte;
}
接收:
do {
// 檢索RTP資料來源
sess.BeginDataAccess();
if (sess.GotoFirstSourceWithData() ) {
do {
RTPPacket* packet;
RTPSourceData *srcdata;
// 擷取RTP資料報
packetflage =0;
recvlength =0; //初始化接收資料 以及資料接收標示
while ((packet = sess.GetNextPacket()) != NULL && packetflage==0) { //標示為零 接收同一packet的剩餘資料
//printf("Got packet !/n");
if(processpacket(*srcdata,*packet)){
packetflage = 1; //processpacket() 返回1 已經接受到所有的packet 可以調用解碼,SDL顯示
//printf("Debug...packetflage: %d/n",packetflage);
jpegsize = readjpeg(&buf,headerframe);
//printf("Debug...jpegsize: %d/n",jpegsize);
if(!jpegsize && videoOk)
close_sdlvideo();
if(jpegsize && !videoOk)
{
init_sdlvideo();
pscreen = SDL_SetVideoMode (owidth, oheight, bpp * 8,SDL_DOUBLEBUF | SDL_SWSURFACE);
p=(unsigned char*)pscreen->pixels;
}
if(jpegsize && videoOk)
{
jpeg_decode(&picture,buf,&width,&height);
resize (p,picture,owidth,oheight,width,height) ;
SDL_WM_SetCaption (titre, NULL);
SDL_Flip (pscreen);
}
if(SDL_PollEvent (&sdlevent)<0) goto error;
}
else packetflage =0; //返回0,packet還沒接受完 繼續sess.GetNextPacket()
delete packet; // 刪除RTP資料報
}
} while (sess.GotoNextSourceWithData()); //接收另一個packet
}
sess.EndDataAccess();
// 接受RTP資料
status = sess.Poll();
checkerror(status);
RTPTime::Wait(RTPTime(1,0));
} while(1);
int processpacket(const RTPSourceData &srcdat,const RTPPacket &rtppack)
{
unsigned char* payloadpointer = rtppack.GetPayloadData(); //接收該資料包資料
bool packetmarker = rtppack.HasMarker(); //察看部否是已經傳完該資料包
int flage =1;
//printf("Debug..........1/n");
if(!packetmarker) //未傳完資料包
{
memcpy(recvpointer+recvoffset,payloadpointer,rtppack.GetPayloadLength());
recvlength += rtppack.GetPayloadLength();
recvoffset += rtppack.GetPayloadLength(); //更新接收資料儲存的指標
// printf("Debug..........2/n");
flage = 0; //標示接受位 繼續執行sess.GetNextPacket()
}
else{
memcpy(recvpointer +recvoffset,payloadpointer,rtppack.GetPayloadLength());
recvlength += rtppack.GetPayloadLength();
recvoffset = 0; //傳完,初始化
// printf("Debug..........3/n");
}
return flage;
}
小結:
RTP/RTCP傳輸資料的流程:
Server端:
發送定長的資料報到Client端,發送的時候是分批以packet的形式發送到Client,就是說發送一個資料包需要幾次packet發送來完成。發送成功以後發送下一個資料包,始終調用函數:session.SendPacket();
Client端:
依次迴圈調用sess.GetNextPacket()來接收某一資料包的packet資料,packet的到來不是按順序到來的,完全接收到資料包所用的packets以後,RTP庫在根據時間戳記對接受的packet重新排序產生最終的資料包。接收資料包成功後,調用sess.GotoNextSourceWithData()開始接收下一個資料包