php-vfxapply/presets/executor/executor-scenesplit.yml

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);
}