112 lines
4.8 KiB
YAML
112 lines
4.8 KiB
YAML
|
preset:
|
||
|
name: Split video on black frames (ffmpeg)
|
||
|
group: video
|
||
|
plugin: executor
|
||
|
props:
|
||
|
set:
|
||
|
scenes: { value:"{%uinput}.tmp", escape:true }
|
||
|
script:
|
||
|
analyze:
|
||
|
info: Finding scenes
|
||
|
exec: "ffmpeg -i {%input} -vf blackframe=amount=90:thresh=64 -f null -"
|
||
|
parse: { from: 'stderr', regex: '/^frame=[\s]*([0-9]+)\s/', frame:1 }
|
||
|
extract:
|
||
|
# Write everything matching '[Parsed_blackframe' to file
|
||
|
- { from: 'stderr', regex: '/^\[Parsed_blackframe/', write:'{%scenes}' }
|
||
|
split:
|
||
|
info: Splitting video
|
||
|
call: scenesplitter
|
||
|
cleanup:
|
||
|
info: Cleaning up
|
||
|
exec: "rm {%scenes}"
|
||
|
helpers:
|
||
|
scenesplitter: |
|
||
|
/*
|
||
|
* This is what the contents of %scenes look like:
|
||
|
*
|
||
|
* [Parsed_blackframe_0 @ 0x54f7960] frame:1312 pblack:100 pts:1313312 t:43.777067 type:P last_keyframe:1290
|
||
|
* [Parsed_blackframe_0 @ 0x54f7960] frame:1313 pblack:99 pts:1314313 t:43.810433 type:P last_keyframe:1290
|
||
|
* [Parsed_blackframe_0 @ 0x54f7960] frame:1314 pblack:99 pts:1315314 t:43.843800 type:P last_keyframe:1290
|
||
|
* [Parsed_blackframe_0 @ 0x54f7960] frame:1315 pblack:99 pts:1316315 t:43.877167 type:P last_keyframe:1290
|
||
|
* [Parsed_blackframe_0 @ 0x54f7960] frame:1316 pblack:99 pts:1317316 t:43.910533 type:P last_keyframe:1290
|
||
|
* [Parsed_blackframe_0 @ 0x54f7960] frame:1317 pblack:99 pts:1318317 t:43.943900 type:P last_keyframe:1290
|
||
|
* [Parsed_blackframe_0 @ 0x54f7960] frame:1318 pblack:99 pts:1319318 t:43.977267 type:P last_keyframe:1290
|
||
|
*
|
||
|
* Pick the split point as the last frame in a block with the highest percentage of black pixels, in this case
|
||
|
* it would be on the first line as it has pblack=100 for a single frame.
|
||
|
*/
|
||
|
|
||
|
if (!file_exists({{%scenes}})) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
$frames = file({{%scenes}}, FILE_IGNORE_NEW_LINES);
|
||
|
|
||
|
$chunks = [];
|
||
|
|
||
|
// Split into chunks of blackish frames
|
||
|
$chunk = [];
|
||
|
$last_frame = 0;
|
||
|
foreach ($frames as $frame) {
|
||
|
if (preg_match('/frame:(\d+) pblack:(\d+) pts:(\d+) t:([\d\.]+) type:(.+) last_keyframe:(\d+)$/', $frame, $info)) {
|
||
|
list ($void, $_frame, $_pblack, $_pts, $_t, $_type, $_last_keyframe) = $info;
|
||
|
if ($_frame > $last_frame+1) {
|
||
|
// Add the chunk if its a new block
|
||
|
if (count($chunk)>0) {
|
||
|
$chunks[] = $chunk;
|
||
|
$chunk = [];
|
||
|
}
|
||
|
}
|
||
|
// Store chunk data
|
||
|
$chunk[] = (object)[
|
||
|
'frame' => $_frame,
|
||
|
'pblack' => $_pblack,
|
||
|
'time' => $_t
|
||
|
];
|
||
|
$last_frame = $_frame;
|
||
|
}
|
||
|
}
|
||
|
// Save the last chunk
|
||
|
if (count($chunk)>0) {
|
||
|
$chunks[] = $chunk;
|
||
|
}
|
||
|
|
||
|
// Process chunks and find best split points
|
||
|
$last_time = 0;
|
||
|
$scenes = [];
|
||
|
foreach ($chunks as $chunk) {
|
||
|
// Find split
|
||
|
$best_black = 0; $best_time = 0;
|
||
|
foreach ($chunk as $frame) {
|
||
|
if ($frame->pblack >= $best_black) {
|
||
|
$best_black = $frame->pblack;
|
||
|
$best_time = $frame->time;
|
||
|
}
|
||
|
}
|
||
|
$scenes[] = [ $last_time, $best_time ];
|
||
|
$last_time = $best_time;
|
||
|
}
|
||
|
|
||
|
// Split it up
|
||
|
$input = {{%input}};
|
||
|
$output = {{%output}};
|
||
|
|
||
|
$outdir = dirname($output);
|
||
|
$outext = pathinfo($output, PATHINFO_EXTENSION);
|
||
|
$outfile = basename($output, ".{$outext}");
|
||
|
|
||
|
foreach ($scenes as $_index=>$scene) {
|
||
|
list($_start,$_end) = $scene;
|
||
|
$_duration = $_end-$_start;
|
||
|
$cmdl = sprintf("ffmpeg -ss %s -i '%s' -t %s -codec copy '%s/%s-%d.%s' 2>/dev/null",
|
||
|
$_start,
|
||
|
$input,
|
||
|
$_duration,
|
||
|
$outdir, $outfile, $_index+1, $outext
|
||
|
);
|
||
|
printf("Exec: %s\n", $cmdl);
|
||
|
exec($cmdl, $out, $ret);
|
||
|
}
|
||
|
|
||
|
|