- 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。还有问题的话你可以扫描右下角的二维码加我微信。