Solido wrapper FFmpeg per C # /. NET

Ho cercato sul Web per un po ‘di tempo per un solido wrapper FFmpeg per C # /. NET . Ma devo ancora trovare qualcosa di utile. Ho trovato i seguenti tre progetti, ma tutti sembrano essere morti all’inizio della fase alpha.

FFmpeg.NET
ffmpeg-sharp
FFLIB.NET

Quindi la mia domanda è se qualcuno conosce un progetto di wrapper più maturo?
Non sto cercando un motore di transcodifica completo con code di lavoro e altro ancora. Solo un semplice wrapper, quindi non devo effettuare una chiamata dalla riga di comando e quindi analizzare l’output della console, ma posso effettuare chiamate ai metodi e utilizzare eventlisteners per il progresso.

E per favore sentitevi liberi di menzionare eventuali progetti attivi, anche se sono in una fase iniziale.

Questo è un wrapper tutto mio: https://github.com/AydinAdn/MediaToolkit

MediaToolkit può:

  • Converti file video in vari altri formati video.
  • Eseguire attività di transcodifica video.
    • Opzioni configurabili: Bit rate , Frame rate , Resolution / size , Aspect ratio , Duration of video
  • Esegui attività di transcodifica audio.
    • Opzioni configurabili: Audio sample rate
  • Converti video in formati fisici usando gli standard TV FILM, PAL o NTSC
    • I medi comprendono: DVD , DV , DV50 , VCD , SVCD

Lo aggiorno mentre procedo, e sei libero di usarlo, puoi anche installarlo usando la Console di Gestione pacchetti.

 PM> Install-Package MediaToolkit 

Prova questo, penso che avrei potuto scrivere qualcosa che puoi usare per un semplice wrapper.

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

Ho usato FFmpeg da un’applicazione ASP.NET / Windows (.NET). Ma ho finito per usare la riga di comando, senza analizzare la console. Usando questo – ho avuto un modo semplice per controllare – aggiornamenti di FFmpeg ed eseguire più conversioni su più core.

Dopo aver provato diversi wrapper, sono andato con questo: FFmpeg generato automaticamente binding non sicuri per C # /. NET e Mono .

È un insieme di collegamenti di interoperabilità di basso livello per ogni class nello spazio dei nomi FFmpeg. Forse non è conveniente da usare come un vero wrapper, ma IMO è la soluzione migliore per lavorare con FFmpeg in .Net, se vuoi fare cose non banali.

Professionisti:

  • Lavori
  • Fidato – nessun codice di wrapper di terze parti per introdurre bug, supponendo che tu abbia fiducia in FFMpeg stesso.
  • È sempre aggiornato all’ultima versione di FFmpeg
  • Pacchetto singolo nuget per tutti i binding
  • La documentazione XML è inclusa ma è comunque ansible utilizzare la documentazione FFmpeg della documentazione online.

Contro:

  • Livello basso: devi sapere come lavorare con i puntatori alle strutture di c .
  • Richiede inizialmente del lavoro per farlo funzionare. Suggerisco di imparare dagli esempi ufficiali .

Nota: questo thread riguarda l’utilizzo dell’API FFmpeg, ma per alcuni casi d’uso, è meglio utilizzare semplicemente l’interfaccia della riga di comando di ffmpeg.exe.

Sto giocando con una libreria di wrapper ffmpeg chiamata MediaHandler Pro da

http://www.mediasoftpro.com

sembra promettente finora.

Puoi usare questo pacchetto di nuget:

So che hai chiesto del progetto maturo , ma non ho visto nessun progetto che riempie completamente la mia aspettativa, quindi ho deciso di crearne uno mio. Puoi facilmente accodare le conversioni ed eseguirle in parallelo, i metodi per convertire i media in diversi formati, inviare i tuoi argomenti a ffmpeg e analizzare l’output dal listener di eventi ffmpeg + con il progresso corrente.

Install-Package Xabe.FFmpeg

Sto cercando di creare un wrapper FFmpeg multipiattaforma facile da usare.

Puoi trovare maggiori informazioni al riguardo su https://xabe.net/product/xabe_ffmpeg/

Maggiori informazioni qui: https://xabe.net/product/xabe_ffmpeg/#documentation

La conversione è semplice:

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

Se vuoi progressi:

 IConversion conversion = Conversion.ToMp4(Resources.MkvWithAudio, output); conversion.OnProgress += (duration, length) => { currentProgress = duration; } await conversion.Start(); 

Ho fatto ricerche sulla stessa cosa e ho usato originariamente MediaToolKit (menzionato in un’altra risposta) che funzionava benissimo per le conversioni, ma ora ho bisogno di qualcosa di un po ‘più robusto.

Un’opzione che sembra matura e ancora triggers è: https://github.com/hudl/HudlFfmpeg di cui puoi leggere ulteriori informazioni qui: http://public.hudl.com/bits/archives/2014/08/15/announcing -hudlffmpeg-ac-quadro-a-fare-ffmpeg-interaction-semplice /

Un’altra opzione, che potrebbe non essere adatta a molti casi, è quella di richiamare l’exe direttamente dal tuo codice c #: http://www.codeproject.com/Articles/774093/Another-FFmpeg-exe-Csharp-Wrapper

Ecco qua … La maggior parte di questo codice ha 2 o più anni, quindi manca un sacco di cose asincrone e usa una convenzione di denominazione obsoleta. In esecuzione in un ambiente di produzione per un bel po ‘di tempo ~ 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; ///  /// Encodes an MP4 to the specs provided, quality is a value from 0 to 1 ///  /// A value from 0 to 1 /// 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 _colOutputLines; protected List _colErrorLines; private int _iExitCode; public ConsoleCommandBase() { _colOutputLines = new List(); _colErrorLines = new List(); } 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 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(); } 

// per il processo runner roba cerca ProcessRunner di Roger Knapp

  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; 

Questo wrapper non lascerà il metodo cadere in un ciclo. Prova questo, ha funzionato per me.

Ho biforcato FFPMEG.net da codeplex.

Ancora triggersmente lavorando.

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

Non usa le DLL, ma piuttosto l’exe. Quindi tende ad essere più stabile.

Guarda il wrapper FFmpeg generato automaticamente per C # /. NET e Mono , un fantastico progetto che sembra essere l’unico vero wrapper .NET completo per FFmpeg.