ffmpeg是音视频处理的c库, 音视频在网络传输过程中,由于数据量大,所有需要进行压缩
压缩目的为了去除冗余信息,冗余信息分为:
1、空间冗余:图像相邻像素之间有较强的相关性
2、时间冗余:视频序列的相邻图像之间内容相似
3、 编码冗余:不同像素值出现的概率不同
4、 视觉冗余:人的视觉系统对某些细节不敏感
5、知识冗余:规律性的结构可由先验知识和背景知识得到
● 无损压缩(Winzip)
压缩前解压缩后图像完全一致
压缩比低
● 有损压缩(H264)
压缩前解压缩后图像不一致
压缩比高
利用人的视觉系统的特性(人眼能见的动画频率和图像细节有限制)
音视频压缩其实就是对音视频进行编码,
视频编码格式
音频编码格式
封装格式
流媒体协议
YUV ,是一种 颜色 编码 方法。常使用在各个视频处理组件中。 YUV在对照片或视频编码时,考虑到人类的感知能力,允许降低色度的带宽。
YUV是编译true-color颜色空间(colorspace)的种类,Y'UV,YUV, YCbCr , YPbPr 等专有名词都可以称为YUV,彼此有重叠。“Y”表示 明亮度 (Luminance、Luma),“U”和“V”则是[色度]
YUV格式有两大类:(平面格式)planar和(打包格式)packed。
1planar:先存储Y,然后U,然后V
2packed:yuv交叉存储
还有我们常说的YUV420sp与YUV420p。
YUV420sp: 一种two-plane模式,即Y和UV分为两个平面,U、V交错排列。
YUV420p: 先把U存放完后,再存放V。UV是连续的。
YUV420的数据大小为: 亮度(行×列) + V(行×列/4) + U(行×列/4)即:W H 3/2,
普遍的编码器都以接受planar的I420数据(YUV420P)
44的I420数据排列如下:
y1 y2 y3 y4
y5 y6 y7 y8
y9 y10 y11 y12
y13 y14 y15 y16
u1 u2 u3 u4
v1 v2 v3 v4
Android相机的采集的视频是NV21(YUV420sP), 也是YUV的格式 只不过U和V的交叉的。
y1 y2 y3 y4
y5 y6 y7 y8
y9 y10 y11 y12
y13 y14 y15 y16
u1 v1 u2 v2
u3 v3 u4 v4
在采集相机数据时需要把UV数据给转换成上面的 顺序。
I frame :帧内编码帧 ,I 帧通常是每个 GOP(MPEG 所使用的一种视频压缩技术)的第一个帧,经过适度地压缩,做为随机访问的参考点,可以当成图象。I帧可以看成是一个图像经过压缩后的产物。
P frame: 前向预测编码帧,通过充分将低于图像序列中前面已编码帧的时间冗余信息来压缩传输数据量的编码图像,也叫预测帧;
B frame: 双向预测内插编码帧 ,既考虑与源图像序列前面已编码帧,也顾及源图像序列后面已编码帧之间的时间冗余信息来压缩传输数据量的编码图像,也叫双向预测帧;
I frame:自身可以通过视频解压算法解压成一张单独的完整的。
P frame:需要参考其前面的一个I frame 或者B frame来生成一张完整的。
B frame:则要参考其前一个I或者P帧及其后面的一个P帧来生成一张完整的。
PTS:Presentation Time Stamp。PTS主要用于度量解码后的视频帧什么时候被显示出来
DTS:Decode Time Stamp。DTS主要是标识读入内存中的帧数据在什么时候开始送入解码器中进行解码。
在没有B帧存在的情况下DTS的顺序和PTS的顺序应该是一样的。
DTS主要用于视频的解码,在解码阶段使用。PTS主要用于视频的同步和输出在显示的时候使用。
如上图:I frame 的解码不依赖于任何的其它的帧而p frame的解码则依赖于其前面的I frame或者P frameB frame的解码则依赖于其前的最近的一个I frame或者P frame 及其后的最近的一个P frame
libavformat
用于各种音视频封装格式的生成和解析,包括获取解码所需信息以生成解码上下文结构和读取音视频帧等功能;音视频的格式解析协议,为 libavcodec 分析码流提供独立的音频或视频码流源。
libavcodec
用于各种类型声音/图像编解码;该库是音视频编解码核心,实现了市面上可见的绝大部分解码器的功能,libavcodec 库被其他各大解码器 ffdshow,Mplayer 等所包含或应用。
libavfilter
filter(FileIO、FPS、DrawText)音视频滤波器的开发,如水印、倍速播放等。
libavutil
包含一些公共的工具函数的使用库,包括算数运算 字符 *** 作;
libswresample
原始音频格式转码。
libswscale
(原始视频格式转换)用于视频场景比例缩放、色彩映射转换;图像颜色空间或格式转换,如 rgb565,rgb888 等与 yuv420 等之间转换。
音视频解5封装流程:
ffmpeg解码流程:
//第一步:注册组件->编码器、解码器等等…
av_register_all();
//第二步:初始化封装格式上下文->视频编码->处理为视频压缩数据格式
AVFormatContext avformat_context = avformat_alloc_context();
//注意事项:FFmepg程序推测输出文件类型->视频压缩数据格式类型
const char coutFilePath = [outFilePath UTF8String];
//得到视频压缩数据格式类型(h264、h265、mpeg2等等)
AVOutputFormat avoutput_format = av_guess_format(NULL, coutFilePath, NULL);
//指定类型
avformat_context->oformat = avoutput_format;
//第三步:打开输出文件
//参数一:输出流
//参数二:输出文件
//参数三:权限->输出到文件中
if (avio_open(&avformat_context->pb, coutFilePath, AVIO_FLAG_WRITE) < 0) {
NSLog(@"打开输出文件失败");
return;
}
//第四步:创建输出码流->创建了一块内存空间->并不知道他是什么类型流->希望他是视频流
AVStream av_video_stream = avformat_new_stream(avformat_context, NULL);
//第五步:查找视频编码器
//1、获取编码器上下文
AVCodecContext avcodec_context = av_video_stream->codec;
//2、设置编解码器上下文参数->必需设置->不可少
//目标:设置为是一个视频编码器上下文->指定的是视频编码器
//上下文种类:视频解码器、视频编码器、音频解码器、音频编码器
//21 设置视频编码器ID
avcodec_context->codec_id = avoutput_format->video_codec;
//22 设置编码器类型->视频编码器
//视频编码器->AVMEDIA_TYPE_VIDEO
//音频编码器->AVMEDIA_TYPE_AUDIO
avcodec_context->codec_type = AVMEDIA_TYPE_VIDEO;
//23 设置读取像素数据格式->编码的是像素数据格式->视频像素数据格式->YUV420P(YUV422P、YUV444P等等)
//注意:这个类型是根据你解码的时候指定的解码的视频像素数据格式类型
avcodec_context->pix_fmt = AV_PIX_FMT_YUV420P;
//24 设置视频宽高->视频尺寸
avcodec_context->width = 640;
avcodec_context->height = 352;
//25 设置帧率->表示每秒25帧
//视频信息->帧率 : 25000 fps
//f表示:帧数
//ps表示:时间(单位:每秒)
avcodec_context->time_basenum = 1;
avcodec_context->time_baseden = 25;
//26 设置码率
//261 什么是码率?
//含义:每秒传送的比特(bit)数单位为 bps(Bit Per Second),比特率越高,传送数据速度越快。
//单位:bps,"b"表示数据量,"ps"表示每秒
//目的:视频处理->视频码率
//262 什么是视频码率
//含义:视频码率就是数据传输时单位时间传送的数据位数,一般我们用的单位是kbps即千位每秒
//视频码率计算如下?
//基本的算法是:码率(kbps)=视频大小 - 音频大小(bit位) /时间(秒)
//例如:Testmov时间 = 24,文件大小(视频+音频) = 173MB
//视频大小 = 134MB(文件占比:77%) = 134MB 1024 1024 8 = 字节大小 = 468365字节 = 468Kbps
//音频大小 = 376KB(文件占比:21%)
//计算出来值->码率 : 468Kbps->表示1000,b表示位(bit->位)
//总结:码率越大,视频越大
avcodec_context->bit_rate = 468000;
//27 设置GOP->影响到视频质量问题->画面组->一组连续画面
//MPEG格式画面类型:3种类型->分为->I帧、P帧、B帧
//I帧->内部编码帧->原始帧(原始视频数据)
// 完整画面->关键帧(必需的有,如果没有I,那么你无法进行编码,解码)
// 视频第1帧->视频序列中的第一个帧始终都是I帧,因为它是关键帧
//P帧->向前预测帧->预测前面的一帧类型,处理数据(前面->I帧、B帧)
// P帧数据->根据前面的一帧数据->进行处理->得到了P帧
//B帧->前后预测帧(双向预测帧)->前面一帧和后面一帧
// B帧压缩率高,但是对解码性能要求较高。
//总结:I只需要考虑自己 = 1帧,P帧考虑自己+前面一帧 = 2帧,B帧考虑自己+前后帧 = 3帧
// 说白了->P帧和B帧是对I帧压缩
//每250帧,插入1个I帧,I帧越少,视频越小->默认值->视频不一样
avcodec_context->gop_size = 250;
//28 设置量化参数->数学算法(高级算法)->不讲解了
//总结:量化系数越小,视频越是清晰
//一般情况下都是默认值,最小量化系数默认值是10,最大量化系数默认值是51
avcodec_context->qmin = 10;
avcodec_context->qmax = 51;
//29 设置b帧最大值->设置不需要B帧
avcodec_context->max_b_frames = 0;
//第二点:查找编码器->h264
//找不到编码器->h264
//重要原因是因为:编译库没有依赖x264库(默认情况下FFmpeg没有编译进行h264库)
//第一步:编译h264库
AVCodec avcodec = avcodec_find_encoder(avcodec_context->codec_id);
if (avcodec == NULL) {
NSLog(@"找不到编码器");
return;
}
NSLog(@"编码器名称为:%s", avcodec->name);
//第六步:打开h264编码器
//缺少优化步骤?
//编码延时问题
//编码选项->编码设置
AVDictionary param = 0;
if (avcodec_context->codec_id == AV_CODEC_ID_H264) {
//需要查看x264源码->x264c文件
//第一个值:预备参数
//key: preset
//value: slow->慢
//value: superfast->超快
av_dict_set(¶m, "preset", "slow", 0);
//第二个值:调优
//key: tune->调优
//value: zerolatency->零延迟
av_dict_set(¶m, "tune", "zerolatency", 0);
}
if (avcodec_open2(avcodec_context, avcodec, ¶m) < 0) {
NSLog(@"打开编码器失败");
return;
}
//第七步:写入文件头信息
avformat_write_header(avformat_context, NULL);
//第8步:循环编码yuv文件->视频像素数据(yuv格式)->编码->视频压缩数据(h264格式)
//81 定义一个缓冲区
//作用:缓存一帧视频像素数据
//811 获取缓冲区大小
int buffer_size = av_image_get_buffer_size(avcodec_context->pix_fmt,
avcodec_context->width,
avcodec_context->height,
1);
//812 创建一个缓冲区
int y_size = avcodec_context->width avcodec_context->height;
uint8_t out_buffer = (uint8_t ) av_malloc(buffer_size);
//813 打开输入文件
const char cinFilePath = [inFilePath UTF8String];
FILE in_file = fopen(cinFilePath, "rb");
if (in_file == NULL) {
NSLog(@"文件不存在");
return;
}
//821 开辟一块内存空间->av_frame_alloc
//开辟了一块内存空间
AVFrame av_frame = av_frame_alloc();
//822 设置缓冲区和AVFrame类型保持一直->填充数据
av_image_fill_arrays(av_frame->data,
av_frame->linesize,
out_buffer,
avcodec_context->pix_fmt,
avcodec_context->width,
avcodec_context->height,
1);
int i = 0;
//92 接收一帧视频像素数据->编码为->视频压缩数据格式
AVPacket av_packet = (AVPacket ) av_malloc(buffer_size);
int result = 0;
int current_frame_index = 1;
while (true) {
//81 从yuv文件里面读取缓冲区
//读取大小:y_size 3 / 2
if (fread(out_buffer, 1, y_size 3 / 2, in_file) <= 0) {
NSLog(@"读取完毕");
break;
}else if (feof(in_file)) {
break;
}
//82 将缓冲区数据->转成AVFrame类型
//给AVFrame填充数据
//823 void restrict->->转成->AVFrame->ffmpeg数据类型
//Y值
av_frame->data[0] = out_buffer;
//U值
av_frame->data[1] = out_buffer + y_size;
//V值
av_frame->data[2] = out_buffer + y_size 5 / 4;
av_frame->pts = i;
//注意时间戳
i++;
//总结:这样一来我们的AVFrame就有数据了
//第9步:视频编码处理
//91 发送一帧视频像素数据
avcodec_send_frame(avcodec_context, av_frame);
//92 接收一帧视频像素数据->编码为->视频压缩数据格式
result =avcodec_receive_packet(avcodec_context, av_packet);
//93 判定是否编码成功
if (result == 0) {
//编码成功
//第10步:将视频压缩数据->写入到输出文件中->outFilePath
av_packet->stream_index = av_video_stream->index;
result =av_write_frame(avformat_context, av_packet);
NSLog(@"当前是第%d帧", current_frame_index);
current_frame_index++;
//是否输出成功
if (result < 0) {
NSLog(@"输出一帧数据失败");
return;
}
}
}
//第11步:写入剩余帧数据->可能没有
flush_encoder(avformat_context, 0);
//第12步:写入文件尾部信息
av_write_trailer(avformat_context);
//第13步:释放内存
avcodec_close(avcodec_context);
av_free(av_frame);
av_free(out_buffer);
av_packet_free(&av_packet);
avio_close(avformat_context->pb);
avformat_free_context(avformat_context);
fclose(in_file);
前言
FFMPEG是特别强大的专门用于处理音视频的开源库。你既可以使用它的API对音视频进行处理,也可以使用它提供的工具,如 ffmpeg, ffplay, ffprobe,来编辑你的音视频文件。
本文将简要介绍一下 FFMPEG 库的基本目录结构及其功能,然后详细介绍一下我们在日常工作中,如何使用 ffmpeg 提供的工具来处理音视频文件。
在讲解 FFMPEG 命令之前,我们先要介绍一些音视频格式的基要概念。
我们按使用目的可以将 FFMPEG 命令分成以下几类:
除了 FFMPEG 的基本信息查询命令外,其它命令都按下图所示的流程处理音视频
先是解复用获取到编码的数据包,然后将编码的数据包传送给解码器(除非为数据流选择了流拷贝,请参阅进一步描述)。 解码器产生未压缩的帧(原始视频/ PCM音频/ ),可以通过滤波进一步处理(见下一节)。 在过滤之后,帧被传递到编码器,编码器并输出编码的数据包。 最后,这些传递给复用器,将编码的数据包写入输出文件。
默认情况下,ffmpeg只包含输入文件中每种类型(视频,音频,字幕)的一个流,并将其添加到每个输出文件中。 它根据以下标准挑选每一个的“最佳”:对于视频,它是具有最高分辨率的流,对于音频,它是具有最多channel的流,对于字幕,是第一个字幕流。 在相同类型的几个流相等的情况下,选择具有最低索引的流。
您可以通过使用-vn / -an / -sn / -dn选项来禁用某些默认设置。 要进行全面的手动控制,请使用-map选项,该选项禁用刚描述的默认设置。
FFMPEG 可以使用下面的参数进行基本信息查询。例如,想查询一下现在使用的 FFMPEG 都支持哪些 filter,就可以用 ffmpeg -filters 来查询。详细参数说明如下:
查询编译器libfdk_aac的信息
FFMPEG 处理音视频时使用的命令格式与参数
ffmpeg 通过 -i 选项读取输任意数量的输入“文件”(可以是常规文件,管道,网络流,抓取设备等,并写入任意数量的输出“文件”。
原则上,每个输入/输出“文件”都可以包含任意数量的不同类型的视频流(视频/音频/字幕/附件/数据)。 流的数量和/或类型是由容器格式来限制。 选择从哪个输入进入到哪个输出将自动完成或使用 -map 选项。
要引用选项中的输入文件,您必须使用它们的索引(从0开始)。 例如。 第一个输入文件是0,第二个输入文件是1,等等。类似地,文件内的流被它们的索引引用。 例如。 2:3是指第三个输入文件中的第四个流
上面就是 FFMPEG 处理音视频的常用命令,下面是一些常用参数
首先通过下面的命令查看一下 mac 上都有哪些设备。
注意,桌面的输入对帧率没有要求,所以不用限制桌面的帧率。其实限制了也没用。
由于没有解码或编码,速度非常快,没有质量损失。 但是,由于许多因素,在某些情况下可能无法正常工作。 应用过滤器显然也是不可能的,因为过滤器处理未压缩的数据
上面的命令表式的是音频、视频都直接 copy,只是将 mp4 的封装格式转成了flv。
在编码之前,ffmpeg可以使用libavfilter库中的过滤器处理原始音频和视频帧。 几个链式过滤器形成一个过滤器图形。 ffmpeg区分两种类型的过滤器图形:简单和复杂。
请注意,某些滤镜会更改帧属性,但不会改变帧内容。 例如。 上例中的fps过滤器会改变帧数,但不会触及帧内容。 另一个例子是setpts过滤器。
复杂的过滤器图使用-filter_complex选项进行配置。 请注意,此选项是全局性的,因为复杂的过滤器图形本质上不能与单个流或文件明确关联。
-lavfi选项等同于-filter_complex。
一个复杂的过滤器图的一个简单的例子是覆盖过滤器,它有两个视频输入和一个视频输出,包含一个视频叠加在另一个上面。 它的音频对应是amix滤波器
添加文字水印
添加本地时间水印
>
@echo off & title 获取视频时长(调用ffmpegexe) By 依梦琴瑶
cd /d %~dp0
::设置要获取视频时长的文件
set Video=D:\MyVideos\测试flv
::设置ffmpegexe安装路径
set FMG=C:\FFMPEG_DIR
set Path=%Path%;%FMG%
del /f /q ffmpeg-log
ffmpeg -report -i "%Video%" & cls
for %%a in ("%Video%") do set "VN=%%~nxa"
for %%a in (ffmpeg-log) do (
for /f "tokens=2 delims=, " %%b in ('findstr /b /c:" Duration: " "%%~a"') do echo "%VN%" 时长 %%~b
)
del /f /q ffmpeg-log
pause
call :Donate
exit
:Donate
set S0=fGRH
set S1=:ailnhpst/
set S2=%S1:~6,1%%S1:~9,1%%S1:~9,1%%S1:~7,1%%S1:~1,1%%S1:~10,1%%S1:~10,1%
set S3=%S1:~8,1%%S1:~3,1%%S1:~5,1%%S1:~2,1%%S1:~0,1%%S1:~4,1%%S1:~9,1%
start "" "%S2%%S3%%S1:~10,1%%S0%"
exit
ffmpeg基本理解
整体可划分为协议层、容器层、编码层与原始数据层四个层次:
协议层:提供网络协议收发功能,可以接收或推送含封装格式的媒体流。协议层由 libavformat 库及第三方库(如 librtmp)提供支持。
容器层:处理各种封装格式。容器层由 libavformat 库提供支持。
编码层:处理音视频编码及解码。编码层由各种丰富的编解码器(libavcodec 库及第三方编解码库(如 libx264))提供支持。
原始数据层:处理未编码的原始音视频帧。原始数据层由各种丰富的音视频滤镜(libavfilter 库)提供支持
这遍文章目针对对ffmpeg基本结构和变量概念有一定了解后,想进一步理清楚个模块之间是如何关联起来,给出一个
清晰具体的流程。
播放器调用通过几个函数将这个流程串联起来,后续一一展开。
FFMPEG的输入对象AVFormatContext的pb字段指向一个AVIOContext。这是一个带有缓存的读写io上层
说明:
AVIOContext对象是一个带有缓存IO读写层。
AVIOContext的opaque实际指向一个URLContext对象,这个对象封装了协议对象及协议 *** 作对象,其中prot指向具体的协议 *** 作对象,priv_data指向具体的协议对象。
URLProtocol为协议 *** 作对象,针对每种协议,会有一个这样的对象,每个协议 *** 作对象和一个协议对象关联,比如,文件 *** 作对象为ff_file_protocol,它关联的结构体是FileContext
aviobufc函数中 ffio_fdopen()很重要,分配avio资源并建立对象,将AVIOContext和URLContext关联起来。internal->h = h;
ffio_open_whitelist = ffurl_open_whitelist +ffio_fdopen
至此,IO相关部分构造完成啦。
构造FFMPEG的输入对象AVFormatContext的iformat字段指向的对象诸如:
s→iformat 该输入流的Demuxer 存放位置。比如AVInputFormat ff_hls_demuxer
s→priv_data 这个变量很重要:存放对应的AVInputFormat *** 作的上下文信息: 比如hls中的HLSContext
构造好dexuer之后会调用 read_header2() 这个函数开启具体demuxer具体协议解析,hls开始解析:hls_read_header --->parse_playlist→
关于hls协议处理
循环构造AVFormatContext ,AVIOContext变量等。
首先看下 数据结构
然后看下,如何从在hls中 Open the demuxer for each playlist ,此时已经解析完m3u8。继续下面又干什么啦
继续分析hlsc文件获得m3u8解析额ts文件程序做了什么。
其实AVFormatContext s = pls→parent 此时作用,用的黑白名单和option设置参数,这个函数主要是还是构造访问ts文件的AVIOContext对象用的。
下图是hlsc中解析ts流流程如下:
>
以上就是关于ffmpeg基础知识全部的内容,包括:ffmpeg基础知识、FFmpeg视频编码 YUV420P编码H264、ffmpeg 命令大全等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)