FFmpeg簡單使用:解封裝h264 ---- 提取SPS PPS


=====================================================

FFmpeg簡單使用:解封裝 ---- 基本流程

FFmpeg簡單使用:解封裝 ---- 提取aac

FFmpeg簡單使用:音頻解碼 ---- 提取pcm

FFmpeg簡單使用:視頻解碼 ---- 提取yuv

FFmpeg簡單使用:音頻編碼 ---- pcm轉aac

FFmpeg簡單使用:視頻編碼 ---- YUV轉H264

FFmpeg簡單使用:過濾器 ---- 視頻過濾

FFmpeg簡單使用:過濾器 ---- 視頻過濾2

FFmpeg簡單使用:過濾器 ---- h264_mp4toannexb

FFmpeg簡單使用:解封裝h264 ---- 提取SPS PPS

=====================================================

前言

我們從flv和mp4等文件解封裝讀取的AVPacket並沒有SPS、PPS數據,而是保存在 AVFormatContext -> streams -> codecpar -> extradata里面,下面我們打開一個flv文件看一下

 

 

 

 

分析一下這塊數據,起始位置:0980 大小:39 結束位置:09a6

前4個字節:

  0x01: version

  0x42: avc profile (首個SPS的第1個字節)

  0xc0: avc compatibility (首個SPS的第2個字節)

     0x28: avc level (首個SPS的第3個字節,可以發現后面0x0989位置的3個字,和這3個是一樣的)

第5個字節:

  0xff:

    6_bit: 默認111111

      2_bit: 數據長度-1:avcc格式是extradata | [length] [nalu]  | [length][nalu],這里length所占的字節數就是3(11)+ 1 = 4 

第6個字節:

  0xe1: [111 00001]

    3_bit: 默認 111

    5_bit: 接下來的sps或pps的個數::這里為1

第7 8個字節:

  0x00 0x18: 表示接下來sps或者pps的長度為24

第9個字節:

  0x67: [0110 0111] nalu_type為7,表示SPS,就是說從0988到099f這24個數據為sps

第33個字節:9 (sps_pos) + 24(sps_size)

  0x01: 接下來的sps或pps的個數::這里為1

第34 35字節:

  0x00 0x04: 表示接下來sps或者pps的長度為4

第36個字節:

  0x68: [0110 1000] nalu_type為8,表示PPS

編碼

int VideoSend::ParseExtradata(VideoState* is)
{
    uint8_t* start_data = is->video_st->codecpar->extradata;
    uint8_t* extradata = is->video_st->codecpar->extradata;
    int extradata_size = is->video_st->codecpar->extradata_size;
    std::cout << "extradata_size:" << extradata_size << std::endl;
    for (int i = 0; i < extradata_size; ++i) {
        printf(" %02x", *(extradata+i));
    }
    std::cout << std::endl;
    // 1. 跳過前4個字節
    extradata = extradata +4;
    
    // 2. byte_5:獲取nalu length所占字節數
    d->nalu_length_bytes = (*extradata++ & 0x03) + 1;
    std::cout << "nalu_length_bytes:" << (int)d->nalu_length_bytes << std::endl;

    int sps_seen = 0, pps_seen = 0;
    while ((extradata - start_data) < extradata_size) {
        // 3. byte_6:獲取下面sps/pps個數
        uint8_t item_nb = *extradata++ & 0x1f;
        std::cout << "item_nb:" << (int)item_nb << std::endl;
        if (!item_nb) {
            continue;
        }

        int ret;
        while(item_nb--){
            // 4. byte_7 byte_8: 表示sps或者pps長度
            uint16_t uint_size = (extradata[0] << 8) | extradata[1];
            std::cout << "uint_size: " << uint_size << std::endl;

            // 5. 數據異常
            if ((extradata - start_data + uint_size + 2) > extradata_size) {
                 std::cout << "extradata exception " << uint_size << std::endl;
                 goto failed;
            }

            // 6. byte_9: 檢查包類型
            uint8_t nalu_type =  extradata[2];
            nalu_type = nalu_type & 0x1f;
            std::cout << "nalu_type: " << (int)nalu_type << std::endl;

            // 7. sps
            if (nalu_type == 7 && !sps_seen) {
                std::cout << "--sps--" << std::endl;
                // a.分配內存
                if (ret = av_reallocp(&d->sps_pkt.buf, uint_size) < 0) {
                    goto failed;
                }
                // b.數據拷貝
                memcpy(d->sps_pkt.buf, extradata + 2, uint_size);
                sps_seen = 1;
                d->sps_pkt.size = uint_size;
            }

            // 8. pps
            if (nalu_type == 8 && !pps_seen) {
                std::cout << "--pps--" << std::endl;
                 // a.分配內存
                if (ret = av_reallocp(&d->pps_pkt.buf, uint_size) < 0) {
                    goto failed;
                }
                // b.數據拷貝
                memcpy(d->pps_pkt.buf, extradata + 2, uint_size);
                pps_seen = 1;
                d->pps_pkt.size = uint_size;
            }
            extradata = extradata + 2 + uint_size;
        }  
    }

    if (pps_seen && sps_seen) {
        std::cout << "sps:";
        for (int i = 0; i < d->sps_pkt.size; ++i) {
            printf(" %02x", d->sps_pkt.buf[i]);
        }
        std::cout << std::endl;

        std::cout << "pps:";
        for (int i = 0; i < d->pps_pkt.size; ++i) {
            printf(" %02x", d->pps_pkt.buf[i]);
        }
        std::cout << std::endl;
        return 0;
    }
  
failed:
    std::cout << "VideoSend::ParseExtradata failed." << std::endl;
    if (d->pps_pkt.buf) {
        av_free(d->pps_pkt.buf);
        d->pps_pkt.buf = NULL;
    }

    if (d->sps_pkt.buf) {
        av_free(d->sps_pkt.buf);
        d->sps_pkt.buf = NULL;
    }
    return -1;
}

輸出:

extradata_size:40
 01 42 c0 1e ff e1 00 18 67 42 c0 1e db 02 c0 c7 96 a1 00 00 03 00 01 00 00 03 00 32 8f 16 2e e0 01 00 05 68 ca 83 cb 20
nalu_length_bytes:4
item_nb:1
uint_size: 24
nalu_type: 7
--sps--
item_nb:1
uint_size: 5
nalu_type: 8
--pps--
sps: 67 42 c0 1e db 02 c0 c7 96 a1 00 00 03 00 01 00 00 03 00 32 8f 16 2e e0
pps: 68 ca 83 cb 20

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM