首页  编辑  

安卓Android Java中从RTP数据包中抽取完整H264帧以便解码?

Tags: /Android/   Date Created:
如何从RTP包中,抽取完整H264帧,以便用于解码器解码(MediaCodec)?
How to extract full H264 frame from RTP package for MediaCodec decode?
BE.java:
package abc.camera;

/**
 *  Big Endian get/set functions.
 *
 *  Usage : network packets, Motorola CPU style, etc.
 *
 *  Could use java.nio.ByteBuffer but sometimes this is faster.
 *
 *  Created : Dec 5, 2013
 */

public class BE {

  public static int getuint8(byte[] data, int offset) {
    return ((int)data[offset]) & 0xff;
  }

  public static int getuint16(byte[] data, int offset) {
    int ret;
    ret = ((int) data[offset] & 0xff) << 8;
    ret += ((int)data[offset + 1]) & 0xff;
    return ret;
  }

  public static int getuint32(byte[] data, int offset) {
    int ret;
    ret = ((int) data[offset] & 0xff) << 24;
    ret += ((int) data[offset + 1] & 0xff) << 16;
    ret += ((int) data[offset + 2] & 0xff) << 8;
    ret += ((int) data[offset + 3] & 0xff);
    return ret;
  }

  public static long getuint64(byte[] data, int offset) {
    long ret;
    ret = ((long) data[offset] & 0xff) << 56;
    ret += ((long) data[offset + 1] & 0xff) << 48;
    ret += ((long) data[offset + 2] & 0xff) << 40;
    ret += ((long) data[offset + 3] & 0xff) << 32;
    ret += ((long) data[offset + 4] & 0xff) << 24;
    ret += ((long) data[offset + 5] & 0xff) << 16;
    ret += ((long) data[offset + 6] & 0xff) << 8;
    ret += ((long) data[offset + 7] & 0xff);
    return ret;
  }

  public static void setuint8(byte data[], int offset, int value) {
    data[offset+0] = (byte)(value & 0xff);
  }

  public static void setuint16(byte data[], int offset, int value) {
    data[offset+1] = (byte)(value & 0xff);
    value >>= 8;
    data[offset+0] = (byte)(value & 0xff);
  }

  public static void setuint32(byte data[], int offset, int value) {
    data[offset+3] = (byte)(value & 0xff);
    value >>= 8;
    data[offset+2] = (byte)(value & 0xff);
    value >>= 8;
    data[offset+1] = (byte)(value & 0xff);
    value >>= 8;
    data[offset+0] = (byte)(value & 0xff);
  }

  public static void setuint64(byte data[], int offset, long value) {
    data[offset+7] = (byte)(value & 0xff);
    value >>= 8;
    data[offset+6] = (byte)(value & 0xff);
    value >>= 8;
    data[offset+5] = (byte)(value & 0xff);
    value >>= 8;
    data[offset+4] = (byte)(value & 0xff);
    value >>= 8;
    data[offset+3] = (byte)(value & 0xff);
    value >>= 8;
    data[offset+2] = (byte)(value & 0xff);
    value >>= 8;
    data[offset+1] = (byte)(value & 0xff);
    value >>= 8;
    data[offset+0] = (byte)(value & 0xff);
  }


  //Using arrays are 40% faster than using java.nio.ByteBuffer

  public static short[] byteArray2shortArray(byte in[], short out[]) {
    int len = in.length / 2;
    if (out == null) out = new short[len];
    int p = 0;
    short val;
    for (int a = 0; a < len; a++) {
      val  = (short)((((short)in[p++]) & 0xff) << 8);
      val += (((short)in[p++]) & 0xff);
      out[a] = val;
    }
    return out;
  }
  public static int[] byteArray2intArray(byte in[], int out[]) {
    int len = in.length / 4;
    if (out == null) out = new int[len];
    int p = 0;
    int val;
    for (int a = 0; a < len; a++) {
      val  = (((int)in[p++]) & 0xff) << 24;
      val += (((int)in[p++]) & 0xff) << 16;
      val += (((int)in[p++]) & 0xff) << 8;
      val += (((int)in[p++]) & 0xff);
      out[a] = val;
    }
    return out;
  }
  public static byte[] shortArray2byteArray(short in[], byte out[]) {
    int len = in.length;
    if (out == null) out = new byte[len * 2];
    int p = 0;
    short val;
    for (int a = 0; a < len; a++) {
      val = in[a];
      out[p++] = (byte) ((val & 0xff00) >>> 8);
      out[p++] = (byte) (val & 0xff);
    }
    return out;
  }
  public static byte[] intArray2byteArray(int in[], byte out[]) {
    int len = in.length;
    if (out == null) out = new byte[len * 4];
    int p = 0;
    int val;
    for (int a = 0; a < len; a++) {
      val = in[a];
      out[p++] = (byte) ((val & 0xff000000) >>> 8);
      out[p++] = (byte) ((val & 0xff0000) >> 8);
      out[p++] = (byte) ((val & 0xff00) >> 8);
      out[p++] = (byte) (val & 0xff);
    }
    return out;
  }

}
RTPH264.java
package abc.h264;

/**
 * Encodes/Decodes RTP/H264 packets
 * <p>
 * http://tools.ietf.org/html/rfc3984
 *
 * @author pquiring
 */

import java.util.ArrayList;
import java.util.Arrays;

public class RTPH264 {

    //mtu = 1500 - 14(ethernet) - 20(ip) - 8(udp) - 12(rtp) = 1446 bytes payload per packet
    private static final int mtu = 1440;
    private byte partial[] = new byte[0];
    private int lastseqnum = -1;
    private int last_timestamp = -1;

    private int find_best_length(byte data[], int offset, int length) {
        //see if there is a 0,0,1 and return a length to that
        //this way the next packet will start at a resync point
        for (int a = 1; a < length - 3; a++) {
            if (data[offset + a] == 0 && data[offset + a + 1] == 0 && data[offset + a + 2] == 1)
                return a;
        }
        return length;
    }

    /*
     * NAL Header : F(1) NRI(2) TYPE(5) : F=0 NRI=0-3 TYPE=1-23:full_packet 28=FU-A
     * FUA Header : S(1) E(1) R(1) TYPE(5) : S=start E=end R=reserved TYPE=???
     */

    /** Encodes raw H.264 data into multiple RTP packets. */
    public boolean NeedDecode(byte rtp[]) {
        if (rtp.length < 1500) return false;

        int fu_header_len = 12;

        int extension = (rtp[0] >> 4) & 1// X: 扩展为是否为1
        if (extension > 0) {
            // 计算扩展头的长度
            //int extLen = (rtpData[12] << 24) + (rtpData[13] << 16) + (rtpData[14] << 8) + rtpData[15];
            int extLen = (rtp[14] << 8) + rtp[15];
            fu_header_len += (extLen + 1) * 4;
        }

        int type = rtp[fu_header_len] & 0x1f;
        return type == 28;
    }

    public static int getTimestamp(byte[] data, int off) {
        return BE.getuint32(data, 4 + off);
    }

    public static int getSeqnum(byte[] data, int off) {
        return BE.getuint16(data, 2 + off);
    }

    /**
     * Returns last full packet.
     */
    public byte[] decode(byte rtp[], int length) {
        int fu_header_len = 12;
        if (length < 12 + 2) return null//bad packet

        int extension = (rtp[0] >> 4) & 1// X: 扩展为是否为1
        if (extension > 0) {
            if (length < fu_header_len + 4)
                return null;
            // 计算扩展头的长度
            //int extLen = (rtpData[12] << 24) + (rtpData[13] << 16) + (rtpData[14] << 8) + rtpData[15];
            int extLen = (rtp[14] << 8) + rtp[15];
            fu_header_len += (extLen + 1) * 4;
        }

        int h264Length = length - fu_header_len;

        if (h264Length == 0) {
            // 空包,原样返回一个空包!
            //return new byte[0];
            return null;
        }

        int type = rtp[fu_header_len] & 0x1f;
        if (partial == null) {
            partial = new byte[0];
        }

        //Log.e("", "decode: Type !!!! " + type);

        byte[] ret = null;

        if (type == 28) {
            int thistimestamp = getTimestamp(rtp, 0);
            if (last_timestamp == -1) last_timestamp = thistimestamp;

            if (thistimestamp != last_timestamp && partial.length > 0) {
                ret = partial;
                partial = new byte[0];
            }

            //FU-A Packet
            if ((rtp[13] & 0x80) == 0x80) {
                //first NAL packet (restore first byte)
                int nri = rtp[12] & 0x60;
                type = rtp[13] & 0x1f;
                partial = Arrays.copyOf(partial, partial.length + 5);
                partial[partial.length - 2] = 1//0,0,0,1
                partial[partial.length - 1] = (byte) (nri + type);  //NRI TYPE (first byte)
                lastseqnum = getSeqnum(rtp, 0);
            } else {
                int thisseqnum = getSeqnum(rtp, 0);
                if (thisseqnum != lastseqnum + 1) {
                    partial = null;
                    lastseqnum = -1;
                    last_timestamp = -1;
                    return null;
                }
                lastseqnum = thisseqnum;
            }

            last_timestamp = thistimestamp;
            int partialLength = partial.length;
            h264Length -= 2;
            partial = Arrays.copyOf(partial, partial.length + h264Length);
            System.arraycopy(rtp, fu_header_len + 2, partial, partialLength, h264Length);
        } else {
            ret = new byte[h264Length + 3];
            ret[0] = 0;
            ret[1] = 0;
            ret[2] = 1;
            System.arraycopy(rtp, fu_header_len, ret, 3, h264Length);
        }

        return ret;
    }

    boolean leak = false;
    int leak_len = 0;

    // 提取 0xE0的PES包
    public byte[][] decode2(byte rtp[]) {
        ArrayList<byte[]> packets = new ArrayList<byte[]>();

        int start = 2;
        if (leak) {  // 缺少包
            if (leak_len > rtp.length) {  /// 仍然不够
                int partialLength = partial.length;
                partial = Arrays.copyOf(partial, partial.length + rtp.length);
                System.arraycopy(rtp, 0, partial, partialLength, rtp.length);
                return null;
            } else {
                int partialLength = partial.length;
                partial = Arrays.copyOf(partial, partial.length + leak_len);
                System.arraycopy(rtp, 0, partial, partialLength, leak_len);
                byte[] full = partial;
                packets.add(full);

                partial = new byte[0];
                start = leak_len;
                leak_len = 0;
                leak = false;
            }
        }

        while (start < rtp.length) {
            start++;
            if (rtp[start - 3] == 0 && rtp[start - 2] == 0 && rtp[start - 1] == 1 && rtp[start] == -32) { // 找到 0x000001E0
                int len = (rtp[start + 1] << 8) + (rtp[start + 2] & 0xff);
                start += 3;
                if (start + len > rtp.length) { // 本次不足长度
                    leak_len = rtp.length - start;
                    len = rtp.length - start;
                    int partialLength = partial.length;
                    partial = Arrays.copyOf(partial, partial.length + len);
                    System.arraycopy(rtp, start, partial, partialLength, len);
                    leak = true;
                    break;
                } else // 本次足够长度
                {
                    byte[] full = new byte[len];
//           Log.e("Decode2", "本包长度 " + len);
                    System.arraycopy(rtp, start, full, 0, len);
                    packets.add(full);
                    start += len;
                } // end 本次足够长度
            }  // if 找到 0x000001E0
        } // end while start < rtp.length

        return packets.toArray(new byte[0][0]);
    }

    public byte[] decodePS(byte rtp[]) {
        int start = 3;

        while (start < rtp.length) {
            if (rtp[start - 3] == 0 && rtp[start - 2] == 0 && rtp[start - 1] == 1 && rtp[start] == -70) { // 找到 0x000001BA
                int len = (rtp[start + 1] << 8) + rtp[start + 2];
                start += 3;
                if (start + len > rtp.length) { // 本次不足长度
                    leak_len = rtp.length - start;
                    len = rtp.length - start;
                    int partialLength = partial.length;
                    partial = Arrays.copyOf(partial, partial.length + len);
                    System.arraycopy(rtp, start, partial, partialLength, len);
                    leak = true;
                    break;
                } else // 本次足够长度
                {
                    byte[] full = new byte[len];
                    //Log.e("Decode2", "本包长度 " + len);
                    System.arraycopy(rtp, start, full, 0, len);
//                    packets.add(full);
                    start += len;
                } // end 本次足够长度
            }  // if 找到 0x000001E0
        } // end while start < rtp.length
        return null;
//        return packets.toArray(new byte[0][0]);
    }

}

/*
 Type Name
    0 [invalid]
    1 Coded slice
    2 Data Partition A
    3 Data Partition B
    4 Data Partition C
    5 IDR (Instantaneous Decoding Refresh) Picture
    6 SEI (Supplemental Enhancement Information)
    7 SPS (Sequence Parameter Set)
    8 PPS (Picture Parameter Set)
    9 Access Unit Delimiter
   10 EoS (End of Sequence)
   11 EoS (End of Stream)
   12 Filter Data
13-23 [extended]
24-31 [unspecified]
*/
用法:
private RTPH264 rtp = new RTPH264();
byte[] frame = rtp.decode(pBuffer, dwBufSize);
if (frame != null) {
    Bitmap bitmap = decodeVideo(frame, frame.length);
}
frame就是一个完整的H264帧,可以用于MediaCodec解码,下面代码就可以在界面上看到视频了!!
下面decode初始化如下:
            decodec = MediaCodec.createDecoderByType("video/avc");
            MediaFormat mediaFormat = MediaFormat.createVideoFormat("video/avc", 1920, 1080);
            mediaFormat.setInteger(MediaFormat.KEY_BITRATE_MODE, MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_CQ);
            mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible);
            decodec.configure(mediaFormat, svPreview.getHolder().getSurface(), null, 0); // svPreview为一个SurfaceView对象
            //开始解码
            decodec.start();
 private void decodeVideo(byte[] buf, int size) {
        ByteBuffer[] inputBuffers = decodec.getInputBuffers();
        int inputBufferIndex = decodec.dequeueInputBuffer(-1);

        if (inputBufferIndex >= 0) {
            ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
            inputBuffer.clear();
            inputBuffer.put(buf, 0, size);
            decodec.queueInputBuffer(inputBufferIndex, 0, size, System.nanoTime() / 1000, 0);
        }

        MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
        int outputBufferIndex = decodec.dequeueOutputBuffer(bufferInfo, 0);
        while (outputBufferIndex >= 0) {
            decodec.releaseOutputBuffer(outputBufferIndex, true);
            outputBufferIndex = decodec.dequeueOutputBuffer(bufferInfo, 0);
        }
    }