diff --git a/FFMpegCore.Test/ArgumentBuilderTest.cs b/FFMpegCore.Test/ArgumentBuilderTest.cs index 9cf7e39..906a87b 100644 --- a/FFMpegCore.Test/ArgumentBuilderTest.cs +++ b/FFMpegCore.Test/ArgumentBuilderTest.cs @@ -114,7 +114,7 @@ public void Builder_BuildString_Copy_Both() { var str = FFMpegArguments.FromFileInput("input.mp4") .OutputToFile("output.mp4", false, opt => opt.CopyChannel()).Arguments; - Assert.AreEqual("-i \"input.mp4\" -c copy \"output.mp4\"", str); + Assert.AreEqual("-i \"input.mp4\" -c:a copy -c:v copy \"output.mp4\"", str); } [TestMethod] diff --git a/FFMpegCore/FFMpeg/Arguments/CopyArgument.cs b/FFMpegCore/FFMpeg/Arguments/CopyArgument.cs index 91419d5..eeac4c8 100644 --- a/FFMpegCore/FFMpeg/Arguments/CopyArgument.cs +++ b/FFMpegCore/FFMpeg/Arguments/CopyArgument.cs @@ -16,9 +16,8 @@ public CopyArgument(Channel channel = Channel.Both) public string Text => Channel switch { - Channel.Audio => "-c:a copy", - Channel.Video => "-c:v copy", - _ => "-c copy" + Channel.Both => "-c:a copy -c:v copy", + _ => $"-c{Channel.StreamType()} copy" }; } } diff --git a/FFMpegCore/FFMpeg/Arguments/MapStreamArgument.cs b/FFMpegCore/FFMpeg/Arguments/MapStreamArgument.cs index b904be5..a9813c7 100644 --- a/FFMpegCore/FFMpeg/Arguments/MapStreamArgument.cs +++ b/FFMpegCore/FFMpeg/Arguments/MapStreamArgument.cs @@ -1,19 +1,30 @@ -namespace FFMpegCore.Arguments +using FFMpegCore.Enums; + +namespace FFMpegCore.Arguments { /// - /// Represents choice of video stream + /// Represents choice of stream by the stream specifier /// public class MapStreamArgument : IArgument { private readonly int _inputFileIndex; private readonly int _streamIndex; + private readonly Channel _channel; + private readonly bool _negativeMap; - public MapStreamArgument(int streamIndex, int inputFileIndex) + public MapStreamArgument(int streamIndex, int inputFileIndex, Channel channel = Channel.All, bool negativeMap = false) { + if (channel == Channel.Both) + { + // "Both" is not valid in this case and probably means all stream types + channel = Channel.All; + } _inputFileIndex = inputFileIndex; _streamIndex = streamIndex; + _channel = channel; + _negativeMap = negativeMap; } - public string Text => $"-map {_inputFileIndex}:{_streamIndex}"; + public string Text => $"-map {(_negativeMap?"-":"")}{_inputFileIndex}{_channel.StreamType()}:{_streamIndex}"; } } \ No newline at end of file diff --git a/FFMpegCore/FFMpeg/Enums/Enums.cs b/FFMpegCore/FFMpeg/Enums/Enums.cs index 7520fea..a6eb413 100644 --- a/FFMpegCore/FFMpeg/Enums/Enums.cs +++ b/FFMpegCore/FFMpeg/Enums/Enums.cs @@ -46,10 +46,43 @@ public enum Filter Aac_AdtstoAsc } + /// + /// https://ffmpeg.org/ffmpeg.html#Stream-specifiers-1 + /// ’v’ or ’V’ for video, ’a’ for audio, ’s’ for subtitle, ’d’ for data, and ’t’ for attachments + /// ’V’ only matches video streams which are not attached pictures, video thumbnails or cover arts. + /// Both for audio + video + /// All for all types + /// public enum Channel { Audio, Video, - Both + Both, + VideoNoAttachedPic, + Subtitle, + Data, + Attachments, + All } + internal static class ChannelMethods + { + /// + /// is left as empty because it cannot be in a single stream specifier + /// + /// The stream_type used in stream specifiers + public static string StreamType(this Channel channel) + { + return channel switch + { + Channel.Audio => ":a", + Channel.Video => ":v", + Channel.VideoNoAttachedPic => ":V", + Channel.Subtitle => ":s", + Channel.Data => ":d", + Channel.Attachments => ":t", + _ => string.Empty + }; + } + } + } \ No newline at end of file diff --git a/FFMpegCore/FFMpeg/FFMpegArgumentOptions.cs b/FFMpegCore/FFMpeg/FFMpegArgumentOptions.cs index 7b3da7a..1ff892a 100644 --- a/FFMpegCore/FFMpeg/FFMpegArgumentOptions.cs +++ b/FFMpegCore/FFMpeg/FFMpegArgumentOptions.cs @@ -1,6 +1,7 @@ using System; +using System.Collections.Generic; using System.Drawing; - +using System.Linq; using FFMpegCore.Arguments; using FFMpegCore.Enums; @@ -60,7 +61,16 @@ public FFMpegArgumentOptions WithAudioFilters(Action audioFi public FFMpegArgumentOptions Seek(TimeSpan? seekTo) => WithArgument(new SeekArgument(seekTo)); public FFMpegArgumentOptions Loop(int times) => WithArgument(new LoopArgument(times)); public FFMpegArgumentOptions OverwriteExisting() => WithArgument(new OverwriteArgument()); - public FFMpegArgumentOptions SelectStream(int streamIndex, int inputFileIndex = 0) => WithArgument(new MapStreamArgument(streamIndex, inputFileIndex)); + public FFMpegArgumentOptions SelectStream(int streamIndex, int inputFileIndex = 0, + Channel channel = Channel.All) => WithArgument(new MapStreamArgument(streamIndex, inputFileIndex, channel)); + public FFMpegArgumentOptions SelectStreams(IEnumerable streamIndices, int inputFileIndex = 0, + Channel channel = Channel.All) => streamIndices.Aggregate(this, + (options, streamIndex) => options.SelectStream(streamIndex, inputFileIndex, channel)); + public FFMpegArgumentOptions DeselectStream(int streamIndex, int inputFileIndex = 0, + Channel channel = Channel.All) => WithArgument(new MapStreamArgument(streamIndex, inputFileIndex, channel, true)); + public FFMpegArgumentOptions DeselectStreams(IEnumerable streamIndices, int inputFileIndex = 0, + Channel channel = Channel.All) => streamIndices.Aggregate(this, + (options, streamIndex) => options.DeselectStream(streamIndex, inputFileIndex, channel)); public FFMpegArgumentOptions ForceFormat(ContainerFormat format) => WithArgument(new ForceFormatArgument(format)); public FFMpegArgumentOptions ForceFormat(string format) => WithArgument(new ForceFormatArgument(format));