diff --git a/splitter-cli/CommandLine.cs b/splitter-cli/CommandLine.cs
index 8e9609c..4b4087f 100644
--- a/splitter-cli/CommandLine.cs
+++ b/splitter-cli/CommandLine.cs
@@ -1,122 +1,9 @@
using System.Globalization;
+using splitter.algo;
+using splitter.util;
namespace splitter;
-public class SingleJob
-{
- ///
- /// File path of the input video. This is required for each job and should be
- /// set to a valid video file path. The splitter will read this file, analyze it,
- /// and split it into segments based on the specified parameters.
- /// The output segments will be saved in the OutputFolder with names
- /// derived from this input file and the Mask pattern if provided.
- ///
- public string InputFile { get; set; } = null!;
- ///
- /// Output folder where the split segments will be saved. This should be set
- /// to a valid directory path.
- ///
- public string OutputFolder { get; set; } = null!;
- ///
- /// Crop parameters. Width and height for cropping the video. If set, the
- /// splitter will crop the video to the specified dimensions while tracking the subject.
- ///
- public (int width, int height)? Crop { get; set; }
- ///
- /// The fallback point to gravitate towards when tracking the subject. Coordinates are normalized (0.0 to 1.0).
- /// By default , the splitter gravitates towards the center of the frame (0.5, 0.5).
- /// Setting this allows you to bias the tracking towards a specific area of the frame,
- /// such as left-center (0.2, 0.5) or top-right (0.8, 0.2). This can be useful for
- /// videos where the subject tends to be off-center or for creative framing choices.
- ///
- public Point2f? GravitateTo { get; set; }
- ///
- /// Destination file mask.
- ///
- public string? Mask { get; set; }
- ///
- /// Instead of producing the output, just generate debug frames with tracking
- /// overlay to visually verify that the tracking is working correctly.
- ///
- public bool Debug { get; set; }
- ///
- /// Type of detector to use for tracking. Supported values are: face (UltraFace),
- /// body (YoloOnnx, default), none (no tracking, just a center point).
- ///
- public string? Detect { get; set; }
- ///
- /// Set starget segments length explicitly. By default, the splitter calculates segment
- /// lengths to be equal and not exceed 58 seconds.
- ///
- public double? OverrideTargetDuration { get; set; }
- ///
- /// Parameters to pass thru to ffmpeg. These are specified after "--" in the command
- /// line and are passed directly to the ffmpeg command line for each segment.
- ///
- public string[] Passthrough { get; set; } = [];
- ///
- /// Debugging parameter. Instead of text UI putput lines in plain text.
- /// This is useful when the output is being piped to a file or another program,
- /// or when the user prefers a simpler log format without progress bars and dynamic updates.
- ///
- public bool PlainText { get; set; }
- ///
- /// Debugging parameter. Just show estimated segments length, count, and other info
- /// without actually performing the splitting.
- ///
- public bool EstimateOnly { get; set; }
- ///
- /// Do not adapt segment length. When set, the splitter will use the exact
- /// segment duration specified by --duration for all segments except possibly
- /// the last one, which may be shorter.
- ///
- public bool ForceFixed { get; set; }
- ///
- /// Use single thread for operations. When set, the splitter will not run
- /// multiple ffmpeg processes in parallel.
- ///
- public bool SingleThreaded { get; set; }
- ///
- /// Rotation angle: 90, 180, or 270 degrees. This is useful for videos that
- /// have incorrect orientation metadata.
- ///
- public int? Rotate { get; set; }
- ///
- /// Autodetect if rotation is needed. Not very reliable but can work for some videos.
- /// Uses edge orientation statistics to determine if the video is rotated and
- /// applies the appropriate rotation if needed.
- ///
- public bool RotateAuto { get; set; }
- ///
- /// Override internal parameters. This allows you to set custom parameters for the
- /// object detector or rotation detector.
- ///
- public Dictionary Parameters { get; set; } = [];
-
- public void Override(ref T member, string name)
- {
- if (!Parameters.TryGetValue(name, out var raw))
- return;
-
- try
- {
- // Convert.ChangeType handles int, float, double, etc.
- var converted = (T)Convert.ChangeType(
- raw,
- typeof(T),
- CultureInfo.InvariantCulture
- );
-
- member = converted;
- }
- catch
- {
- Console.WriteLine($"Invalid value for parameter '{name}': {raw}");
- }
- }
-
-}
-
public sealed class CommandLine
{
public SingleJob Master { get; } = new SingleJob();
diff --git a/splitter-cli/FfprobeFormat.cs b/splitter-cli/FfprobeFormat.cs
deleted file mode 100644
index fbacf8f..0000000
--- a/splitter-cli/FfprobeFormat.cs
+++ /dev/null
@@ -1,40 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Text.Json.Serialization;
-
-namespace splitter;
-
-public sealed class FfprobeFormat
-{
- public string? Filename { get; set; }
-
- [JsonConverter(typeof(FlexibleIntConverter))]
- public int? Nb_streams { get; set; }
-
- [JsonConverter(typeof(FlexibleIntConverter))]
- public int? Nb_programs { get; set; }
-
- [JsonConverter(typeof(FlexibleIntConverter))]
- public int? Nb_stream_groups { get; set; }
-
- public string? Format_name { get; set; }
- public string? Format_long_name { get; set; }
-
- [JsonConverter(typeof(FlexibleDoubleConverter))]
- public double? Start_time { get; set; }
-
- [JsonConverter(typeof(FlexibleDoubleConverter))]
- public double? Duration { get; set; }
-
- [JsonConverter(typeof(FlexibleLongConverter))]
- public long? Size { get; set; }
-
- [JsonConverter(typeof(FlexibleLongConverter))]
- public long? Bit_rate { get; set; }
-
- [JsonConverter(typeof(FlexibleIntConverter))]
- public int? Probe_score { get; set; }
-
- public Dictionary? Tags { get; set; }
-}
diff --git a/splitter-cli/FfprobeStream.cs b/splitter-cli/FfprobeStream.cs
deleted file mode 100644
index e9cf8e0..0000000
--- a/splitter-cli/FfprobeStream.cs
+++ /dev/null
@@ -1,129 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Text.Json.Serialization;
-
-namespace splitter;
-
-public sealed class FfprobeStream
-{
- [JsonConverter(typeof(FlexibleIntConverter))]
- public int? Index { get; set; }
-
- public string? Codec_name { get; set; }
- public string? Codec_long_name { get; set; }
- public string? Profile { get; set; }
- public string? Codec_type { get; set; }
- public string? Codec_tag_string { get; set; }
- public string? Codec_tag { get; set; }
- public string? Mime_codec_string { get; set; }
-
- [JsonConverter(typeof(FlexibleIntConverter))]
- public int? Width { get; set; }
-
- [JsonConverter(typeof(FlexibleIntConverter))]
- public int? Height { get; set; }
-
- [JsonConverter(typeof(FlexibleIntConverter))]
- public int? Coded_width { get; set; }
-
- [JsonConverter(typeof(FlexibleIntConverter))]
- public int? Coded_height { get; set; }
-
- [JsonConverter(typeof(FlexibleIntConverter))]
- public int? Closed_captions { get; set; }
-
- [JsonConverter(typeof(FlexibleIntConverter))]
- public int? Film_grain { get; set; }
-
- [JsonConverter(typeof(FlexibleIntConverter))]
- public int? Has_b_frames { get; set; }
-
- public string? Sample_aspect_ratio { get; set; }
- public string? Display_aspect_ratio { get; set; }
-
- public string? Pix_fmt { get; set; }
-
- [JsonConverter(typeof(FlexibleIntConverter))]
- public int? Level { get; set; }
-
- public string? Color_range { get; set; }
- public string? Color_space { get; set; }
- public string? Color_transfer { get; set; }
- public string? Color_primaries { get; set; }
- public string? Chroma_location { get; set; }
- public string? Field_order { get; set; }
-
- public string? Is_avc { get; set; }
- public string? Nal_length_size { get; set; }
-
- public string? Id { get; set; }
- public string? R_frame_rate { get; set; }
- public string? Avg_frame_rate { get; set; }
- public string? Time_base { get; set; }
-
- [JsonConverter(typeof(FlexibleLongConverter))]
- public long? Start_pts { get; set; }
-
- [JsonConverter(typeof(FlexibleDoubleConverter))]
- public double? Start_time { get; set; }
-
- [JsonConverter(typeof(FlexibleLongConverter))]
- public long? Duration_ts { get; set; }
-
- [JsonConverter(typeof(FlexibleDoubleConverter))]
- public double? Duration { get; set; }
-
- [JsonConverter(typeof(FlexibleLongConverter))]
- public long? Bit_rate { get; set; }
-
- [JsonConverter(typeof(FlexibleLongConverter))]
- public long? Max_bit_rate { get; set; }
-
- [JsonConverter(typeof(FlexibleIntConverter))]
- public int? Bits_per_raw_sample { get; set; }
-
- [JsonConverter(typeof(FlexibleIntConverter))]
- public int? Bits_per_sample { get; set; }
-
- [JsonConverter(typeof(FlexibleLongConverter))]
- public long? Nb_frames { get; set; }
-
- [JsonConverter(typeof(FlexibleIntConverter))]
- public int? Extradata_size { get; set; }
-
- [JsonConverter(typeof(FlexibleIntConverter))]
- public int? Channels { get; set; }
-
- public string? Channel_layout { get; set; }
- public string? Sample_fmt { get; set; }
-
- [JsonConverter(typeof(FlexibleIntConverter))]
- public int? Sample_rate { get; set; }
-
- [JsonConverter(typeof(FlexibleIntConverter))]
- public int? Initial_padding { get; set; }
-
- public string? Disposition_raw { get; set; }
- public Dictionary? Disposition { get; set; }
-
- public Dictionary? Tags { get; set; }
-
- public string? Language { get; set; }
- public string? Title { get; set; }
-
- [JsonConverter(typeof(FlexibleIntConverter))]
- public int? Bits_per_coded_sample { get; set; }
-
- public string? Codec_time_base { get; set; }
-
- [JsonConverter(typeof(FlexibleDoubleConverter))]
- public double? Start_pts_time { get; set; }
-
- [JsonConverter(typeof(FlexibleDoubleConverter))]
- public double? Duration_time { get; set; }
-
- public string? Extradata { get; set; }
- public string? Default { get; set; }
- public string? Forced { get; set; }
-}
diff --git a/splitter-cli/GlobalUsing.cs b/splitter-cli/GlobalUsing.cs
new file mode 100644
index 0000000..41f0eee
--- /dev/null
+++ b/splitter-cli/GlobalUsing.cs
@@ -0,0 +1,5 @@
+global using OpenCvSharp;
+global using splitter.algo;
+global using splitter.probe;
+global using splitter.tui;
+
diff --git a/splitter-cli/Point2f.cs b/splitter-cli/Point2f.cs
deleted file mode 100644
index a997ea0..0000000
--- a/splitter-cli/Point2f.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-namespace splitter;
-
-public struct Point2f
-{
- public float X;
- public float Y;
-
- public Point2f(float x, float y)
- {
- X = x;
- Y = y;
- }
-}
diff --git a/splitter-cli/SimpleSplitter.cs b/splitter-cli/SimpleSplitter.cs
index 21d9da5..5a4d0e8 100644
--- a/splitter-cli/SimpleSplitter.cs
+++ b/splitter-cli/SimpleSplitter.cs
@@ -1,5 +1,7 @@
using System.Diagnostics;
using System.Globalization;
+using splitter.algo;
+using splitter.tui;
namespace splitter;
diff --git a/splitter-cli/SingleJob.cs b/splitter-cli/SingleJob.cs
new file mode 100644
index 0000000..5871afc
--- /dev/null
+++ b/splitter-cli/SingleJob.cs
@@ -0,0 +1,119 @@
+using System.Globalization;
+using splitter.algo;
+
+namespace splitter;
+
+public class SingleJob
+{
+ ///
+ /// File path of the input video. This is required for each job and should be
+ /// set to a valid video file path. The splitter will read this file, analyze it,
+ /// and split it into segments based on the specified parameters.
+ /// The output segments will be saved in the OutputFolder with names
+ /// derived from this input file and the Mask pattern if provided.
+ ///
+ public string InputFile { get; set; } = null!;
+ ///
+ /// Output folder where the split segments will be saved. This should be set
+ /// to a valid directory path.
+ ///
+ public string OutputFolder { get; set; } = null!;
+ ///
+ /// Crop parameters. Width and height for cropping the video. If set, the
+ /// splitter will crop the video to the specified dimensions while tracking the subject.
+ ///
+ public (int width, int height)? Crop { get; set; }
+ ///
+ /// The fallback point to gravitate towards when tracking the subject. Coordinates are normalized (0.0 to 1.0).
+ /// By default , the splitter gravitates towards the center of the frame (0.5, 0.5).
+ /// Setting this allows you to bias the tracking towards a specific area of the frame,
+ /// such as left-center (0.2, 0.5) or top-right (0.8, 0.2). This can be useful for
+ /// videos where the subject tends to be off-center or for creative framing choices.
+ ///
+ public Point2f? GravitateTo { get; set; }
+ ///
+ /// Destination file mask.
+ ///
+ public string? Mask { get; set; }
+ ///
+ /// Instead of producing the output, just generate debug frames with tracking
+ /// overlay to visually verify that the tracking is working correctly.
+ ///
+ public bool Debug { get; set; }
+ ///
+ /// Type of detector to use for tracking. Supported values are: face (UltraFace),
+ /// body (YoloOnnx, default), none (no tracking, just a center point).
+ ///
+ public string? Detect { get; set; }
+ ///
+ /// Set starget segments length explicitly. By default, the splitter calculates segment
+ /// lengths to be equal and not exceed 58 seconds.
+ ///
+ public double? OverrideTargetDuration { get; set; }
+ ///
+ /// Parameters to pass thru to ffmpeg. These are specified after "--" in the command
+ /// line and are passed directly to the ffmpeg command line for each segment.
+ ///
+ public string[] Passthrough { get; set; } = [];
+ ///
+ /// Debugging parameter. Instead of text UI putput lines in plain text.
+ /// This is useful when the output is being piped to a file or another program,
+ /// or when the user prefers a simpler log format without progress bars and dynamic updates.
+ ///
+ public bool PlainText { get; set; }
+ ///
+ /// Debugging parameter. Just show estimated segments length, count, and other info
+ /// without actually performing the splitting.
+ ///
+ public bool EstimateOnly { get; set; }
+ ///
+ /// Do not adapt segment length. When set, the splitter will use the exact
+ /// segment duration specified by --duration for all segments except possibly
+ /// the last one, which may be shorter.
+ ///
+ public bool ForceFixed { get; set; }
+ ///
+ /// Use single thread for operations. When set, the splitter will not run
+ /// multiple ffmpeg processes in parallel.
+ ///
+ public bool SingleThreaded { get; set; }
+ ///
+ /// Rotation angle: 90, 180, or 270 degrees. This is useful for videos that
+ /// have incorrect orientation metadata.
+ ///
+ public int? Rotate { get; set; }
+ ///
+ /// Autodetect if rotation is needed. Not very reliable but can work for some videos.
+ /// Uses edge orientation statistics to determine if the video is rotated and
+ /// applies the appropriate rotation if needed.
+ ///
+ public bool RotateAuto { get; set; }
+ ///
+ /// Override internal parameters. This allows you to set custom parameters for the
+ /// object detector or rotation detector.
+ ///
+ public Dictionary Parameters { get; set; } = [];
+
+ public void Override(ref T member, string name)
+ {
+ if (!Parameters.TryGetValue(name, out var raw))
+ return;
+
+ try
+ {
+ // Convert.ChangeType handles int, float, double, etc.
+ var converted = (T)Convert.ChangeType(
+ raw,
+ typeof(T),
+ CultureInfo.InvariantCulture
+ );
+
+ member = converted;
+ }
+ catch
+ {
+ Console.WriteLine($"Invalid value for parameter '{name}': {raw}");
+ }
+ }
+
+}
diff --git a/splitter-cli/TrackingSplitter.cs b/splitter-cli/TrackingSplitter.cs
index 9f3cca6..d669020 100644
--- a/splitter-cli/TrackingSplitter.cs
+++ b/splitter-cli/TrackingSplitter.cs
@@ -2,6 +2,8 @@
using System.Globalization;
using System.Runtime.InteropServices;
using OpenCvSharp;
+using splitter.algo;
+using splitter.tui;
namespace splitter;
diff --git a/splitter-cli/CameraController.cs b/splitter-cli/algo/CameraController.cs
similarity index 99%
rename from splitter-cli/CameraController.cs
rename to splitter-cli/algo/CameraController.cs
index a0a6e12..25b850b 100644
--- a/splitter-cli/CameraController.cs
+++ b/splitter-cli/algo/CameraController.cs
@@ -1,6 +1,6 @@
using OpenCvSharp;
-namespace splitter;
+namespace splitter.algo;
public enum TrackState
{
diff --git a/splitter-cli/IObjectDetector.cs b/splitter-cli/algo/IObjectDetector.cs
similarity index 84%
rename from splitter-cli/IObjectDetector.cs
rename to splitter-cli/algo/IObjectDetector.cs
index d87f005..6ce5839 100644
--- a/splitter-cli/IObjectDetector.cs
+++ b/splitter-cli/algo/IObjectDetector.cs
@@ -1,6 +1,6 @@
using OpenCvSharp;
-namespace splitter;
+namespace splitter.algo;
public interface IObjectDetector : IDisposable
{
diff --git a/splitter-cli/ISegmentProcessor.cs b/splitter-cli/algo/ISegmentProcessor.cs
similarity index 74%
rename from splitter-cli/ISegmentProcessor.cs
rename to splitter-cli/algo/ISegmentProcessor.cs
index f8ee12d..68034f9 100644
--- a/splitter-cli/ISegmentProcessor.cs
+++ b/splitter-cli/algo/ISegmentProcessor.cs
@@ -1,4 +1,4 @@
-namespace splitter;
+namespace splitter.algo;
public interface ISegmentProcessor
{
diff --git a/splitter-cli/KalmanTracker.cs b/splitter-cli/algo/KalmanTracker.cs
similarity index 98%
rename from splitter-cli/KalmanTracker.cs
rename to splitter-cli/algo/KalmanTracker.cs
index 9e6f04e..32576ad 100644
--- a/splitter-cli/KalmanTracker.cs
+++ b/splitter-cli/algo/KalmanTracker.cs
@@ -1,4 +1,4 @@
-namespace splitter;
+namespace splitter.algo;
public sealed class KalmanTracker
{
diff --git a/splitter-cli/algo/Point2f.cs b/splitter-cli/algo/Point2f.cs
new file mode 100644
index 0000000..75998cf
--- /dev/null
+++ b/splitter-cli/algo/Point2f.cs
@@ -0,0 +1,13 @@
+//namespace splitter.algo;
+
+//public struct Point2f
+//{
+// public float X;
+// public float Y;
+
+// public Point2f(float x, float y)
+// {
+// X = x;
+// Y = y;
+// }
+//}
diff --git a/splitter-cli/UltraFaceDetector.cs b/splitter-cli/algo/UltraFaceDetector.cs
similarity index 98%
rename from splitter-cli/UltraFaceDetector.cs
rename to splitter-cli/algo/UltraFaceDetector.cs
index effafb9..bb9020a 100644
--- a/splitter-cli/UltraFaceDetector.cs
+++ b/splitter-cli/algo/UltraFaceDetector.cs
@@ -1,8 +1,9 @@
using System.Runtime.InteropServices;
using OpenCvSharp;
+using splitter.tui;
using UltraFaceDotNet;
-namespace splitter;
+namespace splitter.algo;
public sealed class UltraFaceDetector: LoggingBase, IDisposable, IObjectDetector
{
diff --git a/splitter-cli/YoloOnnxObjectDetector.cs b/splitter-cli/algo/YoloOnnxObjectDetector.cs
similarity index 99%
rename from splitter-cli/YoloOnnxObjectDetector.cs
rename to splitter-cli/algo/YoloOnnxObjectDetector.cs
index 3cd695b..90ce27e 100644
--- a/splitter-cli/YoloOnnxObjectDetector.cs
+++ b/splitter-cli/algo/YoloOnnxObjectDetector.cs
@@ -2,8 +2,9 @@
using Microsoft.ML.OnnxRuntime;
using Microsoft.ML.OnnxRuntime.Tensors;
using OpenCvSharp;
+using splitter.tui;
-namespace splitter;
+namespace splitter.algo;
public sealed class YoloOnnxObjectDetector : LoggingBase, IObjectDetector, IDisposable
{
diff --git a/splitter-cli/probe/FfprobeFormat.cs b/splitter-cli/probe/FfprobeFormat.cs
new file mode 100644
index 0000000..c8abd6b
--- /dev/null
+++ b/splitter-cli/probe/FfprobeFormat.cs
@@ -0,0 +1,40 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Text.Json.Serialization;
+
+namespace splitter.probe;
+
+public sealed class FfprobeFormat
+{
+ public string? Filename { get; set; }
+
+ [JsonConverter(typeof(FlexibleIntConverter))]
+ public int? Nb_streams { get; set; }
+
+ [JsonConverter(typeof(FlexibleIntConverter))]
+ public int? Nb_programs { get; set; }
+
+ [JsonConverter(typeof(FlexibleIntConverter))]
+ public int? Nb_stream_groups { get; set; }
+
+ public string? Format_name { get; set; }
+ public string? Format_long_name { get; set; }
+
+ [JsonConverter(typeof(FlexibleDoubleConverter))]
+ public double? Start_time { get; set; }
+
+ [JsonConverter(typeof(FlexibleDoubleConverter))]
+ public double? Duration { get; set; }
+
+ [JsonConverter(typeof(FlexibleLongConverter))]
+ public long? Size { get; set; }
+
+ [JsonConverter(typeof(FlexibleLongConverter))]
+ public long? Bit_rate { get; set; }
+
+ [JsonConverter(typeof(FlexibleIntConverter))]
+ public int? Probe_score { get; set; }
+
+ public Dictionary? Tags { get; set; }
+}
diff --git a/splitter-cli/FfprobeResult.cs b/splitter-cli/probe/FfprobeResult.cs
similarity index 76%
rename from splitter-cli/FfprobeResult.cs
rename to splitter-cli/probe/FfprobeResult.cs
index db930bd..bab6049 100644
--- a/splitter-cli/FfprobeResult.cs
+++ b/splitter-cli/probe/FfprobeResult.cs
@@ -1,9 +1,9 @@
using System;
using System.Collections.Generic;
using System.Text;
-using static splitter.ProbeVideo;
+using static splitter.probe.ProbeVideo;
-namespace splitter;
+namespace splitter.probe;
public sealed class FfprobeResult
{
diff --git a/splitter-cli/probe/FfprobeStream.cs b/splitter-cli/probe/FfprobeStream.cs
new file mode 100644
index 0000000..d1348f1
--- /dev/null
+++ b/splitter-cli/probe/FfprobeStream.cs
@@ -0,0 +1,129 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Text.Json.Serialization;
+
+namespace splitter.probe;
+
+public sealed class FfprobeStream
+{
+ [JsonConverter(typeof(FlexibleIntConverter))]
+ public int? Index { get; set; }
+
+ public string? Codec_name { get; set; }
+ public string? Codec_long_name { get; set; }
+ public string? Profile { get; set; }
+ public string? Codec_type { get; set; }
+ public string? Codec_tag_string { get; set; }
+ public string? Codec_tag { get; set; }
+ public string? Mime_codec_string { get; set; }
+
+ [JsonConverter(typeof(FlexibleIntConverter))]
+ public int? Width { get; set; }
+
+ [JsonConverter(typeof(FlexibleIntConverter))]
+ public int? Height { get; set; }
+
+ [JsonConverter(typeof(FlexibleIntConverter))]
+ public int? Coded_width { get; set; }
+
+ [JsonConverter(typeof(FlexibleIntConverter))]
+ public int? Coded_height { get; set; }
+
+ [JsonConverter(typeof(FlexibleIntConverter))]
+ public int? Closed_captions { get; set; }
+
+ [JsonConverter(typeof(FlexibleIntConverter))]
+ public int? Film_grain { get; set; }
+
+ [JsonConverter(typeof(FlexibleIntConverter))]
+ public int? Has_b_frames { get; set; }
+
+ public string? Sample_aspect_ratio { get; set; }
+ public string? Display_aspect_ratio { get; set; }
+
+ public string? Pix_fmt { get; set; }
+
+ [JsonConverter(typeof(FlexibleIntConverter))]
+ public int? Level { get; set; }
+
+ public string? Color_range { get; set; }
+ public string? Color_space { get; set; }
+ public string? Color_transfer { get; set; }
+ public string? Color_primaries { get; set; }
+ public string? Chroma_location { get; set; }
+ public string? Field_order { get; set; }
+
+ public string? Is_avc { get; set; }
+ public string? Nal_length_size { get; set; }
+
+ public string? Id { get; set; }
+ public string? R_frame_rate { get; set; }
+ public string? Avg_frame_rate { get; set; }
+ public string? Time_base { get; set; }
+
+ [JsonConverter(typeof(FlexibleLongConverter))]
+ public long? Start_pts { get; set; }
+
+ [JsonConverter(typeof(FlexibleDoubleConverter))]
+ public double? Start_time { get; set; }
+
+ [JsonConverter(typeof(FlexibleLongConverter))]
+ public long? Duration_ts { get; set; }
+
+ [JsonConverter(typeof(FlexibleDoubleConverter))]
+ public double? Duration { get; set; }
+
+ [JsonConverter(typeof(FlexibleLongConverter))]
+ public long? Bit_rate { get; set; }
+
+ [JsonConverter(typeof(FlexibleLongConverter))]
+ public long? Max_bit_rate { get; set; }
+
+ [JsonConverter(typeof(FlexibleIntConverter))]
+ public int? Bits_per_raw_sample { get; set; }
+
+ [JsonConverter(typeof(FlexibleIntConverter))]
+ public int? Bits_per_sample { get; set; }
+
+ [JsonConverter(typeof(FlexibleLongConverter))]
+ public long? Nb_frames { get; set; }
+
+ [JsonConverter(typeof(FlexibleIntConverter))]
+ public int? Extradata_size { get; set; }
+
+ [JsonConverter(typeof(FlexibleIntConverter))]
+ public int? Channels { get; set; }
+
+ public string? Channel_layout { get; set; }
+ public string? Sample_fmt { get; set; }
+
+ [JsonConverter(typeof(FlexibleIntConverter))]
+ public int? Sample_rate { get; set; }
+
+ [JsonConverter(typeof(FlexibleIntConverter))]
+ public int? Initial_padding { get; set; }
+
+ public string? Disposition_raw { get; set; }
+ public Dictionary? Disposition { get; set; }
+
+ public Dictionary? Tags { get; set; }
+
+ public string? Language { get; set; }
+ public string? Title { get; set; }
+
+ [JsonConverter(typeof(FlexibleIntConverter))]
+ public int? Bits_per_coded_sample { get; set; }
+
+ public string? Codec_time_base { get; set; }
+
+ [JsonConverter(typeof(FlexibleDoubleConverter))]
+ public double? Start_pts_time { get; set; }
+
+ [JsonConverter(typeof(FlexibleDoubleConverter))]
+ public double? Duration_time { get; set; }
+
+ public string? Extradata { get; set; }
+ public string? Default { get; set; }
+ public string? Forced { get; set; }
+}
diff --git a/splitter-cli/FlexibleDoubleConverter.cs b/splitter-cli/probe/FlexibleDoubleConverter.cs
similarity index 97%
rename from splitter-cli/FlexibleDoubleConverter.cs
rename to splitter-cli/probe/FlexibleDoubleConverter.cs
index 5622071..456a098 100644
--- a/splitter-cli/FlexibleDoubleConverter.cs
+++ b/splitter-cli/probe/FlexibleDoubleConverter.cs
@@ -2,7 +2,7 @@
using System.Text.Json;
using System.Text.Json.Serialization;
-namespace splitter;
+namespace splitter.probe;
public sealed class FlexibleDoubleConverter : JsonConverter
{
diff --git a/splitter-cli/FlexibleIntConverter.cs b/splitter-cli/probe/FlexibleIntConverter.cs
similarity index 97%
rename from splitter-cli/FlexibleIntConverter.cs
rename to splitter-cli/probe/FlexibleIntConverter.cs
index ba29204..805ebe3 100644
--- a/splitter-cli/FlexibleIntConverter.cs
+++ b/splitter-cli/probe/FlexibleIntConverter.cs
@@ -2,7 +2,7 @@
using System.Text.Json;
using System.Text.Json.Serialization;
-namespace splitter;
+namespace splitter.probe;
public sealed class FlexibleIntConverter : JsonConverter
{
diff --git a/splitter-cli/FlexibleLongConverter.cs b/splitter-cli/probe/FlexibleLongConverter.cs
similarity index 97%
rename from splitter-cli/FlexibleLongConverter.cs
rename to splitter-cli/probe/FlexibleLongConverter.cs
index a0c2423..b44c496 100644
--- a/splitter-cli/FlexibleLongConverter.cs
+++ b/splitter-cli/probe/FlexibleLongConverter.cs
@@ -2,7 +2,7 @@
using System.Text.Json;
using System.Text.Json.Serialization;
-namespace splitter;
+namespace splitter.probe;
public sealed class FlexibleLongConverter : JsonConverter
{
diff --git a/splitter-cli/FrameRotationDetector.cs b/splitter-cli/probe/FrameRotationDetector.cs
similarity index 85%
rename from splitter-cli/FrameRotationDetector.cs
rename to splitter-cli/probe/FrameRotationDetector.cs
index 1350740..e5f62df 100644
--- a/splitter-cli/FrameRotationDetector.cs
+++ b/splitter-cli/probe/FrameRotationDetector.cs
@@ -1,6 +1,6 @@
using OpenCvSharp;
-namespace splitter;
+namespace splitter.probe;
public sealed class FrameRotationDetector
{
@@ -18,17 +18,17 @@ public sealed class FrameRotationDetector
public FrameRotationDetector(int width = 320, int height = 180, int bins = 36)
{
- _w = width;
- _h = height;
- _bins = bins;
+ _w = width;
+ _h = height;
+ _bins = bins;
- _gray = new Mat(height, width, MatType.CV_8UC1);
- _gx = new Mat(height, width, MatType.CV_32F);
- _gy = new Mat(height, width, MatType.CV_32F);
- _mag = new Mat(height, width, MatType.CV_32F);
+ _gray = new Mat(height, width, MatType.CV_8UC1);
+ _gx = new Mat(height, width, MatType.CV_32F);
+ _gy = new Mat(height, width, MatType.CV_32F);
+ _mag = new Mat(height, width, MatType.CV_32F);
_angle = new Mat(height, width, MatType.CV_32F);
- _hist = new float[bins]; // allocated once
+ _hist = new float[bins]; // allocated once
}
public int GetRotation(Mat frame)
diff --git a/splitter-cli/ProbeVideo.cs b/splitter-cli/probe/ProbeVideo.cs
similarity index 88%
rename from splitter-cli/ProbeVideo.cs
rename to splitter-cli/probe/ProbeVideo.cs
index f19ca39..842da42 100644
--- a/splitter-cli/ProbeVideo.cs
+++ b/splitter-cli/probe/ProbeVideo.cs
@@ -2,8 +2,9 @@
using System.Globalization;
using System.Text.Json;
using System.Text.Json.Serialization;
+using splitter.algo;
-namespace splitter;
+namespace splitter.probe;
public static class ProbeVideo
{
@@ -14,23 +15,20 @@ public static class ProbeVideo
_ffprobeJsonOptions.Converters.Add(new FlexibleLongConverter());
}
- public static async Task Probe(SingleJob job)
+ public static async Task Probe(string inputFile, bool detectRotation)
{
- var info = ProbeSize(job.InputFile);
- if ( job.RotateAuto)
+ var info = ProbeSize(inputFile);
+ if (detectRotation)
{
- var rotation = await ProbeRotation(job, info.Duration);
+ var rotation = await ProbeRotation(inputFile, info.Duration);
info = info with { Rotation = rotation };
}
return info;
}
- private static async Task ProbeRotation(SingleJob job, double duration)
- {
- var rotation = await new VideoRotationSampler(job).DetectRotationAsync(job.InputFile, duration);
- return rotation;
- }
+ private static async Task ProbeRotation(string inputFile, double duration)
+ => await new VideoRotationSampler(null).DetectRotationAsync(inputFile, duration);
private static readonly JsonSerializerOptions _ffprobeJsonOptions =
new ()
diff --git a/splitter-cli/VideoInfo.cs b/splitter-cli/probe/VideoInfo.cs
similarity index 71%
rename from splitter-cli/VideoInfo.cs
rename to splitter-cli/probe/VideoInfo.cs
index 60743eb..b1525c9 100644
--- a/splitter-cli/VideoInfo.cs
+++ b/splitter-cli/probe/VideoInfo.cs
@@ -1,4 +1,7 @@
-namespace splitter;
+using OpenCvSharp;
+using splitter.algo;
+
+namespace splitter.probe;
public record VideoInfo(
double Duration,
diff --git a/splitter-cli/VideoRotationSampler.cs b/splitter-cli/probe/VideoRotationSampler.cs
similarity index 86%
rename from splitter-cli/VideoRotationSampler.cs
rename to splitter-cli/probe/VideoRotationSampler.cs
index 7fa3ce9..8337af5 100644
--- a/splitter-cli/VideoRotationSampler.cs
+++ b/splitter-cli/probe/VideoRotationSampler.cs
@@ -1,7 +1,7 @@
using OpenCvSharp;
using System.Diagnostics;
-namespace splitter;
+namespace splitter.probe;
public sealed class VideoRotationSampler
{
@@ -16,16 +16,19 @@ public sealed class VideoRotationSampler
private readonly byte[] _buffer;
private readonly Mat _frameMat;
- public VideoRotationSampler(SingleJob _master)
+ public VideoRotationSampler(IDictionary? overrides)
{
- if (_master.Parameters.TryGetValue("RotationDetectorSampleCount", out var s))
- RotationDetectorSampleCount = int.Parse(s);
- if (_master.Parameters.TryGetValue("RotationDetectorSampleLength", out s))
- RotationDetectorSampleLength = double.Parse(s);
- if (_master.Parameters.TryGetValue("RotationDetectorFrameWidth", out s))
- RotationDetectorFrameWidth = int.Parse(s);
- if (_master.Parameters.TryGetValue("RotationDetectorFrameHeight", out s))
- RotationDetectorFrameHeight = int.Parse(s);
+ if (overrides != null)
+ {
+ if (overrides.TryGetValue("RotationDetectorSampleCount", out var s))
+ RotationDetectorSampleCount = int.Parse(s);
+ if (overrides.TryGetValue("RotationDetectorSampleLength", out s))
+ RotationDetectorSampleLength = double.Parse(s);
+ if (overrides.TryGetValue("RotationDetectorFrameWidth", out s))
+ RotationDetectorFrameWidth = int.Parse(s);
+ if (overrides.TryGetValue("RotationDetectorFrameHeight", out s))
+ RotationDetectorFrameHeight = int.Parse(s);
+ }
int w = RotationDetectorFrameWidth;
int h = RotationDetectorFrameHeight;
diff --git a/splitter-cli/splitter.cs b/splitter-cli/splitter.cs
index 5d8d1e4..f7d5413 100644
--- a/splitter-cli/splitter.cs
+++ b/splitter-cli/splitter.cs
@@ -2,6 +2,9 @@ using System.Collections.Concurrent;
using System.Diagnostics;
using Spectre.Console;
using splitter;
+using splitter.algo;
+using splitter.probe;
+using splitter.tui;
static partial class Program
{
@@ -76,7 +79,7 @@ static partial class Program
if (!Directory.Exists(job.OutputFolder))
Directory.CreateDirectory(job.OutputFolder);
- var info = await ProbeVideo.Probe(job);
+ var info = await ProbeVideo.Probe(job.InputFile, job.RotateAuto);
if (info.Duration <= 0)
{
LogError($"{baseName}: Could not read duration.");
diff --git a/splitter-cli/splitter.csproj b/splitter-cli/splitter.csproj
index a99264e..d9bf3ef 100644
--- a/splitter-cli/splitter.csproj
+++ b/splitter-cli/splitter.csproj
@@ -50,7 +50,6 @@
-
PreserveNewest
diff --git a/splitter-cli/ILogger.cs b/splitter-cli/tui/ILogger.cs
similarity index 95%
rename from splitter-cli/ILogger.cs
rename to splitter-cli/tui/ILogger.cs
index 3eaf793..a546c6e 100644
--- a/splitter-cli/ILogger.cs
+++ b/splitter-cli/tui/ILogger.cs
@@ -1,4 +1,4 @@
-namespace splitter;
+namespace splitter.tui;
public interface ILogger
{
diff --git a/splitter-cli/LoggingBase.cs b/splitter-cli/tui/LoggingBase.cs
similarity index 96%
rename from splitter-cli/LoggingBase.cs
rename to splitter-cli/tui/LoggingBase.cs
index ca27ea7..0c230f4 100644
--- a/splitter-cli/LoggingBase.cs
+++ b/splitter-cli/tui/LoggingBase.cs
@@ -1,4 +1,4 @@
-namespace splitter;
+namespace splitter.tui;
public abstract class LoggingBase(ILogger _logger, int _progressLine)
{
diff --git a/splitter-cli/SpectreConsoleLogger.cs b/splitter-cli/tui/SpectreConsoleLogger.cs
similarity index 99%
rename from splitter-cli/SpectreConsoleLogger.cs
rename to splitter-cli/tui/SpectreConsoleLogger.cs
index 595cfe9..ca9a535 100644
--- a/splitter-cli/SpectreConsoleLogger.cs
+++ b/splitter-cli/tui/SpectreConsoleLogger.cs
@@ -2,7 +2,7 @@
using Spectre.Console;
using Spectre.Console.Rendering;
-namespace splitter;
+namespace splitter.tui;
///
diff --git a/splitter-cli/TextLogger.cs b/splitter-cli/tui/TextLogger.cs
similarity index 94%
rename from splitter-cli/TextLogger.cs
rename to splitter-cli/tui/TextLogger.cs
index 8651a9d..4cbe09d 100644
--- a/splitter-cli/TextLogger.cs
+++ b/splitter-cli/tui/TextLogger.cs
@@ -1,4 +1,4 @@
-namespace splitter;
+namespace splitter.tui;
public class TextLogger() : ILogger
{
diff --git a/splitter-cli/FileMaskExpander.cs b/splitter-cli/util/FileMaskExpander.cs
similarity index 95%
rename from splitter-cli/FileMaskExpander.cs
rename to splitter-cli/util/FileMaskExpander.cs
index 31dec18..e51d539 100644
--- a/splitter-cli/FileMaskExpander.cs
+++ b/splitter-cli/util/FileMaskExpander.cs
@@ -1,4 +1,4 @@
-namespace splitter;
+namespace splitter.util;
public static class FileMaskExpander
{
diff --git a/splitter-cli/SingleTask.cs b/splitter-cli/util/SingleTask.cs
similarity index 86%
rename from splitter-cli/SingleTask.cs
rename to splitter-cli/util/SingleTask.cs
index 770780f..e4c8b36 100644
--- a/splitter-cli/SingleTask.cs
+++ b/splitter-cli/util/SingleTask.cs
@@ -1,4 +1,6 @@
using splitter;
+using splitter.algo;
+using splitter.probe;
public record SingleTask(
SingleJob Job,