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