如何让 FFmpeg 支持异步并行转码、截图等等操作?
2019-03-29 16:31 音乐让我说 阅读(1632) 评论(0) 编辑 收藏 举报直接贴代码了:
ffmpegTest02.cs
public partial class ffmpegTest02 : FormBase { private static readonly string TaskffmpegNETExeFullPath = ConfigurationManager.AppSettings["TaskffmpegNETExeFullPath"]; string _videoFileFullPath = @"D:\Workspace\TestVideo.mp4"; string _othereVideoFileFullPath = @"D:\Workspace\TestVideo{0}.mp4"; string snapshotParentDir = @"D:\Workspace\TestVideoSnapshot"; int exeMoreCrawlSnapshotCount = 10; 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 IEnumerable<TimeSpan> CreateTimeSpanArray() { for (int i = 0; i < 20; i++) { yield return new TimeSpan(0, i, 5); } } private ConversionOptions GetConversionOptions(TimeSpan tsItem) { return 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') ), ExtraFFmpegArgs = " -y " // -y 表示目标文件已经存在,则覆盖输出文件 }; } 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(@"{0}\TestVideo-mkv-thumb-{1}.png", snapshotParentDir, 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, GetConversionOptions(TimeSpan.FromSeconds(65))); var metadata = await ffmpeg.GetMetaDataAsync(output); Console.WriteLine(metadata.FileInfo.FullName); Console.WriteLine(metadata); } catch (Exception exc) { Console.WriteLine(exc); } } 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); } private async void btnStartSingleSnapshot_Click(object sender, EventArgs e) { ShowAndLog("ffmpeg 准备开始转换,请稍后...", false, null); Stopwatch globalWatch = Stopwatch.StartNew(); IEnumerable<TimeSpan> timeSpanArray = CreateTimeSpanArray(); 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(@"{0}\TestVideo-mp4-thumb-{1}-{2}.jpg", snapshotParentDir, DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss.fff"), i); allThumbFullName.Add(mkvThumbOutputFileFullPath); var thumbNailFile = new MediaFile(mkvThumbOutputFileFullPath); await ffmpeg.GetThumbnailAsync(inputFile, thumbNailFile, GetConversionOptions(tsItem) ); } ShowAndLog("ffmpeg 转换完成。结果如下:", false, null); globalWatch.Stop(); ShowAndLog(string.Format("运行结束!共耗时 {0} 毫秒。", globalWatch.ElapsedMilliseconds), false, null); } private async void btnParelleSyncStartMoreCrawlSnapshot_ClickAsync(object sender, EventArgs e) { ShowAndLog("准备开始同步多个 TestVideo{n}.mp4 上截图,请稍后...", false, null); Stopwatch globalWatch = Stopwatch.StartNew(); for (int i = 0; i < exeMoreCrawlSnapshotCount; i++) { await CreateSnapshotCoreAsync(i + 1); } ShowAndLog("同步多个 TestVideo{n}.mp4 上截图完成。结果如下:", false, null); globalWatch.Stop(); ShowAndLog(string.Format("运行结束!共耗时 {0} 毫秒。", globalWatch.ElapsedMilliseconds), false, null); } private async Task CreateSnapshotCoreAsync(int fileId) { IEnumerable<TimeSpan> timeSpanArray = CreateTimeSpanArray(); var ffmpeg = new Engine(TaskffmpegNETExeFullPath); //ffmpeg.Progress += OnProgress; //ffmpeg.Data += OnDataSimpleShow; ffmpeg.Error += OnError; ffmpeg.Complete += OnComplete; var inputFile = new MediaFile(string.Format(_othereVideoFileFullPath, fileId)); //_videoFileFullPath int i = 0; string mkvThumbOutputFileFullPath; List<string> allThumbFullName = new List<string>(); foreach (TimeSpan tsItem in timeSpanArray) { i++; mkvThumbOutputFileFullPath = string.Format(@"{0}\TestVideo{1}-mp4-thumb-{2}-{3}.jpg", snapshotParentDir, fileId, DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss.fff"), i); allThumbFullName.Add(mkvThumbOutputFileFullPath); var thumbNailFile = new MediaFile(mkvThumbOutputFileFullPath); await ffmpeg.GetThumbnailAsync(inputFile, thumbNailFile, GetConversionOptions(tsItem)); } } private async void btnParelleAsyncStartMoreCrawlSnapshot_ClickAsync(object sender, EventArgs e) { ShowAndLog("准备开始异步并行多个 TestVideo{n}.mp4 上截图,请稍后...", false, null); Stopwatch globalWatch = Stopwatch.StartNew(); var tasks = Enumerable.Range(0, exeMoreCrawlSnapshotCount).Select(i => { return Task.Run(async () => { await CreateSnapshotCoreAsync(i + 1); }); }); await Task.WhenAll(tasks); ShowAndLog("异步并行多个 TestVideo{n}.mp4 上截图完成。结果如下:", false, null); globalWatch.Stop(); ShowAndLog(string.Format("运行结束!共耗时 {0} 毫秒。", globalWatch.ElapsedMilliseconds), false, null); } private async void btnParelleAsyncStartMoreCrawlSnapshotLimitMaxDop_Click(object sender, EventArgs e) { ShowAndLog("准备开始异步并行多个 TestVideo{n}.mp4 上截图(限制最大异步处理单元),请稍后...", false, null); Stopwatch globalWatch = Stopwatch.StartNew(); var tasks = Enumerable.Range(0, exeMoreCrawlSnapshotCount).ParallelForEachAsync(i => { return Task.Run(async () => { await CreateSnapshotCoreAsync(i + 1); }); }, Environment.ProcessorCount); await Task.WhenAll(tasks); ShowAndLog("异步并行多个 TestVideo{n}.mp4 上截图(限制最大异步处理单元)完成。结果如下:", false, null); globalWatch.Stop(); ShowAndLog(string.Format("运行结束!共耗时 {0} 毫秒。", globalWatch.ElapsedMilliseconds), false, null); } }
ffmpegTest02.Designer.cs
partial class ffmpegTest02 { /// <summary> /// Required designer variable. /// </summary> private System.ComponentModel.IContainer components = null; /// <summary> /// Clean up any resources being used. /// </summary> /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param> protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// <summary> /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// </summary> private void InitializeComponent() { this.btnStartConvertAndSnapshot = new System.Windows.Forms.Button(); this.listInfoLog = new System.Windows.Forms.ListBox(); this.btnStartSingleSnapshot = new System.Windows.Forms.Button(); this.btnGetVideoInfo = new System.Windows.Forms.Button(); this.btnParelleSyncStartMoreCrawlSnapshot = new System.Windows.Forms.Button(); this.btnParelleAsyncStartMoreCrawlSnapshot = new System.Windows.Forms.Button(); this.btnParelleAsyncStartMoreCrawlSnapshotLimitMaxDop = new System.Windows.Forms.Button(); this.SuspendLayout(); // // btnStartConvertAndSnapshot // this.btnStartConvertAndSnapshot.Location = new System.Drawing.Point(12, 12); this.btnStartConvertAndSnapshot.Name = "btnStartConvertAndSnapshot"; this.btnStartConvertAndSnapshot.Size = new System.Drawing.Size(837, 23); this.btnStartConvertAndSnapshot.TabIndex = 0; this.btnStartConvertAndSnapshot.Text = "1. 开始先把 TestVideo.mp4 转换 mkv,再在第 1 分钟的时候截图"; this.btnStartConvertAndSnapshot.UseVisualStyleBackColor = true; this.btnStartConvertAndSnapshot.Click += new System.EventHandler(this.btnStartConvertAndSnapshot_Click); // // listInfoLog // this.listInfoLog.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.listInfoLog.Font = new System.Drawing.Font("宋体", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); this.listInfoLog.FormattingEnabled = true; this.listInfoLog.HorizontalScrollbar = true; this.listInfoLog.Location = new System.Drawing.Point(2, 287); this.listInfoLog.Name = "listInfoLog"; this.listInfoLog.ScrollAlwaysVisible = true; this.listInfoLog.Size = new System.Drawing.Size(857, 238); this.listInfoLog.TabIndex = 1; // // btnStartSingleSnapshot // this.btnStartSingleSnapshot.Location = new System.Drawing.Point(12, 50); this.btnStartSingleSnapshot.Name = "btnStartSingleSnapshot"; this.btnStartSingleSnapshot.Size = new System.Drawing.Size(837, 23); this.btnStartSingleSnapshot.TabIndex = 2; this.btnStartSingleSnapshot.Text = "2. 不转码,直接在第 1分钟的时候从 TestVideo.mp4 上截图"; this.btnStartSingleSnapshot.UseVisualStyleBackColor = true; this.btnStartSingleSnapshot.Click += new System.EventHandler(this.btnStartSingleSnapshot_Click); // // btnGetVideoInfo // this.btnGetVideoInfo.Location = new System.Drawing.Point(12, 90); this.btnGetVideoInfo.Name = "btnGetVideoInfo"; this.btnGetVideoInfo.Size = new System.Drawing.Size(837, 23); this.btnGetVideoInfo.TabIndex = 3; this.btnGetVideoInfo.Text = "3. 仅仅获取 TestVideo.mp4 视频的信息"; this.btnGetVideoInfo.UseVisualStyleBackColor = true; this.btnGetVideoInfo.Click += new System.EventHandler(this.btnGetVideoInfo_Click); // // btnParelleSyncStartMoreCrawlSnapshot // this.btnParelleSyncStartMoreCrawlSnapshot.Location = new System.Drawing.Point(12, 137); this.btnParelleSyncStartMoreCrawlSnapshot.Name = "btnParelleSyncStartMoreCrawlSnapshot"; this.btnParelleSyncStartMoreCrawlSnapshot.Size = new System.Drawing.Size(837, 23); this.btnParelleSyncStartMoreCrawlSnapshot.TabIndex = 4; this.btnParelleSyncStartMoreCrawlSnapshot.Text = "4. 同步多个 TestVideo{n}.mp4 上截图"; this.btnParelleSyncStartMoreCrawlSnapshot.UseVisualStyleBackColor = true; this.btnParelleSyncStartMoreCrawlSnapshot.Click += new System.EventHandler(this.btnParelleSyncStartMoreCrawlSnapshot_ClickAsync); // // btnParelleAsyncStartMoreCrawlSnapshot // this.btnParelleAsyncStartMoreCrawlSnapshot.Location = new System.Drawing.Point(12, 182); this.btnParelleAsyncStartMoreCrawlSnapshot.Name = "btnParelleAsyncStartMoreCrawlSnapshot"; this.btnParelleAsyncStartMoreCrawlSnapshot.Size = new System.Drawing.Size(837, 23); this.btnParelleAsyncStartMoreCrawlSnapshot.TabIndex = 5; this.btnParelleAsyncStartMoreCrawlSnapshot.Text = "5. 异步并行多个 TestVideo{n}.mp4 上截图"; this.btnParelleAsyncStartMoreCrawlSnapshot.UseVisualStyleBackColor = true; this.btnParelleAsyncStartMoreCrawlSnapshot.Click += new System.EventHandler(this.btnParelleAsyncStartMoreCrawlSnapshot_ClickAsync); // // btnParelleAsyncStartMoreCrawlSnapshotLimitMaxDop // this.btnParelleAsyncStartMoreCrawlSnapshotLimitMaxDop.Location = new System.Drawing.Point(12, 222); this.btnParelleAsyncStartMoreCrawlSnapshotLimitMaxDop.Name = "btnParelleAsyncStartMoreCrawlSnapshotLimitMaxDop"; this.btnParelleAsyncStartMoreCrawlSnapshotLimitMaxDop.Size = new System.Drawing.Size(837, 23); this.btnParelleAsyncStartMoreCrawlSnapshotLimitMaxDop.TabIndex = 6; this.btnParelleAsyncStartMoreCrawlSnapshotLimitMaxDop.Text = "6. 异步并行多个 TestVideo{n}.mp4 上截图(限制最大异步处理单元)"; this.btnParelleAsyncStartMoreCrawlSnapshotLimitMaxDop.UseVisualStyleBackColor = true; this.btnParelleAsyncStartMoreCrawlSnapshotLimitMaxDop.Click += new System.EventHandler(this.btnParelleAsyncStartMoreCrawlSnapshotLimitMaxDop_Click); // // ffmpegTest02 // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(861, 531); this.Controls.Add(this.btnParelleAsyncStartMoreCrawlSnapshotLimitMaxDop); this.Controls.Add(this.btnParelleAsyncStartMoreCrawlSnapshot); this.Controls.Add(this.btnParelleSyncStartMoreCrawlSnapshot); this.Controls.Add(this.btnGetVideoInfo); this.Controls.Add(this.btnStartSingleSnapshot); this.Controls.Add(this.listInfoLog); this.Controls.Add(this.btnStartConvertAndSnapshot); this.Name = "ffmpegTest02"; this.Text = "ffmpegTest02"; this.ResumeLayout(false); } #endregion private System.Windows.Forms.Button btnStartConvertAndSnapshot; private System.Windows.Forms.ListBox listInfoLog; private System.Windows.Forms.Button btnStartSingleSnapshot; private System.Windows.Forms.Button btnGetVideoInfo; private System.Windows.Forms.Button btnParelleSyncStartMoreCrawlSnapshot; private System.Windows.Forms.Button btnParelleAsyncStartMoreCrawlSnapshot; private System.Windows.Forms.Button btnParelleAsyncStartMoreCrawlSnapshotLimitMaxDop; }
运行结果
图 01
图 02
图 03
图 04
图 05
图 06
图 07
图 08
谢谢浏览!
作者:音乐让我说(音乐让我说 - 博客园)
出处:http://music.cnblogs.com/
文章版权归本人所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
2012-03-29 一个枚举(Enum)的位操作的例子
2012-03-29 ASP.NET MVC 3 中 RemoteAttribute 的使用 Demo
2012-03-29 Html 在线编辑器 KindEditor 的使用例子