溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

java、android實現可用的rtp封包解包h264

發(fā)布時間:2020-10-28 18:21:56 來源:億速云 閱讀:278 作者:Leah 欄目:開發(fā)技術

這篇文章運用簡單易懂的例子給大家介紹java、android實現可用的rtp封包解包h264,內容非常詳細,感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。

首先看看關鍵類:

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"; 
 
  //------------視頻轉換數據監(jiān)聽-----------
  public interface H264ToRtpLinsener {
    void h364ToRtpResponse(byte[] out, int len);
  }
 
  private H264ToRtpLinsener h364ToRtpLinsener;
 
  //執(zhí)行回調
  private void exceuteH264ToRtpLinsener(byte[] out, int len) {
    if (this.h364ToRtpLinsener != null) {
      h364ToRtpLinsener.h364ToRtpResponse(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 h364ToRtpLinsener) {
    this.h364ToRtpLinsener = h364ToRtpLinsener;
  } 
 
  /**
   * 一幀一幀的RTP封包
   *
   * @param r
   * @return
   */
  public void h364ToRtp(byte[] r, int h364len) throws Exception {
 
    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); //標志位,由具體協議規(guī)定其值,其值為:01100000
    sendbuf[11] = 10;//隨機指定10,并在本RTP回話中全局唯一,java默認采用網絡字節(jié)序號 不用轉換(同源標識符的最后一個字節(jié))
    if (h364len <= packageSize) {
      sendbuf[1] = (byte) (sendbuf[1] | 0x80); // 設置rtp M位為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默認的網絡字節(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頭已經寫到sendbuf[12]中,接下來則存放的是NAL的第一個字節(jié)之后的數據。所以從r的第二個字節(jié)開始復制
      System.arraycopy(r, 1, sendbuf, 13, h364len - 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 = h364len + 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 (h364len > packageSize) {
      int k = 0, l = 0;
      k = h364len / packageSize;
      l = h364len % 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位(第一位)設為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,保留位,必須設置為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個字節(jié)之后。前14個字節(jié)包括:12字節(jié)的RTP Header,FU indicator,FU 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,保留位必須設為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) {//如果不能整除,則有剩下的包,執(zhí)行此代碼。如果包大小恰好是1400的倍數,不執(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位(第一位)設為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,保留位必須設為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(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é)數組的轉換!
   *
   * @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 當做有符處理;我們可以通過將其和 0xFF 進行二進制與得到它的無符值
    return b & 0xFF;
  } 
 
  //byte 數組與 int 的相互轉換
  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;
  } 
}

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

 @Override
  public void h364ToRtpResponse(byte[] out, int len) {
    //h364轉rtp監(jiān)聽
 
    if (out != null) {
      Log.v(TAG, "---發(fā)送數據---" + len);
      netSendTask.pushBuf(out, len);      
    }
 }
 
 rtspPacketEncode.h364ToRtp(h364, ret);

組包類:

package com.imsdk.socket.udp.codec; 
public class RtspPacketDecode { 
 
  private byte[] h364Buffer;
  private int h364Len = 0;
  private int h364Pos = 0;
  private static final byte[] start_code = {0, 0, 0, 1};   // h364 start code
 
 //傳入視頻的分辨率
  public RtspPacketDecode(int width, int height) {
    h364Buffer = new byte[getYuvBuffer(width, height)];
  } 
 
  /**
   * RTP解包H264
   *
   * @param rtpData
   * @return
   */
  public byte[] rtp2h364(byte[] rtpData, int rtpLen) {
 
    int fu_header_len = 12;     // FU-Header長度為12字節(jié)
    int extension = (rtpData[0] & (1 << 4)); // X: 擴展為是否為1
    if (extension > 0) {
      // 計算擴展頭的長度
      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);  // 取出h3bit and h4bit
    byte f = (byte) (CalculateUtil.byteToInt(rtpData[fu_header_len]) >> 7);        // 取出h2bit
    byte h364_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 (h364Pos >= 0) {
          h364Pos = -1;
          if (h364Len > 0) {
            byte[] h364Data = new byte[h364Len];
            System.arraycopy(h364Buffer, 0, h364Data, 0, h364Len);
            h364Len = 0;
            return h364Data;
          }
        }
      } else if (s == -128) { // start of fu-a
        h364Pos = 0;   // 指針歸0
        writeData2Buffer(start_code, 4);    // 寫入H264起始碼
        h364_nal_header = (byte) ((fu_header & 0x1f) | (nri << 5) | (f << 7));
        writeData2Buffer(new byte[]{h364_nal_header}, 1);
        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 {
        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
      h364Pos = 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 (h364Pos >= 0) {
        h364Pos = -1;
        if (h364Len > 0) {
          byte[] h364Data = new byte[h364Len];
          System.arraycopy(h364Buffer, 0, h364Data, 0, h364Len);
          h364Len = 0;
          return h364Data;
        }
      }
    }
    return null;
  }
 
  private void writeData2Buffer(byte[] data, int len) {
    if (h364Pos >= 0) {
      System.arraycopy(data, 0, h364Buffer, h364Pos, len);
      h364Pos += len;
      h364Len += len;
    }
  }
 
  //計算h364大小
  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.rtp2h364(out,len);

關于java、android實現可用的rtp封包解包h264就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI