From 8c562c19a6c1493f8898d0c58cbf86b18af46d37 Mon Sep 17 00:00:00 2001 From: haungdezhi Date: Fri, 8 Mar 2019 13:04:58 +0800 Subject: [PATCH] =?UTF-8?q?h264=E6=96=87=E4=BB=B6=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=B8=A7=E5=89=8D=E6=B7=BB=E5=8A=A0=E5=B8=A7=E9=95=BF=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- H264Parser.java | 176 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 136 insertions(+), 40 deletions(-) diff --git a/H264Parser.java b/H264Parser.java index 9e83c86..8ec5f8d 100644 --- a/H264Parser.java +++ b/H264Parser.java @@ -1,58 +1,104 @@ import java.io.File; import java.io.FileInputStream; +import java.io.FileOutputStream; import java.io.InputStream; import java.util.ArrayList; import java.util.List; import java.util.Formatter; public class H264Parser { - - //0x00000001或0x000001是一个nalu的起始标志,遇到下一个此标志时为该nalu的结尾。 - //起始标志的后面第一个字节(type)里包含有nalu的类型,type & 0x1F即为该nalu的类型(nalu_type)。 - private static final int NALU_TYPE_SLICE = 1; //非IDR图像中不采用数据划分的片段,为P或B帧 - private static final int NALU_TYPE_DPA = 2; //非IDR图像中A类数据划分片段 - private static final int NALU_TYPE_DPB = 3; //非IDR图像中B类数据划分片段 - private static final int NALU_TYPE_DPC = 4; //非IDR图像中C类数据划分片段 - private static final int NALU_TYPE_IDR = 5; //IDR图像的片段,关键帧,I帧 - private static final int NALU_TYPE_SEI = 6; //补充增强信息 - private static final int NALU_TYPE_SPS = 7; //序列参数集(Sequence Parameter Set) - private static final int NALU_TYPE_PPS = 8; //图像参数集PPS(Picture Parameter Set) - private static final int NALU_TYPE_AUD = 9; //分割符 - private static final int NALU_TYPE_EOSEQ = 10; //序列结束符 - private static final int NALU_TYPE_EOSTREAM = 11; //流结束符 - private static final int NALU_TYPE_FILL = 12; //填充数据 - + //0x00000001鎴0x000001鏄竴涓猲alu鐨勮捣濮嬫爣蹇楋紝閬囧埌涓嬩竴涓鏍囧織鏃朵负璇alu鐨勭粨灏俱 + //璧峰鏍囧織鐨勫悗闈㈢涓涓瓧鑺傦紙type锛夐噷鍖呭惈鏈塶alu鐨勭被鍨嬶紝type & 0x1F鍗充负璇alu鐨勭被鍨嬶紙nalu_type锛夈 + + private static final int NALU_TYPE_SLICE = 1; //闈濱DR鍥惧儚涓笉閲囩敤鏁版嵁鍒掑垎鐨勭墖娈碉紝涓篜鎴朆甯 + private static final int NALU_TYPE_DPA = 2; //闈濱DR鍥惧儚涓瑼绫绘暟鎹垝鍒嗙墖娈 + private static final int NALU_TYPE_DPB = 3; //闈濱DR鍥惧儚涓瑽绫绘暟鎹垝鍒嗙墖娈 + private static final int NALU_TYPE_DPC = 4; //闈濱DR鍥惧儚涓瑿绫绘暟鎹垝鍒嗙墖娈 + private static final int NALU_TYPE_IDR = 5; //IDR鍥惧儚鐨勭墖娈碉紝鍏抽敭甯э紝I甯 + private static final int NALU_TYPE_SEI = 6; //琛ュ厖澧炲己淇℃伅 + private static final int NALU_TYPE_SPS = 7; //搴忓垪鍙傛暟闆嗭紙Sequence Parameter Set锛 + private static final int NALU_TYPE_PPS = 8; //鍥惧儚鍙傛暟闆哖PS锛圥icture Parameter Set锛 + private static final int NALU_TYPE_AUD = 9; //鍒嗗壊绗 + private static final int NALU_TYPE_EOSEQ = 10; //搴忓垪缁撴潫绗 + private static final int NALU_TYPE_EOSTREAM = 11; //娴佺粨鏉熺 + private static final int NALU_TYPE_FILL = 12; //濉厖鏁版嵁 + private static final int NALU_PRIORITY_DISPOSABLE = 0; private static final int NALU_PRIORITY_LOW = 1; private static final int NALU_PRIORITY_HIGH = 2; private static final int NALU_PRIORITY_HIGHEST = 3; - private boolean mFirstFind = true; //第一次查找起始码 - private int mStartCodeLen = 0; //起始码长度 + private boolean mFirstFind = true; //绗竴娆℃煡鎵捐捣濮嬬爜 + private int mStartCodeLen = 0; //璧峰鐮侀暱搴 private int mCurStartCodeLen = 0; - private int mFrameLen = 0; //帧长度 - private int mFrameFirstByte = 0; //帧数据中的第一个字节 + private int mFrameLen = 0; //甯ч暱搴 + private int mFrameFirstByte = 0; //甯ф暟鎹腑鐨勭涓涓瓧鑺 private int mCurFrameFirstByte = 0; private int mPos = 0; private int mLen = 0; private String mStartCode = ""; - + private int mNaluType = 0; private int mForbiddenBit = 0; private int mNalReferenceIdc = 0; - + + private byte[] mFrameBuffer = null; + private FileOutputStream mFileOutputStream = null; + private boolean mIsAddFrameLen = false; + public static void main(String[] args) { - if (args.length > 0) { - H264Parser h264Parser = new H264Parser(); + H264Parser h264Parser = null; + if (args.length == 2) { + h264Parser = new H264Parser(); + if ("1".equals(args[1])) { + h264Parser.mIsAddFrameLen = true; + } + h264Parser.init(); + h264Parser.ParseH264(args[0]); + } else if (args.length == 1) { + h264Parser = new H264Parser(); + h264Parser.init(); h264Parser.ParseH264(args[0]); } else { System.out.println("Missing h264 filename."); + String path = "C:\\Users\\hw\\Desktop\\test.h264"; + h264Parser = new H264Parser(); + h264Parser.init(); + h264Parser.ParseH264(path); + } + if (h264Parser != null) { + h264Parser.clear(); } } - - //如果NALU对应的Slice为一帧的开始就用0x00000001(即4个字节长度),否则就用0x000001(即3个字节长度)。 + + public H264Parser() { + + } + public void init() { + mFrameBuffer = new byte[2 * 1024 * 1024]; + if (mIsAddFrameLen) { + File file = new File("test_2.h264"); + try { + mFileOutputStream = new FileOutputStream(file); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + public void clear() { + if (mFileOutputStream != null) { + try { + mFileOutputStream.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + + //濡傛灉NALU瀵瑰簲鐨凷lice涓轰竴甯х殑寮濮嬪氨鐢0x00000001(鍗4涓瓧鑺傞暱搴)锛屽惁鍒欏氨鐢0x000001(鍗3涓瓧鑺傞暱搴)銆 public int FindStartCode(InputStream inputStream) { byte[] startCode = new byte[4]; @@ -102,7 +148,7 @@ public class H264Parser { public int GetNalu(InputStream inputStream) { mPos = mPos + mLen; - //第一次查找起始码,只是为了移动文件指针 + //绗竴娆℃煡鎵捐捣濮嬬爜锛屽彧鏄负浜嗙Щ鍔ㄦ枃浠舵寚閽 if (mFirstFind) { mFirstFind = false; if (-1 == (mStartCodeLen = FindStartCode(inputStream))) { @@ -112,40 +158,55 @@ public class H264Parser { if (mStartCodeLen == 3) { mStartCode = "001"; + // 娣诲姞璧峰鐮 + mFrameBuffer[0] = 0; + mFrameBuffer[1] = 0; + mFrameBuffer[2] = 1; } else { mStartCode = "0001"; + // 娣诲姞璧峰鐮 + mFrameBuffer[0] = 0; + mFrameBuffer[1] = 0; + mFrameBuffer[2] = 0; + mFrameBuffer[3] = 1; } - + List startCodeList = new ArrayList<>(); int frameLen = 0; while(true) { int tmp = -1; - //一个字节一个字节从文件流中读取 + //涓涓瓧鑺備竴涓瓧鑺備粠鏂囦欢娴佷腑璇诲彇 try { tmp = inputStream.read(); } catch (Exception e) { e.printStackTrace(); } - //读取到文件尾 + //璇诲彇鍒版枃浠跺熬 if (tmp == -1) { mFrameLen = frameLen; + // 鍙﹀瓨涓 + saveAs(frameLen); return -1; } - - //帧数据长度累加 + + //璇诲彇鍒扮殑鏁版嵁淇濆瓨鍒癰uffer + mFrameBuffer[mStartCodeLen+frameLen] = (byte)(0xff & tmp); + //甯ф暟鎹暱搴︾疮鍔 frameLen++; - //将从文件中读取的数据保存到list中 + //灏嗕粠鏂囦欢涓鍙栫殑鏁版嵁淇濆瓨鍒發ist涓 startCodeList.add(tmp); - //从一帧的第一个字节中获取NALU信息 + //浠庝竴甯х殑绗竴涓瓧鑺備腑鑾峰彇NALU淇℃伅 if (frameLen == 1) { - //如果上一次查找到的起始码长度是3,则上次已经保存了第一个字节的数据 + //濡傛灉涓婁竴娆℃煡鎵惧埌鐨勮捣濮嬬爜闀垮害鏄3锛屽垯涓婃宸茬粡淇濆瓨浜嗙涓涓瓧鑺傜殑鏁版嵁 if (mStartCodeLen == 3) { tmp = mFrameFirstByte; - frameLen++; //帧长度加上第一个字节 + //璇诲彇鍒扮殑鏁版嵁淇濆瓨鍒癰uffer + mFrameBuffer[mStartCodeLen+frameLen] = (byte)(0xff & tmp); + frameLen++; //甯ч暱搴﹀姞涓婄涓涓瓧鑺 } mCurFrameFirstByte = tmp; mForbiddenBit = tmp & 0x80; @@ -153,12 +214,12 @@ public class H264Parser { mNaluType = tmp & 0x1f; } - //保存四个字节到list中 + //淇濆瓨鍥涗釜瀛楄妭鍒發ist涓 if (startCodeList.size() < 4) { continue; } - - //查找起始码,如果返回值大于0就表示找到了 + + //鏌ユ壘璧峰鐮侊紝濡傛灉杩斿洖鍊煎ぇ浜0灏辫〃绀烘壘鍒颁簡 int startCodeLen = FindStartCode(startCodeList); if (startCodeLen > 0) { if (startCodeLen == 3) { @@ -172,9 +233,29 @@ public class H264Parser { startCodeList.remove(0); } mFrameLen = frameLen; + // 鍙﹀瓨涓 + saveAs(frameLen); return 0; } - + + /** + * 娣诲姞甯ч暱搴﹀苟鍙﹀瓨涓 + * + * @param frameLen 闄ゅ幓璧峰鐮佺殑甯ч暱搴 + */ + public void saveAs(int frameLen) { + if (mFileOutputStream != null) { + try { + //System.out.println("len = " + mStartCodeLen+frameLen); + mFileOutputStream.write(intToBytes(mStartCodeLen+frameLen), 0, 4); + mFileOutputStream.write(mFrameBuffer, 0, mStartCodeLen+frameLen); + } catch (Exception e) { + e.printStackTrace(); + } + mFrameBuffer[0] = '\0'; + } + } + public boolean ParseH264(String fileName) { if (fileName.equals("")) { return false; @@ -227,6 +308,9 @@ public class H264Parser { if (dataLen == -1) { break; } + if (iNaluNum == 3) { + //break; + } } try { @@ -236,4 +320,16 @@ public class H264Parser { } return true; } + + /** + * int鍨嬭浆鍥涘瓧鑺俠yte鏁扮粍 + */ + public static byte[] intToBytes(int i){ + byte[] result=new byte[4]; + result[3]=(byte)((i >> 24)& 0xFF); + result[2]=(byte)((i >> 16)& 0xFF); + result[1]=(byte)((i >> 8)& 0xFF); + result[0]=(byte)(i & 0xFF); + return result; + } } \ No newline at end of file