Drollery Medieval drollery of a knight on a horse

🏆 欢迎来到本站: https://xuchangwei.com/希望这里有你感兴趣的内容

flowery border with man falling
flowery border with man falling

Linux: 强大的多媒体视频处理工具 FFmpeg

FFmpeg 简介

FFmpeg 是领先的多媒体框架,能够解码、编码、转码、多路复用、解复用、流式传输、过滤和播放人类和机器创造的几乎任何内容。它支持最晦涩难懂的古老格式,直至最前沿。是一个完整的跨平台音视频录制、转换及串流方案。

官方网站: https://ffmpeg.org/

关于作者 Fabrice Bellard

  • 博客: https://bellard.org/
  • 作品:
    • 信号学:FFmpeg(开源多媒体系统),5G
    • 虚拟化领域:QEMU(开源机器仿真器和虚拟器)
    • 数学领域: 圆周率,利用普通电脑计算出了PI的小数点后第2.7万亿位
    • 编译原理:TinyCC
    • 图形学领域:TinyGL

音视频基础

封装/解封装

  • 封装:将视频码流/音频码流按照一定的格式存储在容器(文件)中,常见的封装格式为MP4、FLV、MKV等
  • 解封装:封装的逆过程,将音视频文件分离为音频、视频等码流的过程,比如将MP4解封装为H.264和AAC

编码/解码

  • 编码:将原始的视频数据(RGB、YUV等)压缩为视频码流,音频数据(PCM等) 压缩为音频码流的过程叫做编码。音\视频原始数据如果不经过压缩编码的话, 通常体积是非常大的,不利于存储和网络传输。常见的视频编码标准有H.263, H.264,MPEG2等,音频编码标准AAC,MP3,AC-3等
  • 解码:编码的逆过程,将音\视频压缩编码的数据转为原始数据

软编(解)/硬编(解)

  • 软编(解):使用CPU进行编解码处理
  • 硬编(解):使用非CPU进行编解码,如显卡GPU、专用的DSP、FPGA等

软编(解)的时候CPU负载重,性能比硬编(解)低,但是通用性更好;硬编 (解)性能高但是兼容性问题比较突出,特别是在Android平台,碎片化严重, MediaCodec的坑也是不少

视频帧率

视频一秒显示的帧数

音频采样率

即取样频率,指录音设备在单位时间内对模拟信号采样的多少采样率越高,声音 的质量越好,还原越真实。目前主流采集卡上,采样频率一般共分为11025Hz、 22050Hz、24000Hz、44100Hz、48000Hz五个等级

不过人耳的分辨能力有限,太高的频率也区分不出来。人耳能感觉到的最低波长 为1.7cm,即20000Hz,因此要满足人耳的听觉要求,根据奈奎斯特采样定理,1s 采样至少需要40000次,即40kHz

音视频同步

音视频文件经过解封装后,音频/视频解码便开始独立进行,渲染也是独立的。 在音频流中播放速度按照音频采样率进行,视频流中播放速度按照帧率进行

理想情况下音视频独立播放是同步的,但实际上如果不做音视频同步处理,基本 上都会出现音画不同步的问题,造成的原因主要还是一帧的播放时间很难控制在 理想情况,音视频每帧的解码和渲染的耗时不同,可能造成每帧都存在一定误差 且误差会逐渐积累

音视频同步的三种方式:视频参考音频时钟、音频/视频参考外部时钟、音频参 考视频时钟,常用的是前两种,更详细的内容见后面FFmpeg音视频同步

码率

也叫比特率,单位时间内音频/视频的比特数量。比特率越高文件大小越大,消耗的带宽也就越多,一般用kbps(千比特/秒)来表示

音频比特率:采样率 * 采样精度 * 声道数

视频比特率:帧率 * 每帧数据大小

声道数

即声音的通道的数目。常见的有单声道,双声道,4声道,5.1声道等

采样位数

即采样值,采样精度,用来衡量声音波动变化的一个参数,一般有8bit,16bit等。

分辨率

视频画面的大小或尺寸

I、P、B帧

  • I帧(内部编码帧):使用帧内压缩,不使用运动补偿,不依赖其它帧所以可以独立解码为一幅完整的图像。I帧图像的压缩倍数相对较低
  • P帧(前向预测帧):采用帧间编码方式,同时利用了空间和时间上的相关性。P帧图像只采用前向时间预测,可以提高压缩效率和图像质量
  • B帧(双向内插帧):采用帧间编码方式且双向时间预测,提供了最高的压缩比。B帧既参考之前的I帧或P帧,也参考后面的P帧,所以会造成视频帧的解码顺序和显示顺序不同

GOP

GOP(Group Of Pictures):一组连续的图像,由一个I帧开始和多个B/P帧组成,是编/解码器存取的基本单位

GOP分为闭合GOP和开放GOP

闭合GOP以一个被称为 IDR(即时解码刷新) 的I帧开始,当解码器遇到IDR帧时,会立即刷新解码图片缓冲区,在IDR之前出现的帧都不能作为该GOP内B/P帧的参考帧,这样就形成了图片序列的中断,可以防止错误的持续传递

640.png

开放GOP和闭合GOP相反,允许其内的帧参考其他GOP内的帧

两种GOP更详细的作用和差异: https://cloud.tencent.com/developer/article/1919128

DTS、PTS

  • DTS(Decoding Time Stamp):解码时间戳,告知解码器在什么时间点解码这一帧的数据
  • PTS(Presentation Time Stamp):显示时间戳,告知播放器什么时间点显示这一帧数据

下面这张图非常方便的辅助理解GOP,I/B/P帧和DTS和PTS

111.png

FFmpeg基础

常用so

  • libavformat :封装了Protocal/demuxer/muxer层,FFmpeg能否支持一种封装格式的视频的封装和解封装,依赖这个库。例如mp4、flv等容器的封装和解封装;rtmp、rtsp等协议的封装和解封装;
  • libavcodec :编码解码模块,封装了codec层。如libx264、FDK-AAC等库因为License的关系不会被FFmpeg带上,如需要可以通过第三方codec插件的形式注册添加到FFmpeg
  • libavutil :核心工具模块,提供音视频处理的一些基本操作,比如数学函数、错误码及错误处理、内存相关管理等
  • libswresample :音频重采样,可以对数字音频进行声道数、数据格式、采样率等多种基本信息的转换
  • libswscale :图像格式转换,比如将YUV转RGB等
  • libavfilter :音视频滤镜模块,包含了音频特效和视频特效的处理

重要结构体

  • AVFormatContext :在FFmpeg开发中是一个贯穿整个流程的数据结构,存储了整个音视频流和metadata信息
  • AVCodecContext :存储视频/音频流使用解码方式的相关数据
  • AVStream :存储一个视频/音频流的相关数据,每个AVStream对应一个AVCodecContext,每个AVCodecContext对应一个AVCodec,包含该视频/音频流对应的编解码器
  • AVPacket :保存了解复用(demuxer)之后的压缩数据和附加信息,比如pts,dts,duration等
  • AVFrame :保存解码后的原始数据
1743.png

最重要的结构体大概就这几个,我们先知道有这些东西和作用即可,后面在开发中慢慢完善知识树

时间基(time_base)

在FFmpeg中,对时间基time_base的理解也是一个非常基础且重要的点

time_base是时间戳的单位,时间戳乘以时间基可以得到实际的时间值(以秒为单位),我们可以把time_base看作一个时钟脉冲,dts/pts等看作时钟脉冲的计数

例如某一个视频帧dts是40,pts是100,time_base是1/1000秒,那么该视频帧的解码时间点是40ms,显示时间点是100ms

FFmpeg有三种time_base,用ffprob探测音视频文件时可以看到有tbr,tbn,tbc

范例:使用 ffprobe 探测媒体文件格式

PS D:\download\baiducloud\拉勾:架构师的 36 项修炼> ffprobe.exe 'a.mp4'
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'a.mp4':
... ...
  Duration: 00:07:00.34, start: 0.000000, bitrate: 1728 kb/s
  Stream #0:0[0x1](eng): Video: h264 (Main) (avc1 / 0x316371), yuv420p(progressive), 1920x1080 [SAR 1:1 DAR 16:9], 1407 kb/s, 23.98 fps, 23.98 tbr, 24k tbn (default)
... ...
  • tbn对应容器中的时间基,值为AVStream.time_base的倒数
  • tbc对应编解码器中的时间基,值为AVCodecContext.time_base的倒数
  • tbr是从视频流中猜算得到,可能是帧率或者场率(帧率的2倍)

命令范例

常用参数解释

- i 表示input,即输入文件
- f 表示format,即输出格式
- vn 表示vedio not,即输出不包含视频
- ar 设定采样率
- ac 设定声音的channel数
- ab 音频数据流量,一般选择32、64、96、128
- acodec 设定声音解码器
- y 覆盖输出文件

mp3

视频转换为 MP3 格式

范例: 视频转音频 mp3,音频编码 libmp3lam,音频采样频率 44100,2声道,声音比特率 32k,截取一段时间,声音放大 1.1 倍,音频1.7速度播放,文件存在直接覆盖

ffmpeg.exe -i "a.mp4"  -acodec libmp3lame -ar 44100 -ac 2 -ab 32k -ss 00:04:30 -to 00:25:03  -af volume=1.1 -filter:a "atempo=1.7" -y  b.mp3
ffmpeg.exe -i b.mp3 -vn -ar 44100 -ac 2 -ab 32k  -filter:a "atempo=1.7" -y  c.mp3
-i代表输入参数
-acodec aac(音频编码用AAC) 
-ar 设置音频采样频率
-ac  设置音频通道数
-ab 设定声音比特率
-ss 开始截取的时间点
-to 结束时间点
-t 00:02:00  截取音频时间长度
-af 'volume=2' 放大音量(volune=1表示正常音量,=0.5表示变为一半,=2表示变为2倍)
-filter:a "atempo=2.0" 调整音频速率,倍率调整范围为[0.5, 2.0]。注意和 -ss -to -t 截取时间一起时,以速率时间为准,如 1.7 倍时间的起点。
#88MB mp4 转--> 18MB mp3 压缩-->4.5MB mp3
ffmpeg.exe -i 'a.mp4' -vn a.mp3
ffmpeg.exe -i 'a.mp4'  -acodec libmp3lame -ar 44100 -ac 2 -ab 32k -y  a.mp3

解释:
-i代表输入参数
-acodec aac(音频编码用AAC) 
-ar 设置音频采样频率
-ac  设置音频通道数
-ab 设定声音比特率
-af 'volume=2' 放大音量(volune=1表示正常音量,=0.5表示变为一半,=2表示变为2倍)

#音频文件截取指定时间部分
ffmpeg64.exe -i 124.mp3 -vn -acodec copy -ss 00:00:00 -t 00:01:32 output.mp3

解释
-ss 开始截取的时间点
-t 截取音频时间长度 

其他

# 把MP4转换成MP3
ffmpeg -i old.mp4 new.mp3

# 将MP4转换成wav
ffmpeg -i 606120.mp4 -f wav -vn test.wav

# 生成16位、单声道、采样率为16k。其中“-f wav”是必不可少的,否则生成的就是pcm数据,而非wav。
ffmpeg -i 123.mp4 -acodec pcm_s16le -f s16le -ac 1 -ar 16000 -f wav 123.wav

# 如果只想获得原始的pcm文件,可以用下面的命令:
ffmpeg -i 123.mp4 -acodec pcm_s16le -f s16le -ac 1 -ar 16000 123.pcm

# 从视频中提取aac文件
ffmpeg -i 1.mp4 -vn -y -acodec copy vn.aac

音频格式转换:wav转mp3

ffmpeg -i input.wav -acodec libmp3lame -aq 4 -ar 44100 -ab 320k -ac 2 output.mp3 

# 将双通道wav转换成pcm:
ffmpeg -y -i 1.wav -acodec pcm_s16le -f s16le -ac 1 -ar 16000 1.pcm

# pcm转换成wav
ffmpeg -f s16le -ar 16000 -ac 1 -i input.pcm output.wav
# s16le:有符号、整形、小端
# 把mp3转换为wav文件
ffmpeg -i input.mp3 output.wav

删除静音片段

ffmpeg -i vn436.aac -af silenceremove=stop_periods=-1:stop_duration=1:stop_threshold=-30dB vn436.aac
# 经过测试,可以有效删除静音片段,可以用于aac音频格式。
# stop_duration:静音片段阈值,超过多少秒就会删除静音片段。

增大音量

#-af 'volume=2' 放大音量(volune=1表示正常音量,=0.5表示变为一半,=2表示变为2倍)

音频拼接

# 音频拼接,将多个MP3按顺序拼接生成一个mp3
ffmpeg -i "concat:1.mp3|ding.mp3|2.mp3" -acodec copy output.mp3

音频补白

# 手动生成一条10秒长的空白音频 :test.mp3
ffmpeg -f lavfi -t 10 -i anullsrc test.mp3 -y

判断是否有音轨-ffprobe

ffprobe -i INPUT -show_streams -select_streams a -loglevel error
# loglevel error,控制日志打印级别

调整音视频播放速度

调整视频速率

#调整视频速率的原理为:修改视频的pts,dts
ffmpeg -i input.mkv -an -filter:v "setpts=0.5*PTS" output.mkv

#注意:视频调整的速度倍率范围为:[0.25, 4]
#如果只调整视频的话最好把音频禁掉。

#对视频进行加速时,如果不想丢帧,可以用-r 参数指定输出视频FPS,方法如下:
ffmpeg -i input.mkv -an -r 60 -filter:v "setpts=2.0*PTS" output.mkv

调整音频速率

#调整视频速率的原理为:简单的方法是调整音频采样率,但是这种方法会改变音色, 一般采用通过对原音进行重采样,差值等方法
ffmpeg -i input.mkv -filter:a "atempo=2.0" -vn output.mkv
#注意:倍率调整范围为[0.5, 2.0]

#调整4倍可采用以下方法
#ffmpeg -i input.mkv -filter:a "atempo=2.0,atempo=2.0" -vn output.mkv

#如果需要同时调整音频和视频,可以采用如下的方式来实现:
ffmpeg -i input.mkv -filter_complex "[0:v]setpts=0.5*PTS[v];[0:a]atempo=2.0[a]" -map "[v]" -map "[a]" output.mkv

mp4

压缩

# 88MB mp4 压缩--> 24MB mp4
ffmpeg -i input.mp4 -vf scale=1280:-1 -c:v libx264 -preset veryslow -crf 24 output.mp4

合并 mp4 文件 要使用ffmpeg合并两个MP4文件,你可以使用concat协议或者concat过滤器。以下是两种方法的示例: 范例:使用concat协议

cat input.txt
file '/path/to/first_video.mp4'  
file '/path/to/second_video.mp4'

ffmpeg -f concat -safe 0 -i input.txt -c copy output.mp4

png

压缩

图片压缩工具有很多,这里使用 FFmpeg 视频处理工具

透明度/动画 支持

  • jpg 不支持透明度,不支持动画
  • png 支持透明度,支持动画(apng,效果不好)
  • webp 支持透明度,支持动画

浏览器支持

  • jpg 现代浏览器都支持
  • png 现代浏览器都支持
  • webp 大部分现代浏览器支持

范例: 300KB png 格式文件压缩测试

#png格式 有中间调色板,300-->102KB
ffmpeg -i t.png  -vf palettegen=max_colors=256:stats_mode=single -y  out_3.png  
ffmpeg -i t.png -i out_3.png -lavfi "[0][1:v] paletteuse" -pix_fmt pal8 -y c.png

#webp格式 300-->32KB
#-lossless 是设置无损压缩
ffmpeg -i t.png  -vf scale=iw:ih -codec libwebp -lossless 0 -quality 75  -y d.png

#jpg格式 300-->78KB
# -q:v选项来设置压缩质量,值为2。这个值范围为0到31,其中0为最高质量,31为最低质量。
ffmpeg -i t.png -q:v 4 -y output.jpg

#改变大小
#图片压缩为尺寸为640x480像素
ffmpeg -i t.png -vf scale=640:480 -y output.jpg
#单纯调整图片大小,无损压缩把图片宽高缩小2倍iw/2:ih/2
ffmpeg -i t.png -vf scale=iw/2:ih/2 -y a.png

# 静态输出
-loglevel quiet

范例: 准备 powershell 脚本 png-compress.ps1

# 指定包含 PNG 图片的目录  
$pngSourceDirectory = "images"  
# 指定输出目录  
$pngDestDirectory = "d_images"  

# 复制文件夹及其内容  
Copy-Item -Path $pngSourceDirectory -Destination $pngDestDirectory -Recurse -Force

# 确保输出目录存在  
if (!(Test-Path -Path $pngDestDirectory -PathType Container)) {  
    New-Item -ItemType Directory -Force -Path $pngDestDirectory  
}  

# 获取目录下的所有 PNG 文件  
$pngFiles = Get-ChildItem -Path $pngSourceDirectory -Filter *.png  

# 遍历每个 PNG 文件  
foreach ($pngFile in $pngFiles) {  
    # 定义输入文件路径  
    $inputPath = $pngFile.FullName  
    # 定义输出文件路径,使用 "dest 目录/文件名" 的格式  
    $outputPath = Join-Path $pngDestDirectory ($pngFile.BaseName + ".png")  

    # 构建 FFmpeg 命令  
    $ffmpegCommand = "ffmpeg -i `"$inputPath`" -vf 'palettegen=max_colors=256:stats_mode=single' -loglevel quiet -y -y  out_3.png; ffmpeg -i `"$inputPath`" -i out_3.png -lavfi '[0][1:v] paletteuse' -pix_fmt pal8 -loglevel quiet -y  `"$outputPath`"; Remove-Item -Path out_3.png  -Force"  

    # 执行 FFmpeg 命令  
    Invoke-Expression $ffmpegCommand  

    # 可选:打印执行的信息  
    Write-Host "Processed $($pngFile.Name) -> $($pngFile.BaseName).png in $pngDestDirectory"  
}

运行脚本。你可以使用以下命令之一来执行脚本:

# 如果你的执行策略允许运行脚本,并且你对脚本所在的目录有信任,你可以直接调用脚本:
.\png-compress.ps1

#如果你需要绕过执行策略(请小心使用,确保你信任脚本的内容),你可以使用
PowerShell -NoProfile -ExecutionPolicy Bypass -File .\png-compress.ps1

gif

视频 与 gif转换

  • 截取5秒长度生成gif
ffmpeg -t 5 -ss 00:00:02  -f gif -i 8_9f6fa300bacded7b.mp4 /data/dev/think_file/html/8.gif
#-ss 00:00:02: 从第二秒开始
#-t 5:   持续5秒
#-f gif: 指定文件格式为gif(因为输出文件名的后缀是gif,所以这个参数可以不添加)
  • 生成指定宽高的gif
ffmpeg -v quiet -t 5 -ss 00:00:02 -i 8_9f6fa300bacded7b.mp4 -s 320x180 -y /data/dev/think_file/html/8_2.gif  

#-v quiet:  不显示版本和处理过程等log信息
#-s 320x180: 指定生成的gif图的宽高
#-y : 文件存在时自动覆盖,不再询问
  • 生成gif时指定fps

查看原视频的fps:

ffprobe -v error -select_streams v -of default=noprint_wrappers=1:nokey=1 -show_entries stream=r_frame_rate 8_9f6fa300bacded7b.mp4
#原视频的fps并不高,值为30

ffmpeg -v quiet -t 5 -ss 00:00:02 -i 8_9f6fa300bacded7b.mp4 -f gif -r 15 -y /data/dev/think_file/html/8_3.gif
# -r: 指定fps
  • gif转mp4
ffmpeg -i 8_2.gif 8_2.mp4

压缩

  • 查看原gif的宽高及大小
[root@blog img]$ file dog.gif
dog.gif: GIF image data, version 89a, 250 x 250
  • 缩放gif
ffmpeg -i dog.gif -s 120x120 dog_tmb.gif

压缩

Gif压缩工具Gifsicle下载地址:https://eternallybored.org/misc/gifsicle/

加入到 Path 变量中

gifsicle XXX.gif --optimize=3 --colors 128 --lossy=1000 -o NEW.gif
# --optimize 优化等级[1,2,3]  --lossy=有损优化等级[0~无穷] --colors Num Num必须在2到256之间

MP4转Gif

Ffmpeg+Git Bash+Gifsicle组合转为超小体积的Gif

gif.sh

set -e
# 全局设定,fps帧数,scale分辨率为320
fps=8 
scale=320:-1
scale_=320
interpolation=lanczos

# max_colors设置彩色数量,彩色越小gif体积越小
max_colors=128 # up to 256
reserve_transparent=on
stats_mode=diff # chosen from [full, diff, single]

# for paletteuse
dither=sierra2 # chosen from [bayer, heckbert, floyd_steinberg, sierra2, sierra2_4a, none]
bayer_scale=5 # [0, 5]. only works when dither=bayer. higher means more color banding but less crosshatch pattern and smaller file size
diff_mode=rectangle # chosen from [rectangle, none]
new=on # when stats_mode=single and new=on, each frame uses different palette

# 文件用时间命名
time=$(date +"%Y-%m-%d--%H-%M-%S")

# 设定Gif播放率,0.5为2倍播放,1为正常播放,4为放慢4倍播放
ffmpeg -i $1 -vf "setpts=0.5*PTS" output_temp.mp4

# 使用上面的参数加入到命令中执行,$1为输入的文件名
ffmpeg -i output_temp.mp4 -vf "fps=$fps,scale=$scale:flags=$interpolation,split[split1][split2];[split1]palettegen=max_colors=$max_colors:reserve_transparent=$reserve_transparent:stats_mode=$stats_mode[pal];[split2][pal]paletteuse=dither=$dither:bayer_scale=$bayer_scale:diff_mode=$diff_mode:new=$new" -y new_mp4_temp.gif

# 删除产生的临时文件
rm output_temp.mp4

# --optimize 优化等级[1,2,3]  --lossy=有损优化等级[0~无穷]
gifsicle new_mp4_temp.gif --optimize=3 --lossy=100 -o net_output.gif

# 删除产生的临时文件
rm new_mp4_temp.gif

# 对转换后的文件进行重命名
mv net_output.gif $time\_fps=$fps\_scale=$scale_\_max_colors=$max_colors.gif

使用:

gif.sh a.mp4

类似压缩方法

尝试把 demo.mp4 生成高质量的 gif. 同样 fps=10, lanczos 插值到 480P 分辨率.

ffmpeg -i demo.mp4 -vf "fps=10,scale=480:-1:flags=lanczos,split[split1][split2];[split1]palettegen[pal];[split2][pal]paletteuse=dither=none" demo_dither_none.gif


  • -i demo.mp4:指定输入文件为 demo.mp4。
  • -vf:指定视频滤镜链。这条命令中包含多个滤镜,它们通过分号和标签(labels)串联起来。
    • fps=10:将视频的帧率限制为 10 帧每秒。实际上,由于你最终是要生成 GIF(通常只包含一帧或多帧的动画),这个设置对最终结果的影响不大,但它会确保 ffmpeg 在处理过程中以这个帧率来工作。
    • scale=480:-1:flags=lanczos:调整视频的大小,使其宽度为 480 像素,高度自动计算以保持原始纵横比。lanczos 是一个高质量的缩放算法。
    • split[split1][split2]:将视频流分割成两个相同的流,分别标记为 split1 和 split2。这允许你对同一个视频流进行不同的处理。
    • [split1]palettegen[pal]:使用 split1 流的视频帧来生成一个调色板(palette)。调色板是 GIF 动画中用于定义所有颜色的集合。这个调色板被标记为 pal。
    • [split2][pal]paletteuse=dither=none:使用 split2 流的视频帧和前面生成的调色板 pal 来创建一个 GIF 动画。dither=none 表示不使用任何抖动算法来平滑颜色过渡。抖动通常用于减少颜色数量时的颜色近似,但在这个设置中它被禁用了。
  • demo_dither_none.gif:指定输出文件的名称

最终,这条命令会从 demo.mp4 中提取一帧(实际上,由于 fps 滤镜和可能的默认行为,可能会提取多帧,但通常 GIF 生成只使用第一帧),将其大小调整为宽度 480 像素,生成一个调色板,并使用这个调色板将视频帧转换为 GIF 格式,同时禁用颜色抖动。

打开抖动

gifgen.sh:

set -e

# global filter
fps=10
scale=480:-1
interpolation=lanczos

# for palettegen
max_colors=256  # up to 256
reserve_transparent=on
stats_mode=full  # chosen from [full, diff, single]

# for paletteuse
dither=bayer  # chosen from [bayer, heckbert, floyd_steinberg, sierra2, sierra2_4a, none]
bayer_scale=3  # [0, 5]. only works when dither=bayer. higher means more color banding but less crosshatch pattern and smaller file size
diff_mode=rectangle  # chosen from [rectangle, none]
new=off  # when stats_mode=single and new=on, each frame uses different palette

ffmpeg -i $1 -vf "fps=$fps,scale=$scale:flags=$interpolation,split[split1][split2];[split1]palettegen=max_colors=$max_colors:reserve_transparent=$reserve_transparent:stats_mode=$stats_mode[pal];[split2][pal]paletteuse=dither=$dither:bayer_scale=$bayer_scale:diff_mode=$diff_mode:new=$new" -y $2

使用

./gifgen.sh demo.mp4 res.gif