/// <summary> /// Gets file streams information of specified file via FFmpeg. /// </summary> /// <param name="source">The file to get information about.</param> /// <param name="options">The options for starting the process.</param> /// <returns>A FFmpegProcess object containing the file information.</returns> public static FFmpegProcess GetFileInfo(string source, ProcessStartOptions options) { FFmpegProcess Worker = new FFmpeg.FFmpegProcess(options); Worker.RunFFmpeg(string.Format(@"-i ""{0}""", source)); return(Worker); }
/// <summary> /// Returns the version information from FFmpeg. /// </summary> /// <param name="options">The options for starting the process.</param> /// <returns>A FFmpegProcess object containing the version information.</returns> public static FFmpegProcess GetVersion(ProcessStartOptions options) { FFmpegProcess Worker = new FFmpeg.FFmpegProcess(options); Worker.RunFFmpeg("-version", ProcessOutput.Standard); return(Worker); }
/// <summary> /// Returns the exact frame count of specified video file. /// </summary> /// <param name="source">The file to get information about.</param> /// <param name="options">The options for starting the process.</param> /// <returns>The number of frames in the video.</returns> public static long GetFrameCount(string source, ProcessStartOptions options) { long Result = 0; FFmpegProcess Worker = new FFmpeg.FFmpegProcess(options); Worker.StatusUpdated += (sender, e) => { Result = e.Status.Frame; }; Worker.RunFFmpeg(string.Format(@"-i ""{0}"" -f null /dev/null", source)); return(Result); }
/// <summary> /// Encodes a media file with specified arguments. /// </summary> /// <param name="source">The file to convert.</param> /// <param name="videoCodec">The codec(s) to use to encode the video stream(s).</param> /// <param name="audioCodec">The codec(s) to use to encode the audio stream(s).</param> /// <param name="encodeArgs">Additional arguments to pass to FFmpeg.</param> /// <param name="destination">The destination file.</param> /// <param name="options">The options for starting the process.</param> /// <returns>The process completion status.</returns> public static CompletionStatus Encode(string source, string[] videoCodec, string[] audioCodec, string encodeArgs, string destination, ProcessStartOptions options) { File.Delete(destination); StringBuilder Query = new StringBuilder(); Query.Append("-y -i "); // AviSynth source will pipe through Avs2Yuv into FFmpeg. bool SourceAvisynth = source.ToLower().EndsWith(".avs"); if (SourceAvisynth) { Query.Append("-"); // Pipe source } else { Query.Append("\""); Query.Append(source); Query.Append("\""); } // Add video codec. if (videoCodec == null || videoCodec.Length == 0) { Query.Append(" -vn"); } else if (videoCodec.Length == 1) { Query.Append(" -vcodec "); Query.Append(videoCodec[0]); } else { for (int i = 0; i < videoCodec.Length; i++) { Query.Append(" -vcodec:"); Query.Append(i); Query.Append(" "); Query.Append(videoCodec[i]); } } // Add audio codec. if (audioCodec == null || audioCodec.Length == 0) { Query.Append(" -an"); } else if (audioCodec.Length == 1) { Query.Append(" -acodec "); Query.Append(audioCodec[0]); } else { for (int i = 0; i < audioCodec.Length; i++) { Query.Append(" -acodec:"); Query.Append(i); Query.Append(" "); Query.Append(audioCodec[i]); } } if (!string.IsNullOrEmpty(encodeArgs)) { Query.Append(" "); Query.Append(encodeArgs); } Query.Append(" \""); Query.Append(destination); Query.Append("\""); // Run FFmpeg with query. FFmpegProcess Worker = new FFmpeg.FFmpegProcess(options); CompletionStatus Result = SourceAvisynth ? Worker.RunAvisynthToFFmpeg(source, Query.ToString()) : Worker.RunFFmpeg(Query.ToString()); return(Result); }
/// <summary> /// Encodes a media file with specified arguments. /// </summary> /// <param name="source">The file to convert.</param> /// <param name="videoCodec">The codec to use to encode the video stream.</param> /// <param name="audioCodec">The codec to use to encode the audio stream.</param> /// <param name="encodeArgs">Additional arguments to pass to FFmpeg.</param> /// <param name="destination">The destination file.</param> /// <param name="options">The options for starting the process.</param> /// <returns>The process completion status.</returns> public static CompletionStatus Encode(string source, string videoCodec, string audioCodec, string encodeArgs, string destination, ProcessStartOptions options) { string[] VideoCodecList = string.IsNullOrEmpty(videoCodec) ? null : new string[] { videoCodec }; string[] AudioCodecList = string.IsNullOrEmpty(audioCodec) ? null : new string[] { audioCodec }; return(Encode(source, VideoCodecList, AudioCodecList, encodeArgs, destination, options)); }
/// <summary> /// Converts specified file into AVI UT Video format. /// </summary> /// <param name="source">The file to convert.</param> /// <param name="destination">The destination file, ending with .AVI</param> /// <param name="options">The options for starting the process.</param> /// <returns>The process completion status.</returns> public static CompletionStatus ConvertToAvi(string source, string destination, ProcessStartOptions options) { // -vcodec huffyuv or utvideo, -acodec pcm_s16le return(Encode(source, "utvideo", "pcm_s16le", null, destination, options)); }
/// <summary> /// Merges the specified list of file streams. /// </summary> /// <param name="fileStreams">The list of file streams to include in the output.</param> /// <param name="destination">The destination file.</param> /// <param name="options">The options for starting the process.</param> /// <returns>The process completion status.</returns> public static CompletionStatus Muxe(IEnumerable <FFmpegStream> fileStreams, string destination, ProcessStartOptions options) { CompletionStatus Result = CompletionStatus.Success; List <string> TempFiles = new List <string>(); File.Delete(destination); // FFMPEG fails to muxe H264 into MKV container. Converting to MP4 and then muxing with the audio, however, works. foreach (FFmpegStream item in fileStreams) { if (item.Type == FFmpegStreamType.Video && (item.Path.EndsWith(".264") || item.Path.EndsWith(".265")) && destination.EndsWith(".mkv")) { string NewFile = item.Path.Substring(0, item.Path.Length - 4) + ".mp4"; Result = Muxe(item.Path, null, NewFile, options); TempFiles.Add(NewFile); if (Result != CompletionStatus.Success) { break; } } } if (Result == CompletionStatus.Success) { // Join audio and video files. StringBuilder Query = new StringBuilder(); StringBuilder Map = new StringBuilder(); Query.Append("-y "); int StreamIndex = 0; bool HasVideo = false, HasAudio = false, HasPcmDvdAudio = false; foreach (FFmpegStream item in fileStreams.OrderBy(f => f.Type)) { if (item.Type == FFmpegStreamType.Video) { HasVideo = true; } if (item.Type == FFmpegStreamType.Audio) { HasAudio = true; if (item.Format == "pcm_dvd") { HasPcmDvdAudio = true; } } Query.Append("-i \""); Query.Append(item.Path); Query.Append("\" "); Map.Append("-map "); Map.Append(StreamIndex++); Map.Append(":"); Map.Append(item.Index); Map.Append(" "); } if (HasVideo) { Query.Append("-vcodec copy "); } if (HasAudio) { Query.Append(HasPcmDvdAudio ? "-acodec pcm_s16le " : "-acodec copy "); } Query.Append(Map); // FFMPEG-encoded AAC streams are invalid and require an extra flag to join. if (fileStreams.Any(f => f.Path.ToLower().EndsWith(".aac"))) { Query.Append(" -bsf:a aac_adtstoasc"); } Query.Append("\""); Query.Append(destination); Query.Append("\""); FFmpegProcess Worker = new FFmpegProcess(options); Worker.RunFFmpeg(Query.ToString()); } // Delete temp file. foreach (string item in TempFiles) { File.Delete(item); } return(Result); }
/// <summary> /// Merges specified audio and video files. /// </summary> /// <param name="videoFile">The file containing the video.</param> /// <param name="audioFile">The file containing the audio.</param> /// <param name="destination">The destination file.</param> /// <param name="options">The options for starting the process.</param> /// <returns>The process completion status.</returns> public static CompletionStatus Muxe(string videoFile, string audioFile, string destination, ProcessStartOptions options) { CompletionStatus Result = CompletionStatus.Success; File.Delete(destination); // FFMPEG fails to muxe H264 into MKV container. Converting to MP4 and then muxing with the audio, however, works. string OriginalVideoFile = videoFile; if ((videoFile.EndsWith(".264") || videoFile.EndsWith(".265")) && destination.ToLower().EndsWith(".mkv")) { videoFile = videoFile.Substring(0, videoFile.Length - 4) + ".mp4"; Result = Muxe(OriginalVideoFile, null, videoFile, options); } if (Result == CompletionStatus.Success) { // Join audio and video files. FFmpegProcess Worker = new FFmpegProcess(options); // FFMPEG-encoded AAC streams are invalid and require an extra flag to join. bool FixAac = audioFile != null?audioFile.ToLower().EndsWith(".aac") : false; string Query; if (string.IsNullOrEmpty(audioFile)) { Query = string.Format(@"-y -i ""{0}"" -vcodec copy -an ""{1}""", videoFile, destination); } else if (string.IsNullOrEmpty(videoFile)) { Query = string.Format(@"-y -i ""{0}"" -acodec copy -vn ""{1}""", videoFile, destination); } else { Query = string.Format(@"-y -i ""{0}"" -i ""{1}"" -acodec copy -vcodec copy -map 0:v -map 1:a{2} ""{3}""", videoFile, audioFile, FixAac ? " -bsf:a aac_adtstoasc" : "", destination); } Result = Worker.RunFFmpeg(Query); } // Delete temp file. if (OriginalVideoFile != videoFile) { File.Delete(videoFile); } return(Result); }
/// <summary> /// Extracts the audio stream from specified file. /// </summary> /// <param name="source">The media file to extract from.</param> /// <param name="destination">The destination file.</param> /// <param name="options">The options for starting the process.</param> /// <returns>The process completion status.</returns> public static CompletionStatus ExtractAudio(string source, string destination, ProcessStartOptions options) { File.Delete(destination); FFmpegProcess Worker = new FFmpeg.FFmpegProcess(options); return(Worker.RunFFmpeg(string.Format(@"-y -i ""{0}"" -vn -acodec copy ""{1}""", source, destination))); }
/// <summary> /// Concatenates (merges) all specified files. /// </summary> /// <param name="files">The files to merge.</param> /// <param name="destination">The destination file.</param> /// <param name="options">The options for starting the process.</param> /// <returns>The process completion status.</returns> public static CompletionStatus Concatenate(IEnumerable <string> files, string destination, ProcessStartOptions options) { CompletionStatus Result = CompletionStatus.None; // Write temp file. string TempFile = Path.Combine(Path.GetDirectoryName(destination), "MergeList.txt"); StringBuilder TempContent = new StringBuilder(); foreach (string item in files) { TempContent.AppendFormat("file '{0}'", item).AppendLine(); } File.WriteAllText(TempFile, TempContent.ToString()); string Query = string.Format(@"-y -f concat -fflags +genpts -async 1 -safe 0 -i ""{0}"" -c copy ""{1}""", TempFile, destination); FFmpegProcess Worker = new FFmpegProcess(options); Result = Worker.RunFFmpeg(Query.ToString()); File.Delete(TempFile); return(Result); }
/// <summary> /// Initializes a new instances of the FFmpegProcess class with specified options. /// </summary> /// <param name="options">The options for starting the process.</param> public FFmpegProcess(ProcessStartOptions options) { Options = options ?? new ProcessStartOptions(); }
/// <summary> /// Runs specified application with specified arguments. /// </summary> /// <param name="fileName">The application to start.</param> /// <param name="arguments">The set of arguments to use when starting the application.</param> /// <param name="isFFmpeg">Whether to parse the output as FFmpeg.</param> /// <param name="nestedProcess">If true, killing the process with kill all sub-processes.</param> /// <returns>The process completion status.</returns> public CompletionStatus Run(string fileName, string arguments, bool isFFmpeg, bool nestedProcess) { if (!File.Exists(FFmpegConfig.FFmpegPath)) { throw new FileNotFoundException(string.Format(@"File ""{0}"" specified by FFmpegConfig.FFmpegPath is not found.", FFmpegConfig.FFmpegPath)); } if (WorkProcess != null) { throw new InvalidOperationException("This instance of FFmpeg is busy. You can run concurrent commands by creating other class instances."); } Process P = new Process(); this.isFFmpeg = isFFmpeg; WorkProcess = P; output = new StringBuilder(); isStarted = false; FileStreams = null; FrameCount = 0; FileDuration = TimeSpan.Zero; cancelWork = new CancellationTokenSource(); if (Options == null) { Options = new ProcessStartOptions(); } P.StartInfo.FileName = fileName; P.StartInfo.Arguments = arguments; P.OutputDataReceived += FFmpeg_DataReceived; P.ErrorDataReceived += FFmpeg_DataReceived; if (Options.DisplayMode != FFmpegDisplayMode.Native) { if (Options.DisplayMode == FFmpegDisplayMode.Interface && FFmpegConfig.UserInterfaceManager != null) { FFmpegConfig.UserInterfaceManager.Display(this); } P.StartInfo.CreateNoWindow = true; P.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; P.StartInfo.RedirectStandardError = true; P.StartInfo.UseShellExecute = false; } Options.RaiseStarted(this); P.Start(); try { if (!P.HasExited) { P.PriorityClass = Options.Priority; } } catch { } P.BeginErrorReadLine(); bool Timeout = Wait(); // ExitCode is 0 for normal exit. Different value when closing the console. CompletionStatus Result = Timeout ? CompletionStatus.Timeout : cancelWork.IsCancellationRequested ? CompletionStatus.Cancelled : P.ExitCode == 0 ? CompletionStatus.Success : CompletionStatus.Error; isStarted = false; cancelWork = null; LastCompletionStatus = Result; Completed?.Invoke(this, new CompletedEventArgs(Result)); if ((Result == CompletionStatus.Error || Result == CompletionStatus.Timeout) && Options.DisplayMode == FFmpegDisplayMode.ErrorOnly) { FFmpegConfig.UserInterfaceManager?.DisplayError(this); } return(Result); }