FFmpeg之二 摄像头录制保存视频, API编解码从理论到实战
这篇文章,详细讲解了用FFmpeg的API代码的方式,如何把摄像头的录制的视频,保存为MP4、YUV格式,会详细介绍视频的相关知识,已经遇到的问题文章目录YUVYUV采样格式与RGB比较YUV格式间的转换视频的比特率(bit_rate)、帧率(framerate)、分辨率I、B、P帧time_base三种时间基 tbr、tbn 和 tbcPTS、DTSPTS的转换对frame的pts,进行解码器-编码器的时间基转换编码后,对packet,进行编码器-容器的时间基转换录制视频的代码:YUVYUV中 “Y” 表示明亮度( Luminance 或 Luma ),也就是灰阶值;而 “U” 和 “V” 表示的则是色度( Chrominance 或 Chroma ),作用是描述影像色彩及饱和度,用于指定像素的颜色。早期技术不发达的时候,照片打印和电视播放都只能实现黑白的灰度显示,所以Y数据就成了标准。之后才有了彩色打印和彩色电视,为了兼容之前的黑白数据,发明了UV数据。YUV一起就可以实现彩色,只用Y数据,可实现黑白画面,这样一套数据格式就同时兼容了黑白设备和彩色设备。人眼对亮点信息更敏感。也就是说,可以压缩UV数据,而人眼难以发现。把RGB数据转换成YUV数据。对Y少压缩一点,对UV多压缩一点,以平衡图像效果和压缩率。YUV采样格式#NV12、NV21的存储格式为Y平面,UV打包一个平面,共两个平面,即:先连续存储Y,然后连续交叉存储UV# 不同点在于UV的排列顺序,SP为Semi-Planar的缩写 #NV12:先是 w*h 长度的Y,后面跟 w*h*0.5长度的UV(交叉存储),总长度为 w*h*1.5#NV21:先是 w*h 长度的Y,后面跟 w*h*0.5长度的VU(交叉存储),总长度为 w*h*1.5NV12:YYYYYYYYUVUV=YUV420SP# iOS 平台常用NV12NV21:YYYYYYYYVUVU=YUV420SP#Android平台常用NV21#I420、YV12三个分量均为平面格式,共三个平面,即:先连续存储Y,然后连续存储U,最后连续存储V# 不同点在于UV的排列顺序,P为Planar的缩写 #I420:先是 w*h 长度的Y,后面跟 w*h*0.25长度的U, 最后是 w*h*0.25长度的V,总长度为 w*h*1.5#YV12:先是 w*h 长度的Y,后面跟 w*h*0.25长度的V, 最后是 w*h*0.25长度的U,总长度为 w*h*1.5I420:YYYYYYYYUUVV=YUV420P#Android平台常用I420YV12:YYYYYYYYVVUU=YUV420P按照颜色可找到Y对应的UV值,以上图的NV12为例, 每个像素点都有一个Y值,像素1、2、7、8位置的Y值是Y1、Y2,、Y7、Y8(蓝色) 对应的UV是U1,V1(蓝色),像素1=Y1U1V1, 像素2=Y2U1V1,以此类推。知道了存储格式,就可以把上面二维的YUV值(上图只是为了方便理解),连续存储了,如下图:与RGB比较一帧width*height的图像RGB格式,R/G/B各需要1byte(8bit),总存储空间=width*height*3byte,YUV420P格式,Y存储空间 = width*height*1byte ,一个U用于4个像素,平均到单个像素用到1/4个U,U存储空间 = width*height*1/4byte,同理V存储空间 = width*height*1/4byte,总存储空间= width*height*(1byte + 1/4byte + 1/4byte),占用空间比RGB减少了一半。YUV格式间的转换例如:NV12 转 YUV420P#includestdint.h#includestring.hvoidnv12_to_yuv420p(uint8_t*src_yuv,uint8_t*dst_yuv,intwidth,intheight){inty_size=width*height;intuv_size=y_size/2;// NV12 UV plane size (interleaved)intu_size=y_size/4;// YUV420P U or V plane sizeinti;// Copy Y plane directly (identical in both formats)memcpy(dst_yuv,src_yuv,y_size);// Pointers to U and V planes in YUV420Puint8_t*dst_u=dst_yuv+y_size;// U plane starts after Yuint8_t*dst_v=dst_yuv+y_size+u_size;// V plane starts after U// Pointer to NV12 UV planeuint8_t*src_uv=src_yuv+y_size;// Deinterleave NV12 UV plane into separate U and V planesfor(i=0;iuv_size/2;i++){dst_u[i]=src_uv[2*i];// U values are at even indicesdst_v[i]=src_uv[2*i+1];// V values are at odd indices}}从转换代码可以看出,YUV只有像素的数据,所有YUV格式保存到文件,没有包装头,读取后可直接对YUV操作,所以视频的原始数据就是YUV视频的比特率(bit_rate)、帧率(framerate)、分辨率帧率每秒中视频帧数量 ,单位为 fps(frame per second), 帧率越高 , 视频越流畅 ;在ffmpeg中,为编码器设置帧率// 设置帧率encoder_codec_ctx-time_base=(AVRational){1,30};encoder_codec_ctx-framerate=(AVRational){30,1};framerate 和 time_base 值互为倒数,time_base 是每一帧视频的持续时间,此处是1/30s,framerate 是30fps。 后续需要根据解码器 time_base和编码器的time_base,来计算pts分辨率分辨率 = 宽度的像素点数* 高度的像素点数比特率每秒的bit数量,单位为 bps(bit per second)。 一个文件的比特率 =文件大小(字节)X8/ 文件时长(秒))分辨率越大、帧率越大,都会使比特率增大。为了保证视频的观看体验,这三者关系如下:I、B、P帧想起了大学时做的一款桌面远程控制软件,把桌面一张一张截图,发送过来,那个延时,适合树獭用。视频也是一个个帧,连续播放的效果,其中两帧之间,大部分时间差异很小,所以可以通过算法来提取一下相邻两帧之间的不变的画面,减少重复数据量。这个算法比较流行的就是H.264,经过压缩后,帧分为 I、B、P帧通俗理解:I帧 就是完整的一帧图片,P帧 是与它前面I帧或P帧的差异部分,可以与前帧结合,还原出一帧图。B帧 需要与前后的I帧或P帧,进行解析,还原出一帧图。I帧:帧内编码帧(intra picture),又称全帧压缩编码帧。I帧通常是每个GOP(MPEG所使用的一种视频压缩技术)的第一个帧,经过适度地压缩,作为随机访问的参考点,可以当成静态图像。P帧:前向预测编码帧(predictive-frame),通常将图像序列中前面已经编码帧的冗余信息去除,达到压缩传输数据量的效果,也称为预测帧。B帧:双向预测内插编码帧(bi-directional interpolated prediction frame),既考虑源图像序列前面的已编码帧,又顾及源图像序列后面的已编码帧之间的冗余信息,来压缩传输数据量的编码图像,也称为双向预测帧。以图中“B[1]”帧为例进行说明,“B[1]”帧解码时需要参考“I[0]”帧和“P[3]”帧,因此“P[3]”帧必须比“B[1]”帧先解码。这就导致了解码顺序和显示顺序的不一致,后显示的帧需要先解码。采集顺序指图像传感器采集原始信号得到图像帧的顺序。编码顺序指编码器编码后图像帧的顺序。存储到磁盘的本地视频文件中图像帧的顺序与编码顺序相同。传输顺序指编码后的流在网络中传输过程中图像帧的顺序。解码顺序指解码器解码图像帧的顺序。显示顺序指图像帧在显示器上显示的顺序。采集顺序与显示顺序相同。编码顺序、传输顺序和解码顺序相同。直播时,不使用B帧,因为解析B帧,需要先接收到更后面的I/P帧,降低了实时性。直播秒开,也可以通过设置更小的GOP(一般是5),能更快的找到I帧,显示出画面在没有B帧的情况下,存放帧的顺序和显示帧的顺序就是一样的,PTS和DTS的值也是一样的。time_base在 FFmpeg 中,时间基(time_base)是时间戳(timestamp)的单位,时间戳值乘以时间基,可以得到实际的时刻值(以秒等为单位)。例如,如果一个视频帧的 dts 是 40,pts 是 160,其 time_base 是 1/1000 秒,那么可以计算出此视频帧的解码时刻是 40 毫秒(40/1000),显示时刻是 160 毫秒(160/1000)。三种时间基 tbr、tbn 和 tbc使用ffmpeg 、ffplay 操作视频时,经常会看到视频的这些参数,它们是不同的时间基不同的封装格式具有不同的时间基。在 FFmpeg 处理音视频过程中的不同阶段,也会采用不同的时间基。FFmepg 中有三种时间基,命令行中 tbr、tbn 和 tbc 的打印值就是这三种时间基的倒数:tbn:对应容器中的时间基。值是 AVStream.time_base 的倒数tbc:对应编解码器中的时间基。值是 AVCodecContext.time_base 的倒数tbr:从视频流中猜算得到,可能是帧率或场率(帧率的 2 倍)PTS、DTSDTS(Decoding Time Stamp, 解码时间戳),表示压缩帧的解码时间。PTS(Presentation Time Stamp, 显示时间戳),表示将压缩帧解码后得到的原始帧的显示时间。音频中 DTS 和 PTS 是相同的。视频中由于 B 帧需要双向预测,B 帧依赖于其前和其后的帧,因此含 B 帧的视频解码顺序与显示顺序不同,即 DTS 与 PTS 不同。不含 B 帧的视频,其 DTS 和 PTS 是相同的。PTS的转换编码器、解码器、要写入的容器(例如写到MP4的容器) 都有自己的time_base,上面说过,pts、dts的值的单位是time_base,那么如果time_base不相同,pts还是原样复制过去,播放时就会有问题。如果是录制视频,pts的起点不是从0开始,如不处理,会导致录制完的视频时间特别长。下面以摄像头录制文件为例,介绍pts的处理:对frame的pts,进行解码器-编码器的时间基转换staticvoidprocess_frame_use_decode(AVPacket*pkt,AVFrame*decoded_frame,AVFrame*converted_frame,AVCodecContext*av_decoder_ctx,AVCodecContext*h264_encoder_ctx,AVFormatContext*ofmtCtx,structSwsContext*sws_ctx,intframeIndex){//av_read_frame 接收到的是原始数据,也可以解码后再发送给编码器。如果接收到是编码后的数据packet,需要解码后再发送给编码器intret=avcodec_send_packet(av_decoder_ctx,pkt);if(ret0){printf("Decode error.\n");return;}if(avcodec_receive_frame(av_decoder_ctx,decoded_frame)=0){// frame的格式,转换成YUV420Psws_scale(sws_ctx,decoded_frame-data,decoded_frame-linesize,0,av_decoder_ctx-height,converted_frame-data,converted_frame-linesize);// Store the first PTS valueif(first_pts==AV_NOPTS_VALUE){first_pts=decoded_frame-pts;}//打印av_decoder_ctx-time_base、 h264_encoder_ctx-time_baseprintf("av_decoder_ctx time_base: %d/%d\n",av_decoder_ctx-time_base.num,av_decoder_ctx-time_base.den);printf("h264_encoder_ctx time_base: %d/%d\n",h264_encoder_ctx-time_base.num,h264_encoder_ctx-time_base.den);// 得到相对于起始时间first_pts的PTS,pts从0开始,保证了视频时长的正确性converted_frame-pts=decoded_frame-pts-first_pts;// 将 decoded_frame 的 PTS 进行时间基转换,传递给 converted_frameconverted_frame-pts=av_rescale_q(converted_frame-pts,av_decoder_ctx-time_base,h264_encoder_ctx-time_base);// 打印pts,pkt_dts,// frame 的pkt_dts 是取自packet的dts,此处打印pkt_dts,意义不大,只是想看看// 经过h264编码后,会自动设置packet的dtsprintf("decoded_frame pts: %ld, pkt_dts: %ld\n",decoded_frame-pts,decoded_frame-pkt_dts);printf("converted_frame pts: %ld, pkt_dts: %ld\n",converted_frame-pts,converted_frame-pkt_dts);encode_and_write_frame(h264_encoder_ctx,converted_frame,ofmtCtx,pkt,frameIndex);}}转换后的一系列 converted_frame-pts,是0、1、2、3、、、,说明转换对了编码后,对packet

相关新闻

UE5 Sequencer动画制作全流程实战:从关键帧到渲染输出

UE5 Sequencer动画制作全流程实战:从关键帧到渲染输出

1. 项目概述:从蓝图到银幕的UE5动画革命如果你是一名游戏开发者、影视动画师,或者任何对用引擎制作高质量动画感兴趣的人,那么UE5的Sequencer绝对是你绕不开的核心工具。它早已超越了早期版本中简单的过场动画编辑器定位,进化成了…

2026/8/2 20:03:06阅读更多 →
LaTeX编译报错:\Bbbk already defined 的深度解析与解决方案

LaTeX编译报错:\Bbbk already defined 的深度解析与解决方案

1. 问题引入:一个看似简单却令人困惑的报错如果你正在用LaTeX撰写数学论文、技术报告或者任何包含大量数学符号的文档,那么对amssymb宏包一定不陌生。它是AMS(美国数学学会)提供的一套标准数学符号扩展,包含了像\mathb…

2026/8/2 20:03:06阅读更多 →
计算机网络基础:Windows 与 Linux 网络配置

计算机网络基础:Windows 与 Linux 网络配置

计算机网络基础:Windows 与 Linux 网络配置 一、前言 二、网络基础概念 2.1 IP 地址与子网掩码 2.2 网关与 DNS 2.3 网络协议(TCP/IP, UDP, ICMP) 2.4 网络接口与路由表 三、Windows 网络配置 3.1 图形界面配置 3.1.1 配置 IP 地址 3.1.2 配置 DNS 3.1.3 配置网关 3.2 命令行…

2026/8/2 20:03:06阅读更多 →
GMR:机器人运动重定向技术革命 - 通用化运动迁移的突破性解决方案

GMR:机器人运动重定向技术革命 - 通用化运动迁移的突破性解决方案

GMR:机器人运动重定向技术革命 - 通用化运动迁移的突破性解决方案 【免费下载链接】GMR [ICRA 2026] GMR: General Motion Retargeting. Retarget human motions into diverse humanoid robots in real time on CPU. Retargeter for TWIST. 项目地址: https://git…

2026/8/2 21:27:39阅读更多 →
如何快速上手Infisical:面向新手的完整密钥管理指南

如何快速上手Infisical:面向新手的完整密钥管理指南

如何快速上手Infisical:面向新手的完整密钥管理指南 【免费下载链接】infisical Infisical is the open-source platform for secrets, certificates, and privileged access management. 项目地址: https://gitcode.com/GitHub_Trending/in/infisical Infis…

2026/8/2 21:27:39阅读更多 →
Windows 11极致精简实战:使用tiny11builder打造高效开发环境

Windows 11极致精简实战:使用tiny11builder打造高效开发环境

Windows 11极致精简实战:使用tiny11builder打造高效开发环境 【免费下载链接】tiny11builder Scripts to build a trimmed-down Windows 11 image. 项目地址: https://gitcode.com/GitHub_Trending/ti/tiny11builder 在Windows 11系统日益臃肿的今天&#xf…

2026/8/2 21:27:39阅读更多 →
单片机毕设项目:基于 STM32/51 单片机的酒驾预防酒精检测硬件系统 融合声光提示与语音播报的单片机酒精超标报警设计(020401)

单片机毕设项目:基于 STM32/51 单片机的酒驾预防酒精检测硬件系统 融合声光提示与语音播报的单片机酒精超标报警设计(020401)

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于嵌入式单片机,Java、小程序技术领域和毕业项目实战 ✌️…

2026/8/2 21:27:39阅读更多 →
SharpXDecrypt:一键找回Xshell遗忘密码的终极解决方案

SharpXDecrypt:一键找回Xshell遗忘密码的终极解决方案

SharpXDecrypt:一键找回Xshell遗忘密码的终极解决方案 【免费下载链接】SharpXDecrypt Xshell全版本密码恢复工具 项目地址: https://gitcode.com/gh_mirrors/sh/SharpXDecrypt 你是否曾经因为忘记Xshell中保存的服务器密码而感到焦虑?当紧急需要…

2026/8/2 21:27:39阅读更多 →
PyQt-Fluent-Widgets:用50+现代化组件解决桌面应用UI开发难题

PyQt-Fluent-Widgets:用50+现代化组件解决桌面应用UI开发难题

PyQt-Fluent-Widgets:用50现代化组件解决桌面应用UI开发难题 【免费下载链接】PyQt-Fluent-Widgets A fluent design widgets library based on C Qt/PyQt/PySide. Make Qt Great Again. 项目地址: https://gitcode.com/gh_mirrors/py/PyQt-Fluent-Widgets 在…

2026/8/2 21:25:39阅读更多 →
MATLAB xcorr函数详解:从互相关原理到四大实战应用

MATLAB xcorr函数详解:从互相关原理到四大实战应用

1. 从一次信号“找茬”说起:为什么我们需要互相关几年前,我在处理一组声学传感器数据时遇到了一个棘手的问题。我有两个麦克风记录了一段相同的音频信号,理论上它们接收到的声音波形应该非常相似,只是由于麦克风位置不同&#xff…

2026/8/2 0:00:10阅读更多 →
限时公开!某头部SaaS公司内部AI模板工厂架构文档(含5类行业模板源码+性能压测报告)

限时公开!某头部SaaS公司内部AI模板工厂架构文档(含5类行业模板源码+性能压测报告)

更多请点击: https://intelliparadigm.com 第一章:AI模板批量生成的核心价值与落地全景 AI模板批量生成正从实验性工具演进为现代软件工程的关键基础设施。它通过语义理解、上下文感知与结构化约束,将重复性高、模式明确的代码/文档/配置生成…

2026/8/2 0:00:12阅读更多 →
如何快速找回消失的网页:Web Archives浏览器扩展终极指南

如何快速找回消失的网页:Web Archives浏览器扩展终极指南

如何快速找回消失的网页:Web Archives浏览器扩展终极指南 【免费下载链接】web-archives Browser extension for viewing archived and cached versions of web pages, available for Chrome, Edge and Safari 项目地址: https://gitcode.com/gh_mirrors/we/web-a…

2026/8/2 0:00:13阅读更多 →
MATLAB xcorr函数详解:从互相关原理到四大实战应用

MATLAB xcorr函数详解:从互相关原理到四大实战应用

1. 从一次信号“找茬”说起:为什么我们需要互相关几年前,我在处理一组声学传感器数据时遇到了一个棘手的问题。我有两个麦克风记录了一段相同的音频信号,理论上它们接收到的声音波形应该非常相似,只是由于麦克风位置不同&#xff…

2026/8/2 0:00:10阅读更多 →
限时公开!某头部SaaS公司内部AI模板工厂架构文档(含5类行业模板源码+性能压测报告)

限时公开!某头部SaaS公司内部AI模板工厂架构文档(含5类行业模板源码+性能压测报告)

更多请点击: https://intelliparadigm.com 第一章:AI模板批量生成的核心价值与落地全景 AI模板批量生成正从实验性工具演进为现代软件工程的关键基础设施。它通过语义理解、上下文感知与结构化约束,将重复性高、模式明确的代码/文档/配置生成…

2026/8/2 0:00:12阅读更多 →
如何快速找回消失的网页:Web Archives浏览器扩展终极指南

如何快速找回消失的网页:Web Archives浏览器扩展终极指南

如何快速找回消失的网页:Web Archives浏览器扩展终极指南 【免费下载链接】web-archives Browser extension for viewing archived and cached versions of web pages, available for Chrome, Edge and Safari 项目地址: https://gitcode.com/gh_mirrors/we/web-a…

2026/8/2 0:00:13阅读更多 →
无损视频剪辑终极指南:如何实现快速高效的多媒体处理

无损视频剪辑终极指南:如何实现快速高效的多媒体处理

无损视频剪辑终极指南:如何实现快速高效的多媒体处理 【免费下载链接】lossless-cut The swiss army knife of lossless video/audio editing 项目地址: https://gitcode.com/gh_mirrors/lo/lossless-cut 在数字媒体创作领域,视频编辑处理的质量损…

2026/8/2 1:29:34阅读更多 →
AI辅助本科论文写作:8大工具评测与高效使用指南

AI辅助本科论文写作:8大工具评测与高效使用指南

1. 本科生论文写作的AI辅助现状本科毕业论文是每个大学生必须跨越的一道坎。记得我当年写论文时,光是文献检索就花了整整两周时间,打印的参考文献堆满了半个书桌。如今AI技术的发展为学术写作带来了革命性变化,合理使用这些工具可以节省80%以…

2026/8/2 2:32:55阅读更多 →
如何快速配置大麦自动抢票系统:从零开始搭建Python抢票助手

如何快速配置大麦自动抢票系统:从零开始搭建Python抢票助手

如何快速配置大麦自动抢票系统:从零开始搭建Python抢票助手 【免费下载链接】ticket-purchase 大麦自动抢票,支持人员、城市、日期场次、价格选择 项目地址: https://gitcode.com/GitHub_Trending/ti/ticket-purchase 还在为抢不到热门演唱会门票…

2026/8/2 2:09:20阅读更多 →