• 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
  • 相关阅读:
    Javascript的作用域、作用域链以及闭包
    C#当中的泛型和java中的对比
    MongoDB的主从复制和副本集
    MongoDB启动配置等
    JavaScript学习系列1 基础-变量
    Umbraco项目发布错误 --More than one type want to be a model for content type authorize
    项目中gulp使用发生的错误及解决
    JavaScript学习系列2一JavaScript中的变量作用域
    ASP.NET MVC中的ActionFilter介绍学习
    Razor中的 内容标记块语法
  • 原文地址:https://www.cnblogs.com/vczf/p/13844580.html
Copyright © 2020-2023  润新知