1. 程式人生 > >H264 獲取SPS與PPS(附原始碼)

H264 獲取SPS與PPS(附原始碼)

轉載請註明出處!

在用Android手機進行h264硬編碼的時候如果要進行視訊流的實時傳輸與播放,就需要知道視訊流的Sequence Parameter Sets (SPS) 和Picture Parameter Set (PPS)。

今天算是看明白如何獲取SPS和PPS,在這裡記錄下來,希望有需要的朋友可以在這裡獲取到一些些的幫助。

首先說一下大前提,我設定的視訊錄製引數為:

mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);

mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);

為了讓大家更加明白,我先貼出avcC的資料結構:

  1. aligned(8) class AVCDecoderConfigurationRecord {  
  2.    unsigned int(8) configurationVersion = 1;  
  3.    unsigned int(8) AVCProfileIndication;  
  4.    unsigned int(8) profile_compatibility;  
  5.    unsigned int(8) AVCLevelIndication;  
  6.    bit(6) reserved = '111111'b;  
  7.    unsigned int
    (2) lengthSizeMinusOne;  
  8.    bit(3) reserved = '111'b;  
  9.    unsigned int(5) numOfSequenceParameterSets;  
  10.    for (i=0; i< numOfSequenceParameterSets; i++) {  
  11.       unsigned int(16) sequenceParameterSetLength ;  
  12.       bit(8*sequenceParameterSetLength) sequenceParameterSetNALUnit;  
  13.    }  
  14.    unsigned int
    (8) numOfPictureParameterSets;  
  15.    for (i=0; i< numOfPictureParameterSets; i++) {  
  16.       unsigned int(16) pictureParameterSetLength;  
  17.       bit(8*pictureParameterSetLength) pictureParameterSetNALUnit;  
  18.    }  
  19. }  

ok,資料結構貼出來了,我再貼出錄製的3gp輸出型別的h264碼流片斷。

陰影部分就是avcC的全部資料了。

其中: 0x61 0x76 0x63 0x43 就是字元avcC

0x01 是configurationVersion 

0x42 是AVCProfileIndication

0x00 是profile_compatibility

0x1F是AVCLevelIndication

0xFF 是6bit的reserved 和2bit的lengthSizeMinusOne

0xE1 是3bit的reserved 和5bit的numOfSequenceParameterSets

0x00 0x09是sps的長度為9個位元組。

故SPS的內容為接下來的9個位元組:67 42 00 1f e9 02 c1 2c 80 

接下來的:01為numOfPictureParameterSets

0x00和0x04是pps的長度為4個位元組。

故PPS的內容為接下來的4個位元組:68 ce 06 f2 

通過這段資料片斷,就可以獲取到SPS和PPS了。

下面我將貼上用java程式碼獲取輸出格式為3gp的h264碼流的SPS與PPS程式碼:

  1. package cn.edu.xmu.zgy;  
  2. import java.io.File;  
  3. import java.io.FileInputStream;  
  4. import java.io.IOException;  
  5. publicclass ObtainSPSAndPPS {  
  6.     publicvoid getSPSAndPPS(String fileName) throws IOException {  
  7.         File file = new File(fileName);  
  8.         FileInputStream fis = new FileInputStream(file);  
  9.         int fileLength = (int) file.length();  
  10.         byte[] fileData = newbyte[fileLength];  
  11.         fis.read(fileData);  
  12.         // 'a'=0x61, 'v'=0x76, 'c'=0x63, 'C'=0x43
  13.         byte[] avcC = newbyte[] { 0x610x760x630x43 };  
  14.         // avcC的起始位置
  15.         int avcRecord = 0;  
  16.         for (int ix = 0; ix < fileLength; ++ix) {  
  17.             if (fileData[ix] == avcC[0] && fileData[ix + 1] == avcC[1]  
  18.                     && fileData[ix + 2] == avcC[2]  
  19.                     && fileData[ix + 3] == avcC[3]) {  
  20.                 // 找到avcC,則記錄avcRecord起始位置,然後退出迴圈。
  21.                 avcRecord = ix + 4;  
  22.                 break;  
  23.             }  
  24.         }  
  25.         if (0 == avcRecord) {  
  26.             System.out.println("沒有找到avcC,請檢查檔案格式是否正確");  
  27.             return;  
  28.         }  
  29.         // 加7的目的是為了跳過
  30.         // (1)8位元組的 configurationVersion
  31.         // (2)8位元組的 AVCProfileIndication
  32.         // (3)8位元組的 profile_compatibility
  33.         // (4)8 位元組的 AVCLevelIndication
  34.         // (5)6 bit 的 reserved
  35.         // (6)2 bit 的 lengthSizeMinusOne
  36.         // (7)3 bit 的 reserved
  37.         // (8)5 bit 的numOfSequenceParameterSets
  38.         // 共6個位元組,然後到達sequenceParameterSetLength的位置
  39.         int spsStartPos = avcRecord + 6;  
  40.         byte[] spsbt = newbyte[] { fileData[spsStartPos],  
  41.                 fileData[spsStartPos + 1] };  
  42.         int spsLength = bytes2Int(spsbt);  
  43.         byte[] SPS = newbyte[spsLength];  
  44.         // 跳過2個位元組的 sequenceParameterSetLength
  45.         spsStartPos += 2;  
  46.         System.arraycopy(fileData, spsStartPos, SPS, 0, spsLength);  
  47.         printResult("SPS", SPS, spsLength);  
  48.         // 底下部分為獲取PPS
  49.         // spsStartPos + spsLength 可以跳到pps位置
  50.         // 再加1的目的是跳過1位元組的 numOfPictureParameterSets
  51.         int ppsStartPos = spsStartPos + spsLength + 1;  
  52.         byte[] ppsbt = newbyte[] { fileData[ppsStartPos],  
  53.                 fileData[ppsStartPos + 1] };  
  54.         int ppsLength = bytes2Int(ppsbt);  
  55.         byte[] PPS = newbyte[ppsLength];  
  56.         ppsStartPos += 2;  
  57.         System.arraycopy(fileData, ppsStartPos, PPS, 0, ppsLength);  
  58.         printResult("PPS", PPS, ppsLength);  
  59.     }  
  60.     privateint bytes2Int(byte[] bt) {  
  61.         int ret = bt[0];  
  62.         ret <<= 8;  
  63.         ret |= bt[1];  
  64.         return ret;  
  65.     }  
  66.     privatevoid printResult(String type, byte[] bt, int len) {  
  67.         System.out.println(type + "長度為:" + len);  
  68.         String cont = type + "的內容為:";  
  69.         System.out.print(cont);  
  70.         for (int ix = 0; ix < len; ++ix) {  
  71.             System.out.printf("%02x ", bt[ix]);  
  72.         }  
  73.         System.out.println("\n----------");  
  74.     }  
  75.     publicstaticvoid main(String[] args) throws IOException {  
  76.         new ObtainSPSAndPPS().getSPSAndPPS("c:\\zgy.h264");  
  77.     }  
  78. }  

執行結果如下:

  1. SPS長度為:9  
  2. SPS的內容為:67 42 00 1f e9 02 c1 2c 80   
  3. ----------  
  4. PPS長度為:4  
  5. PPS的內容為:68 ce 06 f2   
  6. ----------  

需要下載原始碼以及我使用的h264碼流片斷的朋友可以點選這裡下載

看完希望您能有所收穫 ^_^