代码改变世界

如何让 FFmpeg 支持异步并行转码、截图等等操作?

2019-03-29 16:31  音乐让我说  阅读(1499)  评论(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

 

 

 

谢谢浏览!