当前位置: 首页 > news >正文

FFMPEG H264

一、H264压缩编码

1.1 H264 中的 I 帧、P帧和 B帧

H264 使用帧内压缩和帧间压缩的方式提高编码压缩率;H264 采用了独特的 I 帧、P 帧和 B 帧策略来实现,连续帧之间的压缩;

1.2 其他概念

GOP(图像组):一个IDR帧到下一个IDR帧之间间隔了多少个帧

IDR:一个序列的第一个图像叫做IDR图像(立即刷新图像),IDR图像都是I帧图像。I帧不用参考任何帧,但之后的P帧和B帧是有可能参考这个I帧之前的帧。IDR帧不允许后面PB参考前面的帧。IDR的核心作用是为了序列出现重大错误后重新同步,不用参考IDR之前的图像的数据来解码

1.3 NLU

视频流至少发从一帧SPS和一帧PPS来告诉客户端视频流信息

H.264原始码流(裸流)是由⼀个接⼀个NALU组成。它的功能分为两层,VCL (视频编码层) 和
NAL (⽹络提取层)。VCL:负责压缩视频;NAL:把VCL压缩的视频封装成NLU帧

1、NLU帧包括三个部分

[StartCode] [NALU Header] [NALU Payload]

StartCode必须是 "00 00 00 01" 或 "00 00 01"

NALU Header是NALU的头部

RBSP是I、P、B帧的数据。

NLU帧的结束也是根据00 00 00 01或00 00 01来判断的

3字节的0x000001只有⼀种场合下使⽤,就是⼀个完整的帧被编为多个slice(⽚)的时
候,包含这些sliceNALU 使⽤3字节起始码。其余场合都是4字节0x00000001的。前面的I帧就是分别存储在两个NALU中,这两个NALU的开头就是00 00 01

2、NLU Header第一个字节

其中:
T为负荷数据类型,占5bit
nal_unit_type:这个NALU单元的类型,112H.264使⽤,2431H.264以外的应⽤
使⽤
R为重要性指示位,占2bit
nal_ref_idc.:取00~11,似乎指示这个NALU的重要性,00NALU解码器可以丢弃它⽽不
影响图像的回放,03,取值越⼤,表示当前NAL越重要,需要优先受到保护。如果当前
NAL是属于参考帧的⽚,或是序列参数集,或是图像参数集这些重要的单位时,本句法元
素必需⼤于0
最后的F为禁⽌位,占1bit
forbidden_zero_bit: 在 H.264 规范中规定了这⼀位必须为 0.

3、NLU的类型

0x00 00 00 01 67
67
⼆进制:0110 0111   
00111 = 7(⼗进制)
nal_unit_typeNAL单元和RBSP语法结构的内容
0未指定
1一个非IDR图像的编码条带slice_layer_without_partitioning_rbsp( )
2
编码条带数据分割块A slice_data_partition_a_layer_rbsp( )
3
编码条带数据分割块B slice_data_partition_b_layer_rbsp( )
4
编码条带数据分割块C slice_data_partition_c_layer_rbsp( )
5
IDR图像的编码条带() slice_layer_without_partitioning_rbsp ( )
6
辅助增强信息 (SEI) sei_rbsp( )
7
序列参数集 seq_parameter_set_rbsp( )
8
图像参数集 pic_parameter_set_rbsp( )
9
访问单元分隔符 access_unit_delimiter_rbsp( )
10
序列结尾 end_of_seq_rbsp( )
11
流结尾  end_of_stream_rbsp( )
12
填充数据 filler_data_rbsp( )
13
序列参数集扩展9 seq_parameter_set_extension_rbsp( )
14...18
保留
19
未分割的辅助编码图像的编码条带 slice_layer_without_partitioning_rbsp( )
20...23
保留
24...31
未指定

1.4 H264 annexb模式

H264有两种封装

⼀种是Annex B模式,传统模式,有startcodeSPSPPS是在ES
⼀种是mp4模式,⼀般mp4 mkv都是mp4模式,没有startcodeSPSPPS以及其它信息
被封装在container中,每⼀个frame前⾯4个字节是这个frame的⻓度。很多解码器只⽀持Annex B这种模式,因此需要将mp4做转换:ffmpeg中⽤ h264_mp4toannexb_filter可以做转换
实现:
const AVBitStreamFilter *bsfilter = av_bsf_get_by_name("h264_mp4toannexb");
AVBSFContext *bsf_ctx = NULL;
// 2 初始化过滤器上下⽂
av_bsf_alloc(bsfilter, &bsf_ctx); //AVBSFContext;
// 3 添加解码器属性
avcodec_parameters_copy(bsf_ctx->par_in, ifmt_ctx->streams[videoindex]->codecpar);
av_bsf_init(bsf_ctx);

1. Annex B内部数据,对应的了前面讲解的内容

[4字节起始码] + [SPS NALU数据] 
[4字节起始码] + [PPS NALU数据] 
[3字节起始码] + [I帧NALU数据] 
[3字节起始码] + [P帧NALU数据] 
[3字节起始码] + [B帧NALU数据] 
...(后续NALU以此类推)

输出的文件是H.264视频文件

2. MP4直接的码流对应下图右边,开头记录的是NALU帧的长度,都是4字节

二、MP4->H.264代码

#include <iostream>extern "C"{#include <libavutil/log.h>#include <libavformat/avio.h>#include <libavformat/avformat.h>#include <libavcodec/bsf.h> 
}using namespace std;static char err_buf[128] = {0};
static char* av_get_err(int errnum)
{av_strerror(errnum, err_buf, 128);return err_buf;
}int main(int argc, char **argv) {string inputFile = "believe.mp4";string outputFile = "believe.h264"; FILE * outfp = fopen(outputFile.c_str(),"wb");printf("in:%s out:%s\n", inputFile.c_str(), outputFile.c_str());// 创建AVFormatContext上下文AVFormatContext * ifmt_ctx = avformat_alloc_context();if (!ifmt_ctx) {printf("[error] Could not allocate context.\n");return -1;}int ret = avformat_open_input(&ifmt_ctx, inputFile.c_str(), NULL, NULL);if (ret != 0) {printf("[error] avformat_open_input: %s\n", av_get_err(ret));return -1;}ret = avformat_find_stream_info(ifmt_ctx, NULL);if (ret < 0) {printf("[error] avformat_find_stream_info: %s\n", av_get_err(ret));avformat_close_input(&ifmt_ctx);return -1;}// 查找出哪个码流是video/audio/subtitlesint videoindex = -1;videoindex = av_find_best_stream(ifmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);if(videoindex == -1) {printf("[error] Didn't find a video stream.\n");avformat_close_input(&ifmt_ctx);return -1;}// 创建AVPacket包AVPacket * pkt = av_packet_alloc();av_init_packet(pkt);// 1 获取一个名为"h264_mp4toannexb"的比特流过滤器// 用于将MP4容器中的H.264视频流转换为Annex B格式的H.264流const AVBitStreamFilter * bsfilter = av_bsf_get_by_name("h264_mp4toannexb");AVBSFContext * bsf_ctx = NULL;// 2 初始化过滤器上下文av_bsf_alloc(bsfilter, &bsf_ctx); //AVBSFContext;// 3 复制视频流的编解码参数到过滤器上下文avcodec_parameters_copy(bsf_ctx->par_in, ifmt_ctx->streams[videoindex]->codecpar);av_bsf_init(bsf_ctx);int file_end = 0;while (0 == file_end) {if((ret = av_read_frame(ifmt_ctx, pkt)) < 0) {// 没有更多包可读file_end = 1;printf("read file end: ret:%d\n", ret);}if(ret == 0 && pkt->stream_index == videoindex) {int input_size = pkt->size;int out_pkt_count = 0;if (av_bsf_send_packet(bsf_ctx, pkt) != 0) // bitstreamfilter内部去维护内存空间{av_packet_unref(pkt);   // 你不用了就把资源释放掉continue;       // 继续送}av_packet_unref(pkt);   // 释放资源while(av_bsf_receive_packet(bsf_ctx, pkt) == 0) {out_pkt_count++;// printf("fwrite size:%d\n", pkt->size);size_t size = fwrite(pkt->data, 1, pkt->size, outfp);if(size != pkt->size){printf("fwrite failed-> write:%u, pkt_size:%u\n", size, pkt->size);}av_packet_unref(pkt);}if(out_pkt_count >= 2){printf("cur pkt(size:%d) only get 1 out pkt, it get %d pkts\n", input_size, out_pkt_count);}// 注释掉前面代码,可以直接保存mp4流// size_t size = fwrite(pkt->data, 1, pkt->size, outfp);// if(size != pkt->size)// {//     printf("fwrite failed-> write:%u, pkt_size:%u\n", size, pkt->size);// }// av_packet_unref(pkt);}else {if(ret == 0) av_packet_unref(pkt); // 释放内存}}if (outfp) fclose(outfp);if (bsf_ctx) av_bsf_free(&bsf_ctx);if (pkt) av_packet_free(&pkt);if (ifmt_ctx) avformat_close_input(&ifmt_ctx);printf("finish\n");return 0;
}


文章转载自:

http://8AlIMHRS.qtLtg.cn
http://MsUIA3xc.qtLtg.cn
http://HQKE0wgM.qtLtg.cn
http://Oc6OrRJD.qtLtg.cn
http://bNr7Tjwo.qtLtg.cn
http://CMl78Kki.qtLtg.cn
http://8wsaW1w7.qtLtg.cn
http://4GIJL3Uv.qtLtg.cn
http://gCJEMcJt.qtLtg.cn
http://9VeXyEeN.qtLtg.cn
http://rzI16Bgm.qtLtg.cn
http://JmCYRMlT.qtLtg.cn
http://Ep25ADVj.qtLtg.cn
http://qqgBtPYU.qtLtg.cn
http://WwCu66E9.qtLtg.cn
http://npcSK7b3.qtLtg.cn
http://MCX9kXUU.qtLtg.cn
http://xJwIFkfY.qtLtg.cn
http://xgQXMjgQ.qtLtg.cn
http://jZizLXtl.qtLtg.cn
http://Xe46z1TN.qtLtg.cn
http://slhlKKDZ.qtLtg.cn
http://y9ZuydMr.qtLtg.cn
http://zEtIqrnM.qtLtg.cn
http://AqIzTYwO.qtLtg.cn
http://1AX0VXbH.qtLtg.cn
http://Bno3n8ga.qtLtg.cn
http://pHTBtcNn.qtLtg.cn
http://JgxVt6X2.qtLtg.cn
http://8QnMroQj.qtLtg.cn
http://www.dtcms.com/a/365890.html

相关文章:

  • @Resource与@Autowired的区别
  • Parasoft C/C++test案例:基于CERT/CWE的代码合规自动化
  • 万家灯火背后的守护者:耐达讯自动化RS485转Profinet如何让石化生产“零隐患”
  • Java 的 Stream 流太难用了?——一名开发者的真实体验
  • Linux 的 swap 是什么
  • 1.0 机械加工基础-1-表面粗糙度、公差、几何公差
  • uni app 的app 端调用tts 进行文字转语音
  • LeetCode 392.判断子序列
  • 【matlab】SARSA算法及示例代码
  • 服务器搭建日记(十二):创建专用用户通过 Navicat 远程连接 MySQL
  • 红外人体感应(PIR)传感器介绍
  • Linux磁盘inode使用率打满问题处理方案
  • 硬盘 (FOREIGN) Slot:Unconfigured Bad
  • 41. 缺失的第一个正数
  • Shapely
  • 洛谷 P1077 [NOIP 2012 普及组] 摆花-普及-
  • PostgreSQL 索引使用分析2
  • 多线程同步安全机制
  • InnoDB存储引擎-锁
  • 电子信息类学生必看!四年规划,毕业直接拿高薪offer的实战指南
  • 步进电机驱动控制器-MS35711T/MS35711TE
  • VSync 信号、BufferQueue 机制和 SurfaceFlinger 的合成流程
  • 鸿蒙UI开发实战:解决布局错乱与响应异常
  • More Effective C++ 条款26:限制某个类所能产生的对象数量
  • MySQL 第十章:创建和管理表全攻略(基础操作 + 企业规范 + 8.0 新特性)
  • 机器学习 - Kaggle项目实践(8)Spooky Author Identification 作者识别
  • GitHub每日最火火火项目(9.3)
  • 杂记 09
  • 涨粉5万,Coze智能体工作流3分钟一键生成猫咪打工视频,无需剪辑
  • Matlab使用小技巧合集(系列二):科研绘图与图片排版终极指南