FFmpeg SPS/PPS剖析

場景說明
            在解碼過程中,需要設置SPS/PPS等解碼信息,才能夠初始化解碼器。有兩種方式可以設置SPS/PPS,一種是手動指定SPS/PPS內容,指定AVCodecContext
結構體中extradata的值;一種是讓FFmpeg通過讀取傳輸數據來分析SPS/PPS信息,一般情況下在每一個I幀之前都會發送一個SPS幀和PPS幀


方法1)一般的攝像機都會在RTSP指令中SDP攜帶的SPS/PPS內容,例如海康IPC發送的RTSP指令中攜帶信息如下:sprop-parameter-sets=Z0IAKpY1QPAET8s3AQEBQAABwgAAV+QB,aM48gA==
分號前面是SPS,後面是PPS,這是base64編碼的結果,需要解碼,才能夠傳遞給extradata,並且還需要在SPS/PPS字符串前面添加起始碼(0x00 0x00 0x00 0x01)。補充一點SPS的類型是
0x67, PPS的類型是0x68

char szBase64SPSBuffer[] = { "Z0IAKpY1QPAET8s3AQEBQAABwgAAV+QB" };
uint8_t szDecodedSPS[128] = { 0 };
int nSPSLen = av_base64_decode(szDecodedSPS, szBase64SPSBuffer, sizeof(szBase64SPSBuffer));//nSPSLen=24

char szBase64PPSBuffer[] = { "aM48gA==" };
uint8_t szDecodedPPS[128] = { 0 };
int nPPSLen = av_base64_decode(szDecodedPPS, szBase64PPSBuffer, sizeof(szBase64PPSBuffer));//nPPSLen=4

將SPS/PPS拼湊的結果如下:
unsigned char szSPSPPS[] = { 0x00 ,0x00 ,0x01,0x67,0x42,0x00 ,0x2a ,0x96 ,0x35 ,0x40 ,0xf0 ,0x04 ,0x4f ,0xcb ,0x37 ,0x01 ,0x01 ,0x01 ,0x40 ,0x00 ,0x01 ,0xc2 ,0x00 ,0x00 ,0x57 ,0xe4 ,0x01 ,0x00 ,0x00 ,0x00 ,0x01 ,0x68 ,0xce ,0x3c ,0x80, 0x00};


然後拷貝SPS/PPS數據到AVFormatContext的extradata
unsigned char szSPSPPS[] = { 0x00 ,0x00 ,0x01,0x67,0x42,0x00 ,0x2a ,0x96 ,0x35 ,0x40 ,0xf0 ,0x04 ,0x4f ,0xcb ,0x37 ,0x01 ,0x01 ,0x01 ,0x40 ,0x00 ,0x01 ,0xc2 ,0x00 ,0x00 ,0x57 ,0xe4 ,0x01 ,0x00 ,0x00 ,0x00 ,0x01 ,0x68 ,0xce ,0x3c ,0x80, 0x00};
pFormatContext->streams[0]->codecpar->extradata_size = sizeof(szSPSPPS);
pFormatContext->streams[0]->codecpar->extradata = (uint8_t*)av_mallocz(pFormatContext->streams[0]->codecpar->extradata_size + AV_INPUT_BUFFER_PADDING_SIZE);
memcpy(pFormatContext->streams[0]->codecpar->extradata, szSPSPPS, sizeof(szSPSPPS));

通過avcodec_parameters_to_context將信息從pFormatContext->streams[0]->codecpar拷貝到m_pAVCodecContext
avcodec_open2函數在調用的時候,會解析extradata數據的內容


出錯異常

I:2018-01-08 14:23:00 ms:221:nal_unit_type: 7, nal_ref_idc: 3
I:2018-01-08 14:23:00 ms:221:nal_unit_type: 8, nal_ref_idc: 3
I:2018-01-08 14:23:00 ms:221:sps:0 profile:66/42 poc:0 ref:1 120x68 FRM 8B8 crop:0/0/0/8 VUI 420 1800/90000 b8 reo:-1
I:2018-01-08 14:23:00 ms:221:pps:0 sps:0 CAVLC slice_groups:1 ref:1/1  qp:26/26/0/0 LPAR   
I:2018-01-08 14:23:00 ms:222:deprecated pixel format used, make sure you did set range correctly
I:2018-01-08 14:23:00 ms:250:non-existing PPS 0 referenced

錯誤分析
        從av_log日誌可以看出已經正確的解析出來PPS,但是在進行avcodec_read_frame的時候 還是打印出錯non-existing PPS 0 referenced,這是什麼問題?
這主要是沒有在緩衝區中讀取到包含SPS/PPS信息的幀,而不是說解碼器沒有正確初始化SPS/PPS參數,這一點需要注意

展望

        從這裏就可以看出,實際上並沒有多大的必要手動設置SPS/PPS,因爲FFmpeg會從緩衝中分析出SPS/PPS,然後解析出碼流參數,估計唯一的一種極端情況是I幀之前沒有任何的SPS/PPS信息,或者需要很長時間纔會發送一幀包含SPS/PPS的信息


相關代碼定義
/**
 * This struct describes the properties of an encoded stream.
 
*
 * sizeof(AVCodecParameters) is not a part of the public ABI, this struct must
 
* be allocated with avcodec_parameters_alloc() and freed with
 
* avcodec_parameters_free().
 
*/
typedef struct AVCodecParameters {
/**
     * Extra binary data needed for initializing the decoder, codec-dependent.
     *
     
* Must be allocated with av_malloc() and will be freed by
     
* avcodec_parameters_free(). The allocated size of extradata must be at
     
* least extradata_size + AV_INPUT_BUFFER_PADDING_SIZE, with the padding
     * bytes zeroed.
     */

    uint8_t *extradata;
    
/**
     * Size of the extradata content in bytes.  
*/
    int      extradata_size;
}

描述說明
額外的二進制數據用來初始化解碼器,主要是初始化SPS/PPS,必須通過av_malloc進行內存分配,然後通過avcodec_parameters_free進行釋放。內存分配的大小必須是extradata_size的大小加上AV_INPUT_BUFFER_PADDING_SIZE二進制數據的真實長度大小保存在extradata_size

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