用于C#/ .NET的固体FFmpeg包装器
我一直在网上search一段时间为C#/。NET一个坚实的FFmpeg包装。 但是我还没有想出一些有用的东西。 我发现了以下三个项目,但是所有这些项目都在早期的alpha阶段就已经死了。
FFmpeg.NET
ffmpeg的锐
FFLIB.NET
所以我的问题是,如果有人知道一个更成熟的包装项目?
我不是在寻找一个完整的代码转换引擎和更多的工作。 只是一个简单的包装,所以我不必进行命令行调用,然后parsing控制台输出,但可以进行方法调用,并使用eventlisteners进度。
请随时提及任何活跃的项目,即使它们在初期阶段仍然存在。
这是我自己的包装: https : //github.com/AydinAdn/MediaToolkit
MediaToolkit可以:
- 将video文件转换成各种其他video格式。
- 执行video转码任务。
- 选项可configuration:
Bit rate
,Frame rate
,Resolution / size
,Aspect ratio
,Duration of video
- 选项可configuration:
- 执行audio转码任务。
- 选项可configuration:
Audio sample rate
- 选项可configuration:
- 使用FILM,PAL或NTSC电视标准将video转换为物理格式
- 介质包括:
DVD
,DV
,DV50
,VCD
,SVCD
- 介质包括:
我正在更新它,并欢迎您使用它,您还可以使用软件包pipe理器控制台进行安装。
PM> Install-Package MediaToolkit
试试这个,我想我可能已经写了一些你可以用来做一个简单的包装的东西。
http://jasonjano.wordpress.com/2010/02/09/a-simple-c-wrapper-for-ffmpeg/
我从ASP.NET / Windows服务(.NET)应用程序使用FFmpeg。 但是我结束了使用命令行,而不parsing控制台。 通过使用这个 – 我有一个简单的方法来控制 – 更新FFmpeg并在多个核心上运行多个转换。
尝试了几个包装,我去了这个: 自动生成的FFmpeg包装C#/。NET和单声道 。
它是FFmpeg命名空间中每个类的一组低级别的互操作绑定。 也许不如使用真正的包装器那样方便,但是如果你想要做一些不重要的事情,那么IMO就是在.Net中使用FFmpeg的最佳解决scheme。
优点:
- 作品
- 值得信赖的 – 假设你信任FFMpeg本身,没有第三方包装代码来引入错误。
- 它总是更新到最新版本的FFmpeg
- 单个文件的所有绑定
缺点:
- 低级别:您必须知道如何使用指向c结构的指针。
- 最初需要一些工作才能使其工作。 我build议从官方的例子中学习。
- 没有评论…。 请参阅在线FFmpeg文档 。
注意:这个线程是关于使用FFmpeg API的,但是对于一些用例,最好只使用ffmpeg.exe的命令行界面 。
这里还有一个简单的例子 : http : //ivolo.mit.edu/post/Metamorph-Convert-Audio-Video-to-Any-Format-on-Windows-Linux-and-Mac.aspx
我一直在研究同样的事情,最初使用MediaToolKit(在另一个答案中提到),这对转换很有用,但是现在我需要一些更强大的function。
一个看似成熟并且仍然活跃的选项是: https : //github.com/hudl/HudlFfmpeg你可以在这里阅读更多的内容: http : //public.hudl.com/bits/archives/2014/08/15/announcing -hudlffmpeg-AC-框架对做-ffmpeg的交互,简单/
另一个选项,可能不适合许多情况下,直接从您的C#代码调用exe文件: http : //www.codeproject.com/Articles/774093/Another-FFmpeg-exe-Csharp-Wrapper
这里雅去…这个代码大部分是2岁以上,所以缺less了大量的asynchronous的东西,并使用过时的命名约定。 JT在生产环境中运行了一段时间〜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(); }
//对于stream程运行者来说,查找由Roger Knapp提供的ProcessRunner
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;
这个包装不会让方法陷入循环。 试试这个,它为我工作。
我从codeplex分叉FFPMEG.net。
仍在积极努力。
https://github.com/spoiledtechie/FFMpeg.Net
它不使用dll,而是使用exe。 所以它往往更稳定。
你可以使用这个nuget包:
我知道你问过成熟的项目,但是我还没有看到任何项目满员我的期望,所以我决定做我自己的。 您可以轻松排列转换并行运行,将媒体转换为不同格式的方法,将自己的参数发送到ffmpeg,并使用当前进度parsingffmpeg +事件侦听器的输出。
Install-Package Xabe.FFmpeg
我试图使易于使用,跨平台的FFmpeg包装。
你可以在https://github.com/tomaszzmuda/Xabe.FFmpegfind更多关于这方面的信息;
更多信息在这里: https : //github.com/tomaszzmuda/Xabe.FFmpeg/wiki/Getting-an-information-about-video
转换很简单:
bool result = await ConversionHelper.ToMp4("myVideo.mkv", "output.mp4") .Start()
如果你想要进步:
var conversion = new Conversion() .StreamCopy(Channel.Both) .SetBitstreamFilter(Channel.Audio, Filter.Aac_AdtstoAsc) .SetOutput(outputPath) .Concat(Resources.TsWithAudio.FullName, Resources.TsWithAudio.FullName); conversion.OnProgress += (duration, length) => { currentProgress = duration; };
请参阅C#/ .NET和Mono的自动生成的FFmpeg包装器,这是一个非常棒的项目,它似乎是FFmpeg互操作的唯一真正的,完整的.NET包装器。