package com.zhuyun.rtp;
import java.nio.ByteBuffer;
import org.jitsi.service.neomedia.RawPacket;
public class RtpUtils {
public static byte[] h264Pack(RawPacket rtpPacket){
if (rtpPacket.getPayloadType() == 96) { //以下处理仅针对H264码流
ByteBuffer bb = null; //存放RTP解析后的NALU的数据
byte[] rtpPayload = rtpPacket.getPayload();
byte fu_indicator = rtpPayload[0];
byte fu_header = rtpPayload[1];
byte nalu_type = (byte) (fu_indicator & 0x1f);
// System.out.println("=======nalu_type========" + nalu_type);
if (nalu_type == 0x1C) { //FU-A //分片封包模式
byte start_flag = (byte) (fu_header & 0x80);
byte end_flag = (byte) (fu_header & 0x40);
byte nalu_header = (byte) ((fu_indicator & 0xe0) | (fu_header & 0x1f)); //根据fu_indicator和fu_header来重构出nalu_header
if (start_flag != 0) { //第一个分片
// System.out.println("===================start");
bb = ByteBuffer.allocate(rtpPayload.length + 3);
bb.put(new byte[]{0x0, 0x0, 0x0, 0x1});
bb.put(nalu_header);
byte[] dest = new byte[rtpPayload.length-2];
System.arraycopy(rtpPayload, 2, dest, 0, rtpPayload.length-2);
bb.put(dest);
} else if (end_flag != 0) { //最后一个分片
// System.out.println("===================end");
bb = ByteBuffer.allocate(rtpPayload.length-2);
byte[] dest = new byte[rtpPayload.length-2];
System.arraycopy(rtpPayload, 2, dest, 0, rtpPayload.length-2);
bb.put(dest);
} else { //中间分片
// System.out.println("===================middle");
bb = ByteBuffer.allocate(rtpPayload.length-2);
byte[] dest = new byte[rtpPayload.length-2];
System.arraycopy(rtpPayload, 2, dest, 0, rtpPayload.length-2);
bb.put(dest);
}
} else if (nalu_type == 0x18) { //STAP-A //组合封包模式
int srcOffset = 1; //第一个字节是STAP-A头,跳过
int bufferLen = 0;
//先计算需要的ByteBuffer长度,再将内容放进去
while ((rtpPayload.length - srcOffset) > 2) //循环解析RTP,将组合后的NALU取出来,再加上起始码
{
int size = 0; //NALU的长度,2个字节
size |= rtpPayload[srcOffset] << 8;
size |= rtpPayload[srcOffset + 1];
srcOffset += 2; //将NALU header和NALU payload一起放进去,然后进入下一个循环
bufferLen += (4+size);
srcOffset += size;
}
srcOffset = 1;
bb = ByteBuffer.allocate(bufferLen);
while ((rtpPayload.length - srcOffset) > 2) //循环解析RTP,将组合后的NALU取出来,再加上起始码
{
int size = 0; //NALU的长度,2个字节
size |= rtpPayload[srcOffset] << 8;
size |= rtpPayload[srcOffset + 1];
srcOffset += 2; //将NALU header和NALU payload一起放进去,然后进入下一个循环
byte[] dest = new byte[size];
System.arraycopy(rtpPayload, srcOffset, dest, 0, size);
bb.put(new byte[]{0x0, 0x0, 0x0, 0x1}); //NALU的起始码
bb.put(dest);
srcOffset += size;
}
} else if (nalu_type == 0x1) { //单一NAL 单元模式
bb = ByteBuffer.allocate(rtpPayload.length + 4); //将整个rtpPayload一起放进去
bb.put(new byte[]{0x0, 0x0, 0x0, 0x1});
bb.put(rtpPayload);
} else {
System.out.println("Unsupport nalu type!");
}
return bb.array();
}
return null;
}
}
主要修改部分,是在nalu_type == 0x18的时候。之前长度直接定为了rtpPayload.length,其实是不对的。
这种情况下,必须要循环解析RTP,才能获取到长度,即:
while ((rtpPayload.length - srcOffset) > 2) //循环解析RTP,将组合后的NALU取出来,再加上起始码
{
int size = 0; //NALU的长度,2个字节
size |= rtpPayload[srcOffset] << 8;
size |= rtpPayload[srcOffset + 1];
srcOffset += 2;
bufferLen += (4+size);
srcOffset += size;
}
其他内容不变。