ffmpeg 常用命令及开发方法(持续更新)

2,851 阅读10分钟

ffmpeg 命令行工具

此次为一些基本命令,其他命令可以类推扩展

基础信息查询命令

  • ffmpeg -help 可出现很多查询命令,此次不在详诉

录制命令

  1. 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 代表 第二块显示屏:内建麦克风
    
  2. 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 帧率15output.mp4 输出 不过,ffmpeg官方wiki比较建议,设置b:v时,同时加上 -bufsize 
-bufsize 用于设置码率控制缓冲器的大小,设置的好处是,让整体的码率更趋近于希望的值,减少波动。(简单来说,比如1 2的平均值是1.51.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

注释:

  1. 按照-i 对应输入按照0,1,2,3 输入在filter_complex中使用

  2. [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]
  3. [pip3]scale=iw/2:ih/2[pip_3];

    • [pip3] 为前面处理后输出的流
    • scale 对视频进行缩放处理,
    • iw,ih为视频原始宽高,这里表示缩小1倍后输出为[pip_3]
  4. [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为画中画所在主视频的位置
  5. -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

文件处理

  1. avpriv_io_delete(path) 文件删除 path 为文件路径 return int类型,< 0 表示出错,>=0 成功

  2. avpriv_io_move(src, dist) 文件重命名 src为源文件路径,dist为目的文件 return int类型,< 0 表示出错,>=0 成功

  3. int ret = avio_open_dir(&ctx, path, NULL) 打开目录 AVIODirContext *ctx = NULL; //结构体AVIODirContext为 操作目录的上下文 return ret 为 int类型错误码(可使用av_err2str(ret)转成字符串) ,< 0 表示出错,>=0 成功

  4. avio_read_dir(&ctx, &AVIODirEntry) 读取目录

  5. 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) 根据后缀名识别输入的多媒体格式,获取到格式上下文
    1. $0: 表示输入格式上下文 AVFormatContext
    2. $1: path 多媒体路径
    3. $2: type 表示输入文件的格式,如果使用NULL,ffmpeg会从路径后缀作为文件格式
    4. $3 options 打开多媒体的参数,常用NULL即可
  • avformat_close_input(&AVFormatContext) 释放到上下文
  • av_dump_format(AVFormatContext,index, vName, type) 打印多媒体信息
    1. $0: 表示输入格式上下文 AVFormatContext
    2. $1: index 表示输入流的索引值,一般使用0
    3. $2: vName 多媒体文件名字
    4. $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) 初始化数据包结构体
    1. $0: 获取包数据
  • av_find_best_stream(AVFormatContext, type, index, _index, codec, flag) 在多媒体文件中找到最好的一路流
    1. $0: AVFormatContext fmt_ctx文件格式上下文
    2. $1: 获取流的类型 (音频流 宏:AVMEDIA_TYPE_AUDIO,视频流,字幕流)
    3. $2: 处理流的索引号,使用-1 即可
    4. $3: 相关流的索引号,
    5. $4: 处理流的解码器 默认填NULL
    6. $5: 标志 不清楚时填0
  • av_read_frame(AVFormatContext, AVPacket) 读取流中数据包
    1. $0: AVFormatContext fmt_ctx文件格式上下文
    2. $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; 
}

音视频同步

基本概念

  • 时间戳
  1. PTS: Presentation timestamp 展示时间戳
  2. 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
    1. AVPacket中PTS
    2. AVFrame 中PTS 一般和AVPacket一直,有时候也为0
    3. av_frame_get_best_effort_timestamp(pFrame) 获取准确的时间戳 从Frame帧报中获取最合适的PTS, 这里要使用av_q2d进行时间基换算
  • 时间基
    1. tbr 帧率 time base of rate 一般30帧,帧率就是 1s/30
    2. tbn 流时间基 time base of stream
    3. tbc 解码时间基 time base of codec
  • 计算当前帧的PTS
    1. PTS *= av_q2d(video_stream->time_base);
    2. av_q2d(AVRotional a) {return a.num / (double)a.den;} // 讲分数形式转化成double形式
  • 计算下一帧的PTS
    1. video_clock: 预测的下一帧视频的PTS 上一帧的pts+frame_delay
    2. frame_delay: 1 / tbr 下一帧的时间间隔
    3. audio_clock: 音频当前播放的时间戳
  • 音视频同步方式
    1. 视频同步到音频 (最常用):一般展示第一帧视频帧后,获得要显示的下一帧的PTS,然后设置一个定时器,当定时器超时后,刷新新的视频帧,如此反复操作。
    2. 音频同步视频
    3. 音频和视频同步到系统时钟