<?php
/* 
 * functions.php is created by Jai Dhar, FPS-Tech, for use with eyeZm
 * iPhone application. This is not intended for use with any other applications,
 * although source-code is provided under GPL.
 *
 * For questions, please email support@eyezm.com (http://www.eyezm.com)
 *
 */
/* These functions are taken from functions.php */
function validInteger( $input )
{
    return( preg_replace( '/\D/', '', $input ) );
}

function validString( $input )
{
    return( strip_tags( $input ) );
}
/*
 * Escape an SQL string, with optional parameters for max. length 
 */
function escapeSql($str, $maxlen = 0) {
	if (!$maxlen) $maxlen = 512;
	$string = substr($str, 0, $maxlen);
	return (get_magic_quotes_gpc())?mysql_real_escape_string(stripslashes($string)):mysql_real_escape_string($string);
}
/* There appears to be some discrepancy btw. 1.24.1/2 and .3 for EventPaths, to escape them here */
function getEventPathSafe($event)
{
	if (ZM_USE_DEEP_STORAGE) {
		$ret = ZM_DIR_EVENTS."/".$event['MonitorId'].'/'.strftime( "%y/%m/%d/%H/%M/%S", strtotime($event['StartTime']) );
	} else {
		$ret = ZM_DIR_EVENTS."/".$event['MonitorId']."/".$event['Id'];
	}
	return $ret;
}
function updateClientVer()
{
	$str = $_SERVER['HTTP_USER_AGENT'];
	/* Check if it starts with eyeZm */
	if (!strcmp(substr($str, 0, 5),"eyeZm")) {
		/* Found eyeZm */
		$ver = substr($str, 6);
		$verarray = explode(".", $ver);
		$_SESSION['vermaj']=$verarray[0];
		$_SESSION['vermin']=$verarray[1];
		$_SESSION['verbuild']=$verarray[2];
	}
	logXml("(".$_SERVER['REMOTE_ADDR'].") GET: ".$_SERVER['REQUEST_URI']." - eyeZm ".getClientVerMaj().".".getClientVerMin());
}
function getClientVerMaj()
{
	if (isset($_SESSION['vermaj'])) return $_SESSION['vermaj'];
	return "0";
}
function getClientVerMin()
{
	if (isset($_SESSION['vermin'])) return $_SESSION['vermin'];
	return "0";
}
function requireVer($maj, $min)
{
	if (getClientVerMaj() > $maj) return 1;
	if ((getClientVerMaj() == $maj) && (getClientVerMin() >= $min)) return 1;
	return 0;
}
function logXmlErr($str)
{
	logXml($str, 1);
}
function logXml($str, $err = 0)
{
	if (!defined("ZM_EYEZM_DEBUG")) {
		/* Check session variable */
		if (isset($_SESSION['xml_debug'])) define("ZM_EYEZM_DEBUG", $_SESSION['xml_debug']);
		else define ("ZM_EYEZM_DEBUG", "0");
	}
	if (!defined("ZM_EYEZM_LOG_TO_FILE")) {
		/* Check session variable */
		if (isset($_SESSION['xml_log_to_file'])) define("ZM_EYEZM_LOG_TO_FILE", $_SESSION['xml_log_to_file']);
		else define ("ZM_EYEZM_LOG_TO_FILE", "1");
	}
	if (!defined("ZM_EYEZM_LOG_FILE")) {
		/* Check session variable */
		if (isset($_SESSION['xml_log_file'])) define("ZM_EYEZM_LOG_FILE", $_SESSION['xml_log_file']);
		else define ("ZM_EYEZM_LOG_FILE", "/tmp/zm_xml.log");
	}
	/* Only log if debug is enabled */
	if (ZM_EYEZM_DEBUG == 0) return;
	/* Logging is enabled, set log string */
	$logstr = "XML_LOG (".($err?"ERROR":"NOTICE")."): ".$str.(ZM_EYEZM_LOG_TO_FILE?"\n":"");
	if (ZM_EYEZM_LOG_TO_FILE) {
		error_log("[".date("r")."] ".$logstr, 3, ZM_EYEZM_LOG_FILE);
	} else {
		error_log($logstr);
	}
}
/* Returns defval if varname is not set, otherwise return varname */
function getset($varname, $defval)
{
	if (isset($_GET[$varname])) return $_GET[$varname];
	return $defval;
}
function xml_header()
{
	header ("content-type: text/xml");
	echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
}
function xml_tag_val($tag, $val)
{
	echo "<".$tag.">".$val."</".$tag.">";
}
function xml_tag_sec($tag, $open)
{
	if ($open) $tok = "<";
	else $tok = "</";
	echo $tok.$tag.">";
}
function xhtmlHeaders( $file, $title )
{
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<style type="text/css">
body {
	border: 0px solid;
	margin: 0px;
	padding: 0px;
}
</style>
	<script type="text/javascript">
	</script>
</head>
<?php
}
/** Returns whether necessary components for H264 streaming
 * are present */
function canStream264($sup = 0) {
	if (!ffmpegSupportsCodec("libx264")) {
		if (!$sup) logXmlErr("FFMPEG not installed, accessible in path/ZM_PATH_FFMPEG, or doesn't support libx264");
		return FALSE;
	}
	/* Make sure segmenter exists */
	if (!exeExists(shell_exec("which segmenter"))) {
		if (!$sup) logXmlErr("HTTP segmenter not installed or not accessible in path");
		return FALSE;
	}
	/* Check for zmstreamer */
	if (!exeExists(shell_exec("which zmstreamer"))) {
		if (!$sup) logXmlErr("ZMSTREAMER not installed or not accessible in path");
		return FALSE;
	}
	return TRUE;
}
/* Returns the path of ffmpeg by using define */
function getFfmpegPath()
{
	if (defined("ZM_PATH_FFMPEG")) {
		return ZM_PATH_FFMPEG;
	} else {
		/* Not defined, get it from using 'which' */
		return shell_exec("which ffmpeg");
	}
}
/* Returns whether ffmpeg supports a given codec. Takes into account
 * whether FFMPEG exists or not */
function ffmpegSupportsCodec($codec)
{
	if (!ffmpegExists()) return FALSE;
	/* FFMPEG exists */
	if (preg_match("/\b".$codec."\b/", shell_exec(getFfmpegPath()." -codecs 2> /dev/null")) > 0) {
		/* More than one match */
		return TRUE;
	} else {
		/* Check -formats tag also if we fail -codecs */
		if (preg_match("/\b".$codec."\b/", shell_exec(getFfmpegPath()." -codecs 2> /dev/null")) > 0) return TRUE;
		return FALSE;
	}
}
function exeExists($exepath)
{
	$path = trim($exepath);
	return (file_exists($path) && is_readable($path) && ($path != ""));
}
/* Returns whether ffmpeg exists or not */
function ffmpegExists()
{
	return exeExists(getFfmpegPath());
}
/* Returns with PHP-GD exists */
function gdExists()
{
	if (extension_loaded('gd') && function_exists('gd_info')) {
		return TRUE;
	}
	return FALSE;
}
	
function getFfmpeg264FoutParms($br, $fout)
{
	$ffparms = "-analyzeduration 0 -acodec copy";
	$ffparms .= " -vcodec libx264 -b ".$br;
	$ffparms .= " -flags +loop -cmp +chroma -partitions +parti4x4+partp8x8+partb8x8";
        $ffparms .= " -subq 5 -trellis 1 -refs 1 -coder 0 -me_range 16 -keyint_min 25";
        $ffparms .= " -sc_threshold 40 -i_qfactor 0.71 -bt 16k";
	$ffparms .= " -rc_eq 'blurCplx^(1-qComp)' -qcomp 0.6";
	$ffparms .= " -qmin 10 -qmax 51 -qdiff 4 -level 30";
	$ffparms .= " -g 30 -analyzeduration 0 -async 2 ".$fout." 2> /dev/null";
	return $ffparms;
}
/** Return FFMPEG parameters for H264 streaming */
function getFfmpeg264Str($width, $height, $br, $fin, $fout)
{
	$ffparms = getFfmpeg264FoutParms($br, $fout);
	$ffstr = getFfmpegPath()." -t ".ZM_EYEZM_H264_MAX_DURATION." -analyzeduration 0 -i ";
	$ffstr .= $fin." -f mpegts ".$ffparms;
	return $ffstr;
}
/** Returns true when monitor exists */
function isMonitor($monitor)
{
	$query = "select Id from Monitors where Id = ".$monitor;
	$res = dbFetchOne(escapeSql($query));
	if ($res) return TRUE;
	logXml("Monitor ID ".$monitor." does not exist");	
	return FALSE;
}
/** Returns the width and height of a monitor */
function getMonitorDims($monitor)
{
	$query = "select Width,Height from Monitors where Id = ".$monitor;
	$res = dbFetchOne(escapeSql($query));
	return $res;
}
/** Returns the temp directory for H264 encoding */
function getTempDir()
{
	/* Assume that the directory structure is <base>/skins/xml/views */
	return dirname(__FILE__)."/../../../temp";
}
/** Returns the name of the m3u8 playlist based on monitor */
function m3u8fname($monitor) {
	return "stream_".$monitor.".m3u8";
}

/** Erases the M3u8 and TS file names for a given monitor */
function eraseH264Files($monitor) {
	/** NOTE: This command executes an 'rm' command, so $monitor parameter
	 * should be properly validated before executing */
	/* Remove wdir/.m3u8 and wdir/sample_<mon>*.ts */
	shell_exec("rm -f ".getTempDir()."/".m3u8fname($monitor)." ".getTempDir()."/sample_".$monitor."*.ts");
}
function kill264proc($monitor) {
	/** NOTE: This command executes an 'kill' command, so $monitor parameter
	 * should be properly validated before executing */
	$pid = trim(shell_exec("pgrep -f -x \"zmstreamer -m ".$monitor."\""));
	if ($pid == "") {
		logXml("No PID found for ZMStreamer to kill");
	} else {
		shell_exec("kill -9 ".$pid);
		logXml("Killed process ".$pid." for Monitor ".$monitor);
	}
}
/** Return the command-line shell function to setup H264 stream */
function stream264fn ($mid, $width, $height, $br) {
	$cdir = "./temp";
	$zmstrm = "zmstreamer -m ".$mid." 2> /dev/null";
	$ffstr = getFfmpeg264Str($width, $height, $br, "-", "-");
	$seg = "segmenter - ".ZM_EYEZM_SEG_DURATION." ".$cdir."/sample_".$mid." ".$cdir."/".m3u8fname($mid)." ../ 2> /dev/null";
	$url = $zmstrm . " | ".$ffstr." | " . $seg;
	return "nohup ".$url." & echo $!";
}

/** Generate the web-page presented to the viewer when using H264 */
function h264vidHtml($width, $height, $monitor, $br, $thumbsrc) {
	function printTermLink() {
		$str = "H264 Streaming Launching...<br>Tap to re-load if stream fails";
		$str2 = "document.getElementById(\"loaddiv\").innerHTML = \"".$str."\";";
		echo $str2;

	}
	$ajaxUrl = "?view=actions&action=spawn264&&monitor=".$monitor."&br=".$br;
	/* Call these two directly to bypass server blocking issues */
	$ajax2Url = "./skins/xml/views/actions.php?action=chk264&monitor=".$monitor;
	$ajax2Url .= "&timeout=".ZM_EYEZM_H264_TIMEOUT;
	$ajax3Url = "./skins/xml/views/actions.php?action=kill264&monitor=".$monitor;
?>
<html>
<head>
	<script type="text/javascript">
	/* Called when paused or done is pressed */
	function vidAbort() {
		document.getElementById('viddiv').style.display = 'none';
		var pElement = document.getElementsByTagName('video')[0];
		var ajaxKill = new AjaxConnection("<?php echo $ajax3Url;?>");
		ajaxKill.connect("cbKilled");
		pElement.stop();
		pElement.src="";
		
	}
	function reloadStreamImage() {
		var obj = document.getElementById('liveStream');
		var src = obj.src;
		var date = new Date();
		obj.src = src + '&vrand=' + date.getTime();
		return false;
	}
	/* Callback when spawn264 process is ended */
	function cbVidLoad()
	{
		reloadStreamImage();
<?php 
		printTermLink(); 
?>
	}
	function vidLoaded() {
		window.setTimeout("startVid()", 500);
	}
	function bindListeners()
	{
		var pElement = document.getElementsByTagName('video')[0];
		/* Bind abort */
		pElement.addEventListener('abort', vidAbort, false);
		pElement.addEventListener('done', vidAbort, false);
		pElement.addEventListener('ended', vidAbort, false);
		pElement.addEventListener('pause', vidAbort, false);
		pElement.addEventListener('loadstart', vidLoaded, false);
	}
	/* Callback when kill264 process is ended */
	function cbKilled()
	{
		<?php printTermLink(); ?>
	}
	/* Called after an interval from cbFileExists() */
	function loadVid()
	{
		var pElement = document.getElementById("vidcontainer");
<?php
		echo "pElement.src=\"./temp/".m3u8fname($monitor)."\"\n";
?>
		pElement.load();
	}
	function startVid()
	{
		document.getElementById('viddiv').style.display = 'block';
		var pElement = document.getElementById("vidcontainer");
		pElement.play();
	}
	/* Callback when stream is active and ready to be played */
	function cbFileExists()
	{
		window.setTimeout("loadVid()", 500);
	}
	/* On-load triggers two requests immediately: spawn264 and chk264 */
	window.onload = function() {
		bindListeners();
		var ajax1 = new AjaxConnection("<?php echo "$ajaxUrl";?>");
		var ajax2 = new AjaxConnection("<?php echo "$ajax2Url";?>");
		ajax1.connect("cbVidLoad");
		/* Don't initiate file-exists since eyeZm will */
		/*ajax2.connect("cbFileExists");*/
	}
	function AjaxConnection(url) {
		this.connect = connect;
		this.url = url;
	}
	function connect(return_func) {
		this.x = new XMLHttpRequest();
		this.x.open("GET", this.url, true);
		var self = this;
		this.x.onreadystatechange = function() {
			if (self.x.readyState != 4)
				return;
			eval(return_func + '()');
			delete self.x;
		}
		this.x.send(null);
	}
	</script>
<style type="text/css">
body {
	border: 0px solid;
	margin: 0px;
	padding: 0px;
	background-color: black;
	width: <?php echo $width ?>px;
	height: <?php echo $height ?>px;
}
.textcl {
	text-align: center;
	font-family: Arial;
	font-size: larger;
	width: 100%;
<?php echo "padding-top: ".(($height/2)-100)."px;"; ?>
<?php echo "padding-bottom: ".(($height/2)-100)."px;"; ?>
	z-index: 2;
	position: absolute;
	top: 0px;
	left: 0px;
	height: 100%;
}
.textcl2 {
	width: auto;
	height: auto;
	background-color: black;
	padding: 5px 5px;
	margin-left: 10px;
	margin-right: 10px;
	opacity: 0.7;
}
.textcl3 {
	width: auto;
	height: auto;
	padding: 2px 2px;
	margin: auto;
	color: white;
}
.imgdiv {
	position: absolute;
	padding: 0px;
	background-color: black;
	top: 0px;
	left: 0px;
	margin: 0px;
	width: <?php echo $width ?>px;
	height: <?php echo $height ?>px;
	z-index: 1;
	opacity: 0.7;
}

</style>
</head>
<body>
<div id="viddiv" style="display: none;">
<?php
		echo "<video id=\"vidcontainer\" width='".$width."' height='".$height."' />\n";
?>
</div>
<div id="loaddiv2" class="textcl"><div id="loaddiv3" class="textcl2">
<div id="loaddiv" class="textcl3">
Initializing H264 Stream (<?php echo($br); ?>)...<br>
<span style="font-size: small;"><i>This may take a few seconds</i></span>
</div>
</div></div>

<div class="imgdiv" id="imagediv">
<?php outputImageStream("liveStream", $thumbsrc, $width, $height, "stream"); ?>
</div>
</body>
</html>
<?php
}
?>
