Skip to content
Snippets Groups Projects
Commit a935bbc2 authored by Peter Gerwinski's avatar Peter Gerwinski
Browse files

Vortragsfolien, Beispiele und Tafelbilder 2.11.2022

parent 791cb92c
No related branches found
No related tags found
No related merge requests found
File added
% es-20221102.pdf - Lecture Slides on Embedded Systems
% Copyright (C) 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022 Peter Gerwinski
%
% This document is free software: you can redistribute it and/or
% modify it either under the terms of the Creative Commons
% Attribution-ShareAlike 3.0 License, or under the terms of the
% GNU General Public License as published by the Free Software
% Foundation, either version 3 of the License, or (at your option)
% any later version.
%
% This document is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with this document. If not, see <http://www.gnu.org/licenses/>.
%
% You should have received a copy of the Creative Commons
% Attribution-ShareAlike 3.0 Unported License along with this
% document. If not, see <http://creativecommons.org/licenses/>.
% README: VNC, Netzwerkprogrammierung
\documentclass[10pt,t]{beamer}
\usepackage{pgslides}
\usepackage{rotating}
\usepackage{tikz}
\newcommand{\underconstruction}{%
\begin{picture}(0,0)
\put(9,0.9){\makebox(0,0)[b]{\includegraphics[width=1.5cm]{Zeichen_123.pdf}}}
\put(9,0.6){\makebox(0,0)[t]{\shortstack{Änderungen\\vorbehalten}}}
\end{picture}}
\title{Eingebettete Systeme}
\author{Prof.\ Dr.\ rer.\ nat.\ Peter Gerwinski}
\date{2.\ November 2022}
\newcommand{\greyurl}[1]{\href{#1}{\color{gray}\nolinkurl{#1}}}
\newcommand{\ItwoC}{I\raisebox{0.5ex}{\footnotesize 2}C}
\begin{document}
\maketitleframe
\nosectionnonumber{\inserttitle}
\begin{frame}
\shownosectionnonumber
\begin{itemize}
\item[\textbf{1}] \textbf{Einführung}
\hfill\makebox(0,0)[br]{\raisebox{2.25ex}{\url{https://gitlab.cvh-server.de/pgerwinski/es}}}%
\item[\textbf{2}] \textbf{Einführung in Unix}
\item[\textbf{3}] \textbf{TCP/IP in der Praxis}
\begin{itemize}
\color{medgreen}
\item [3.1] IP-Adressen
\item [3.2] MAC-Adressen
\item [3.3] TCP- und UDP-Ports
\item [3.4] TCP-Protokolle
\item [3.5] Routing
\item [3.6] Netzwerkanalyse
\item [3.7] SSH
\item [3.8] X11 \color{red}und VNC
\item[3.9] Programmierung
\end{itemize}
\item[\textbf{\dots}]
\end{itemize}
\strut\hfill \url{https://www.peter.gerwinski.de/download/net-2013ss.tar.gz}
% \vspace*{-1cm plus 1filll}
% \underconstruction
\end{frame}
\setcounter{section}{2}
\section{TCP/IP in der Praxis}
\setcounter{subsection}{4}
\subsection{Routing}
\begin{frame}[fragile]
\showsection
\showsubsection
\begin{itemize}
\item
\lstinline[style=cmd]{ip route} (Linux)\\
\lstinline[style=cmd]{route} (MS-Windows, Unix)\\
\lstinline[style=cmd]{netstat -nr} (MacOS)
% \\[\medskipamount]
% \includegraphics[width=11cm]{../20171026/photo-20171026-162455.jpg}
\end{itemize}
\medskip
\begin{lstlisting}[style=terminal]
# route -n
Kernel-IP-Routentabelle
Ziel Router Genmask [...] Iface
0.0.0.0 192.168.42.1 0.0.0.0 [...] wlan0
169.254.0.0 0.0.0.0 255.255.0.0 [...] wlan0
192.168.42.0 0.0.0.0 255.255.255.0 [...] wlan0
\end{lstlisting}
\bigskip
Netzmaske:\\
Wenn nach Und-Verknüpfung mit IP-Adresse gleich,
\textarrow\ im gleichen Netz
\medskip
\lstinline[style=terminal]{255.255.240.0} ist dasselbe wie
\lstinline[style=terminal]{/20}\\
(20 Bit sind 1; die restlichen 12 Bit sind 0)
\end{frame}
\subsection{Netzwerkanalyse}
\begin{frame}
\showsection
\showsubsection
\begin{itemize}
\item
\lstinline[style=cmd]{tcpdump}
\item
\lstinline[style=cmd]{wireshark}
\item
\lstinline[style=cmd]{ettercap}
\end{itemize}
\end{frame}
\subsection{SSH}
\begin{frame}
\showsection
\showsubsection
\begin{itemize}
\item
\lstinline[style=cmd]{SSH <Rechner>}
\item
\lstinline[style=cmd]{-C}: Komprimierung
\item
\lstinline[style=cmd]{-L}: lokalen Port auf Remote-Port umleiten
\item
\lstinline[style=cmd]{-R}: Remote-Port auf lokalen Port umleiten
\end{itemize}
\end{frame}
\subsection{X11 und VNC}
\begin{frame}
\showsection
\showsubsection
\begin{itemize}
\item
Grafik-Bildschirm und Eingabegeräte über's Netz
\item
\lstinline[style=cmd]{DISPLAY}-Variable: X-Server: Rechner und Bildschirm
\item
\lstinline[style=cmd]{ssh -X}: X11-Forwarding
\pause
\bigskip
\item
VNC = Virtual Network Computing
\item
VNC-Server stellt Bildschirminhalt zur Verfügung
\begin{itemize}
\item entweder: eigener, virtueller X11-Server
\item oder: ruft Inhalt von anderem (X11-) Bildschirm ab
\end{itemize}
\item
VNC-Client ruft Bildschirminhalt ab und stellt ihn dar
\begin{itemize}
\item z.\,B.\ per X11
\item z.\,B.\ per Web-Interface: noVNC
\end{itemize}
\end{itemize}
\end{frame}
\subsection{Programmierung}
\begin{frame}
\showsection
\showsubsection
\begin{itemize}
\item
Shell-Skripte: \file{nc} (\file{traditional} oder \file{OpenBSD})
\item
C: Sockets, \lstinline{select()}
\item
C++: Callbacks
\end{itemize}
\pause
\bigskip
Beispiel: Programmierung eines VNC-Servers per Web-Interface
\begin{itemize}
\item
Grundlagen: RFC~6143
\item
Praxis: Untersuchung mit Wireshark
\item
Implementation: JavaScript, WebSockets, \file{websockify}, Callbacks
\end{itemize}
\end{frame}
\end{document}
../common/logo-hochschule-bochum-cvh-text-v2.pdf
\ No newline at end of file
../common/logo-hochschule-bochum.pdf
\ No newline at end of file
../common/pgslides.sty
\ No newline at end of file
File added
20221102/vnc-01-vnc-cvh-und-prof-notebook.png

219 KiB

20221102/vnc-01-vnc-cvh.png

177 KiB

20221102/vnc-01-x11-und-vnc-protokolle.png

170 KiB

20221102/vnc-01-x11-und-vnc.png

164 KiB

20221102/vnc-01-x11.png

77.9 KiB

File added
<!doctype html public "-//W3C//DTD HTML 4.0 Strict//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf8">
<title>yesVNC Web Connector</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta charset="utf-8">
<link rel="stylesheet" href="stylesheets/yesvnc-wc.css" type="text/css">
</head>
<body>
<main class="main">
<header class="main__header">
<h2 class="main__header-title">yesVNC Web Connector</h2>
<h3 id="room-indicator" class="main__header-subtitle">
Channel&nbsp;6
</h3>
</header>
<hr class="main__seperator" />
<div id="preview-section" class="hidden">
<div id="preview-container">
<video id="video" style="display: none;" autoplay></video>
<canvas id="canvas0" style="display:none;"></canvas>
<canvas class="preview" id="canvas1"></canvas>
</div>
<hr class="main__seperator" />
</div>
<div class="main__controls">
<button id="start">Start</button>
<button id="stop" disabled>Stop</button>
</div>
</main>
<script>
const debugging = 0;
const combine_rects_x_min_distance = 24;
const combine_rects_y_min_distance = 4;
const scroll_detection_x_min = -64;
const scroll_detection_x_max = 64;
const scroll_detection_y_min = -64;
const scroll_detection_y_max = 64;
const scroll_detection_r_max = 16;
const scroll_detection_min_width = 256;
const scroll_detection_min_height = 256;
const scroll_detection_min_stripe_width = 16;
const scroll_detection_min_stripe_height = 16;
const scroll_detection_point_divisor = 1024;
const scroll_detection_length_divisor = 8;
const scroll_detection_same_color_threshold = 0.995;
const scroll_detection_hit_threshold = 0.995;
let video = document.getElementById ("video");
const canvas0 = document.getElementById ("canvas0");
const canvas1 = document.getElementById ("canvas1");
let ctx0 = undefined;
let ctx1 = undefined;
function hideElement (el)
{
el.classList.add ('hidden');
}
function showElement (el)
{
el.classList.remove ('hidden');
}
const displayMediaOptions = { video: { cursor: "always" }, audio: false };
async function startCapture ()
{
video.srcObject = await navigator.mediaDevices.getDisplayMedia (displayMediaOptions);
showElement (document.getElementById ('preview-section'));
}
function stopCapture (evt)
{
hideElement (document.getElementById ('preview-section'));
let tracks = video.srcObject.getTracks ();
tracks.forEach (
function (track)
{
track.stop ();
});
video.srcObject = null;
}
const startButton = document.getElementById ("start");
const stopButton = document.getElementById ("stop");
startButton.addEventListener ("click",
function (evt)
{
startCapture ();
startButton.setAttribute ('disabled', '');
stopButton.removeAttribute ('disabled');
},
false);
stopButton.addEventListener ("click",
function (evt)
{
stopCapture ();
start.removeAttribute ('disabled');
stopButton.setAttribute ('disabled', '');
window.location.reload();
},
false);
let socket = undefined;
function buf2hex (buffer)
{
const b = new Uint8Array (buffer);
const hexdump = Array.prototype.map.call (b,
function (x)
{
return ("00" + x.toString (16)).slice (-2);
}).join (" ");
return hexdump + " (" + buffer.byteLength.toString ()
+ (buffer.byteLength == 1 ? " byte)" : " bytes)");
}
let wScreen = 0;
let hScreen = 0;
const rfb_padding = 0;
const rfb_encoding_raw = 0;
const rfb_encoding_copyrect = 1;
const rfb_encoding_rre = 2;
const rfb_encoding_tight = 7;
const rfb_name = "wc:6";
const rfb_name_length = rfb_name.length;
class Rect
{
constructor (x, y, w, h, encoding)
{
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.encoding = encoding;
}
send ()
{
const buffer = new ArrayBuffer (12);
const b = new Uint8Array (buffer);
b[0] = this.x >>> 8;
b[1] = this.x & 0xff;
b[2] = this.y >>> 8;
b[3] = this.y & 0xff;
b[4] = this.w >>> 8;
b[5] = this.w & 0xff;
b[6] = this.h >>> 8;
b[7] = this.h & 0xff;
b[8] = this.encoding >>> 24
b[9] = (this.encoding >>> 16) & 0xff;
b[10] = (this.encoding >>> 8) & 0xff;
b[11] = this.encoding & 0xff;
if (debugging)
console.log ("sending Rect object: " + buf2hex (buffer));
socket.send (buffer);
}
}
class RawRect extends Rect
{
constructor (x, y, w, h, data)
{
super (x, y, w, h, rfb_encoding_raw);
this.data = data;
}
send ()
{
super.send ();
if (debugging)
console.log ("sending RawRect object data (" + this.data.byteLength.toString () + " pixel bytes)");
socket.send (this.data);
}
}
class ScrollRect extends Rect
{
constructor (x, y, w, h, dx, dy)
{
super (x, y, w, h, rfb_encoding_copyrect);
this.dx = dx;
this.dy = dy;
}
send ()
{
super.send ();
const buffer = new ArrayBuffer (4);
const b = new Uint8Array (buffer);
b[0] = this.dx >>> 8;
b[1] = this.dx & 0xff;
b[2] = this.dy >>> 8;
b[3] = this.dy & 0xff;
if (debugging)
console.log ("sending ScrollRect object: " + buf2hex (buffer));
socket.send (buffer);
}
}
class SolidRect extends Rect
{
constructor (x, y, w, h, color)
{
super (x, y, w, h, rfb_encoding_rre);
this.color = color;
}
send ()
{
super.send ();
const buffer = new ArrayBuffer (8);
const b = new Uint8Array (buffer);
b[0] = 0; // number of sub-rectangles
b[1] = 0;
b[2] = 0;
b[3] = 0;
b[4] = this.color >>> 16;
b[5] = (this.color >>> 8) & 0xff;
b[6] = this.color & 0xff;
b[7] = rfb_padding;
if (debugging)
console.log ("sending SolidRect object: " + buf2hex (buffer));
socket.send (buffer);
}
}
class TightRect extends Rect
{
constructor (x, y, w, h, data)
{
super (x, y, w, h, rfb_encoding_tight);
this.data = data;
}
send ()
{
super.send ();
const data_length = this.data.byteLength;
let data_length_length = undefined;
if (data_length <= 0x7f)
data_length_length = 1;
else if (data_length <= 0x3fff)
data_length_length = 2;
else
data_length_length = 3;
const buffer = new ArrayBuffer (1 + data_length_length);
const b = new Uint8Array (buffer);
b[0] = 0x90; // Tight-JPEG
if (data_length <= 0x7f)
b[1] = data_length & 0x7f;
else if (data_length <= 0x3fff)
{
b[1] = (data_length & 0x7f) | 0x80;
b[2] = (data_length & 0x3f80) >>> 7;
}
else
{
b[1] = (data_length & 0x7f) | 0x80;
b[2] = ((data_length & 0x3f80) >>> 7) | 0x80;
b[3] = (data_length & 0x3fc000) >>> 14;
}
if (debugging)
console.log ("sending TightRect object header: " + buf2hex (buffer));
socket.send (buffer);
if (debugging)
console.log ("sending TightRect object data (" + data_length.toString () + " pixel bytes)");
socket.send (this.data);
}
}
let rects = new Array ();
class ScrollParams
{
constructor (x, y, w, h, dx, dy)
{
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.dx = dx;
this.dy = dy;
}
}
let scrollRects = new Array ();
let rfb_bits_per_pixel = 32;
let rfb_depth = 24;
let rfb_big_endian_flag = 0;
let rfb_true_color_flag = 1;
let rfb_red_maximum = 255;
let rfb_green_maximum = 255;
let rfb_blue_maximum = 255;
let rfb_red_shift = 24;
let rfb_green_shift = 16;
let rfb_blue_shift = 8;
let isFirstFrame = 1;
let processing = 0;
const rfb_aborted = 42;
function fillRectBuffer (px, x, y, w, h, color)
{
for (let yy = 0; yy < h; yy++)
{
let o = (y + yy) * wScreen + x;
for (let xx = 0; xx < w; xx++)
px[o++] = color;
}
}
function scrollRectBuffer (px, x, y, w, h, dx, dy)
{
const ww = w - Math.abs (dx);
const hh = h - Math.abs (dy);
const do0 = dy > 0 ? -wScreen : wScreen;
let o10 = (dy > 0 ? y + h - 1 : y) * wScreen + (dx > 0 ? x + w - 1 : x);
let o00 = ((dy > 0 ? y + h - 1 : y) - dy) * wScreen + (dx > 0 ? x + w - 1 : x) - dx;
for (let i = 0; i < hh; i++)
{
o1 = o10;
o0 = o00;
if (dx > 0)
for (let j = 0; j < ww; j++)
px[o1--] = px[o0--];
else
for (let j = 0; j < ww; j++)
px[o1++] = px[o0++];
o10 += do0;
o00 += do0;
}
}
function sleep (delay)
{
return new Promise (
function (resolve)
{
setTimeout (resolve, delay)
});
}
function canvasToBlob (canvas, type, options)
{
return new Promise (
function (resolve)
{
canvas.toBlob (resolve, type, options);
});
}
function pushRectRaw (x, y, w, h)
{
const data_length = w * h * 4;
const buffer = new ArrayBuffer (data_length);
const b = new Uint8Array (buffer);
const id1 = ctx1.getImageData (x, y, w, h);
const px1 = new Uint8Array (id1.data.buffer);
let bo = 0;
let po = 0;
for (let yy = 0; yy < h; yy++)
for (let xx = 0; xx < w; xx++)
{
b[bo + 0] = px1[po + 2];
b[bo + 1] = px1[po + 1];
b[bo + 2] = px1[po + 0];
b[bo + 3] = rfb_padding;
bo += 4;
po += 4;
}
if (debugging)
console.log ("storing RawRect (" + data_length.toString () + " pixel bytes)");
rects.push (new RawRect (x, y, w, h, buffer));
}
function pushRectScroll (x, y, w, h, dx, dy)
{
if (debugging)
console.log ("storing ScrollRect");
rects.push (new ScrollRect (x, y, w, h, dx, dy));
}
function pushRectSolid (x, y, w, h, color)
{
if (debugging)
console.log ("storing SolidRect");
rects.push (new SolidRect (x, y, w, h, color));
}
async function pushRectTight (x, y, w, h)
{
if (debugging)
console.log ("pushRectTight: x = " + x.toString () + ", y = " + y.toString () + ", w = " + w.toString () + ", h = " + h.toString ());
let canvas = undefined;
if (x == 0 && y == 0 && w == wScreen && h == hScreen)
canvas = canvas1;
else
{
canvas = document.createElement ('canvas');
canvas.width = w;
canvas.height = h;
canvas.getContext ('2d').drawImage (canvas1, -x, -y);
}
// if (debugging)
// console.log ("calling canvasToBlob()");
const blob = await canvasToBlob (canvas, "image/jpeg");
// if (debugging)
// console.log ("calling blob.arrayBuffer()");
const jpeg_buffer = await blob.arrayBuffer ();
// if (debugging)
// console.log ("storing TightRect (" + jpeg_buffer.byteLength.toString () + " instead of " + (w * h * 4).toString () + " pixel bytes)");
rects.push (new TightRect (x, y, w, h, jpeg_buffer));
}
function detectScrolling (px0, px1, x, y, w, h)
{
if (debugging)
console.log ("scrolling: detection started");
const pixels = w * h;
const points = Math.floor (pixels / scroll_detection_point_divisor);
const qxBuffer = new ArrayBuffer (2 * points);
const qx = new Uint16Array (qxBuffer);
const qyBuffer = new ArrayBuffer (2 * points);
const qy = new Uint16Array (qyBuffer);
const qc1Buffer = new ArrayBuffer (4 * points);
const qc1 = new Uint32Array (qc1Buffer);
const qc0Buffer = new ArrayBuffer (4 * points);
const qc0 = new Uint32Array (qc0Buffer);
for (let i = 0; i < points; i++)
{
const p = Math.floor (Math.random () * pixels);
// I lost a whole day to find out that p / w is a floating point division,
// which causes crazy things to happen when we use yy as an array index.
// I hate JavaScript. -- PG 20201222
const yy = Math.floor (p / w);
const xx = p % w;
const o = (y + yy) * wScreen + x + xx;
qy[i] = yy;
qx[i] = xx;
qc1[i] = px1[o];
qc0[i] = px0[o];
}
const xDensityBuffer = new ArrayBuffer (2 * w);
const xDensity = new Uint16Array (xDensityBuffer);
const yDensityBuffer = new ArrayBuffer (2 * h);
const yDensity = new Uint16Array (yDensityBuffer);
for (let xx = 0; xx < w; xx++)
{
xDensity[xx] = 0;
for (let i = 0; i < points; i++)
if (qx[i] < xx && qc1[i] != qc0[i])
xDensity[xx]++;
}
const lx = Math.floor (w / scroll_detection_length_divisor);
const halflx = Math.floor (lx / 2);
const averageDensity = Math.floor (xDensity[w - 1] / scroll_detection_length_divisor);
if (debugging)
console.log ("scrolling: averageDensity =", averageDensity);
let x0 = 0;
while (x0 + lx < w)
{
while (x0 + lx < w && xDensity[x0 + lx] - xDensity[x0] < averageDensity)
x0++;
let x1 = x0;
while (x1 + lx < w && xDensity[x1 + lx] - xDensity[x1] >= averageDensity)
x1++;
if (x1 - x0 >= scroll_detection_min_stripe_width)
{
if (debugging)
console.log ("scrolling: vertical stripe:", x0 + halflx, x1 - x0);
for (let yy = 0; yy < h; yy++)
{
yDensity[yy] = 0;
for (let i = 0; i < points; i++)
if (qy[i] < yy && qc1[i] != qc0[i] && qx[i] >= x0 && qx[i] < x1)
yDensity[yy]++;
}
const ly = Math.floor (h / scroll_detection_length_divisor);
const halfly = Math.floor (ly / 2);
const averageDensity = Math.floor (yDensity[h - 1] / scroll_detection_length_divisor);
if (debugging)
console.log ("scrolling: averageDensity =", averageDensity);
let y0 = 0;
while (y0 + ly < h)
{
while (y0 + ly < h && yDensity[y0 + ly] - yDensity[y0] < averageDensity)
y0++;
let y1 = y0;
while (y1 + ly < h && yDensity[y1 + ly] - yDensity[y1] >= averageDensity)
y1++;
if (y1 - y0 >= scroll_detection_min_stripe_height)
{
if (debugging)
console.log ("scrolling: horizontal stripe:", y0 + halfly, y1 - y0);
let sx = x0 + halflx;
let sy = y0 + halfly;
let sw = x1 - x0;
let sh = y1 - y0;
if (debugging)
{
console.log ("scrolling: checking area:", sx, sy, sw, sh);
console.log ("scrolling: " + scrollRects.length.toString () + " rects");
}
const redundant = scrollRects.find (
function (r)
{
if (debugging)
console.log ("scrolling: checking rect", sx, sy, sw, sh, "against", r.x, r.y, r.w, r.h, r.dx, r.dy);
if (sx + sw <= r.x || sx >= r.x + r.w)
return 0;
else if (sy + sh <= r.y || sy >= r.y + r.h)
return 0;
else
{
if (debugging)
console.log ("scrolling: new rect", sx, sy, sw, sh, "is redundant with", r.x, r.y, r.w, r.h, r.dx, r.dy);
return 1;
}
});
if (!redundant)
{
const color = new Array;
for (let i = 0; i < points; i++)
if (qx[i] >= sx && qx[i] < sx + sw && qy[i] >= sy && qy[i] < sy + sh)
color.push (qc1[i]);
const points_inside = color.length;
color.sort (
function (a, b)
{
return a - b;
});
let color_abundance = 1;
let i0 = 0;
let i1 = 1;
while (i1 < points_inside)
{
if (color[i1] != color[i0])
{
const a = i1 - i0;
if (a > color_abundance)
{
color_abundance = a;
i0 = i1;
}
}
i1++
}
const a = i1 - i0;
if (a > color_abundance)
color_abundance = a;
if (debugging)
console.log ("scrolling: color_abundance = " + color_abundance.toString () + ", points_inside = " + points_inside.toString ()
+ ", threshold = " + (points_inside * scroll_detection_same_color_threshold).toString ());
if (color_abundance < points_inside * scroll_detection_same_color_threshold)
{
let min_mishits = points_inside;
let sdx = 0;
let sdy = 0;
// We intentionally include the case dx == dy == 0.
// Without this, we get false positives where unchanged content
// is scrolled by 1 pixel because the "repair" after the scrolling
// is minimal.
for (let dy = scroll_detection_y_max; dy >= scroll_detection_y_min && min_mishits > 0; dy--)
for (let dx = scroll_detection_x_max; dx >= scroll_detection_x_min && min_mishits > 0; dx--)
if ((dx == 0 || dy == 0 || dx * dx + dy * dy < scroll_detection_r_max * scroll_detection_r_max))
{
let i = 0;
let mishits = 0;
while (i < points && mishits < min_mishits)
{
if (qx[i] >= sx && qx[i] < sx + sw && qy[i] >= sy && qy[i] < sy + sh)
{
const o0 = (y + qy[i] - dy) * wScreen + x + qx[i] - dx;
if (o0 >= 0 && o0 < pixels && qc1[i] != px0[o0])
mishits++;
// if (i < 10)
// console.log ("scrolling: point:", qx[i], qy[i], "rect:", sx, sx + sw, sy, sy + sh,
// "o0, px:", o0, qc1[i], px0[o0]);
}
i++;
}
if (mishits < min_mishits)
{
min_mishits = mishits;
sdx = dx;
sdy = dy;
}
if (debugging)
console.log ("scrolling: dx = " + dx.toString () + ", dy = " + dy.toString ()
+ ", mishits = " + mishits.toString () + ", points_inside = " + points_inside.toString ());
}
if (debugging)
console.log ("scrolling: sdx = " + sdx.toString () + ", sdy = " + sdy.toString ()
+ ", min_mishits = " + min_mishits.toString () + ", points_inside = " + points_inside.toString ());
if (min_mishits < points_inside * (1 - scroll_detection_hit_threshold))
{
if (debugging)
console.log ("scrolling: expanding rect:", sx, sy, sw, sh, sdx, sdy);
let xx = sx - 1;
let scrollable = 1;
while (xx > 0 && scrollable)
{
let yy = sy;
let scrollable_points = 0;
let o1 = (y + yy) * wScreen + x + xx;
let o0 = (y + yy - sdy) * wScreen + x + xx - sdx;
while (yy < sy + sh)
{
if (px1[o1] == px0[o0])
scrollable_points++;
yy++;
o1 += wScreen;
o0 += wScreen;
}
if (scrollable_points < sh * scroll_detection_hit_threshold)
scrollable = 0;
xx--;
}
if (!scrollable)
sx = xx + 1;
else
sx = xx;
xx = sx + sw;
scrollable = 1;
while (xx < w && scrollable)
{
let yy = sy;
let scrollable_points = 0;
let o1 = (y + yy) * wScreen + x + xx;
let o0 = (y + yy - sdy) * wScreen + x + xx - sdx;
while (yy < sy + sh)
{
if (px1[o1] == px0[o0])
scrollable_points++;
yy++;
o1 += wScreen;
o0 += wScreen;
}
if (scrollable_points < sh * scroll_detection_hit_threshold)
scrollable = 0;
xx++;
}
if (!scrollable)
sw = xx - sx - 1;
else
sw = xx - sx;
let yy = sy - 1;
scrollable = 1;
while (yy > 0 && scrollable)
{
let xx = sx;
let scrollable_points = 0;
let o1 = (y + yy) * wScreen + x + xx;
let o0 = (y + yy - sdy) * wScreen + x + xx - sdx;
while (xx < sx + sw)
{
if (px1[o1] == px0[o0])
scrollable_points++;
xx++;
o1++;
o0++;
}
if (scrollable_points < sw * scroll_detection_hit_threshold)
scrollable = 0;
yy--;
}
if (!scrollable)
sy = yy + 1;
else
sy = yy;
yy = sy + sh;
scrollable = 1;
while (yy < h && scrollable)
{
let xx = sx;
let scrollable_points = 0;
let o1 = (y + yy) * wScreen + x + xx;
let o0 = (y + yy - sdy) * wScreen + x + xx - sdx;
while (xx < sx + sw)
{
if (px1[o1] == px0[o0])
scrollable_points++;
xx++;
o1++;
o0++;
}
if (scrollable_points < sw * scroll_detection_hit_threshold)
scrollable = 0;
yy++;
}
if (!scrollable)
sh = yy - sy - 1;
else
sh = yy - sy;
// if (debugging)
console.log ("scrolling: pushing rect:", sx, sy, sw, sh, sdx, sdy);
scrollRects.forEach (
function (r)
{
if (!(sx + sw <= r.x || sx >= r.x + r.w || sy + sh <= r.y || sy >= r.y + r.h))
{
if (debugging)
console.log ("scrolling: neutralizing redundant rect:", r.x, r.y, r.w, r.h, r.dx, r.dy);
r.dx = 0;
r.dy = 0;
}
});
scrollRects.push (new ScrollParams (sx, sy, sw, sh, sdx, sdy));
}
}
}
}
y0 = y1;
}
}
x0 = x1;
}
scrollRects.forEach (
function (r)
{
if (r.dx != 0 || r.dy != 0)
{
const adx = Math.abs (r.dx);
const ady = Math.abs (r.dy);
if (r.w > adx && r.h > ady)
{
// pushRectSolid (r.x, r.y, r.w, r.h, 0xff0000);
// fillRectBuffer (px0, r.x, r.y, r.w, r.h, 0xffff0000);
const xx = r.dx > 0 ? r.x + r.dx : r.x;
const yy = r.dy > 0 ? r.y + r.dy : r.y;
pushRectScroll (xx, yy, r.w - adx, r.h - ady, xx - r.dx, yy - r.dy);
scrollRectBuffer (px0, r.x, r.y, r.w, r.h, r.dx, r.dy);
}
}
});
if (debugging)
console.log ("scrolling: " + scrollRects.length.toString ()
+ " rectangle" + (scrollRects.length == 1 ? "" : "s") + " detected");
scrollRects.length = 0;
}
function determineBackgroundColor (px, x, y, w, h)
{
const pixels = w * h;
const max_points = Math.floor (pixels / 16);
const points = max_points > 256 ? 256 : max_points;
const color = new Array (points);
for (let i = 0; i < points; i++)
{
const p = Math.floor (Math.random () * pixels);
const yy = Math.floor (p / w);
const xx = p % w;
const o = yy * wScreen + x;
color[i] = px[o];
}
color.sort (
function (a, b)
{
return a - b;
});
let background_index = 0;
let background_abundance = 1;
let i0 = 0;
let i1 = 1;
while (i1 < points)
{
if (color[i1] != color[i0])
{
const a = i1 - i0;
if (a > background_abundance)
{
background_index = i0;
background_abundance = a;
i0 = i1;
}
}
i1++
}
const a = i1 - i0;
if (a > background_abundance)
background_index = i0;
return color[background_index];
}
async function updateFullRectangle (x, y, w, h)
{
if (w * h * 4 > 1024)
await pushRectTight (x, y, w, h);
else
pushRectRaw (x, y, w, h);
}
async function updateInnerStructureHorizontal (px0, px1, x, y, w, h, final)
{
// if (debugging)
// console.log ("updateInnerStructureHorizontal: x = " + x.toString () + ", y = " + y.toString ()
// + ", w = " + w.toString () + ", h = " + h.toString () + ", final = ", final.toString ());
const empty = new Array (w);
const pixel_unchanged = 0x01000000;
const same_color = 0x2000000;
let xx = 0;
while (xx < w)
{
let yy = 0;
let o = y * wScreen + x + xx;
const start_color = px1[o];
empty[xx] = (start_color & 0x00ffffff) | (pixel_unchanged | same_color);
while (yy < h && empty[xx] & (pixel_unchanged | same_color))
{
if (px1[o] != px0[o])
empty[xx] &= ~pixel_unchanged;
if (px1[o] != start_color)
empty[xx] &= ~same_color;
o += wScreen;
yy++;
}
xx++;
}
let x0 = 0;
while (x0 < w && empty[x0] & (pixel_unchanged | same_color))
x0++;
while (x0 < w)
{
while (x0 < w && !(empty[x0] & (pixel_unchanged | same_color)))
x0++;
let x1 = x0;
while (x1 < w && empty[x1] & (pixel_unchanged | same_color))
x1++;
if (x1 < w && x1 - x0 < combine_rects_x_min_distance)
for (let xx = x0; xx < x1; xx++)
empty[xx] &= ~(pixel_unchanged | same_color);
x0 = x1;
}
let nothing_empty = 1;
for (let xx = 0; xx < w; xx++)
if (empty[xx] & (pixel_unchanged | same_color))
nothing_empty = 0;
// if (debugging)
// console.log ("nothing_empty = " + nothing_empty.toString () + ", empty =", empty);
x0 = 0;
while (x0 < w)
{
let x1 = x0;
if (empty[x0] & pixel_unchanged)
{
while (x1 < w && empty[x1] & pixel_unchanged)
x1++;
}
else if (empty[x0] & same_color)
{
while (x1 < w && empty[x1] & same_color && (empty[x1] & 0x00ffffff) == (empty[x0] & 0x00ffffff))
x1++;
x1--;
while (x1 > x0 && empty[x1] & pixel_unchanged)
x1--;
x1++;
const start_color = empty[x0] | 0xff000000;
pushRectSolid (x + x0, y, x1 - x0, h, debugging ? start_color | 0x00ffff : start_color);
fillRectBuffer (px0, x + x0, y, x1 - x0, h, start_color);
}
else
{
while (x1 < w && !(empty[x1] & (pixel_unchanged | same_color)))
x1++;
if (final)
await updateFullRectangle (x + x0, y, x1 - x0, h);
else
await updateInnerStructureVertical (px0, px1, x + x0, y, x1 - x0, h, nothing_empty);
}
x0 = x1;
}
}
async function updateInnerStructureVertical (px0, px1, x, y, w, h, final)
{
// if (debugging)
// console.log ("updateInnerStructureVertical: x = " + x.toString () + ", y = " + y.toString ()
// + ", w = " + w.toString () + ", h = " + h.toString () + ", final = ", final.toString ());
const empty = new Array (h);
const pixel_unchanged = 1;
const same_color = 2;
let yy = 0;
while (yy < h)
{
let xx = 0;
let o = (y + yy) * wScreen + x;
const start_color = px1[o];
empty[yy] = (start_color & 0x00ffffff) | (pixel_unchanged | same_color);
while (xx < w && empty[yy] & (pixel_unchanged | same_color))
{
if (px1[o] != px0[o])
empty[yy] &= ~pixel_unchanged;
if (px1[o] != start_color)
empty[yy] &= ~same_color;
o++;
xx++;
}
yy++;
}
let y0 = 0;
while (y0 < h && empty[y0] & (pixel_unchanged | same_color))
y0++;
while (y0 < h)
{
while (y0 < h && !(empty[y0] & (pixel_unchanged | same_color)))
y0++;
let y1 = y0;
while (y1 < h && empty[y1] & (pixel_unchanged | same_color))
y1++;
if (y1 < h && y1 - y0 < combine_rects_y_min_distance)
for (let yy = y0; yy < y1; yy++)
empty[yy] &= ~(pixel_unchanged | same_color);
y0 = y1;
}
let nothing_empty = 1;
for (let yy = 0; yy < h; yy++)
if (empty[yy] & (pixel_unchanged | same_color))
nothing_empty = 0;
// if (debugging)
// console.log ("nothing_empty = " + nothing_empty.toString () + ", empty =", empty);
y0 = 0;
while (y0 < h)
{
let y1 = y0;
if (empty[y0] & pixel_unchanged)
{
while (y1 < h && empty[y1] & pixel_unchanged)
y1++;
}
else if (empty[y0] & same_color)
{
while (y1 < h && empty[y1] & same_color && (empty[y1] & 0x00ffffff) == (empty[y0] & 0x00ffffff))
y1++;
y1--;
while (y1 > y0 && empty[y1] & pixel_unchanged)
y1--;
y1++;
const start_color = empty[y0] | 0xff000000;
pushRectSolid (x, y + y0, w, y1 - y0, debugging ? start_color | 0xffff00 : start_color);
fillRectBuffer (px0, x, y + y0, w, y1 - y0, start_color);
}
else
{
while (y1 < h && !(empty[y1] & (pixel_unchanged | same_color)))
y1++;
if (final)
await updateFullRectangle (x, y + y0, w, y1 - y0);
else
await updateInnerStructureHorizontal (px0, px1, x, y + y0, w, y1 - y0, nothing_empty);
}
y0 = y1;
}
}
async function decomposeRect (px0, px1, x, y, w, h)
{
await updateInnerStructureVertical (px0, px1, x, y, w, h, 0);
}
async function rfbFramebufferUpdate (isIncremental, x, y, w, h)
{
if (debugging)
console.log ("rfbFramebufferUpdate: isIncremental = " + isIncremental.toString ()
+ ", x = " + x.toString () + ", y = " + h.toString ()
+ ", w = " + w.toString () + ", h = " + h.toString ());
let id1 = ctx1.getImageData (x, y, w, h);
ctx0.putImageData (id1, x, y, x, y, w, h);
const id0 = ctx0.getImageData (0, 0, wScreen, hScreen);
const px0 = new Uint32Array (id0.data.buffer);
ctx1.drawImage (video, 0, 0, wScreen, hScreen);
id1 = ctx1.getImageData (0, 0, wScreen, hScreen);
let px1 = new Uint32Array (id1.data.buffer);
if (!isIncremental)
{
const color = determineBackgroundColor (px1, x, y, w, h);
if (debugging)
console.log ("background color = " + color.toString ());
pushRectSolid (x, y, w, h, debugging ? color | 0x7f00ff : color);
fillRectBuffer (px0, x, y, w, h, color);
}
do
{
const length = px0.length;
let o = 0;
while (o < length && px0[o] == px1[o])
o++;
if (o < length)
{
// if (isIncremental && w >= scroll_detection_min_width && h >= scroll_detection_min_height)
// detectScrolling (px0, px1, x, y, w, h);
await decomposeRect (px0, px1, x, y, w, h);
}
if (rects.length == 0)
{
await sleep (100);
ctx1.drawImage (video, 0, 0, wScreen, hScreen);
id1 = ctx1.getImageData (x, y, w, h);
px1 = new Uint32Array (id1.data.buffer);
}
}
while (rects.length == 0);
}
function rfbReceivePixelFormat (buffer, offset)
{
if (debugging)
console.log ("received: SetPixelFormat");
const b = new Uint8Array (buffer);
rfb_bits_per_pixel = b[4];
rfb_depth = b[5];
rfb_big_endian_flag = b[6];
rfb_true_color_flag = b[7];
rfb_red_maximum = b[8] << 8 + b[9];
rfb_green_maximum = b[10] << 8 + b[11];
rfb_blue_maximum = b[12] << 8 + b[13];
rfb_red_shift = b[14];
rfb_green_shift = b[15];
rfb_blue_shift = b[16];
return 20;
}
function rfbReceiveEncodings (buffer, offset)
{
if (debugging)
console.log ("received: SetEncodings");
const b = new Uint8Array (buffer);
const encodings = b[offset + 2] << 8 + b[offset + 3];
return 4 + 4 * encodings;
}
function rfbAbort ()
{
if (debugging)
console.log ("Aborting RFB transmission ...");
processing += rfb_aborted;
}
async function rfbFramebufferUpdateRequest (buffer, offset)
{
if (debugging)
console.log ("received: FramebufferUpdateRequest");
if (processing)
{
// if (debugging)
if (processing >= rfb_aborted)
console.log ("aborted");
else
console.log ("already being processed");
}
else
{
processing++;
let b = new Uint8Array (buffer);
let isIncremental = b[1];
const x = b[2] * 256 + b[3];
const y = b[4] * 256 + b[5];
const w = b[6] * 256 + b[7];
const h = b[8] * 256 + b[9];
if (debugging)
{
console.log ("FramebufferUpdateRequest: Initiating processing ...");
console.log ("wScreen = ", wScreen, ", hScreen = ", hScreen + ", w = ", w, ", h = ", h);
}
rects.length = 0;
if (processing < rfb_aborted)
{
if (isFirstFrame)
{
await rfbFramebufferUpdate (0, 0, 0, wScreen, hScreen);
isFirstFrame = 0;
}
else if (isIncremental)
await rfbFramebufferUpdate (1, x, y, w, h);
else
await rfbFramebufferUpdate (0, x, y, w, h);
if (debugging)
console.log ("FramebufferUpdateRequest: Processing completed. Sending data ... ("
+ rects.length.toString ()
+ " rectangle" + (rects.length != 1 ? "s" : "") + ")");
const header_buffer = new ArrayBuffer (4);
b = new Uint8Array (header_buffer);
const num_rects = rects.length;
b[0] = 0;
b[1] = rfb_padding;
b[2] = num_rects >>> 8;
b[3] = num_rects & 0xff;
if (debugging)
console.log ("sending rfbFramebufferUpdate header: " + buf2hex (header_buffer));
socket.send (header_buffer);
rects.forEach (
function sendIt (r)
{
r.send ();
});
}
processing--;
}
return 10;
}
function rfbReceiveKeyEvent (buffer, offset)
{
if (debugging)
console.log ("received: KeyEvent");
return 8;
}
function rfbReceivePointerEvent (buffer, offset)
{
if (debugging)
console.log ("received: PointerEvent");
return 6;
}
function rfbReceiveCutText (buffer, offset)
{
if (debugging)
console.log ("received: ClientCutText");
const b = new Uint8Array (buffer);
const length = b[offset + 4] << 24 + b[offset + 5] << 16 + b[offset + 6] << 8 + b[offset + 7];
return 8 + length;
}
function rfbServer (event)
{
if (debugging)
console.log ("received: " + buf2hex (event.data));
const b = new Uint8Array (event.data);
let offset = 0;
while (offset < event.data.byteLength)
{
if (debugging)
console.log ("received RFB packet type " + b[offset].toString ());
if (b[offset] == 0)
offset += rfbReceivePixelFormat (event.data, offset);
else if (b[offset] == 2)
offset += rfbReceiveEncodings (event.data, offset);
else if (b[offset] == 3)
offset += rfbFramebufferUpdateRequest (event.data, offset);
else if (b[offset] == 4)
offset += rfbReceiveKeyEvent (event.data, offset);
else if (b[offset] == 5)
offset += rfbReceivePointerEvent (event.data, offset);
else if (b[offset] == 6)
offset += rfbReceiveCutText (event.data, offset);
else
{
if (debugging)
console.log ("unknown RFB packet");
offset = event.data.byteLength;
}
}
}
function rfbFramebufferHandshake (event)
{
if (debugging)
console.log ("received: " + buf2hex (event.data));
socket.removeEventListener ("message", rfbFramebufferHandshake);
socket.addEventListener ("message", rfbServer);
const buffer = new ArrayBuffer (24 + rfb_name_length);
const b = new Uint8Array (buffer);
b[0] = wScreen >>> 8;
b[1] = wScreen & 0xff;
b[2] = hScreen >>> 8;
b[3] = hScreen & 0xff;
b[4] = rfb_bits_per_pixel;
b[5] = rfb_depth;
b[6] = rfb_big_endian_flag;
b[7] = rfb_true_color_flag;
b[8] = rfb_red_maximum >>> 8;
b[9] = rfb_red_maximum & 0xff;
b[10] = rfb_green_maximum >>> 8;
b[11] = rfb_green_maximum & 0xff;
b[12] = rfb_blue_maximum >>> 8;
b[13] = rfb_blue_maximum & 0xff;
b[14] = rfb_red_shift;
b[15] = rfb_green_shift;
b[16] = rfb_blue_shift;
b[17] = rfb_padding;
b[18] = rfb_padding;
b[19] = rfb_padding;
b[20] = rfb_name_length >>> 24;
b[21] = rfb_name_length >>> 16;
b[22] = rfb_name_length >>> 8;
b[23] = rfb_name_length & 0xff;
for (let i = 0; i < rfb_name_length; i++)
b[24 + i] = rfb_name[i];
if (debugging)
console.log ("sending framebuffer parameters: " + buf2hex (buffer));
socket.send (buffer);
}
function rfbAuthenticate (event)
{
if (debugging)
console.log ("received: " + buf2hex (event.data));
socket.removeEventListener ("message", rfbAuthenticate);
socket.addEventListener ("message", rfbFramebufferHandshake);
if (debugging)
console.log ("sending: 00 00 00 00");
const buffer = new ArrayBuffer (4);
const b = new Uint8Array (buffer);
b[0] = 0;
b[1] = 0;
b[2] = 0;
b[3] = 0;
socket.send (buffer);
}
function rfbSecurityHandshake (event)
{
if (debugging)
console.log ("received: " + buf2hex (event.data));
socket.removeEventListener ("message", rfbSecurityHandshake);
socket.addEventListener ("message", rfbAuthenticate);
if (debugging)
console.log ("sending: 01 01");
const buffer = new ArrayBuffer (2);
const b = new Uint8Array (buffer);
b[0] = 1;
b[1] = 1;
socket.send (buffer);
}
function rfbConnect (event)
{
socket.addEventListener ("message", rfbSecurityHandshake);
if (debugging)
console.log ("sending: RFB 003.008\\n");
socket.send ("RFB 003.008\n");
}
document.addEventListener ("DOMContentLoaded",
function ()
{
video.addEventListener ("play",
function ()
{
wScreen = video.videoWidth;
hScreen = video.videoHeight;
canvas1.width = wScreen;
canvas1.height = hScreen;
ctx1 = canvas1.getContext ("2d");
canvas0.width = wScreen;
canvas0.height = hScreen;
ctx0 = canvas0.getContext ("2d");
ctx0.beginPath ();
ctx0.rect (0, 0, wScreen, hScreen);
ctx0.fillStyle = "black";
ctx0.fill ();
if (debugging)
console.log ("Opening WebSocket ...");
socket = new WebSocket ("wss://streaming.cvh-server.de/websock/wc-6/", ['binary', 'base64']);
socket.binaryType = "arraybuffer";
socket.addEventListener ("open", rfbConnect);
},
false);
video.addEventListener ("abort", rfbAbort, false);
video.addEventListener ("emptied", rfbAbort, false);
video.addEventListener ("ended", rfbAbort, false);
video.addEventListener ("error", rfbAbort, false);
video.addEventListener ("pause", rfbAbort, false);
video.addEventListener ("stalled", rfbAbort, false);
// video.addEventListener ("suspend", rfbAbort, false);
});
</script>
</body>
</html>
......@@ -9,3 +9,4 @@ Eingebettete Systeme: Termine
*** Es bleibt dabei: ab 11:30 Uhr in Heiligenhaus bzw. online ***
02.11.2022: bis 13:00 Uhr
09.11.2022: bis 18:45 Uhr
16.11.2022: bis 18:45 Uhr
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment