ffmpeg 命令行工具
此次为一些基本命令,其他命令可以类推扩展
基础信息查询命令
- ffmpeg -help 可出现很多查询命令,此次不在详诉
录制命令
- mac系统
ffmpeg -f avfoundation -video_size 1920x1080 -r 30 \ -i "2:0" -ac 2 \ -vcodec libx264 -maxrate 2000k \ -bufsize 2000k -acodec libmp3lame -ar 44100 -b:a 128k \ -f flv "rtmp://example.com/path?key=xx" # rtmp 地址 # -f: 指定使用avfoundation(mac下独有的音视频采集处理库)采集数据 # -i: 指定采集数据源,使用文件索引号 # -r: 指定帧率 # 2:0 代表 第二块显示屏:内建麦克风 - win系统
-
基于GDI录屏,FFmpeg直接从Windows的GDI gdigrab中拷贝图形,然后再合成视频。
优点:省事,不需要依赖其他库
缺点:内存拷贝性能不是很好,没有硬件加速
ffmpeg -y -f gdigrab -t 30 -r 30 -i desktop -c:v libx264 -s 1280x720 desktop.mp4 # -t 表示录屏时间:缺省没有录屏时间限制 # -i: 指定采集数据源 # -r: 指定帧率 # c:v 表示编码器的库为libx264-
基于directshow方案,FFmpeg从directshow驱动接口中读取。
优点:性能较好,directshow可以控制采样帧率,硬件加速。
缺点:需要安装directshow驱动,不过directshow驱动网上很容易可以找到并下载。
ffmpeg -y --framerate 25 -f dshow -i video="screen-capture-recorder" -s 1280x720 -r 25 -t 30 yo.mp4 # 默认使用libx264编码 -
分解/复用命令
ffmpeg -i test.mp4 -c:v copy -an test.h264
# -c:v copy 即-vcodec copy 复制成h264格式文件
# -an 即 禁止音频
# 将h264的mp4转码成h265的MP4,并将MP4中moov元数据提前
ffmpeg -i webplay.mp4 -c:v libx265 -c:a copy -movflags faststart webplay_h265.mp4
# -movflags faststart 将moov的元数据提前,这样播放器可以对mp4边获取边播放
# -c:v libx265 对video使用libx265编码即为h.265
处理原始数据命令
提取YUV数据
ffmpeg -i test.mp4 -c:v rawvideo -an -pix_fmt yuv420p out.yuv
# -c:v rawvideo 表示使用原始数据
# -pix_fmt 表示使用视频像素格式使用yuv420p, 其他的还有4:2:2 4:4:4
# -an 即 禁止音频
提取PCM数据
ffmpeg -i test.mp4 -vn -ar 44100 -ac2 -f s16le out.pcm
# -ar audio read 音频采样率
# -ac audio channel 声音通道 (单声道,双声道,立体声), 2表示双声道
# -f pcm 表示数据存储格式,s表示有符号,16表示16位表示,l表示little end
# 播放pcm数据
ffplay -ar 44100 -ac 2 -f s16le out.pcm
AVContent
| 参数 | 类型 | 说明 |
|---|---|---|
| b | 整数 | 设置音频与视频码率,默认是 200 kb/s,可单独使用 b:v 设置视频码率,使用 b:a 设置音频码率 |
| ab | 整数 | 设置音频的码率,默认是 128 kb/s |
| g | 整数 | 设置视频 GOP 大小,默认是 12 帧一个 GOP |
| ar | 整数 | 设置音频采样率,默认为 0 |
| ac | 整数 | 设置音频通道数,默认为 0 |
| bf | 整数 | 设置连续编码为 B 帧的个数,默认为 0 |
| maxrate | 整数 | 最大码率设置,与 bufsize 一同使用,默认为 0 |
| minrate | 整数 | 最小码率设置,配合 maxrate 和 bufsize 可以设置为 CBR 模式,默认为 0 |
| bufsize | 整数 | 设置控制码率的 buffer 大小,默认为 0 |
| keyint_min | 整数 | 设置关键帧最小间隔,默认为 25 |
| sc_threshold | 整数 | 设置场景切换支持,默认为 0 |
| me_threshold | 整数 | 设置运动估计阈值,默认为 0 |
| mb_threshold | 整数 | 设置宏块阈值,默认为 0 |
| profile | 整数 | 设置音视频的 profile,默认为 -99 |
| level | 整数 | 设置音视频的 level,默认为 -99 |
| timecode_frame_start | 整数 | 设置 GOP 的开始时间,需要在 non-drop-frame 默认情况下使用 |
| channel_layout | 整数 | 设置音频通道的布局格式 |
| threads | 整数 | 设置编解码工作的线程数 |
ffmpeg -i ~/sample.mkv -vf scale=1024*768 -vcodec mpeg4 -b:v 200k -bufsize 200k -b:a 200k -r 15 output.mp4
# 设置视频码率200kbps,音频码率200kbps 帧率15的output.mp4 输出 不过,ffmpeg官方wiki比较建议,设置b:v时,同时加上 -bufsize
-bufsize 用于设置码率控制缓冲器的大小,设置的好处是,让整体的码率更趋近于希望的值,减少波动。(简单来说,比如1 2的平均值是1.5, 1.49 1.51 也是1.5, 当然是第二种比较好)
-vf scale 用以等比缩放分辨率为1024*768, 还可以设成720*540
用-fs选项。
例如设置输出文件的最大的size为10M
ffmpeg -i input.avi -fs 10MB output.mp4
裁剪与合并命令
裁剪
ffmpeg -i test.mp4 -ss 00:00:00 -t 10 out.mp4
# -ss 裁剪开始的时间 -t 多长时间 10s
合并
ffmpeg -f concat -i inputs.txt out.flv
# -f concat 表示进行拼接
# inputs.txt 内容为 'file filename' 格式 例如
file '1.flv'
file '2.flv'
图片、视频互转命令
视频转图片
ffmpeg -i test.mp4 -r 1 -f image2 image-%3d.jpeg
# -r 制定转换图片的帧率,1 表示每秒1张
# image2 表示图片的协议格式
# image-%3d.jpeg 3d表示3个数字
图片转视频
ffmpeg -i image-%3d. -r 10 out.mp4
# -r 10 表示定义帧率为10,这样输出的视频就是每秒播放十帧
直播相关命令
ffmpeg -re -i out.mp4 -c copy -f flv rtmp://server/live/streamName
# -f 指定流格式
# -re 减慢帧率速度,与拉流保持同步
ffmpeg -i rtmp://server/live/streamName -c copy test.mp4
# 直播拉流
nginx rtmp流地址设置
rtmp {
server {
listen 1999;
# 直播流设置
application rtmplive {
live on;
# 最大链接数
max_connections 1024;
}
application hls {
live on;
hls on;
hls path /temp/hls;
hls fragment 1s;
}
}
}
推流本地视频和摄像头
ffmpeg -f avfoundation -framerate 30 -i 1:0 -f avfoundation -framerate 30 -video_size 640x480 -i "0" -c:v libx264 -preset ultrafast -filter_complex 'overlay=main_w-overlay_w-10:main_h-overlay_h-10' -acodec libmp3lame -ar 44100 -ac 2 -f flv rtmp://localhost:1999/live/home
画中画推流
ffmpeg -re -i ./th.jpeg \
-i "1.m3u8" \
-i "2.m3u8" \
-i "3.m3u8" \
-filter_complex "[1]delogo=x=1:y=1:w=100:h=100:show=1[mainpip];[2]delogo=x=1:y=10:w=950:h=100:show=0[pip2];[3]delogo=x=1:y=10:w=950:h=100:show=1[pip3];[pip3]scale=iw/2:ih/2[pip_3];[pip2]scale=iw/2:ih/2[pip_2];[0][pip_2]overlay=main_w-overlay_w:0[mainpip1];[mainpip1][pip_3]overlay=1:1[mainpip2];[mainpip2][mainpip]overlay=main_w/2-overlay_w/2:0" \
-vcodec libx264 -acodec aac -b:a 96k -r 30 \
-f flv "rtmp://localhost:1999/live/home" -async 1 -vsync 1
注释:
-
按照-i 对应输入按照0,1,2,3 输入在filter_complex中使用
-
[1]delogo=x=1:y=1:w=100:h=100:show=1[mainpip];
- [1] 表示第一个视频流即 1.m3u8,
- delogo 为去水印方法,x,y表示去水印在视频中位置,w,h表示去水印的宽高
- show =1 表示去水印区域,1会显示绿色框,最后一个为处理之后输出的视频流 [mainpip]
-
[pip3]scale=iw/2:ih/2[pip_3];
- [pip3] 为前面处理后输出的流
- scale 对视频进行缩放处理,
- iw,ih为视频原始宽高,这里表示缩小1倍后输出为[pip_3]
-
[mainpip2][mainpip]overlay=main_w/2-overlay_w/2:0
- overlay 表示视频叠加
- [mainpip2]表示主视频 [mainpip]表示画中画视频
- main_w,main_h 为主视频的宽高,overlay_w,overlay_h为画中画的宽高
- overlay = x:y , x,y为画中画所在主视频的位置
-
-async 1 -vsync 1 防止出现 warning: Past duration 0.603386 too large
各种滤镜命令
# 将视频裁剪200像素
ffmpeg -i test.mp4 -vf crop=in_w-200:in_h-200 -c:v copy -c:a copy out.mp4
# -vf 视频滤镜 crop 裁剪 in_w,in_h表示输入的视频宽度和高度
# -vf crop=out_w:out_h:x:y 裁剪输出输出宽度x,高度y的视频
ffmpeg 开发介绍
日志系统
- 库函数:
include <libavutil/log.h> - 函数
av_log_set_level(AV_LOG_DEBUG)设置日志级别(AV_LOG_DEBUG是最低级别) av_log(NULL, AV_LOG_INFO, "...%s\n", op)参数:NULL, 日志级别,其他同prinf- 常用日志级别
AV_LOG_ERROR > AV_LOG_WARNING > AV_LOG_INFO > AV_LOG_DEBUG
文件处理
-
avpriv_io_delete(path)文件删除 path 为文件路径 return int类型,< 0 表示出错,>=0 成功 -
avpriv_io_move(src, dist)文件重命名 src为源文件路径,dist为目的文件 return int类型,< 0 表示出错,>=0 成功 -
int ret = avio_open_dir(&ctx, path, NULL)打开目录AVIODirContext *ctx = NULL; //结构体AVIODirContext为 操作目录的上下文return ret 为 int类型错误码(可使用av_err2str(ret)转成字符串) ,< 0 表示出错,>=0 成功 -
avio_read_dir(&ctx, &AVIODirEntry)读取目录 -
avio_close_dir(&ctx)关闭目录 demo:
#include <libavutil/log.h>
#incldue <libavformat/libavformat.h>
int main(int argc, char* argv[]) {
av_log_set_level(AV_LOG_INFO);
int ret = 0;
AVIODirContext *ctx = NULL;
AVIODirEntry *entry = NULL;
ret = avio_open_dir(&ctx, "./", NULL);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, "canot open dir:%s\n", av_err2str(ret);
return -1;
}
while(1) {
ret = avio_read_dir(ctx, &entry); // ret 依然是返回错误码或者成功码
if (ret < 0) {
// 出错之后需要释放
goto __fail;
}
if(!entry) break; // 不存在entry就为最后一项
av_log(NULL, AV_LOG_INFO, "%12"PRID64"%s \n", entry->size, entry-name); // PRID64 为64位宏,文件的大小和内容
// 释放掉entry的内存空间
avio_free_directory_entry(&entry);
}
__fail:
avio_close_dir(&ctx);
return 0;
}
多媒体操作
$0-n 为函数参数第一个到第n个
常用结构体:
AVFormatContext *fmt_ctx = NULL;格式上下文,链接桥梁,多媒体文件的上下文AVStream流或轨内容AVPacket pkt流中的包
打印音/视频信息
av_register_all()注册程序,类似于初始化程序,3.3.9以后废弃avformat_open_input(&AVFormatContext, path, type, options)根据后缀名识别输入的多媒体格式,获取到格式上下文- $0: 表示输入格式上下文 AVFormatContext
- $1: path 多媒体路径
- $2: type 表示输入文件的格式,如果使用NULL,ffmpeg会从路径后缀作为文件格式
- $3 options 打开多媒体的参数,常用NULL即可
avformat_close_input(&AVFormatContext)释放到上下文av_dump_format(AVFormatContext,index, vName, type)打印多媒体信息- $0: 表示输入格式上下文 AVFormatContext
- $1: index 表示输入流的索引值,一般使用0
- $2: vName 多媒体文件名字
- $3: type 表示0 输入流或者 1 输出流
// dump.c 显示文件mate信息
/**
* 使用动态库链接方式编译
gcc -g dump.c `pkg-config --cflags --libs lavformat lavutil` -o dump && ./dump
*/
#include <libavutil/log.h>
#include <libavformat/avformat.h>
int main(int argc, char* argv[]){
int ret;
AVFormatContext *fmt_ctx = NULL;
av_log_set_level(AV_LOG_DEBUG);
// av_register_all(); 已废弃可以不用
ret = avformat_open_input(&fmt_ctx, "./webplay.mp4", NULL, NULL);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, "can't open file: %s n", av_err2str(ret));
return -1;
}
av_dump_format(fmt_ctx, 0, "./webplay.mp4", 0);
avformat_close_input(&fmt_ctx);
return 0;
}
抽取音频数据
av_init_packet(&AVPacket)初始化数据包结构体- $0: 获取包数据
av_find_best_stream(AVFormatContext, type, index, _index, codec, flag)在多媒体文件中找到最好的一路流- $0: AVFormatContext fmt_ctx文件格式上下文
- $1: 获取流的类型 (音频流 宏:
AVMEDIA_TYPE_AUDIO,视频流,字幕流) - $2: 处理流的索引号,使用-1 即可
- $3: 相关流的索引号,
- $4: 处理流的解码器 默认填NULL
- $5: 标志 不清楚时填0
av_read_frame(AVFormatContext, AVPacket)读取流中数据包- $0: AVFormatContext fmt_ctx文件格式上下文
- $1: AVPacket pkt 在av_init_packet 函数中获取的数据包
av_packet_unref(&AVPacket)将数据包引用数减1释放
// 抽取音频信息,此时输出的acc数据不能播放,需要添加音频数据头 (todo)
#include <stdio.h>
#include <libavutil/log.h>
#include <libavformat/avformat.h>
int main(int argc, char* argv[]){
int ret, audio_index;
int return_code = 0;
char* src = NULL;
char* dst = NULL;
AVFormatContext *fmt_ctx = NULL;
AVPacket pkt;
av_log_set_level(AV_LOG_DEBUG);
// av_register_all();
// 读取2个参数
if (argc < 3) {
av_log(NULL, AV_LOG_ERROR, "请输入 输入媒体和输出媒体");
return -1;
}
src = argv[1],dst = argv[2];
if (!src || !dst) {
av_log(NULL, AV_LOG_ERROR, "src or dst 为空!\n");
}
ret = avformat_open_input(&fmt_ctx, src, NULL, NULL);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, "can't open file: %s n", av_err2str(ret));
return -1;
}
FILE* dst_fd = fopen(dst, "wb");
if (!dst_fd) {
av_log(NULL, AV_LOG_ERROR, "文件不能打开\n");
return -1;
}
av_dump_format(fmt_ctx, 0, src, 0);
// 获取流
ret = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
if (ret < 0) {
av_log(NULL,AV_LOG_ERROR, "无法打开最好的流数据!\n");
return_code = -1;
goto __fail;
}
audio_index = ret;
av_init_packet(&pkt);
while(av_read_frame(fmt_ctx, &pkt) >= 0) {
if (pkt.stream_index == audio_index) {
int len = fwrite(pkt.data, 1, pkt.size, dst_fd); // pkt.data 数据 pkt.size 大小 pkt.stream_index 流的索引
if (len != pkt.size) {
av_log(NULL, AV_LOG_WARNING, "数据长度和文件长度不相同!\n");
}
}
av_packet_unref(&pkt);
}
__fail:
if (dst_fd) fclose(dst_fd);
avformat_close_input(&fmt_ctx);
return return_code;
}
音视频同步
基本概念
- 时间戳
- PTS: Presentation timestamp 展示时间戳
- DTS: Decoding timestamp 解码时时间戳 因B帧打乱解码时间戳,有b帧情况下DTS和PTS不一致
I(intra)帧: 关键帧,一个包的首个帧一般是I帧,帧内压缩,本身图像中不是很敏感的数据压缩
B(bidirectional)帧:前后参考帧,只记录变化的数据,一般参考3帧,前后三帧内相同数据不在重复压缩
P(predicted)帧:帧间压缩,向前参考帧, 参考上一帧数据的,只记录变化的数据
- 时间戳顺序
实际帧顺序 I B B P
存放帧顺序 I P B B
解码时间戳 1 4 2 3
展示时间戳 1 2 3 4
- 获取PTS
- AVPacket中PTS
- AVFrame 中PTS 一般和AVPacket一直,有时候也为0
- av_frame_get_best_effort_timestamp(pFrame) 获取准确的时间戳 从Frame帧报中获取最合适的PTS, 这里要使用av_q2d进行时间基换算
- 时间基
- tbr 帧率 time base of rate 一般30帧,帧率就是 1s/30
- tbn 流时间基 time base of stream
- tbc 解码时间基 time base of codec
- 计算当前帧的PTS
- PTS *= av_q2d(video_stream->time_base);
- av_q2d(AVRotional a) {return a.num / (double)a.den;} // 讲分数形式转化成double形式
- 计算下一帧的PTS
- video_clock: 预测的下一帧视频的PTS 上一帧的pts+frame_delay
- frame_delay: 1 / tbr 下一帧的时间间隔
- audio_clock: 音频当前播放的时间戳
- 音视频同步方式
- 视频同步到音频 (最常用):一般展示第一帧视频帧后,获得要显示的下一帧的PTS,然后设置一个定时器,当定时器超时后,刷新新的视频帧,如此反复操作。
- 音频同步视频
- 音频和视频同步到系统时钟