<?
//
// $ php ./ebs.php > a.mp3
//

error_reporting(E_ERROR);

define("SERVER_ADDR", "219.240.12.254");
define("SERVER_PORT_CTL", 5056);
define("SERVER_PORT_DAT", 5057);

// MPEG ADTS, layer III, v2,  64 kBits, 24 kHz, JntStereo
define("MP3_MAGIC", pack("C4", 0xff, 0xf3, 0x84, 0x64));

include 'ebs.inc';

$REQ_TOK = array ('0', '0', '0', '0');

$stderr = fopen('php://stderr', 'w');
$stdout = fopen('php://stdout', 'w');

function print_to_stderr($msg) {
	global $stderr;
        fwrite($stderr,$msg);
}

function print_to_stdout($msg) {
	global $stdout;
        fwrite($stdout,$msg);
}

class ebs_conn {
	private $host, $port, $sock;

	private function conn_error($msg) {
		$log = $msg . "(" . $this->host . ":" . $this->port . "): " .
			socket_strerror(socket_last_error()) . "\n";
		print_to_stderr($log);
	}

	protected function connect() {
		$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
		if ($socket === false) {
			$this->conn_error("socket_create");
			return false;
		}
		socket_set_block($socket);
		$ret = socket_connect($socket, $this->host, $this->port);
		if ($ret === false) {
			$this->conn_error("socket_connect");
			return false;
		}
		$this->sock = $socket;
		return true;
	}
	protected function disconnect() {
		if ($this->sock === false)
			return;
		socket_close($this->sock);
		$this->sock = false;
	}

	protected function write($buf, $len) {
		if ($len <= 0)
			return 0;

		$left = $len;
		while ($left > 0) {
			$ret = socket_write($this->sock, $buf, $len);
			if ($ret === false) {
				$this->conn_error("socket_write");
				return -1;
			}
			if ($ret == 0) {
				$this->conn_error("socket_write:disconnected?");
				return -1;
			}
			$left = $left - $ret;
			$buf = substr($buf, $ret, $left);
		}
		return $len - $left;
	}
	protected function read(&$buf, $len) {
		if ($len <= 0)
			return 0;

		$left = $len;
		$b = '';
		while ($left > 0) {
			$b = socket_read($this->sock, $len, PHP_BINARY_READ);
			if ($buf === false) {
				$this->conn_error("socket_read");
				return -1;
			}
			$ret = strlen($b);
			if ($ret == 0) {
				$this->conn_error("socket_read:disconnected?");
				return -1;
			}
			$left = $left - $ret;
			$buf .= $b;
		}
		return $len - $left;
	}
	protected function write_and_read($wr_buf, &$rd_buf) {
		$ret = $this->write($wr_buf, 288);
		if ($ret < 0) {
			return false;
		}
		$ret = $this->read($rd_buf, 288);
		if ($ret < 0) {
			return false;
		}
		return true;
	}
	public function __construct($host, $port) {
		$this->host = $host;
		$this->port = $port;
		$this->sock = false;
	}
	public function __destruct() {
		$this->disconnect();
	}
}

class ebs_ctl extends ebs_conn {
	private $state;

	public function loop() {
		global $PACKET_0, $PACKET_1, $PACKET_2, $PACKET_3;
		global $REQ_TOK;
		$ret = 0;
		$dummy_buf = '';
		print_to_stderr("ctl_loop:".$this->state."\n");
		switch ($this->state) {
		case 0:
			$this->disconnect();
			$ret = $this->connect();
			if ($ret == false) {
				$this->state = -1;
				return false;
			}
			$ret = $this->write_and_read($PACKET_0, $dummy_buf);
			if ($ret == false) {
				$this->state = -1;
				return false;
			}
			$this->state = 1;
			$ret = 0;
			break;
		case 1:
			$ret = $this->write_and_read($PACKET_1, $dummy_buf);
			if ($ret == false) {
				$this->state = -1;
				return false;
			}
			$REQ_TOK[0] = $dummy_buf[20];
			$REQ_TOK[1] = $dummy_buf[21];
			$REQ_TOK[2] = $dummy_buf[22];
			$REQ_TOK[3] = $dummy_buf[23];
			print_to_stderr($REQ_TOK);
			$this->state = 2;
			$ret = 0;
			break;
		case 2:
			$ret = $this->write_and_read($PACKET_2, $dummy_buf);
			if ($ret == false) {
				$this->state = -1;
				return false;
			}
			$this->state = 3;
			$ret = 0;
			break;

		case 3:
			$ret = $this->write_and_read($PACKET_0, $dummy_buf);
			if ($ret == false) {
				$this->state = -1;
				return false;
			}
			$this->state = 4;
			$ret = 0;
			break;

		case 4:
			$ret = $this->write_and_read($PACKET_3, $dummy_buf);
			if ($ret == false) {
				$this->state = -1;
				return false;
			}
			$ret = $this->read($dummy_buf, 288);
			if ($ret === false) {
				$this->state = -1;
				return false;
			}
			$this->state = 5;
			$ret = 1; /* for recv data */
			break;
		
		case 5:
			// ping
			$ret = $this->write_and_read($PACKET_0, $dummy_buf);
			if ($ret == false) {
				$this->state = -1;
				return false;
			}
			$ret = $this->write_and_read($PACKET_3, $dummy_buf);
			if ($ret == false) {
				$this->state = -1;
				return false;
			}
			$ret = $this->read($dummy_buf, 288);
			if ($ret <= 0) {
				$this->state = -1;
				return false;
			}
			$this->state = $this->state; /* self */
			$ret = 1; /* for recv data */
			break;
		
		default:
			$ret = false;
			break;
		}
		return $ret;
	}
	public function __construct() {
		$this->state = 0;
		parent::__construct(SERVER_ADDR, SERVER_PORT_CTL);
	}
	public function __destruct() {
		parent::__destruct();
	}
}

class ebs_dat extends ebs_conn {
	private $state, $count;
	private function read_and_parse_header() {
		$dummy_buf = '';
		$packet_len = 0;
		$ret = $this->read($dummy_buf, 32); // EBS header
		if ($ret < 0) {
			return -1;
		}

		$packet_len = hexdec(bin2hex($dummy_buf[19]));
		$packet_len *= 256;
		$packet_len += hexdec(bin2hex($dummy_buf[18]));
		print_to_stderr("data len = ".$packet_len."\n");
		return $packet_len;
	}
	private function scan_and_recv_mp3($len) {
		$dummy_buf = '';
		$ret = $this->read($dummy_buf, $len);
		if ($ret < 0) {
			return -1;
		}
		$pos = strpos($dummy_buf, MP3_MAGIC, 0);
		if ($pos === false) {
			print_to_stderr("no mp3 mgic\n");
			return -1;
		}
		print_to_stderr("@".$pos."\n");
		$dummy_buf = substr($dummy_buf, $pos, $ret - $pos);
		print_to_stdout($dummy_buf);
		return 0;
	}
	private function recv_mp3($len) {
		$dummy_buf = '';
		$ret = $this->read($dummy_buf, $len);
		if ($ret < 0) {
			return -1;
		}
		print_to_stdout($dummy_buf);
		return 0;
	}
	public function loop() {
		global $PACKET_R;
		global $REQ_TOK;
		$ret = 0;
		$dummy_buf = '';
		print_to_stderr("dat_loop:".$this->state."\n");
		switch ($this->state) {
		case 0:
			$this->disconnect();
			$ret = $this->connect();
			if ($ret == false) {
				$this->state = -1;
				return false;
			}
			$PACKET_R[20] = $REQ_TOK[0];
			$PACKET_R[21] = $REQ_TOK[1];
			$PACKET_R[22] = $REQ_TOK[2];
			$PACKET_R[23] = $REQ_TOK[3];
			$ret = $this->write_and_read($PACKET_R, $dummy_buf);
			if ($ret == false) {
				$this->state = -1;
				return false;
			}
			$this->state = 1;
			$ret = 0;
			break;
		case 1:
			$packet_len = $this->read_and_parse_header();
			if ($packet_len <= 0)
				return false;

			$ret = $this->scan_and_recv_mp3($packet_len);
			if ($ret < 0) {
				$this->state = -1;
				return false;
			}

			$this->state = 2;
			$ret = 0;
			break;

		case 2:
			$packet_len = $this->read_and_parse_header();
			if ($packet_len <= 0)
				return false;

			$ret = $this->recv_mp3($packet_len);
			if ($ret < 0) {
				$this->state = -1;
				return false;
			}

			$this->count = $this->count + 1;
			if ($this->count > 10) {
				$this->count = 0;
				return 1; /* for ctl::ping */
			}
			$this->state = $this->state; /* self */
			$ret = 0;
			break;

		default:
			$ret = false;
			break;
		}
		return $ret;
	}
	public function __construct() {
		$this->state = 0;
		$this->count = 0;
		parent::__construct(SERVER_ADDR, SERVER_PORT_DAT);
	}
	public function __destruct() {
		parent::__destruct();
	}
}

$ebs_ctl = new ebs_ctl();
$ebs_dat = new ebs_dat();

while (1) {
	$ret = $ebs_ctl->loop();
	if ($ret === false)
		break; /* error */
	if ($ret === 1) {
		while (1) {
			$ret = $ebs_dat->loop();
			if ($ret !== 0)
				break;
		}
		if ($ret === false)
			break; /* error */
	}
}

?>