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案例就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持毛票票。