Videostreaming mit 64HDD und 64net/2

Voraussetzungen

Um Videodaten flüssig darzustellen sind vor allem hohe Durchsatzraten nötig. Es gibt etliche Modi für die der C64 theoretisch schnell genug ist. Einschränkungen gibt es aber oft in der Auflösung und der Farbpalette. Das Nadelöhr ist vor allem der Transfer vom Medium in den Speicher. Also gilt es den Bandbreite zu erhöhen und gleichzeitig die Daten pro Frame zu reduzieren. Mit den schnellen Lademöglichkeiten von 64HDD und 64net/2 sind die Transfergeschwindigkeiten bereits sehr hoch. Der C64 bremst sogar unter Umständen den Transfer aus, etwa dann wenn ein komplexer Bildaufbau wie bei den 4x4 Modi stattfindet und entsprechend Taktzyklen auch für andere Aufgaben aufgewendet werden müssen. Dennoch bietet das entsprechend genutzte Datenformat noch Platz für Optimierungen, die Multicolor-Zeichensatz Methode ist ein gutes Beispiel hierfür. Passende Dateien in den hier beschriebenen Modi können mit einem Konverter aus einem Satz durchnummerierter BMP-Files (24Bit) erzeugt werden. Der Konverter liegt stets aktuell dem CVS-Repository von 64net/2 im Verzeichnis 'video' bei. Alternativ gibt es den Konverter auch hier (allerdings nicht immer aktuell).

Enhanced Multicolor Modus

Für diesen Modus wird der sogenannte Enhanced Multicolor Modus verwendet. In diesem Modus kann der VIC Zeichen mit einer Vordergrundfarbe pro Zeichen und je nach Zeichen eine aus 4 verschiedenen Hintergrundfarben anzeigen. Die Zuordnung der Farben sieht dabei wie folgt aus:

Vordergrundfarbe ($d800-$dbe8 = $00..$0f): Alle Chars
Hintergrundfarbe 1 ($d021 = $0b): Chars $00-$3f
Hintergrundfarbe 2 ($d022 = $0c): Chars $40-$7f
Hintergrundfarbe 3 ($d023 = $0f): Chars $80-$bf
Hintergrundfarbe 4 ($d024 = $01): Chars $c0-$ff

Pro Zeichen sind also immer Kombinationen von jeweils einer der vier Hintergrundfarben und der Vordergrundfarbe möglich. Die Farben die pro Char dargestellt werden sind also auf 2 begrenzt. Geht man nun von einem 4x4 Modus aus so fallen 4 Pixel auf einen Charblock. Dadaurch das beim klassichen 4x4 Modus jede 4. Zeile der Screen gewechselt wird beschreibt ein Char also jeweils 2 Pixel.
Definiert man nun den Zeichensatz so dass er aus Patterns besteht die von der Hintergrundfarbe zur Vordergrundfarbe Überblenden so ist jedes Pixel entweder eine reine Hintergrund- oder Vordergrundfarbe oder eine prozentuale Mischung aus beiden. In der Regel sind 5 verschiedene Patterns ausreichend um von einer Farbe in die nächste Überzublenden. Bei der Verwendung von mehr Patterns stechen bei manchen Mustern einzelne Pixel zu sehr heraus, da sie zu stark isoliert sind. Bei maximal 5 Patterns ergeben sich bei 2 Pixeln insgesamt 5^2 = 25 Permutationen.
Die ausgewählte Vordergrundfarbe wird durch das Farbram gesetzt, die Hintergrundfarbe wird aus den vier im voraus festgelegten Farben gewählt indem man jedem Char mit jeweils einen entsprechenden Offset nach obiger Auflistung aufaddiert. Für die festen vier Hintergrundfarben wählt man am besten die drei verschiedenen Graustufen und Weiss.
Wenn wir also z.B. ein sehr helles grün darstellen wollen, dann setzen wir das Farbram für diesen Char z.B. auf $0d (hellgrün) wählen Hintergrundfarbe 4 aus (als weiss vordefiniert) und wählen die gewünschte Farbabstufung zwischen beiden Werten durch die Wahl des entsprechenden Zeichensatzpatterns (0-5). Nehmen wir etwa das mittlere Pattern (2) so müssen wir als Char also $c0 + $02 = $c2 wählen.
Das Pattern für den zweiten Pixel wird mit der Anzahl der Abstufungen multipliziert (5) und ebenfalls aufaddiert. Der offensichtliche Nachteil ist, dass das jeweils zweite Pixel ebenso aus den bereits gewählten Farben bestehen muss und sich nur in der Abstufung unterscheiden kann. Zusätzlich ist die gewählte Vordergrundfarbe pro 4x4 Block auch für die jeweils darunterliegenden zwei Pixel zwingend vorgegeben. Lediglich die Wahl der Hintergrundfarbe bleibt hier noch frei (anderer Screen). Der Konverter hierzu ist aufgrund dieser Abhängigkeiten also relativ komplex. Die Darstellung hingegen ist relativ simpel, es müssen pro Frame lediglich beide Screens befüllt werden und die Farbdaten ins Farbram kopiert werden. Auch das Charset mit den entsprechenden Patterns ist schnell erzeugt. Es ist ausreichend alle 25 Permutationen zu erzeugen. Für jede Ebene mit verschiedener Hintergrundfarbe können diese Chars einfach kopiert werden. Um das Ergebnis noch zu verbessern kann man zwischen zwei Charsets interlacen.

pattern3pattern4
Verteilung der Farbpattern:
Pixel 1: Pattern 1 | Pixel 2: Pattern 3
Char = $c0 + $01 + $03 * $05 = $d0
Pixel 3: Pattern 0 | Pixel 4: Pattern 2
Char = $80 + $00 + $02 * $05 = $8a


Die obere Abbildung zeigt das Resultat für einen 4x4 Pixelblock in den zwei Interlacestufen. Vordergrundfarbe ist hellgrün ($0d), für die oberen zwei Pixel wurde Hintergrundfarbe 4 (weiss = $01) gewählt und für die unteren zwei Pixel Hintergrundfarbe 3 (hellgrau = $0f).
64hdd_ecm Und so sieht am Ende ein kompletter Frame aus. Die geringe Auflösung von 80x50 Pixeln ist deutlich zu erkennen, dafür sind eine Vielzahl an Farben möglich. Links tritt wegen des erwzungenen Screenwechsels zudem der sog. FLI-Bug auf, der aber von Sprites abgedeckt werden kann. Das grobpixelige Aussehen verschwimmt etwas durch die Bewegung der Bilder. Aufgrund der insgesamt 50 Rasterinterrupts geht einiges an Taktzyklen verloren. Dafür eignet sich der Rasterinterrupt als gute Synchronisation für die Soundausgabe (z.B. Ausgabe eines Samples pro Interrupt). Die erreichte Framerate beträgt dadurch nur noch etwa 8fps, pro Frame werden 3kb Daten übertragen. Es ergibt sich somit also ein Datendurchsatz von insgesamt 24kb/s.

Multicolor Zeichensatz

Würde man einen Frame als Bitmap übertragen, so werden 8kb/Frame benötigt, ein Anreiz zu Optimierungen. Man kann ein Multicolorbild weiter verkleinern, indem man es in einen Charset umwandelt. Der Vorteil, es müsste nur noch ein Charset mit 2kb Grösse und ein Screen mit 1kb Grösse übertragen werden. Das Problem hierbei ist jedoch, dass ein Charset nur maximal 256 verschiedene Chars abdecken kann, ein ganzes Bild aber im schlechtesten Fall aus bis zu 1000 verschiedenen Charblöcken bestehen kann. Man muss also das Bild soweit (verlustbehaftet) anpassen dass es aus 256 verschiedenen Blöcken zusammengesetzt werden kann. Viele Chars ähneln sich oder unterscheiden sich gar nur in einem Pixel, es fällt also kaum auf wenn solche Chars durch ähnliche ersetzt werden. Idealerweise kann man auf diese Weise auch ein Charset über mehrere Frames berechnen und damit auch mehrmals für mehrere aufeinanderfolgende Frams verwenden. Somit muss nur noch alle x Frames ein neues Charset übertragen werden. So sind noch höhere Frameraten möglich.
64hdd_orig Links zu sehen ist das Originalbild. Es wird zuerst in 7 Graustufen umgewandelt. Ingesamt können wir bei einem Multicolor Charset 4 feste Farben verwenden. Die Vordergrundfarbe kann jedoch nur aus den ersten 8 gewählt werden, günstigerweise legen wir sie deshalb auf schwarz ($00) und haben bei den übrigen 3 Farben freie Wahl aus den üblichen 16 Farben. Idealerweise verwendet man ein fleischfarbenes Schema für z.B. konvertierten Porno ($00, $09, $08, $0a) oder ein schwarz-weiss Schema ($00, $0b, $0d, $0f). Die restlichen 3 Farben aus den ingesamt 7 Graustufen verwenden wir zum dithern, sprich es wird abwechselnd ein Pixel der Farbe A und Farbe B gesetzt. Generell können auch noch weitere Ditheringmuster für weitere Farbabstufungen eingeführt werden.
64hdd_multicol Für dieses Bild wurden solange die am häufigsten vorkommenden Chars in das Charset aufgenommen bis keine freien Chars mehr zur Verfügung stehen. Blöcke für die kein Char mehr vorhanden war wurden anschliessend durch den nächstbesten ausgetauscht. Schön zu sehen sind einige blockige Artefakte am unterem Bogen der Brücke und dem Übergang zum Schwarz im unteren Bereich, die durch das Austauschen von Chars mit dem nächstbesten entstehen. Um nun das Charset soweit zu reduzieren dass es aus maximal 256 Chars besteht bedarf es also eines ausgefeilteren Algorithmus, da sonst die ersetzten Chars schnell ins Auge fallen. Im gezeigten Beispiel benachteiligt man Chars, die zwar nicht häufig vorkommen aber dennoch viele Details einbringen und vor allem nach dem Austausch deutliche Fehler (Blöcke, falsche Graustufen, ...) im Bild erzeugen. Deutlich bessere Resultate kann man, indem man immer das Originalcharset als ganzes betrachtet und es solange Char für Char reduziert bis noch 256 verbleiben. Das Verfahren dazu ist nicht ganz trivial: Zuerst wird zu jedem Char der ähnlichste herausgesucht und zusammen mit der Differenz in einem Array vermerkt. Ebenso wird eine Statistik darüber erstellt, wie oft ein Charblock insgesamt im Bild vorhanden ist, um später eine Gewichtung vornehmen zu können (diff * ammount). Nun wird aus allen Chars der Char mit der geringsten Differenz (gewichtet nach seinem Vorkommen) ausgewählt und durch seinen bereits bekannten Favouriten ersetzt. Anschliessend müssen alle Chars die den herausfallenden Char als Favouriten gewählt hatten einen neuen Favouriten wählen. Die gesamte Prozedur wird solange fortgesetzt bis noch 256 chars verbleiben. Hierzu ein wenig Beispielcode:

1  for(a=0;<lastchar;a++) find_favourite(a);
2  while(chars_left>256) {
3    found_char=find_closest_char();
4    ammount[favourite[found_char]]+=ammount[found_char];
5    ammount[found_char]=0;
6    chars_left--;
7    for(a=0;a<lastchar:a++) {
8      if(favourite[a]==found_char) find_favourite(a);
9    }
10 }

multicol_adv Somit kann man davon ausgehen, dass der Austausch von Chars den minimalsten Fehler in das Bild einbringt. Dies kann man auf dem mit dem erweitertem Algorithmus berechneten Bild (im Vergleich zum herkömmlich berechneten) deutlich sehen.

Petscii Modus

Eine weitere Möglichkeit, wenn auch eher als künstlerisch wertvoll zu betrachten, ist die Darstellung eines Videos als Petscii. Der grosse Vorteil: geringe Bandbreite und daher extrem flüssige Darstellung. Die Detailtiefe lässt jedoch schwer zu wünschen übrig. Dennoch ist das Resultat deutlich hochwertiger als eigentlich erwartet. In diesem Modus empfiehlt es sich die am häufigsten vorkommende Farbe in den Hintergrund zu legen, da nur im Kontrast zur Hintergrundfarbe Details darstellbar sind (lediglich 2 Farben pro Char, davon eine Hintergrundfarbe). Hierzu ist es am besten das Bild zuerst auf eine 4-Bit Bitmap herunterzurechnen. Auf dessen Basis kann man nun anhand einer Statistik die Hintergrundfarbe wählen. Um Rechenzeit beim Konvertieren zu sparen, kann man nun mittels dieser Bitmap weiter Verfahren und sie in einzelne Charblöcke zerteilen und für jeden Block die häufigste Farbe (exklusive der Hintergrundfarbe) als Vordergrundfarbe zuordnen. Die noch verbleibenden Farben werden entweder der Vorder oder Hintergrundfarbe zugeordnet. Anschliessend werden für den errechneten Block der passendste Petscii-Char ermittelt. Bei diesem Verfahren kann sich aufgrund von zweimaligem Herunterbrechen der Farben jedoch ein erhöhter Fehler einschleichen. Dies ist im ersten Bild auch deutlich an den Querstreben der Brücke zu erkennen. Besser ist es daher nach Ermittlung von Vorder- und Hintergrundfarbe, jeden Charblock erneut aus den Rohdaten des Bildes zu errechnen. Dadurch werden Details wie etwa die Brückenstreben wesentlich besser wiedergegeben, wie man im zweiten Bild erkennen kann.
64hdd_petsciipetscii_adv