=====================================================
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