From: Dan Dennedy Date: Wed, 10 Feb 2010 04:03:19 +0000 (-0800) Subject: Add PHP Client and Playout classes from Skyler Sully X-Git-Url: http://research.m1stereo.tv/gitweb?a=commitdiff_plain;h=dec6007d0bac803200018c23fc9104dbd4360097;p=melted Add PHP Client and Playout classes from Skyler Sully --- diff --git a/src/swig/php/client.php b/src/swig/php/client.php new file mode 100644 index 0000000..7dd499a --- /dev/null +++ b/src/swig/php/client.php @@ -0,0 +1,242 @@ +setup($address, $port, $mode); + $this->_initialize(); + } + + /** + * Client Destructor + */ + function __destruct() { + + } + + /** + * Sets the address and port of the server + * + * @access public + * @return void + */ + function setup($address, $port, $mode = 1) { + $this->address = $address; + $this->port = $port; + + $this->connected = false; + $this->expectWelcome = false; + if( $mode > 1 ) { + $mode = 1; + } + $this->mode = $mode; + } + + /** + * Initialization function to be extended by descendent classes + * + * @access protected + * @return void + */ + function _initialize() { + + } + + /** + * Connects to the server using $address and $port + * + * @access public + * @param $expect_welcome - should we expect a welcome message + * @param $welcome - welcome message + * @return boolean - true if connected, false if not connected + */ + function connect($expectWelcome = false, &$welcome = null) { + if( $this->connected ) { + $this->disconnect(); + } + $this->connected = false; + $this->connection = @fsockopen($this->address, $this->port, $errno, $errstr, 30); + $this->expectWelcome = $expectWelcome; + if( !$this->connection === false ) { + $this->setBlockingMode($this->mode); + $this->connected = true; + if( $expectWelcome === true ) { + $welcome = $this->read(); + } + } + return $this->connected; + } + + /** + * Disconnects from the server + * + * @access public + */ + function disconnect() { + if( is_resource($this->connection) ) { + fclose($this->connection); + } + $this->connected = false; + return !$this->connected; + } + + /** + * Attempts to reconnect to the server + * + * @access public + */ + function reconnect() { + $this->disconnect(); + return $this->connect($this->expectWelcome); + } + + /** + * Writes a message to the server + * + * @access public + * @param String - Message to write to the server + * @return number of bytes written or false on error + */ + function write($message) { + if( $this->connected ) { + if( !is_string($message) ) { + return false; + } + $message = trim($message).PHP_EOL; + $result = @fputs($this->connection, $message, strlen($message)); + if( $result == false ) { + if( $this->reconnect() ) { + return $this->write($message); + } else { + return $this->connected = false; + } + } + } else { + return false; + } + + return $result; + } + + /** + * Reads a message from the server + * + * @access public + * @return Response from server, or false on error + */ + function read() { + $response = ''; + if( $this->connected ) { + if( $this->mode ) { + $response = @fgets($this->connection, 65535); + if($response === false) { + if( $this->reconnect() ) { + return $this->read(); + } else { + return $this->connected = false; + } + } + } else { + while($tmp = @fgets($this->connection)) { + $response .= $tmp; + } + } + } else { + $response = '*** NOT CONNECTED ***'; + } + + return trim($response); + } + + /** + * Sets the blocking mode of the connection + * + * @access public + * @param $mode + * @return boolean + */ + function setBlockingMode($mode) { + if( !is_int($mode) ) { + return false; + } + if( $mode == 0 ) { + $this->mode = 0; + } else { + $this->mode = 1; + } + stream_set_blocking($this->connection, $this->mode); + } + + /** + * Returns the connection status of the server + * + * @access public + * @return boolean + */ + function isConnected() { + return $this->connected; + } +} + +?> \ No newline at end of file diff --git a/src/swig/php/playout.php b/src/swig/php/playout.php new file mode 100644 index 0000000..9441311 --- /dev/null +++ b/src/swig/php/playout.php @@ -0,0 +1,581 @@ + 'localhost', + 'port' => 5250, + 'mode' => 1, + ); + + /** + * Contains the settings necessary for the Playout Client. + * + * @access public + * @var array + */ + var $settings = array(); + + /** + * SDI Cards + * + * @access public + * @var array + */ + var $units = array( + 'U0' => 0, + 'U1' => 1, + 'U2' => 2, + 'U3' => 3 + ); + + /** + * Array of command responses from the MVCP server. + * + * @access protected + * @var array + */ + var $responses = array(); + + /** + * Contains the MVCP client + * + * @access public + * @var object + */ + var $Mvcp; + + /** + * Contains the playlist for the current hour + * + * @access public + * @var array + */ + var $playlist = array(); + + /** + * Playout constructor + */ + function __construct($config = array()) { + $this->settings = array_merge($this->_defaults, $config); + $this->_initialize(); + } + + /** + * Initializes the MVCP client + * + * @access protected + * @return void + */ + function _initialize() { + extract($this->settings); + $this->Mvcp = new MvcpClient($host, $port, $mode); + $this->_connect(); + } + + /** + * Connects to the MVCP server. If the connection has failed because the service is down, + * it attempts to restart the MVCP service. If the service cannot be restarted after the + * $maxRestartTries setting, it will reboot the server. + * + * @access protected + * @return boolean + */ + function connect() { + if( !$this->Mvcp->open() ) { + return false; + } else { + return $this->Mvcp->isConnected(); + } + } + + /** + * Disconnects from the MVCP server + * + * @access protected + * @return void + */ + function disconnect() { + $this->Mvcp->close(); + } + + /** + * Shuts down the Playout service by stopping whatever is playing and clearing the unit. + * + * @access public + * @return void + */ + function shutdown() { + if( !$this->Mvcp->isConnected() ) { + $this->_connect(); + } + $this->_command('stop'); + $this->_command('clear'); + $this->_disconnect(); + } + + /** + * Sets the key and value on the unit + * + * @access public + * @param $key + * @param $value + * @param $unit (optional) + * @return void + */ + function uset($key, $value, $unit = 0) { + $this->command('uset', array($key, $value), $errors, $unit); + return $errors; + } + + /** + * Get the value for the specified key on the unit + * + * @access public + * @param $key + * @param $unit (optional) + * @return boolean + */ + function uget($key, $unit = 0) { + $this->command('uget', array($key), $errors, $unit); + return $errors; + } + + /** + * Loads a clip onto the unit + * + * @access public + * @param $clip + * @param $in (optional) + * @param $out (optional) + * @param $unit (optional) + * @return boolean + */ + function load($clip, $in = null, $out = null, $unit = 0) { + $this->command('load', array($clip, $in, $out), $errors, $unit); + return $errors; + } + + /** + * Appends a clip to the unit + * + * @access public + * @param $clip + * @param $in (optional) + * @param $out (optional) + * @param $unit (optional) + * @return boolean + */ + function append($clip, $in = null, $out = null, $unit = 0) { + $this->command('apnd', array($clip, $in, $out), $errors, $unit); + return $errors; + } + + /** + * Sets the in point for the current clip + * + * @access public + * @param $frame + * @param $unit (optional) + * @return boolean + */ + function set_in($frame, $unit = 0) { + $this->command('set_in', array($frame), $errors, $unit); + return $errors; + } + + /** + * Sets the out point for the current clip + * + * @access public + * @param $frame + * @param $unit (optional) + * @return boolean + */ + function set_out($frame, $unit = 0) { + $this->command('set_out', array($frame), $errors, $unit); + return $errors; + } + + /** + * Steps a certain number of frames (positive or negative) + * + * @access public + * @param $frames + * @param $unit (optional) + * @return boolean + */ + function step($frames, $unit = 0) { + $this->command('step', array($frames), $errors, $unit); + return $errors; + } + + /** + * Plays the current clip(s) on the specified unit + * + * @access public + * @param $unit (optional) + * @return boolean + */ + function play($unit = 0) { + $this->command('play', array(), $errors, $unit); + return $errors; + } + + /** + * Stops the current playing clips on the specified unit + * + * @access public + * @param $unit (optional) + * @return boolean + */ + function stop($unit = 0) { + $this->command('stop', array(), $errors, $unit); + return $errors; + } + + /** + * Clears the entire unit + * + * @access public + * @param $unit (optional) + * @return boolean + */ + function clear($unit = 0) { + $this->command('clear', array(), $errors, $unit); + return $errors; + } + + /** + * Clears everything from the playlist that has already been played + * + * @access public + * @param $unit (optional) + * @return boolean + */ + function wipe($unit = 0) { + $this->command('wipe', array(), $errors, $unit); + return $errors; + } + + /** + * Convenience function to send commands to the MVCP server via the Mvcp class. + * + * @access protected + * @param $command - function name that corresponds with the command + * in the Mvcp class + * @param $arguments - array of arguments for command + * @return boolean - status of command + */ + function command($command, $arguments = array(), &$errors = array(), $unit = 0) { + $response = null; + $this->Mvcp->setUnit($unit); + $errors[] = $this->Mvcp->sendCommand($command, $arguments, $response); + usleep(100000); + $this->responses[] = $response; + } +} + +/** + * Melted Client. + * + * Handles communication between the application and the MVCP server. + * + * @package playout + */ +class MvcpClient extends Client +{ + /** + * Contains the name of the program + * + * @access public + * @var string + */ + var $name = 'melted'; + + /** + * Contains all of the units that can be used in + * the MVCP server + * + * @access public + * @var array + */ + var $units = array('U0', 'U1', 'U2', 'U3'); + + /** + * The current unit that operations are being done on + * + * @access public + * @var string + */ + var $_unit; + + /** + * Initializes the MVCP by setting the default unit to U0 + * + * @access protected + * @return void + */ + function _initialize() { + $this->setUnit(); + } + + /** + * Opens a connection with the MVCP server + * + * @access public + * @return boolean + */ + function open() { + if( $this->isConnected() ) { + return true; + } + $retval = $this->connect(true, $status); + sleep(1); + return $retval; + } + + /** + * Closes the connection with the MVCP server + * + * @access public + * @return boolean + */ + function close() { + if( !$this->isConnected() ) { + return true; + } + $this->write('bye'); + return !$this->disconnect(); + } + + /** + * Restarts the MVCP service. This should be done if a connection cannot be made + * with the Mvcp Client class + * + * @access public + * @return boolean + */ + function restart() { + $output = array(); + $return = null; + exec("/sbin/service {$this->name} restart 2&>1", $output, $return); + if( $return != 0 ) { + return false; + } else { + return true; + } + } + + /** + * Sets the current unit. By default, sets the unit to the default + * unit U0 + * + * @access public + * @param $unit + * @return boolean + */ + function setUnit($unit = 0) { + if( !is_int($unit) ) { + $this->_unit = $this->units[0]; + } elseif( $unit < 0 || $unit > sizeof($this->units) - 1 ) { + return false; + } + $this->_unit = $this->units[$unit]; + return true; + } + + /** + * Sends the appropriate command to the MVCP server. + * + * @access public + * @param $command - command to run + * @param $arguments - array of arguments + * @param $response - response to return + * @return boolean - status of command + */ + function sendCommand($command, $arguments = array(), &$response) { + switch($command) { + case 'uset': + list($key, $value) = $arguments; + $_command = "uset {$this->_unit} {$key}={$value}"; + break; + case 'uget': + list($key) = arguments; + $_command = "uget {$this->_unit} {$key}"; + break; + case 'load': + list($clip, $in, $out) = $arguments; + $_command = trim("load {$this->_unit} {$clip} {$in} {$out}"); + break; + case 'apnd': + case 'append': + list($clip, $in, $out) = $arguments; + $_command = trim("apnd {$this->_unit} {$clip} {$in} {$out}"); + break; + case 'set_in': + case 'setIn': + list($frame) = $arguments; + $_command = "sin {$this->_unit} {$frame}"; + break; + case 'set_out': + case 'setOut': + list($frame) = $arguments; + $_command = "sout {$this->_unit} {$frame}"; + break; + case 'play': + $_command = "play {$this->_unit}"; + break; + case 'stop': + $_command = "stop {$this->_unit}"; + break; + case 'step': + list($frames) = $arguments; + $_command = "step {$this->_unit} {$frames}"; + break; + case 'wipe': + $_command = "wipe {$this->_unit}"; + break; + case 'clear': + $_command = "clear {$this->_unit}"; + break; + default: + return false; + break; + } + return $this->runCommand($_command, $response); + } + + /** + * Gets the status of the unit + * + * @access public + * @return array + */ + function status() { + if( !$this->isConnected() ) { + return array(); + } + $this->write("usta {$this->_unit}"); + sleep(1); + + $return = $this->read(); + $status = $this->read(); + + list($unit, $mode, $currentClip, $currentFrame, $speed, $fps, $in, $out, + $length, $tailClip, $tailPosition, $tailIn, $tailOut, $tailLength, + $seekable, $playlistGenNumber, $clipIndex) = split(' ', $status); + + return compact(array('unit', 'mode', 'currentClip', 'currentFrame', 'speed', 'fps', 'in', 'out', + 'length', 'tailClip', 'tailPosition', 'tailIn', 'tailOut', 'tailLength', + 'seekable', 'playlistGenNumber', 'clipIndex')); + } + + /** + * Lists the clips on the unit + * + * @access public + * @return array + */ + function listClips() { + if( !$this->isConnected() ) { + return array(); + } + $this->setBlockingMode(0); + $this->write("list {$this->_unit}"); + sleep(1); + + $response = $this->read(); + $_list = explode(PHP_EOL, $response); + $response = array_shift($_list); + $genNum = array_shift($_list); + foreach( $_list as $clip ) { + list($index, $file, $in, $out, $realLength, $estimatedLength) = explode(' ', $clip); + $list[] = compact(array('index', 'file', 'in', 'out', 'realLength', 'estimatedLength')); + } + $this->setBlockingMode(1); + return $list; + } + + /** + * Writes a command to the miracle server, gets the reponse and returns the + * status of the command as well as the command itself and the response code. + * + * @access public + * @param $command + * @param $response - stores the command and response code + * @return boolean true on success, false on failure + */ + function runCommand($command, &$response) { + if( !$this->isConnected() ) { + $response = "command - \"{$command}\" : not connected"; + return false; + } + $this->write($command); + usleep(500000); + $_response = $this->read(); + if( !empty($_response) ) { + $_codes = explode(" ", $_response); + if( isset($_codes[1]) && $_codes[1] != 'OK' ) { + $error = false; + } else { + $error = true; + } + } else { + $_response = "not connected"; + $error = true; + } + $response = "command - \"{$command}\" : {$_response}"; + return $error; + } +} + +?> \ No newline at end of file