mirror of
https://github.com/unclshura/splitter.git
synced 2026-06-22 00:22:01 +00:00
README updated. Fixed bug with OutputFolder.
This commit is contained in:
parent
a1371ef6ae
commit
1fd938416e
261
README.md
261
README.md
@ -1,191 +1,202 @@
|
|||||||
# Splitter
|
# Splitter
|
||||||
|
|
||||||
Splitter is a command line tool for cutting video files into equal length or fixed length segments using FFmpeg.
|
Splitter is a high‑performance command line tool for cutting one or more video files into equal or fixed‑length segments using multi‑threaded FFmpeg execution.
|
||||||
It supports multi threaded splitting, ETA and speed reporting, flexible duration formats, and a simple terminal user interface.
|
It supports batch input, flexible duration formats, rotation, smart face/body‑aware cropping, ETA and speed reporting, and both rich and plain‑text terminal output.
|
||||||
|
|
||||||
This tool is intended for creators, archivists, and automation workflows that need fast and predictable video segmentation.
|

|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- Multi threaded splitting for high performance
|
- Multi‑threaded FFmpeg splitting for maximum throughput
|
||||||
- Equal length segments (default)
|
- Equal or fixed‑length segmentation
|
||||||
- Fixed length segments with the `--force` option
|
- Batch input via file masks or list files
|
||||||
- Flexible duration formats (`Ns`, `NmMs`, or plain seconds)
|
- Smart cropping with face/body tracking
|
||||||
- Estimate only mode (`--estimate`)
|
- Rotation correction
|
||||||
- Terminal progress display with ETA and speed
|
- ETA, speed, and progress display
|
||||||
- Custom output filename masks
|
- FFmpeg passthrough for advanced control
|
||||||
- FFmpeg passthrough for advanced users
|
- [Potentially] Cross‑platform (.NET 10)
|
||||||
- Cross platform (.NET 10)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
- FFmpeg and FFprobe installed and available in the system PATH
|
- FFmpeg and FFprobe available in system PATH
|
||||||
- .NET 10 SDK or newer
|
- .NET 10 Runtime or newer
|
||||||
|
|
||||||
>Note: You must download YuNet ONNX model:
|
If you want to update model:
|
||||||
https://github.com/opencv/opencv_zoo/tree/master/models/face_detection_yunet (github.com in Bing)
|
|
||||||
|
- 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 Tracking Using UltraFace 320
|
||||||
|
|
||||||
|
Splitter uses the UltraFace 320 ONNX model to perform lightweight, real‑time 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 per‑frame
|
||||||
|
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 portrait‑oriented camera operator. The UltraFace 320 model is
|
||||||
|
fast enough to run alongside multi‑threaded FFmpeg splitting without becoming a bottleneck,
|
||||||
|
making it suitable for long recordings and batch processing.
|
||||||
|
|
||||||
|
### Benefits of Full‑Body 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.
|
||||||
|
Full‑body 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 lost‑tracking
|
||||||
|
moments. For creators converting horizontal gig footage into short vertical clips for YouTube
|
||||||
|
Shorts or TikTok, body‑based 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 three‑state 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
|
||||||
|
natural‑looking 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 off‑screen 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
|
## 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
|
## Options
|
||||||
|
|
||||||
### --duration=<value>
|
Below is a clean, ASCII‑only **options table** version of your content.
|
||||||
|
All option names are preserved exactly, and descriptions are consolidated for clarity.
|
||||||
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.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### --force
|
## Options
|
||||||
|
|
||||||
Uses the duration exactly as provided.
|
| Option | Description |
|
||||||
The last segment may be shorter.
|
|--------|-------------|
|
||||||
|
| **--out=<folder>** | Output folder for generated segments. Default: `<input folder>/Splitter`. |
|
||||||
Example:
|
| **--file=<path>** | Input file list or file mask. If omitted, the first non‑option 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. |
|
||||||
--duration=45 --force
|
| **--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 plain‑text logging instead of the rich terminal UI. |
|
||||||
|
| **--single-thread** | Disable parallel FFmpeg execution. Useful for debugging or low‑resource 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
|
## FFmpeg Passthrough
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
Anything after `--` is passed directly to FFmpeg.
|
Anything after `--` is passed directly to FFmpeg.
|
||||||
|
|
||||||
Example:
|
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
|
- `input.mp4` may be a file mask (`videos/*.mp4`)
|
||||||
2. Parses the target duration (default 60 seconds or from `--duration=`)
|
- Output filenames follow the `--mask` pattern
|
||||||
3. Computes the number of segments
|
- Output folder defaults to `<input folder>/Splitter` unless overridden
|
||||||
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
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
Split into equal 60 second segments:
|
Split into equal 60‑second segments:
|
||||||
|
|
||||||
```
|
```
|
||||||
splitter video.mp4 out/
|
splitter vertical-video.mp4
|
||||||
```
|
```
|
||||||
|
|
||||||
Split into equal 90 second segments:
|
Split into equal 90‑second 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:
|
Estimate only:
|
||||||
|
|
||||||
```
|
```
|
||||||
splitter video.mp4 out/ --duration=2m30s --estimate
|
splitter vertical-video.mp4 --estimate
|
||||||
```
|
```
|
||||||
|
|
||||||
Custom output names:
|
Fixed 45‑second 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
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
Batch processing with body tracking:
|
||||||
|
|
||||||
## Building
|
|
||||||
|
|
||||||
```
|
```
|
||||||
dotnet build
|
splitter --file=file_names.txt --out=Cropped/ --crop --detect=body
|
||||||
```
|
|
||||||
|
|
||||||
Or run directly:
|
|
||||||
|
|
||||||
```
|
|
||||||
dotnet run -- video.mp4 out/
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@ -211,6 +211,8 @@ public sealed class CommandLine
|
|||||||
{
|
{
|
||||||
var firstInput = Jobs[0].InputFile;
|
var firstInput = Jobs[0].InputFile;
|
||||||
Master.OutputFolder = Path.Combine(Path.GetDirectoryName(Path.GetFullPath(firstInput))!, "Splitter");
|
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}");
|
Console.WriteLine($"Using default output folder: {Master.OutputFolder}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
splitter.png
Normal file
BIN
splitter.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 274 KiB |
@ -1,3 +1,9 @@
|
|||||||
<Solution>
|
<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" />
|
<Project Path="splitter-cli/splitter.csproj" />
|
||||||
</Solution>
|
</Solution>
|
||||||
|
|||||||
BIN
tracking.png
Normal file
BIN
tracking.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 542 KiB |
Loading…
x
Reference in New Issue
Block a user