AppleScript C Perl Shell Xcode Other

Create PIP Video from two cameras

Post Reply
coding / perl     Views: 429Prev .. Next
Create PIP Video from two camerasPosted: Sunday, February 23, 2020 [20:47:07] - 1
rootPosted by:rootMember Since:
June 16 2010
Posts: 357
If technical video required to include two source cameras this is a simple solution to make one.
Perl code:
View Code#!/usr/bin/env perl
## Copyright 2020 CodeMacs.Com
## Program to collect videos and make PIP video from two cameras
## Requires install of ffmpeg and QTCoffe
## QTCoffe: www.3am.pair.com/
## ffmpeg: ffmpeg.org/
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute,
# copies of the Software, and to permit persons to whom the Software
# is furnished to do so, subject to the following conditions:

# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.

# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

use strict;
use warnings;
print "Please select video files directory: ";
my $dir = <STDIN>;$dir =~ s/\n|\r//g;$dir =~ s/\/+$//; ## Get dir and sanitize
unless(-d $dir) {print "Directory $dir now found - exiting\n";exit(0);}
unless(-d "$dir/tmp") {`mkdir -p $dir/tmp`;} ## CREATE TEMP DIR
## SETTINGS FOR CUTTING VIDEO BY MARKERS
my $beforemarker=4;## Before marker
my $aftermarker=6;## After marker
## Placement of PIP window
my %fplace = ('br','overlay=W-w-XX:H-h-YY','bl','overlay=XX:H-h-YY','tr','overlay=W-w-XX:YY','tl','overlay=XX:YY');
## PIP position and coordinates br - bottom right, bl - bottom left, tr - not right, tl - top left
my $pipposition='overlay=50:50'; ## This can be further automated, replace XX and YY with pixels from appropriate side

## read file description at the bottom
my $d = `cat $dir/timies.txt`; ## VIDEO FILES DATA WITH TIME MARKS WHERE TO CUT AND OFFSETS FOR PIP VIDEO
my @all = split(/\n/,$d);$d='';my %splits=();my %viddata=();my $sw='';my $mainfiles='';my $overfiles='';
my %offset=();
foreach my $l (@all) {my($file,$data);
if($l =~ /___/) {$sw='overlay';next;}
unless($sw) {
($file,$data) = split(/\t/,$l);
my @tms = split(/\,/,$data);
foreach my $t (@tms) {$t =~ s/ //g;$splits{$file}{$t}=1;}
} else {($file,$data)=split(/\t/,$l);if($data) {$offset{$file}=$data;}}
getvidinfo($file,$sw);
} ## FOREACH END

chomp $mainfiles;my @main = split(/\n/,$mainfiles);
chomp $overfiles;my @over = split(/\n/,$overfiles);
my @m = @main;my $mfps='';foreach my $f (@m) {$mfps = $viddata{$f}{fps};}
@m = @over;my $ofps='';foreach my $f (@m) {$ofps = $viddata{$f}{fps};}

## create supporting video file to fill-up no video in PIP
unless(-f "$dir/tmp/empty.mp4") {
`ffmpeg -f lavfi -i color=c=blue:s=480x270:d=400 -vf "drawtext=fontfile=/Library/Fonts/digital-7.ttf:fontsize=80: fontcolor=white:x=(w-text_w)/2:y=(h-text_h)/2:text='No Video'" -codec:v libx264 -an -crf 20 -preset slow -r $mfps $dir/tmp/empty.mp4`;}

my $ovcmb='';@m = @over;my $ovrsecs=0;my %mapover=();my $from=0;my $tocut='';my %overmap=();
foreach my $f (@m) { ## Resize all PIP videos for the size
unless($mfps == $ofps) {
unless(-f "$dir/tmp/$f") {
`ffmpeg -i $dir/$f -vf scale=480:270 -codec:v libx264 -an -crf 20 -preset slow -r $mfps $dir/tmp/$f`;
} ## END UNLESS FILE ALREADY PRESENT
} ## END DIFFERNET FPS
$overmap{$f}=$ovrsecs;
my $seconds = addtime($viddata{$f}{'duration min'},$viddata{$f}{'duration sec'});
if($offset{$f}) {my($omin,$osec) = split(/\:/,$offset{$f});my $mseconds = addtime($omin,$osec);my $fname=$mseconds;
$mapover{$ovrsecs}=$mseconds;my $lspace='';
if($ovrsecs) {$lspace = $mseconds - $ovrsecs;my($rmin) = backtomin($lspace);$tocut="$rmin";$fname=$lspace;}
else {$tocut=$offset{$f};}
unless(-f "$dir/tmp/$fname.mp4") {
#print "splitmovie $dir/tmp/empty.mp4 -splitAt $tocut -self-contained -o $dir/tmp/$fname.mp4\n";
`splitmovie $dir/tmp/empty.mp4 -splitAt $tocut -self-contained -o $dir/tmp/$fname.mp4`;
if(-f "$dir/tmp/$fname\-2.mp4") {`rm $dir/tmp/$fname\-2.mp4`;}
if(-f "$dir/tmp/$fname\-1.mp4") {
`mv $dir/tmp/$fname\-1.mp4 $dir/tmp/$fname.mp4`;
}
} ## END UNLESS FILE PRESENT

$ovcmb .= " $dir/tmp/$fname.mp4";
$ovrsecs += $mseconds;
} ## END OFFSET PRESENT
$ovcmb .= " $dir/tmp/$f";
$ovrsecs += $seconds;
} # FOREACH OVER FILE END
unless(-f "$dir/tmp/overlay.mp4") {
print "catmovie$ovcmb -self-contained -o $dir/tmp/overlay.mp4\n";
`catmovie$ovcmb -self-contained -o $dir/tmp/overlay.mp4`;
} ## END UNLESS OVERLAY PRESENT

@m = @main;my $mcmb='';my $mainsec=0;my %mapmain=();
foreach my $f (@m) {
$mcmb .= " $dir/$f";
my $seconds = addtime($viddata{$f}{'duration min'},$viddata{$f}{'duration sec'});
$mapmain{$f}=$mainsec;
$mainsec += $seconds;
} ## FOREACH FILE END
unless(-f "$dir/tmp/main.mp4") {
print "catmovie$mcmb -self-contained -o $dir/tmp/main.mp4\n";
`catmovie$mcmb -self-contained -o $dir/tmp/main.mp4`;
} ## END UNLESS MAIN PRESENT
print "Main Seconds: $mainsec\n";
print "Overlay Seconds: $ovrsecs\n"; ## Overlay is a PIP video

my %making=();my $nmbcount=0;my $pushcomb='';
my %delete=();
foreach my $f (sort keys(%splits)) {my $cuts='';my $spr='';my $parts=0;my $ovspl='';my $zero='';my $nokeys=0;
print "$dir/$f\n";my($fn,$fe) = split(/\./,$f);
$nokeys = (keys %{$splits{$f}});
if($nokeys > 4) {$zero='0';} ## Make sure we match the auto-name files created by QTCoffe
## Procedure only support two digits auto increment, so if you have more than 49 markers per video - it will not work
foreach my $t (sort keys %{$splits{$f}}) {
$parts++;if(length($parts) > 1) {$zero='';}
$delete{"$fn\-$zero$parts.$fe"}=1;$delete{"$fn.ov\-$zero$parts.$fe"}=1;
$parts++;if(length($parts) > 1) {$zero='';}
$making{$parts}{ma}="$fn\-$zero$parts.$fe";$making{$parts}{ov}="$fn.ov\-$zero$parts.$fe";
$parts++;if(length($parts) > 1) {$zero='';}
$delete{"$fn\-$zero$parts.$fe"}=1;$delete{"$fn.ov\-$zero$parts.$fe"}=1;$parts--;
my $secst='';my $secend='';my $minst='';my $minend='';
## Video markers can be formatted either simply separated by commas 00:12, 03:45 etc.
## or full duration of the cut included and mixed i.e. 02:35-03:14, 05:15, 07:28
if($t =~ /\-/) {
my($stt,$ett) = split(/\-/,$t);
($minst,$secst) = split(/\:/,$stt);
($minend,$secend) = split(/\:/,$ett);
} else { ## END MATCHES HYPHEN
## Configure how much time before and after the marker needed to be included in a cut in seconds
my($min,$sec) = split(/\:/,$t);$secst=$sec-$beforemarker;$secend=$sec+$aftermarker;$minst=$min;$minend=$min;
} ## END REGULAR TIME
if($secst < 0) {$minst--;$secst =~ s/\-//g;$secst = 60 - $secst;}
if($secend > 59) {$minend++;$secend -= 60;}
if(length($secend) < 2) {$secend="0$secend";}
if(length($secst) < 2) {$secst="0$secst";}
if(length($minst) < 2) {$minst="0$minst";}
if(length($minend) < 2) {$minend="0$minend";}
print "\t\"$t\"\t$minst\:$secst - $minend\:$secend\n";
$cuts .= "$spr\-splitAt $minst\:$secst -splitAt $minend\:$secend";$spr=' ';
my $stsecs = addtime($minst,$secst);
my $ensecs = addtime($minend,$secend);
$stsecs += $mapmain{$f};$ensecs += $mapmain{$f};
my $startov = backtomin($stsecs);
my $endov = backtomin($ensecs);
$ovspl .= " -splitAt $startov -splitAt $endov";
} ## FOREACH TIME END
my $code = "splitmovie $dir/$f $cuts -self-contained -o $dir/tmp/$f";## Cut movie by markers
#print "\n$code\n";
`$code`;
my $ovrlc = "splitmovie $dir/tmp/overlay.mp4$ovspl -self-contained -o $dir/tmp/$fn.ov.$fe"; ## Cut PIP by the same markers
#print "$ovrlc\n";
`$ovrlc`;
## Combine main movie cut with PIP video
foreach my $n (sort num keys (%making)) {$nmbcount++;
`ffmpeg -i $dir/tmp/$making{$n}{ov} -i $dir/tmp/$making{$n}{ma} -filter_complex "[0]scale=iw/1:ih/1 [pip]; [1][pip] $pipposition" -codec:v libx264 -crf 20 -preset slow $dir/tmp/$nmbcount.$n.mp4`;
$pushcomb .= " $dir/tmp/$nmbcount.$n.mp4";
print "\n\tMAKE: $nmbcount.$n.mp4\n\n";
$delete{"$nmbcount.$n.mp4"}=1;`rm $dir/tmp/$making{$n}{ov}`;`rm $dir/tmp/$making{$n}{ma}`;
} ## FOREACH V NUMB END
%making=();
} ## FOREACH FILES

## Combine all the cuts together
print "catmovie$pushcomb -self-contained -o $dir/tmp/combined.mp4\n\n$pushcomb\n";
`catmovie$pushcomb -self-contained -o $dir/tmp/combined.mp4`;

## Remove temp files
print "\nDeleting files:\n";
foreach my $f (sort keys(%delete)) {
if(-f "$dir/tmp/$f") {
print "$dir/tmp/$f\n";
`rm $dir/tmp/$f`;
}
} ## FOREAH DELETE END

exit(0);

sub num {$a <=> $b;}

sub getvidinfo {
my $file=shift;my $sw=shift;unless($sw) {$sw='main';$mainfiles .= "$file\n";} else {$overfiles .= "$file\n";}
my $vinf = `ffmpeg -i $dir/$file 2>&1`;$vinf =~ s/\r//g;
$vinf =~ s#\n(.*?)(Duration\:)\s(\d{2})\:(\d{2})\:(\d{2})\.(\d{2})(.*?)\n##;my $durm=$4;my $durs=$5;
$vinf =~ s#\n(.*?)(Stream)(.*?)(\d{2}|\d{2}\.\d{2})\s(fps)(.*?)\n##;my $fps=$4;
print "$file\nDuration: $durm\:$durs\nFPS: $fps\n";
$viddata{$file}{'duration min'}=$durm;$viddata{$file}{'duration sec'}=$durs;$viddata{$file}{fps}=$fps;
} ## END GET VID INFO

sub addtime {
my($min,$sec) = @_;
my $totsecs = $min * 60;$totsecs += $sec;return($totsecs);
} ## END SUB ADD TIME

sub backtomin {
my $s=shift;
return sprintf ":%02d", $s if $s < 60;
my $m = $s / 60; $s = $s % 60;
return sprintf "%02d:%02d", $m, $s if $m < 60;
my $h = $m / 60; $m %= 60;
return sprintf "%02d:%02d:%02d", $h, $m, $s if $h < 24;
my $d = $h / 24; $h %= 24;
return sprintf "%d:%02d:%02d:%02d", $d, $h, $m, $s;
} ## END BACK TO MINUTES


Next, timies.txt file for program to properly cut videos.There's no place like ~
timies.txt file contentPosted: Sunday, February 23, 2020 [20:50:31] - 2
rootPosted by:rootMember Since:
June 16 2010
Posts: 357
This file provides markers and offset information to properly build PIP video:
Example:
View CodeGOPR0150.MP409:42, 10:34
GP010150.MP400:01-02:02, 02:24, 08:13
GP020150.MP402:46, 03:58, 04:46, 05:10, 05:13, 06:06, 06:12, 06:31, 06:51, 07:07
_________________
ch0_20200222170744_20200222171244.mp41:35
ch0_20200222171245_20200222171745.mp4
ch0_20200222171745_20200222172113.mp4
ch0_20200222172423_20200222172923.mp418:12
ch0_20200222172924_20200222173424.mp4
ch0_20200222173424_20200222173855.mp4

Explanations:
View Code#File Nametime markers
#GOPR0150.MP409:42, 10:34
#GP010150.MP400:01-02:02, 02:24, 08:13
#GP020150.MP402:46, 03:58, 04:46, 05:10, 05:13, 06:06, 06:12, 06:31, 06:51, 07:07
#_________________this is separator between main video and PIP overlay
#File nameTime Offset
#ch0_20200222170744_20200222171244.mp41:35PIP video starts 1 min 35 seconds later than beginning of a main video
#ch0_20200222171245_20200222171745.mp4
#ch0_20200222171745_20200222172113.mp4
#ch0_20200222172423_20200222172923.mp418:12This file starts 18 minutes 12 seconds from beginning of a main video
#ch0_20200222172924_20200222173424.mp4
#ch0_20200222173424_20200222173855.mp4
#files without offset - just follow each other

As simple as thatThere's no place like ~
Video frame examplePosted: Sunday, February 23, 2020 [21:09:15] - 3
rootPosted by:rootMember Since:
June 16 2010
Posts: 357
This is a frame example from the actual video built:

Image 1

This is a simple technical video,There's no place like ~
coding / perlPrev .. Next
 
Post Reply
Home - Coding: AppleScript C Perl Shell Xcode Other
Our Telegram Group