#!/usr/bin/perl -w
#  $Id: probe 19769 2010-08-26 21:10:02Z atsibulnik $
# -----------------------------------------------------------------------------
#  Purpose:
#    - probe the camera
#    - pre-tune camera befor for retriever (RC and Audio)
# -----------------------------------------------------------------------------
#  Call:
#    probe DEVID=123   
#    probe DEVID=123 PROBE=FAST  
#    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
#       STATUS=WARNING: PCW-0009 [101] PROBE=RESET is not supported
#       
# -----------------------------------------------------------------------------
#  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 Socket qw(:DEFAULT);
use Errno 'EADDRINUSE';
use HTML::Parser;
use Node::Conf;
use SKM::DB;
use Device::Conf;
#use Data::Dumper;
# cons -------------------------------------------------------------------------
# no code for Cornet

# vars -------------------------------------------------------------------------

my $UDP_PORT_BASE = 16400;
my $MAX_PORT = 65535;

# Network settings
my %EncEther = (
    netIpAddress  => undef,
    netSubnet     => undef,
    netGateway    => undef,
    netMacAddr    => undef,
    vdoTxIpAddr   => undef,
    vdoTxIpPort   => undef,
    vdoTxTtl      => '32',
    vdoTxProto    => '1',   	# 0 - UDP, 1 - RTP
    vdoMaxPakSize => '1500',
    nwSubmit      => 'Submit'
);
# Video settings
my %VideoEnc = (
    vidEncCompre    => '0',	# H.264 only
    vidEncMediaTyp  => '1',	# 0 - Audio-Video, 1 - Video only
    vidEncStrmTyp   => '1',	# 0 - Elementary, 1 - Transport
    vidEncResl      => '4',	# 0 - D1, 1 - VGA, 2 - Half D1, 3 - CIF, 4 - QCIF
    vidEncFrRate    => '30',	# 1 to 30
    vidEncBitRate   => '3000',	# 64 to 6500 Kbps
    vidEncRateMode  => '0',	# 0 - Constant, 1 - Variable
    vidEncI2pRatio  => '30',
    vidEncFieldCode => '0',	# 0 - Frame, 1 - Field, 2 - Adaptive
    vidScanMode     => '0',	# 0 - Interlace, 1 - Progressive
    vidDeBlockSts   => '0',	# 0 - Disable, 1 - Enable
    vidEncSts       => '1',	# 0 - Disable, 1 - Enable
    viEncSet        => 'Submit'
);

# subroutines ------------------------------------------------------------------
sub get_own_ip
{
    my $dbm;
    my $ip = eval {
	my $dbm = DBMaster({PrintError=>0,RaiseError=>1});
	my $ra = $dbm->selectall_arrayref(
	    "SELECT val AS ip FROM _objs o 
		INNER JOIN _obj_attr oa ON o.obj=oa.obj
	    WHERE otype='D' and subtype='N' 
		and o.name=? and attr='IP'",
	    undef,
	    UNI(),
	);
	$ra->[0][0];
    };
    my $err = $@;
    chomp $err;
    eval { $dbm->disconnect } if $dbm;
    ProbeErr("Unable to get own IP: $err") if $err;
    return $ip;
}

sub udp_port_search
{
    my $protocol = getprotobyname('udp');
    my $port = $UDP_PORT_BASE;
    my $ok = 0;
	
    socket(SOCK, AF_INET, SOCK_DGRAM, $protocol)
	or return undef;
    while ($port <= $MAX_PORT && !$ok) {
	my $local_addr = sockaddr_in($port, INADDR_ANY);
	bind(SOCK, $local_addr) or do {
	    $port++,next if $! == EADDRINUSE;
	    last;
	};
	$ok = 1;
    }
    close SOCK;
	
    return $ok ? $port : 0;
}

# --------- check required parameters: DEVID,USRNAME,PASSWD,
my $conf=ProbeInit();  # uses <BRAND> from path and ARGV
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};
ProbeWarn("PCW-0009","PROBE=RESET is not supported") if $conf->{PROBE} eq 'RESET';
#------------------------------------------------------------------------------
# Probe camera MODELID & FIRMWARE 
#------------------------------------------------------------------------------
my %result=(MODELID=>'0',FIRMWARE=>'0.0',STATUS=>'OK',RC_SET=>'NONE',AUDIO_SET=>'NONE');

# Model
my $respond =ProbeRequest("/VideoEnc.ssi");
if($respond!~ /^HTTP ERROR/) {
  $result{MODELID} = "iVDO 264" if $respond =~ /iVDO 264/i;
}else {
  my ($err)= $respond=~/^HTTP ERROR \[(\d+)\]/;
  ProbeErr("PCE-0500","Device does not respond",$respond) if $err=~/^5\d\d/;
  ProbeErr("PCE-0401","Authorization error",    $respond) if $err==401;
}
ProbeErr("PCE-0030","Cannot get MODELID or FIRMWARE",$respond) if ! $result{MODELID};

# Firmware
$result{FIRMWARE} = "2.48"; # Single model and single firmware supported

# 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
#------------------------------------------------------------------------------

# no code for Cornet

#------------------------------------------------------------------------------
# interim report & exit 
#------------------------------------------------------------------------------
ProbeResult(\%result)  if $conf->{PROBE} =~ /^(DEFINE|FAST)$/;
ProbeResult(\%result)  if $conf->{DEVID}==0;

#------------------------------------------------------------------------------
# set RC (rate control) parameters + quality + imagesize
#------------------------------------------------------------------------------

# Fetch current Network settings
$respond = ProbeRequest("/EncEther.ssi");
if ($respond !~ /^HTTP ERROR/) {
    my $parser = HTML::Parser->new(
	api_version => 3, 
	start_h => [ 
	    sub {
		my ($tagname,$attr) = @_;
	        return if $tagname !~ /^input$/i;
	        return unless defined $attr->{id} and defined $attr->{value};
	        return unless exists $EncEther{$attr->{id}};
	        $EncEther{$attr->{id}} = $attr->{value};
	    }, 
	    "tagname,attr" 
	]
    );
    $parser->parse($respond);
}
# Set Target IP Address
$EncEther{vdoTxIpAddr} = get_own_ip;
ProbeErr("Failed to get own IP address!") unless $EncEther{vdoTxIpAddr};
# Set Transfer Port Number
my $udp_port = udp_port_search;
ProbeErr("Failed to find free udp port for streaming!") unless $udp_port;
$EncEther{vdoTxIpPort} = $udp_port;
$result{RTP_UNICAST_PORT} = $udp_port;
# Validate values received from encoder
ProbeErr("Failed to get parameter value for '$_'") 
    foreach grep { not defined $EncEther{$_} } ('netIpAddress','netSubnet','netGateway','netMacAddr');
    
# Update Network settings
ProbeRequest(
    "/cdx_enc_ether_action",
    'POST',
    ['Content-Type'=>'application/x-www-form-urlencoded'],
    \%EncEther
);

# Wait a little
sleep 3;

# Update Video settings
$VideoEnc{vidEncMediaTyp} = $conf->{AUDIO} eq 'on' ? '0' : '1'; # Video/Audio
$VideoEnc{vidEncResl} = $conf->{IMAGESIZE}; # Resolution
$VideoEnc{vidEncFrRate} = $conf->{FRAMERATE}; # Framerate
$VideoEnc{vidEncRateMode} = $conf->{RC_MODE} eq 'cbr' ? '0' : '1'; # Bit Rate mode
$VideoEnc{vidEncI2pRatio} = $conf->{I2P_RATIO};
# Validate camera bitrate
my $bitrate = $conf->{RC_TARGETBITRATE};
$bitrate = '64'   if $bitrate < 64;
$bitrate = '6500' if $bitrate > 6500;
$VideoEnc{vidEncBitRate} = $bitrate;
# Send request
$respond = ProbeRequest(
    "/cdx_video_encoder_action",
    'POST',
    ['Content-Type'=>'application/x-www-form-urlencoded'],
    \%VideoEnc
);
$result{RC_SET}="OK\n#".join("\n#",split(/\n/,$respond));

# Reboot and wait
#$respond = ProbeRequest("/EncReset.ssi");
#sleep 5;

#------------------------------------------------------------------------------
# set AudioParams
#------------------------------------------------------------------------------
if ( $conf->{AUDIO} eq 'on' ) { 
# no code for Cornet
}
# final report-----------------------------------------------------------------------
ProbeResult(\%result);
