资讯

精准传达 • 有效沟通

从品牌网站建设到网络营销策划,从策略到执行的一站式服务

FFmpeg简单使用:解封装h264----提取SPSPPS-创新互联

前言

成都创新互联是一家专业提供抚州企业网站建设,专注与网站设计、成都网站建设、H5技术、小程序制作等业务。10年已为抚州众多企业、政府机构等服务。创新互联专业网站设计公司优惠进行中。

我们从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

你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧


分享文章:FFmpeg简单使用:解封装h264----提取SPSPPS-创新互联
本文网址:http://cdkjz.cn/article/dojdjp.html
多年建站经验

多一份参考,总有益处

联系快上网,免费获得专属《策划方案》及报价

咨询相关问题或预约面谈,可以通过以下方式与我们联系

业务热线:400-028-6601 / 大客户专线   成都:13518219792   座机:028-86922220