日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

您的位置:首頁技術(shù)文章
文章詳情頁

java、android可用的rtp封包解包h264案例

瀏覽:24日期:2022-08-23 09:19:57

做直播,音視頻通訊。經(jīng)常需要通過rtp協(xié)議封裝音視頻數(shù)據(jù)來發(fā)送。網(wǎng)上找到的基本都是c或c++版本的,沒有JAVA版本的。就算千辛萬苦找到一篇java版本的,要么不能用,要么就是一些片段,要么有封包沒解包。

很是蛋疼,本人也是這樣,剛開始不太熟悉rtp協(xié)議,不太明白怎么封包組包,痛苦了幾天,終于搞出來了,分享給有需要的朋友,希望對你們有所幫助。

直接看代碼吧。不多說了。

首先看看關(guān)鍵類:

package com.imsdk.socket.udp.codec; import android.os.SystemClock;import android.util.Log; import java.io.ByteArrayInputStream;import java.io.IOException;import java.io.InputStream;import java.math.BigDecimal;import java.util.Random;import java.util.concurrent.Semaphore; public class RtspPacketEncode { private static final String TAG = 'RtspPacketEncode'; //------------視頻轉(zhuǎn)換數(shù)據(jù)監(jiān)聽----------- public interface H264ToRtpLinsener { void h264ToRtpResponse(byte[] out, int len); } private H264ToRtpLinsener h264ToRtpLinsener; //執(zhí)行回調(diào) private void exceuteH264ToRtpLinsener(byte[] out, int len) { if (this.h264ToRtpLinsener != null) { h264ToRtpLinsener.h264ToRtpResponse(out, len); } } // -------視頻-------- private int framerate = 10; private byte[] sendbuf = new byte[1500]; private int packageSize = 1400; private int seq_num = 0; private int timestamp_increse = (int) (90000.0 / framerate);//framerate是幀率 private int ts_current = 0; private int bytes = 0; // -------視頻END-------- public RtspPacketEncode(H264ToRtpLinsener h264ToRtpLinsener) { this.h264ToRtpLinsener = h264ToRtpLinsener; } /** * 一幀一幀的RTP封包 * * @param r * @return */ public void h264ToRtp(byte[] r, int h264len) throws Exception { CalculateUtil.memset(sendbuf, 0, 1500); sendbuf[1] = (byte) (sendbuf[1] | 96); // 負(fù)載類型號96,其值為:01100000 sendbuf[0] = (byte) (sendbuf[0] | 0x80); // 版本號,此版本固定為2 sendbuf[1] = (byte) (sendbuf[1] & 254); //標(biāo)志位,由具體協(xié)議規(guī)定其值,其值為:01100000 sendbuf[11] = 10;//隨機(jī)指定10,并在本RTP回話中全局唯一,java默認(rèn)采用網(wǎng)絡(luò)字節(jié)序號 不用轉(zhuǎn)換(同源標(biāo)識符的最后一個字節(jié)) if (h264len <= packageSize) { sendbuf[1] = (byte) (sendbuf[1] | 0x80); // 設(shè)置rtp M位為1,其值為:11100000,分包的最后一片,M位(第一位)為0,后7位是十進(jìn)制的96,表示負(fù)載類型 sendbuf[3] = (byte) seq_num++; System.arraycopy(CalculateUtil.intToByte(seq_num++), 0, sendbuf, 2, 2);//send[2]和send[3]為序列號,共兩位 {// java默認(rèn)的網(wǎng)絡(luò)字節(jié)序是大端字節(jié)序(無論在什么平臺上),因為windows為小字節(jié)序,所以必須倒序/**參考: * http://blog.csdn.net/u011068702/article/details/51857557 * http://cpjsjxy.iteye.com/blog/1591261 */byte temp = 0;temp = sendbuf[3];sendbuf[3] = sendbuf[2];sendbuf[2] = temp; } // FU-A HEADER, 并將這個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頭已經(jīng)寫到sendbuf[12]中,接下來則存放的是NAL的第一個字節(jié)之后的數(shù)據(jù)。所以從r的第二個字節(jié)開始復(fù)制 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個字節(jié),存儲后也需要倒序 {byte temp = 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個字節(jié)) //client.send(new DatagramPacket(sendbuf, bytes, addr, port/*9200*/)); //send(sendbuf,bytes); exceuteH264ToRtpLinsener(sendbuf, bytes); } else if (h264len > packageSize) { int k = 0, l = 0; k = h264len / packageSize; l = h264len % packageSize; int t = 0; ts_current = ts_current + timestamp_increse; System.arraycopy(CalculateUtil.intToByte(ts_current), 0, sendbuf, 4, 4);//時間戳,并且倒序 {byte temp = 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);//序列號,并且倒序{ byte temp = 0; temp = sendbuf[3]; sendbuf[3] = sendbuf[2]; sendbuf[2] = temp;}if (t == 0) {//分包的第一片 sendbuf[1] = (byte) (sendbuf[1] & 0x7F);//其值為:01100000,不是最后一片,M位(第一位)設(shè)為0 //FU indicator,一個字節(jié),緊接在RTP header之后,包括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 //FU header,一個字節(jié),S,E,R,TYPE sendbuf[13] = (byte) (sendbuf[13] & 0xBF);//E=0,表示是否為最后一個包,是則為1 sendbuf[13] = (byte) (sendbuf[13] & 0xDF);//R=0,保留位,必須設(shè)置為0 sendbuf[13] = (byte) (sendbuf[13] | 0x80);//S=1,表示是否為第一個包,是則為1 sendbuf[13] = (byte) (sendbuf[13] | ((byte) (r[0] & 0x1f)));//TYPE,即NALU頭對應(yīng)的TYPE //將除去NALU頭剩下的NALU數(shù)據(jù)寫入sendbuf的第14個字節(jié)之后。前14個字節(jié)包括:12字節(jié)的RTP Header,F(xiàn)U indicator,F(xiàn)U header System.arraycopy(r, 1, sendbuf, 14, packageSize); //client.send(new DatagramPacket(sendbuf, packageSize + 14, addr, port/*9200*/)); exceuteH264ToRtpLinsener(sendbuf, packageSize + 14); t++;} else if (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,保留位必須設(shè)為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頭對應(yīng)的type if (0 != l) {//如果不能整除,則有剩下的包,執(zhí)行此代碼。如果包大小恰好是1400的倍數(shù),不執(zhí)行此代碼。 System.arraycopy(r, t * packageSize + 1, sendbuf, 14, l - 1);//l-1,不包含NALU頭 bytes = l - 1 + 14; //bytes=l-1+14; //client.send(new DatagramPacket(sendbuf, bytes, addr, port/*9200*/)); //send(sendbuf,bytes); exceuteH264ToRtpLinsener(sendbuf, bytes); }//pl t++;} else if (t < k && 0 != t) {//既不是第一片,又不是最后一片的包 sendbuf[1] = (byte) (sendbuf[1] & 0x7F); //M=0,其值為:01100000,不是最后一片,M位(第一位)設(shè)為0. 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,保留位必須設(shè)為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頭對應(yīng)的type System.arraycopy(r, t * packageSize + 1, sendbuf, 14, packageSize);//不包含NALU頭 //client.send(new DatagramPacket(sendbuf, packageSize + 14, addr, port/*9200*/)); //send(sendbuf,1414); exceuteH264ToRtpLinsener(sendbuf, packageSize + 14); t++;} } } }}

計算類:

package com.imsdk.socket.udp.codec; /** * 計算類 * * @author kokJuis */public class CalculateUtil { /** * 注釋:int到字節(jié)數(shù)組的轉(zhuǎn)換! * * @param number * @return */ public static byte[] intToByte(int number) { int temp = number; byte[] b = new byte[4]; for (int i = 0; i < b.length; i++) { b[i] = new Integer(temp & 0xff).byteValue();// 將最低位保存在最低位 temp = temp >> 8; // 向右移8位 } return b; } public static int byteToInt(byte b) { //Java 總是把 byte 當(dāng)做有符處理;我們可以通過將其和 0xFF 進(jìn)行二進(jìn)制與得到它的無符值 return b & 0xFF; } //byte 數(shù)組與 int 的相互轉(zhuǎn)換 public static int byteArrayToInt(byte[] b) { return b[3] & 0xFF |(b[2] & 0xFF) << 8 |(b[1] & 0xFF) << 16 |(b[0] & 0xFF) << 24; } public static byte[] intToByteArray(int a) { return new byte[] {(byte) ((a >> 24) & 0xFF),(byte) ((a >> 16) & 0xFF),(byte) ((a >> 8) & 0xFF),(byte) (a & 0xFF) }; } // 清空buf的值 public static void memset(byte[] buf, int value, int size) { for (int i = 0; i < size; i++) { buf[i] = (byte) value; } } public static void dump(NALU_t n) { System.out.println('len: ' + n.len + ' nal_unit_type:' + n.nal_unit_type); } // 判斷是否為0x000001,如果是返回1 public static int FindStartCode2(byte[] Buf, int off) { if (Buf[0 + off] != 0 || Buf[1 + off] != 0 || Buf[2 + off] != 1) return 0; else return 1; } // 判斷是否為0x00000001,如果是返回1 public static int FindStartCode3(byte[] Buf, int off) { if (Buf[0 + off] != 0 || Buf[1 + off] != 0 || Buf[2 + off] != 0 || Buf[3 + off] != 1) return 0; else return 1; } }

使用的話,實(shí)現(xiàn)監(jiān)聽就可以了:

@Override public void h264ToRtpResponse(byte[] out, int len) { //h264轉(zhuǎn)rtp監(jiān)聽 if (out != null) { Log.v(TAG, '---發(fā)送數(shù)據(jù)---' + len); netSendTask.pushBuf(out, len); } } rtspPacketEncode.h264ToRtp(h264, ret);

組包類:

package com.imsdk.socket.udp.codec; public class RtspPacketDecode { private byte[] h264Buffer; private int h264Len = 0; private int h264Pos = 0; private static final byte[] start_code = {0, 0, 0, 1}; // h264 start code //傳入視頻的分辨率 public RtspPacketDecode(int width, int height) { h264Buffer = new byte[getYuvBuffer(width, height)]; } /** * RTP解包H264 * * @param rtpData * @return */ public byte[] rtp2h264(byte[] rtpData, int rtpLen) { int fu_header_len = 12; // FU-Header長度為12字節(jié) int extension = (rtpData[0] & (1 << 4)); // X: 擴(kuò)展為是否為1 if (extension > 0) { // 計算擴(kuò)展頭的長度 int extLen = (rtpData[12] << 24) + (rtpData[13] << 16) + (rtpData[14] << 8) + rtpData[15]; fu_header_len += (extLen + 1) * 4; } // 解析FU-indicator byte indicatorType = (byte) (CalculateUtil.byteToInt(rtpData[fu_header_len]) & 0x1f); // 取出low 5 bit 則為FU-indicator type byte nri = (byte) ((CalculateUtil.byteToInt(rtpData[fu_header_len]) >> 5) & 0x03); // 取出h2bit and h3bit byte f = (byte) (CalculateUtil.byteToInt(rtpData[fu_header_len]) >> 7);// 取出h1bit byte h264_nal_header; byte fu_header; if (indicatorType == 28) { // FU-A fu_header = rtpData[fu_header_len + 1]; byte s = (byte) (rtpData[fu_header_len + 1] & 0x80); byte e = (byte) (rtpData[fu_header_len + 1] & 0x40); if (e == 64) { // end of fu-a//ZOLogUtil.d('RtpParser', 'end of fu-a.....;;;');byte[] temp = new byte[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 = new byte[h264Len]; System.arraycopy(h264Buffer, 0, h264Data, 0, h264Len); h264Len = 0; return h264Data; }} } else if (s == -128) { // start of fu-ah264Pos = 0; // 指針歸0writeData2Buffer(start_code, 4); // 寫入H264起始碼h264_nal_header = (byte) ((fu_header & 0x1f) | (nri << 5) | (f << 7));writeData2Buffer(new byte[]{h264_nal_header}, 1);byte[] temp = new byte[rtpLen - (fu_header_len + 2)];System.arraycopy(rtpData, fu_header_len + 2, temp, 0, temp.length); // 負(fù)載數(shù)據(jù)writeData2Buffer(temp, temp.length); } else {byte[] temp = new byte[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 = new byte[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 = new byte[h264Len]; System.arraycopy(h264Buffer, 0, h264Data, 0, h264Len); h264Len = 0; return h264Data;} } } return null; } private void writeData2Buffer(byte[] data, int len) { if (h264Pos >= 0) { System.arraycopy(data, 0, h264Buffer, h264Pos, len); h264Pos += len; h264Len += len; } } //計算h264大小 public int getYuvBuffer(int width, int height) { // stride = ALIGN(width, 16) int stride = (int) Math.ceil(width / 16.0) * 16; // y_size = stride * height int y_size = stride * height; // c_stride = ALIGN(stride/2, 16) int c_stride = (int) Math.ceil(width / 32.0) * 16; // c_size = c_stride * height/2 int c_size = c_stride * height / 2; // size = y_size + c_size * 2 return y_size + c_size * 2; } }

使用:

byte[] tmp = rtspPacketDecode.rtp2h264(out,len);

以上這篇java、android可用的rtp封包解包h264案例就是小編分享給大家的全部內(nèi)容了,希望能給大家一個參考,也希望大家多多支持好吧啦網(wǎng)。

標(biāo)簽: Java
相關(guān)文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
日韩福利在线观看| 欧美激情福利| 日本蜜桃在线观看视频| 美女久久久久久| 欧美激情一区| 久久精品午夜| av高清不卡| 亚洲婷婷免费| 亚洲在线网站| 亚洲精品中文字幕99999| 亚洲精品在线国产| 97精品资源在线观看| 国产精品亚洲人成在99www| 日韩av字幕| 91成人在线精品视频| 国产三级精品三级在线观看国产| 国产欧美91| 精品视频99| 天堂网av成人| 91久久久精品国产| 亚久久调教视频| 国产精品亚洲一区二区在线观看 | 日韩激情啪啪| 国产精品丝袜在线播放| 91亚洲一区| 伊人久久亚洲影院| 欧美午夜不卡影院在线观看完整版免费| 国产精品美女久久久| 日韩欧美四区| 麻豆久久久久久| 亚洲成人av观看| 欧美中文日韩| 欧美国产免费| 国产综合亚洲精品一区二| 免费在线观看视频一区| 久久国产日韩欧美精品| 国产一区2区| 视频一区二区三区中文字幕| 欧美亚洲三区| 日韩国产欧美一区二区| 美女网站久久| 亚州精品视频| 日韩免费视频| 蜜臀va亚洲va欧美va天堂| 国产精品.xx视频.xxtv| 日韩三区在线| 亚洲精品进入| 日韩1区在线| 六月婷婷一区| 国产一区二区三区精品在线观看| 国产免费成人| 久久伊人久久| 9久re热视频在线精品| 国产精品最新| 五月综合激情| 精品中文字幕一区二区三区 | www成人在线视频| 亚洲+小说+欧美+激情+另类| av高清不卡| 欧美日韩1区2区3区| 国模 一区 二区 三区| 国产精品亚洲欧美日韩一区在线| 欧美~级网站不卡| 国产日本精品| 在线综合亚洲| 中文av在线全新| 日韩高清在线观看一区二区| 日韩和的一区二在线| 国产欧美一区二区三区国产幕精品| 日本高清不卡一区二区三区视频 | 久久不见久久见免费视频7| 韩日一区二区三区| 美女av一区| 亚洲一级淫片| 日韩免费av| 久久99免费视频| 日本在线成人| 伊人久久大香线蕉av超碰演员| 丰满少妇一区| 国产精品流白浆在线观看| 亚洲专区在线| 欧美不卡高清一区二区三区| 国产精品最新| 亚洲aⅴ网站| 国产精品美女久久久| 久久久一本精品| 久久久免费人体| 日本欧美一区二区| 先锋亚洲精品| 九一精品国产| 91精品韩国| 精品国产鲁一鲁****| 日本不卡不码高清免费观看 | 在线一区二区三区视频| 欧美日韩在线二区| 国产高潮在线| 精品久久久久中文字幕小说| 日韩国产在线一| 男人的天堂久久精品| 国产在线日韩| 成人va天堂| 伊人久久av| 成人日韩av| 久久婷婷国产| 911精品国产| 日日夜夜免费精品| 亚洲精品伊人| 美女久久一区| 香蕉久久久久久久av网站| 欧美日韩国产一区二区三区不卡| 日本а中文在线天堂| 91视频一区| 91一区二区| 中文字幕在线官网| 国产美女高潮在线观看| 精品成人18| 日韩成人免费| 日韩免费在线| 久久国产日韩| 久久久夜夜夜| 国内精品福利| 国产亚洲精品v| 蜜桃久久av| 亚洲a成人v| 欧美亚洲人成在线| 国产日韩欧美一区在线| 欧美日韩一区自拍| 国产欧美一级| 久久超级碰碰| 国产精品二区不卡| 成人免费网站www网站高清| 99精品视频在线观看免费播放| 亚洲一本视频| 亚洲综合欧美| 亚洲精品在线国产| 国产日产一区| 四季av一区二区凹凸精品| 国产网站在线| av一区二区高清| 男人的天堂久久精品| 日韩精品欧美大片| 国产乱码精品| zzzwww在线看片免费| 久久精品不卡| 视频在线在亚洲| 欧美一区自拍| 高清一区二区| 亚洲天堂成人| 日韩在线视频一区二区三区| 91亚洲精品视频在线观看 | 免费日韩成人| 毛片在线网站| 欧美 日韩 国产精品免费观看| 国产亚洲欧洲| 欧美一区成人| 日韩精品1区| 日韩午夜电影| 国产麻豆一区| 日韩欧美精品一区| 老鸭窝毛片一区二区三区| 日韩不卡在线观看日韩不卡视频| 精品免费视频| 免费精品国产| 日韩不卡一区二区三区 | 欧美日韩视频| 日本va欧美va欧美va精品| 国产成人精品福利| 国产视频一区在线观看一区免费| 国产欧美三级| 免费久久精品| 久久激情五月婷婷| 秋霞国产精品| 97久久超碰| 精品亚洲美女网站| 日韩精品一区二区三区中文 | 国产精品日韩精品在线播放| a天堂资源在线| 亚洲欧美日韩精品一区二区 | 欧美日韩在线观看首页| 午夜亚洲精品| 久久三级中文| 丝袜美腿高跟呻吟高潮一区| 国产精品久久乐| 91成人精品| 麻豆精品蜜桃视频网站| 婷婷综合激情| 欧美韩一区二区| 久久视频精品| 国产欧美综合一区二区三区| 久久久精品午夜少妇| 日本午夜精品久久久| 999精品一区| 国产麻豆精品久久| 一区二区亚洲精品| 麻豆91精品视频| 视频一区欧美精品| 天堂中文在线播放| 国产乱论精品| 亚洲天堂日韩在线| 国内亚洲精品|