项目地址: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/

 对于 FFmpeg.NET 开源项目,我的更改

官方示例

代码如下:

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 类 

对于 FFmpeg.NET 开源项目,我的更改

 

文本代码:

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

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

 

 

 

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

 

对于 FFmpeg.NET 开源项目,我的更改

 

文本代码:

            // 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 方法

对于 FFmpeg.NET 开源项目,我的更改

 

文本代码:

            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>

对于 FFmpeg.NET 开源项目,我的更改

 

代码如下:

 

    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.NET 开源项目,我的更改

 

我的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 开源项目,我的更改

 

对于 FFmpeg.NET 开源项目,我的更改

 

对于 FFmpeg.NET 开源项目,我的更改

对于 FFmpeg.NET 开源项目,我的更改

 

对于 FFmpeg.NET 开源项目,我的更改

对于 FFmpeg.NET 开源项目,我的更改

对于 FFmpeg.NET 开源项目,我的更改

 

对于 FFmpeg.NET 开源项目,我的更改

 

对于 FFmpeg.NET 开源项目,我的更改

对于 FFmpeg.NET 开源项目,我的更改

 

对于 FFmpeg.NET 开源项目,我的更改

对于 FFmpeg.NET 开源项目,我的更改

对于 FFmpeg.NET 开源项目,我的更改

对于 FFmpeg.NET 开源项目,我的更改

 

对于 FFmpeg.NET 开源项目,我的更改

对于 FFmpeg.NET 开源项目,我的更改

 

 对于 FFmpeg.NET 开源项目,我的更改

 

 

获取视频信息的运行结果

 

 

对于 FFmpeg.NET 开源项目,我的更改

 

谢谢浏览!

 

相关文章:

  • 2021-12-10
  • 2021-07-31
  • 2021-04-03
  • 2022-01-26
  • 2021-10-15
  • 2021-08-22
猜你喜欢
  • 2021-07-04
  • 2021-08-17
  • 2021-06-15
  • 2022-12-23
  • 2021-04-16
  • 2021-11-25
  • 2022-02-12
相关资源
相似解决方案