<?php
/*
MP3 STREAMING SERVER
Copyright Ivun 2002
*/
//error_reporting (E_NONE);

/* Allow the script to hang around waiting for connections. */
set_time_limit (0);

/* Turn on implicit output flushing so we see what we're getting
 * as it comes in. */
ob_implicit_flush ();

//$filename = "fd.mp3";
//$bitrate = 128;
//$filename = "2.mp3";
$bitrate = 128;

$smallbufsize = $bitrate*80;

$bufsize = $bitrate*1024/8;
$overbuffer = 1.3; // the value must be between 1 and 1.5

$shuffle = true;

$is_open = false;

$outbufsize = round($bufsize*$overbuffer);


$address = '127.0.0.1';
//$address = '192.168.4.142';
$port = 2222;


$playlist = fopen("../radio.pls", "w");
	fwrite($playlist, "[playlist]\r\nnumberofentries=1\r\nversion=2\r\nFile1=http://$address:$port\r\nTitle1=Ivun's Broadcasting\r\nLength1=-1");
fclose($playlist);

if (($sock = socket_create (AF_INET, SOCK_STREAM, 0)) < 0) {
    echo "socket_create() failed: reason: " . socket_strerror ($sock) . "\n";
}

if (($ret = socket_bind ($sock, $address, $port)) < 0) {
    echo "socket_bind() failed: reason: " . socket_strerror ($ret) . "\n";
}

if (($ret = socket_listen ($sock, 5)) < 0) {
    echo "socket_listen() failed: reason: " . socket_strerror ($ret) . "\n";
}

//$sock = socket_create_listen($port, 5);
//socket_setopt($sock, SOL_SOCKET, SO_REUSEADDR, -1);


//$master = socket_fd_alloc();
//$w_fds = socket_fd_alloc();

//socket_fd_zero($master);    // clear the master and temp sets
//socket_fd_zero($w_fds);

echo "<b>Began listening $address:$port. Listener is $sock</b><BR>";

$output = array();
$input = array();

$timer = time();
$status = 0;
$plsnum = 0;

load_playlist ("hi.pls"); //here is the playlist we are going to play

if ($shuffle) {
	srand ((float)microtime()*1000000);
	shuffle ($pls);
}

$fset = array($sock);
//$fdmax = $sock;

do {
	//sleep(1);
        //echo "<H1>Pass $br</h1>" . time() . "<BR>\n";
	unset($r_fds, $w_fds);
	$r_fds = array();
	$w_fds = array();
	//here is the very strange thing:
	foreach ($fset as $fd) {
		if ($input[$fd]=="w") { //sockets need to be checked for non-blocked writing
			$w_fds[] = $fd;
		} else { // ...or reading
			$r_fds[] = $fd;
		}
	}
	unset ($fd);


	$res = socket_select($r_fds, $w_fds, $e = NULL, NULL);
	if (false === $res) {
	    echo "socket_select() failed, reason: " . socket_strerror(socket_last_error()) . "<BR>\n";
	}
    unset($res);
              //var_dump ($r_fds);
              //var_dump ($w_fds);
              //echo "<BR>\n";

 	foreach($r_fds as $i) {
		if ($i == $sock) { //the listener!
			// handle new connections
			if (($newfd = socket_accept($sock)) < 0) {
					echo "socket_accept() failed: reason: " . socket_strerror ($newfd) .. "\n";
			        //break;
			} else { //accepted new connection
				array_push ($fset, $newfd);
				/*if ($newfd > $fdmax) {    // keep track of the maximum
					$fdmax = $newfd;
				}*/
				socket_getpeername($newfd, $addr);
				//socket_set_nonblock($newfd);
				echo "Accepted new connection from $addr ($newfd)<BR>";
			}
		} else {
			// handle data from a client
			if ($input[$i]!="w") { //unwritable
			        $buf = "";
			        if (FALSE === ($buf = socket_read ($i, 2048))) {
			                if ($res==false) { //kill the socket
			                        socket_kill_my_socket($i);
			                }
			        }
			        $input[$i] .= $buf;
			        if (substr($input[$i], 0 , 4) == "kill") { //killing the serve
						echo "<b>The server is going down...</b><br>";
						foreach($fset as $kl) {
							socket_kill_my_socket($kl);
						}
						unset ($input, $output, $fset, $r_fds, $w_fds);
						echo "<b>The server is down ;)</b><BR>";
						exit;
			        }
			        if (substr($input[$i], -4) == "\r\n\r\n") {
			                echo "Socket $i became writable! Recieved data: <blockquote>" . nl2br($input[$i]) . "</blockquote>\n";
			                $input[$i] = "w";
			                $songbuf = "ICY 200 OK
icy-notice1:This stream requires Winamp
icy-name:Ivun's broadcasting
icy-genre:Pop, dance, rock, hits
icy-url:http://mp3.lytkarino.ru
icy-pub:1
icy-br:$bitrate\r\n\r\n$out_buf";
					socket_write ($i, $songbuf, strlen ($songbuf));
					}
				}
			}
	}
	unset($i);
	
	
	if ($timer<=time()) { //updating buffers
		//checking if the buffers need to be updated
		$is_need_update = false;
		foreach($fset as $i) {
			if ($input[$i]=="w") { //writable socket
				if (strlen($output[$i]) < $outbufsize) $is_need_update = true;
			}
		}
		unset($i);
		
		if ($is_need_update) {
			/*
			$ln=0;
			foreach(array_values(get_defined_vars()) as $a) {
				$ln += strlen($a);
			}
			echo "Memory: $ln<BR>";
			*/
			
			/*echo "<PRE>";
			var_dump(get_defined_vars());
			echo "</PRE>";
			*/
			
			//echo "<i>out=$output[$sock]</i><BR>";
			$curfile = $pls[$plsnum];
			$outsize = (filesize($curfile)-128)-$status;
			if ($outsize>$outbufsize) $outsize=$outbufsize;
			//echo "outsize=$outsize<BR>";
			
			if (!$is_open) {
				$outfile = fopen($curfile, "rb");
				$is_open = true;
			}
//			fseek($outfile, $status);
			$out_buf = fread($outfile, $outsize);
			$status += $outsize;
			
			if ($outsize<$outbufsize) { //EOF of the current file
				//swithcing to the next one
				$status = 0;
				$plsnum++;
				if ($plsnum>=count($pls)) $plsnum=0; //this was the last song in playlist
				echo "<b>Playing " . $pls[$plsnum] . "</b> (" . time() . ")<BR>";
				fclose($outfile);
				$is_open=false;
			}
			
			foreach($fset as $i) {
				if ($input[$i]=="w") { //writable socket
					$output[$i] .= $out_buf;
					//echo "Added " . strlen($out_buf) . " to $i<BR>";
				}
			}
			
			unset ($outsize, $curfile, $i);
		}
		$timer = time()+1;
	}

	
	
	foreach($w_fds as $i) {
		if ($input[$i]=="w") { //writable socket
			$songbuf = substr($output[$i], 0, $smallbufsize);
			$output[$i] = substr($output[$i], $smallbufsize);
			$res = @socket_write ($i, $songbuf, $smallbufsize);
			if ($res==false && strlen($songbuf)>0) { //kill the socket
				socket_kill_my_socket ($i);
			}
			unset ($songbuf, $res);
			//echo "Sent " . strlen($songbuf) . " to sock# $i (" . strlen($output[$i]) . ")<BR>\n";
		}
	}
	unset($i);
	
} while (true);
//socket_close ($msgsock);

socket_close ($sock);

function socket_kill_my_socket($i) {
global $fset, $output, $input;
	$fnd = array_search($i, $fset);
	unset($fset[$fnd]);
	unset($output[$i]);
	unset($input[$i]);
	socket_shutdown($i); //???
	socket_close($i);
	echo "Closed $i<BR>";
	//print_r($GLOBALS['status']); echo "<BR>";
}

function load_playlist($plsfile, $action = "L") { //$action is either A(dd) or L(oad)
global $pls;
if ($action=="L") $pls = array(); //clearing the array
$plstype = strtolower(substr($plsfile, -3)); //pls and m3u are supported
	$file = fopen($plsfile, "r");
		do {
			$line = fgets($file, 1024);
			$line = trim($line);
			if ($plstype=="m3u") {
				if (substr($line, 0, 1)!="#") $pls[]=$line;
			} elseif ($plstype=="pls") {
				if (strtolower(substr($line, 0, 4))=="file") {
					$pos = strpos($line, "=");
					$pls[] = substr($line, ++$pos);
				}
			}
		} while (!feof($file));
	fclose ($file);
	//var_dump($pls);
}
?>
