代码之家  ›  专栏  ›  技术社区  ›  Jacob Poul Richardt

C/.NET的固体FFMPEG包装

  •  74
  • Jacob Poul Richardt  · 技术社区  · 15 年前

    我在网上搜索了一段时间 FFMPEG 包装材料 C.//NET . 但我还没有想出有用的办法。我发现了以下三个项目,但它们都在阿尔法早期死亡。

    FFmpeg.NET
    ffmpeg-sharp
    FFLIB.NET

    所以我的问题是,是否有人知道一个更成熟的包装器项目?
    我不是在寻找一个完整的转码引擎与工作队列等。 只是一个简单的包装器,所以我不必进行命令行调用,然后解析控制台输出,但可以进行方法调用,并使用事件侦听器来进行处理。

    请随意提及任何活跃的项目,即使它们还在早期阶段。

    12 回复  |  直到 6 年前
        1
  •  20
  •   Aydin    11 年前

    这是我自己的包装: https://github.com/AydinAdn/MediaToolkit

    MediaToolkit可以:

    • 将视频文件转换为各种其他视频格式。
    • 执行视频转码任务。
      • 可配置选项: Bit rate , Frame rate , Resolution / size , Aspect ratio , Duration of video
    • 执行音频转码任务。
      • 可配置选项: Audio sample rate
    • 使用电影、PAL或NTSC电视标准将视频转换为物理格式
      • 介质包括: DVD , DV , DV50 , VCD , SVCD

    我正在进行更新,欢迎您使用它,您也可以使用包管理器控制台安装它。

    PM> Install-Package MediaToolkit
    
        2
  •  11
  •   Jason    15 年前

    试试这个,我想我可能写了一些东西,你可以用来做一个简单的包装。

    http://jasonjano.wordpress.com/2010/02/09/a-simple-c-wrapper-for-ffmpeg/

        3
  •  9
  •   Famdam    13 年前

    我使用了来自asp.net/windows服务(.net)应用程序的ffmpeg。 但我最终使用了命令行,没有解析控制台。 通过使用这个——我有一个简单的方法来控制——ffmpeg的更新和在多个内核上运行多个转换。

        4
  •  6
  •   Ruslan Balanukhin orca    7 年前

    在试了几次包装之后,我接着说: FFmpeg auto generated unsafe bindings for C#/.NET and Mono .

    它是ffmpeg命名空间中每个类的一组低级互操作绑定。 也许不像实际的包装器那样方便使用,但是如果您想做一些不重要的事情,imo是在.net中使用ffmpeg的最佳解决方案。

    赞成的意见:

    • 作品
    • 可信-没有第三方包装代码引入错误,假设您信任ffmpeg本身。
    • 它总是更新到ffmpeg的最新版本
    • 单一的 nuget package 对于所有绑定
    • 包含了XML文档,但您仍然可以使用联机文档 FFmpeg documentation .

    欺骗:

    • 低级:你必须知道如何使用指向 C结构 .
    • 一开始需要一些工作才能让它工作。我建议向你学习 the official examples .

    注意:这个线程是关于使用ffmpeg api的,但是对于某些用例,最好简单地使用ffmpeg.exe command line interface .

        5
  •  4
  •   Christophe Chang    13 年前

    我在玩一个名为mediahandler pro的ffmpeg包装库

    http://www.mediasoftpro.com

    目前看来很有希望。

        7
  •  2
  •   JTtheGeek    11 年前

    给你……这段代码大部分已经有2年多的历史了,因此缺少了很多异步的东西,并且使用了过时的命名约定。在生产环境中运行一段时间 ~JT

    internal static class FFMpegArgUtils
        {
            public static string GetEncodeVideoFFMpegArgs(string sSourceFile, MP4Info objMp4Info, double nMbps, int iWidth, int iHeight, bool bIncludeAudio, string sOutputFile)
            {
                //Ensure file contains a video stream, otherwise this command will fail
                if (objMp4Info != null && objMp4Info.VideoStreamCount == 0)
                {
                    throw new Exception("FFMpegArgUtils::GetEncodeVideoFFMpegArgs - mp4 does not contain a video stream");
                }
    
                int iBitRateInKbps = (int)(nMbps * 1000);
    
    
                StringBuilder sbArgs = new StringBuilder();
                sbArgs.Append(" -y -threads 2 -i \"" + sSourceFile + "\" -strict -2 "); // 0 tells it to choose how many threads to use
    
                if (bIncludeAudio == true)
                {
                    //sbArgs.Append(" -acodec libmp3lame -ab 96k");
                    sbArgs.Append(" -acodec aac -ar 44100 -ab 96k");
                }
                else
                {
                    sbArgs.Append(" -an");
                }
    
    
                sbArgs.Append(" -vcodec libx264 -level 41 -r 15 -crf 25 -g 15  -keyint_min 45 -bf 0");
    
                //sbArgs.Append(" -vf pad=" + iWidth + ":" + iHeight + ":" + iVideoOffsetX + ":" + iVideoOffsetY);
                sbArgs.Append(String.Format(" -vf \"scale=iw*min({0}/iw\\,{1}/ih):ih*min({0}/iw\\,{1}/ih),pad={0}:{1}:({0}-iw)/2:({1}-ih)/2\"",iWidth, iHeight));
    
                //Output File
                sbArgs.Append(" \"" + sOutputFile + "\"");
                return sbArgs.ToString();
            }
    
            public static string GetEncodeAudioFFMpegArgs(string sSourceFile, string sOutputFile)
            {
                var args = String.Format(" -y -threads 2 -i \"{0}\" -strict -2  -acodec aac -ar 44100 -ab 96k -vn \"{1}\"", sSourceFile, sOutputFile);
                return args;
    
    
                //return GetEncodeVideoFFMpegArgs(sSourceFile, null, .2, 854, 480, true, sOutputFile);
                //StringBuilder sbArgs = new StringBuilder();
                //int iWidth = 854;
                //int iHeight = 480;
                //sbArgs.Append(" -y -i \"" + sSourceFile + "\" -strict -2 "); // 0 tells it to choose how many threads to use
                //sbArgs.Append(" -acodec aac -ar 44100 -ab 96k");
                //sbArgs.Append(" -vcodec libx264 -level 41 -r 15 -crf 25 -g 15  -keyint_min 45 -bf 0");
                //sbArgs.Append(String.Format(" -vf \"scale=iw*min({0}/iw\\,{1}/ih):ih*min({0}/iw\\,{1}/ih),pad={0}:{1}:({0}-iw)/2:({1}-ih)/2\"", iWidth, iHeight));
                //sbArgs.Append(" \"" + sOutputFile + "\"");
                //return sbArgs.ToString();
            }
        }
    
    internal class CreateEncodedVideoCommand : ConsoleCommandBase
        {
            public event ProgressEventHandler OnProgressEvent;
    
            private string _sSourceFile;
            private  string _sOutputFolder;
            private double _nMaxMbps;
    
            public double BitrateInMbps
            {
                get { return _nMaxMbps; }
            }
    
            public int BitrateInKbps
            {
                get { return (int)Math.Round(_nMaxMbps * 1000); }
            }
    
            private int _iOutputWidth;
            private int _iOutputHeight;
    
            private bool _bIsConverting = false;
            //private TimeSpan _tsDuration;
            private double _nPercentageComplete;
            private string _sOutputFile;
            private string _sOutputFileName;
    
    
            private bool _bAudioEnabled = true;
            private string _sFFMpegPath;
            private string _sExePath;
            private string _sArgs;
            private MP4Info _objSourceInfo;
            private string _sOutputExt;
    
            /// <summary>
            /// Encodes an MP4 to the specs provided, quality is a value from 0 to 1
            /// </summary>
            /// <param name="nQuality">A value from 0 to 1</param>
            /// 
            public CreateEncodedVideoCommand(string sSourceFile, string sOutputFolder, string sFFMpegPath, double nMaxBitrateInMbps, MP4Info objSourceInfo, int iOutputWidth, int iOutputHeight, string sOutputExt)
            {
                _sSourceFile = sSourceFile;
                _sOutputFolder = sOutputFolder;
                _nMaxMbps = nMaxBitrateInMbps;
                _objSourceInfo = objSourceInfo;
                _iOutputWidth = iOutputWidth;
                _iOutputHeight = iOutputHeight;
                _sFFMpegPath = sFFMpegPath;
                _sOutputExt = sOutputExt;
            }
    
            public void SetOutputFileName(string sOutputFileName)
            {
                _sOutputFileName = sOutputFileName;
            }
    
    
            public override void Execute()
            {
                try
                {
                    _bIsConverting = false;
    
                    string sFileName = _sOutputFileName != null ? _sOutputFileName : Path.GetFileNameWithoutExtension(_sSourceFile) + "_" + _iOutputWidth + "." + _sOutputExt;
                    _sOutputFile = _sOutputFolder + "\\" + sFileName;
    
                    _sExePath = _sFFMpegPath;
                    _sArgs = FFMpegArgUtils.GetEncodeVideoFFMpegArgs(_sSourceFile, _objSourceInfo,_nMaxMbps, _iOutputWidth, _iOutputHeight, _bAudioEnabled, _sOutputFile);
    
                    InternalExecute(_sExePath, _sArgs);
                }
                catch (Exception objEx)
                {
                    DispatchException(objEx);
                }
            }
    
            public override string GetCommandInfo()
            {
                StringBuilder sbInfo = new StringBuilder();
                sbInfo.AppendLine("CreateEncodeVideoCommand");
                sbInfo.AppendLine("Exe: " + _sExePath);
                sbInfo.AppendLine("Args: " + _sArgs);
                sbInfo.AppendLine("[ConsoleOutput]");
                sbInfo.Append(ConsoleOutput);
                sbInfo.AppendLine("[ErrorOutput]");
                sbInfo.Append(ErrorOutput);
    
                return base.GetCommandInfo() + "\n" + sbInfo.ToString();
            }
    
            protected override void OnInternalCommandComplete(int iExitCode)
            {
                DispatchCommandComplete( iExitCode == 0 ? CommandResultType.Success : CommandResultType.Fail);
            }
    
            override protected void OnOutputRecieved(object sender, ProcessOutputEventArgs objArgs)
            {
                //FMPEG out always shows as Error
                base.OnOutputRecieved(sender, objArgs);
    
                if (_bIsConverting == false && objArgs.Data.StartsWith("Press [q] to stop encoding") == true)
                {
                    _bIsConverting = true;
                }
                else if (_bIsConverting == true && objArgs.Data.StartsWith("frame=") == true)
                {
                    //Capture Progress
                    UpdateProgressFromOutputLine(objArgs.Data);
                }
                else if (_bIsConverting == true && _nPercentageComplete > .8 && objArgs.Data.StartsWith("frame=") == false)
                {
                    UpdateProgress(1);
                    _bIsConverting = false;
                }
            }
    
            override protected void OnProcessExit(object sender, ProcessExitedEventArgs args)
            {
                _bIsConverting = false;
                base.OnProcessExit(sender, args);
            }
    
            override public void Abort()
            {
                if (_objCurrentProcessRunner != null)
                {
                    //_objCurrentProcessRunner.SendLineToInputStream("q");
                    _objCurrentProcessRunner.Dispose();
                }
            }
    
            #region Helpers
    
            //private void CaptureSourceDetailsFromOutput()
            //{
            //    String sInputStreamInfoStartLine = _colErrorLines.SingleOrDefault(o => o.StartsWith("Input #0"));
            //    int iStreamInfoStartIndex = _colErrorLines.IndexOf(sInputStreamInfoStartLine);
            //    if (iStreamInfoStartIndex >= 0)
            //    {
            //        string sDurationInfoLine = _colErrorLines[iStreamInfoStartIndex + 1];
            //        string sDurantionTime = sDurationInfoLine.Substring(12, 11);
    
            //        _tsDuration = VideoUtils.GetDurationFromFFMpegDurationString(sDurantionTime);
            //    }
            //}
    
            private void UpdateProgressFromOutputLine(string sOutputLine)
            {
                int iTimeIndex = sOutputLine.IndexOf("time=");
                int iBitrateIndex = sOutputLine.IndexOf(" bitrate=");
    
                string sCurrentTime = sOutputLine.Substring(iTimeIndex + 5, iBitrateIndex - iTimeIndex - 5);
                double nCurrentTimeInSeconds = double.Parse(sCurrentTime);
                double nPercentageComplete = nCurrentTimeInSeconds / _objSourceInfo.Duration.TotalSeconds;
    
                UpdateProgress(nPercentageComplete);
                //Console.WriteLine("Progress: " + _nPercentageComplete);
            }
    
            private void UpdateProgress(double nPercentageComplete)
            {
                _nPercentageComplete = nPercentageComplete;
                if (OnProgressEvent != null)
                {
                    OnProgressEvent(this, new ProgressEventArgs( _nPercentageComplete));
                }
            }
    
            #endregion
    
            //public TimeSpan Duration { get { return _tsDuration; } }
    
            public double Progress { get { return _nPercentageComplete;  } }
            public string OutputFile { get { return _sOutputFile; } }
    
            public bool AudioEnabled
            {
                get { return _bAudioEnabled; }
                set { _bAudioEnabled = value; }
            }
    }
    
    public abstract class ConsoleCommandBase : CommandBase, ICommand
        {
            protected ProcessRunner _objCurrentProcessRunner;
            protected   List<String> _colOutputLines;
            protected List<String> _colErrorLines;
    
    
            private int _iExitCode;
    
            public ConsoleCommandBase()
            {
                _colOutputLines = new List<string>();
                _colErrorLines = new List<string>();
            }
    
            protected void InternalExecute(string sExePath, string sArgs)
            {
                InternalExecute(sExePath, sArgs, null, null, null);
            }
    
            protected void InternalExecute(string sExePath, string sArgs, string sDomain, string sUsername, string sPassword)
            {
                try
                {
                    if (_objCurrentProcessRunner == null || _bIsRunning == false)
                    {
                        StringReader objStringReader = new StringReader(string.Empty);
    
                        _objCurrentProcessRunner = new ProcessRunner(sExePath, sArgs);
    
                        _objCurrentProcessRunner.SetCredentials(sDomain, sUsername, sPassword);
    
                        _objCurrentProcessRunner.OutputReceived += new ProcessOutputEventHandler(OnOutputRecieved);
                        _objCurrentProcessRunner.ProcessExited += new ProcessExitedEventHandler(OnProcessExit);
                        _objCurrentProcessRunner.Run();
    
                        _bIsRunning = true;
                        _bIsComplete = false;
                    }
                    else
                    {
                        DispatchException(new Exception("Processor Already Running"));
                    }
                }
                catch (Exception objEx)
                {
                    DispatchException(objEx);
                }
            }
    
            protected virtual void OnOutputRecieved(object sender, ProcessOutputEventArgs args)
            {
                try
                {
                    if (args.Error == true)
                    {
                        _colErrorLines.Add(args.Data);
                        //Console.WriteLine("Error: " + args.Data);
                    }
                    else
                    {
                        _colOutputLines.Add(args.Data);
                        //Console.WriteLine(args.Data);
                    }
                }
                catch (Exception objEx)
                {
                    DispatchException(objEx);
                }
            }
    
            protected virtual void OnProcessExit(object sender, ProcessExitedEventArgs args)
            {
                try
                {
                    Console.Write(ConsoleOutput);
                    _iExitCode = args.ExitCode;
    
                    _bIsRunning = false;
                    _bIsComplete = true;
    
                    //Some commands actually fail to succeed
                    //if(args.ExitCode != 0)
                    //{
                    //    DispatchException(new Exception("Command Failed: " + this.GetType().Name + "\nConsole: " + ConsoleOutput + "\nConsoleError: " + ErrorOutput));
                    //}
    
                    OnInternalCommandComplete(_iExitCode);
    
                    if (_objCurrentProcessRunner != null)
                    {
                        _objCurrentProcessRunner.Dispose();
                        _objCurrentProcessRunner = null;    
                    }
                }
                catch (Exception objEx)
                {
                    DispatchException(objEx);
                }
            }
    
            abstract protected void OnInternalCommandComplete(int iExitCode);
    
            protected string JoinLines(List<String> colLines)
            {
                StringBuilder sbOutput = new StringBuilder();
                colLines.ForEach( o => sbOutput.AppendLine(o));
                return sbOutput.ToString();
            }
    
            #region Properties
            public int ExitCode
            {
                get { return _iExitCode; }
            }
            #endregion
    
            public override string GetCommandInfo()
            {
                StringBuilder sbCommandInfo = new StringBuilder();
                sbCommandInfo.AppendLine("Command:  " + this.GetType().Name);
                sbCommandInfo.AppendLine("Console Output");
                if (_colOutputLines != null)
                {
                    foreach (string sOutputLine in _colOutputLines)
                    {
                        sbCommandInfo.AppendLine("\t" + sOutputLine);
                    }
                }
                sbCommandInfo.AppendLine("Error Output");
                if (_colErrorLines != null)
                {
                    foreach (string sErrorLine in _colErrorLines)
                    {
                        sbCommandInfo.AppendLine("\t" + sErrorLine);
                    }
                }
                return sbCommandInfo.ToString();
            }
    
            public String ConsoleOutput { get { return JoinLines(_colOutputLines); } }
            public String ErrorOutput { get { return JoinLines(_colErrorLines);} }
    
        }
    
    CommandBase : ICommand
        {
            protected IDedooseContext _context;
            protected Boolean _bIsRunning = false;
            protected Boolean _bIsComplete = false;
    
            #region Custom Events
            public event CommandCompleteEventHandler OnCommandComplete;
            event CommandCompleteEventHandler ICommand.OnCommandComplete
            {
                add { if (OnCommandComplete != null) { lock (OnCommandComplete) { OnCommandComplete += value; } } else { OnCommandComplete = new CommandCompleteEventHandler(value); } }
                remove { if (OnCommandComplete != null) { lock (OnCommandComplete) { OnCommandComplete -= value; } } }
            }
    
            public event UnhandledExceptionEventHandler OnCommandException;
            event UnhandledExceptionEventHandler ICommand.OnCommandException
            {
                add { if (OnCommandException != null) { lock (OnCommandException) { OnCommandException += value; } } else { OnCommandException = new UnhandledExceptionEventHandler(value); } }
                remove { if (OnCommandException != null) { lock (OnCommandException) { OnCommandException -= value; } } }
            }
    
            public event ProgressEventHandler OnProgressUpdate;
            event ProgressEventHandler ICommand.OnProgressUpdate
            {
                add { if (OnProgressUpdate != null) { lock (OnProgressUpdate) { OnProgressUpdate += value; } } else { OnProgressUpdate = new ProgressEventHandler(value); } }
                remove { if (OnProgressUpdate != null) { lock (OnProgressUpdate) { OnProgressUpdate -= value; } } }
            }
            #endregion
    
            protected CommandBase()
            {
                _context = UnityGlobalContainer.Instance.Context;
            }
    
            protected void DispatchCommandComplete(CommandResultType enResult)
            {
                if (enResult == CommandResultType.Fail)
                {
                    StringBuilder sbMessage = new StringBuilder();
                    sbMessage.AppendLine("Command Commpleted with Failure: "  + this.GetType().Name);
                    sbMessage.Append(GetCommandInfo());
                    Exception objEx = new Exception(sbMessage.ToString());
                    DispatchException(objEx);
                }
                else
                {
                    if (OnCommandComplete != null)
                    {
                        OnCommandComplete(this, new CommandCompleteEventArgs(enResult));
                    }
                }
            }
    
            protected void DispatchException(Exception objEx)
            {
                if (OnCommandException != null)
                { 
                    OnCommandException(this, new UnhandledExceptionEventArgs(objEx, true)); 
                }
                else
                {
                    _context.Logger.LogException(objEx, MethodBase.GetCurrentMethod());
                    throw objEx;
                }
            }
    
            protected void DispatchProgressUpdate(double nProgressRatio)
            {
                if (OnProgressUpdate != null) { OnProgressUpdate(this, new ProgressEventArgs(nProgressRatio)); } 
            }
    
            public virtual string GetCommandInfo()
            {
                return "Not Implemented: " + this.GetType().Name;
            }
    
            public virtual void Execute() { throw new NotImplementedException(); }
            public virtual void Abort() { throw new NotImplementedException(); }
    
            public Boolean IsRunning { get { return _bIsRunning; } }
            public Boolean IsComplete { get { return _bIsComplete; } }
    
            public double GetProgressRatio()
            {
                throw new NotImplementedException();
            }
        }
    
    public delegate void CommandCompleteEventHandler(object sender, CommandCompleteEventArgs e);
    
        public interface ICommand
        {
            event CommandCompleteEventHandler OnCommandComplete;
            event UnhandledExceptionEventHandler OnCommandException;
            event ProgressEventHandler OnProgressUpdate;
    
            double GetProgressRatio();
            string GetCommandInfo();
    
            void Execute();
            void Abort();
        }
    

    //roger knapp在process runner中查找processrunner的内容

        8
  •  2
  •   ickydime    8 年前

    我一直在研究同样的东西,最初使用的mediatoolkit(在另一个答案中提到)对转换非常有效,但现在我需要一些更健壮的东西。

    一个看似成熟但仍然活跃的选择是: https://github.com/hudl/HudlFfmpeg 你可以在这里了解更多: http://public.hudl.com/bits/archives/2014/08/15/announcing-hudlffmpeg-a-c-framework-to-make-ffmpeg-interaction-simple/

    另一种可能不适合很多情况的选择是直接从C代码调用EXE: http://www.codeproject.com/Articles/774093/Another-FFmpeg-exe-Csharp-Wrapper

        9
  •  2
  •   Tomasz Å»muda    6 年前

    您可以使用这个nuget包:

    我知道你问过 成熟 项目,但我没有看到任何项目完全满足我的预期,所以我决定自己做。 您可以轻松地对转换进行排队并并行运行转换,将媒体转换为不同格式的方法,将您自己的参数发送到ffmpeg,并以当前进度分析ffmpeg+事件侦听器的输出。

    Install-Package Xabe.FFmpeg

    我正在努力使易于使用,跨平台的ffmpeg包装。

    你可以在 https://xabe.net/product/xabe_ffmpeg/

    更多信息请点击此处: https://xabe.net/product/xabe_ffmpeg/#documentation

    转换很简单:

    IConversionResult result = await Conversion.ToMp4(Resources.MkvWithAudio, output).Start();
    

    如果你想要进步:

    IConversion conversion = Conversion.ToMp4(Resources.MkvWithAudio, output);
    conversion.OnProgress += (duration, length) => { currentProgress = duration; } 
    await conversion.Start();
    
        10
  •  1
  •   Mrinal    10 年前
            string result = String.Empty;
            StreamReader srOutput = null;
            var oInfo = new ProcessStartInfo(exePath, parameters)
            {
                UseShellExecute = false,
                CreateNoWindow = true,
                RedirectStandardOutput = true,
                RedirectStandardError = true
            };
    
            var output = string.Empty;
    
            try
            {
                Process process = System.Diagnostics.Process.Start(oInfo);
                output = process.StandardError.ReadToEnd();
                process.WaitForExit();
                process.Close();
            }
            catch (Exception)
            {
                output = string.Empty;
            }
            return output;
    

    此包装器不会让方法落入循环中。 试试这个,对我有用。

        11
  •  1
  •   SpoiledTechie.com    9 年前

    我从codeplex分叉ffpmeg.net。

    仍在积极努力。

    https://github.com/spoiledtechie/FFMpeg.Net

    它不使用dll,而是使用exe。所以它更稳定。

        12
  •  0
  •   superware    9 年前

    Auto Generated FFmpeg wrapper for C#/.NET and Mono ,这是一个非常棒的项目,似乎是ffmpeg互操作的唯一真正、完整的.net包装。