对于 FFmpeg.NET 开源项目,我的更改
2019-03-27 17:51 音乐让我说 阅读(2708) 评论(2) 编辑 收藏 举报项目地址: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(3) }); 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 ?? 100, conversionOptions.CustomHeight ?? 100); } 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(61) }); 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(0,0, 5), new TimeSpan(0,1, 5), new TimeSpan(0,2, 5), new TimeSpan(0,3, 5), new TimeSpan(0,4, 5), new TimeSpan(0,4, 55), }; var ffmpeg = new Engine(TaskffmpegNETExeFullPath); //ffmpeg.Progress += OnProgress; //ffmpeg.Data += OnDataSimpleShow; ffmpeg.Error += OnError; ffmpeg.Complete += OnComplete; var inputFile = new MediaFile(_videoFileFullPath); int i = 0; 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 = 800, // 截图的宽度 CustomHeight = 450, // 截图的高度 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(2, '0'), tsItem.Minutes.ToString().PadLeft(2, '0'), tsItem.Seconds.ToString().PadLeft(2, '0') ) }); } 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
运行结果
获取视频信息的运行结果
谢谢浏览!
作者:音乐让我说(音乐让我说 - 博客园)
出处:http://music.cnblogs.com/
文章版权归本人所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
2012-03-27 代码练习在 C# 中得到一个 object (包含匿名对象)的属性和属性值