如何從H264原始數據SPS裏面得到視頻的長寬

有這樣一種需求,給你一個h264原始數據文件,讓你直接播放出來,如何實現?

思路是這樣的,H264原始數據格式都是 0x00000001後面跟0x67 0x68 0x65 0x41這樣的數據,解碼需要一個完整的NAL數據單元,我們需要將每個0x00000001以及下一個0x00000001之前的數據讀出來,交給解碼器解碼。

讀文件我就不囉嗦了,本文主要講解如何從SPS獲取視頻長寬,SPS即0x67開頭數據,讀到0x00000001 後一個字節是0x67的後面跟的數據就是SPS了。我們看一個封包裏面sps的結構:


我們可以看到 最下面的42開始就是0x67後面的SPS數據了,看H264的官方文檔可以知道,SPS裏面的參數都是按照bit的形式存儲的。

我自己寫了一個解析SPS的類,代碼如下:

import android.util.Log;

/*
 * Author:Vincent Luo
 * Date: 20150615
 * Description:參考H264標準語法實現對SPS參數的解析
 */
public class H264SPSPaser {
	private static final String TAG = "H264SPSPaser";
	private static int startBit = 0;
	/*
	 * 從數據流data中第StartBit位開始讀,讀bitCnt位,以無符號整形返回
	 */
	public static short u(byte[] data,int bitCnt,int StartBit){
		short ret = 0;
		int start = StartBit;
		for(int i = 0;i < bitCnt;i++){
			ret<<=1;
			if ((data[start / 8] & (0x80 >> (start%8))) != 0)
			{
				ret += 1;
			}
			start++;
		}
		return ret;
	}
	/*
	 * 無符號指數哥倫布編碼
	 * leadingZeroBits = −1;
	 * for( b = 0; !b; leadingZeroBits++ )
	 *    b = read_bits( 1 )
	 * 變量codeNum 按照如下方式賦值:
	 * codeNum = 2^leadingZeroBits − 1 + read_bits( leadingZeroBits )
	 * 這裏read_bits( leadingZeroBits )的返回值使用高位在先的二進制無符號整數表示。
	 */
	public static short ue(byte[] data,int StartBit){
		short ret = 0;
		int leadingZeroBits = -1;
		int tempStartBit = (StartBit == -1)?startBit:StartBit;//如果傳入-1,那麼就用上次記錄的靜態變量
		 for( int b = 0; b != 1; leadingZeroBits++ ){//讀到第一個不爲0的數,計算前面0的個數
			 b = u(data,1,tempStartBit++);
		 }
		 Log.d(TAG,"ue leadingZeroBits = " + leadingZeroBits + ",Math.pow(2, leadingZeroBits) = " + Math.pow(2, leadingZeroBits) + ",tempStartBit = " + tempStartBit);
		 ret = (short) (Math.pow(2, leadingZeroBits) - 1 + u(data,leadingZeroBits,tempStartBit));
		 startBit = tempStartBit + leadingZeroBits;
		 Log.d(TAG,"ue startBit = " + startBit);
		return ret;
	}
	/*
	 * 有符號指數哥倫布編碼
	 * 9.1.1 有符號指數哥倫布編碼的映射過程
	 *按照9.1節規定,本過程的輸入是codeNum。
	 *本過程的輸出是se(v)的值。
	 *表9-3中給出了分配給codeNum的語法元素值的規則,語法元素值按照絕對值的升序排列,負值按照其絕對
	 *值參與排列,但列在絕對值相等的正值之後。
	 *表 9-3-有符號指數哥倫布編碼語法元素se(v)值與codeNum的對應
	 *codeNum 語法元素值
	 *	0 		0
	 *	1		1
	 *	2		−1
	 *	3		2
	 *	4		−2
	 *	5		3
	 *	6		−3
	 *	k		(−1)^(k+1) Ceil( k÷2 )
	 */
	public static int se(byte[] data,int StartBit){
		int ret = 0;
		short codeNum = ue(data,StartBit);
		ret = (int) (Math.pow(-1, codeNum + 1)*Math.ceil(codeNum/2));
		return ret;
	}
}
只需調用如下接口即可得到寬高:

int width = (H264SPSPaser.ue(sps,34) + 1)*16;
int height = (H264SPSPaser.ue(sps,-1) + 1)*16;
其中參數sps就是42開始的那一串字節流,34指的是寬開始的位置(bit位)。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章