README updated. Fixed bug with OutputFolder.

This commit is contained in:
Alexander Shabarshov 2026-05-16 09:06:35 +01:00
parent a1371ef6ae
commit 1fd938416e
5 changed files with 144 additions and 125 deletions

261
README.md
View File

@ -1,191 +1,202 @@
# Splitter
Splitter is a command line tool for cutting video files into equal length or fixed length segments using FFmpeg.
It supports multi threaded splitting, ETA and speed reporting, flexible duration formats, and a simple terminal user interface.
Splitter is a highperformance command line tool for cutting one or more video files into equal or fixedlength segments using multithreaded FFmpeg execution.
It supports batch input, flexible duration formats, rotation, smart face/bodyaware cropping, ETA and speed reporting, and both rich and plaintext terminal output.
This tool is intended for creators, archivists, and automation workflows that need fast and predictable video segmentation.
---
![Splitter](splitter.png)
## Features
- Multi threaded splitting for high performance
- Equal length segments (default)
- Fixed length segments with the `--force` option
- Flexible duration formats (`Ns`, `NmMs`, or plain seconds)
- Estimate only mode (`--estimate`)
- Terminal progress display with ETA and speed
- Custom output filename masks
- FFmpeg passthrough for advanced users
- Cross platform (.NET 10)
---
- Multithreaded FFmpeg splitting for maximum throughput
- Equal or fixedlength segmentation
- Batch input via file masks or list files
- Smart cropping with face/body tracking
- Rotation correction
- ETA, speed, and progress display
- FFmpeg passthrough for advanced control
- [Potentially] Crossplatform (.NET 10)
## Requirements
- FFmpeg and FFprobe installed and available in the system PATH
- .NET 10 SDK or newer
- FFmpeg and FFprobe available in system PATH
- .NET 10 Runtime or newer
>Note: You must download YuNet ONNX model:
https://github.com/opencv/opencv_zoo/tree/master/models/face_detection_yunet (github.com in Bing)
If you want to update model:
- For face detection: [opencv_zoo/models/face_detection_yunet at main · opencv/opencv_zoo](https://github.com/opencv/opencv_zoo/tree/main/models/face_detection_yunet)
- For body detection: [yolov8s.pt · Ultralytics/YOLOv8 at main](https://huggingface.co/Ultralytics/YOLOv8/blob/main/yolov8s.pt)
To convert models from PyTorch to ONNX, you can use the following command:
```python
from ultralytics import YOLO
model = YOLO("yolov8x.pt")
model.export(format="onnx", opset=12, half=False) # FP32 ONNX
```
## How It Works
1. Reads total duration using ffprobe
2. Parses target duration
3. Computes number of segments
4. If not forced, equalizes segment lengths
5. Runs multiple FFmpeg processes in parallel
6. Applies rotation, crop, and tracking if enabled
7. Displays progress, ETA, and speed
---
## Face Tracking vs Body Tracking
Face tracking and body tracking serve different purposes, and Splitter supports both because each
excels in different recording environments. When converting horizontal footage into vertical clips,
the choice of detector determines how stable, reliable, and natural the automated camera motion will be.
![Face vs Body Tracking](tracking.png)
### Face Tracking Using UltraFace 320
Splitter uses the UltraFace 320 ONNX model to perform lightweight, realtime face detection on each
frame of the input video. The detector produces bounding boxes for visible faces, and the tracking
system maintains a stable, smoothed target region across time. This is achieved by combining perframe
detections with temporal smoothing (EMA), dropout tolerance, and camera easing. The result is a
continuous, stable crop window that follows the performer even when the face is partially occluded,
briefly lost, or moving rapidly.
During segmentation, the crop window is recalculated for every frame, ensuring that each output
segment inherits the same smooth camera motion. This makes the vertical clips appear as if they
were recorded with a dedicated portraitoriented camera operator. The UltraFace 320 model is
fast enough to run alongside multithreaded FFmpeg splitting without becoming a bottleneck,
making it suitable for long recordings and batch processing.
### Benefits of FullBody Detection Using YOLOv8s for Live Gig Recordings
When recording concerts or live gigs, performers often move unpredictably, turn away from the
camera, or become partially obscured by lighting, instruments, or stage effects.
Fullbody detection using a YOLOv8s ONNX model provides a more reliable tracking anchor than
face detection alone. Because YOLOv8s can detect the entire human silhouette, the tracker
maintains stable framing even when the face is not visible, when the performer is far from
the camera, or when stage lighting makes facial features hard to detect. This produces vertical
clips that feel intentional and professionally framed, with fewer sudden jumps or losttracking
moments. For creators converting horizontal gig footage into short vertical clips for YouTube
Shorts or TikTok, bodybased tracking significantly improves consistency, reduces manual editing,
and preserves the energy and motion of the performance.
### Automated Camera Control
Splitter includes an automated camera control system that simulates the behavior of a virtual
camera operator when generating vertical crops from horizontal footage. The goal is to maintain
smooth, intentional framing around the tracked subject, even when detections are noisy, intermittent,
or temporarily lost.
The controller receives object detections (face or body) and converts them into a stable crop
window using a combination of Kalman filtering, exponential smoothing, dropout tolerance,
and a threestate tracking model. The Kalman filter provides predictive motion smoothing,
while the EMA factor blends the predicted position with the previous camera center to avoid jitter.
The camera easing value controls how quickly the virtual camera follows the subject, producing
naturallooking motion rather than abrupt jumps.
When detections disappear, the controller enters one of two fallback modes. In LostFreeze mode,
the camera holds its last known position for a configurable number of frames, preventing sudden
jumps during brief occlusions. If the subject remains lost beyond that threshold, the controller
transitions to LostDrift mode, slowly drifting the camera back toward a neutral center position.
This prevents the crop from drifting offscreen and ensures that the output remains usable even
when tracking fails. All positions are clamped to valid bounds, guaranteeing that the crop window
never leaves the video frame.
---
## Usage
```
splitter <input.mp4> <output_folder> [options] [--] <ffmpeg passthrough>
splitter [<input.mp4> ...] [options] [--] <ffmpeg passthrough>
```
Inputs may be provided directly, via `--file=...`, or using file masks such as `videos/*.mp4`.
---
## Options
### --duration=<value>
Overrides the default 60 second target chunk size.
Accepted formats:
| Format | Meaning |
|--------|---------|
| Ns | N seconds |
| NmMs | N minutes and M seconds |
| N | N seconds (plain number) |
Examples:
```
--duration=90s
--duration=2m30s
--duration=45
```
Without `--force`, the program adjusts the segment length so that all segments are equal.
Below is a clean, ASCIIonly **options table** version of your content.
All option names are preserved exactly, and descriptions are consolidated for clarity.
---
### --force
## Options
Uses the duration exactly as provided.
The last segment may be shorter.
Example:
```
--duration=45 --force
```
| Option | Description |
|--------|-------------|
| **--out=<folder>** | Output folder for generated segments. Default: `<input folder>/Splitter`. |
| **--file=<path>** | Input file list or file mask. If omitted, the first nonoption argument is used as input. Examples: `--file=videos/*.mp4`, `--file=file_list.txt`. |
| **--mask=<pattern>** | Custom output filename pattern. Default: `[NAME]_seg[NN].[EXT]`. Supports `[NAME]`, `[N]`, `[NN]`, `[NNN]`, `[NNNN]`, `[EXT]`. Example: `--mask="[NAME]_[NNNN].mp4"`. |
| **--duration=<value>** | Override target segment duration. Formats: `Ns`, `NmMs`, `N`. Examples: `--duration=90s`, `--duration=2m30s`, `--duration=45`. Without `--force`: max 58 seconds, equalized across segments. |
| **--force** | Use the duration exactly as provided. Last segment may be shorter. |
| **--rotate=<degrees>** | Rotate video by 90, 180, or 270 degrees. Useful for correcting orientation metadata. |
| **--estimate** | Print calculated segment information and exit. No splitting is performed. |
| **--crop[=<w:h>]** | Crop video to a target width and height with face/body tracking. Default: 607x1080. Ideal for Shorts, TikTok, Reels. |
| **--detect=<name>** | Object detector for tracking. Values: `face` (UltraFace), `body` (YoloOnnx, default), `none` (center crop). |
| **--gravitate=<x:y>** | Bias the crop window toward a normalized point in the frame. Example: `--gravitate=0.2:0.5`. |
| **--text** | Use plaintext logging instead of the rich terminal UI. |
| **--single-thread** | Disable parallel FFmpeg execution. Useful for debugging or lowresource systems. |
| **--debug** | Show debug overlay during tracking. No cropping performed, but crop region shown. |
| **-p:<name>=<value>** | Set custom parameters for the object detector. Example: `-p:confidence=0.5`. Defaults: DropoutToleranceFrames=20, EmaFactor=0.65, CameraEasing=0.03, LostFreezeFrames=60. |
---
### --estimate
Prints calculated segment information and exits.
No splitting is performed.
Example:
```
splitter video.mp4 out/ --duration=2m30s --estimate
```
---
### --mask=<pattern>
Custom output filename pattern.
Default:
```
<OriginalName>_Seg%03d.mp4
```
Supports:
- `%03d` for zero padded index
- `%d` for plain index
Example:
```
--mask="Part_%03d.mp4"
```
---
### FFmpeg passthrough
## FFmpeg Passthrough
Anything after `--` is passed directly to FFmpeg.
Example:
```
splitter video.mp4 out/ -- -an -sn
splitter video.mp4 --force --duration=45 -- -an -sn
```
---
## How It Works
## Input and Output Behavior
1. Reads total duration using ffprobe
2. Parses the target duration (default 60 seconds or from `--duration=`)
3. Computes the number of segments
4. If not forced, adjusts segment length so all segments are equal
5. Runs multiple FFmpeg processes in parallel
6. Displays progress, ETA, and speed in the terminal
- `input.mp4` may be a file mask (`videos/*.mp4`)
- Output filenames follow the `--mask` pattern
- Output folder defaults to `<input folder>/Splitter` unless overridden
---
## Examples
Split into equal 60 second segments:
Split into equal 60second segments:
```
splitter video.mp4 out/
splitter vertical-video.mp4
```
Split into equal 90 second segments:
Split into equal 90second segments:
```
splitter video.mp4 out/ --duration=90s
splitter vertical-video.mp4 --duration=90s
```
Split into fixed 45 second segments:
Custom naming:
```
splitter video.mp4 out/ --duration=45 --force
splitter vertical-video.mp4 --duration=2m30s --mask="[NAME]_[NNNN].mp4"
```
Estimate only:
```
splitter video.mp4 out/ --duration=2m30s --estimate
splitter vertical-video.mp4 --estimate
```
Custom output names:
Fixed 45second segments with passthrough:
```
splitter video.mp4 out/ --mask="Clip_%03d.mp4"
splitter vertical-video.mp4 --force --duration=45 -- -an -sn
```
Pass extra flags to FFmpeg:
Smart crop for Shorts:
```
splitter video.mp4 out/ -- -an -sn
splitter horizontal-video.mp4 --out=Cropped/ --crop
```
---
## Building
Batch processing with body tracking:
```
dotnet build
```
Or run directly:
```
dotnet run -- video.mp4 out/
splitter --file=file_names.txt --out=Cropped/ --crop --detect=body
```

View File

@ -211,6 +211,8 @@ public sealed class CommandLine
{
var firstInput = Jobs[0].InputFile;
Master.OutputFolder = Path.Combine(Path.GetDirectoryName(Path.GetFullPath(firstInput))!, "Splitter");
foreach (var job in Jobs)
job.OutputFolder = Master.OutputFolder;
Console.WriteLine($"Using default output folder: {Master.OutputFolder}");
}
}

BIN
splitter.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 274 KiB

View File

@ -1,3 +1,9 @@
<Solution>
<Folder Name="/Solution items/">
<File Path=".github/workflows/publish.yml" />
<File Path=".gitignore" />
<File Path="LICENSE.txt" />
<File Path="README.md" />
</Folder>
<Project Path="splitter-cli/splitter.csproj" />
</Solution>

BIN
tracking.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 542 KiB