#!/usr/bin/perl -w
#  $Id: probe 18145 2010-04-01 01:13:50Z teetov $
# -----------------------------------------------------------------------------
#  Purpose:
#    - probe the camera
#    - pre-tune camera befor for retriever (RC and Audio)
# -----------------------------------------------------------------------------
#  Call:
#    probe DEVID=123
#    probe DEVID=123 PROBE=RESET
#    probe DEVID=123 DEVIP=192.168.17.177 [ ... ]
#    probe DEVIP=192.168.17.177 USRNAME=admin PASSWD=pass PROBE=DEFINE
# -----------------------------------------------------------------------------
#  Does:
#   0. can be called without DEVID argument.
#      In this case DEVIP,USRNAME,PASSWD should be provided in command line
#   1. load $APL_CONF/<DEVID>/conf if DEVID is provided
#   2. combine conf and args into one hash
#   3. connect to camera over http and read MODELID and FIRMWARE
#   4. report MODELID|FIRMWARE|STATUS to $APL_CONF/<DEVID>/conf.probe and stdout
#   5. if PROBE=DEFINE then get
#         IMAGESIZE_LIST,MEDIA_FORMAT_LIST
#         AUDIO_LIST,AUDIO_FORMAT_LIST
#         SNAPSHOT (picture)
#   6. set RC camera attributes    if mpeg4 | h264
#   7. set AUDIO camera attributes if AUDIO is on
#   8. example output:
#       AUDIO_SET=OK
#       FIRMWARE=5.02
#       MODELID=Q1755
#       RC_SET=OK
#       STATUS=OK
#       -------------
#       AUDIO_SET=NONE
#       FIRMWARE=5.02
#       MODELID=Q1755
#       RC_SET=NONE
#       STATUS=OK
#       SNAPSHOT=/tmp/probe/192.168.17.177-12367576123.jpg
#       IMAGESIZE_LIST=640x480,480x360,320x240,240x180,176x144,160x120
#       MEDIA_FORMAT_LIST=mjpeg,h264
#       AUDIO_LIST=off,on
#       AUDIO_FORMAT_LIST=g711,g726,aac
#   9. sample errors:
#       STATUS=ERROR: PCE-0001 [101] configuration is not found
#       STATUS=ERROR: PCE-0002 [101] USRNAME and PASSWD should provided
#       STATUS=ERROR: PCE-0003 [101] DEVIP is not defined
#       STATUS=ERROR: PCE-0500 [101] Device does not respond (http://207.107.163.123:80)
#       STATUS=ERROR: PCE-0401 [101] Authorization error
#       STATUS=ERROR: PCE-0403 [101] Forbidden
#       STATUS=ERROR: PCE-0030 [101] Cannot get MODELID
#       note: [101] is DEVID
#  10. warnings:
#       STATUS=WARNING: PCW-0001 [101] MODELID does not match configuration
#       STATUS=WARNING: PCW-0002 [101] FIRMWARE does not match configuration
#
# -----------------------------------------------------------------------------
#  Note:
#   1. CAMERAMODEL file is obsolite. ptz_axisv2.pl & ptz_udp.pl has to be modified
#   2. Script mast be in the directory .../camera/<BRAND>/bin/
# -----------------------------------------------------------------------------
#  Author: teetov, 03/22/10
#  Edited by:
#  QA by:
#  Copyright: videoNEXT Network Solutions, Inc, 2010
# -----------------------------------------------------------------------------
#
use strict;
use Device::Conf qw(:all);
use XML::Simple;
use JSON "to_json";
#use Data::Dumper;
# cons -------------------------------------------------------------------------
# no code for UDP

# --------- check required parameters: DEVID,USRNAME,PASSWD,
my $conf=ProbeInit();  # uses <BRAND> from path and ARGV
my $APL = $ENV{APL} || '/opt/sarch';
ProbeErr("PCE-0001","configuration is not found") if not defined $conf->{DEVID};
ProbeErr("PCE-0003","DEVIP is not defined")       if not defined $conf->{DEVIP};
ProbeErr("PCE-0002","USRNAME and PASSWD should be provided")
                 if not defined $conf->{USRNAME} or not defined $conf->{PASSWD};
#------------------------------------------------------------------------------
# Probe camera MODELID & FIRMWARE
#------------------------------------------------------------------------------
my %result=(MODELID=>'0',FIRMWARE=>'0.0',STATUS=>'OK',RC_SET=>'NONE',AUDIO_SET=>'NONE');

# Checking model
# NVE: /enc-cgi/admin/param.cgi?action=list&group=Brand
# root.Brand.Brand=CAP
# root.Brand.ProdFullName=NVE4000 Network Video Encoder
# root.Brand.ProdShortName=NVE4000
# root.Brand.ProdNbr=4000
# root.Brand.ProdType=Network Video Encoder
# root.Brand.WebURL=http://
# NVC: /nvc-cgi/admin/param.cgi?action=list&group=Brand
# Brand.brand=CAP
# Brand.prodfullname=NVC1000 Network Video Encoder
# Brand.prodshortname=NVC1000
# Brand.prodnbr=0
# Brand.prodtype=encoder
# Brand.weburl=http://www.
# Brand.authenkey=8BA4D163B633007248531BA8CB6D2F07

# DN:  /nvc-cgi/admin/param.cgi?action=list&group=Brand
# Brand.brand=Dante Security
# Brand.prodfullname=DN9131 Network Video Encoder, 12VDC/POE
# Brand.prodshortname=DN9131
# Brand.prodnbr=0
# Brand.prodtype=Network Video Encoder
# Brand.weburl=www.dantesecurity.net
# Brand.authenkey=8BA4D163B633007248531BA8CB6D2F07

UA()->timeout(30); # Slow camera, increase timeout
my $respond=ProbeRequest("/enc-cgi/admin/param.cgi?action=list&group=Brand"); # If NVE
if($respond=~ /^HTTP ERROR/) {
  my ($err)= $respond=~/^HTTP ERROR \[(\d+)\]/;
  ProbeErr("PCE-0500","Device does not respond",$respond) if $err=~/^5\d\d/;
  # Check for NVC and IPE
  $respond=ProbeRequest("/nvc-cgi/admin/param.cgi?action=list&group=Brand"); # If NVC
  if ($respond=~ /^HTTP ERROR/) {
    ($err)= $respond=~/^HTTP ERROR \[(\d+)\]/;
    ProbeErr("PCE-0401","Authorization error",    $respond) if $err==401;
    ProbeErr("PCE-0030","Cannot get MODELID or FIRMWARE",$respond);
  }
}

my ($m,$id)=$respond=~m{.*Brand\.prodshortname\s*=\s*(\D+)(\d+)}i;
ProbeErr("PCE-0030","Cannot get MODELID or FIRMWARE",$respond) if $m !~ /^(NVE|NVC|IPE)$/;
$result{MODELID}=$m;

# Get firmware
my $fwpath=$m eq 'NVE' ?
  "/enc-cgi/admin/param.cgi?action=list&group=Version.firmware" : # NVE
  "/nvc-cgi/admin/param.cgi?action=list&group=Version.firmware"; # NVC and IPE
$respond=ProbeRequest($fwpath);
if($respond!~ /^HTTP ERROR/) {
  $result{FIRMWARE} = $1 if $respond =~ /^Version.firmware=(.+?)\s*$/im;
}else{
  ProbeErr("PCE-0030","Cannot get MODELID or FIRMWARE",$respond);
}

# Check MODELID & FIRMWARE ---------------------------------------------------
if(defined $conf->{MODELID} and $result{MODELID} ne $conf->{MODELID}) {
  $result{STATUS}="WARNING: PCW-0001 [$conf->{DEVID}] MODELID does not match configuration";
}elsif(defined $conf->{CAMERAFIRMWARE} and $result{FIRMWARE} ne $conf->{CAMERAFIRMWARE}) {
  $result{STATUS}="WARNING: PCW-0001 [$conf->{DEVID}] FIRMWARE does not match configuration";
}

#------------------------------------------------------------------------------
# get Sample picture
#------------------------------------------------------------------------------
if($conf->{PROBE} eq 'DEFINE') {
   $result{SNAPSHOT}=ProbeSamplePicture();
}

#------------------------------------------------------------------------------
# get video and audio properties
#------------------------------------------------------------------------------

# Get list of Inputs and Outputs (NVC only)
if ($conf->{PROBE} eq 'DEFINE' and $m eq 'NVC') {
    $respond = ProbeRequest("/nvc-cgi/admin/param.fcgi?action=list&group=DIDO");
    if ($respond !~ /^HTTP_ERROR/) {
	my ($do_nbr) = $respond=~/DO\.nbrofchannel=(\d+)\s*$/im;
	$result{AE_DESCR} = to_json({
	    relay => { NUMBER => $do_nbr, PORT_NUMBERS => join(',',0..$do_nbr-1), TEMPLATE => 'GENERIC' }
	});
    }
}

#------------------------------------------------------------------------------
# interim report & exit
#------------------------------------------------------------------------------
ProbeResult(\%result)  if $conf->{PROBE} ne 'RESET';
ProbeResult(\%result)  if $conf->{DEVID}==0;

#------------------------------------------------------------------------------
# set RC (rate control) parameters + qality + imagesize
#------------------------------------------------------------------------------
my $err=0;
my $postmortem='';
my $camera=$conf->{CAMERA}-1;
$camera=0 if $camera < 0;
my $channel=$conf->{CHANNEL};
$channel=(defined($channel) and $channel ne "")?$channel:'0';

# 1. Setting codec
my $codec="";
for ($conf->{MEDIA_FORMAT}) {
	/mjpg/i  && do { $codec="mjpeg"; last };
	/mpeg4/i && do { $codec="mpeg4"; last };
	/h264/i  && do { $codec="h264" ; last };
}

my $update_codec_url;
if ($m eq 'NVE')
{
	$update_codec_url  = "/enc-cgi/admin/param.cgi?action=update&Image.I$camera.Codec=$codec";
	$respond = ProbeRequest($update_codec_url);

	if ($respond =~ /error/i)
	{
		$err++;
		$postmortem .= "Could not set codec: $respond\n";
	}
}
elsif ($m eq 'NVC' or $m eq 'IPE')
{
	# reboot encoder
	$respond = ProbeRequest("/nvc-cgi/admin/param.fcgi?action=update&group=System.Power&control=coldboot");
	if ($respond =~ /^HTTP ERROR/)
	{
	    $err++;
	    $postmortem .= "Fail to reboot encoder: $respond\n";
	} else {
	    my $test_req = "/nvc-cgi/admin/param.cgi?action=list&group=Brand";
	    for(;;sleep 2) {
		last if ProbeRequest($test_req) =~ /^HTTP ERROR/;
	    }
	    #print "#Rebooted, time=".time."\n";
	    for(;;sleep 5) {
		last if ProbeRequest($test_req) !~ /^HTTP ERROR/;
	    }
	    #print "#Recovered after reboot, time=".time."\n";

	    sleep 20; # let's wait while encoder is fully loaded
	}

	# Let's check up some encoder parameters whose
	# change demands the considerable expense of time.
	# Probably it is not necessary to change them.
	$respond = ProbeRequest("/nvc-cgi/admin/param.cgi?action=list&group=VideoCodec.Ch$channel.St$camera.standard");
	if($respond !~ /^HTTP ERROR/ and not $respond =~ /=$codec\s*$/im) {
	    $update_codec_url = "/nvc-cgi/admin/param.cgi?action=update&group=VideoCodec.Ch$channel.St$camera&enable=yes&standard=$codec";
	    $respond = ProbeRequest($update_codec_url);
	    if ($respond =~ /error/i)
	    {
		$err++;
		$postmortem .= "Could not set codec: $respond\n";
	    }
	}

	my $vca_enabled = ($conf->{"VAE_ACTIVE"} ne "no" and $conf->{"VAE_EDVA_ACTIVE"} ne "no") ? 1 : 0;
	$respond=ProbeRequest("/nvc-cgi/admin/param.cgi?action=list&group=Server");
	if($respond=~/error/i
	   or not $respond =~ /^Server.RTSP.S0.Unicast.RTP$camera.enable=yes\s*$/im
	   or not $respond =~ /^Server.RTSP.S0.Unicast.RTP$camera.name=$conf->{CAMERA}\s*$/im
	   or ($vca_enabled and not $respond =~ /^Server.RTSP.S0.Unicast.RTP$camera.includemeta=yes\s*$/im)) {
	    # changing stream name corresponding to camera number

	    if($vca_enabled) {
		$respond=ProbeRequest(
		    "/nvc-cgi/admin/param.cgi?action=update&group=Server.RTSP.S0.Unicast.RTP$camera&enable=yes&name=$conf->{CAMERA}&includemeta=yes"
		);
	    } else {
		$respond=ProbeRequest(
		    "/nvc-cgi/admin/param.cgi?action=update&group=Server.RTSP.S0.Unicast.RTP$camera&enable=yes&name=$conf->{CAMERA}"
		);
	    }

	    if ($respond =~ /error/i)
	    {
		$err++;
		$postmortem .= "Could not set stream parameters: $respond\n";
	    }
	}

	if($vca_enabled) {
	    $respond=ProbeRequest("/nvc-cgi/admin/vca.cgi?action=list&group=VCA.Ch$channel");
	    if($respond =~ /error/i
	       or not $respond =~ /^VCA.Ch$channel.enable=yes\s*$/im
	       or not $respond =~ /^VCA.Ch$channel.enablestb=yes\s*$/im
	       or not $respond =~ /^VCA.Ch$channel.enablemovobj=yes\s*$/im
	       or not $respond =~ /^VCA.Ch$channel.metafmt=object,event,/im) {
		# enable VCAsys engine
		$respond=ProbeRequest(
		    "/nvc-cgi/admin/vca.cgi?action=update&group=VCA.Ch$channel&enable=yes&enablestb=yes&enablemovobj=yes&metafmt=object,event,"
		);
		if ($respond =~ /error/i)
		{
		    $err++;
		    $postmortem .= "Could not set channel parameters: $respond\n";
		}
	    }
	}
}

# 2. Setting framerate, bitrate, resolution
my $update_codec_params_url;
my $fps = $conf->{FRAMERATE};
$fps = 1 if ($fps < 1);
my $resolution = $conf->{IMAGESIZE};
$resolution = "vga" if ($resolution =~ /hugesize/i);
$resolution = "cif" if ($resolution =~ /fullsize/i);
$resolution = "qcif" if ($resolution =~ /halfsize/i);

my $rc_mode          = $conf->{RC_MODE};
my $rc_maxbitrate    = $conf->{RC_MAXBITRATE};
my $rc_targetbitrate = $conf->{RC_TARGETBITRATE};
my $compression      = $conf->{CAMCOMPRESSION};
my $qvalue           = $conf->{QVALUE};
my $pcount           = $conf->{GOP}; # GOP settings

if ($m eq 'NVE')
{
	$update_codec_params_url = "/enc-cgi/admin/param.cgi?action=update&Image.I$camera.Appearance.Resolution=$resolution";

	$respond=ProbeRequest($update_codec_params_url);
	if ($respond=~/error/i)
	{
		$err++;
		$postmortem .= "Could not set codec resolution: $respond\n";
	}
	if ($rc_targetbitrate ne "cam-defined")
	{
		$rc_targetbitrate *= 1024;
	}
	if ($rc_maxbitrate ne "cam-defined")
	{
		$rc_maxbitrate *= 1024;
	}
	$update_codec_params_url =
	  "/enc-cgi/admin/param.cgi?action=update&Image.I$camera.RateControl.Mode=$rc_mode"
	. "&Image.I$camera.RateControl.MaxFPS=$fps"
	. ($rc_maxbitrate ne "cam-defined" ? "&Image.I$camera.RateControl.MaxBitrate=$rc_maxbitrate" : "")
	. ($rc_targetbitrate ne "cam-defined" ? "&Image.I$camera.RateControl.TargetBitrate=$rc_targetbitrate" : "")
	. ($pcount ne "cam-defined" and $conf->{MEDIA_FORMAT}=~/mpeg4/i ? "&Image.I$camera.MPEG.PCount=$pcount" : "");
}
elsif ($m eq 'NVC' or $m eq 'IPE')
{
	if ($conf->{MEDIA_FORMAT} =~ /mjpg/i)
	{
            $update_codec_params_url =
		    "/nvc-cgi/admin/param.cgi?action=update&group=VideoCodec.Ch$channel.St$camera.mjpeg"
		   ."&resolution=$resolution&quality=$compression&maxfps=$fps";
	}
	elsif ($conf->{MEDIA_FORMAT} =~ /mpeg4/i or $conf->{MEDIA_FORMAT} =~ /h264/i)
	{
		# Normalize Maximum bitrate
		if ($rc_maxbitrate ne "cam-defined")
		{
		    if ($rc_maxbitrate =~ /unlimited/i)
		    {
				$rc_maxbitrate = $rc_mode eq "cbr" ? 4096 : 0;
		    }
		    elsif ($rc_maxbitrate > 4096)
		    {
				$rc_maxbitrate = 4096;
		    }
		    elsif ($rc_maxbitrate < 128)
		    {
				$rc_maxbitrate = 128;
		    }
		}

		# Normalize Target bitrate
		if ($rc_targetbitrate ne "cam-defined")
		{
		    if ($rc_targetbitrate =~ /unlimited/i)
		    {
				$rc_targetbitrate = 4096;
		    }
		    elsif ($rc_targetbitrate > 4096)
		    {
		    	$rc_targetbitrate = 4096;
		    }
		    elsif ($rc_targetbitrate < 128)
		    {
				$rc_targetbitrate = 128;
		    }
		}

		# Normalize qvalue
		$qvalue = 1 if not defined $qvalue or $qvalue < 1;
		$qvalue = 255 if $qvalue > 255;

		# Normalize pcount
		if ($pcount ne "cam-defined")
		{
			$pcount = 0 if $pcount < 1;
			$pcount = 127 if $pcount > 127;
			$pcount = "cam-defined" if not defined $pcount;
		}

		my $codec =  "mpeg4" if ($conf->{MEDIA_FORMAT} =~ /mpeg4/i);
		$codec = "h264" if ($conf->{MEDIA_FORMAT} =~ /h264/i);

		$update_codec_params_url =
		  "/nvc-cgi/admin/param.cgi?action=update&group=VideoCodec.Ch$channel.St$camera.$codec"
		. "&resolution=$resolution&maxfps=$fps&bitratectrl=$rc_mode&qvalue=$qvalue"
		. ($rc_maxbitrate ne "cam-defined" ? "&maxbitrate=$rc_maxbitrate" : "")
		. ($rc_targetbitrate ne "cam-defined" ? "&bitrate=$rc_targetbitrate" : "")
		. ($pcount ne "cam-defined" ? "&pcount=$pcount" : "");
	}

	$respond=ProbeRequest($update_codec_params_url);
	if ($respond =~ /error/i)
	{
		$err++;
		$postmortem .= "Could not set codec bitrate: $respond\n";
	}

	$respond = `$APL/analytics/bin/nvcprog $conf->{DEVID} 2>/dev/null`;
	if($respond =~ /Status=FAIL/i) {
	    $err++;
	    $postmortem .= "Fail to reset encoder\n";
	}
}

ProbeErr("PCE-0040","Cannot update video settings or fail to reboot encoder", $postmortem) if $err;
$result{RC_SET}="OK";
#------------------------------------------------------------------------------
# set AudioParams
#------------------------------------------------------------------------------
if ( $conf->{AUDIO} eq 'on' ) {
# no code for UDP
}
# final report-----------------------------------------------------------------------
ProbeResult(\%result);

