#!/usr/bin/php
<?php
#
# input-play - Play Line-Input to specified Multicast IP Addresses
#
# This script will kick off a media stream from Line-Input to specified
# Multicast IP Addresses using ffmpeg to create a stream using opus codec.
#
# Usage: input-play <command>;<input_channel>;<destination>;<duration>
#
#   <command>       - Action to take, one of: stream, pause, resume, stop
#   <input_channel> - The Input-Channel to use for streaming
#   <destination>   - List of multicast IP addresses to stream to or Line-Output
#

$debug = TRUE;

if ($argc < 2) exit;

# Turn off error reporting if not in debug mode. We do this to prevent errors
# from filling up the disk if cron runs the script and sends emails on
# error output.
#
if ($debug == FALSE) {
	error_reporting(0);
}

$pid_dir = "/var/opt/nyquist/streams";
$stream_pid = 0;
$duration = 0;
$streamDest = "";

$parts = preg_split("/[;]+/",$argv[1]);

if (count($parts) < 2) exit;

if ($debug) print_r($parts);

$command = $parts[0];
$channel = $parts[1];

$pid_file = "$pid_dir/channels-$channel.pid";

if (!file_exists($pid_dir))
{
	mkdir($pid_dir, 0770, true);
}

# If the number of parts is < 3, the command is probably not a 'stream' command.
if (count($parts) < 3)
{
	
	# Make sure pid file exists before processing pause/resume/stop command.
	#if (!file_exists($pid_file))
	if (($pid_fd = fopen($pid_file, "r")) == FALSE)
	{
		if ($debug) echo "PID file $pid_file does not exist\n";
		exit;
	}

	$stream_pid = fgets($pid_fd);
	fclose($pid_fd);

	switch($command)
	{
		case "stream":
			echo "Missing multicast addresses for OPUS, exiting\n";
			break;
		case "stream-722":
			echo "Missing multicast addresses for G722, exiting\n";
			break;
		case "pause":
			if ($debug) echo "Pause\n";
			if ($stream_pid != 0) {
				posix_kill((int)$stream_pid, SIGSTOP);
			}
			break;
		case "resume":
			if ($debug) echo "Resume\n";
			if ($stream_pid != 0) {
				posix_kill((int)$stream_pid, SIGCONT);
			}
			break;
		case "stop":
			if ($debug) echo "Stop\n";
			if ($stream_pid != 0) {
				posix_kill((int)$stream_pid, SIGKILL);
			}
			unlink($pid_file);
			break;
		default:
			echo "Invalid command";
			break;
	}
	exit;
}

#
# Start Stream...
#
# If $pid_file exists, check to see if stream is still running,
# bail if stream is still running; otherwise delete existing pid file.
#
if (file_exists($pid_file))
{
	if (($pid_fd = fopen($pid_file, "r")) != FALSE)
	{
		$stream_pid = fgets($pid_fd);
		if ($stream_pid != "" && posix_getpgid($stream_pid) != FALSE)
		{
			echo "Already streaming to $channel, exiting\n";
			exit;
		}
		else
		{
			fclose($pid_fd);
			unlink($pid_file);
		}
	}
}

if (($pid_fd = fopen($pid_file, "x")) == FALSE) exit;

# Parse Multicast address list and Duration...
$addrStart = 0;
$numZones = 0;

for ($addrIdx = 2; $addrIdx < count($parts); $addrIdx++)
{
	if (preg_match("/(.*):(.*)/", $parts[$addrIdx], $addrParts))
	{
		if ($debug) echo "Addr: $parts[$addrIdx]\n";

		if ($addrStart == 0)
		{
			$addrStart = $addrIdx;
		}

		$addrEnd = $addrIdx;
		$numZones++;
	}
	else
	{
		if ($debug) echo "Duration: $parts[$addrIdx]\n";
		$duration = $parts[$addrIdx];
	}
}

if (($addrStart == 0) || ($numZones == 0))
{
	fclose($pid_fd);
	unlink($pid_file);
	if ($debug) echo "No multicast addresses, exiting\n";
	exit;
}

# ffmpeg PID
$stream_pid = 0;

if ($command == "stream")
{
  $filterSettings = "-filter_complex anull";
  $bitEncoding = "-vbr off -payload_type 120 -compression_level 4";
  $chanEncoding = "-ac 1 -b:a 48000";
  $chanDecoding = "-ac 1";
  $streamDest = "$filterSettings -acodec libopus $bitEncoding $chanEncoding -vn -f tee \"";
}
elseif ($command == "stream-g722")
{
  $filterSettings = "-filter_complex 'aresample=16000'";
  $chanEncoding = "-ac 1 ";
  $chanDecoding = "-ac 1";
  $streamDest = "$filterSettings -acodec g722 $chanEncoding -vn -f tee \"";
}
elseif ($command == "stream-ulaw")
{
  $filterSettings = "-filter_complex anull";
  $chanEncoding = "-ac 1 -fflags +genpts -ar 8000 -b:a 64k";
  $chanDecoding = "-ac 1 -channel_layout mono -ar 8000";
  $streamDest = "$filterSettings -acodec pcm_mulaw $chanEncoding -vn -f tee \"";
}
elseif ($command == "stream-alaw")
{
  $filterSettings = "-filter_complex anull";
  $chanEncoding = "-ac 1 -fflags +genpts -ar 8000 -b:a 64k";
  $chanDecoding = "-ac 1 -channel_layout mono -ar 8000";
  $streamDest = "$filterSettings -acodec pcm_alaw $chanEncoding -vn -f tee \"";
}

$sep = ""; 
for ($addrIdx = $addrStart; $addrIdx <= $addrEnd; $addrIdx++)
{
	preg_match("/(.*):(.*)/", $parts[$addrIdx], $addrParts);
	$multicastIp   = $addrParts[1];        
	$multicastPort = $addrParts[2];

	$streamDest.= "$sep"."[f=rtp]udp://$multicastIp:$multicastPort";
	$sep = "|";
}

$streamDest.= "\"";

$streamSrc = "-f alsa $chanDecoding -i record_ch$channel";

if ($debug)
{
	exec("/tmp/test > /dev/null 2>&1 & echo $!", $pid);
}

exec("ffmpeg $streamSrc $streamDest >/dev/null 2>&1 & echo $!", $pid);

$stream_pid = $pid[1];
exec("renice -2 -p $stream_pid");

if ($debug) echo "Stream-PID: $stream_pid\n";

fwrite($pid_fd, $stream_pid);
fclose($pid_fd);

#
# If duration > 0, we need to sleep $duration seconds, then kill the ffmpeg
# process and unlink the PID file.
if ($duration > 0)
{
	if ($debug) echo "Sleeping $duration seconds\n";

	sleep($duration);

	# After sleep, make sure PID file is still there and it's still for
	# the stream_pid that we started.

	if (($pid_fd = fopen($pid_file, "r")) != FALSE)
	{
		$file_stream_pid = fgets($pid_fd);
		if ($file_stream_pid == $stream_pid)
		{
			posix_kill((int)$stream_pid, SIGSTOP);
			if ($debug) echo "Killed player\n";
		}
		fclose($pid_fd);
		unlink($pid_file);
		if ($debug) echo "Unlinked PID file\n";
	}
	
}

exit;

###############
# END OF MAIN #
###############

?>
