- 1:一、FFmpeg简介
- 2:二、下载FFmpeg
- 3:三、FFmpeg常用参数及命令
- 4:四、FFmpeg在Unity 3D中的使用
- 5:五、补充说明 2022.11.03
一、FFmpeg简介
其官网上是这样介绍的:
FFmpeg能够实现对视频音频编码、解码、转码、流传输等等一系列功能。它包含有libavcodec, libavutil, libavformat, libavfilter, libavdevice, libswscale,libswresample 库。其中:
libavcodec 是一个包含用于音频/视频编解码器的解码器和编码器的库。 libavutil 是一个包含简化编程功能的库,包括随机数生成器,数据结构,数学例程,核心多媒体实用程序等等。 libavformat 是一个包含多媒体容器格式的解复用器和复用器的库。 libavdevice 是一个包含输入和输出设备的库,用于从许多常见的多媒体输入/输出软件框架中获取和呈现,包括Video4Linux,Video4Linux2,VfW和ALSA。 libavfilter 是一个包含媒体过滤器的库。 libswscale 是一个执行高度优化的图像缩放和色彩空间/像素格式转换操作的库。 libswresample 是一个执行高度优化的音频重采样,重新矩阵化和样本格式转换操作的库。
二、下载FFmpeg
下载地址:http://www.ffmpeg.org/download.html
我们找到Windows对应的下载链接,官网提供了三种文件:1.下载编译成可执行的程序;2.下载编译好的库;3.开发版的库和头文件。
①Static里面只有3个应用程序:ffmpeg.exe,ffplay.exe,ffprobe.exe,每个exe的体积都很大,相关的Dll已经被编译到exe里面去了。
②Shared里面除了3个应用程序之外,还有一些Dll,比如说avcodec-54.dll之类的。Shared里面的exe体积很小,他们在运行的时候,到相应的Dll中调用功能。
③Dev版本是用于开发的,里面包含了库文件xxx.lib以及头文件xxx.h,这个版本不包含exe文件。
我们在unity中使用FFmpeg时一般是使用ffmpeg.exe,把该exe放到固定位置然后再使用相应的参数命令控制ffmpeg执行对应功能。
三、FFmpeg常用参数及命令
1、命令的格式:
ffmpeg [global_options] {[input_file_options] -i input_url} ... {[output_file_options] output_url} ...
示例: ffmpeg -f dshow -i video="screen-capture-recorder" -f dshow -i audio="virtual-audio-capturer" -y -preset ultrafast -c:v libx264 -b:v 1500k -r 24 -s 1280x720 -y D:Records/output.mp4
2、常用参数:
-f :格式 gdigrab :ffmpeg内置的用于抓取Windows桌面的方法,支持抓取指定名称的窗口 dshow :依赖于第三方软件Screen Capture Recorder(后面简称SCR,下载地址在下面) 补充 :-f也可以用于指定输出视频地格式,例如-f flv;gdigrab和dshow可以混用,分别捕获视频和音频 -i :输入源 title :要录制的窗口的名称,仅用于gdigrab方式 video :视频播放硬件名称或者"screen-capture-recorder",后者依赖SCR audio :音频播放硬件名称或者"virtual-audio-capturer",后者依赖SCR -preset ultrafast :以最快的速度进行编码,实时录制过程中如果编码速度慢会丢帧 -c:v :视频编码方式 -c:a :音频编码方式 -b:v :视频比特率 -r :视频帧率 -s :视频分辨率 -y :输出文件覆盖已有文件不提示
3、常用命令
(1)将视频的帧速率改为24
ffmpeg -i input.avi -r 24 output.avi
(2)视频格式转换,将avi转成mp4
ffmpeg -i input.avi output.mp4
(3)从视频中提取音频
ffmpeg -i test.mp4 -acodec libmp3lame output.mp3
(4)视频剪切
ffmpeg -ss 00:00:15 -t 00:00:05 -i input.mp4 -vcodec copy -acodec copy output.mp4
(5)将100张图片合成视频,并为其添加背景音频(注意:图片要放在同一个文件夹下,并将图片按001--100的格式命名)
ffmpeg -i 001.mp3 -i %3d.jpg -s 1024x768 -author fy -vcodec mpeg4 darkdoor.avi
(6)官方文档:http://www.ffmpeg.org/ffmpeg.html
四、FFmpeg在Unity 3D中的使用
1、如果需要使用dshow的方式录屏需要提前在电脑上安装Screen Capture Recorder
https://github.com/rdp/screen-capture-recorder-to-video-windows-free
2、命令行调用FFmpeg的核心代码如下:
using System; using System.Collections; using System.Diagnostics; using System.Runtime.InteropServices; using UnityEngine; [ExecuteInEditMode] public class FFRecorder : MonoBehaviour { #region 模拟控制台信号需要使用的DLL [DllImport("kernel32.dll")] static extern bool GenerateConsoleCtrlEvent(int dwCtrlEvent, int dwProcessGroupId); [DllImport("kernel32.dll")] static extern bool SetConsoleCtrlHandler(IntPtr handlerRoutine, bool add); [DllImport("kernel32.dll")] static extern bool AttachConsole(int dwProcessId); [DllImport("kernel32.dll")] static extern bool FreeConsole(); #endregion #region 设置菜单 public enum RecordType { GDIGRAB, DSHOW } public enum Bitrate { _1000k, _1500k, _2000k, _2500k, _3000k, _3500k, _4000k, _5000k, _8000k } public enum Framerate { _14, _24, _30, _45, _60 } public enum Resolution { _1280x720, _1920x1080, _Auto } public enum OutputPath { Desktop, StreamingAsset, DataPath, Custom } #endregion #region 成员 [Tooltip("启用Debug则显示CMD窗口,否则不显示。")] [SerializeField] private bool _debug = false; [Tooltip("DSHOW - 录制全屏 \nGUIGRAB - 录制游戏窗口(仅用于发布版)")] public RecordType recordType = RecordType.DSHOW; public Resolution resolution = Resolution._1280x720; public Framerate framerate = Framerate._24; public Bitrate bitrate = Bitrate._1500k; public OutputPath outputPath = OutputPath.Desktop; public string customOutputPath = @"D:/Records"; public bool IsRecording { get { return _isRecording; } } /** ffmpeg参数说明 * -f :格式 * gdigrab :ffmpeg内置的用于抓取Windows桌面的方法,支持抓取指定名称的窗口 * dshow :依赖于第三方软件Screen Capture Recorder(后面简称SCR) * -i :输入源 * title :要录制的窗口的名称,仅用于GDIGRAB方式 * video :视频播放硬件名称或者"screen-capture-recorder",后者依赖SCR * audio :音频播放硬件名称或者"virtual-audio-capturer",后者依赖SCR * -preset ultrafast :以最快的速度进行编码,生成的视频文件大 * -c:v :视频编码方式 * -c:a :音频编码方式 * -b:v :视频比特率 * -r :视频帧率 * -s :视频分辨率 * -y :输出文件覆盖已有文件不提示 * * FFMPEG官方文档:http://ffmpeg.org/ffmpeg-all.html * Screen Capture Recorder主页:https://github.com/rdp/screen-capture-recorder-to-video-windows-free */ // 参数:窗口名称 -b比特率 -r帧率 -s分辨率 文件路径 文件名 private const string FFARGS_GDIGRAB = "-f gdigrab -i title={0} -f dshow -i audio=\"virtual-audio-capturer\" -y -preset ultrafast -rtbufsize 3500k -b:v {1} -r {2} -s {3} {4}/{5}.mp4"; // 参数:-b比特率 -r帧率 -s分辨率 文件路径 文件名 private const string FFARGS_DSHOW = "-f dshow -i video=\"screen-capture-recorder\" -f dshow -i audio=\"virtual-audio-capturer\" -y -preset ultrafast -rtbufsize 3500k -b:v {0} -r {1} -s {2} {3}/{4}.mp4"; private string _ffpath; private string _ffargs; private int _pid; private bool _isRecording = false; #endregion #if !UNITY_EDITOR && !DEVELOPMENT_BUILD private void Start() { _debug = false; } #endif #if UNITY_EDITOR || DEVELOPMENT_BUILD private void OnGUI() { if (GUILayout.Button("Start")) StartRecording(); if (GUILayout.Button("Stop")) StopRecording(() => { UnityEngine.Debug.Log("结束录制。"); }); } #endif #if UNITY_EDITOR private void OnValidate() { if (_debug) UnityEngine.Debug.Log("FFRecorder - CMD窗口已启用。"); else UnityEngine.Debug.Log("FFRecorder - CMD窗口已禁用。"); if (recordType == RecordType.GDIGRAB) { UnityEngine.Debug.Log("FFRecorder - 使用【GDIGRAB】模式录制当前窗口。"); UnityEngine.Debug.LogError("FFRecorder - 【GDIGRAB】模式在编辑器中不可用。"); } else if (recordType == RecordType.DSHOW) { UnityEngine.Debug.Log("FFRecorder - 使用【DSHOW】模式录制全屏。"); } } #endif public void StartRecording() { if (_isRecording) { UnityEngine.Debug.LogError("FFRecorder::StartRecording - 当前已有录制进程。"); return; } // 杀死已有的ffmpeg进程,不要加.exe后缀 Process[] goDie = Process.GetProcessesByName("ffmpeg"); foreach (Process p in goDie) p.Kill(); // 解析设置,如果设置正确,则开始录制 bool validSettings = ParseSettings(); if (validSettings) { UnityEngine.Debug.Log("FFRecorder::StartRecording - 执行命令:" + _ffpath + " " + _ffargs); StartCoroutine(IERecording()); } else { UnityEngine.Debug.LogError("FFRecorder::StartRecording - 设置不当,录制取消,请检查控制台输出。"); } } public void StopRecording(Action _OnStopRecording) { if (!_isRecording) { UnityEngine.Debug.LogError("FFRecorder::StopRecording - 当前没有录制进程,已取消操作。"); return; } StartCoroutine(IEExitCmd(_OnStopRecording)); } private bool ParseSettings() { _ffpath = Application.streamingAssetsPath + @"/ffmpeg/ffmpeg.exe"; string name = Application.productName + "_" + DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss"); // 分辨率 string s; if (resolution == Resolution._1280x720) { int w = 1280; int h = 720; if (Screen.width < w) { w = Screen.width; UnityEngine.Debug.LogWarning(string.Format("录制水平分辨率大于屏幕水平分辨率,已自动缩小为{0}。", w)); } if (Screen.height < h) { h = Screen.height; UnityEngine.Debug.LogWarning(string.Format("录制垂直分辨率大于屏幕垂直分辨率,已自动缩小为{0}。", h)); } s = w.ToString() + "x" + h.ToString(); } else if (resolution == Resolution._1920x1080) { int w = 1920; int h = 1080; if (Screen.width < w) { w = Screen.width; UnityEngine.Debug.LogWarning(string.Format("录制水平分辨率大于屏幕水平分辨率,已自动缩小为{0}。", w)); } if (Screen.height < h) { h = Screen.height; UnityEngine.Debug.LogWarning(string.Format("录制垂直分辨率大于屏幕垂直分辨率,已自动缩小为{0}。", h)); } s = w.ToString() + "x" + h.ToString(); } else /*(resolution == Resolution._Auto)*/ { s = Screen.width.ToString() + "x" + Screen.height.ToString(); } // 帧率 string r = framerate.ToString().Remove(0, 1); // 比特率 string b = bitrate.ToString().Remove(0, 1); // 输出位置 string output; if (outputPath == OutputPath.Desktop) output = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory) + "/" + Application.productName + "_Records"; else if (outputPath == OutputPath.DataPath) output = Application.dataPath + "/" + Application.productName + "_Records"; else if (outputPath == OutputPath.StreamingAsset) output = Application.streamingAssetsPath + "/" + Application.productName + "_Records"; else /*(outputPath == OutputPath.Custom)*/ output = customOutputPath; // 命令行参数 if (recordType == RecordType.GDIGRAB) { _ffargs = string.Format(FFARGS_GDIGRAB, Application.productName, b, r, s, output, name); } else /*(recordType == RecordType.DSHOW)*/ { _ffargs = string.Format(FFARGS_DSHOW, b, r, s, output, name); } // 创建输出文件夹 if (!System.IO.Directory.Exists(output)) { try { System.IO.Directory.CreateDirectory(output); } catch (Exception e) { UnityEngine.Debug.LogError("FFRecorder::ParseSettings - " + e.Message); return false; } } return true; } // 不一定要用协程 private IEnumerator IERecording() { yield return null; Process ffp = new Process(); ffp.StartInfo.FileName = _ffpath; // 进程可执行文件位置 ffp.StartInfo.Arguments = _ffargs; // 传给可执行文件的命令行参数 ffp.StartInfo.CreateNoWindow = !_debug; // 是否显示控制台窗口 ffp.StartInfo.UseShellExecute = _debug; // 是否使用操作系统Shell程序启动进程 ffp.Start(); // 开始进程 _pid = ffp.Id; _isRecording = true; } private IEnumerator IEExitCmd(Action _OnStopRecording) { // 将当前进程附加到pid进程的控制台 AttachConsole(_pid); // 将控制台事件的处理句柄设为Zero,即当前进程不响应控制台事件 // 避免在向控制台发送【Ctrl C】指令时连带当前进程一起结束 SetConsoleCtrlHandler(IntPtr.Zero, true); // 向控制台发送 【Ctrl C】结束指令 // ffmpeg会收到该指令停止录制 GenerateConsoleCtrlEvent(0, 0); // ffmpeg不能立即停止,等待一会,否则视频无法播放 yield return new WaitForSeconds(3.0f); // 卸载控制台事件的处理句柄,不然之后的ffmpeg调用无法正常停止 SetConsoleCtrlHandler(IntPtr.Zero, false); // 剥离已附加的控制台 FreeConsole(); _isRecording = false; if (_OnStopRecording != null) { _OnStopRecording(); } } // 程序结束时要杀掉后台的录制进程,但这样会导致输出文件无法播放 private void OnDestroy() { if (_isRecording) { try { UnityEngine.Debug.LogError("FFRecorder::OnDestroy - 录制进程非正常结束,输出文件可能无法播放。"); Process.GetProcessById(_pid).Kill(); } catch (Exception e) { UnityEngine.Debug.LogError("FFRecorder::OnDestroy - " + e.Message); } } } }
3、推流服务
需要安装配置了rtmp模块的nginx。
参考资料: https://zhuanlan.zhihu.com/p/93525011 https://blog.csdn.net/mazaiting/article/details/79709753 https://blog.csdn.net/qq_21397217/article/details/80537263
五、补充说明 2022.11.03
1.解决Windows中录屏不能使用默认播放器播放,ffmpeg编码视频时采用了 yuv444,而windows 对yuv444 应该支持不够,需要增加-pix_fmt yuv420p。
./ffmpeg.exe -f gdigrab -i desktop -f dshow -i audio="virtual-audio-capturer" -vcodec libx264 -pix_fmt yuv420p -acodec aac test.mp4
2.解决需要额外安装Screen Capture Recorder。
ffmpeg只使用了SCR的部分功能,不需要全部安装,只需要把其中两个dll注册即可。
注册dll一般是在dll目录下打开有管理员权限的命令行,直接输入regsvr32+xxx.dll注册,注意一定要是管理员权限,dll可以放在任何文件夹。 1.注册录屏dll regsvr32 screen-capture-recorder-x64.dll 2.注册录音dll regsvr32 audio_sniffer-x64.dll 注册dll 需要用到regsvr32命令,其用法为: "regsvr32 [/s] [/n] [/u] [/i[:cmdline]] dllname”。其中dllname为dll文件名 参数有如下意义: /u——反注册控件 /s——不管注册成功与否,均不显示提示框 /c——控制台输出 /i——跳过控件的选项进行安装(与注册不同) /n——不注册控件,此选项必须与/i选项一起使用
3.使用代码进行dll注册。
private bool RegisterDll() { bool result = true; try { string dllPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "XXX.dll");//获得要注册的dll的物理路径 if (!File.Exists(dllPath)) { Console.Write(string.Format("“{0}”目录下无“XXX.dll”文件!", AppDomain.CurrentDomain.BaseDirectory)); return false; } //拼接命令参数 string startArgs = string.Format("/s \"{0}\"", dllPath); Process p = new Process();//创建一个新进程,以执行注册动作 p.StartInfo.FileName = "regsvr32"; p.StartInfo.Arguments = startArgs; //以管理员权限注册dll文件 WindowsIdentity winIdentity = WindowsIdentity.GetCurrent(); //引用命名空间 System.Security.Principal WindowsPrincipal winPrincipal = new WindowsPrincipal(winIdentity); if (!winPrincipal.IsInRole(WindowsBuiltInRole.Administrator)) { p.StartInfo.Verb = "runas";//管理员权限运行 } p.Start(); p.WaitForExit(); p.Close(); p.Dispose(); } catch (Exception ex) { result = false; //记录日志,抛出异常 } return result; }
你好,我想问问我按照这个教程做了,我放好了ffmpeg.exe,用GDIGRAB的方式录制视频,在录制完成后他会创建导出视频的路径,但不会生成视频文件,也没有任何报错,这可能是什么原因呢?
有可能是参数错误,你可以先用命令行(cmd)的方式调用FFmpeg.exe,这样在命令行窗口会打印出错误信息。
我试着这么做了,我用win+R然后输入cmd来启动了cmd,然后我把目录切换到ffmpeg所在的地方,然后我输入了start ffmpeg.exe并按了回车,然后标题似乎有一帧变成了ffmpeg,然后又变回了原样,除此之外什么都没有发生,也没有任何Log,我怀疑这会不会是我下载的ffmpeg.exe有问题?如果是的话我想问问可以请您给我邮箱发一份正确的文件吗?对不起又来打扰您还提出了这么麻烦的请求,如果您愿意帮忙的话真的感激不尽!
我的邮箱是1678555697@qq.com,顺便一提我看到你的网站上写收到回复后会在邮箱收到邮件提醒,但昨天收到你的回复之后我并没有收到新邮件,不知道是不是有什么问题)
啊对了顺便送您一个我的软件的Steam激活码,因为没有您的联系方式,我就直接发在这里了,希望不会被其他人兑换走
R5LEI-6NA0E-HV6X0
不过感觉我的exe文件或许其实没问题?我是直接下载的BtbN的Github上打的最新包(只有3个exe文件的那个),然后log中似乎也成功发出指令了,运行时的Log是:
FFRecorder::StartRecording - 执行命令:D:/问问/活字引擎_Data/StreamingAssets/ffmpeg/ffmpeg.exe -f gdigrab -i title=活字引擎 -f dshow -i audio="virtual-audio-capturer" -y -preset ultrafast -rtbufsize 3500k -b:v 1500k -r 24 -s 1632x999 D:\/12.mp4/活字引擎_2022-11-03-09-26-05.mp4
1.你的输出路径是不是没有写对, D:\/12.mp4不是一个路径格式。
2.有人天天在我评论区刷广告,我后台没有配置邮箱,所以不能给你发送邮件通知。
我换成了D://12.mp4,但依旧没用)
然后我也试了一下D:\12.mp4,但这个也不行
顺便一提我还试过选择在StreamingAssest导出而不是我的自定义位置,但也没用)
我复制了你的命令,发现是你重复使用了-f ,你写了-f gdigrab和 -f dshow。
似乎还是不行,我现在的指令是:
FFRecorder::StartRecording - 执行命令:D:/超级大问问/活字引擎_Data/StreamingAssets/ffmpeg/ffmpeg.exe -f gdigrab -i title=活字引擎 dshow -i audio="virtual-audio-capturer" -y -preset ultrafast -rtbufsize 3500k -b:v 1500k -r 30 -s 1632x999 D:/12.mp4/活字引擎_2022-11-03-10-21-37.mp4
(顺便一提您提供的代码里就是带有两个-f的)
我发现问题所在了,我写的是录屏方式是gdigrab,但是录音还是需要dshow的。使用了dshow的话,需要在电脑上安装Screen Capture Recorder。还有问题的话你可以扫描右下角的二维码加我微信。