项目地址:https://github.com/cmxl/FFmpeg.NET

官方介绍

.NET wrapper for common ffmpeg tasks

FFmpeg.NET provides a straightforward interface for handling media data, making tasks such as converting, slicing and editing both audio and video completely effortless.

Under the hood, FFmpeg.NET is a .NET wrapper for FFmpeg; a free (LGPLv2.1) multimedia framework containing multiple audio and video codecs, supporting muxing, demuxing and transcoding tasks on many media formats.

Some major parts are taken from https://github.com/AydinAdn/MediaToolkit. Many features have been refactored. The library has been ported to Netstandard and made threadsafe.

You need to provide the ffmpeg executable path to the Engine constructor.

关于下载 FFmpeg

网站:http://ffmpeg.org/   然后点击“Download” 或者直接跳转到 https://ffmpeg.zeranoe.com/builds/

官方示例

代码如下:

using FFmpeg.NET.Events;
using System;
using System.Threading.Tasks; namespace FFmpeg.NET.Sample
{
internal class Program
{
private static async Task Main(string[] args)
{
try
{
var inputFile = new MediaFile(@"..\..\..\..\..\tests\FFmpeg.NET.Tests\MediaFiles\SampleVideo_1280x720_1mb.mp4");
var outputFile = new MediaFile(@"output.mkv");
var thumbNailFile = new MediaFile(@"thumb.png"); var ffmpeg = new Engine(@"..\..\..\..\..\lib\ffmpeg\v4\ffmpeg.exe");
ffmpeg.Progress += OnProgress;
ffmpeg.Data += OnData;
ffmpeg.Error += OnError;
ffmpeg.Complete += OnComplete;
var output = await ffmpeg.ConvertAsync(inputFile, outputFile);
var thumbNail = await ffmpeg.GetThumbnailAsync(output, thumbNailFile, new ConversionOptions { Seek = TimeSpan.FromSeconds() });
var metadata = await ffmpeg.GetMetaDataAsync(output);
Console.WriteLine(metadata.FileInfo.FullName);
Console.WriteLine(metadata);
}
catch (Exception exc)
{
Console.WriteLine(exc);
}
finally
{
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
} private static void OnProgress(object sender, ConversionProgressEventArgs e)
{
Console.WriteLine("[{0} => {1}]", e.Input.FileInfo.Name, e.Output?.FileInfo.Name);
Console.WriteLine("Bitrate: {0}", e.Bitrate);
Console.WriteLine("Fps: {0}", e.Fps);
Console.WriteLine("Frame: {0}", e.Frame);
Console.WriteLine("ProcessedDuration: {0}", e.ProcessedDuration);
Console.WriteLine("Size: {0} kb", e.SizeKb);
Console.WriteLine("TotalDuration: {0}\n", e.TotalDuration);
} private static void OnData(object sender, ConversionDataEventArgs e)
=> Console.WriteLine("[{0} => {1}]: {2}", e.Input.FileInfo.Name, e.Output?.FileInfo.Name, e.Data); private static void OnComplete(object sender, ConversionCompleteEventArgs e)
=> Console.WriteLine("Completed conversion from {0} to {1}", e.Input.FileInfo.FullName, e.Output?.FileInfo.FullName); private static void OnError(object sender, ConversionErrorEventArgs e)
=> Console.WriteLine("[{0} => {1}]: Error: {2}\n{3}", e.Input.FileInfo.Name, e.Output?.FileInfo.Name, e.Exception.ExitCode, e.Exception.InnerException);
}
}

我对源代码的更改

1. 在 FFmpeg.NET.ConversionOptions 类

文本代码:

        /// <summary>
/// 额外增加的 FFmpeg 参数字符串(注意:特殊字符请注意转义)
/// </summary>
public string ExtraFFmpegArgs { get; set; } = null; /// <summary>
/// FFmpeg 的 DrawText 参数字符串(注意:特殊字符请注意转义)
/// </summary>
public string FFmpegDrawTextArgs { get; set; } = null;

2.  在 FFmpeg.NET.FFmpegArgumentBuilder 类的 GetThumbnail 方法

文本代码:

            // Video size / resolution
if (conversionOptions.VideoSize == VideoSize.Custom)
{
commandBuilder.AppendFormat(" -s {0}:{1} ",
conversionOptions.CustomWidth ?? ,
conversionOptions.CustomHeight ?? );
}
if (!string.IsNullOrEmpty(conversionOptions.FFmpegDrawTextArgs))
{
commandBuilder.AppendFormat(" -vf drawtext=\"{0}\" ", conversionOptions.FFmpegDrawTextArgs);
}
if (!string.IsNullOrEmpty(conversionOptions.ExtraFFmpegArgs))
{
commandBuilder.AppendFormat(" {0} ", conversionOptions.ExtraFFmpegArgs);
}

3. 在 FFmpeg.NET.FFmpegArgumentBuilder 类的 Convert 方法

文本代码:

            if (!string.IsNullOrEmpty(conversionOptions.FFmpegDrawTextArgs))
{
commandBuilder.AppendFormat(" -vf drawtext=\"{0}\" ", conversionOptions.FFmpegDrawTextArgs);
}
if (!string.IsNullOrEmpty(conversionOptions.ExtraFFmpegArgs))
{
commandBuilder.AppendFormat(" {0} ", conversionOptions.ExtraFFmpegArgs);
}

我的 WinForm 示例

首先需要在 web.config 或 app.config 中配置

  <appSettings>
<!-- Task FFmpeg.NET 需要的参数 -->
<add key="TaskffmpegNETExeFullPath" value="D:\参考资料\C#\FFmpeg_Binary\ffmpeg-20190325-6e42021-win32and64-shared\v4\ffmpeg.exe" />
</appSettings>

代码如下:

    public partial class ffmpegTest02 : FormBase
{
private static readonly string TaskffmpegNETExeFullPath = ConfigurationManager.AppSettings["TaskffmpegNETExeFullPath"]; string _videoFileFullPath = @"D:\Workspace\TestVideo.mp4"; public ffmpegTest02()
{
base.InitForm();
InitializeComponent();
base.InitControls(this.listInfoLog);
} private void OnProgress(object sender, ConversionProgressEventArgs e)
{
ShowAndLog(string.Format("[{0} => {1}]", e.Input.FileInfo.Name, e.Output?.FileInfo.Name), false, null);
ShowAndLog(string.Format("Bitrate: {0}", e.Bitrate), false, null);
ShowAndLog(string.Format("Fps: {0}", e.Fps), false, null);
ShowAndLog(string.Format("Frame: {0}", e.Frame), false, null);
ShowAndLog(string.Format("ProcessedDuration: {0}", e.ProcessedDuration), false, null);
ShowAndLog(string.Format("Size: {0} kb", e.SizeKb), false, null);
ShowAndLog(string.Format("TotalDuration: {0}\n", e.TotalDuration), false, null);
} /// <summary>
/// 此事件短时间(比如:1秒以内)会调用多次
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void OnData(object sender, ConversionDataEventArgs e)
{
ShowAndLog(string.Format("[从源文件 {0} 到目标文件 {1}],数据 {2}", e.Input.FileInfo.Name, e.Output?.FileInfo.Name, e.Data),
false,
null);
} /// <summary>
/// 此事件短时间(比如:1秒以内)会调用多次
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void OnDataSimpleShow(object sender, ConversionDataEventArgs e)
{
ShowAndLog(string.Format("[从源文件 {0} 到目标文件 {1}]。", e.Input.FileInfo.Name, e.Output?.FileInfo.Name),
false,
null);
} private void OnComplete(object sender, ConversionCompleteEventArgs e)
{
ShowAndLog(string.Format("从 {0} 到 {1} 处理完成。 ", e.Input.FileInfo.FullName, e.Output?.FileInfo.FullName),
false,
null);
} private void OnError(object sender, ConversionErrorEventArgs e)
{
ShowAndLog(string.Format("[{0} => {1}]: 错误: {2}\n{3}", e.Input.FileInfo.Name, e.Output?.FileInfo.Name, e.Exception.ExitCode, e.Exception.InnerException),
false,
null);
} private async void btnStartConvertAndSnapshot_Click(object sender, EventArgs e)
{
string mkvOutputFileFullPath = string.Format(@"D:\Workspace\TestVideo-{0}.mkv", DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss"));
string mkvThumbOutputFileFullPath = string.Format(@"D:\Workspace\TestVideo-mkv-thumb-{0}.png", DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss"));
try
{
var inputFile = new MediaFile(_videoFileFullPath);
var outputFile = new MediaFile(mkvOutputFileFullPath);
var thumbNailFile = new MediaFile(mkvThumbOutputFileFullPath); var ffmpeg = new Engine(TaskffmpegNETExeFullPath);
//ffmpeg.Progress += OnProgress;
//ffmpeg.Data += OnDataSimpleShow;
ffmpeg.Error += OnError;
ffmpeg.Complete += OnComplete;
var output = await ffmpeg.ConvertAsync(inputFile, outputFile);
var thumbNail = await ffmpeg.GetThumbnailAsync(output, thumbNailFile, new ConversionOptions
{
Seek = TimeSpan.FromSeconds()
});
var metadata = await ffmpeg.GetMetaDataAsync(output);
Console.WriteLine(metadata.FileInfo.FullName);
Console.WriteLine(metadata);
}
catch (Exception exc)
{
Console.WriteLine(exc);
}
} private async void btnStartSnapshot_Click(object sender, EventArgs e)
{
ShowAndLog("ffmpeg 准备开始转换,请稍后...", false, null);
Stopwatch globalWatch = Stopwatch.StartNew();
TimeSpan[] timeSpanArray = new TimeSpan[]
{
new TimeSpan(,, ),
new TimeSpan(,, ),
new TimeSpan(,, ),
new TimeSpan(,, ),
new TimeSpan(,, ),
new TimeSpan(,, ),
};
var ffmpeg = new Engine(TaskffmpegNETExeFullPath);
//ffmpeg.Progress += OnProgress;
//ffmpeg.Data += OnDataSimpleShow;
ffmpeg.Error += OnError;
ffmpeg.Complete += OnComplete; var inputFile = new MediaFile(_videoFileFullPath); int i = ;
string mkvThumbOutputFileFullPath;
List<string> allThumbFullName = new List<string>();
foreach (TimeSpan tsItem in timeSpanArray)
{
i++;
mkvThumbOutputFileFullPath = string.Format(@"D:\Workspace\TestVideo-mp4-thumb-{0}-{1}.jpg", DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss.fff"), i);
allThumbFullName.Add(mkvThumbOutputFileFullPath);
var thumbNailFile = new MediaFile(mkvThumbOutputFileFullPath);
await ffmpeg.GetThumbnailAsync(inputFile, thumbNailFile, new ConversionOptions
{
Seek = tsItem,
//下面表示3行代码表示需要自定义截图的尺寸
VideoSize = VideoSize.Custom,
CustomWidth = , // 截图的宽度
CustomHeight = , // 截图的高度
FFmpegDrawTextArgs = string.Format("borderw=10:bordercolor=white:fontcolor=#A9A9A8:fontsize=100:fontfile=FreeSerif.ttf:text='{0}\\:{1}\\:{2}':x =(w-text_w-50):y=(h-text_h-50)",
tsItem.Hours.ToString().PadLeft(, ''),
tsItem.Minutes.ToString().PadLeft(, ''),
tsItem.Seconds.ToString().PadLeft(, '')
)
});
}
ShowAndLog("ffmpeg 转换完成。结果如下:", false,null);
globalWatch.Stop();
ShowAndLog(string.Format("运行结束!共耗时 {0} 毫秒。", globalWatch.ElapsedMilliseconds), false, null);
} private async void btnGetVideoInfo_Click(object sender, EventArgs e)
{
ShowAndLog("ffmpeg 准备开始获取,请稍后...", false, null);
Stopwatch globalWatch = Stopwatch.StartNew();
var ffmpeg = new Engine(TaskffmpegNETExeFullPath);
//ffmpeg.Progress += OnProgress;
//ffmpeg.Data += OnDataSimpleShow;
ffmpeg.Error += OnError;
ffmpeg.Complete += OnComplete; var inputFile = new MediaFile(_videoFileFullPath);
MetaData md = await ffmpeg.GetMetaDataAsync(inputFile); ShowAndLog(string.Format("Duration:{0}", md.Duration), false, null);
ShowAndLog(string.Format("VideoData:{0}", JsonHelper.SerializeToJson(md.VideoData)), false, null);
ShowAndLog(string.Format("AudioData:{0}", JsonHelper.SerializeToJson(md.AudioData)), false, null); globalWatch.Stop();
ShowAndLog(string.Format("运行结束!共耗时 {0} 毫秒。", globalWatch.ElapsedMilliseconds), false, null); }
}

我的 UI

我的FFmpeg配置

获取截图,FFmpeg 运行的命令大概是:

ffmpeg.exe -ss 00:00:05 -i TestVideo.mp4 -vframes 1 -s  800:450   hello.jpg

其中, -ss 表示从第 5 秒开始截图,  -i 表示视频文件的路径,-vframes 表示截取 1 帧, -s 表示截取宽度为 800,高度为 450 像素的图片,hello.jpg 表示图片的名称。

其它的 FFmpeg 配置

1.下面是 FFmpeg 配置是在截图的时候,在图片的正中间显示 “当前时间”

ffmpeg.exe -ss 00:00:05 -i TestVideo.mp4 -vframes 1 -s  800:450 -vf  drawtext="fontcolor=red:fontsize=50:fontfile=FreeSerif.ttf:text='%{localtime\:%H\\\:%M\\\:%S}':x =(w-text_w)/2:y=(h-text_h)/ 2"   hello.jpg

2.下面是 FFmpeg 配置是在截图的时候,在图片的右下角(距离右边100像素,距离底部100像素)显示 “当前时间”

ffmpeg.exe -ss 00:00:05 -i TestVideo.mp4 -vframes 1 -s  800:450 -vf  drawtext="fontcolor=red:fontsize=50:fontfile=FreeSerif.ttf:text='%{localtime\:%H\\\:%M\\\:%S}':x =(w-text_w-100):y=(h-text_h-100)"   hello.jpg

3. 显示当前播放进度

ffmpeg.exe -ss 00:00:07 -i TestVideo.mp4 -vframes 1 -s  800:450 -vf  drawtext="fontcolor=red:fontsize=50:fontfile=FreeSerif.ttf:text='hms\: %{pts\:hms}, flt\: %{pts\:flt}':x =(w-text_w-50):y=(h-text_h-50)"   hello.jpg

4. 经过实际测试,暂时还是无法自动获取播放进度,只好把时间写死在参数中了

ffmpeg.exe -ss 00:04:45 -i TestVideo.mp4 -vframes 1 -s  800:450 -vf  drawtext="fontcolor=red:fontsize=50:fontfile=FreeSerif.ttf:text='00\:04\:45':x =(w-text_w-50):y=(h-text_h-50)"   hello.jpg

运行结果

获取视频信息的运行结果

谢谢浏览!

对于 FFmpeg.NET 开源项目,我的更改的更多相关文章

  1. FFMPEG相关开源项目

    1.FFmpeg build for android random architectures with example jnihttps://github.com/appunite/AndroidF ...

  2. Github上关于iOS的各种开源项目集合(强烈建议大家收藏,查看,总有一款你需要)

    下拉刷新 EGOTableViewPullRefresh - 最早的下拉刷新控件. SVPullToRefresh - 下拉刷新控件. MJRefresh - 仅需一行代码就可以为UITableVie ...

  3. iOS及Mac开源项目和学习资料【超级全面】

    UI 下拉刷新 EGOTableViewPullRefresh – 最早的下拉刷新控件. SVPullToRefresh – 下拉刷新控件. MJRefresh – 仅需一行代码就可以为UITable ...

  4. 直接拿来用!最火的iOS开源项目

    1. AFNetworking 在众多iOS开源项目中,AFNetworking可以称得上是最受开发者欢迎的库项目.AFNetworking是一个轻量级的iOS.Mac OS X网络通信类库,现在是G ...

  5. iOS开发--iOS及Mac开源项目和学习资料

    文/零距离仰望星空(简书作者)原文链接:http://www.jianshu.com/p/f6cdbc8192ba著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”. 原文出处:codecl ...

  6. iOS、mac开源项目及库汇总

    原文地址:http://blog.csdn.net/qq_26359763/article/details/51076499    iOS每日一记------------之 中级完美大整理 iOS.m ...

  7. github上关于iOS的各种开源项目集合(转)

    UI 下拉刷新 EGOTableViewPullRefresh - 最早的下拉刷新控件. SVPullToRefresh - 下拉刷新控件. MJRefresh - 仅需一行代码就可以为UITable ...

  8. 直接拿来用!最火的iOS开源项目(一~三)

    结束了GitHub平台上“最受欢迎的Android开源项目”系列盘点之后,我们正式迎来了“GitHub上最受欢迎的iOS开源项目”系列盘点.今天,我们将介绍20个在GitHub上非常受开发者欢迎的iO ...

  9. 直接拿来用!最火的iOS开源项目(三)

    相比Android,GitHub上的iOS开源项目更可谓是姹紫嫣红.尽管效果各异,但究其根源,却都是因为开发者本身对于某种效果的需求以及热爱.在“直接拿来用!最火的iOS开源项目”系列文章(一).(二 ...

随机推荐

  1. mockjs 在项目中vue项目中使用

    一.为什么要使用mockjs 总结起来就是在后端接口没有开发完成之前,前端可以用已有的接口文档,在真实的请求上拦截ajax,并根据mockjs的mock数据的规则,模拟真实接口返回的数据,并将随机的模 ...

  2. LeetCode——Employees Earning More Than Their Managers

    The Employee table holds all employees including their managers. Every employee has an Id, and there ...

  3. Python 定时调度

    APScheduler APScheduler是基于Quartz的一个Python定时任务框架,实现了Quartz的所有功能,使用起来十分方便.提供了基于日期.固定时间间隔以及crontab类型的任务 ...

  4. vlan技术简单了解

    VLAN(虚拟局域网)是对连接到的第二层交换机端口的网络用户的逻辑分段,不受网络用户的物理位置限制而根据用户需求进行网络分段.一个VLAN可以在一个交换机或者跨交换机实现.VLAN可以根据网络用户的位 ...

  5. windows经典主题 桌面颜色(R58 G110 U165)

  6. XLM论文原理解析

    1. 前言 近一年来,NLP领域发展势头强劲,从ELMO到LSTM再到去年最牛叉的Google Bert,在今年年初,Facebook又推出了XLM模型,在跨语言预训练领域表现抢眼.实验结果显示XLM ...

  7. linux (08) nginx入门详解

    一. nginx 入门 nginx 入门学习 web服务器软件 windows IIS服务器 linux nginx apache 收费​lighthttp 公司的技术栈 收费版技术栈 apache ...

  8. VC 静态库与动态库(四)动态库创建与使用_显示调用

    在第三章的基础上,接着添加一个显示调用项目 显示调用项目创建: 1.给解决方案添加一个新的控制台项目DisplayCall用于测试动态库,创建完成后设置为启动项目 2.DisplayCall.cpp添 ...

  9. CF891B Gluttony

    原题链接 DOWNLOAD AS PDF 题目大意 给你一个有\(n\)个元素的数组\(a\),让你构造一个数组\(b\),满足从 \(a\).\(b\)中任选出\(k\)个下标对应的元素,它们的和不 ...

  10. 11/10 <priorityQueue> 215 347

    215. Kth Largest Element in an Array 快速排序法,选择一个数,比这个数大的交换到左边,比这个数小的交换到右边. class Solution { public in ...