xharx: pic refreshen

Ich benutze den Browser als Interface für ein Simulationsprogramm, das ich entwickle. Jetzt will ich grafische Ausgaben meiner Simulation im Browser darstellen. Die daten liegen zur Zeit als BMP- Datei vor. Wie kann ich den Browser im HTML- Code anweisen, dieses Bild jeweils neu zu laden? Der Weg, den ich bisher benutzt habe, mit Hilfe des Javascripts, funktioniert zwar irgendwie, allerdings gelingen damit nur langsame Refresh- Raten und die Anzeige flackert beim Udaten( der Browser lädt für kurze Zeit den Alternativtext der darzustellenden Grafik).

setInterval(getnews, 1000, "getarenapic");

wie kann ich eine solche Datei in der Geschwindigkeit des Bildschirm- refhesh (also zum Beispiel bei meiner Maschine ca 60 Hz) aktualisieren? Vielen Dank.

<html>
<head>
	<style> 
		table { border: 2px solid black; } 
		tr { border: 2px solid black; } 
		td { 
			border: 1px solid black; 
			vertical-align: top;
		} 
	</style>
	<style type="text/css">
		body { background-color: #d8d8a8; }
	</style>
	<script type="text/javascript">
		//https://de.wikibooks.org/wiki/Websiteentwicklung:_AJAX:_Erstes_Programm
		function getnews(idname){
			if (window.XMLHttpRequest){
				var myAjax = new XMLHttpRequest();
			}else{
				//Dieser Code wird als Fallback für den IE5 und IE6 benötigt, da diese die obrige Schreibweise nicht unterstützen.
				var myAjax = new ActiveXObject("Microsoft.XMLHTTP");
			}

			myAjax.onreadystatechange=function(){
				if (myAjax.readyState==4 && myAjax.status==200){
					document.getElementById(idname).innerHTML=myAjax.responseText;
				}
			};
			myAjax.open("GET", idname, true);
			myAjax.send();
		}
		//Benutzung: setInterval(getnews, interval, name des id- Feldes im body) benutzen, um regelmäßig im Interval abzudaten
		setInterval(getnews, 1000, "refreshdata");
		setInterval(getnews, 5000, "signaturen");
		setInterval(getnews, 1000, "getarenapic");
		//getnews(name des Feldes) benutzen, um Inhalt einmalig zu laden.
		//im server GET- Nachricht abfangen und Inhalt mittels send zurücksenden
		getnews("prefs");
		getnews("kosten");
		getnews("mutabor");
	</script>
</head>
<body>
	<h1>Welcome to EvoProgs!</h1>
	<p>Bad times for organisms on this planet. The age of programisms is about to come.<p/>
	<table>
		<tr>
			<td>
				<a href="ende">Progsramm beenden</a><br>
				<a href="arenazeigen">Arena zeigen</a><br>
				<a href="#">max</a><br>
				<a href="langsam">langsam</a><br>
			</td>
			<td id="prefs">prefs</td>
			<td id="kosten">kosten</td>
			<td id="mutabor">mutabor</td>
			<td rowspan="2" id="refreshdata">Daten werden vorbereitet...</>
			</td>
		</tr>
		<tr>
			<td id="getarenapic" colspan =4></td>
			</td>
		</tr>
		<tr>
			<td>
				<td colspan="3" id="signaturen">signaturen</td>
			</td>
			<td>
				bl6
			</td>
		</tr>
	</table>

</body>
</html>
  1. Hello,

    das Problem ist meistens der Cache.

    Wenn Du den Browser (wie auch immer) dazu aufforderst, die src des Images neu zu laden und dabei anstelle von <path-to-src> <path-to-src?timecode> angibst, tun dir die Browser i.d.R. den Gefallen, das Bild neu zu laden und zu rendern. Wenn es dieselben Abmessungen wie das alte hat, geschieht dies meistens auch flimmerfrei.

    Liebe Grüße
    Tom S.

    --
    Es gibt nichts Gutes, außer man tut es!
    Das Leben selbst ist der Sinn.
    1. Wäre dieser Code korrekt? <td colspan = 4> <img src="bild1.bmp?1000">

      Wenn ich es richtig verstehe, müsste an den Server die Anforderung mit bild1.bmp gehen. Ich kriege aber keine Anfrage.

      1. Hello,

        Wäre dieser Code korrekt? <td colspan = 4> <img src="bild1.bmp?1000">

        Prinzipiell schon.

        Wenn ich es richtig verstehe, müsste an den Server die Anforderung mit bild1.bmp gehen. Ich kriege aber keine Anfrage.

        Wenn diese Anforderung nicht schon einmal gesendet und befriedigt wurde. Das nächste Mal müsste sie dann eben

        	<td colspan = 4><img src="bild1.bmp?1001" alt="Bild vom Gnumpf"> 
        

        heißen.

        Man kann aber mit den Cache-Anwseisungen vom Server an den Browser noch eine Menge Blödsinn anstellen.

        Hast Du mal die Konsole (Netzwerk-Tab) deines Browsers (bei FireFox->F12) bemüht? Da kannst Du schon eine Menge bezüglich der Kommunikation erkennen.

        Liebe Grüße
        Tom S.

        --
        Es gibt nichts Gutes, außer man tut es!
        Das Leben selbst ist der Sinn.
  2. Hallo,

    so richtig durchblicke ich deinen Code nicht, wo werden denn die Bilder geladen?

    Verstehe ich das richtig: du willst 60 Bitmaps pro Sekunde vom Server holen? Wie groß ist denn eine Bitmap?

    Gruß
    Jürgen

    1. Die Bitmaps sind für Testzwecke erstmal sehr klein (320*240). Aber wahrscheinlich spielt die Größe gar keine so große Rolle, weil der Server ja über localhost (127.0.0.1) verbunden ist. Der Server ist in meiner Simulation, habe dort einen Socket gemacht, starte den Browser mit einem systen("")- Aufruf und antworte dann auf die Anforderungen des Clienten (Browser, firefox).

    2. Die Bilder werden geladen, weil die Antwort auf "getarenapic" im Server die Zeile

      "<img src="bild%i.bmp"

      wobei für %i eine Laufnummer generiert wird.

      1. Hallo,

        Die Bilder werden geladen, weil die Antwort auf "getarenapic" im Server die Zeile

        "<img src="bild%i.bmp"

        wobei für %i eine Laufnummer generiert wird.

        hast du mal versucht, per JavaScript das src-Attribut des img zu ändern?

        Gruß
        Jürgen

  3. Hallo xharx,

    in Anbetracht der gewünschten Frequenz würde ich entweder serverseitig einen Film generieren und als Datenstrom zur Verfügung stellen oder die Bilder gar nicht auf dem Server produzieren, sondern nur die sich sich ändernden Daten übertragen und mittels Javascript in ein <canvas> oder <svg> einbauen.

    MfG, at

  4. @@xharx

    Die daten liegen zur Zeit als BMP- Datei vor.

    Ändere das.

    wie kann ich eine solche Datei in der Geschwindigkeit des Bildschirm- refhesh (also zum Beispiel bei meiner Maschine ca 60 Hz) aktualisieren?

    Du willst Bewegtbild. Je nach Medium auch Film oder Video genannt. Dafür gibt es spezielle Formate, wobei die Datenmenge sinnvoll komprimiert wird. Mach aus deiner Sequenz von Bitmap-Grafiken serverseitig einen Videostream!

    LLAP 🖖

    --
    “When UX doesn’t consider all users, shouldn’t it be known as ‘Some User Experience’ or... SUX? #a11y” —Billy Gregory
    1. Hello @at, @Gunnar Bittersmann ,

      Du willst Bewegtbild. Je nach Medium auch Film oder Video genannt. Dafür gibt es spezielle Formate, wobei die Datenmenge sinnvoll komprimiert wird. Mach aus deiner Sequenz von Bitmap-Grafiken serverseitig einen Videostream!

      wenn sich zwischen den Frames die Rahmendaten ändern, ist es kein vorberechenbarer Film, sondern bleibt eine Folge von Einzelbildern, die aber vielleicht teilweise aufeinander aufbauen.

      Liebe Grüße
      Tom S.

      --
      Es gibt nichts Gutes, außer man tut es!
      Das Leben selbst ist der Sinn.
      1. @@TS

        wenn sich zwischen den Frames die Rahmendaten ändern, ist es kein vorberechenbarer Film

        Doch. Man kann in einem Videostream auch ein Standbild zeigen. Wenn es nach Ablauf der 16 Komma Millisekunden noch kein akuellisiertes Bild, zeigt man das vorige nochmal.

        LLAP 🖖

        --
        “When UX doesn’t consider all users, shouldn’t it be known as ‘Some User Experience’ or... SUX? #a11y” —Billy Gregory
        1. Hello,

          wenn sich zwischen den Frames die Rahmendaten ändern, ist es kein vorberechenbarer Film

          Doch. Man kann in einem Videostream auch ein Standbild zeigen. Wenn es nach Ablauf der 16 Komma Millisekunden noch kein akuellisiertes Bild, zeigt man das vorige nochmal.

          Grundsätzlich eine gute Idee. :-)

          Mit welchem serverseitigen Programm würdest Du das machen? Ein einzelnes Frame in einem (M)JPEG- oder MPEG-Stream zeitnah serverseitig zu ersetzen erfordert viel Rechenarbeit, einen schnellen Server und viel Speicher.

          Mach bitte einen realistischen Vorschlag!

          Liebe Grüße
          Tom S.

          --
          Es gibt nichts Gutes, außer man tut es!
          Das Leben selbst ist der Sinn.
          1. Welches Format nehme ich denn da? Gibt es da etwas, wo ich meine BMP- Daten erben kann?

            1. Hallo xharx,

              MJPEG kommt deiner Vorstellung wahrscheinlich am nächsten.

              MfG, at

          2. Oder gibt es nicht in HTML oder in java eine Schnittstelle, die Daten als bewegte Frames direkt abspielen kann?

            1. Ich denke, ich nehme mp4 Format, das Wiki rät dazu. Und dann https://trac.ffmpeg.org/wiki/StreamingGuide das -re Flag... to be continued.

          3. Hallo TS,

            Mit welchem serverseitigen Programm würdest Du das machen? Ein einzelnes Frame in einem (M)JPEG- oder MPEG-Stream zeitnah serverseitig zu ersetzen erfordert viel Rechenarbeit, einen schnellen Server und viel Speicher.

            Es muss nichts innerhalb des Streams ersetzt werden, weil der Stream ja selbst zusammengesetzt wird.

            Mach bitte einen realistischen Vorschlag!

            FFmpeg.

            MfG, at

    2. Extra ein Format für Bewegtbild einsetzen, kommt mir zu umständlich vor. Was spricht dagegen, einfach Daten in einen canvas einzukopieren?

      1. Hi there,

        Extra ein Format für Bewegtbild einsetzen, kommt mir zu umständlich vor. Was spricht dagegen, einfach Daten in einen canvas einzukopieren?

        Genau das Problem mit der Geschwindigkeit, das Du jetzt hast…

        (Abgesehen davon, daß das Anzeigen von BMP-Bildern bei Browsern erstens eher ein Glückspielspiel ist und zweitens schon alleine auf Grund der Größe der Bilder von der Ladezeiten bei Deinem Vorhaben ein Problem werden kann; Gunnar hat das ja schon erwähnt...)

        1. BMP ist nicht mehr aktuell. Das canvas hat ja sein eigenes Format. Diese Daten stelle ich im Server bereit

  5. wie kann ich eine solche Datei in der Geschwindigkeit des Bildschirm- refhesh (also zum Beispiel bei meiner Maschine ca 60 Hz) aktualisieren?

    60 Hz ist kein Problem. Aber: Die Images müssen im Cache des Clients vorliegen, also Preload. MfG

    1. Kann eine Lösung werden. Ich lade also zum Beispiel zehn Bilder vor und spiele sie dann aus dem Array ab? Das müsste doch auch ohne cache gehen?!

      1. Hello,

        Kann eine Lösung werden. Ich lade also zum Beispiel zehn Bilder vor und spiele sie dann aus dem Array ab? Das müsste doch auch ohne cache gehen?!

        Nicht den normalem Browsercache benutzten, sondern den Local Storage.

        Hatten wir dazu nicht etwas im Wiki?

        Liebe Grüße
        Tom S.

        --
        Es gibt nichts Gutes, außer man tut es!
        Das Leben selbst ist der Sinn.
      2. Hmm, wie der Browser ein Preload cacht kann ich Dir gar nicht sagen. Aber wenn Du da selbst aktiv werden willst, lege die Images in den Hauptspeicher, und zwar so, daß Du sie direkt einem src= zuweisen kannst. JS kann ja mittlerweile mit Binaries umgehen und z.B. DataURLs aus ArrayBuffer oder Blob erzeugen.

        MfG

  6. Sorry, wenn ich das Forum nicht so bedient habe, wie es gewünscht wird. Die Benutzung ist zum Teil nicht selbsterklärend. Ich bleibe jetzt also bei diesem thread und versuche mal, die Fragen hier zusammenhängend neu zu stellen. Der neuestes Stand ist jetzt, dass ich Bilddaten vom Server hole, die in ein canvas- Element kopiert werden sollen. Der Server ist über localhost angebunden, Bandbreiten sollten also kein Thema sein. Ich möchte aus Kompatibilitätsgründen so wenig komplexe Formate benutzen wie möglich, deshalb präferiere ich die Lösung, direkt in den canvas zu kopieren.

    <!DOCTYPE html>
    <html>
    <head>
    	<style> 
    		table { border: 2px solid black; } 
    		tr { border: 2px solid black; } 
    		td { 
    			border: 1px solid black; 
    			vertical-align: top;
    		} 
    	</style>
    	<style type="text/css">
    		body { background-color: #d8d8a8; }
    		mytext {;}
    	</style>
    	<script type="text/javascript">
    		function getnews(idname){
    			var myAjax = new XMLHttpRequest();
    			myAjax.responseType="text"
    			myAjax.onreadystatechange=function(){
    				if (myAjax.readyState==4 && myAjax.status==200){
    					document.getElementById(idname).innerHTML=myAjax.responseText;
    				}
    			};
    			myAjax.open("GET", idname, true);
    			myAjax.send();
    		}
    
    		imgData=0;
    		canvas=0;
    		canvasheight=480;
    		canvaswidth=640;
    		function render() {
    			var myAjax = new XMLHttpRequest();
    			myAjax.responseType="arraybuffer";
    			myAjax.onreadystatechange=function(){
    				if (myAjax.readyState==4 && myAjax.status==200){
    					if (!canvas) {
    						canvas=document.getElementById("canvasid");
    						console.log("canvasneu");
    						context=canvas.getContext('2d');
    						if (context) {
    							if (!imgData) {
    								imgData=context.getImageData(0,0,canvaswidth,canvasheight);
    							}
    						}
    					}
    					else {
    						responsearray=new Uint8Array(myAjax.response);
    						i=responsearray.length;
    						while (i--) imgData.data[i]=responsearray[i];
    						responsearray=null;
    						myAjax.response=null;//das ist wohl überflüssig
    						context.putImageData(imgData, 0, 0); //Daten ins canvas schreiben
    					}
    				}
    			};
    			myAjax.open("GET", "canvasdata", true);
    			myAjax.send();
    		}
    		function animate() {
    			render();
    			requestAnimationFrame(animate);
    		}
    		animate();
    
    		//Benutzung: setInterval(getnews, interval, name des id- Feldes im body) benutzen, um regelmäßig im Interval abzudaten
    		//im server GET- Nachricht abfangen und Inhalt mittels send zurücksenden
    		setInterval(getnews, 1000, "refreshdata");
    		setInterval(getnews, 1000, "signaturen");
    		getnews("prefs");
    		getnews("kosten");
    		getnews("mutabor");
    	</script>
    </head>
    <body>
    	<h1>Welcome to EvoProgs 12!</h1>
    	<p>Bad times for organisms on this planet. The age of programisms is about to come.<p/>
    	<table>
    		<tr>
    			<td>
    				<a href="ende">Progsramm beenden</a><br>
    				<a href="arenazeigen">Arena zeigen</a><br>
    				<a href="#">max</a><br>
    				<a href="langsam">langsam</a><br>
    			</td>
    			<td id="prefs">prefs</td>
    			<td id="kosten">kosten</td>
    			<td id="mutabor">mutabor</td>
    			<td rowspan="2" id="refreshdata">Daten werden vorbereitet...</>
    			</td>
    		</tr>
    		<tr>
    			<!-- td id="getarenapic" colspan =4></td -->
    			<td colspan = 4> <canvas id="canvasid" width=640 height=480 alt="neu canvas">
    			<!--td id="getarenapic" colspan =4>
    				<img src="bild1.bmp" alt="Grafik kann nicht angezeigt werden" / -->
    			<!--td colspan =4>
    				<img src="bild1.bmp" alt="Grafik kann nicht angezeigt werden" / -->
    			</td>
    		</tr>
    		<tr>
    			<td>
    				<td colspan="3" id="signaturen">signaturen</td>
    			</td>
    			<td>
    				bl6
    			</td>
    		</tr>
    	</table>
    
    	<!-- img src="/home/xharx/Dropbox/Programmieren/evsim/evoprogs1/PNG_transparency_demonstration_1.png" alt="bla" /-->
    </body>
    </html>
    

    In diesem Code entsteht leider ein Memoryleak von etwa hunder megabyte pro Sekunde (großes canvas, 60 hz). Wie kann ich dieses abstellen? Ist es ok, die Daten als arraybuffer zu holen (nur dann kommt es zu dem leak). Oder besser per blob? Wie kopiere ich die Daten aus dem .response am besten in die Struktur imgData.data?

    Sorry für die Doppelpostings, ich wollte nichts durcheinanderbringen.

    1. Hallo xharx,

      Ich fürchte du wirst dein Ziel auf diesem Wege nicht erreichen können. Du peilst 60 Bilder pro Sekunde an und für jedes Bild startest du eine eigene http-Anfrage. Jede Frage-Antwort-Runde hat damit gerade mal ein Zeitfenster von 16,67ms und da ist die clientseitige Weiterverarbeitung noch gar nicht berücksichtigt, die nimmt auch nochmal Zeit in Anspruch. Du müsstest auch noch berücksichtigen, dass die Server-Antworten nicht zwingend in der Reihenfolge eintreffen, in der du sie angefragt hast. Du müsstest die Antworten also noch ordnen und ggf. alle überholten Anfragen abbrechen, um die Netzwerk-Auslastung nicht unkontrolliert in die Höhe zu treiben.

      		imgData=0;
      		canvas=0;
      		canvasheight=480;
      		canvaswidth=640;
      		function render() {
      
      			var myAjax = new XMLHttpRequest();
      			myAjax.responseType="arraybuffer";
      			if (myAjax.response==0) console.log("network error");
      			myAjax.onreadystatechange=function(){
      
      				if (myAjax.readyState==4 && myAjax.status==200){
      					if (!canvas) {
      						canvas=document.getElementById("canvasid");
      						console.log("canvasneu");
      						context=canvas.getContext('2d');
      						if (context) {
      							if (!imgData) {
      								imgData=context.getImageData(0,0,canvaswidth,canvasheight);
      							}
      						}
      					}
      					else {
      						responsearray=new Uint8Array(myAjax.response);
      						i=responsearray.length;
      						while (i--) imgData.data[i]=responsearray[i];
      						responsearray=null;
      						context.putImageData(imgData, 0, 0);
      					}
      				}
      
      
      			};
      			myAjax.open("GET", "canvasdata", true);
      			myAjax.send();
      
      		}
      		function animate() {
      			render();
      			requestAnimationFrame(animate);
      		}
      		animate();
      

      Und dann hast du noch ein Problem in deinem Rendering-Loop: Bei jedem Aufruf von requestAnimationFrame wird jeweils nur der grün markierte Code ausgeführt. In diesem Bereich sendest du die http-Anfragen ab, gezeichnet wird da gar nichts. Das kann nämlich erst passieren, wenn die Server-Antwort eintrifft, also in dem rot-markierten Bereich, aber bis dahin hat der Browser den Frame längst gezeichnet. Dann trifft irgendwann die Server-Antwort ein, du zeichnest tatsächlich etwas auf das Canvas und unterbrichst dann trotzdem den Browser bei seinem aktuellen Zeichenvorgang. Das Skelett, für deinen Rendering-Loop, sollte also eher so aussehen:

      function fetchBitmap() {
        const request = new XMLHttpRequest()
        request.open("GET", "canvasdata")
        request.addEventListener("load", onLoad)
      }
      
      function onLoad (response) {
        // … verarbeite response weiter zu imgData …
        requestAnimationFrame(() => render(imgData))
      }
      
      function render (imgData) {
        context.putImageData(imgData, 0, 0)
        fetchBitmap()
      }
      

      Letzten Endes bleibt aber das Problem, dass jeder Frame auch eine http-Anfrage voraussetzt und dafür zu wenig Zeit bleibt. Ich rate dir auf jeden Fall dazu, serverseitig ein richtiges Video zu generieren und keine Einzelbilder. Der Unterschied ist in etwa der zwischen einem HD-Kino und einem Daumenkino.

      1. Danke für diesen Vorschlag. Aber das Problem mit dem Memoryleak bleibt. Wie kann ich einen XMLHttpRequest mit .responseType="arraybuffer" so verwenden, dass es zu keinem Memoryleak kommt?

        Ich schicke die Daten für den Canvas von einem selbstgeschriebenen Server. Bei einer Refreshrate von 60 Hz entsteht dabei ein Memoryleak von ca 100 MB / sec.

  7. Ich komme immer noch nicht weiter mit meinem Problem wegen des memoryleaks.

    <!DOCTYPE html>
    <html>
    <head>
    	<style> 
    		table { border: 2px solid black; } 
    		tr { border: 2px solid black; } 
    		td { 
    			border: 1px solid black; 
    			vertical-align: top;
    		} 
    	</style>
    	<style type="text/css">
    		body { background-color: #d8d8a8; }
    		mytext {;}
    	</style>
    	<script type="text/javascript">
    		function render() {
    			var myAjax = new XMLHttpRequest();
    			myAjax.responseType="blob"
    			myAjax.open("GET", "getbmp");
    			myAjax.send();
    		}
    		setInterval(render, 100);
    	</script>
    </head>
    <body>
    	<h1>Welcome to EvoProgs 18!</h1>
    				<img id="getarenapic" alt="getarenapic Grafik kann nicht angezeigt werden" / >
    </body>
    </html>
    

    Mein Server liefert im Intverall seine Daten, Firefox entwickelt ein riesiges Memoryleak, mehrere MB / sec. Der Code macht doch gar nichts, trotzdem dieses leak. Was übersehe ich? Bitte um Hilfe, ich komme nicht weiter.

    1. Hallo,

      Der Code macht doch gar nichts

      Ich hab zwar keine Ahnung von JS, aber für mich sieht es so aus, als ob der Browser 10 pro Sekunde ein neues xmlhttprequest-Objekt erstellen soll…

      Gruß
      Kalk

    2. Hallo,

      du lässt dem Garbagecollektor keine Zeit zum Aufräumen. Das musst du selbst machen.

      Aber, warum überhaupt Ajax? Warum setzt du nicht einfach das src-Attribut eines img?

      Gruß
      Jürgen

      1. Die Idee ist gut und so habe ich es umgesetzt. Ich hatte nur überlegt, dass canvas noch nahtloser wäre, statt jedesmal ein .bmp zu laden. Aber so funktioniert es ausreichend gut.

        Kleine Frage noch nebenbei: lässt sich XMLHttpRequest so anpassen, dass der Garbagecollector nachkommt? Oder würde man den request irgendwie anders durchführen?