java、android可用的rtp封包解包h264案例
做直播,音视频通讯。经常需要通过rtp协议封装音视频数据来发送。网上找到的基本都是c或c++版本的,没有JAVA版本的。就算千辛万苦找到一篇java版本的,要么不能用,要么就是一些片段,要么有封包没解包。
很是蛋疼,本人也是这样,刚开始不太熟悉rtp协议,不太明白怎么封包组包,痛苦了几天,终于搞出来了,分享给有需要的朋友,希望对你们有所帮助。
直接看代码吧。不多说了。
首先看看关键类:
packagecom.imsdk.socket.udp.codec;
importandroid.os.SystemClock;
importandroid.util.Log;
importjava.io.ByteArrayInputStream;
importjava.io.IOException;
importjava.io.InputStream;
importjava.math.BigDecimal;
importjava.util.Random;
importjava.util.concurrent.Semaphore;
publicclassRtspPacketEncode{
privatestaticfinalStringTAG="RtspPacketEncode";
//------------视频转换数据监听-----------
publicinterfaceH264ToRtpLinsener{
voidh264ToRtpResponse(byte[]out,intlen);
}
privateH264ToRtpLinsenerh264ToRtpLinsener;
//执行回调
privatevoidexceuteH264ToRtpLinsener(byte[]out,intlen){
if(this.h264ToRtpLinsener!=null){
h264ToRtpLinsener.h264ToRtpResponse(out,len);
}
}
//-------视频--------
privateintframerate=10;
privatebyte[]sendbuf=newbyte[1500];
privateintpackageSize=1400;
privateintseq_num=0;
privateinttimestamp_increse=(int)(90000.0/framerate);//framerate是帧率
privateintts_current=0;
privateintbytes=0;
//-------视频END--------
publicRtspPacketEncode(H264ToRtpLinsenerh264ToRtpLinsener){
this.h264ToRtpLinsener=h264ToRtpLinsener;
}
/**
*一帧一帧的RTP封包
*
*@paramr
*@return
*/
publicvoidh264ToRtp(byte[]r,inth264len)throwsException{
CalculateUtil.memset(sendbuf,0,1500);
sendbuf[1]=(byte)(sendbuf[1]|96);//负载类型号96,其值为:01100000
sendbuf[0]=(byte)(sendbuf[0]|0x80);//版本号,此版本固定为2
sendbuf[1]=(byte)(sendbuf[1]&254);//标志位,由具体协议规定其值,其值为:01100000
sendbuf[11]=10;//随机指定10,并在本RTP回话中全局唯一,java默认采用网络字节序号不用转换(同源标识符的最后一个字节)
if(h264len<=packageSize){
sendbuf[1]=(byte)(sendbuf[1]|0x80);//设置rtpM位为1,其值为:11100000,分包的最后一片,M位(第一位)为0,后7位是十进制的96,表示负载类型
sendbuf[3]=(byte)seq_num++;
System.arraycopy(CalculateUtil.intToByte(seq_num++),0,sendbuf,2,2);//send[2]和send[3]为序列号,共两位
{
//java默认的网络字节序是大端字节序(无论在什么平台上),因为windows为小字节序,所以必须倒序
/**参考:
*http://blog.csdn.net/u011068702/article/details/51857557
*http://cpjsjxy.iteye.com/blog/1591261
*/
bytetemp=0;
temp=sendbuf[3];
sendbuf[3]=sendbuf[2];
sendbuf[2]=temp;
}
//FU-AHEADER,并将这个HEADER填入sendbuf[12]
sendbuf[12]=(byte)(sendbuf[12]|((byte)(r[0]&0x80))<<7);
sendbuf[12]=(byte)(sendbuf[12]|((byte)((r[0]&0x60)>>5))<<5);
sendbuf[12]=(byte)(sendbuf[12]|((byte)(r[0]&0x1f)));
//同理将sendbuf[13]赋给nalu_payload
//NALU头已经写到sendbuf[12]中,接下来则存放的是NAL的第一个字节之后的数据。所以从r的第二个字节开始复制
System.arraycopy(r,1,sendbuf,13,h264len-1);
ts_current=ts_current+timestamp_increse;
System.arraycopy(CalculateUtil.intToByte(ts_current),0,sendbuf,4,4);//序列号接下来是时间戳,4个字节,存储后也需要倒序
{
bytetemp=0;
temp=sendbuf[4];
sendbuf[4]=sendbuf[7];
sendbuf[7]=temp;
temp=sendbuf[5];
sendbuf[5]=sendbuf[6];
sendbuf[6]=temp;
}
bytes=h264len+12;//获sendbuf的长度,为nalu的长度(包含nalu头但取出起始前缀,加上rtp_header固定长度12个字节)
//client.send(newDatagramPacket(sendbuf,bytes,addr,port/*9200*/));
//send(sendbuf,bytes);
exceuteH264ToRtpLinsener(sendbuf,bytes);
}elseif(h264len>packageSize){
intk=0,l=0;
k=h264len/packageSize;
l=h264len%packageSize;
intt=0;
ts_current=ts_current+timestamp_increse;
System.arraycopy(CalculateUtil.intToByte(ts_current),0,sendbuf,4,4);//时间戳,并且倒序
{
bytetemp=0;
temp=sendbuf[4];
sendbuf[4]=sendbuf[7];
sendbuf[7]=temp;
temp=sendbuf[5];
sendbuf[5]=sendbuf[6];
sendbuf[6]=temp;
}
while(t<=k){
System.arraycopy(CalculateUtil.intToByte(seq_num++),0,sendbuf,2,2);//序列号,并且倒序
{
bytetemp=0;
temp=sendbuf[3];
sendbuf[3]=sendbuf[2];
sendbuf[2]=temp;
}
if(t==0){//分包的第一片
sendbuf[1]=(byte)(sendbuf[1]&0x7F);//其值为:01100000,不是最后一片,M位(第一位)设为0
//FUindicator,一个字节,紧接在RTPheader之后,包括F,NRI,header
sendbuf[12]=(byte)(sendbuf[12]|((byte)(r[0]&0x80))<<7);//禁止位,为0
sendbuf[12]=(byte)(sendbuf[12]|((byte)((r[0]&0x60)>>5))<<5);//NRI,表示包的重要性
sendbuf[12]=(byte)(sendbuf[12]|(byte)(28));//TYPE,表示此FU-A包为什么类型,一般此处为28
//FUheader,一个字节,S,E,R,TYPE
sendbuf[13]=(byte)(sendbuf[13]&0xBF);//E=0,表示是否为最后一个包,是则为1
sendbuf[13]=(byte)(sendbuf[13]&0xDF);//R=0,保留位,必须设置为0
sendbuf[13]=(byte)(sendbuf[13]|0x80);//S=1,表示是否为第一个包,是则为1
sendbuf[13]=(byte)(sendbuf[13]|((byte)(r[0]&0x1f)));//TYPE,即NALU头对应的TYPE
//将除去NALU头剩下的NALU数据写入sendbuf的第14个字节之后。前14个字节包括:12字节的RTPHeader,FUindicator,FUheader
System.arraycopy(r,1,sendbuf,14,packageSize);
//client.send(newDatagramPacket(sendbuf,packageSize+14,addr,port/*9200*/));
exceuteH264ToRtpLinsener(sendbuf,packageSize+14);
t++;
}elseif(t==k){//分片的最后一片
sendbuf[1]=(byte)(sendbuf[1]|0x80);
sendbuf[12]=(byte)(sendbuf[12]|((byte)(r[0]&0x80))<<7);
sendbuf[12]=(byte)(sendbuf[12]|((byte)((r[0]&0x60)>>5))<<5);
sendbuf[12]=(byte)(sendbuf[12]|(byte)(28));
sendbuf[13]=(byte)(sendbuf[13]&0xDF);//R=0,保留位必须设为0
sendbuf[13]=(byte)(sendbuf[13]&0x7F);//S=0,不是第一个包
sendbuf[13]=(byte)(sendbuf[13]|0x40);//E=1,是最后一个包
sendbuf[13]=(byte)(sendbuf[13]|((byte)(r[0]&0x1f)));//NALU头对应的type
if(0!=l){//如果不能整除,则有剩下的包,执行此代码。如果包大小恰好是1400的倍数,不执行此代码。
System.arraycopy(r,t*packageSize+1,sendbuf,14,l-1);//l-1,不包含NALU头
bytes=l-1+14;//bytes=l-1+14;
//client.send(newDatagramPacket(sendbuf,bytes,addr,port/*9200*/));
//send(sendbuf,bytes);
exceuteH264ToRtpLinsener(sendbuf,bytes);
}//pl
t++;
}elseif(t>5))<<5);
sendbuf[12]=(byte)(sendbuf[12]|(byte)(28));
sendbuf[13]=(byte)(sendbuf[13]&0xDF);//R=0,保留位必须设为0
sendbuf[13]=(byte)(sendbuf[13]&0x7F);//S=0,不是第一个包
sendbuf[13]=(byte)(sendbuf[13]&0xBF);//E=0,不是最后一个包
sendbuf[13]=(byte)(sendbuf[13]|((byte)(r[0]&0x1f)));//NALU头对应的type
System.arraycopy(r,t*packageSize+1,sendbuf,14,packageSize);//不包含NALU头
//client.send(newDatagramPacket(sendbuf,packageSize+14,addr,port/*9200*/));
//send(sendbuf,1414);
exceuteH264ToRtpLinsener(sendbuf,packageSize+14);
t++;
}
}
}
}
}
计算类:
packagecom.imsdk.socket.udp.codec;
/**
*计算类
*
*@authorkokJuis
*/
publicclassCalculateUtil{
/**
*注释:int到字节数组的转换!
*
*@paramnumber
*@return
*/
publicstaticbyte[]intToByte(intnumber){
inttemp=number;
byte[]b=newbyte[4];
for(inti=0;i>8;//向右移8位
}
returnb;
}
publicstaticintbyteToInt(byteb){
//Java总是把byte当做有符处理;我们可以通过将其和0xFF进行二进制与得到它的无符值
returnb&0xFF;
}
//byte数组与int的相互转换
publicstaticintbyteArrayToInt(byte[]b){
returnb[3]&0xFF|
(b[2]&0xFF)<<8|
(b[1]&0xFF)<<16|
(b[0]&0xFF)<<24;
}
publicstaticbyte[]intToByteArray(inta){
returnnewbyte[]{
(byte)((a>>24)&0xFF),
(byte)((a>>16)&0xFF),
(byte)((a>>8)&0xFF),
(byte)(a&0xFF)
};
}
//清空buf的值
publicstaticvoidmemset(byte[]buf,intvalue,intsize){
for(inti=0;i
使用的话,实现监听就可以了:
@Override
publicvoidh264ToRtpResponse(byte[]out,intlen){
//h264转rtp监听
if(out!=null){
Log.v(TAG,"---发送数据---"+len);
netSendTask.pushBuf(out,len);
}
}
rtspPacketEncode.h264ToRtp(h264,ret);
组包类:
packagecom.imsdk.socket.udp.codec;
publicclassRtspPacketDecode{
privatebyte[]h264Buffer;
privateinth264Len=0;
privateinth264Pos=0;
privatestaticfinalbyte[]start_code={0,0,0,1};//h264startcode
//传入视频的分辨率
publicRtspPacketDecode(intwidth,intheight){
h264Buffer=newbyte[getYuvBuffer(width,height)];
}
/**
*RTP解包H264
*
*@paramrtpData
*@return
*/
publicbyte[]rtp2h264(byte[]rtpData,intrtpLen){
intfu_header_len=12;//FU-Header长度为12字节
intextension=(rtpData[0]&(1<<4));//X:扩展为是否为1
if(extension>0){
//计算扩展头的长度
intextLen=(rtpData[12]<<24)+(rtpData[13]<<16)+(rtpData[14]<<8)+rtpData[15];
fu_header_len+=(extLen+1)*4;
}
//解析FU-indicator
byteindicatorType=(byte)(CalculateUtil.byteToInt(rtpData[fu_header_len])&0x1f);//取出low5bit则为FU-indicatortype
bytenri=(byte)((CalculateUtil.byteToInt(rtpData[fu_header_len])>>5)&0x03);//取出h2bitandh3bit
bytef=(byte)(CalculateUtil.byteToInt(rtpData[fu_header_len])>>7);//取出h1bit
byteh264_nal_header;
bytefu_header;
if(indicatorType==28){//FU-A
fu_header=rtpData[fu_header_len+1];
bytes=(byte)(rtpData[fu_header_len+1]&0x80);
bytee=(byte)(rtpData[fu_header_len+1]&0x40);
if(e==64){//endoffu-a
//ZOLogUtil.d("RtpParser","endoffu-a.....;;;");
byte[]temp=newbyte[rtpLen-(fu_header_len+2)];
System.arraycopy(rtpData,fu_header_len+2,temp,0,temp.length);
writeData2Buffer(temp,temp.length);
if(h264Pos>=0){
h264Pos=-1;
if(h264Len>0){
byte[]h264Data=newbyte[h264Len];
System.arraycopy(h264Buffer,0,h264Data,0,h264Len);
h264Len=0;
returnh264Data;
}
}
}elseif(s==-128){//startoffu-a
h264Pos=0;//指针归0
writeData2Buffer(start_code,4);//写入H264起始码
h264_nal_header=(byte)((fu_header&0x1f)|(nri<<5)|(f<<7));
writeData2Buffer(newbyte[]{h264_nal_header},1);
byte[]temp=newbyte[rtpLen-(fu_header_len+2)];
System.arraycopy(rtpData,fu_header_len+2,temp,0,temp.length);//负载数据
writeData2Buffer(temp,temp.length);
}else{
byte[]temp=newbyte[rtpLen-(fu_header_len+2)];
System.arraycopy(rtpData,fu_header_len+2,temp,0,temp.length);
writeData2Buffer(temp,temp.length);
}
}else{//nalu
h264Pos=0;
writeData2Buffer(start_code,4);
byte[]temp=newbyte[rtpLen-fu_header_len];
System.arraycopy(rtpData,fu_header_len,temp,0,temp.length);
writeData2Buffer(temp,temp.length);
if(h264Pos>=0){
h264Pos=-1;
if(h264Len>0){
byte[]h264Data=newbyte[h264Len];
System.arraycopy(h264Buffer,0,h264Data,0,h264Len);
h264Len=0;
returnh264Data;
}
}
}
returnnull;
}
privatevoidwriteData2Buffer(byte[]data,intlen){
if(h264Pos>=0){
System.arraycopy(data,0,h264Buffer,h264Pos,len);
h264Pos+=len;
h264Len+=len;
}
}
//计算h264大小
publicintgetYuvBuffer(intwidth,intheight){
//stride=ALIGN(width,16)
intstride=(int)Math.ceil(width/16.0)*16;
//y_size=stride*height
inty_size=stride*height;
//c_stride=ALIGN(stride/2,16)
intc_stride=(int)Math.ceil(width/32.0)*16;
//c_size=c_stride*height/2
intc_size=c_stride*height/2;
//size=y_size+c_size*2
returny_size+c_size*2;
}
}
使用:
byte[]tmp=rtspPacketDecode.rtp2h264(out,len);
以上这篇java、android可用的rtp封包解包h264案例就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持毛票票。