Anything DIY

Select and convert ANNKE camera footage program

Post Reply
other / anything     Views: 545Prev .. Next
Select and convert ANNKE camera footage programPosted: Wednesday, May 12, 2021 [15:45:05] - 1
rootPosted by:rootMember Since:
June 16 2010
Posts: 357
Since H.265/H.265+ codec is not immediately available for players and best accepted format is H.264 lets' make a conversion painless for security cameras.
Once the time frame and camera becomes known either by scrolling NVR's records interface or any other way it can be selected in GUI on a local machine:

Select and convert ANNKE camera footage program
Select the date
Select and convert ANNKE camera footage program
and time
Select and convert ANNKE camera footage program
Selection is displayed in a box in a top right corner.
Submit information for automatic processing.

Program grabs proper video files from NVR, cuts the proper portions and assembles them in a single MP4 H.264 file.
Program also emails links for local and Internet view/download. Very handy for sharing the file with local Law Enforcement if required or sharing some event.
This procedure can be done locally or remotely. No cloud service or Internet connection required for local program to work.
Access to the interface and program is 100% depends on a LAN settings.There's no place like ~
Back-end programPosted: Wednesday, May 12, 2021 [20:10:58] - 2
rootPosted by:rootMember Since:
June 16 2010
Posts: 357
Here is the program we run from cron. It takes a text file as an input and creates a hash for processing.
Program written in Perl:
View Code#!/usr/bin/perl
# This software is free under AL/GPL, copyright (c) 2021 CodeMacs.com
# You can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.
# Use it at your own risk!
use strict;
use warnings;
use POSIX qw(mktime);

## If running from cron - make sure PATH is set properly
$ENV{'PATH'} = '/bin:/sbin:/usr/bin:/usr/sbin:/usr/local:/usr/local/bin:/opt/local/bin';

my $nowd = time;
my $finaltime = localtime($nowd);$finaltime =~ tr/ //s;
my($wd,$mo,$da,$ti,$ye) = split(/\s/,$finaltime);my($hrs,$min,$sec) = split(/\:/,$ti);

# Record program process
open STDOUT, '>>/program/location/process.last.txt' or die "Can't write to /program/location/process.last.txt: $!";
print "$finaltime\n";
unless(-f '/help/folder/camera.footage.request.txt') {print "No source file found - exit\n";exit(0);}
if(-f "/help/folder/ipLogD/cam.process.running") {print "Program busy\n";exit(0);}
my $exip='';`touch /help/folder/ipLogD/cam.process.running`;
## Get your external IP if sending link accessible on the Internet
if(-f "/help/folder/ipLogD/currentIPeXt.txt") {$exip = `cat /help/folder/ipLogD/currentIPeXt.txt`;}
else {$exip = `curl ipecho.net/plain ; echo`;$exip =~ s/\n|\r//g;
open(IPT,">/help/folder/ipLogD/currentIPeXt.txt");print IPT $exip;close(IPT);
} ## END NO FILE PRESENT
my %dataread=();my %cleanup=();

## Read data submitted by GUI program
open(TXT,"</help/folder/camera.footage.request.txt");
while(<TXT>) {$_ =~ s/\n|\r//g;unless($_) {next;}
my($ut,$k,$v) = split(/\t/,$_);$dataread{$ut}{$k}=$v;
} ## WHILE END
close(TXT);
`mv /help/folder/camera.footage.request.txt /help/folder/camera.footage.request.old.txt`;
$cleanup{"/help/folder/camera.footage.request.old.txt"}=1;
$cleanup{"/help/folder/ipLogD/cam.process.running"}=1;
our %filesinfo = ();my $fupdate='';
if(-f '/program/location/files.data.txt') {eval {require '/program/location/files.data.txt'};}# %filesinfo
my($user,$pass,$nvrip) = ('NVR_User_Name','NVR_Password','NVR_IP_Address');
our %caldata=();our %daytimes=();
my %convmvis = (
'Jan','January','Feb','February','Mar','March','Apr','April','May','May','Jun','June',
'Jul','July','Aug','August','Sep','September','Oct','October','Nov','November','Dec','December'
);
my %convmdig = (
'Jan','01','Feb','02','Mar','03','Apr','04','May','05','Jun','06',
'Jul','07','Aug','08','Sep','09','Oct','10','Nov','11','Dec','12'
);
my %convmdigmk = (
'Jan','0','Feb','1','Mar','2','Apr','3','May','4','Jun','5','Jul','6','Aug','7','Sep','8','Oct','9','Nov','10','Dec','11'
);
my %weekd = ('Mon','Monday','Tue','Tuesday','Wed','Wednesday','Thu','Thursday','Fri','Friday','Sat','Saturday','Sun','Sunday');
my @allm = (
'Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'
);
my %cameras = ( ## Add Your cameras here, Key is cameras' NVR number
1,'Front Yard',
2,'Porch Top',
3,'Side Back',
4,'Back Yard'
#5,'Cam 5',
#6,'Cam 6'
);
my %working=();my %datesget=();my %cutmovie=();
foreach my $t (keys(%dataread)) {
my $cam = $dataread{$t}{cam};delete $dataread{$t}{cam};
if(length($dataread{$t}{day}) < 2) {$dataread{$t}{day}='0'.$dataread{$t}{day};}
my $sttmdt="<startTime>$dataread{$t}{year}\-$convmdig{$dataread{$t}{month}}\-$dataread{$t}{day}T00:00:00Z</startTime>";
my $entmdt="<endTime>$dataread{$t}{year}\-$convmdig{$dataread{$t}{month}}\-$dataread{$t}{day}T23:59:59Z</endTime>";
my $gtkey=" $sttmdt\n $entmdt";
$datesget{$cam}{$gtkey}="$cam.$dataread{$t}{month}.$dataread{$t}{day}.$dataread{$t}{year}";
my $sttm="<startTime>$dataread{$t}{year}\-$convmdig{$dataread{$t}{month}}\-$dataread{$t}{day}T$dataread{$t}{hourst}:$dataread{$t}{minst}:00Z</startTime>";
my $entm="<endTime>$dataread{$t}{year}\-$convmdig{$dataread{$t}{month}}\-$dataread{$t}{day}T$dataread{$t}{houren}:$dataread{$t}{minen}:59Z</endTime>";
my $tkey=" $sttm\n $entm";
$working{$cam}{$tkey}="$dataread{$t}{month}.$dataread{$t}{day}.$dataread{$t}{year}-$dataread{$t}{hourst}.$dataread{$t}{minst}.00-$dataread{$t}{houren}.$dataread{$t}{minen}.59";
my $wday='0';my $sec='00';
my $yer = $dataread{$t}{year}; $yer -= 1900;
my $epstart = mktime($sec,$dataread{$t}{minst},$dataread{$t}{hourst},$dataread{$t}{day},$convmdigmk{$dataread{$t}{month}},$yer,$wday,0,-1);
$cutmovie{$cam}{$tkey}{start}=$epstart;$sec='59';
my $epend = mktime($sec,$dataread{$t}{minen},$dataread{$t}{houren},$dataread{$t}{day},$convmdigmk{$dataread{$t}{month}},$yer,$wday,0,-1);
$cutmovie{$cam}{$tkey}{end}=$epend;
} ## FOREACH UNIX TIME END
%dataread=();

my $cook='/program/location/cookies/cookies.txt';
my $brows='Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0)';
getallfiles(); ## Get all files and their information for given camera for one day

my $cnts=0;my %process=();
foreach my $cam (keys(%working)) {
foreach my $time (keys %{$working{$cam}}) {$cnts++;
my $vfilename=$working{$cam}{$time};
# Create XML request for event's files on NVR
my $rqest = '<?xml version="1.0" encoding="utf-8"?>
<CMSearchDescription>
<searchID>{K13J2KE1-7226-0008-B4ZZ-3A8199B021B0}</searchID>
<trackList>
<trackID>'.$cam.'01</trackID>
</trackList>
<timeSpanList>
<timeSpan>
'.$time.'
</timeSpan>
</timeSpanList>
<contentTypeList>
<contentType>video</contentType>
</contentTypeList>
<maxResults>40</maxResults>
<searchResultPostion>0</searchResultPostion>
<metadataList>
<metadataDescriptor>//recordType.meta.std-cgi.com/CMR</metadataDescriptor>
</metadataList>
</CMSearchDescription>';
open(TXD,">/help/folder/iplogD/req.$cam.$cnts.txt");print TXD $rqest;close(TXD);
my $run = "curl -X POST -b $cook -c $cook -A \"$brows\" -d \@/help/folder/iplogD/req.$cam.$cnts.txt http://$user:$pass\@$nvrip/ISAPI/ContentMgmt/search";
print "$run\n\n";
my $result = `$run`;
print "$result\n";my @getvid=();
# Get rtsp requests for the files
while($result =~ /(.*?)(\<playbackURI\>)(.*?)(\<\/playbackURI\>)/gs) {push @getvid,$3;print "$3\n\n";}
my $subcnt=0;
## FOREACH VIDEO FILE START
foreach my $vfile (@getvid) {$subcnt++;
my $trun = "curl -d \"<downloadRequest><playbackURI>$vfile</playbackURI><userName>$user</userName><password>$pass</password></downloadRequest>\" http://$user:$pass\@$nvrip/ISAPI/ContentMgmt/download-o /program/location/temp/$vfilename.$cnts.$subcnt.mp4";
unless(-f "/program/location/temp/$vfilename.$cnts.$subcnt.mp4") {`$trun`;}
$vfile =~ s#name=(.*?)\&##;my $filename=$1;
print "$trun\n\n";my $vidfilename='';
$process{$cnts}{$subcnt}{full}=$filename;
$process{$cnts}{$subcnt}{loc}="/program/location/temp/$vfilename.$cnts.$subcnt.mp4";
$process{$cnts}{cutstart}=$cutmovie{$cam}{$time}{start};
$process{$cnts}{cutend}=$cutmovie{$cam}{$time}{end};
$process{$cnts}{camera}=$cam;
} ## FOREACH VIDEO FILE END
} ## FOREACH TIMES END
} ## FOREACH CAM END
%cutmovie=();%working=();

# Write locally data for files on NVR
if($fupdate) { #####################
my $wrttm=time;print "\nRe-writing cameras.nvr/files.data.txt file ##########\n\n";
open(TXT,">/program/location/files.data.txt");print TXT "\%filesinfo = (\n";my $cma='';
foreach my $n (sort keys (%filesinfo)) {
if($filesinfo{$n}{delete}{when} < $wrttm) {next;} ## SKIP OLD RECORDS
print TXT "$cma'$n' => {\n";$cma=",\n";my $cma1="\t";
foreach my $d (sort keys %{$filesinfo{$n}}) {
print TXT "$cma1'$d' => {\n";$cma1 = ",\n\t";my $cma2="\t\t";
foreach my $k (sort keys %{$filesinfo{$n}{$d}}) {
print TXT "$cma2'$k','$filesinfo{$n}{$d}{$k}'";$cma2=",\n\t\t";
} ## FOREACH K2 END
print TXT "\n\t}";
} ## FOREACH K1 END
print TXT "\n}";
} ## FOREACH FILE NAME
print TXT "\n);\n";close(TXT);
} ## END IF FILE UPDATE REQUIRED ###

## Now process video files, convert, cut and combine them as needed
my $errors='';my $filesaved='';my $emailmess = '';
foreach my $n (keys (%process)) { #########
print "Processing $n\n";
my $movstart = $process{$n}{cutstart};delete $process{$n}{cutstart};my $part1=getfnconverted($movstart);
my $movend = $process{$n}{cutend};delete $process{$n}{cutend};my $part2=getfnconverted($movend);
my $camera = $process{$n}{camera};delete $process{$n}{camera};
my $cameraname=$cameras{$camera};$cameraname =~ s/\W/\./g;
my $finalsave="$cameraname.$part1\-$part2";
if(-f "/help/folder/web/cameras/dnl/$finalsave.mp4") { ## SKIP IF ALREADY PRESENT
$emailmess .= "File /cameras/dnl/$finalsave.mp4 already exist - skipping\n\n";next;}
my $cmbnmb=0;my $tofile='';
foreach my $sf (sort num keys %{$process{$n}}) {
my $fkey = $process{$n}{$sf}{full};
my $ifile = $process{$n}{$sf}{loc};
unless($movstart && $movend && $fkey && $ifile) {
$errors .= "Camera: $cameras{$camera}\nTime frame: $part1 $part2
Missing data found:\nMovie start: $movstart\nMovie end: $movend\nOrig file: $fkey\nLocal file: $ifile\n----------------\n";
next;}
$cmbnmb++;
$filesaved=getmovie($movstart,$movend,$fkey,$ifile,$cmbnmb);
print "\t$movstart\t$movend\t$fkey\t$ifile\t$cmbnmb\n";
$tofile .= "file '$filesaved'\n";
$cleanup{$filesaved}=1;
} ## FOREACH FILE END
if($cmbnmb > 1) {chomp $tofile;
print "Combining all files\n";
open(FLS,">/program/location/temp/$finalsave.txt");print FLS $tofile;close(FLS);
$cleanup{"/program/location/temp/$finalsave.txt"}=1;
# Turn off ffmpeg safety check for full path files: "-safe 0" or programs will error-out
`ffmpeg -safe 0 -f concat -i /program/location/temp/$finalsave.txt -codec copy /program/location/temp/$finalsave.mp4`;
} ## END MULTIPLE FILES
else {`mv $filesaved /program/location/temp/$finalsave.mp4`;print "Just moving file, no need to combine\n";}
$filesaved='';
`mv /program/location/temp/$finalsave.mp4 /help/folder/web/cameras/dnl/$finalsave.mp4`;
# PORT is a port forwarding port on your router
$emailmess .= "http://LAN_IP_Address/cameras/dnl/$finalsave.mp4\nhttp://$exip:PORT/cameras/dnl/$finalsave.mp4\n\n";
} ## FOREACH NMB END ######################
%process=();%filesinfo=();

# Now, email results
my $sendmail = '/usr/sbin/sendmail -t -oi';
if($emailmess) {
print "Sending email\n";
open(MAIL,"|$sendmail");
print MAIL <<EOM;
From: your_user\@your_domain\nTo: to_user\@from_domain\nSubject: Video processed\n
The following files were processed and ready for download:
$emailmess
EOM
close(MAIL);
} ## END EMAIL MESSAGES PRESENT

if($errors) {
print "Sending errors\n";
open(MAIL,"|$sendmail");
print MAIL <<EOM;
From: your_user\@your_domain\nTo: to_user\@from_domain\nSubject: While processing Videos error(s) found\n
The following errors were found while processing video files:
$errors
EOM
close(MAIL);
} ## END IF ERRORS PRESENT

print "Cleaning-up\n";
foreach my $f (keys (%cleanup)) {
if(-f $f) {unlink $f;print "\trm $f\n";}
}
exit(0); # Program finished

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

sub getmovie { ## GET MOVIE
my($from,$to,$name,$read,$savet) = @_;my $offset='';my $duration='';
my $recfrom = $filesinfo{$name}{start}{epoch};
my $recto = $filesinfo{$name}{end}{epoch};
print "Get from/to: $from $to\nMovie from/to: $recfrom $recto\n";
if($recfrom < $from) {my $diff = $from - $recfrom;my($hr,$min,$sec)=getoffset($diff);
$offset = " -ss $hr\:$min\:$sec";
} ## END OFFSET AT THE START
if($recto > $to) {my $dstrt='';
if($recfrom < $from) {$dstrt=$from;} else {$dstrt=$recfrom;}
my $diff = $to - $dstrt;my($hr,$min,$sec)=getoffset($diff);
$duration = " -t $hr\:$min\:$sec";
} ## END IF MOVIE ENDS AFTER CLIP END
my $frun="ffmpeg$offset$duration -i $read -vf scale=3414:1920 -c:v libx264 -crf 25 -preset slow /program/location/temp/making.$savet.mp4";
print "$frun\n";
unless(-f "/program/location/temp/making.$savet.mp4") {`$frun`;}
return("/program/location/temp/making.$savet.mp4");
} ## END SUB GET MOVIE ####

sub getoffset {
my $elapsed=shift;
my $hours = int($elapsed / (60 * 60));my $mins = $elapsed / 60 % 60;my $secs = $elapsed % 60;
if(length($hours) < 2) {$hours = '0'.$hours;}
if(length($mins) < 2) {$mins = '0'.$mins;}
if(length($secs) < 2) {$secs = '0'.$secs;}
return($hours,$mins,$secs);
} ## END SUB GET OFFSET

sub getfileinfo { ## Getting file data for easy time processing
# Example of rtsp request
#<playbackURI>rtsp://NVR_IP/Streaming/tracks/201/?starttime=20210511T000000Z&endtime=20210511T015812Z&name=00000000283000000&size=1066098420</playbackURI>
# Based on start time and end time we can calculate the offset for our portion
# by using the epoch time
my $d=shift;my $rectz=time;my $deletewhen=$rectz + (86400 * 70); ## Keep record for 70 days
$d =~ s#starttime=(.*?)\&##;my $start=$1;
$d =~ s#endtime=(.*?)\&##;my $end=$1;
$d =~ s#name=(.*?)\&##;my $name=$1;
$d =~ s#size=(\d+)##;my $size=$1;
my $szmb = $size / (1024 * 1024);$szmb = sprintf("%0.2f",$szmb);
unless($filesinfo{$name}{recorded}{time}) {$fupdate=1;} else {return;}
my($ye,$mo,$da,$hr,$min,$sec)=getdates($start);
$filesinfo{$name}{file}{size}=$size;
$filesinfo{$name}{file}{sizeMb}=$szmb;
$filesinfo{$name}{recorded}{time}=$rectz;
$filesinfo{$name}{delete}{when}=$deletewhen;
$filesinfo{$name}{start}{hrs}=$hr;
$filesinfo{$name}{start}{min}=$min;
$filesinfo{$name}{start}{sec}=$sec;
$filesinfo{$name}{start}{mo}=$mo;my $mo1=$mo-1;
$filesinfo{$name}{start}{da}=$da;
$filesinfo{$name}{start}{ye}=$ye;my $wday='0';
my $yer = $ye; $yer -= 1900;
my $timestamp = mktime($sec,$min,$hr,$da,$mo1,$yer,$wday,0,-1); # Epoch time
$filesinfo{$name}{start}{epoch}=$timestamp;
## ---------- ##
($ye,$mo,$da,$hr,$min,$sec)=getdates($end);
$filesinfo{$name}{end}{hrs}=$hr;
$filesinfo{$name}{end}{min}=$min;
$filesinfo{$name}{end}{sec}=$sec;
$filesinfo{$name}{end}{mo}=$mo;$mo1=$mo-1;
$filesinfo{$name}{end}{da}=$da;
$filesinfo{$name}{end}{ye}=$ye;
$yer = $ye; $yer -= 1900;
$timestamp = mktime($sec,$min,$hr,$da,$mo1,$yer,$wday,0,-1); # Epoch time
$filesinfo{$name}{end}{epoch}=$timestamp;
} ## END SUB GET FILE INFO

sub getdates {
my $r=shift;$r =~ s#(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})Z##;
return($1,$2,$3,$4,$5,$6);
} ## END GET DATES

sub getallfiles { ## GET INFO ON FILES THIS DAY
print "\nGetting all Files\n";
foreach my $cam (keys (%datesget)) {
foreach my $r (keys %{$datesget{$cam}}) {
my $rqest = '<?xml version="1.0" encoding="utf-8"?>
<CMSearchDescription>
<searchID>{K13J2KE1-7226-0008-B4ZZ-3A8199B021B0}</searchID>
<trackList>
<trackID>'.$cam.'01</trackID>
</trackList>
<timeSpanList>
<timeSpan>
'.$r.'
</timeSpan>
</timeSpanList>
<contentTypeList>
<contentType>video</contentType>
</contentTypeList>
<maxResults>40</maxResults>
<searchResultPostion>0</searchResultPostion>
<metadataList>
<metadataDescriptor>//recordType.meta.std-cgi.com/CMR</metadataDescriptor>
</metadataList>
</CMSearchDescription>';
open(RQ,">/help/folder/iplogD/reqest.$datesget{$cam}{$r}.txt");print RQ $rqest;close(RQ);
my $run = "curl -X POST -b $cook -c $cook -A \"$brows\" -d \@/help/folder/iplogD/reqest.$datesget{$cam}{$r}.txt http://$user:$pass\@$nvrip/ISAPI/ContentMgmt/search";
print "$run\n\n";
my $result = `$run`;
print "$result\n";my @getvid=();
while($result =~ /(.*?)(\<playbackURI\>)(.*?)(\<\/playbackURI\>)/gs) {push @getvid,$3;print "$3\n\n";}
foreach my $vfile (@getvid) {getfileinfo($vfile);}@getvid=();
} ## FOREACH GET RECS END
} ## FOREACH CAM END
%datesget=();
} ## END SUB GET ALL DATES

sub getfnconverted {
my $t=shift;my $finaltime = localtime($t);$finaltime =~ tr/ //s;
my($wd,$mo,$da,$ti,$ye) = split(/\s/,$finaltime);my($hrs,$min,$sec) = split(/\:/,$ti);
return("$mo.$da.$ye\-$hrs.$min");
} ## END GET DATE CONVERTED

Just as simple as that!
Prerequisites: ffmpeg, curlThere's no place like ~
other / anythingPrev .. Next
 
Post Reply
Home - Other: Anything DIY
Our Telegram Group