项目地址: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. PHP获得毫秒数

    因为前端需要写函数处理时间戳,比较麻烦,所以我们有的时候,需要接口传递毫秒数给前端. 下面可以通过这个函数来获得毫秒数 <?php function getMillisecond() { lis ...

  2. Java操作zip-压缩和解压文件

    一.说明 rar格式的压缩包收费,java支持zip格式的压缩和解压 二.工具类 import java.io.*; import java.util.Enumeration; import java ...

  3. 图书推荐《图解HTTP》

    作品简介 本书对互联网基盘——HTTP协议进行了全面系统的介绍.作者由HTTP协议的发展历史娓娓道来,严谨细致地剖析了HTTP协议的结构,列举诸多常见通信场景及实战案例,最后延伸到Web安全.最新技术 ...

  4. 第一章 权限管理DEMO简介

    源代码GitHub:https://github.com/ZhaoRd/Zrd_0001_AuthorityManagement 1.系列介绍 工作已有五年之久,一直有想通过博客写点自己知道的,在博客 ...

  5. Mybatis插入实体类字段为关键字解决方案

    1. Mybatis插入实体类字段为关键字解决方案 1.1. 前言 可能你插入字段为关键字时报如下错误,且字段名不适合改变 You have an error in your SQL syntax; ...

  6. linux7系统进入单用户模式

    1.重启系统,在出现选择内核界面的时候按“e”键 2.移动光标到红色找到LANG=zh_CN.UTF-8 增加“init=/sysroot/bin/sh” 修改后如下图 3.使用"ctrl+ ...

  7. HDU 1072 Nightmare 题解

    Nightmare Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total S ...

  8. Java八大排序之插入排序

    插入排序 也可叫直接插入排序,该算法的思路是:初始可认为文件中的第1个记录已排好序,然后将第2个到第n个记录依次插入到已排序的记录组成的文件中. 步骤: 假设有一组数组为(数组下标0—n-1): ar ...

  9. Deep Q Network(DQN)原理解析

    1. 前言 在前面的章节中我们介绍了时序差分算法(TD)和Q-Learning,当状态和动作空间是离散且维数不高时可使用Q-Table储存每个状态动作对的Q值,而当状态和动作空间是高维连续时,使用Q- ...

  10. appium 基础:常用api接口(2)

    一.获取手机分辨率 size=driver.get_window_size()#获取手机屏幕大小,分辨率 print(size)#{'width': 720, 'height': 1280} 得到的是 ...