/// <summary> /// Converts an image sequence to a video. /// </summary> /// <param name="output">Output video file.</param> /// <param name="frameRate">FPS</param> /// <param name="images">Image sequence collection</param> /// <returns>Output video information.</returns> public VideoInfo JoinImageSequence(FileInfo output, double frameRate = 30, params ImageInfo[] images) { var temporaryImageFiles = images.Select((image, index) => { FFMpegHelper.ConversionSizeExceptionCheck(Image.FromFile(image.FullName)); var destinationPath = image.FullName.Replace(image.Name, $"{index.ToString().PadLeft(9, '0')}{image.Extension}"); File.Copy(image.FullName, destinationPath); return(destinationPath); }).ToList(); var firstImage = images.First(); var args = Arguments.FrameRate(frameRate) + Arguments.Size(new Size(firstImage.Width, firstImage.Height)) + Arguments.StartNumber(0) + Arguments.Input($"{firstImage.Directory}\\%09d.png") + Arguments.FrameOutputCount(images.Length) + Arguments.Video(VideoCodec.LibX264) + Arguments.Output(output); try { if (!RunProcess(args, output)) { throw new FFMpegException(FFMpegExceptionType.Operation, "Could not join the provided image sequence."); } return(new VideoInfo(output)); } finally { Cleanup(temporaryImageFiles); } }
/// <summary> /// Joins a list of video files. /// </summary> /// <param name="output">Output video file.</param> /// <param name="videos">List of vides that need to be joined together.</param> /// <returns>Output video information.</returns> public VideoInfo Join(FileInfo output, params VideoInfo[] videos) { FFMpegHelper.OutputExistsExceptionCheck(output); FFMpegHelper.InputsExistExceptionCheck(videos.Select(video => video.ToFileInfo()).ToArray()); var temporaryVideoParts = videos.Select(video => { FFMpegHelper.ConversionSizeExceptionCheck(video); var destinationPath = video.FullName.Replace(video.Extension, FileExtension.Ts); Convert( video, new FileInfo(destinationPath), VideoType.Ts ); return(destinationPath); }).ToList(); var args = Arguments.InputConcat(temporaryVideoParts) + Arguments.Copy() + Arguments.BitStreamFilter(Channel.Audio, Filter.Aac_AdtstoAsc) + Arguments.Output(output); try { if (!RunProcess(args, output)) { throw new FFMpegException(FFMpegExceptionType.Operation, "Could not join the provided video files."); } return(new VideoInfo(output)); } finally { Cleanup(temporaryVideoParts); } }
/// <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> /// 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> /// 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> /// Adds a poster image to an audio file. /// </summary> /// <param name="image">Source image file.</param> /// <param name="audio">Source audio file.</param> /// <param name="output">Output video file.</param> /// <returns></returns> public VideoInfo PosterWithAudio(FileInfo image, FileInfo audio, FileInfo output) { FFMpegHelper.InputsExistExceptionCheck(image, audio); FFMpegHelper.ExtensionExceptionCheck(output, FileExtension.Mp4); FFMpegHelper.ConversionSizeExceptionCheck(Image.FromFile(image.FullName)); var args = Arguments.Loop(1) + Arguments.Input(image) + Arguments.Input(audio) + Arguments.Video(VideoCodec.LibX264, 2400) + Arguments.Audio(AudioCodec.Aac, AudioQuality.Normal) + Arguments.FinalizeAtShortestInput(true) + Arguments.Output(output); if (!RunProcess(args, output)) { throw new FFMpegException(FFMpegExceptionType.Operation, "An error occured while adding the audio file to the image."); } return(new VideoInfo(output)); }
/// <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> /// Adds a poster image to an audio file. /// </summary> /// <param name="image">Source image file.</param> /// <param name="audio">Source audio file.</param> /// <param name="output">Output video file.</param> /// <returns></returns> public VideoInfo PosterWithAudio(FileInfo image, FileInfo audio, FileInfo output) { FFMpegHelper.InputsExistExceptionCheck(image, audio); FFMpegHelper.ExtensionExceptionCheck(output, FileExtension.Mp4); FFMpegHelper.ConversionSizeExceptionCheck(Image.FromFile(image.FullName)); var container = new ArgumentContainer( new LoopArgument(1), new InputArgument(image.FullName, audio.FullName), new VideoCodecArgument(VideoCodec.LibX264, 2400), new AudioCodecArgument(AudioCodec.Aac, AudioQuality.Normal), new ShortestArgument(true), new OutputArgument(output) ); if (!RunProcess(container, output)) { throw new FFMpegException(FFMpegExceptionType.Operation, "An error occured while adding the audio file to the image."); } 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 static bool Convert( string input, string output, ContainerFormat format, Speed speed = Speed.SuperFast, VideoSize size = VideoSize.Original, AudioQuality audioQuality = AudioQuality.Normal, bool multithreaded = false) { FFMpegHelper.ExtensionExceptionCheck(output, format.Extension); var source = FFProbe.Analyse(input); FFMpegHelper.ConversionSizeExceptionCheck(source); var scale = VideoSize.Original == size ? 1 : (double)source.PrimaryVideoStream !.Height / (int)size; var outputSize = new Size((int)(source.PrimaryVideoStream !.Width / scale), (int)(source.PrimaryVideoStream.Height / scale)); if (outputSize.Width % 2 != 0) { outputSize.Width += 1; } return(format.Name switch { "mp4" => FFMpegArguments .FromFileInput(input) .OutputToFile(output, true, options => options .UsingMultithreading(multithreaded) .WithVideoCodec(VideoCodec.LibX264) .WithVideoBitrate(2400) .WithVideoFilters(filterOptions => filterOptions .Scale(outputSize)) .WithSpeedPreset(speed) .WithAudioCodec(AudioCodec.Aac) .WithAudioBitrate(audioQuality)) .ProcessSynchronously(), "ogv" => FFMpegArguments .FromFileInput(input) .OutputToFile(output, true, options => options .UsingMultithreading(multithreaded) .WithVideoCodec(VideoCodec.LibTheora) .WithVideoBitrate(2400) .WithVideoFilters(filterOptions => filterOptions .Scale(outputSize)) .WithSpeedPreset(speed) .WithAudioCodec(AudioCodec.LibVorbis) .WithAudioBitrate(audioQuality)) .ProcessSynchronously(), "mpegts" => FFMpegArguments .FromFileInput(input) .OutputToFile(output, true, options => options .CopyChannel() .WithBitStreamFilter(Channel.Video, Filter.H264_Mp4ToAnnexB) .ForceFormat(VideoType.Ts)) .ProcessSynchronously(), "webm" => FFMpegArguments .FromFileInput(input) .OutputToFile(output, true, options => options .UsingMultithreading(multithreaded) .WithVideoCodec(VideoCodec.LibVpx) .WithVideoBitrate(2400) .WithVideoFilters(filterOptions => filterOptions .Scale(outputSize)) .WithSpeedPreset(speed) .WithAudioCodec(AudioCodec.LibVorbis) .WithAudioBitrate(audioQuality)) .ProcessSynchronously(), _ => throw new ArgumentOutOfRangeException(nameof(format)) });
/// <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)); }