ffmpeg使用入门

1 ffmpeg安装

1.1 安装vcpkg

直接从github上下载Release版本,并进行安装

复制代码
https://github.com/microsoft/vcpkg

从GitHub克隆vcpkg存储库。存储库包含用于获取vcpkg可执行文件的脚本,以及由vcpkg社区维护的特选开放源代码库的注册表。要执行此操作,请运行:

复制代码
git clone https://github.com/microsoft/vcpkg.git

vcpkg特选注册表是一组数量超过2000个的开源库。 这些库已通过vcpkg的持续集成管道进行验证,可以协同工作。虽然vcpkg存储库不包含这些库的源代码,但它保存方案和元数据,以便在系统中生成和安装它们。

导航到vcpkg目录并执行启动脚本:

复制代码
cd vcpkg && bootstrap-vcpkg.bat

启动脚本执行先决条件检查并下载vcpkg可执行文件。就这么简单,vcpkg已安装并可供使用。

配置VCPKG_ROOT环境变量。

复制代码
set "VCPKG_ROOT=C:\path\to\vcpkg"
set PATH=%VCPKG_ROOT%;%PATH%

以这种方式设置环境变量只会影响当前终端会话。若要使这些更改在所有会话中永久存在,请通过"Windows 系统环境变量"面板进行设置。

1.2 安装其他库

一切准备就绪,在cmd命令行执行如下命令安装ffmpeg静态库:

复制代码
vcpkg.exe install ffmpeg:x86-windows-static

在cmd命令行执行如下命令安装ffmpeg动态库:

复制代码
vcpkg.exe install ffmpeg:x86-windows

安装过程如下:

使用vcpkg list可以列出当前所有安装的库,使用vcpkg remove可以卸载安装的库。

1.3 exe文件获取

vcpkg install ffmpeg默认不会安装ffmpeg.exe等工具程序,它只安装了作为库使用的 FFmpeg C/C++开发接口,如:avcodec、avformat、avutil、swscale等.lib/.dll,以及include/*/*.h头文件,因为vcpkg的目标是为C/C++项目提供开发依赖(SDK/库),而不是提供命令行工具(像ffmpeg.exe这种完整可执行程序)。要想使用ffmpeg.exe这种命令行工具,可以从官网https://ffmpeg.org/download.html下载预编译版本:

如下载 "release full"版本,解压后会包含ffmpeg.exe、ffplay.exe、ffprobe.exe三个可执行文件,将其路径添加到环境变量PATH中即可在cmd命令行直接使用这三个命令。

2 命令使用介绍

2.1 ffmpeg.exe

1. 查看帮助

-h,-?,-help,--help [arg]:显示帮助。可以指定可选参数来打印有关特定项目的帮助,如果未指定参数 arg,则仅显示基本工具选项,若指定参数,arg 可能值为:

long:除了基本工具选项之外,还打印高级工具选项。

full:输出完整的选项列表,包括编码器、解码器、解复用器、复用器、过滤器等的共享和私有选项。

type=name:输出相应类型的相关参数。如:decoder=msmpeg4v2,输出有关名为msmpeg4v2编码器的详细信息。

复制代码
Decoder msmpeg4v2 [MPEG-4 part 2 Microsoft variant version 2]:
    General capabilities: horizband dr1
    Threading capabilities: none

接下来还有以下帮助选项

-version:显示查看版本(包括子模块的详细版本信息)。

-muxers:显示所有支持的封装格式(如mp4、mkv)

-demuxers:显示所有支持的解封装格式

-devices:显示支持的音视频输入/输出设备

**-decoders:**显示所有可用的解码器

**-encoders:**显示所有可用的编码器(如H.264、AAC等)

-filters:显示可用过滤器。

**-pix_fmts:**显示支持的像素格式(如yuv420p,rgb24)

-layouts:显示标准的音频通道布局(如stereo,5.1)

-sample_fmts:显示音频采样格式(如s16,flt,dbl)

更多详细命令参数见官方文档及后续部分介绍,如果希望将帮助文档保存到文件中,可以输入ffmpeg -h full > ffmpeg_h_full.log命令,将输出结果重定向到一个文件中,然后再打开该文件即可查看完整的帮助文档。

2. 命令执行常用参数

**-y:**若输出目录已存在同名同容器格式的文件,直接输出当前文件将其覆盖而不再询问。

**-n:**不要覆盖输出文件,如果已经存在同名同容器格式的文件,立即结束运行,效果与 -y 相反。

**-f:**强制设定文件格式,需使用能力集列表中的名称(缺省是根据扩展名选择的)。

**-hide_banner:**隐藏FFmpeg版本号及一些描述信息。

**-itsoffset:**用于指定输入文件的偏移时间,后接时间值,正值向后偏移,负值向前偏移,可以对齐多个输入文件的时间轴,使它们在合并或处理时保持同步。

3. 视频帧操作

(1)基本时间剪辑

-ss:设置媒体文件的起始时间,如想要从视频的2秒开始剪切处理,我们输入"-ss 2",如想要更精确的时间也可以,如"-ss 1:23.789"表示设置从1分23秒789毫秒开始。

-t:设置媒体文件的持续时间,用法同上面-ss ,如"-t 0:13.234"表示持续13秒234毫秒。

-to:设置媒体文件的截止时间,用法与前两个相同,-to和-t是互斥的,并且-t具有优先权,两者不能同时出现使用。

复制代码
ffmpeg.exe -ss 0:10 -t 0:5 -i .\zhouxingchi.mp4 -c copy out.mp4

如以上命令将mp4文件从10秒处开始截取并持续5秒,最后将该部分另存到out.mp4文件。

通常将-ss 、-t和-to放在-i之前,这些参数用于指定输入文件的开始时间(-ss)、持续时间(-t)或结束时间(-to),因此它们需要在输入文件(-i)之前进行设置,以确保正确地应用到指定的输入文件上,以防混乱。

(2)截取视频中某帧

复制代码
ffmpeg -ss 00:00:10 -i zhouxingchi.mp4 -frames:v 1 -q:v 2 out.jpg

-ss 00:00:10:跳到第 10 秒,-frames:v 1:只保存一帧,-q:v 2:图像质量(1 为最好,31 最差,建议用 2~4)。

(3)导出片段视频帧

复制代码
ffmpeg.exe -ss 13 -to 15 -i .\zhouxingchi.mp4 .\out\%03d.png

导出从视频第13秒到15秒这两秒内所有帧图片。该命令还可以增加-vf "fps=1"参数,表示每秒截一帧:

复制代码
ffmpeg.exe -ss 13 -to 20 -i .\zhouxingchi.mp4 -vf "fps=1" .\out\%03d.png

4. 录屏操作

直接使用ffmpeg内置的gdigrab对桌面进行录屏

复制代码
ffmpeg -f gdigrab -i desktop -pix_fmt yuv420p D:\out.mp4

这个命令使用FFmpeg工具来捕获Windows桌面并将其保存为一个mp4格式的视频文件。"-f gdigrab"表示使用 GDI (Graphics Device Interface) 来捕获屏幕;"-i desktop"指定要捕获的对象为desktop即桌面内容;"-pix_fmt yuv420p"指定输出视频的像素格式为 YUV 4:2:0 planar。

5. 解封装与解码

通过ffmpeg命令,可以将MP4视频转为H.264流(裸流.264文件),然后再提取成YUV数据文件(原始像素数据)。

(1)将MP4转为H.264裸流

复制代码
ffmpeg -i out.mp4 -c:v libx264 -bsf:v h264_mp4toannexb -an -f h264 out.264

-c:v libx264:使用H.264编码器(libx264)对视频重新编码。如果不想重新编码,可以用-c:v copy保留原始流。

-bsf:v h264_mp4toannexb:将MP4封装格式中的H.264转为 Annex B 格式(常见裸流格式)。

-an:去除音频流,仅保留视频。

-f h264:指定输出文件格式为H.264裸流。

(2)将H.264裸流转为YUV数据

接着H.264裸流中的编码数据需要解码后才能获得原始YUV数据。

复制代码
ffmpeg -i out.264 -pix_fmt yuv420p -vsync 0 out.yuv

-i output.264:输入文件为 H.264 裸流。

-pix_fmt yuv420p:指定输出像素格式为 YUV 4:2:0(标准格式)。

-vsync 0:禁用帧同步,确保输出的帧数与输入一致。

output.yuv:输出文件名。

(3)直接从MP4提取YUV

如果不需要中间的 H.264 裸流步骤,可以直接从 MP4 转为 YUV:

复制代码
ffmpeg -i out.mp4 -pix_fmt yuv420p -fps_mode passthrough out.yuv

6. 码流提取

(1)提取视频

通过移除音频流,可以单独提取视频使用,保留原始的视频编码。具体命令如下:

复制代码
ffmpeg -i out.mp4 -vcodec copy -an video0.mp4

(2)提取音频

利用ffmpeg命令,可轻松实现视频中的音频提取。具体命令如下:

复制代码
ffmpeg -i out.mp4 -acodec copy -vn voice.aac

如果需要将AAC音频流直接复制到MP3容器中,则需要执行如下命令:

使用ffmpeg命令将LOGO(例如logo.png)叠加到原始视频文件(例如out.mp4)上,可以通过调整overlay参数的位置来改变LOGO的放置位置。具体命令如下:

复制代码
左上角:ffmpeg -i out.mp4 -i logo.png -filter_complex overlay out1.mp4
右上角:ffmpeg -i out.mp4 -i logo.png -filter_complex overlay=W-w out2.mp4
左下角:ffmpeg -i out.mp4 -i logo.png -filter_complex overlay=0:H-h out3.mp4
右下角:ffmpeg -i out.mp4 -i logo.png -filter_complex overlay=W-w:H-h out4.mp4

以上命令中W是视频分辨率中的宽,w是logo文件图片的宽,H是视频分辨率中的高,h是logo文件图片的高。

2.2 ffplay.exe

ffplay是基于SDL与ffmpeg库实现的一个轻量级媒体播放器,可以使用它来播放原始的YUV/PCM 数据、编码后的H.264/H.265等数据,封装好的MP4/M4A等数据,还可以播放来自网络的流媒体数据。

1. 命令行格式

ffplay [选项] [输入文件路径]。它使用ffmpeg库进行解码和解码,并且可以通过命令行参数来控制播放行为,如调整音量、播放速度、画面比例等。

2. 常用基本选项

-x <宽度> 和 -y <高度>:强制设置视频显示的宽度和高度。

-fs:以全屏模式启动。

-an、-vn、-sn:分别禁用音频、视频和字幕。

-acodec、-vcodec、-scodec:分别强制使用设置的音频、视频和字幕解码器来播放。

-threads <个数>:设置线程个数,可以控制 ffplay 在解码和渲染过程中的并行度,从而提高播放性能。

-ss <开始时间>:从特定的时间点开始播放。

-t <持续时间>:设置播放的持续时间。

-vol <音量>:设置播放的初始音量。

-vf 和 -af:应用视频和音频滤镜。

-autoexit:视频播放完毕后自动退出。

-loop <循环播放次数>:指定文件循环播放次数。

-showmode [mode]:设置显示模式,mode 默认为0显示视频,为1显示音频波形,为2显示音频频谱。

3. 播放中按键控制

w:切换播放模式,比如在音频波形图、音频频谱图、视频画面之间切换。

s:步进模式,每按一次就播放下一帧图像。

right:快进 10s。

left:快退 10s。

up:快进 1min。

down:快退 1min。

space:暂停。

esc/q:退出播放。

2.3 ffprobe.exe

ffprobe是FFmpeg源码编译后生成的一个可执行程序,可以从媒体文件或网络媒体流中获得音视频及媒体容器的参数信息,用于查看和分析多媒体文件。

1. 命令行格式

ffprobe [选项] [ [-i] 输入文件路径],不加任何选项时输出如下所示:

根据ffprobe的输出,视频文件"zhouxingchi.mp4"包含了两个流:一个是视频流,另一个是音频流。

  • 视频流的信息:编解码器 av1 (libdav1d) (Main) (av01 / 0x31307661)、像素格式 yuv420p(tv, bt709)、分辨率 1920x1080、帧率 24 fps、比特率 845 kb/s。
  • 音频流的信息:编解码器 aac (LC) (mp4a / 0x6134706D)、采样率 44100 Hz、stereo(立体声)、比特率 128 kb/s。

2. 常用基本选项

-show_format:查看媒体文件的封装信息,输出内容的前半部分和不加选项时的输出一样,后半部分会得到该视频文件的封装文件信息如下所示。

-show_streams:查看媒体文件的流信息,如下所示其中一条媒体流的信息:

复制代码
[STREAM]
index=0
codec_name=av1
codec_long_name=Alliance for Open Media AV1
profile=Main
codec_type=video
codec_tag_string=av01
codec_tag=0x31307661
width=1920
height=1080
coded_width=1920
coded_height=1080
closed_captions=0
film_grain=0
has_b_frames=0
sample_aspect_ratio=1:1
display_aspect_ratio=16:9
pix_fmt=yuv420p
level=8
color_range=tv
color_space=bt709
color_transfer=bt709
color_primaries=bt709
chroma_location=unspecified
field_order=unknown
refs=1
id=0x1
r_frame_rate=24/1

show_streams

-show_packets:查看文件的所有数据包信息,一个视频文件由多个数据包组成。

-show_frames:查看媒体文件的每一帧信息,我们分析其中两个如下所示。如下是音频帧类型,然后key_frame=1表示这是IDR frame,如果key_frame=0表示这是Non-IDR frame。

-select_streams <type>:选择特定类型的流进行显示,<type>可以是v(视频)、a(音频)或s(字幕)。

-of/-print_format <format>:指定输出格式,常用的输出格式有csv、json、flat、xml等。

复制代码
ffprobe.exe -i .\out.mp4 -show_format -show_streams -of json

3 SDK使用Demo实例

3.1 SDL编译

SDL(Simple DirectMedia Layer)是一套开放源代码的跨平台多媒体开发库,使用C语言写成。SDL提供了数种控制图像、声音、输出入的函数,让开发者只要用相同或是相似的代码就可以开发出跨多个平台(Linux、Windows、Mac OS X等)的应用软件。该库编译比较简单,直接下载源码使用CMake进行配置编译即可,需要注意的是要确定编译版本是32位还是64位版本,这里编译器用的VS2019,编译的是64位版本。

配置完成后,直接用VS打开生成的工程进行编译即可。

3.2 基于SDL的视频播放demo

可以直接在SDL解决方案下添加Console项目,主文件内容如下:

复制代码
  1 extern "C" {
  2 #include <libavcodec/avcodec.h>
  3 #include <libavformat/avformat.h>
  4 #include <libavutil/imgutils.h>
  5 #include <libswscale/swscale.h>
  6 }
  7 
  8 #define SDL_MAIN_HANDLED
  9 #include <SDL2/SDL.h>
 10 #include <iostream>
 11 
 12 int main(int argc, char *argv[])
 13 {
 14     if (argc < 2) {
 15         std::cerr << "请指定视频文件路径,例如:\n";
 16         std::cerr << "  " << argv[0] << " sample.mp4\n";
 17         return -1;
 18     }
 19 
 20     const char *filepath = argv[1];
 21 
 22     avformat_network_init();
 23 
 24     AVFormatContext *fmt_ctx = nullptr;
 25     if (avformat_open_input(&fmt_ctx, filepath, nullptr, nullptr) < 0) {
 26         std::cerr << "无法打开文件: " << filepath << "\n";
 27         return -1;
 28     }
 29 
 30     if (avformat_find_stream_info(fmt_ctx, nullptr) < 0) {
 31         std::cerr << "无法获取视频流信息\n";
 32         return -1;
 33     }
 34 
 35     int video_stream_idx = -1;
 36     for (unsigned int i = 0; i < fmt_ctx->nb_streams; ++i) {
 37         if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
 38             video_stream_idx = i;
 39             break;
 40         }
 41     }
 42 
 43     if (video_stream_idx == -1) {
 44         std::cerr << "没有找到视频流\n";
 45         return -1;
 46     }
 47 
 48     AVCodecParameters *codecpar = fmt_ctx->streams[video_stream_idx]->codecpar;
 49     const AVCodec *decoder = avcodec_find_decoder(codecpar->codec_id);
 50     if (!decoder) {
 51         std::cerr << "找不到解码器\n";
 52         return -1;
 53     }
 54 
 55     AVCodecContext *codec_ctx = avcodec_alloc_context3(decoder);
 56     avcodec_parameters_to_context(codec_ctx, codecpar);
 57     avcodec_open2(codec_ctx, decoder, nullptr);
 58 
 59     AVFrame *frame = av_frame_alloc();
 60     AVFrame *rgb_frame = av_frame_alloc();
 61     AVPacket *pkt = av_packet_alloc();
 62 
 63     int width = codec_ctx->width;
 64     int height = codec_ctx->height;
 65     enum AVPixelFormat dst_pix_fmt = AV_PIX_FMT_YUV420P;
 66 
 67     struct SwsContext *sws_ctx = sws_getContext(
 68         width, height, codec_ctx->pix_fmt,
 69         width, height, dst_pix_fmt,
 70         SWS_BILINEAR, nullptr, nullptr, nullptr);
 71 
 72     int num_bytes = av_image_get_buffer_size(dst_pix_fmt, width, height, 1);
 73     uint8_t *buffer = (uint8_t *)av_malloc(num_bytes * sizeof(uint8_t));
 74     av_image_fill_arrays(rgb_frame->data, rgb_frame->linesize, buffer, dst_pix_fmt, width, height, 1);
 75 
 76     // 初始化 SDL
 77     SDL_Init(SDL_INIT_VIDEO);
 78     SDL_Window *window = SDL_CreateWindow("FFmpeg Player",
 79                                           SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
 80                                           width, height, SDL_WINDOW_SHOWN);
 81     SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, 0);
 82     SDL_Texture *texture = SDL_CreateTexture(renderer,
 83                                              SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, width, height);
 84 
 85     // 解码 & 渲染循环
 86     while (av_read_frame(fmt_ctx, pkt) >= 0) {
 87         if (pkt->stream_index == video_stream_idx) {
 88             if (avcodec_send_packet(codec_ctx, pkt) == 0) {
 89                 while (avcodec_receive_frame(codec_ctx, frame) == 0) {
 90                     sws_scale(sws_ctx,
 91                               frame->data, frame->linesize,
 92                               0, height,
 93                               rgb_frame->data, rgb_frame->linesize);
 94 
 95                     SDL_UpdateYUVTexture(texture, nullptr,
 96                                          rgb_frame->data[0], rgb_frame->linesize[0],
 97                                          rgb_frame->data[1], rgb_frame->linesize[1],
 98                                          rgb_frame->data[2], rgb_frame->linesize[2]);
 99 
100                     SDL_RenderClear(renderer);
101                     SDL_RenderCopy(renderer, texture, nullptr, nullptr);
102                     SDL_RenderPresent(renderer);
103                     SDL_Delay(1000 / 30); // 简单帧率控制
104                 }
105             }
106         }
107         av_packet_unref(pkt);
108 
109         SDL_Event event;
110         SDL_PollEvent(&event);
111         if (event.type == SDL_QUIT) {
112             break;
113         }
114     }
115 
116     // 清理
117     sws_freeContext(sws_ctx);
118     av_free(buffer);
119     av_frame_free(&frame);
120     av_frame_free(&rgb_frame);
121     av_packet_free(&pkt);
122     avcodec_free_context(&codec_ctx);
123     avformat_close_input(&fmt_ctx);
124 
125     SDL_DestroyTexture(texture);
126     SDL_DestroyRenderer(renderer);
127     SDL_DestroyWindow(window);
128     SDL_Quit();
129 
130     return 0;
131 }

main.cpp

注意第8行语句,如果不包含该语句编译时会提示错误:

复制代码
MSVCRT.lib(exe_main.obj) : error LNK2001: 无法解析的外部符号 _main

在网上查了下原因,因为SDL2在SDL_main.h中定义了如下代码:

复制代码
#if defined(SDL_MAIN_NEEDED) || defined(SDL_MAIN_AVAILABLE)
#define main    SDL_main
#endif

它会把main重命名为SDL_main,然后SDL库中会试图用它自己的WinMain入口去调用你的SDL_main,从而接管程序,这在Windows GUI项目中是正常的,但现在用的是控制台项目(需要标准main函数),但是上面的语句已经将main重定向到SDL_main,所以链接时会提示"无法解析的外部符号 _main"。

头文件包含目录配置如下:

链接器配置如下:

输入库包含如下:

复制代码
SDL2d.lib;avformat.lib;avcodec.lib;avutil.lib;swscale.lib;

为了能在VS环境下直接运行调试,可以在调试选项配置中增加运行库目录到PATH环境变量,并给出要播放的文件为命令参数。

参考:

https://zhuanlan.zhihu.com/p/684158932

https://blog.csdn.net/lsb2002/article/details/136568262

相关推荐
winfredzhang21 小时前
实战:从零构建一个支持屏幕录制与片段合并的视频管理系统 (Node.js + FFmpeg)
ffmpeg·node.js·音视频·录屏
winfredzhang1 天前
自动化视频制作:深入解析 FFmpeg 图片转视频脚本
ffmpeg·自动化·音视频·命令行·bat·图片2视频
胖_大海_2 天前
【FFmpeg+Surface 底层渲染,实现超低延迟100ms】
ffmpeg
冷冷的菜哥2 天前
springboot调用ffmpeg实现对视频的截图,截取与水印
java·spring boot·ffmpeg·音视频·水印·截图·截取
进击的CJR2 天前
redis哨兵实现主从自动切换
mysql·ffmpeg·dba
huahualaly3 天前
重建oracle测试库步骤
数据库·oracle·ffmpeg
aqi003 天前
FFmpeg开发笔记(九十九)基于Kotlin的国产开源播放器DKVideoPlayer
android·ffmpeg·kotlin·音视频·直播·流媒体
lizongyao3 天前
FFMPEG命令行典型案例
ffmpeg
冷冷的菜哥3 天前
ASP.NET Core调用ffmpeg对视频进行截图,截取,增加水印
开发语言·后端·ffmpeg·asp.net·音视频·asp.net core
冷冷的菜哥3 天前
go(golang)调用ffmpeg对视频进行截图、截取、增加水印
后端·golang·ffmpeg·go·音视频·水印截取截图