<?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($entry01) != '.') {
                    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($filename01) != '.') {
            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($filename112);
        
$minute substr($filename132);
        
$second substr($filename152);
        return 
$hour.':'.$minute.':'.$second;
    }

    function 
print_mp4_files($path='.') {
        
$exts = array('mp4');
        
$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="'.$file.'">Play</a></td>');
                
$this->println('<td><a href="'.$file.'" download>Download</a></td>');
                
$this->println('</tr>');
            }
            
$this->println('</table>');
        }
    }

    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&nbsp;1fps</a></td>');
                
$this->println('<td><a href="'.$_SERVER['PHP_SELF'].
                        
'?play='.urlencode($file).'&fps=5">Play&nbsp;5fps</a></td>');
                
$this->println('<td><a href="'.$_SERVER['PHP_SELF'].
                        
'?play='.urlencode($file).'&fps=25">Play&nbsp;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\r\n");
        print(
"Content-Type: image/jpeg\r\n");
        print(
"Content-Length: ".strlen($data)."\r\n");
        print(
"\r\n");
        print(
$data);
        print(
"\r\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($handle4096);
                if (
$jpeg_content) {
                    
$pos_jpeg_stop strpos($contents$jpeg_end);
                    if (
$pos_jpeg_stop) {
                        
$jpeg_data .= substr($contents0$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_mp4_files($dir);
            
$html->print_mjpg_files($dir);
            
$html->println('</div>');
        }
    }
    
$html->println('</main>');
    
$html->print_foot();
}

?>