(r'^/config/main/(?P<op>set|get)/?$', handlers.ConfigHandler),
(r'^/config/(?P<camera_id>\d+)/(?P<op>get|set|rem|set_preview)/?$', handlers.ConfigHandler),
(r'^/config/(?P<op>add|list|list_devices)/?$', handlers.ConfigHandler),
- (r'^/picture/(?P<camera_id>\d+)/(?P<op>current|list)/?$', handlers.PictureHandler),
+ (r'^/picture/(?P<camera_id>\d+)/(?P<op>current|list|frame)/?$', handlers.PictureHandler),
(r'^/picture/(?P<camera_id>\d+)/(?P<op>download|preview)/(?P<filename>.+)/?$', handlers.PictureHandler),
(r'^/movie/(?P<camera_id>\d+)/(?P<op>list)/?$', handlers.MovieHandler),
(r'^/movie/(?P<camera_id>\d+)/(?P<op>download|preview)/(?P<filename>.+)/?$', handlers.MovieHandler),
--- /dev/null
+
+
+ /* basic */
+
+* {
+ padding: 0px;
+ border: 0px solid black;
+ margin: 0px;
+ outline: 0px;
+ border-spacing: 0px;
+ border-collapse: separate;
+}
+
+html {
+ height: 100%;
+}
+
+body {
+ height: 100%;
+ color: #dddddd;
+ background-color: #212121;
+}
+
+
+ /* layout */
+
+img.main-loading-progress {
+ display: block;
+ margin: auto;
+ margin-top: 50px;
+}
+
+div.add-camera-message {
+ text-align: center;
+ margin-top: 30px;
+}
+
+
+ /* camera frame */
+
+div.camera-frame {
+ position: relative;
+ width: 100%;
+ height: 100%;
+ transition: all 0.2s, opacity 0s;
+ opacity: 0;
+ vertical-align: top;
+}
+
+div.camera-container {
+ height: 100%;
+}
+
+img.camera {
+ width: 100%;
+ height: auto;
+ display: block;
+ transition: opacity 0.2s linear;
+ opacity: 1;
+}
+
+img.camera.error,
+img.camera.loading {
+ opacity: 0;
+ height: 100%;
+}
+
+div.camera-placeholder {
+ position: absolute;
+ top: 0px;
+ right: 0px;
+ bottom: 0px;
+ left: 0px;
+ text-align: center;
+ transition: opacity 0.2s linear;
+}
+
+img.no-camera {
+ margin-top: 20%;
+ width: 30%;
+ opacity: 0.8;
+}
+
+div.camera-progress {
+ background: rgba(0, 0, 0, 0.001); /* otherwise IE would not extend this as expected */
+ position: absolute;
+ top: 0px;
+ right: 0px;
+ bottom: 0px;
+ left: 0px;
+ opacity: 0;
+ transition: all 0.2s linear;
+ text-align: center;
+}
+
+div.camera-progress.visible {
+ opacity: 0.4;
+}
+
+img.camera-progress {
+ border: 10px solid white;
+ border-radius: 10px;
+ position: absolute;
+ top: 0px;
+ left: 0px;
+ bottom: 0px;
+ right: 0px;
+ margin: auto;
+}
--- /dev/null
+
+var refreshDisabled = false;
+var inProgress = false;
+var refreshInterval = 50; /* milliseconds */
+
+
+ /* progress */
+
+function beginProgress() {
+ if (inProgress) {
+ return; /* already in progress */
+ }
+
+ inProgress = true;
+
+ /* show the camera progress indicator */
+ $('div.camera-progress').addClass('visible');
+}
+
+function endProgress() {
+ if (!inProgress) {
+ return; /* not in progress */
+ }
+
+ inProgress = false;
+
+ /* hide the camera progress indicator */
+ $('div.camera-progress').removeClass('visible');
+}
+
+
+ /* camera frame */
+
+function setupCameraFrame() {
+ var cameraFrameDiv = $('div.camera-frame')
+ var cameraPlaceholder = cameraFrameDiv.find('div.camera-placeholder');
+ var cameraProgress = cameraFrameDiv.find('div.camera-progress');
+ var cameraImg = cameraFrameDiv.find('img.camera');
+ var progressImg = cameraFrameDiv.find('img.camera-progress');
+
+ cameraFrameDiv[0].refreshDivider = 0;
+ cameraFrameDiv[0].streamingFramerate = parseInt(cameraFrameDiv.attr('streaming_framerate')) || 1;
+ cameraFrameDiv[0].streamingServerResize = cameraFrameDiv.attr('streaming_server_resize') == 'True';
+ progressImg.attr('src', staticUrl + 'img/camera-progress.gif');
+
+ cameraProgress.addClass('visible');
+ cameraPlaceholder.css('opacity', '0');
+
+ /* fade in */
+ cameraFrameDiv.animate({'opacity': 1}, 100);
+
+ /* error and load handlers */
+ cameraImg.error(function () {
+ this.error = true;
+ this.loading = 0;
+
+ cameraImg.addClass('error').removeClass('loading');
+ cameraPlaceholder.css('opacity', 1);
+ cameraProgress.removeClass('visible');
+ });
+ cameraImg.load(function () {
+ if (refreshDisabled) {
+ return; /* refresh temporarily disabled for updating */
+ }
+
+ this.error = false;
+ this.loading = 0;
+
+ cameraImg.removeClass('error').removeClass('loading');
+ cameraImg.css('height', '');
+ cameraPlaceholder.css('opacity', 0);
+ cameraProgress.removeClass('visible');
+ });
+
+ cameraImg.addClass('loading');
+}
+
+function refreshCameraFrame() {
+ var $cameraFrame = $('div.camera-frame');
+ var cameraFrame = $cameraFrame[0];
+ var img = $cameraFrame.find('img.camera')[0];
+ var cameraId = cameraFrame.id.substring(6);
+
+ /* limit the refresh rate to 20 fps */
+ var count = Math.max(1, 1 / cameraFrame.streamingFramerate * 1000 / refreshInterval);
+
+ if (img.error) {
+ /* in case of error, decrease the refresh rate to 1 fps */
+ count = 1000 / refreshInterval;
+ }
+
+ if (cameraFrame.refreshDivider < count) {
+ cameraFrame.refreshDivider++;
+ }
+ else {
+ (function () {
+ if (refreshDisabled) {
+ /* camera refreshing disabled, retry later */
+
+ return;
+ }
+
+ if (img.loading) {
+ img.loading++; /* increases each time the camera would refresh but is still loading */
+
+ if (img.loading > 2 * 1000 / refreshInterval) { /* limits the retry at one every two seconds */
+ img.loading = 0;
+ }
+ else {
+ return; /* wait for the previous frame to finish loading */
+ }
+ }
+
+ var timestamp = Math.round(new Date().getTime());
+
+ var uri = '/picture/' + cameraId + '/current/?seq=' + timestamp;
+ if (cameraFrame.serverSideResize) {
+ uri += '&width=' + img.width;
+ }
+
+ img.src = uri;
+ img.loading = 1;
+
+ cameraFrame.refreshDivider = 0;
+ })();
+ }
+
+ setTimeout(refreshCameraFrame, refreshInterval);
+}
+
+function checkCameraErrors() {
+ /* properly triggers the onerror event on the cameras whose imgs were not successfully loaded,
+ * but the onerror event hasn't been triggered, for some reason (seems to happen in Chrome) */
+ var cameraFrame = $('div.camera-frame').find('img.camera');
+
+ cameraFrame.each(function () {
+ if (this.complete === true && this.naturalWidth === 0 && !this.error && this.src) {
+ $(this).error();
+ }
+ });
+
+ setTimeout(checkCameraErrors, 500);
+}
+
+
+ /* startup function */
+
+$(document).ready(function () {
+ beginProgress();
+ setupCameraFrame();
+ refreshCameraFrame();
+ checkCameraErrors();
+});
+