ffmpeg 基本用法

ffmpeg 基本用法,第1张

1、libavformat:用于各种音视频封装格式的生成和解析,包括获取解码所需信息以生成解码上下文结构和读取音视频帧等功能,包含demuxers和muxer库;

2、libavcodec:用于各种类型声音/图像编解码;

3、libavutil:包含一些公共的工具函数;

4、libswscale:用于视频场景比例缩放、色彩映射转换;

5、libpostproc:用于后期效果处理;

6、ffmpeg:是一个命令行工具,用来对视频文件转换格式,也支持对电视卡实时编码;

7、ffsever:是一个HTTP多媒体实时广播流服务器,支持时光平移;

8、ffplay:是一个简单的播放器,使用ffmpeg 库解析和解码,通过SDL显示;

在这组成部分中,需要熟悉基础概念有

容器(Container)

容器就是一种文件格式,比如flv,mkv等。包含下面5种流以及文件头信息。

流(Stream)

是一种视频数据信息的传输方式,5种流:音频,视频,字幕,附件,数据。

帧(Frame)

帧代表一幅静止的图像,分为I帧,P帧,B帧。

编解码器(Codec)

是对视频进行压缩或者解压缩,CODEC =COde (编码) +DECode(解码)

复用/解复用(mux/demux)

把不同的流按照某种容器的规则放入容器,这种行为叫做复用(mux)

把不同的流从某种容器中解析出来,这种行为叫做解复用(demux)

1、FFmpeg程序把-i参数指定的若干文件内容读入到内存,按照输入的参数或者程序默认的参数来处理并且把结果写入到若干的文件中。输入和输出文件可以是计算机文件、管道、网络流、捕获设备等。

2、FFmpeg用libavformat包调用解复用器(demuxers)来读取输入文件中被编码的数据包(packets),如果有多个输入文件,FFmpeg以有效输入流的最小时间戳来同步,

3、然后解码器(decoder)从已编码的数据包中产生未被压缩的帧(frame),在那之后调用可选的过滤器。

4、这些帧被传递到编码器,编码器会产生新的编码包

5、把新的编码包传递给复用器(muxer)处理并且把结果写入到输出文件中。

在多媒体处理中,filter的意思是被编码到输出文件之前用来修改输入文件内容的一个软件工具。如:视频翻转,旋转,缩放等。

语法:[input_link_label1]… filter_name=parameters [output_link_label1]…

1、视频过滤器 -vf

如input.mp4视频按顺时针方向旋转90度

ffplay -i input.mp4 -vf transpose=1

如input.mp4视频水平翻转(左右翻转)

ffplay -i input.mp4 -vf hflip

2、音频过滤器 -af

实现慢速播放,声音速度是原始速度的50%

offplay input.mp3 -af atempo=0.5

过滤器链(Filterchain)

Filterchain = 逗号分隔的一组filter

语法:“filter1,filter2,filter3,…filterN-2,filterN-1,filterN”

顺时针旋转90度并水平翻转

ffplay -i input.mp4 -vf transpose=1,hflip

过滤器图(Filtergraph)

第一步: 源视频宽度扩大两倍。

ffmpeg -i jidu.mp4 -t 10 -vf pad=2*iw output.mp4

第二步:源视频水平翻转

ffmpeg -i jidu.mp4 -t 10 -vf hflip output2.mp4

第三步:水平翻转视频覆盖output.mp4

ffmpeg -i output.mp4 -i output2.mp4 -filter_complex overlay=w compare.mp4

是不是很复杂?

用带有链接标记的过滤器图(Filtergraph)只需一条命令

基本语法

Filtergraph = 分号分隔的一组filterchain

“filterchain1filterchain2…filterchainN-1filterchainN”

Filtergraph的分类

1、简单(simple) 一对一

2、复杂(complex)多对一, 多对多

简单过滤器图处理流程:

复杂过滤器图处理流程:

对于刚才用三步处理的方式,用过滤器图可以这样做:

ffplay -f lavfi -i testsrc -vf split[a][b][a]pad=2*iw[1][b]hflip[2][1][2]overlay=w

F1: split过滤器创建两个输入文件的拷贝并标记为[a],[b]

F2: [a]作为pad过滤器的输入,pad过滤器产生2倍宽度并输出到[1].

F3: [b]作为hflip过滤器的输入,vflip过滤器水平翻转视频并输出到[2].

F4: 用overlay过滤器把 [2]覆盖到[1]的旁边.

一些多媒体容器比如AVI,mkv,mp4等,可以包含不同种类的多个流,如何从容器中抽取各种流呢?

语法:

-map file_number[:stream_type][:stream_number]

这有一些特别流符号的说明:

1、-map 0 选择第一个文件的所有流

2、-map i:v 从文件序号i(index)中获取所有视频流, -map i:a 获取所有音频流,-map i:s 获取所有字幕流等等。

3、特殊参数-an,-vn,-sn分别排除所有的音频,视频,字幕流

tip:对比上面的图,可以知道,假设有两个文件ffmpeg -i fist.mp4 -i second.mp4 ..output.mp4

如果想去两个文件的音视频流 ffmpeg -i fist.mp4 -i second.mp4 map:0 -map 1 output.mp4

如果想去第一个文件的视频流,第二个文件的音频流ffmpeg -i fist.mp4 -i second.mp4 -map:v:0 -map:a:0 output.mp4

可用的bit流 :ffmpeg –bsfs

可用的编解码器:ffmpeg –codecs

可用的解码器:ffmpeg –decoders

可用的编码器:ffmpeg –encoders

可用的过滤器:ffmpeg –filters

可用的视频格式:ffmpeg –formats

可用的声道布局:ffmpeg –layouts

可用的license:ffmpeg –L

可用的像素格式:ffmpeg –pix_fmts

可用的协议:ffmpeg -protocals

码率和帧率是视频文件的最重要的基本特征,对于他们的特有设置会决定视频质量。如果我们知道码率和时长那么可以很容易计算出输出文件的大小。

帧率:帧率也叫帧频率,帧率是视频文件中每一秒的帧数,肉眼想看到连续移动图像至少需要15帧。

码率:比特率(也叫码率,数据率)是一个确定整体视频/音频质量的参数,秒为单位处理的字节数,码率和视频质量成正比,在视频文件中中比特率用bps来表达。

设置帧率

1、用 -r 参数设置帧率

ffmpeg –i input.mp4 –r fps output.mp4

2、用fps filter设置帧率

ffmpeg -i clip.mpg -vf fps=fps=25 clip.webm

设置码率 –b 参数

-b

ffmpeg -i film.avi -b 1.5M film.mp4

音频:-b:a 视频: - b:v

设置视频码率为1500kbps

ffmpeg -i input.avi -b:v 1500k output.mp4

控制输出文件大小

-fs (file size首字母缩写)

ffmpeg -i input.avi -fs 1024K output.mp4

计算输出文件大小

(视频码率+音频码率) * 时长 /8 = 文件大小K

用-s参数设置视频分辨率,参数值wxh,w宽度单位是像素,h高度单位是像素

ffmpeg -i input_file -s 320x240 output_file

2、预定义的视频尺寸

下面两条命令有相同效果

ffmpeg -i input.avi -s 640x480 output.avi

ffmpeg -i input.avi -s vga output.avi

下表列出了所有的预定义尺寸

Scale filter调整分辨率

Scale filter的优点是可以使用一些额外的参数

语法:

Scale=width:height[:interl={1|-1}]

下表列出了常用的额外参数

下面两条命令有相同效果

ffmpeg -i input.mpg -s 320x240 output.mp4

ffmpeg -i input.mpg -vf scale=320:240 output.mp4

对输入视频成比例缩放

改变为源视频一半大小

ffmpeg -i input.mpg -vf scale=iw/2:ih/2 output.mp4

改变为原视频的90%大小:

ffmpeg -i input.mpg -vf scale=iw 0.9:ih 0.9 output.mp4

在未知视频的分辨率时,保证调整的分辨率与源视频有相同的横纵比。

宽度固定400,高度成比例:

ffmpeg -i input.avi -vf scale=400:400/a

ffmpeg -i input.avi -vf scale=400:-1

相反地,高度固定300,宽度成比例:

ffmpeg -i input.avi -vf scale=-1:300

ffmpeg -i input.avi -vf scale=300*a:300

从输入文件中选取你想要的矩形区域到输出文件中,常见用来去视频黑边。

语法:crop:ow[:oh[:x[:y:[:keep_aspect]]]]

裁剪输入视频的左三分之一,中间三分之一,右三分之一:

ffmpeg -i input -vf crop=iw/3:ih :0:0 output

ffmpeg -i input -vf crop=iw/3:ih :iw/3:0 output

ffmpeg -i input -vf crop=iw/3:ih :iw/3*2:0 output

裁剪帧的中心

当我们想裁剪区域在帧的中间时,裁剪filter可以跳过输入x和y值,他们的默认值是

Xdefault = ( input width - output width)/2

Ydefault = ( input height - output height)/2

ffmpeg -i input_file -v crop=w:h output_file

裁剪中间一半区域:

ffmpeg -i input.avi -vf crop=iw/2:ih/2 output.avi

比较裁剪后的视频和源视频比较

ffplay -i jidu.mp4 -vf split[a][b][a]drawbox=x=(iw-300)/2:(ih-300)/2:w=300:h=300:c=yellow[A][A]pad=2 iw[C][b]crop=300:300:(iw-300)/2:(ih-300)/2[B][C][B]overlay=w 2.4:40

自动检测裁剪区域�

cropdetect filter 自动检测黑边区域

ffplay jidu.mp4 -vf cropdetect

填充视频(pad)

在视频帧上增加一快额外额区域,经常用在播放的时候显示不同的横纵比

语法:pad=width[:height:[:x[:y:[:color]]]]

创建一个30个像素的粉色宽度来包围一个SVGA尺寸的图片:

ffmpeg -i photo.jpg -vf pad=860:660:30:30:pink framed_photo.jpg

同理可以制作input.mp4视频用30个像素粉色包围视频

ffplay -i input.mp4 -vf pad=iw+60:ih+60:30:30:pink

4:3到16:9

一些设备只能播放16:9的横纵比,4:3的横纵比必须在水平方向的两边填充成16:9,

高度被保持,宽度等于高度乘以16/9,x(输入文件水平位移)值由表达式(output_width - input_width)/2来计算。

4:3到16:9的通用命令是:

ffmpeg -i input.mp4 -vf pad=ih 16/9:ih :(ow-iw)/2:0:color output.mp4

eg:ffplay -f input.mp4 -vf pad=ih 16/9:ih:(ow-iw)/2:0:pink

16:9到4:3

为了用4:3的横纵比来显示16:9的横纵比,填充输入文件的垂直两边,宽度保持不变,高度是宽度的3/4,y值(输入文件的垂直偏移量)是由一个表达式(output_height-input_height)/2计算出来的。

16:9到4:3的通用命令:

ffmpeg -i input.mp4-vf pad=iw :iw 3/4:0:(oh-ih)/2:color output.mp4

eg:ffplay -i input.mp4 =size=320x180 -vf pad=iw:iw 3/4:0:(oh-ih)/2:pink

水平翻转语法: -vf hflip

ffplay -f lavfi -i testsrc -vf hflip

垂直翻转语法:-vf vflip

ffplay -f lavfi -i testsrc -vf vflip

语法:transpose={0,1,2,3}

0:逆时针旋转90°然后垂直翻转

1:顺时针旋转90°

2:逆时针旋转90°

3:顺时针旋转90°然后水平翻转

模糊

语法:boxblur=luma_r:luma_p[:chroma_r:chram_p[:alpha_r:alpha_p]]

ffplay -f lavfi -i testsrc -vf boxblur=1:10:4:10

注意:luma_r和alpha_r半径取值范围是0~min(w,h)/2, chroma_r半径的取值范围是0~min(cw/ch)/2

锐化

语法:-vf unsharp=l_msize_x:l_msize_y:l_amount:c_msize_x:c_msize_y:c_amount

所有的参数是可选的,默认值是5:5:1.0:5:5:0.0

l_msize_x:水平亮度矩阵,取值范围3-13,默认值为5

l_msize_y:垂直亮度矩阵,取值范围3-13,默认值为5

l_amount:亮度强度,取值范围-2.0-5.0,负数为模糊效果,默认值1.0

c_msize_x:水平色彩矩阵,取值范围3-13,默认值5

c_msize_y:垂直色彩矩阵,取值范围3-13,默认值5

c_amount:色彩强度,取值范围-2.0-5.0,负数为模糊效果,默认值0.0

eg:

使用默认值,亮度矩阵为5x5和亮度值为1.0

ffmpeg -i input.mp4 -vf unsharp output.mp4

高斯模糊效果(比较强的模糊):

ffplay -i input.mp4 -vf unsharp=13:13:-2

语法:overlay[=x[:y]

所有的参数都是可选,默认值都是0

Logo在左上角

ffmpeg -i input.mp4 -i logo.png -filter_complex overlay output.mp4

右上角:

ffmpeg -i input.mp4 -i logo.png -filter_complex overlay=W-w output.mp4

左下角:

ffmpeg -i input.mp4 -i logo.png -filter_complex overlay=0:H-h output.mp4

右下角:

ffmpeg -i input.mp4 -i logo.png -filter_complex overlay=W-w:H-h output.mp4

删除logo

语法:-vf delogo=x:y:w:h[:t[:show]]

x:y 离左上角的坐标

w:h logo的宽和高

t: 矩形边缘的厚度默认值4

show:若设置为1有一个绿色的矩形,默认值0.

ffplay -i jidu.mp4 -vf delogo=50:51:60:60:100:0

语法:

drawtext=fontfile=font_f:text=text1[:p3=v3[:p4=v4[…]]]

常用的参数值

x:离左上角的横坐标

y: 离左上角的纵坐标

fontcolor:字体颜色

fontsize:字体大小

text:文本内容

textfile:文本文件

t:时间戳,单位秒

n:帧数开始位置为0

draw/enable:控制文件显示,若值为0不显示,1显示,可以使用函数

1、在左上角添加Welcome文字

ffplay -i color=c=white -vf drawtext=fontfile=arial.ttf:text=Welcom

2、在中央添加Good day

ffplay -i color=c=white -vf drawtext="fontfile=arial.ttf:text='Goodday':x=(w-tw)/2:y=(h-th)/2"

3、设置字体颜色和大小

ffplay -i color=c=white -vf drawtext="fontfile=arial.ttf:text='Happy Holidays':x=(w-tw)/2:y=(h-th)/2:fontcolor=green:fontsize=30"

动态文本

用 t (时间秒)变量实现动态文本

1、顶部水平滚动

ffplay -i jidu.mp4 -vf drawtext="fontfile=arial.ttf:text='Dynamic RTL text':x=w-t 50:fontcolor=darkorange:fontsize=30"

2、底部水平滚动

ffplay -i jidu.mp4 -vf drawtext="fontfile=arial.ttf:textfile=textfile.txt:x=w-t 50:y=h-th:fontcolor=darkorange:fontsize=30"

3、垂直从下往上滚动

ffplay jidu.mp4 -vf drawtext="textfile=textfile:fontfile=arial.ttf:x=(w-tw)/2:y=h-t*100:fontcolor=white:fontsize=30“

在右上角显示当前时间 localtime

ffplay jidu.mp4 -vf drawtext="fontfile=arial.ttf:x=w-tw:fontcolor=white:fontsize=30:text='%{localtime:%H\:%M\:%S}'“

每隔3秒显示一次当前时间

ffplay jidu.mp4 -vf drawtext="fontfile=arial.ttf:x=w-tw:fontcolor=white:fontsize=30:text='%{localtime:%H\:%M\:%S}':enable=lt(mod(t,3),1)"

FFmpeg支持绝大多数图片处理, 除LJPEG(无损JPEG)之外,其他都能被解码,除了EXR,PIC,PTX之外,所有的都能被编码。

截取一张图片使用 –ss(seek from start)参数.

ffmpeg -ss 01:23:45 -i jidu.mp4 image.jpg

从视频中生成GIF图片

ffmpeg -i jidu.mp4 -t 10 -pix_fmt rgb24 jidu.gif

转换视频为图片(每帧一张图)

ffmpeg -i clip.avi frame%4d.jpg

图片转换为视频

ffmpeg -f image2 -i img%4d.jpg -r 25 video.mp4

和视频一样,图片也可以被裁剪和填充

裁剪

ffmpeg -f lavfi -i rgbtestsrc -vf crop=150:150 crop_rg.png

填充

ffmpeg -f lavfi -i smptebars -vf pad=360:280:20:20:orange pad_smpte.jpg

和视频一样图片同样能翻转,旋转和覆盖

翻转

ffmpeg -i orange.jpg -vf hflip orange_hfilp.jpg

ffmpeg -i orange.jpg -vf vflip orange_vfilp.jpg

旋转

ffmpeg -i -vf transpose=1 image_rotated.png

覆盖

ffmpeg -f lavfi -i rgbtestsrc -s 400x300 rgb .png

ffmpeg -f lavfi -i smptebars smpte.png

ffmpeg -i rgb .png -i smpte.png -filter_complex overlay= (W-w)/2:(H-h)/2 rgb_smpte.png

屏幕录像

显示设备名称

ffmpeg -list_devices 1 -f dshow -i dummy

调用摄像头

ffplay -f dshow -i video="Integrated Camera"

保存为文件

ffmpeg -y -f dshow -s 320x240 -r 25 -i video="Integrated Camera" -b:v 800K -vcodec mpeg4 new.mp4

添加字幕subtitles

语法 –vf subtitles=file

ffmpeg -i jidu.mp4 -vf subtitles=rgb.srt output.mp4

视频颤抖、色彩平衡

视频颤抖

ffplay –i jidu.mp4 -vf crop=in_w/2:in_h/2:(in_w-out_w)/2+((in_w-out_w)/2) sin(n/10):(in_h-out_h)/2 +((in_h-out_h)/2) sin(n/7)

色彩平衡

ffplay -i jidu.mp4 -vf curves=vintage

色彩变幻

ffplay -i jidu.mp4 -vf hue="H=2 PI t: s=sin(2 PI t)+1“

彩色转换黑白

ffplay -i jidu.mp4 -vf lutyuv="u=128:v=128"

设置音频视频播放速度

3倍视频播放视频

ffplay -i jidu.mp4 -vf setpts=PTS/3

¾速度播放视频

ffplay -i jidu.mp4 -vf setpts=PTS/(3/4)

2倍速度播放音频

ffplay -i speech.mp3 -af atempo=2

截图

每隔一秒截一张图

ffmpeg -i input.flv -f image2 -vf fps=fps=1 out%d.png

每隔20秒截一张图

ffmpeg -i input.flv -f image2 -vf fps=fps=1/20 out%d.png

注意:ffmpeg version N-57961-gec8e68c版本最多可以每隔20s截一张图。

多张截图合并到一个文件里(2x3) 每隔一千帧(秒数=1000/fps25)即40s截一张图

ffmpeg -i jidu.mp4 -frames 3 -vf "select=not(mod(n,1000)),scale=320:240,tile=2x3" out.png

本篇文章主要记录ffmpeg的一些基础指令 *** 作,该资料的来源是源于网上的一个ppt文档,感谢文档的总结。

avconv -i input_file.mp4 -vcodeccopy -acodeccopy -vbsfh264_mp4toannexb –ss00:00:00 –t00:00:10 output_file.ts

为例说明ffmpeg如何将命令行参数解析处理。

int main(int argc,char**argv)

{

//初始化参数容器

OptionsContext o={0}

//重置参数

reset_options(&o)

//解析参数

parse_options(&o, argc, argv, options,opt_output_file)

}

1.重置参数

staticvoid reset_options(OptionsContext*o)

依次进行了以下步骤:

1.1第一步:释放特殊类型

释放所有的 OPT_SPEC(对应struct SpecifierOpt)和 OPT_STRING (对应 char*)类型的 OptionDef

代码如下:

//指向全局变量options

const OptionDef*po= options

//遍历options

while(po->name){

//dest指针指向当前option对应的OptionContext中的位置

void*dst=(uint8_t*)o+ po->u.off

//判断是否是SpecifierOpt类型

if(po->flags&OPT_SPEC){

//so指向SpecifierOpt*的首地址

SpecifierOpt **so= dst

//获得数组长度

int i,*count=(int*)(so+1)

//循环遍历SpecifierOpt*数组

for(i=0i<*counti++){

//释放SpecifierOpt的specifier(char*类型)

av_freep(&(*so)[i].specifier)

//如果OPT类型是字符串,释放SpecifierOpt的u.str(char*类型)

if(po->flags&OPT_STRING)

av_freep(&(*so)[i].u.str)

}

//释放SpecifierOpt*指针数组

av_freep(so)

//重置计数器

*count=0

}

//判断是否是char*类型

elseif(po->flags&OPT_OFFSET&&po->flags&OPT_STRING)

av_freep(dst)

po++

}

这里需要对OptionContext的内容做一些说明:

OptionContext 包含了在视频编转码过程中需要用到的参数,这些参数来自于命令行的输入。

参数在OptionContext中的存储形式有:

#defineOPT_INT 0x0080

#defineOPT_FLOAT 0x0100

#defineOPT_INT64 0x0400

#defineOPT_TIME 0x10000

#defineOPT_DOUBLE 0x20000

等,详情参见 structOptionDef

在上述代码中,主要循环释放的是OPT_SPEC(对应struct SpecifierOpt)和 OPT_STRING

在OptionContext中,OPT_SPEC类型是成对出现的,如下:

typedefstructOptionsContext{

int64_t start_time

constchar*format

SpecifierOpt *codec_names

int nb_codec_names

SpecifierOpt *audio_channels

int nb_audio_channels

即:

SpecifierOpt *xxx_vars

int nb_xxx_vars //nb_读作number_意思是xxx_vars数组的长度

然后我们来分析对SpecifierOpt*数组的遍历:

SpecifierOpt **so= dst

int i,*count=(int*)(so+1)

for(i=0i<*counti++){

这里可以这么理解:

so —指向—> SpecifierOpt *xxx_vars

so+1—指向—> int nb_xxx_vars

so+1 的含义:so是个SpecifierOpt指针,指针+1则移动了sizeof(SpecifierOpt)的位置,即跳到nb_xxx_vars的位置。

1.2释放其他类型

av_freep(&o->stream_maps)

av_freep(&o->meta_data_maps)

av_freep(&o->streamid_map)

这里说一下 av_freep 的用法。

void av_freep(void*arg)

{

void**ptr=(void**)arg

av_free(*ptr)

*ptr=NULL

}

相比传统的free方法,这里主要多做了一步工作:将释放free之后,指针设置为NULL

同时,要注意到:

Object *obj

free(obj)

等价用法为:

av_freep(&obj)

在ffmpeg中,封装了对应free的方法为:

void av_free(void*ptr)

{

#ifCONFIG_MEMALIGN_HACK

if(ptr)

free((char*)ptr-((char*)ptr)[-1])

#else

free(ptr)

#endif

}

这里除了考虑内存对齐之外,跟传统的free方法没有任何变化。

1.3第三步:设置初始值

memset(o,0,sizeof(*o))

o->mux_max_delay =0.7

o->recording_time= INT64_MAX

o->limit_filesize= UINT64_MAX

o->chapters_input_file= INT_MAX

不需要过多解释。

o->mux_max_delay =0.7

这一行内容以后在视频切片中会用到。可以调整到更小。

1.4重新初始化特殊参数

uninit_opts()

init_opts()

这两行代码对应cmdutils.c 文件中的代码段:

struct SwsContext*sws_opts

AVDictionary*format_opts,*codec_opts

void init_opts(void)

{

#if CONFIG_SWSCALE

sws_opts= sws_getContext(16,16,0,16,16,0, SWS_BICUBIC,

NULL,NULL,NULL)

#endif

}

void uninit_opts(void)

{

#ifCONFIG_SWSCALE

sws_freeContext(sws_opts)

sws_opts=NULL

#endif

av_dict_free(&format_opts)

av_dict_free(&codec_opts)

}

主要进行: SwsContext*sws_opts,AVDictionary*format_opts,*codec_opts三个全局变量的创建和释放工作。

2.解析命令行参数

void parse_options(void*optctx,int argc,char**argv,const OptionDef *options,void(*parse_arg_function)(void*,constchar*))

void*optctx,——OptionContext

int argc,——命令行参数个数

char**argv,——命令行参数列表

const OptionDef*options,——选项列表

void(*parse_arg_function)(void*,constchar*)——自定义的解析方法

2.1总览

constchar*opt

int optindex, handleoptions=1, ret

//处理window的情况

prepare_app_arguments(&argc,&argv)

optindex=1

//循环处理命令行参数

while(optindex<argc){

opt = argv[optindex++]

//如果传入的参数是“-”打头

if(handleoptions&&opt[0]=='-'&&opt[1]!='\0'){

//如果传入的参数是“--”打头

if(opt[1]=='-'&&opt[2]=='\0'){

handleoptions =0

//略过

continue

}

//丢弃第一个字符”-”

opt++

//解析命令行参数

//eg–acodec copy

//对应的 opt和 argv[optindex]为 “acodec” “copy”

if((ret= parse_option(optctx, opt, argv[optindex], options))<0)

exit_program(1)

optindex += ret

}else{

//此时 opt的值为输出文件名如 test.ts

if(parse_arg_function)

//处理输出文件的相关内容,如 struct OutputFile的初始化

parse_arg_function(optctx, opt)

}

}

在此,ffmpeg 默认的处理输出文件名参数为:

staticvoid opt_output_file(void*optctx,constchar*filename)

2.2处理命令行参数

int parse_option(void*optctx,constchar*opt,constchar*arg, const OptionDef*options)

2.2.1查找匹配的Option

const OptionDef*po

int bool_val=1

int*dstcount

void*dst

//从全局变量options数组中查找opt对应的OptionDef

po = find_option(options, opt)

//如果未找到且以”no”打头

//不需要传递参数的选项是bool类型的选项,默认为true

//如果需要设置为false,则需要加上”no”,以下的if则是处理这种情况

if(!po->name&&opt[0]=='n'&&opt[1]=='o'){

//去掉开头的”no”重新查找

po = find_option(options, opt +2)

//如果仍未找到或者找到的选项不是bool类型

if(!(po->name&&(po->flags&OPT_BOOL)))

//报错

goto unknown_opt

bool_val =0

}

//如果未找到且不是以上的”no”打头情况

if(!po->name)

//寻找默认配置进行处理

po = find_option(options,"default")

//default配置也未找到,报错

if(!po->name){

unknown_opt:

av_log(NULL, AV_LOG_ERROR,"Unrecognizedoption '%s'\n", opt)

return AVERROR(EINVAL)

}

//如果选项必须有参数但是没有可用的参数,报错

if(po->flags&HAS_ARG&&!arg){

av_log(NULL, AV_LOG_ERROR,"Missingargument for option '%s'\n", opt)

return AVERROR(EINVAL)

}

现在来查看一下find_option方法的实现:

staticconst OptionDef*find_option(const OptionDef*po,constchar*name)

根据name在全局变量options数组中查找OptionDef

//这里先处理参数带有冒号的情况。比如 codec:a codec:v等

constchar*p= strchr(name,':')

int len= p? p- name: strlen(name)

//遍历options

while(po->name!=NULL){

//比较option的名称与name是否相符。

//这里 codec 与 codec:a相匹配

if(!strncmp(name, po->name, len)&&strlen(po->name)== len)

break

po++

}

return po

2.2.2寻找选项地址

以下的代码用于将 void*dst变量赋值。让dst指向需要赋值的选项地址。

//如果选项在OptionContext中是以偏移量定位或者是 SpecifierOpt*数组的类型

dst= po->flags&(OPT_OFFSET| OPT_SPEC)?

//dst指向从 optctx地址偏移u.off的位置

(uint8_t*)optctx+ po->u.off:

//否则直接指向 OptionDef结构中定义的位置

po->u.dst_ptr

//如果选项是SpecifierOpt*数组

if(po->flags&OPT_SPEC){

//数组首地址

SpecifierOpt **so= dst

char*p= strchr(opt,':')

//这里是取得数组的当前长度+1

//请回顾 1.1中的描述:

//SpecifierOpt *xxx

//int nb_xxx

//当so指向xxx时刻,so+1指向nb_xxx

dstcount =(int*)(so+1)

//动态增长数组

*so = grow_array(*so,sizeof(**so), dstcount,*dstcount+1)

//将创建的SpecifierOpt结构体中的specifier赋值

//如codec:v 则specifier值为 “v”

(*so)[*dstcount-1].specifier= av_strdup(p? p+1:"")

//dst指针指向数组新增的SpecifierOpt中的 u地址

//此时dstcount的值已经变作新数组的长度,亦即原数组长度+1

dst =&(*so)[*dstcount-1].u

分离某些封装格式(例如MP4/FLV/MKV等)中的H.264的时候,需要首先写入SPS和PPS,否则会导致分离出来的数据没有SPS、PPS而无法播放。H.264码流的SPS和PPS信息存储在AVCodecContext结构体的extradata中。需要使用ffmpeg中名称为“h264_mp4toannexb”的bitstream filter处理。

原有的API已被弃用,新的API如下:

Query

Setup

Usage

Cleanup

ps: FFmpeg给出的例子中并未while循环调用av_bsf_receive_packet,也未对其flush。

https://blogs.gentoo.org/lu_zero/2016/03/21/bitstream-filtering/


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/bake/11564788.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-17
下一篇 2023-05-17

发表评论

登录后才能评论

评论列表(0条)

保存