Add PHP Client and Playout classes from Skyler Sully
[melted] / src / swig / php / playout.php
1 <?php
2 /**
3 * Playout Client
4 *
5 * The classes in this file are responsible for controlling the MVCP server.
6 *
7 * The class Melted is responsible for handling the connection to the MVCP server as well
8 * as passing commands and receiving responses from the server.
9 *
10 * The class Playout is responsible for retreiving and loading the appropriate items on the
11 * schedule. It will use the Melted class to initialize and update the server.
12 *
13 * PHP version 5.2.0+
14 *
15 * Licensed under The MIT License
16 * Redistributions of files must retain the above copyright notice.
17 *
18 * @filesource
19 * @copyright 2010 Skyler Sully
20 * @link %link%
21 * @since %since%
22 * @version $Revision: $
23 * @modifiedby $LastChangedBy: ssully$
24 * @lastmodified $Date: $
25 * @license http://www.opensource.org/licenses/mit-license.php The MIT License
26 */
27 /**
28 * Playout Controller.
29 *
30 * Retreives schedule information and performs functions to playout assets based on
31 * the schedule on time. Uses the Melted class
32 *
33 * @package playout
34 * @subpackage application.vendors.socket.library.client
35 */
36 class Playout
37 {
38 /**
39 * Name of the module
40 *
41 * @access public
42 * @var string
43 */
44 var $name = 'playout';
45
46 /**
47 * Contains defaults for the Playout class. They will be overwritten
48 * by the config passed to the constructor
49 *
50 * @access protected
51 * @var array
52 */
53 var $_defaults = array(
54 'host' => 'localhost',
55 'port' => 5250,
56 'mode' => 1,
57 );
58
59 /**
60 * Contains the settings necessary for the Playout Client.
61 *
62 * @access public
63 * @var array
64 */
65 var $settings = array();
66
67 /**
68 * SDI Cards
69 *
70 * @access public
71 * @var array
72 */
73 var $units = array(
74 'U0' => 0,
75 'U1' => 1,
76 'U2' => 2,
77 'U3' => 3
78 );
79
80 /**
81 * Array of command responses from the MVCP server.
82 *
83 * @access protected
84 * @var array
85 */
86 var $responses = array();
87
88 /**
89 * Contains the MVCP client
90 *
91 * @access public
92 * @var object
93 */
94 var $Mvcp;
95
96 /**
97 * Contains the playlist for the current hour
98 *
99 * @access public
100 * @var array
101 */
102 var $playlist = array();
103
104 /**
105 * Playout constructor
106 */
107 function __construct($config = array()) {
108 $this->settings = array_merge($this->_defaults, $config);
109 $this->_initialize();
110 }
111
112 /**
113 * Initializes the MVCP client
114 *
115 * @access protected
116 * @return void
117 */
118 function _initialize() {
119 extract($this->settings);
120 $this->Mvcp = new MvcpClient($host, $port, $mode);
121 $this->_connect();
122 }
123
124 /**
125 * Connects to the MVCP server. If the connection has failed because the service is down,
126 * it attempts to restart the MVCP service. If the service cannot be restarted after the
127 * $maxRestartTries setting, it will reboot the server.
128 *
129 * @access protected
130 * @return boolean
131 */
132 function connect() {
133 if( !$this->Mvcp->open() ) {
134 return false;
135 } else {
136 return $this->Mvcp->isConnected();
137 }
138 }
139
140 /**
141 * Disconnects from the MVCP server
142 *
143 * @access protected
144 * @return void
145 */
146 function disconnect() {
147 $this->Mvcp->close();
148 }
149
150 /**
151 * Shuts down the Playout service by stopping whatever is playing and clearing the unit.
152 *
153 * @access public
154 * @return void
155 */
156 function shutdown() {
157 if( !$this->Mvcp->isConnected() ) {
158 $this->_connect();
159 }
160 $this->_command('stop');
161 $this->_command('clear');
162 $this->_disconnect();
163 }
164
165 /**
166 * Sets the key and value on the unit
167 *
168 * @access public
169 * @param $key
170 * @param $value
171 * @param $unit (optional)
172 * @return void
173 */
174 function uset($key, $value, $unit = 0) {
175 $this->command('uset', array($key, $value), $errors, $unit);
176 return $errors;
177 }
178
179 /**
180 * Get the value for the specified key on the unit
181 *
182 * @access public
183 * @param $key
184 * @param $unit (optional)
185 * @return boolean
186 */
187 function uget($key, $unit = 0) {
188 $this->command('uget', array($key), $errors, $unit);
189 return $errors;
190 }
191
192 /**
193 * Loads a clip onto the unit
194 *
195 * @access public
196 * @param $clip
197 * @param $in (optional)
198 * @param $out (optional)
199 * @param $unit (optional)
200 * @return boolean
201 */
202 function load($clip, $in = null, $out = null, $unit = 0) {
203 $this->command('load', array($clip, $in, $out), $errors, $unit);
204 return $errors;
205 }
206
207 /**
208 * Appends a clip to the unit
209 *
210 * @access public
211 * @param $clip
212 * @param $in (optional)
213 * @param $out (optional)
214 * @param $unit (optional)
215 * @return boolean
216 */
217 function append($clip, $in = null, $out = null, $unit = 0) {
218 $this->command('apnd', array($clip, $in, $out), $errors, $unit);
219 return $errors;
220 }
221
222 /**
223 * Sets the in point for the current clip
224 *
225 * @access public
226 * @param $frame
227 * @param $unit (optional)
228 * @return boolean
229 */
230 function set_in($frame, $unit = 0) {
231 $this->command('set_in', array($frame), $errors, $unit);
232 return $errors;
233 }
234
235 /**
236 * Sets the out point for the current clip
237 *
238 * @access public
239 * @param $frame
240 * @param $unit (optional)
241 * @return boolean
242 */
243 function set_out($frame, $unit = 0) {
244 $this->command('set_out', array($frame), $errors, $unit);
245 return $errors;
246 }
247
248 /**
249 * Steps a certain number of frames (positive or negative)
250 *
251 * @access public
252 * @param $frames
253 * @param $unit (optional)
254 * @return boolean
255 */
256 function step($frames, $unit = 0) {
257 $this->command('step', array($frames), $errors, $unit);
258 return $errors;
259 }
260
261 /**
262 * Plays the current clip(s) on the specified unit
263 *
264 * @access public
265 * @param $unit (optional)
266 * @return boolean
267 */
268 function play($unit = 0) {
269 $this->command('play', array(), $errors, $unit);
270 return $errors;
271 }
272
273 /**
274 * Stops the current playing clips on the specified unit
275 *
276 * @access public
277 * @param $unit (optional)
278 * @return boolean
279 */
280 function stop($unit = 0) {
281 $this->command('stop', array(), $errors, $unit);
282 return $errors;
283 }
284
285 /**
286 * Clears the entire unit
287 *
288 * @access public
289 * @param $unit (optional)
290 * @return boolean
291 */
292 function clear($unit = 0) {
293 $this->command('clear', array(), $errors, $unit);
294 return $errors;
295 }
296
297 /**
298 * Clears everything from the playlist that has already been played
299 *
300 * @access public
301 * @param $unit (optional)
302 * @return boolean
303 */
304 function wipe($unit = 0) {
305 $this->command('wipe', array(), $errors, $unit);
306 return $errors;
307 }
308
309 /**
310 * Convenience function to send commands to the MVCP server via the Mvcp class.
311 *
312 * @access protected
313 * @param $command - function name that corresponds with the command
314 * in the Mvcp class
315 * @param $arguments - array of arguments for command
316 * @return boolean - status of command
317 */
318 function command($command, $arguments = array(), &$errors = array(), $unit = 0) {
319 $response = null;
320 $this->Mvcp->setUnit($unit);
321 $errors[] = $this->Mvcp->sendCommand($command, $arguments, $response);
322 usleep(100000);
323 $this->responses[] = $response;
324 }
325 }
326
327 /**
328 * Melted Client.
329 *
330 * Handles communication between the application and the MVCP server.
331 *
332 * @package playout
333 */
334 class MvcpClient extends Client
335 {
336 /**
337 * Contains the name of the program
338 *
339 * @access public
340 * @var string
341 */
342 var $name = 'melted';
343
344 /**
345 * Contains all of the units that can be used in
346 * the MVCP server
347 *
348 * @access public
349 * @var array
350 */
351 var $units = array('U0', 'U1', 'U2', 'U3');
352
353 /**
354 * The current unit that operations are being done on
355 *
356 * @access public
357 * @var string
358 */
359 var $_unit;
360
361 /**
362 * Initializes the MVCP by setting the default unit to U0
363 *
364 * @access protected
365 * @return void
366 */
367 function _initialize() {
368 $this->setUnit();
369 }
370
371 /**
372 * Opens a connection with the MVCP server
373 *
374 * @access public
375 * @return boolean
376 */
377 function open() {
378 if( $this->isConnected() ) {
379 return true;
380 }
381 $retval = $this->connect(true, $status);
382 sleep(1);
383 return $retval;
384 }
385
386 /**
387 * Closes the connection with the MVCP server
388 *
389 * @access public
390 * @return boolean
391 */
392 function close() {
393 if( !$this->isConnected() ) {
394 return true;
395 }
396 $this->write('bye');
397 return !$this->disconnect();
398 }
399
400 /**
401 * Restarts the MVCP service. This should be done if a connection cannot be made
402 * with the Mvcp Client class
403 *
404 * @access public
405 * @return boolean
406 */
407 function restart() {
408 $output = array();
409 $return = null;
410 exec("/sbin/service {$this->name} restart 2&>1", $output, $return);
411 if( $return != 0 ) {
412 return false;
413 } else {
414 return true;
415 }
416 }
417
418 /**
419 * Sets the current unit. By default, sets the unit to the default
420 * unit U0
421 *
422 * @access public
423 * @param $unit
424 * @return boolean
425 */
426 function setUnit($unit = 0) {
427 if( !is_int($unit) ) {
428 $this->_unit = $this->units[0];
429 } elseif( $unit < 0 || $unit > sizeof($this->units) - 1 ) {
430 return false;
431 }
432 $this->_unit = $this->units[$unit];
433 return true;
434 }
435
436 /**
437 * Sends the appropriate command to the MVCP server.
438 *
439 * @access public
440 * @param $command - command to run
441 * @param $arguments - array of arguments
442 * @param $response - response to return
443 * @return boolean - status of command
444 */
445 function sendCommand($command, $arguments = array(), &$response) {
446 switch($command) {
447 case 'uset':
448 list($key, $value) = $arguments;
449 $_command = "uset {$this->_unit} {$key}={$value}";
450 break;
451 case 'uget':
452 list($key) = arguments;
453 $_command = "uget {$this->_unit} {$key}";
454 break;
455 case 'load':
456 list($clip, $in, $out) = $arguments;
457 $_command = trim("load {$this->_unit} {$clip} {$in} {$out}");
458 break;
459 case 'apnd':
460 case 'append':
461 list($clip, $in, $out) = $arguments;
462 $_command = trim("apnd {$this->_unit} {$clip} {$in} {$out}");
463 break;
464 case 'set_in':
465 case 'setIn':
466 list($frame) = $arguments;
467 $_command = "sin {$this->_unit} {$frame}";
468 break;
469 case 'set_out':
470 case 'setOut':
471 list($frame) = $arguments;
472 $_command = "sout {$this->_unit} {$frame}";
473 break;
474 case 'play':
475 $_command = "play {$this->_unit}";
476 break;
477 case 'stop':
478 $_command = "stop {$this->_unit}";
479 break;
480 case 'step':
481 list($frames) = $arguments;
482 $_command = "step {$this->_unit} {$frames}";
483 break;
484 case 'wipe':
485 $_command = "wipe {$this->_unit}";
486 break;
487 case 'clear':
488 $_command = "clear {$this->_unit}";
489 break;
490 default:
491 return false;
492 break;
493 }
494 return $this->runCommand($_command, $response);
495 }
496
497 /**
498 * Gets the status of the unit
499 *
500 * @access public
501 * @return array
502 */
503 function status() {
504 if( !$this->isConnected() ) {
505 return array();
506 }
507 $this->write("usta {$this->_unit}");
508 sleep(1);
509
510 $return = $this->read();
511 $status = $this->read();
512
513 list($unit, $mode, $currentClip, $currentFrame, $speed, $fps, $in, $out,
514 $length, $tailClip, $tailPosition, $tailIn, $tailOut, $tailLength,
515 $seekable, $playlistGenNumber, $clipIndex) = split(' ', $status);
516
517 return compact(array('unit', 'mode', 'currentClip', 'currentFrame', 'speed', 'fps', 'in', 'out',
518 'length', 'tailClip', 'tailPosition', 'tailIn', 'tailOut', 'tailLength',
519 'seekable', 'playlistGenNumber', 'clipIndex'));
520 }
521
522 /**
523 * Lists the clips on the unit
524 *
525 * @access public
526 * @return array
527 */
528 function listClips() {
529 if( !$this->isConnected() ) {
530 return array();
531 }
532 $this->setBlockingMode(0);
533 $this->write("list {$this->_unit}");
534 sleep(1);
535
536 $response = $this->read();
537 $_list = explode(PHP_EOL, $response);
538 $response = array_shift($_list);
539 $genNum = array_shift($_list);
540 foreach( $_list as $clip ) {
541 list($index, $file, $in, $out, $realLength, $estimatedLength) = explode(' ', $clip);
542 $list[] = compact(array('index', 'file', 'in', 'out', 'realLength', 'estimatedLength'));
543 }
544 $this->setBlockingMode(1);
545 return $list;
546 }
547
548 /**
549 * Writes a command to the miracle server, gets the reponse and returns the
550 * status of the command as well as the command itself and the response code.
551 *
552 * @access public
553 * @param $command
554 * @param $response - stores the command and response code
555 * @return boolean true on success, false on failure
556 */
557 function runCommand($command, &$response) {
558 if( !$this->isConnected() ) {
559 $response = "command - \"{$command}\" : not connected";
560 return false;
561 }
562 $this->write($command);
563 usleep(500000);
564 $_response = $this->read();
565 if( !empty($_response) ) {
566 $_codes = explode(" ", $_response);
567 if( isset($_codes[1]) && $_codes[1] != 'OK' ) {
568 $error = false;
569 } else {
570 $error = true;
571 }
572 } else {
573 $_response = "not connected";
574 $error = true;
575 }
576 $response = "command - \"{$command}\" : {$_response}";
577 return $error;
578 }
579 }
580
581 ?>