mirror of
https://github.com/unclshura/splitter.git
synced 2026-06-21 16:12:01 +00:00
Major structure refactoring
This commit is contained in:
parent
a408d43b61
commit
417d511bc8
@ -1,122 +1,9 @@
|
||||
using System.Globalization;
|
||||
using splitter.algo;
|
||||
using splitter.util;
|
||||
|
||||
namespace splitter;
|
||||
|
||||
public class SingleJob
|
||||
{
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public string InputFile { get; set; } = null!;
|
||||
/// <summary>
|
||||
/// Output folder where the split segments will be saved. This should be set
|
||||
/// to a valid directory path.
|
||||
/// </summary>
|
||||
public string OutputFolder { get; set; } = null!;
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public (int width, int height)? Crop { get; set; }
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public Point2f? GravitateTo { get; set; }
|
||||
/// <summary>
|
||||
/// Destination file mask.
|
||||
/// </summary>
|
||||
public string? Mask { get; set; }
|
||||
/// <summary>
|
||||
/// Instead of producing the output, just generate debug frames with tracking
|
||||
/// overlay to visually verify that the tracking is working correctly.
|
||||
/// </summary>
|
||||
public bool Debug { get; set; }
|
||||
/// <summary>
|
||||
/// Type of detector to use for tracking. Supported values are: face (UltraFace),
|
||||
/// body (YoloOnnx, default), none (no tracking, just a center point).
|
||||
/// </summary>
|
||||
public string? Detect { get; set; }
|
||||
/// <summary>
|
||||
/// Set starget segments length explicitly. By default, the splitter calculates segment
|
||||
/// lengths to be equal and not exceed 58 seconds.
|
||||
/// </summary>
|
||||
public double? OverrideTargetDuration { get; set; }
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public string[] Passthrough { get; set; } = [];
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public bool PlainText { get; set; }
|
||||
/// <summary>
|
||||
/// Debugging parameter. Just show estimated segments length, count, and other info
|
||||
/// without actually performing the splitting.
|
||||
/// </summary>
|
||||
public bool EstimateOnly { get; set; }
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public bool ForceFixed { get; set; }
|
||||
/// <summary>
|
||||
/// Use single thread for operations. When set, the splitter will not run
|
||||
/// multiple ffmpeg processes in parallel.
|
||||
/// </summary>
|
||||
public bool SingleThreaded { get; set; }
|
||||
/// <summary>
|
||||
/// Rotation angle: 90, 180, or 270 degrees. This is useful for videos that
|
||||
/// have incorrect orientation metadata.
|
||||
/// </summary>
|
||||
public int? Rotate { get; set; }
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public bool RotateAuto { get; set; }
|
||||
/// <summary>
|
||||
/// Override internal parameters. This allows you to set custom parameters for the
|
||||
/// object detector or rotation detector.
|
||||
/// </summary>
|
||||
public Dictionary<string, string> Parameters { get; set; } = [];
|
||||
|
||||
public void Override<T>(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();
|
||||
|
||||
@ -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<string, string>? Tags { get; set; }
|
||||
}
|
||||
@ -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<string, int>? Disposition { get; set; }
|
||||
|
||||
public Dictionary<string, string>? 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; }
|
||||
}
|
||||
5
splitter-cli/GlobalUsing.cs
Normal file
5
splitter-cli/GlobalUsing.cs
Normal file
@ -0,0 +1,5 @@
|
||||
global using OpenCvSharp;
|
||||
global using splitter.algo;
|
||||
global using splitter.probe;
|
||||
global using splitter.tui;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,7 @@
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using splitter.algo;
|
||||
using splitter.tui;
|
||||
|
||||
namespace splitter;
|
||||
|
||||
|
||||
119
splitter-cli/SingleJob.cs
Normal file
119
splitter-cli/SingleJob.cs
Normal file
@ -0,0 +1,119 @@
|
||||
using System.Globalization;
|
||||
using splitter.algo;
|
||||
|
||||
namespace splitter;
|
||||
|
||||
public class SingleJob
|
||||
{
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public string InputFile { get; set; } = null!;
|
||||
/// <summary>
|
||||
/// Output folder where the split segments will be saved. This should be set
|
||||
/// to a valid directory path.
|
||||
/// </summary>
|
||||
public string OutputFolder { get; set; } = null!;
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public (int width, int height)? Crop { get; set; }
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public Point2f? GravitateTo { get; set; }
|
||||
/// <summary>
|
||||
/// Destination file mask.
|
||||
/// </summary>
|
||||
public string? Mask { get; set; }
|
||||
/// <summary>
|
||||
/// Instead of producing the output, just generate debug frames with tracking
|
||||
/// overlay to visually verify that the tracking is working correctly.
|
||||
/// </summary>
|
||||
public bool Debug { get; set; }
|
||||
/// <summary>
|
||||
/// Type of detector to use for tracking. Supported values are: face (UltraFace),
|
||||
/// body (YoloOnnx, default), none (no tracking, just a center point).
|
||||
/// </summary>
|
||||
public string? Detect { get; set; }
|
||||
/// <summary>
|
||||
/// Set starget segments length explicitly. By default, the splitter calculates segment
|
||||
/// lengths to be equal and not exceed 58 seconds.
|
||||
/// </summary>
|
||||
public double? OverrideTargetDuration { get; set; }
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public string[] Passthrough { get; set; } = [];
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public bool PlainText { get; set; }
|
||||
/// <summary>
|
||||
/// Debugging parameter. Just show estimated segments length, count, and other info
|
||||
/// without actually performing the splitting.
|
||||
/// </summary>
|
||||
public bool EstimateOnly { get; set; }
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public bool ForceFixed { get; set; }
|
||||
/// <summary>
|
||||
/// Use single thread for operations. When set, the splitter will not run
|
||||
/// multiple ffmpeg processes in parallel.
|
||||
/// </summary>
|
||||
public bool SingleThreaded { get; set; }
|
||||
/// <summary>
|
||||
/// Rotation angle: 90, 180, or 270 degrees. This is useful for videos that
|
||||
/// have incorrect orientation metadata.
|
||||
/// </summary>
|
||||
public int? Rotate { get; set; }
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public bool RotateAuto { get; set; }
|
||||
/// <summary>
|
||||
/// Override internal parameters. This allows you to set custom parameters for the
|
||||
/// object detector or rotation detector.
|
||||
/// </summary>
|
||||
public Dictionary<string, string> Parameters { get; set; } = [];
|
||||
|
||||
public void Override<T>(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}");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -2,6 +2,8 @@
|
||||
using System.Globalization;
|
||||
using System.Runtime.InteropServices;
|
||||
using OpenCvSharp;
|
||||
using splitter.algo;
|
||||
using splitter.tui;
|
||||
|
||||
namespace splitter;
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
using OpenCvSharp;
|
||||
|
||||
namespace splitter;
|
||||
namespace splitter.algo;
|
||||
|
||||
public enum TrackState
|
||||
{
|
||||
@ -1,6 +1,6 @@
|
||||
using OpenCvSharp;
|
||||
|
||||
namespace splitter;
|
||||
namespace splitter.algo;
|
||||
|
||||
public interface IObjectDetector : IDisposable
|
||||
{
|
||||
@ -1,4 +1,4 @@
|
||||
namespace splitter;
|
||||
namespace splitter.algo;
|
||||
|
||||
public interface ISegmentProcessor
|
||||
{
|
||||
@ -1,4 +1,4 @@
|
||||
namespace splitter;
|
||||
namespace splitter.algo;
|
||||
|
||||
public sealed class KalmanTracker
|
||||
{
|
||||
13
splitter-cli/algo/Point2f.cs
Normal file
13
splitter-cli/algo/Point2f.cs
Normal file
@ -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;
|
||||
// }
|
||||
//}
|
||||
@ -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
|
||||
{
|
||||
@ -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
|
||||
{
|
||||
40
splitter-cli/probe/FfprobeFormat.cs
Normal file
40
splitter-cli/probe/FfprobeFormat.cs
Normal file
@ -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<string, string>? Tags { get; set; }
|
||||
}
|
||||
@ -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
|
||||
{
|
||||
129
splitter-cli/probe/FfprobeStream.cs
Normal file
129
splitter-cli/probe/FfprobeStream.cs
Normal file
@ -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<string, int>? Disposition { get; set; }
|
||||
|
||||
public Dictionary<string, string>? 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; }
|
||||
}
|
||||
@ -2,7 +2,7 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace splitter;
|
||||
namespace splitter.probe;
|
||||
|
||||
public sealed class FlexibleDoubleConverter : JsonConverter<double?>
|
||||
{
|
||||
@ -2,7 +2,7 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace splitter;
|
||||
namespace splitter.probe;
|
||||
|
||||
public sealed class FlexibleIntConverter : JsonConverter<int?>
|
||||
{
|
||||
@ -2,7 +2,7 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace splitter;
|
||||
namespace splitter.probe;
|
||||
|
||||
public sealed class FlexibleLongConverter : JsonConverter<long?>
|
||||
{
|
||||
@ -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)
|
||||
@ -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<VideoInfo> Probe(SingleJob job)
|
||||
public static async Task<VideoInfo> 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<int> ProbeRotation(SingleJob job, double duration)
|
||||
{
|
||||
var rotation = await new VideoRotationSampler(job).DetectRotationAsync(job.InputFile, duration);
|
||||
return rotation;
|
||||
}
|
||||
private static async Task<int> ProbeRotation(string inputFile, double duration)
|
||||
=> await new VideoRotationSampler(null).DetectRotationAsync(inputFile, duration);
|
||||
|
||||
private static readonly JsonSerializerOptions _ffprobeJsonOptions =
|
||||
new ()
|
||||
@ -1,4 +1,7 @@
|
||||
namespace splitter;
|
||||
using OpenCvSharp;
|
||||
using splitter.algo;
|
||||
|
||||
namespace splitter.probe;
|
||||
|
||||
public record VideoInfo(
|
||||
double Duration,
|
||||
@ -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<string, string>? 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;
|
||||
@ -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.");
|
||||
|
||||
@ -50,7 +50,6 @@
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="ThisAssembly.g.cs" />
|
||||
|
||||
<Content Include="models/*.*">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
namespace splitter;
|
||||
namespace splitter.tui;
|
||||
|
||||
public interface ILogger
|
||||
{
|
||||
@ -1,4 +1,4 @@
|
||||
namespace splitter;
|
||||
namespace splitter.tui;
|
||||
|
||||
public abstract class LoggingBase(ILogger _logger, int _progressLine)
|
||||
{
|
||||
@ -2,7 +2,7 @@
|
||||
using Spectre.Console;
|
||||
using Spectre.Console.Rendering;
|
||||
|
||||
namespace splitter;
|
||||
namespace splitter.tui;
|
||||
|
||||
|
||||
/// <summary>
|
||||
@ -1,4 +1,4 @@
|
||||
namespace splitter;
|
||||
namespace splitter.tui;
|
||||
|
||||
public class TextLogger() : ILogger
|
||||
{
|
||||
@ -1,4 +1,4 @@
|
||||
namespace splitter;
|
||||
namespace splitter.util;
|
||||
|
||||
public static class FileMaskExpander
|
||||
{
|
||||
@ -1,4 +1,6 @@
|
||||
using splitter;
|
||||
using splitter.algo;
|
||||
using splitter.probe;
|
||||
|
||||
public record SingleTask(
|
||||
SingleJob Job,
|
||||
Loading…
x
Reference in New Issue
Block a user