-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add PHP files for spycam POC example to be refined
- Loading branch information
Showing
4 changed files
with
339 additions
and
0 deletions.
There are no files selected for viewing
30 changes: 30 additions & 0 deletions
30
webcam-capture-addons/webcam-capture-addon-spycam/src/main/php/Heartbeat.class.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
<?php | ||
|
||
namespace v1\resource; | ||
|
||
use tonic\Resource as Resource; | ||
use tonic\Request as Request; | ||
use tonic\Response as Response; | ||
|
||
|
||
/** | ||
* Handle tick picture upload for specific user. | ||
* | ||
* @author Bartosz Firyn (SarXos) | ||
*/ | ||
class Heartbeat extends Resource { | ||
|
||
/** | ||
* Receive streaming hartbeat. | ||
* | ||
* @param RESTRequest request | ||
* @return RESTResponse | ||
*/ | ||
function put(Request $request, $uid) { | ||
$response = new Response($request); | ||
$response->code = Response::OK; | ||
$response->body = $uid; | ||
return $response; | ||
} | ||
|
||
} |
181 changes: 181 additions & 0 deletions
181
webcam-capture-addons/webcam-capture-addon-spycam/src/main/php/MJPEGStreamer.class.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,181 @@ | ||
<?php | ||
|
||
namespace streaming; | ||
|
||
use log\KLogger as KLogger; | ||
|
||
|
||
/** | ||
* MJPEG stream reader. | ||
* | ||
* @author Bartosz Firyn (SarXos) | ||
*/ | ||
class MJPEGStreamer { | ||
|
||
private static $BOUNDARY = 'mjpeg-frame'; | ||
|
||
private $log = null; | ||
|
||
private $initialized = false; | ||
|
||
private $running = true; | ||
|
||
private $buffer = array(); | ||
|
||
private $name = null; | ||
|
||
private $sock = null; | ||
|
||
|
||
public function __construct($name) { | ||
$this->name = $name; | ||
$this->sock = $this->buildSockPath($this->name); | ||
$this->log = new KLogger('files' . DIRECTORY_SEPARATOR . 'log' . DIRECTORY_SEPARATOR . $name . '.log', KLogger::DEBUG); | ||
} | ||
|
||
public function shutdown() { | ||
$this->log->info('Removing sock file'); | ||
unlink($this->sock); | ||
} | ||
|
||
private function buildSockPath($name) { | ||
return getcwd() . DIRECTORY_SEPARATOR . 'files' . DIRECTORY_SEPARATOR . 'sock' . DIRECTORY_SEPARATOR . $this->name; | ||
} | ||
|
||
public function init() { | ||
|
||
if (file_exists($this->sock)) { | ||
$this->log->error("Sock file already exists, concurrent process cannot be started"); | ||
exit(); | ||
} | ||
|
||
$this->log->info('Streamer initialization'); | ||
|
||
// connection keep-alive, in other case browser will close it when receive last frame | ||
header('Connection: keep-alive'); | ||
|
||
// disable caches | ||
header('Cache-Control: no-cache'); | ||
header('Cache-Control: private'); | ||
header('Pragma: no-cache'); | ||
|
||
// x-mixed-replace to stream JPEG images | ||
header('Content-type: multipart/x-mixed-replace; boundary=' . self::$BOUNDARY); | ||
|
||
// set unlimited so PHP doesn't timeout during a long stream | ||
set_time_limit(0); | ||
|
||
// ignore user abort script | ||
ignore_user_abort(true); | ||
|
||
@apache_setenv('no-gzip', 1); // disable apache gzip compression | ||
@ini_set('zlib.output_compression', 0); // disable PHP zlib compression | ||
@ini_set('implicit_flush', 1); | ||
|
||
// flush all current buffers | ||
$k = ob_get_level(); | ||
for ($i = 0; $i < $k; $i++) { | ||
ob_end_flush(); | ||
} | ||
|
||
register_shutdown_function(array($this, 'shutdown')); | ||
|
||
fclose(fopen($this->sock, 'w')); | ||
|
||
$this->initialized = true; | ||
} | ||
|
||
private function read() { | ||
|
||
$this->log->debug('Read sock file'); | ||
|
||
clearstatcache(); | ||
if (filesize($this->sock) === 0) { | ||
$this->log->debug('Sock file is empty'); | ||
return; | ||
} | ||
|
||
$f = fopen($this->sock, 'r'); | ||
flock($f, LOCK_SH); | ||
$frames = @file($this->sock, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); | ||
flock($f, LOCK_UN); | ||
fclose($f); | ||
|
||
fclose(fopen($this->sock, 'w')); | ||
|
||
if (!empty($frames)) { | ||
$this->buffer = array_merge($this->buffer, $frames); | ||
$this->log->debug('Read ' . count($frames) . ' frames'); | ||
} | ||
} | ||
|
||
private function control() { | ||
|
||
$filen = $this->sock . '.ctrl'; | ||
|
||
if (!file_exists($filen)) { | ||
$this->log->debug('Control file does not exist'); | ||
return; | ||
} | ||
|
||
$f = fopen($filen, 'r'); | ||
flock($f, LOCK_SH); | ||
$config = @json_decode(file_get_contents($filen), true); | ||
flock($f, LOCK_UN); | ||
fclose($f); | ||
|
||
$this->log->debug('Read control file ' . print_r($config, true)); | ||
|
||
if (!empty($config)) { | ||
if (isset($config['status']) && $config['status'] !== 'running') { | ||
$this->log->debug('Found close directive, set running false'); | ||
$this->running = false; | ||
} | ||
} else { | ||
$this->log->fatal('Control configuration is empty!'); | ||
$this->log->fatal('Control configuration is empty!'); | ||
} | ||
} | ||
|
||
public function serve() { | ||
|
||
$this->log->info('MJPEG streaming started'); | ||
|
||
if (!$this->initialized) { | ||
$this->init(); | ||
} | ||
|
||
while (true) { | ||
|
||
if(connection_status() != CONNECTION_NORMAL) { | ||
$this->log->debug('Connection closed'); | ||
//break; | ||
} | ||
|
||
$this->control(); | ||
$this->read(); | ||
|
||
$this->log->debug('Frames buffer size is ' . count($this->buffer)); | ||
|
||
if (!empty($this->buffer)) { | ||
$frame = array_shift($this->buffer); | ||
$content = file_get_contents($frame); | ||
print('--' . self::$BOUNDARY . "\nContent-type: image/jpeg\n\n" . $content); | ||
flush(); | ||
} | ||
|
||
sleep(1); | ||
|
||
if (!$this->running) { | ||
$this->log->info('Stopping'); | ||
break; | ||
} | ||
} | ||
|
||
exit(0); | ||
} | ||
} | ||
|
||
|
||
|
||
|
65 changes: 65 additions & 0 deletions
65
webcam-capture-addons/webcam-capture-addon-spycam/src/main/php/Stream.class.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
<?php | ||
|
||
namespace v1\resource; | ||
|
||
use streaming\MJPEGStreamer; | ||
|
||
use tonic\Resource as Resource; | ||
use tonic\Request as Request; | ||
use tonic\Response as Response; | ||
use \PDO as PDO; | ||
|
||
/** | ||
* @author Bartosz Firyn (SarXos) | ||
*/ | ||
class Stream extends Resource { | ||
|
||
/** | ||
* Serve MJPEG stream. | ||
* | ||
* @param RESTRequest request | ||
* @param Request $request | ||
* @param string $uid - user ID | ||
* @param string $camid - camera ID | ||
* @return RESTResponse | ||
*/ | ||
function get(Request $request, $uid, $camid) { | ||
|
||
if ($this->canAcess($uid, $camid)) { | ||
$streamer = new MJPEGStreamer($uid . '.' . $camid); | ||
$streamer->serve(); | ||
} else { | ||
$response = new Response($request); | ||
$response->code = Response::FORBIDDEN; | ||
$response->body = 'You are not authorized to access this resource'; | ||
return $response; | ||
} | ||
} | ||
|
||
private function canAcess($uid, $camid) { | ||
|
||
$uid = mysql_real_escape_string($uid); | ||
$camid = mysql_real_escape_string($camid); | ||
|
||
$pdo = new PDO('mysql:host=localhost;dbname=spycam', 'root', 'secret'); | ||
|
||
$sth = $pdo->prepare('select * from users where uid = :uid'); | ||
$sth->execute(array(':uid' => $uid)); | ||
|
||
$user = $sth->fetch(); | ||
|
||
$sth = $pdo->prepare('select * from cams where uid = :uid and camid = :camid'); | ||
$sth->execute(array(':uid' => $uid, ':camid' => $camid)); | ||
|
||
$camera = $sth->fetch(); | ||
|
||
// TODO validate passwd | ||
|
||
if ($user !== false && $camera !== false) { | ||
return true; | ||
} else { | ||
return false; | ||
} | ||
} | ||
|
||
} |
63 changes: 63 additions & 0 deletions
63
webcam-capture-addons/webcam-capture-addon-spycam/src/main/php/stream.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
<?php | ||
|
||
# Used to separate multipart | ||
$boundary = "spycam-mjpeg-stream"; | ||
|
||
# We start with the standard headers. PHP allows us this much | ||
header("Cache-Control: no-cache"); | ||
header("Cache-Control: private"); | ||
header("Pragma: no-cache"); | ||
header("Content-type: multipart/x-mixed-replace; boundary=$boundary"); | ||
|
||
# From here out, we no longer expect to be able to use the header() function | ||
print "--$boundary\n"; | ||
|
||
# set unlimited so PHP doesn't timeout during a long stream | ||
set_time_limit(0); | ||
|
||
@apache_setenv('no-gzip', 1); // disable apache gzip compression | ||
@ini_set('zlib.output_compression', 0); // disable PHP zlib compression | ||
@ini_set('implicit_flush', 1); // set implicit flush | ||
|
||
// and flush all current buffers | ||
|
||
for ($i = 0; $i < ob_get_level(); $i++) { | ||
ob_end_flush(); | ||
} | ||
|
||
ob_implicit_flush(1); | ||
|
||
|
||
// streaming | ||
|
||
$files = scandir('uploads'); | ||
$i = 0; | ||
|
||
# The loop, producing one jpeg frame per iteration | ||
while (true) { | ||
|
||
if ($i >= count($files)) { | ||
break; | ||
} | ||
|
||
$file = $files[$i++]; | ||
if (substr($file, strlen($file) - 4, 4) !== '.jpg') { | ||
continue; | ||
} | ||
|
||
# Per-image header, note the two new-lines | ||
print("Content-type: image/jpeg\n\n"); | ||
//print("Content-type: text/plain\n\n"); | ||
|
||
# Your function to get one jpeg image | ||
print(file_get_contents("uploads/$file")); | ||
//print("uploads/$file"); | ||
|
||
//flush(); | ||
//ob_flush(); | ||
usleep(200000); | ||
|
||
# The separator | ||
print("--$boundary\n"); | ||
|
||
} |