<?php
/**
* @author Gernot Walzl
*/
class FileSystemObject {
function read_dirs($path='.') {
$result = null;
if ($handle = opendir(dirname(__FILE__).'/'.$path)) {
$result = array();
while (false !== ($entry = readdir($handle))) {
if (substr($entry, 0, 1) != '.') {
if (is_dir($path.'/'.$entry)) {
array_push($result, $path.'/'.$entry);
}
}
}
closedir($handle);
sort($result);
}
return $result;
}
function is_good_filename($filename, $exts=array()) {
$result = false;
if (substr($filename, 0, 1) != '.') {
if (empty($exts)) {
$result = true;
} else {
foreach ($exts as $ext) {
if (substr($filename, -strlen($ext)) === $ext) {
$result = true;
break;
}
}
}
}
return $result;
}
function read_files($path='.', $exts=array()) {
$result = null;
if ($handle = opendir(dirname(__FILE__).'/'.$path)) {
$result = array();
while (false !== ($file = readdir($handle))) {
if ($this->is_good_filename($file, $exts)) {
if (is_file($path.'/'.$file) &&
is_readable($path.'/'.$file)) {
array_push($result, $path.'/'.$file);
}
}
}
closedir($handle);
sort($result);
}
return $result;
}
}
/**
* @author Gernot Walzl
*/
class CameraHTML {
protected $fso = null;
function __construct($fso) {
$this->fso = $fso;
}
function println($msg) {
print($msg."\n");
}
function print_head() {
$this->println('<!DOCTYPE html>');
$this->println('<html lang="en">');
$this->println('<head>');
$this->println('<meta charset="utf-8" />');
$this->println('<title>Camera</title>');
$this->println('<meta name="viewport" '.
'content="width=device-width, initial-scale=1.0" />');
$stylesheet = 'style.css';
$href = $stylesheet.'?mtime='.filemtime($stylesheet);
$this->println('<link rel="stylesheet" href="'.$href.'" />');
$this->println('</head>');
$this->println('<body>');
}
function print_dirs() {
$dirs = $this->fso->read_dirs('rec');
if (!empty($dirs)) {
$this->println('<ul>');
for ($cnt = count($dirs)-1; $cnt >= 0; $cnt--) {
$dir = $dirs[$cnt];
$this->println('<li>');
$this->println('<a href="'.$_SERVER['PHP_SELF'].
'?dir='.urlencode($dir).'">'.
basename($dir).'</a>');
$this->println('</li>');
}
$this->println('</ul>');
}
}
function get_time_from_name($filepath) {
$filename = basename($filepath);
$hour = substr($filename, 11, 2);
$minute = substr($filename, 13, 2);
$second = substr($filename, 15, 2);
return $hour.':'.$minute.':'.$second;
}
function print_mjpg_files($path='.') {
$exts = array('avi', 'mkv', 'mjpg');
$files = $this->fso->read_files($path, $exts);
if (!empty($files)) {
$this->println('<table>');
for ($cnt = count($files)-1; $cnt >= 0; $cnt--) {
$file = $files[$cnt];
$this->println('<tr>');
$this->println('<td>'.basename($file).'</td>');
$this->println('<td><a href="'.$_SERVER['PHP_SELF'].
'?play='.urlencode($file).'&fps=1">Play 1fps</a></td>');
$this->println('<td><a href="'.$_SERVER['PHP_SELF'].
'?play='.urlencode($file).'&fps=5">Play 5fps</a></td>');
$this->println('<td><a href="'.$_SERVER['PHP_SELF'].
'?play='.urlencode($file).'&fps=25">Play 25fps</a></td>');
$this->println('<td><a href="'.$file.'" download>Download</a></td>');
$this->println('</tr>');
}
$this->println('</table>');
}
}
function print_foot() {
$this->println('</body>');
}
}
/**
* @author Gernot Walzl
*/
class MJPEGStreamer {
function send_jpeg($data) {
print("--jpgboundary\n");
print("Content-type: image/jpeg\n");
print("Content-length: ".strlen($data)."\n\n");
print($data."\n");
flush();
}
function stream_mjpeg($path, $fps=10) {
set_time_limit(0);
header('Content-type: multipart/x-mixed-replace; boundary=jpgboundary');
$frametime_us = 1000000/$fps;
$jpeg_begin = hex2bin('ffd8');
$jpeg_end = hex2bin('ffd9');
$jpeg_content = false;
$jpeg_data = '';
if ($handle = fopen($path, 'rb')) {
while (!feof($handle)) {
$contents = fread($handle, 4096);
if ($jpeg_content) {
$pos_jpeg_stop = strpos($contents, $jpeg_end);
if ($pos_jpeg_stop) {
$jpeg_data .= substr($contents, 0, $pos_jpeg_stop+2);
$jpeg_content = false;
$this->send_jpeg($jpeg_data);
usleep($frametime_us);
} else {
$jpeg_data .= $contents;
}
}
if (!$jpeg_content) {
$pos_jpeg_start = strpos($contents, $jpeg_begin);
if ($pos_jpeg_start) {
$jpeg_content = true;
$jpeg_data = substr($contents, $pos_jpeg_start);
}
}
}
fclose($handle);
}
}
}
if (!empty($_GET['play'])) {
$file = stripslashes(urldecode($_GET['play']));
if (!stristr($file, '..') &&
file_exists(dirname(__FILE__).'/'.$file)) {
$fps = 10;
if (!empty($_GET['fps'])) {
$fps = intval($_GET['fps']);
}
$streamer = new MJPEGStreamer();
$streamer->stream_mjpeg($file, $fps);
}
} else {
$fso = new FileSystemObject();
$html = new CameraHTML($fso);
$html->print_head();
$html->println('<header>');
$html->println('<h1>Camera</h1>');
$html->println('<p><a href="http://'.$_SERVER['SERVER_ADDR'].':8000/cam.mjpg">Live View</a></p>');
$html->println('</header>');
$html->println('<nav>');
$html->println('<h2>Recordings</h2>');
$html->print_dirs();
$html->println('</nav>');
$html->println('<main>');
if (!empty($_GET['dir'])) {
$dir = stripslashes(urldecode($_GET['dir']));
if (!stristr($dir, '..') &&
file_exists(dirname(__FILE__).'/'.$dir)) {
$html->println('<div id="files">');
$html->println('<h3>'.basename($dir).'</h3>');
$html->print_mjpg_files($dir);
$html->println('</div>');
}
}
$html->println('</main>');
$html->print_foot();
}
?>