/// <summary> /// Converts a source video to TS format. /// </summary> /// <param name="source">Source video file.</param> /// <param name="IntendedOutput">Output video file.</param> /// <returns>Success state.</returns> public bool ToTS(VideoInfo source, string IntendedOutput, out string ActualOutput) { int i = 0; ActualOutput = IntendedOutput; while (File.Exists(ActualOutput) && FFMpegHelper.IsFileLocked(ActualOutput)) { ActualOutput = Path.Combine(Path.GetDirectoryName(IntendedOutput), Path.GetFileNameWithoutExtension(IntendedOutput) + $"({i}){Path.GetExtension(IntendedOutput)}"); i++; } _probe.SetVideoInfo(ref source); _totalVideoTime = source.Duration; IsConverting = true; if (File.Exists(IntendedOutput) && !FFMpegHelper.IsFileLocked(IntendedOutput)) { return(true); } FFMpegHelper.ConversionExceptionCheck(source, IntendedOutput); FFMpegHelper.ExtensionExceptionCheck(IntendedOutput, ".ts"); string conversionArgs = string.Format("-i \"{0}\" -c copy -bsf:v h264_mp4toannexb -f mpegts \"{1}\"", source.Path, IntendedOutput); return(_RunProcess(conversionArgs)); }
/// <summary> /// Builds parameters string from <see cref="ArgumentsContainer"/> that would be passed to ffmpeg process /// </summary> /// <param name="container">Container of arguments</param> /// <param name="input">Input file</param> /// <param name="output">Output file</param> /// <returns>Parameters string</returns> public string BuildArguments(ArgumentsContainer container, FileInfo input, FileInfo output) { CheckContainerException(container); CheckExtensionOfOutputExtension(container, output); FFMpegHelper.ConversionExceptionCheck(input, output); StringBuilder sb = new StringBuilder(); var inputA = new InputArgument(input); var outputA = new OutputArgument(); sb.Append(inputA.GetStringValue().Trim() + " "); foreach (var a in container) { if (!IsInputOrOutput(a.Key)) { sb.Append(a.Value.GetStringValue().Trim() + " "); } } sb.Append(outputA.GetStringValue().Trim()); var overrideArg = container.Find <OverrideArgument>(); if (overrideArg != null) { sb.Append(" " + overrideArg.GetStringValue().Trim()); } return(sb.ToString()); }
/// <summary> /// Convert a video do a different format. /// </summary> /// <param name="source">Input video source.</param> /// <param name="output">Output information.</param> /// <param name="type">Target conversion video type.</param> /// <param name="speed">Conversion target speed/quality (faster speed = lower quality).</param> /// <param name="size">Video size.</param> /// <param name="audioQuality">Conversion target audio quality.</param> /// <param name="multithreaded">Is encoding multithreaded.</param> /// <returns>Output video information.</returns> public VideoInfo Convert( VideoInfo source, FileInfo output, VideoType type = VideoType.Mp4, Speed speed = Speed.SuperFast, VideoSize size = VideoSize.Original, AudioQuality audioQuality = AudioQuality.Normal, bool multithreaded = false) { FFMpegHelper.ConversionExceptionCheck(source.ToFileInfo(), output); FFMpegHelper.ExtensionExceptionCheck(output, FileExtension.ForType(type)); FFMpegHelper.ConversionSizeExceptionCheck(source); string args = ""; var height = source.Height * (source.Width / (int)size); switch (type) { case VideoType.Mp4: args = Arguments.Input(source) + Arguments.Threads(multithreaded) + Arguments.Scale(size, height) + Arguments.Video(VideoCodec.LibX264, 2400) + Arguments.Speed(speed) + Arguments.Audio(AudioCodec.Aac, audioQuality) + Arguments.Output(output); break; case VideoType.Ogv: args = Arguments.Input(source) + Arguments.Threads(multithreaded) + Arguments.Scale(size, height) + Arguments.Video(VideoCodec.LibTheora, 2400) + Arguments.Speed(16) + Arguments.Audio(AudioCodec.LibVorbis, audioQuality) + Arguments.Output(output); break; case VideoType.Ts: args = Arguments.Input(source) + Arguments.Copy() + Arguments.BitStreamFilter(Channel.Video, Filter.H264_Mp4ToAnnexB) + Arguments.ForceFormat(VideoCodec.MpegTs) + Arguments.Output(output); break; } if (!RunProcess(args, output)) { throw new FFMpegException(FFMpegExceptionType.Conversion, $"The video could not be converted to {Enum.GetName(typeof(VideoType), type)}"); } return(new VideoInfo(output)); }
/// <summary> /// Convert a video do a different format. /// </summary> /// <param name="source">Input video source.</param> /// <param name="output">Output information.</param> /// <param name="type">Target conversion video type.</param> /// <param name="speed">Conversion target speed/quality (faster speed = lower quality).</param> /// <param name="size">Video size.</param> /// <param name="audioQuality">Conversion target audio quality.</param> /// <param name="multithreaded">Is encoding multithreaded.</param> /// <returns>Output video information.</returns> public VideoInfo Convert( VideoInfo source, FileInfo output, VideoType type = VideoType.Mp4, Speed speed = Speed.SuperFast, VideoSize size = VideoSize.Original, AudioQuality audioQuality = AudioQuality.Normal, bool multithreaded = false) { FFMpegHelper.ConversionExceptionCheck(source.ToFileInfo(), output); FFMpegHelper.ExtensionExceptionCheck(output, FileExtension.ForType(type)); FFMpegHelper.ConversionSizeExceptionCheck(source); var scale = VideoSize.Original == size ? 1 : (double)source.Height / (int)size; var outputSize = new Size((int)(source.Width / scale), (int)(source.Height / scale)); if (outputSize.Width % 2 != 0) { outputSize.Width += 1; } return(type switch { VideoType.Mp4 => Convert(new ArgumentContainer( new InputArgument(source), new ThreadsArgument(multithreaded), new ScaleArgument(outputSize), new VideoCodecArgument(VideoCodec.LibX264, 2400), new SpeedArgument(speed), new AudioCodecArgument(AudioCodec.Aac, audioQuality), new OutputArgument(output))), VideoType.Ogv => Convert(new ArgumentContainer( new InputArgument(source), new ThreadsArgument(multithreaded), new ScaleArgument(outputSize), new VideoCodecArgument(VideoCodec.LibTheora, 2400), new SpeedArgument(speed), new AudioCodecArgument(AudioCodec.LibVorbis, audioQuality), new OutputArgument(output))), VideoType.Ts => Convert(new ArgumentContainer( new InputArgument(source), new CopyArgument(), new BitStreamFilterArgument(Channel.Video, Filter.H264_Mp4ToAnnexB), new ForceFormatArgument(VideoCodec.MpegTs), new OutputArgument(output))), VideoType.WebM => Convert(new ArgumentContainer( new InputArgument(source), new ThreadsArgument(multithreaded), new ScaleArgument(outputSize), new VideoCodecArgument(VideoCodec.LibVpx, 2400), new SpeedArgument(speed), new AudioCodecArgument(AudioCodec.LibVorbis, audioQuality), new OutputArgument(output))), _ => throw new ArgumentOutOfRangeException(nameof(type)) });
/// <summary> /// Saves audio from a specific video file to disk. /// </summary> /// <param name="source">Source video file.</param> /// <param name="output">Output audio file.</param> /// <returns>Success state.</returns> public bool SaveAudio(VideoInfo source, string output) { _probe.SetVideoInfo(ref source); FFMpegHelper.ConversionExceptionCheck(source, output); FFMpegHelper.ExtensionExceptionCheck(output, ".mp3"); string args = string.Format("-i \"{0}\" -vn -ab 256 \"{1}\"", source.Path, output); return(_RunProcess(args)); }
/// <summary> /// Strips a video file of audio. /// </summary> /// <param name="source">Source video file.</param> /// <param name="output">Output video file.</param> /// <returns></returns> public bool Mute(VideoInfo source, string output) { _probe.SetVideoInfo(ref source); FFMpegHelper.ConversionExceptionCheck(source, output); FFMpegHelper.ExtensionExceptionCheck(output, source.Extension); string args = string.Format("-i \"{0}\" -c copy -an \"{1}\"", source.Path, output); return(_RunProcess(args)); }
/// <summary> /// Adds audio to a video file. /// </summary> /// <param name="source">Source video file.</param> /// <param name="audio">Source audio file.</param> /// <param name="output">Output video file.</param> /// <param name="stopAtShortest">Indicates if the encoding should stop at the shortest input file.</param> /// <returns>Success state</returns> public bool AddAudio(VideoInfo source, string audio, string output, bool stopAtShortest = false) { _probe.SetVideoInfo(ref source); FFMpegHelper.ConversionExceptionCheck(source, output); FFMpegHelper.InputFilesExistExceptionCheck(audio); FFMpegHelper.ExtensionExceptionCheck(output, source.Extension); string args = string.Format("-i \"{0}\" -i \"{1}\" -c:v copy -c:a aac -strict experimental {3} \"{2}\"", source.Path, audio, output, stopAtShortest ? "-shortest" : ""); return(_RunProcess(args)); }
/// <summary> /// Saves audio from a specific video file to disk. /// </summary> /// <param name="source">Source video file.</param> /// <param name="output">Output audio file.</param> /// <returns>Success state.</returns> public FileInfo ExtractAudio(VideoInfo source, FileInfo output) { FFMpegHelper.ConversionExceptionCheck(source.ToFileInfo(), output); FFMpegHelper.ExtensionExceptionCheck(output, FileExtension.Mp3); var args = Arguments.Input(source) + Arguments.Disable(Channel.Video) + Arguments.Output(output); if (!RunProcess(args, output)) { throw new FFMpegException(FFMpegExceptionType.Operation, "Could not extract the audio from the requested video."); } output.Refresh(); return(output); }
/// <summary> /// Strips a video file of audio. /// </summary> /// <param name="source">Source video file.</param> /// <param name="output">Output video file.</param> /// <returns></returns> public VideoInfo Mute(VideoInfo source, FileInfo output) { FFMpegHelper.ConversionExceptionCheck(source.ToFileInfo(), output); FFMpegHelper.ConversionSizeExceptionCheck(source); FFMpegHelper.ExtensionExceptionCheck(output, source.Extension); var args = Arguments.Input(source) + Arguments.Copy() + Arguments.Disable(Channel.Audio) + Arguments.Output(output); if (!RunProcess(args, output)) { throw new FFMpegException(FFMpegExceptionType.Operation, "Could not mute the requested video."); } return(new VideoInfo(output)); }
/// <summary> /// Strips a video file of audio. /// </summary> /// <param name="source">Source video file.</param> /// <param name="output">Output video file.</param> /// <returns></returns> public VideoInfo Mute(VideoInfo source, FileInfo output) { FFMpegHelper.ConversionExceptionCheck(source.ToFileInfo(), output); FFMpegHelper.ConversionSizeExceptionCheck(source); FFMpegHelper.ExtensionExceptionCheck(output, source.Extension); var container = new ArgumentContainer( new InputArgument(source), new CopyArgument(), new DisableChannelArgument(Channel.Audio), new OutputArgument(output) ); if (!RunProcess(container, output)) { throw new FFMpegException(FFMpegExceptionType.Operation, "Could not mute the requested video."); } return(new VideoInfo(output)); }
/// <summary> /// Saves audio from a specific video file to disk. /// </summary> /// <param name="source">Source video file.</param> /// <param name="output">Output audio file.</param> /// <returns>Success state.</returns> public FileInfo ExtractAudio(VideoInfo source, FileInfo output) { FFMpegHelper.ConversionExceptionCheck(source.ToFileInfo(), output); FFMpegHelper.ExtensionExceptionCheck(output, FileExtension.Mp3); var container = new ArgumentContainer( new InputArgument(source), new DisableChannelArgument(Channel.Video), new OutputArgument(output) ); if (!RunProcess(container, output)) { throw new FFMpegException(FFMpegExceptionType.Operation, "Could not extract the audio from the requested video."); } output.Refresh(); return(output); }
/// <summary> /// Converts a source video to WebM format. /// </summary> /// <param name="source">Source video file.</param> /// <param name="output">Output video file.</param> /// <param name="size">Output video size.</param> /// <param name="multithread">Use multithreading for conversion.</param> /// <returns>Success state.</returns> public bool ToWebM(VideoInfo source, string output, VideoSize size = VideoSize.Original, bool multithread = false) { _probe.SetVideoInfo(ref source); _totalVideoTime = source.Duration; IsConverting = true; string threadCount = multithread ? Environment.ProcessorCount.ToString() : "1", scale = FFMpegHelper.GetScale(size); FFMpegHelper.ConversionExceptionCheck(source, output); FFMpegHelper.ExtensionExceptionCheck(output, ".webm"); string conversionArgs = string.Format("-i \"{0}\" -threads {1} {2} -vcodec libvpx -quality good -cpu-used 0 -b:v 1500k -qmin 10 -qmax 42 -maxrate 500k -bufsize 1000k -codec:a libvorbis -b:a 128k \"{3}\"", source.Path, threadCount, scale, output); return(_RunProcess(conversionArgs)); }
/// <summary> /// Converts a source video to OGV format. /// </summary> /// <param name="source">Source video file.</param> /// <param name="output">Output video file.</param> /// <param name="size">Output video size.</param> /// <param name="multithread">Use multithreading for conversion.</param> /// <returns>Success state.</returns> public bool ToOGV(VideoInfo source, string output, VideoSize size = VideoSize.Original, bool multithread = false) { _probe.SetVideoInfo(ref source); _totalVideoTime = source.Duration; IsConverting = true; string threadCount = multithread ? Environment.ProcessorCount.ToString() : "1", scale = FFMpegHelper.GetScale(size); FFMpegHelper.ConversionExceptionCheck(source, output); FFMpegHelper.ExtensionExceptionCheck(output, ".ogv"); string conversionArgs = string.Format("-i \"{0}\" -threads {1} {2} -codec:v libtheora -qscale:v 7 -codec:a libvorbis -qscale:a 5 \"{3}\"", source.Path, threadCount, scale, output); return(_RunProcess(conversionArgs)); }
/// <summary> /// Adds audio to a video file. /// </summary> /// <param name="source">Source video file.</param> /// <param name="audio">Source audio file.</param> /// <param name="output">Output video file.</param> /// <param name="stopAtShortest">Indicates if the encoding should stop at the shortest input file.</param> /// <returns>Success state</returns> public VideoInfo ReplaceAudio(VideoInfo source, FileInfo audio, FileInfo output, bool stopAtShortest = false) { FFMpegHelper.ConversionExceptionCheck(source.ToFileInfo(), output); FFMpegHelper.InputsExistExceptionCheck(audio); FFMpegHelper.ConversionSizeExceptionCheck(source); FFMpegHelper.ExtensionExceptionCheck(output, source.Extension); var args = Arguments.Input(source) + Arguments.Input(audio) + Arguments.Copy(Channel.Video) + Arguments.Audio(AudioCodec.Aac, AudioQuality.Hd) + Arguments.FinalizeAtShortestInput(stopAtShortest) + Arguments.Output(output); if (!RunProcess(args, output)) { throw new FFMpegException(FFMpegExceptionType.Operation, "Could not replace the video audio."); } return(new VideoInfo(output)); }
/// <summary> /// Converts a source video to MP4 format. /// </summary> /// <param name="source">Source video file.</param> /// <param name="output">Output video file.</param> /// <param name="speed">Conversion speed preset.</param> /// <param name="size">Output video size.</param> /// <param name="multithread">Use multithreading for conversion.</param> /// <returns>Success state.</returns> public bool ToMP4(VideoInfo source, string output, Speed speed = Speed.SuperFast, VideoSize size = VideoSize.Original, bool multithread = false) { _probe.SetVideoInfo(ref source); _totalVideoTime = source.Duration; IsConverting = true; string threadCount = multithread ? Environment.ProcessorCount.ToString() : "1", scale = FFMpegHelper.GetScale(size); FFMpegHelper.ConversionExceptionCheck(source, output); FFMpegHelper.ExtensionExceptionCheck(output, ".mp4"); string conversionArgs = string.Format("-i \"{0}\" -threads {1} {2} -b:v 2000k -vcodec libx264 -preset {3} -g 30 \"{4}\"", source.Path, threadCount, scale, speed.ToString().ToLower(), output); return(_RunProcess(conversionArgs)); }
/// <summary> /// Adds audio to a video file. /// </summary> /// <param name="source">Source video file.</param> /// <param name="audio">Source audio file.</param> /// <param name="output">Output video file.</param> /// <param name="stopAtShortest">Indicates if the encoding should stop at the shortest input file.</param> /// <returns>Success state</returns> public VideoInfo ReplaceAudio(VideoInfo source, FileInfo audio, FileInfo output, bool stopAtShortest = false) { FFMpegHelper.ConversionExceptionCheck(source.ToFileInfo(), output); FFMpegHelper.InputsExistExceptionCheck(audio); FFMpegHelper.ConversionSizeExceptionCheck(source); FFMpegHelper.ExtensionExceptionCheck(output, source.Extension); var container = new ArgumentContainer( new InputArgument(source.FullName, audio.FullName), new CopyArgument(), new AudioCodecArgument(AudioCodec.Aac, AudioQuality.Hd), new ShortestArgument(stopAtShortest), new OutputArgument(output) ); if (!RunProcess(container, output)) { throw new FFMpegException(FFMpegExceptionType.Operation, "Could not replace the video audio."); } return(new VideoInfo(output)); }
/// <summary> /// Saves a 'png' thumbnail from the input video. /// </summary> /// <param name="source">Source video file.</param> /// <param name="output">Output video file</param> /// <param name="seek">Seek position where the thumbnail should be taken.</param> /// <param name="thumbWidth">Thumbnail width.</param> /// <param name="thumbHeight">Thumbnail height.</param> /// <returns>Success state.</returns> public bool SaveThumbnail(VideoInfo source, string output, TimeSpan?seek = null, int thumbWidth = 300, int thumbHeight = 169) { _probe.SetVideoInfo(ref source); if (seek == null) { seek = new TimeSpan(0, 0, 7); } FFMpegHelper.ConversionExceptionCheck(source, output); FFMpegHelper.ExtensionExceptionCheck(output, ".png"); string thumbArgs, thumbSize = thumbWidth.ToString() + "x" + thumbHeight.ToString(); thumbArgs = String.Format("-i \"{0}\" -vcodec png -vframes 1 -ss {1} -s {2} \"{3}\"", source.Path, seek.ToString(), thumbSize, output); return(_RunProcess(thumbArgs)); }
/// <summary> /// Saves a 'png' thumbnail from the input video. /// </summary> /// <param name="source">Source video file.</param> /// <param name="output">Output video file</param> /// <param name="captureTime">Seek position where the thumbnail should be taken.</param> /// <param name="size">Thumbnail size. If width or height equal 0, the other will be computed automatically.</param> /// <param name="persistSnapshotOnFileSystem">By default, it deletes the created image on disk. If set to true, it won't delete the image</param> /// <returns>Bitmap with the requested snapshot.</returns> public Bitmap Snapshot(VideoInfo source, FileInfo output, Size?size = null, TimeSpan?captureTime = null, bool persistSnapshotOnFileSystem = false) { if (captureTime == null) { captureTime = TimeSpan.FromSeconds(source.Duration.TotalSeconds / 3); } if (output.Extension.ToLower() != FileExtension.Png) { output = new FileInfo(output.FullName.Replace(output.Extension, FileExtension.Png)); } if (size == null || (size.Value.Height == 0 && size.Value.Width == 0)) { size = new Size(source.Width, source.Height); } if (size.Value.Width != size.Value.Height) { if (size.Value.Width == 0) { var ratio = source.Width / (double)size.Value.Width; size = new Size((int)(source.Width * ratio), (int)(source.Height * ratio)); } if (size.Value.Height == 0) { var ratio = source.Height / (double)size.Value.Height; size = new Size((int)(source.Width * ratio), (int)(source.Height * ratio)); } } FFMpegHelper.ConversionExceptionCheck(source.ToFileInfo(), output); var container = new ArgumentContainer( new InputArgument(source), new VideoCodecArgument(VideoCodec.Png), new FrameOutputCountArgument(1), new SeekArgument(captureTime), new SizeArgument(size), new OutputArgument(output) ); if (!RunProcess(container, output)) { throw new OperationCanceledException("Could not take snapshot!"); } output.Refresh(); Bitmap result; using (var bmp = (Bitmap)Image.FromFile(output.FullName)) { using var ms = new MemoryStream(); bmp.Save(ms, ImageFormat.Png); result = new Bitmap(ms); } if (output.Exists && !persistSnapshotOnFileSystem) { output.Delete(); } return(result); }
/// <summary> /// Convert a video do a different format. /// </summary> /// <param name="source">Input video source.</param> /// <param name="output">Output information.</param> /// <param name="type">Target conversion video type.</param> /// <param name="speed">Conversion target speed/quality (faster speed = lower quality).</param> /// <param name="size">Video size.</param> /// <param name="audioQuality">Conversion target audio quality.</param> /// <param name="multithreaded">Is encoding multithreaded.</param> /// <returns>Output video information.</returns> public VideoInfo Convert( VideoInfo source, FileInfo output, VideoType type = VideoType.Mp4, Speed speed = Speed.SuperFast, VideoSize size = VideoSize.Original, AudioQuality audioQuality = AudioQuality.Normal, bool multithreaded = false) { FFMpegHelper.ConversionExceptionCheck(source.ToFileInfo(), output); FFMpegHelper.ExtensionExceptionCheck(output, FileExtension.ForType(type)); FFMpegHelper.ConversionSizeExceptionCheck(source); _totalTime = source.Duration; var scale = VideoSize.Original == size ? 1 : (double)source.Height / (int)size; var outputSize = new Size( (int)(source.Width / scale), (int)(source.Height / scale) ); if (outputSize.Width % 2 != 0) { outputSize.Width += 1; } var container = new ArgumentContainer(); switch (type) { case VideoType.Mp4: container.Add( new InputArgument(source), new ThreadsArgument(multithreaded), new ScaleArgument(outputSize), new VideoCodecArgument(VideoCodec.LibX264, 2400), new SpeedArgument(speed), new AudioCodecArgument(AudioCodec.Aac, audioQuality), new OutputArgument(output) ); break; case VideoType.Ogv: container.Add( new InputArgument(source), new ThreadsArgument(multithreaded), new ScaleArgument(outputSize), new VideoCodecArgument(VideoCodec.LibTheora, 2400), new SpeedArgument(speed), new AudioCodecArgument(AudioCodec.LibVorbis, audioQuality), new OutputArgument(output) ); break; case VideoType.Ts: container.Add( new InputArgument(source), new CopyArgument(), new BitStreamFilterArgument(Channel.Video, Filter.H264_Mp4ToAnnexB), new ForceFormatArgument(VideoCodec.MpegTs), new OutputArgument(output) ); break; } if (!RunProcess(container, output)) { throw new FFMpegException(FFMpegExceptionType.Conversion, $"The video could not be converted to {Enum.GetName(typeof(VideoType), type)}"); } _totalTime = TimeSpan.MinValue; return(new VideoInfo(output)); }