流解析的代码长时间测试海康摄像时还不稳定,所以主要以学习为主,有知道的大佬欢迎指点下=。=
涉及到的相关类
BitUtils
public class BitUtils {
public static int byte2ToInt(byte b1,byte b2){
return byteToInt(b1,b2);
}
public static int byte4ToInt(byte b1,byte b2,byte b3,byte b4){
return byteToInt(b1,b2,b3,b4);
}
public static int byteToInt(byte... bs){
int len=bs.length;
int temp=0;
for(byte b:bs){
len--;
if(len==0){
temp+=(b&0xff);
}else{
temp+=(((b&0xff)<< (len*8)));
}
}
return temp;
}
public static void main(String[] args) {
String str="0b873697";
byte[] bytes=HexStringUtils.chars2Bytes(str.toCharArray());
System.out.println(byteToInt(bytes[0],bytes[1],bytes[2],bytes[3]));
System.out.println(byte4ToInt(bytes[0],bytes[1],bytes[2],bytes[3]));
}
}
HexStringUtils
public class HexStringUtils {
private static final char[] DIGITS_HEX = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
protected static char[] encodeHex(byte[] data) {
int l = data.length;
char[] out = new char[l << 1];
for (int i = 0, j = 0; i < l; i++) {
out[j++] = DIGITS_HEX[(0xF0 & data[i]) >>> 4];
out[j++] = DIGITS_HEX[0x0F & data[i]];
}
return out;
}
public static String byte2String(byte b){
byte[] data =new byte[1];
data[0]=b;
int l = data.length;
char[] out = new char[l << 1];
for (int i = 0, j = 0; i < l; i++) {
out[j++] = DIGITS_HEX[(0xF0 & data[i]) >>> 4];
out[j++] = DIGITS_HEX[0x0F & data[i]];
}
return new String(out);
}
protected static byte[] decodeHex(char[] data) {
int len = data.length;
if ((len & 0x01) != 0) {
throw new RuntimeException("字符个数应该为偶数");
}
byte[] out = new byte[len >> 1];
for (int i = 0, j = 0; j < len; i++) {
int f = toDigit(data[j], j) << 4;
j++;
f |= toDigit(data[j], j);
j++;
out[i] = (byte) (f & 0xFF);
}
return out;
}
protected static int toDigit(char ch, int index) {
int digit = Character.digit(ch, 16);
if (digit == -1) {
throw new RuntimeException("Illegal hexadecimal character " + ch + " at index " + index);
}
return digit;
}
public static String toHexString(byte[] bs) {
return new String(encodeHex(bs));
}
public static String hexString2Bytes(String hex) {
return new String(decodeHex(hex.toCharArray()));
}
public static byte[] chars2Bytes(char[] bs) {
return decodeHex(bs);
}
}
Parser
import com.fengyulei.fylsipserver.media.push.RtmpPusher;
import java.util.Map;
public interface Parser {
int TCP_PACKET_LENGTH = 2;
int RTP_HEADER_LENGTH = 12;
int UDP_START_INDEX = RTP_HEADER_LENGTH;
int TCP_START_INDEX = TCP_PACKET_LENGTH +RTP_HEADER_LENGTH;
int UDP_PS_HEADER_STUFFING_LENGTH_INDEX = 25;
int TCP_PS_HEADER_STUFFING_LENGTH_INDEX = UDP_PS_HEADER_STUFFING_LENGTH_INDEX+2;
int CRC_32_LENGTH = 4;
void parseUdp(Map<Integer, Packet> packetMap, int firstSeq, int endSeq,RtmpPusher rtmpPusher) throws Exception;
void parseTcp(Map<Integer,Packet> packetMap,int firstSeq,int endSeq,RtmpPusher rtmpPusher) throws Exception;
}
Packet
public class Packet {
public static final int I = 0;P
public static final int P = 1;
public static final int AUDIO = 2;
public static final int SUB_PACKET = 3;
private int timeStamp;
private int seq;
private byte[] data;
private int packetType;
public int getPacketType(){
return packetType;
}
public Packet(int seq, byte[] data, int packetType) {
this.seq = seq;
this.data = data;
this.packetType = packetType;
}
public byte[] getData(){
return data;
}
public int getTimeStamp() {
return timeStamp;
}
public void setTimeStamp(int timeStamp) {
this.timeStamp = timeStamp;
}
public int getSeq() {
return seq;
}
public void setSeq(int seq) {
this.seq = seq;
}
public void setData(byte[] data) {
this.data = data;
}
}
udp数据包缓存 SsrcUdpHandler
package com.fengyulei.fylsipserver.media.netty;
import com.fengyulei.fylsipserver.media.codec.CommonParser;
import com.fengyulei.fylsipserver.media.codec.Packet;
import com.fengyulei.fylsipserver.media.common.utils.BitUtils;
import com.fengyulei.fylsipserver.media.common.utils.HexStringUtils;
import com.fengyulei.fylsipserver.media.push.RtmpPusher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentLinkedDeque;
public class SsrcUdpHandler {
private static final Logger logger = LoggerFactory.getLogger(SsrcUdpHandler.class);
private ConcurrentLinkedDeque<Integer> mSeqMap = new ConcurrentLinkedDeque<>();
private Map<Integer,Packet> mPacketMap = new HashMap<>(60);
private boolean mIsFirstI;
private String ssrc;
private RtmpPusher rtmpPusher;
public RtmpPusher getRtmpPusher() {
return rtmpPusher;
}
private CommonParser mParser;
private int CACHE_FRAME_LENGTH= 5;
public SsrcUdpHandler(CommonParser mParser,String ssrc){
this.mParser=mParser;
this.ssrc=ssrc;
rtmpPusher=new RtmpPusher("rtmp://192.168.1.201:1935/live/"+ssrc,ssrc,"UDP");
rtmpPusher.startRemux();
}
public void read(byte[] copyData){
int length=copyData.length;
int seq = BitUtils.byte2ToInt(copyData[2],copyData[3]);
try{
Packet packet;
if(length > 16 && copyData[12] == 0 &©Data[13] ==0 &©Data[14] ==01 && (copyData[15]&0xff) == 0xba){
int stuffingLength = copyData[25] & 7;
int startIndex = 25+stuffingLength+1;
if(copyData[startIndex] == 0 && copyData[startIndex+1] == 0&©Data[startIndex+2] == 01&&(copyData[startIndex+3]&0xff) == 0xbb )
{
packet = new Packet(seq,copyData,Packet.I);
if(!mIsFirstI){
mIsFirstI = true;
}
}
else{
if(!mIsFirstI){
return;
}
packet = new Packet(seq,copyData,Packet.P);
}
mSeqMap.add(seq);
}
else if( length > 16 && copyData[12] == 0 &©Data[13] ==0 &©Data[14] ==01 && (copyData[15]&0xff) == 0xc0){
if(!mIsFirstI){
return;
}
mSeqMap.add(seq);
packet = new Packet(seq,copyData,Packet.AUDIO);
}else {
if(!mIsFirstI){
return ;
}
packet = new Packet(seq,copyData,Packet.SUB_PACKET);
}
mPacketMap.put(seq, packet);
if(mSeqMap.size() >= CACHE_FRAME_LENGTH){
Integer firstSeq = mSeqMap.pop();
Integer endSeq = mSeqMap.getFirst()-1;
mParser.parseUdp(mPacketMap,firstSeq,endSeq,rtmpPusher);
}
}catch (Exception e){
logger.error(e.getMessage(),e);
logger.error("UDPHandler解析异常:[{}]", HexStringUtils.toHexString(copyData));
}
}
}
tcp数据包缓存 SsrcTcpHandler
import com.fengyulei.fylsipserver.media.codec.CommonParser;
import com.fengyulei.fylsipserver.media.codec.Packet;
import com.fengyulei.fylsipserver.media.common.utils.BitUtils;
import com.fengyulei.fylsipserver.media.common.utils.HexStringUtils;
import com.fengyulei.fylsipserver.media.push.RtmpPusher;
import io.netty.channel.Channel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentLinkedDeque;
public class SsrcTcpHandler {
private static final Logger logger = LoggerFactory.getLogger(SsrcTcpHandler.class);
private ConcurrentLinkedDeque<Integer> mSeqMap = new ConcurrentLinkedDeque<>();
private Map<Integer,Packet> mPacketMap = new HashMap<>(60);
private boolean mIsFirstI;
private String ssrc;
private RtmpPusher rtmpPusher;
public RtmpPusher getRtmpPusher() {
return rtmpPusher;
}
private Channel channel;
public Channel getChannel() {
return channel;
}
public void setChannel(Channel channel) {
this.channel = channel;
}
private CommonParser mParser;
private int CACHE_FRAME_LENGTH= 2;
public SsrcTcpHandler(CommonParser mParser, String ssrc){
this.mParser=mParser;
this.ssrc=ssrc;
rtmpPusher=new RtmpPusher("rtmp://192.168.1.201:1935/live/"+ssrc,ssrc,"TCP");
rtmpPusher.startRemux();
}
public void read(byte[] copyData){
int length=copyData.length;
int seq = BitUtils.byte2ToInt(copyData[4],copyData[5]);
try{
Packet packet;
if(length > 18 && copyData[14] == 0 &©Data[15] ==0 &©Data[16] ==01 && (copyData[17]&0xff) == 0xba){
int stuffingLength = copyData[27] & 7;
int startIndex = 27+stuffingLength+1;
if(copyData[startIndex] == 0 && copyData[startIndex+1] == 0&©Data[startIndex+2] == 01&&(copyData[startIndex+3]&0xff) == 0xbb )
{
packet = new Packet(seq,copyData,Packet.I);
if(!mIsFirstI){
mIsFirstI = true;
}
}
else{
if(!mIsFirstI){
return;
}
packet = new Packet(seq,copyData,Packet.P);
}
mSeqMap.add(seq);
}
else if( length > 18 && copyData[14] == 0 &©Data[15] ==0 &©Data[16] ==01 && (copyData[17]&0xff) == 0xc0){
if(!mIsFirstI){
return;
}
mSeqMap.add(seq);
packet = new Packet(seq,copyData,Packet.AUDIO);
}else {
if(!mIsFirstI){
return ;
}
packet = new Packet(seq,copyData,Packet.SUB_PACKET);
}
mPacketMap.put(seq, packet);
if(mSeqMap.size() >= CACHE_FRAME_LENGTH){
Integer firstSeq = mSeqMap.pop();
Integer endSeq = mSeqMap.getFirst()-1;
mParser.parseTcp(mPacketMap,firstSeq,endSeq,rtmpPusher);
}
}catch (Exception e){
logger.error(e.getMessage(),e);
logger.error("TCPHandler解析异常:[{}]", HexStringUtils.toHexString(copyData));
}
}
}
解析器
package com.fengyulei.fylsipserver.media.codec;
import java.util.Map;
import com.fengyulei.fylsipserver.media.common.utils.BitUtils;
import com.fengyulei.fylsipserver.media.common.utils.HexStringUtils;
import com.fengyulei.fylsipserver.media.push.RtmpPusher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
@Component
public class CommonParser implements Parser{
private Logger log = LoggerFactory.getLogger(getClass());
@Override
public void parseUdp(Map<Integer,Packet> packetMap,int firstSeq,int endSeq,RtmpPusher rtmpPusher) throws Exception{
parse(packetMap,firstSeq,endSeq,rtmpPusher,rtmpPusher.getType());
}
@Override
public void parseTcp(Map<Integer,Packet> packetMap,int firstSeq,int endSeq,RtmpPusher rtmpPusher) throws Exception{
parse(packetMap,firstSeq,endSeq,rtmpPusher,rtmpPusher.getType());
}
private void parse(Map<Integer,Packet> packetMap,int firstSeq,int endSeq,RtmpPusher rtmpPusher,String type) throws Exception{
int remainEsLength = 0;
int startIndex = 0;
byte[] data=null;
Packet packet=null;
boolean isAudio=false;
boolean flag=true;
long pts=0L;
int i=0;
try {
for(i = firstSeq; i<= endSeq;i++){
packet = packetMap.remove(i);
if(packet == null){
log.error("丢包");
flag=false;
break;
}
data = packet.getData();
int packetType = packet.getPacketType();
boolean hasSubPacket = true;
int pesStartIndex = 0;
if(packetType == Packet.I){
if("UDP".equals(type)){
pesStartIndex = getIFramePesStartIndex(data, UDP_PS_HEADER_STUFFING_LENGTH_INDEX);
}else{
pesStartIndex = getIFramePesStartIndex(data, TCP_PS_HEADER_STUFFING_LENGTH_INDEX);
}
hasSubPacket = false;
}else if(packetType == Packet.P){
if("UDP".equals(type)){
pesStartIndex = getPFramePesStartIndex(data,UDP_PS_HEADER_STUFFING_LENGTH_INDEX);
}else {
pesStartIndex = getPFramePesStartIndex(data,TCP_PS_HEADER_STUFFING_LENGTH_INDEX);
}
hasSubPacket = false;
}else if(packetType == Packet.AUDIO){
if("UDP".equals(type)){
pesStartIndex = UDP_START_INDEX;
}else {
pesStartIndex = TCP_START_INDEX;
}
hasSubPacket = false;
}
isAudio = (packetType == Packet.AUDIO);
if(!hasSubPacket){
pts = getPts(data, pesStartIndex);
onPtsCallBack(pts,isAudio,rtmpPusher);
int pesDataLength = BitUtils.byte2ToInt(data[pesStartIndex+4],data[pesStartIndex+5]);
int pesHeaderDataLength = data[pesStartIndex+8] & 0xFF;
startIndex = pesStartIndex+6+3+pesHeaderDataLength;
remainEsLength = pesDataLength-3-pesHeaderDataLength;
}else {
if("UDP".equals(type)){
startIndex = UDP_START_INDEX;
}else {
startIndex = TCP_START_INDEX;
}
}
int packetLength= data.length;
int dataLength = packetLength - startIndex;
if(dataLength <= remainEsLength){
remainEsLength -= dataLength;
onMediaStreamCallBack(data, startIndex, dataLength,isAudio,rtmpPusher);
startIndex = 0;
continue;
}
onMediaStreamCallBack(data, startIndex, remainEsLength,isAudio,rtmpPusher);
startIndex+=remainEsLength;
remainEsLength = 0;
while(true){
if(packetLength-startIndex<=8){
log.error("解析异常1:[{}],[{}]",rtmpPusher.getSsrc(),type);
break;
}