有這樣一種需求,給你一個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位)。