Merge pull request #403 from slugger7/feat/cut-video

Sub video function
This commit is contained in:
Malte Rosenbjerg 2023-02-21 18:05:05 +01:00 committed by GitHub
commit f488184dba
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 93 additions and 0 deletions

View file

@ -258,6 +258,13 @@ public void Builder_BuildString_Seek()
Assert.AreEqual("-ss 00:00:10.000 -i \"input.mp4\" -ss 00:00:10.000 \"output.mp4\"", str); Assert.AreEqual("-ss 00:00:10.000 -i \"input.mp4\" -ss 00:00:10.000 \"output.mp4\"", str);
} }
[TestMethod]
public void Builder_BuildString_EndSeek()
{
var str = FFMpegArguments.FromFileInput("input.mp4", false, opt => opt.EndSeek(TimeSpan.FromSeconds(10))).OutputToFile("output.mp4", false, opt => opt.EndSeek(TimeSpan.FromSeconds(10))).Arguments;
Assert.AreEqual("-to 00:00:10.000 -i \"input.mp4\" -to 00:00:10.000 \"output.mp4\"", str);
}
[TestMethod] [TestMethod]
public void Builder_BuildString_Shortest() public void Builder_BuildString_Shortest()
{ {

View file

@ -0,0 +1,36 @@
namespace FFMpegCore.Arguments
{
/// <summary>
/// Represents seek parameter
/// </summary>
public class EndSeekArgument : IArgument
{
public readonly TimeSpan? SeekTo;
public EndSeekArgument(TimeSpan? seekTo)
{
SeekTo = seekTo;
}
public string Text
{
get
{
if (SeekTo.HasValue)
{
var hours = SeekTo.Value.Hours;
if (SeekTo.Value.Days > 0)
{
hours += SeekTo.Value.Days * 24;
}
return $"-to {hours.ToString("00")}:{SeekTo.Value.Minutes.ToString("00")}:{SeekTo.Value.Seconds.ToString("00")}.{SeekTo.Value.Milliseconds.ToString("000")}";
}
else
{
return string.Empty;
}
}
}
}
}

View file

@ -247,6 +247,46 @@ public static bool Join(string output, params string[] videos)
} }
} }
private static FFMpegArgumentProcessor BaseSubVideo(string input, string output, TimeSpan startTime, TimeSpan endTime)
{
if (Path.GetExtension(input) != Path.GetExtension(output))
{
output = Path.Combine(Path.GetDirectoryName(output), Path.GetFileNameWithoutExtension(output), Path.GetExtension(input));
}
return FFMpegArguments
.FromFileInput(input, true, options => options.Seek(startTime).EndSeek(endTime))
.OutputToFile(output, true, options => options.CopyChannel());
}
/// <summary>
/// Creates a new video starting and ending at the specified times
/// </summary>
/// <param name="input">Input video file.</param>
/// <param name="output">Output video file.</param>
/// <param name="startTime">The start time of when the sub video needs to start</param>
/// <param name="endTime">The end time of where the sub video needs to end</param>
/// <returns>Output video information.</returns>
public static bool SubVideo(string input, string output, TimeSpan startTime, TimeSpan endTime)
{
return BaseSubVideo(input, output, startTime, endTime)
.ProcessSynchronously();
}
/// <summary>
/// Creates a new video starting and ending at the specified times
/// </summary>
/// <param name="input">Input video file.</param>
/// <param name="output">Output video file.</param>
/// <param name="startTime">The start time of when the sub video needs to start</param>
/// <param name="endTime">The end time of where the sub video needs to end</param>
/// <returns>Output video information.</returns>
public static async Task<bool> SubVideoAsync(string input, string output, TimeSpan startTime, TimeSpan endTime)
{
return await BaseSubVideo(input, output, startTime, endTime)
.ProcessAsynchronously();
}
/// <summary> /// <summary>
/// Records M3U8 streams to the specified output. /// Records M3U8 streams to the specified output.
/// </summary> /// </summary>

View file

@ -54,6 +54,7 @@ public FFMpegArgumentOptions WithAudioFilters(Action<AudioFilterOptions> audioFi
public FFMpegArgumentOptions WithCustomArgument(string argument) => WithArgument(new CustomArgument(argument)); public FFMpegArgumentOptions WithCustomArgument(string argument) => WithArgument(new CustomArgument(argument));
public FFMpegArgumentOptions Seek(TimeSpan? seekTo) => WithArgument(new SeekArgument(seekTo)); public FFMpegArgumentOptions Seek(TimeSpan? seekTo) => WithArgument(new SeekArgument(seekTo));
public FFMpegArgumentOptions EndSeek(TimeSpan? seekTo) => WithArgument(new EndSeekArgument(seekTo));
public FFMpegArgumentOptions Loop(int times) => WithArgument(new LoopArgument(times)); public FFMpegArgumentOptions Loop(int times) => WithArgument(new LoopArgument(times));
public FFMpegArgumentOptions OverwriteExisting() => WithArgument(new OverwriteArgument()); public FFMpegArgumentOptions OverwriteExisting() => WithArgument(new OverwriteArgument());
public FFMpegArgumentOptions SelectStream(int streamIndex, int inputFileIndex = 0, public FFMpegArgumentOptions SelectStream(int streamIndex, int inputFileIndex = 0,

View file

@ -72,6 +72,15 @@ FFMpeg.Join(@"..\joined_video.mp4",
); );
``` ```
### Create a sub video
``` csharp
FFMpeg.SubVideo(inputPath,
outputPath,
TimeSpan.FromSeconds(0)
TimeSpan.FromSeconds(30)
);
```
### Join images into a video: ### Join images into a video:
```csharp ```csharp
FFMpeg.JoinImageSequence(@"..\joined_video.mp4", frameRate: 1, FFMpeg.JoinImageSequence(@"..\joined_video.mp4", frameRate: 1,