Über unsMediaKontaktImpressum
Guido Oelmann 21. Juni 2016

Live-Video-Streaming auf Basis von Web-Standards

Seit einiger Zeit boomen Video-Streaming-Dienste, die Videos zum Abruf zur Verfügung stellen oder Videos live übertragen. Bekannte Vertreter sind beispielsweise YouTube, AmazonPrime oder NetFlix, die in erster Linie bereits vorhandene Videos abrufbar machen. Daneben ist die Übertragung von Live-Streams ein immer stärker werdender Trend. Also die Übertragung dessen, was im Moment des Zuschauens gerade live passiert. Plattformen hier sind zum Beispiel YouNow, UStream, Periscope und twitch.tv. Jeder kann zeigen, was er gerade macht und mit seinen Zuschauern gleichzeitig kommunizieren – egal wo, egal wann.

Neben diesen eher im Unterhaltungsbereich anzusiedelnden Anwendungen gibt es auch im Businessbereich Verwendung. Video-Konferenzsysteme sind bei vielen Firmen bereits an der Tagesordnung, aber auch im Automotivebereich oder bei der Übertragung von Firmenevents gewinnt Streaming an Bedeutung. Noch vor wenigen Jahren war die Umsetzung einer Live-Streaming-Plattform noch eine erhebliche Herausforderung. Es gab kaum unterstützte Standards was die Protokolle zur Übertragung angeht und der Zugriff auf die Kameras in den mobilen Geräten wie z. B. Smartphones war über die APIs häufig nur sehr eingeschränkt möglich.

Was ist das Ziel?

Im Folgenden wird ein Ansatz gezeigt, wie eine Live-Video-Streaming-Lösung auf Basis von Web-Standards gebaut werden kann. Dafür wird eine Architektur vorgestellt, die eine Bitraten-adaptive HTTP-basierte Streaming-Lösung auf Basis von Java, MPEG-DASH, dem Websocket-Protokoll und dem HTML5-Videoelement ermöglicht. Abb.1 zeigt, was gebaut werden soll und welche Fragen zur Umsetzung zu beantworten sind.

Wir haben unterschiedliche Sender eines Video-Streams. Das kann eine Webcam sein, ein Smartphone, ein Tablet oder irgendein anderes Gerät mit Kamera und Internetanschluss. Die Anforderung ist nun, dass der Video-Stream in dem Augenblick kontinuierlich an die Plattform übertragen wird, sobald der Sender beginnt, zu filmen (Punkt 1). Hier ergibt sich bereits die erste Frage: Wie soll der Stream verpackt und übertragen werden? Auf Seiten der Plattform (Punkt 2) ergibt sich als erstes die Frage: Wie soll der Stream entgegengenommen werden? Und falls der Stream zu einem späteren Zeitpunkt noch zur Verfügung gestellt werden soll, ist zu klären, wie der Stream als abrufbare Videodatei gespeichert werden kann. Im nächsten Schritt (Punkt 3) ist der Stream auf die Empfänger zu verteilen. Zu klären ist auch hier, wie soll der Stream verpackt und übertragen werden? Und wie erreicht man die Empfänger? Zu guter Letzt ist der Video-Stream beim Empfänger abzuspielen (Punkt 4). Hier ist die Frage zu beantworten, wie ein solcher Stream auf allen möglichen Empfängern (Smartphones, Tablets, Heimcomputer usw.) angezeigt werden kann.

Es ist also eine Menge an Fragen zu beantworten. Gleichzeitig lässt sich erahnen, dass live nicht wirklich live ist. Die Übertragung und die Verarbeitung des Streams kostet Zeit. Allerdings lässt sich dies in der Praxis auf wenige Sekunden reduzieren.

Die Lösung

Die Antwort auf alle Fragen liefert uns die Kombination verschiedener Technologien: MPEG-DASH, REST, die mit Java EE 7 in die Spezifikation aufgenommenen WebSockets und das HTML5-Video-Element. Abb.2 zeigt die Lösungsidee.

Die Grundidee für das Senden des Video-Streams ist hierbei, dass, sobald gestreamt wird, der Stream päckchenweise an die Plattform gesendet wird – im Schaubild durch die grünen Päckchen symbolisiert. Konkret bedeutet das, dass der Sender z. B. alle 2 Sekunden ein Päckchen an die Plattform sendet, in welchem 2 Sekunden Filmaufnahme enthalten sind. Bei solchen speziellen Päckchen wird von "video chunks" gesprochen. Diese chunks werden per HTTP an die Plattform gesendet und dort über eine REST-Schnittstelle entgegengenommen. Zu dem chunk können natürlich noch weitere Meta-Informationen übertragen werden. Beispielsweise könnten im Fall eines Smartphones als Sender noch Längen- und Breitengrad übermittelt werden, so dass bei den Empfängern auf einer Weltkarte angezeigt werden kann, wo gerade jemand live streamt. Und es ließe sich sogar erkennen, wenn der Streamer sich bewegt. An dieser Stelle sei bereits angemerkt, dass REST an dieser Stelle nicht zwingend notwendig ist. Die chunks könnten natürlich auch anders entgegengenommen werden.

Auf der Plattform können die chunks sofort an die Empfänger verteilt oder zusätzlich noch gespeichert werden. Die Speicherung bietet den Vorteil, dass der Stream später auch noch als "on demand"-Video ausgeliefert werden könnte. Dadurch können Zuschauer das Video auch noch zu einem späteren Zeitpunkt anschauen. In diesem Fall würde man die Videochunks nach Beendigung des Streams idealerweise zu einer einzelnen Videodatei zusammenführen. Dies bringt unter anderem Vorteile bei der Skalierung und Auslieferung und beim Vor- und Zurückspielen innerhalb der Datei mit sich. Vor jeder Auslieferung des eigentlichen Videos könnte natürlich auch leicht zunächst ein Werbejingle gesendet werden. Von der Plattform aus werden die chunks dann über WebSocket-Kanäle an die Empfänger übertragen. Dort angekommen wird der Stream im Webbrowser des Empfängers direkt an das HTML5-Videoelement geleitet und dort zur Anzeige gebracht.

Die Technologien

An dieser Stelle soll ein näherer Blick auf die verwendeten Technologien geworfen werden. Eine detaillierte Behandlung der Technologien würde den Rahmen dieses Artikels sprengen, daher soll eine grobe Beschreibung ausreichend sein.

REST und WebSockets


Eine REST-Schnittstelle dient in dem vorgestellten Ansatz der Entgegennahme der Videochunks und ggf. der Meta-Informationen. Mit REST kann eine Ressource über eine eindeutige URI auf einen Webserver verfügbar gemacht werden. In unserem Fall postet der Sender die chunks per HTTP direkt an die Ressource (s.Abb.3).

Für die Verteilung der chunks werden WebSockets verwendet. Mit WebSockets lässt sich eine bidirektionale Dauerverbindung zwischen der Plattform und dem jeweiligen Empfänger aufbauen. Bidirektional heißt, dass in beide Richtungen Daten gesendet werden können. Auf diesem Wege bekommt man indirekt eine Chatfunktionalität quasi mitgeliefert, weil auch der Empfänger etwas in Richtung Plattform schicken kann, die diese Nachricht dann wieder auf alle verteilt. Eine Dauerverbindung bedeutet, dass der Kanal permanent geöffnet bleibt. Dadurch kann die Plattform, sobald ein neues chunk angekommen ist, dieses von sich aus direkt an die Empfänger senden.

Eine WebSocket-Verbindung wird über HTTP aufgebaut und dann gehalten. Dadurch verringert sich der Daten-Overhead in erheblichem Maße, weil nicht bei jedem Request alle Header-Informationen mitgesendet werden müssen.

Als Gesamtergebnis ergibt sich eine Kommunikationskette, die vom Sender bis hin zum Empfänger auf HTTP basiert. Es werden also keine ominösen Protokolle benötigt und auch Firewalls sollten keine großen Schwierigkeiten machen.

MPEG-DASH

DASH steht für Dynamic Adaptive Streaming over HTTP und liefert die Lösung auf die Fragen, wie die chunks verpackt und angezeigt werden können. An dieser Stelle wird lediglich das Grundprinzip von MPEG-DASH vorgestellt. Abb.5 zeigt die grundlegende Idee hinter DASH.

Der Ausgang ist ein HTTP-Server von welchem eine Videodatei zu einem Empfänger gestreamt werden soll. Diese Videodatei liegt allerdings nicht nur einmal, sondern in drei Qualitätsstufen vor (in unterschiedlichen Bitraten kodiert). Die Datei low.mp4 repräsentiert das Video in einer nicht so guten Qualität, die Datei medium.mp4 in einer besseren und die Datei best.mp4 in einer sehr guten Qualität. Je höher die Qualität, desto größer die Datei und desto mehr Daten müssen zum Empfänger übertragen werden.

Das besondere ist, dass alle drei Videodateien intern nach einem bestimmten Prinzip segmentiert, also unterteilt sind. Dieses Video wird nun über das Internet zum Empfänger gestreamt. Währenddessen ändert sich die Bandbreite immer wieder, d. h. mal können Daten schneller, mal langsamer übertragen werden. Bei dem in Abb.5 dargestellten Beispiel ist die Bandbreite am Anfang der Übertragung zunächst schlecht, wird dann stetig besser und fällt schließlich wieder ab.

Das MPEG DASH-Verfahren sieht nun vor, dass der Empfänger das Video in Abhängigkeit der Bandbreite angezeigt bekommt. Es wird also immer ein anderes Segment aus den Ursprungsdateien genommen. Am Anfang ist die Bandbreite schlecht und es wird das erste Segment aus der Datei low.mp4 genommen. Dieses Segment hat die geringste Größe im Vergleich zu den anderen Segmenten der anderen Dateien. Sobald die Bandbreite besser wird, werden die dann folgenden Segmente aus den Dateien mit der besseren Qualität genommen. Die Bandbreite reicht für eine schnelle Übertragung in diesen Fällen aus. Das Ziel ist, dass der Empfänger auch bei sich ändernder Bandbreite ein durchgehendes Video ohne Aussetzer abspielen kann. Genau dieses Prinzip wird als "Dynamic Adaptive Streaming" bezeichnet. Und diese Segmente sind dann genau die chunks, die an die hier vorgestellte Plattform gesendet werden.

Von Microsoft und Apple gibt es ähnliche Verfahren, die aber natürlich nicht frei zur Verfügung stehen und beim kommerziellen Einsatz zu nicht unerheblichen Lizenzkosten führen würden. Dies war auch eine der ausgehenden Motivationen für MPEG-DASH, welches völlig frei verwendet werden darf. DASH bietet aber noch erheblich mehr, als hier vorgestellt, so dass sich ein Blick in die Spezifikation wirklich lohnt.

Die Anzeige eines MPEG-DASH-Video-Streams ist besonders einfach, weil das HTML5-Videoelement dies direkt unterstützt.

HTML5-Videoelement

Zur Anzeige des Video-Streams wird das HTML5-Video-Element in Kombination mit den sogenannten "Media Source Extensions" verwendet. Letztere werden mittlerweile in allen modernen Browsern unterstützt.

Die Media Source Extensions (Medienquellenerweiterung) erweitern das Video-Element ohne dass zusätzliche Browser-Plug-ins nötig wären. Dadurch können Medien-Streams direkt im Video-Element abgespielt werden. Funktionen wie Adaptives und Live-Streaming sind dann leicht möglich. Große Plattformen wie YouTube unterstützen dies bereits und in Zukunft wird dies wohl auch den Flash-Player ersetzen.

Die Erklärungen zum Lösungsansatz, der Verwendung und dem Zusammenspiel der verwendeten Technologien sollen damit ausreichen und ein Blick in die konkrete Implementierung geworfen werden.

Die Implementierung

Gegenstand dieses Artikels ist der Bau einer Live-Video-Streaming-Plattform, daher wird im Folgenden auf die Implementierungen bzgl. der Plattform und der Anzeige der Streams eingegangen. Nicht behandelt wird der Teil des Senders. Auf Apple iPhones und Android-Systemen ab Version 4.0 ist das Abgreifen des Video-Streams von der Kamera allerdings kein Problem mehr und wurde in diesem Zusammenhang auch schon erfolgreich getestet. Zum schnellen Ausprobieren kann auch ein normales Video genommen werden, welches dann allerdings noch MPEG-DASH tauglich gemacht werden muss, d. h. der richtige Video-Container, die Segmentierung, Splittung usw. müssen vorgenommen werden. Das Video sollte dann in einzelne Dateien gesplittet vorliegen und diese können dann hintereinander an die REST-Schnittstelle gesendet werden. Hilfreiche Tools zur Erzeugung von MPEG-DASH-Content sind MP4Box und X264.

Zunächst soll das Empfangen der Videochunks über die REST-Schnittstelle betrachtet werden.

Stream empfangen

Listing 1

@Path("/video")
public class VideoResource {

...

@Inject
@Chunk
private Event<ByteBuffer> chunkEvent;

@POST
@Consumes(MediaType.APPLICATION_OCTET_STREAM)
public Response postChunk(InputStream chunk) {
try {

byte[] bytes = ByteStreams.toByteArray(chunk);
ByteBuffer buf = ByteBuffer.wrap(bytes);

Files.copy(chunk, Paths.get(PATH_STORE_VIDEOS + filename);

chunkEvent.fire(buf);

} catch (Exception e) {
log.error(e.getMessage());
}
return Response.status(Response.Status.CREATED).build();
}
}

Listing 1 zeigt, wie so eine Schnittstelle zur Entgegennahme der video chunks und der Meta-Informationen gebaut werden kann.

@Path("/video")
public class VideoResource {

Die Klasse VideoResource wird mit Path annotiert, um eine REST-Ressource zu erzeugen. Die URI /video wird festgelegt, auf welche dann der POST-Request abgesetzt wird.

@Inject
@Chunk
private Event<ByteBuffer> chunkEvent;

Im Beispiel wird ein CDI-Event definiert, welches gefeuert wird, wenn ein video chunk reinkommt. Die @Chunk-Annotation ist nur ein einfacher Qualifier.

@POST
@Consumes(MediaType.APPLICATION_OCTET_STREAM)
public Response postChunk(InputStream chunk) {

Die mit @Post annotierte Methode ist für die Entgegennahme der chunks zuständig. Mit @Consumes wird der Mime-Type der Daten angegeben, die entgegengenommen werden.
Konsumiert werden Inhalte vom Medientyp APPLICATION_OCTET_STREAM, was so viel heißt wie, ich nehme alles entgegen. Hier wird den Datenstrom als InputStream entgegengenommen.

An dieser Stelle könnten auch noch zusätzliche Meta-Informationen aufgenommen werden, wie z. B. der Standort des Senders. Möglich wäre z. B. die Übertragung eines JSON-String wie in Abb.6 zu sehen.

byte[] bytes = ByteStreams.toByteArray(chunk);
ByteBuffer buf = ByteBuffer.wrap(bytes);

Zunächst wird der Input-Stream in ein ByteArray umgewandelt. Der Einfachhalt halber wurde im Programmcode das Byte-Streams-Objekt von der Apache commons-Bibliothek verwendet. Danach wird das byte array in einen ByteBuffer gewrappt. Der Grund ist unter anderem der, dass dieses sehr bequem über den WebSocket-Kanal geschickt werden kann.

Files.copy(chunk, Paths.get(PATH_STORE_VIDEOS + filename));

Um den chunk für eine spätere Bereitstellung noch zu sichern, geschieht dies an dieser Stelle. Hier werden alle Bytes des Input-Streams in eine Datei kopiert.

chunkEvent.fire(buf);

Zu guter Letzt wird das Event gefeuert, um darüber zu informieren, dass ein neuer chunk angekommen ist.

Stream empfangen

In Listing 2 wird die Implementierung des WebSocket-Endpunkts gezeigt, der die chunks an die Empfänger verteilt und die Chatnachriochten der Sender entgegennimmt und ebenfalls verteilt.

Listing 2

@ServerEndpoint(value = "/stream", encoders = { MessageEncoder.class }, decoders = { MessageDecoder.class })
public class StreamingEndpoint {

private static final Set<Session> sessions = Collections.synchronizedSet(new HashSet<Session>());

@OnOpen
public void onOpen(Session session) {
sessions.add(session);
}

public void onChunk(@Observes @Chunk ByteBuffer buffer) {
for (Session session : sessions) {
try {
session.getBasicRemote().sendBinary(buffer);
} catch (IOException ex) {
ex.printStackTrace();
}
}
}

@OnMessage
public void onMessage(Message message, Session session) {
for (Session s : sessions) {
try {
s.getBasicRemote().sendObject(message);
} catch (IOException | EncodeException ex) {
ex.printStackTrace();
}
}
}
}

Die WebSockets dienen zum verteilen der chunks und zur Übermittlung der Chatnachrichten.

@ServerEndpoint(value = "/stream", encoders = { MessageEncoder.class }, decoders = { MessageDecoder.class })
public class StreamingEndpoint {

Hier wird der WebSocket-Endpoint definiert, mit dem sich ein Empfänger verbinden kann.
MessageEncoder und MessageDecoder dienen einfach der Umwandlung von Chatnachrichten in JSON-Objekte.

private static final Set<Session> sessions = Collections.synchronizedSet(new HashSet<Session>());

In diesem Set werden alle Sessions der Empfänger gehalten. Wenn eine Methode als synchronized deklariert ist, dann kann kein Thread diese Methode aufrufen, solange noch ein anderer Thread diese Methode gerade ausführt.

@OnOpen
public void onOpen(Session session) {
    sessions.add(session);
}

Die mit @OnOpen annotierte Methode wird aufgerufen, sobald sich ein Client verbindet. Das heißt, sobald sich ein neuer Empfänger mit dem WebSocket verbindet, wird die Session des Empfängers gemerkt, um diesen später direkt kontaktieren zu können.

 public void onChunk(@Observes @Chunk ByteBuffer buffer) {
// send chunk to all
for (Session session : sessions) {
    try {
        session.getBasicRemote().sendBinary(buffer);
    } catch (IOException ex) {
        ex.printStackTrace();
    }
}

Dies ist die eigentlich interessante Stelle. Im Grunde wird hier ein WebSocket-Endpoint zu einem Observer für CDI-Events gemacht. Wenn ein chunk die Plattform erreicht hat, wird das CDI-Event geworfen. Diese Methode fängt das Event und bekommt den chunk geliefert. Alle WebSocket-Sessions werden durchlaufen, die Referenz auf den WebSocket-Endpoint des Benutzers wird geholt und an diesen der chunk bzw. die binären Daten gesendet. Der ByteBuffer kann einfach der Methode sendBinary übergeben werden.

@OnMessage
public void onMessage(Message message, Session session) {
    for (Session s : sessions) {
        try {
            s.getBasicRemote().sendObject(message);
        } catch (IOException | EncodeException ex) {
            ex.printStackTrace();
        }
    }
}

Die mit @onMessage annotierte Methode wird beim Empfang einer Nachricht eines Clients aufgerufen und verteilt diese Nachricht an alle Empfänger. Damit ist die Chatfunktionalität bereits umgesetzt.

Im Folgenden werden die nötigen Implementierungen auf Seiten des Empfängers gezeigt.

Video-Stream anzeigen

Benötigt wird eine HTML-Seite mit dem HTML5-Video-Element und etwas JavaScript-Code. Listing 3 zeigt den JavaScript-Teil der zum Weiterleiten des Streams in das Video-Element nötig ist.

Listing 3

<video id="video" autoplay="true"></video>

var video = document.querySelector('video');

var queue = [];
var buffer;

var mediaSource = new MediaSource();
mediaSource.addEventListener('sourceopen', function(e) {
video.play();

buffer = mediaSource.addSourceBuffer('video/mp4;codecs="avc1.64001f,mp4a.40.2"');
buffer.addEventListener('update', function() {
if (queue.length > 0 && !buffer.updating) {
buffer.appendBuffer(queue.shift());
}
});
}, false);

video.src = window.URL.createObjectURL(mediaSource);

Zu sehen ist das HTML-Element mit der id video, um vom Scriptcode aus auf dieses zugreifen zu können.

var video = document.querySelector('video');

Hiermit wird eine Referenz auf das Videotag geholt.

var queue[];
var buffer;

In der queue werden die chunks gespeichert, die nach und nach reintrudeln.
Die Variable buffer ist der sogenannte SourceBuffer des MediaSource-Objekts. Darin sind die Mediendaten enthalten, die das MediaSource-Objekt zum Abspielen als nächstes nimmt.

var mediaSource = new MediaSource();
 mediaSource.addEventListener('sourceopen', function(e) {
                    video.play();
                    buffer = mediaSource.addSourceBuffer('video/mp4;codecs="avc1.64001f,mp4a.40.2"');                }, false);
 video.src = window.URL.createObjectURL(mediaSource);

Es wird ein MediaSource-Objekt erzeugt, welches die Mediendaten enthält. Danach wird ein Eventlistener hinzugefügt. Sobald event sourceopen, also der allererste chunk ankommt, wird das Video-Tag in den Play-Modus versetzt. Das sorgt dafür, dass der Video-Stream sofort abgespielt wird, wenn die Übertragung beginnt. Die Methode addSourceBuffer erzeugt einen neuen SourceBuffer mit dem angegebenen MIME-type.

Dem SourceBuffer, also dem Puffer des MediaSource-Objekts, der die Mediendaten enthält, wird noch das video format und der codec mitgegeben. Hier gibt es noch Probleme bei verschiedenen Browsern, was die Unterstützung der Codecs angeht. Der hier verwendete Codec wird im Firefox unterstützt. Bei anderen Browsern müssen je nach Version noch Anpassungen vorgenommen werden. Mit window.URL.createObjectURL() wird eine auf den Medien-Stream verweisende URL erzeugt. Und diese wird wiederum dem src-Attribut des <Video>-Tags zugewiesen.

Was jetzt noch fehlt ist die Entgegennahme der chunks und das Befüllen der queue mit diesen. Listing 4 zeigt den entsprechenden clientseitigen WebSocket-Endpoint zur Entgegennahme.

Listing 4

var webSocket = new WebSocket('ws://localhost:8080/streamup/stream');
webSocket.binaryType = 'arraybuffer';

webSocket.onmessage = function(event){
if (event.data instanceof ArrayBuffer) {
console.log('video chunk arrived');
try {
if (buffer.updating || queue.length > 0) {
queue.push(event.data);
} else {
buffer.appendBuffer(event.data);
}
} catch (e) {
console.log(e);
}
} else {
writeResponse(event.data);
}
};
var webSocket = new WebSocket('ws://localhost:8080/streamup/stream');
webSocket.binaryType = 'arraybuffer';

Es wird eine neue WebSocket-Instanz erzeugt. Mit binaryType wird der Typ der Binärdaten angegeben, die von der Verbindung übertragen werden. Hier werden ArrayBuffer-Objekte verwendet, also eben jene, die von der Plattform gesendet werden.

webSocket.onmessage = function(event){
    if (event.data instanceof ArrayBuffer) {
        try {
           if (buffer.updating || queue.length > 0) {
            queue.push(event.data);
           } else {
            buffer.appendBuffer(event.data);
            }
        }  catch (e) {
            console.log(e);
         }                               
}  else  {
    writeResponse(event.data);
}};

Wenn der Buffer gerade upgedated wird oder sich in der Warteschlange noch chunks befinden, dann werden neue chunks in die Warteschlange angefügt. Falls die Warteschlange leer ist und der Buffer aufnahmefähig, wird der chunk direkt in den buffer übertragen. Wie weiter oben schon gezeigt wurde, bedient sich das MediaSource-Objekt genau aus diesem Buffer für neues Videomaterial zur Anzeige im Videoelement. Der SourceBuffer lauscht auf ein update-event und sobald dieses ausgelöst wird, bekommt das Videoelement den nächsten Chunk aus der queue. Und damit sind die wesentlichen Codestellen zur Erstellung einer solchen Plattform bereits abgehandelt.

Fazit

Der auf dem vorgestellten Code basierende Prototyp ist in Abb.7 zu sehen. Die Abbildung zeigt eine Momentaufnahme des Prototypen mit zwei Empfängern, die sich im Chat austauschen und einem Video-Stream, der währenddessen von einem Sender übertragen wird. Bei dem zu sehenden Stream ist eine nach MPEG-DASH-Manier zerteilte Videodatei zum Tragen gekommen, die chunk für chunk übertragen wird. Bei dem verwendeten Browser handelt es sich um den Firefox 44.0.2. Serverseitig wurde das JDK 1.8.0_40 und der Wildfly 8.20 eingesetzt.

Gezeigt wurde, wie sich eine Video-Live-Streaming-Plattform mit einer Kombination von Web-Standards realisieren läßt. Und zwar mit MPEG-DASH, WebSocket und HTML5. Für die Entgegennahme des Streams wurde eine REST-Schnittstelle verwendet, was aber optional ist. In einer Produktivumgebung wäre auch lediglich die Übertragung von Meta-Informationen an die REST-Schnittstelle und die der chunks auf anderem Wege denkbar.

Aktuelle Probleme sind die unterschiedliche Unterstützung von Video-Codecs in den Browsern und die Anwendung der MediaSource-Extensions. Die entsprechende Spezifikation ist zum Zeitpunkt der Erstellung des Artikels noch nicht final und daher ist deren Unterstützung in den Browsern aktuell noch unterschiedlich weit fortgeschritten. Der vorgestellte Programmcode wurde auf allen Browsern erfolgreich getestet, allerdings waren hier und da kleinere Anpassungen notwendig. Zu erwarten ist aber, dass diese Kinderkrankheiten in naher Zukunft überwunden sein werden.

Für den produktiven Einsatz sind natürlich noch sehr viel mehr Fragen zu klären, als hier vorgestellt werden konnten. Insbesondere Fragen zu Performance und Caching-Strategien. Aber auch viele weitere Fragestellungen werden sich beim Bau einer professionellen Live-Streaming-Plattform ergeben. Was muss zum Beispiel passieren, wenn sich ein Streamer im Auto befindet und das Smartphone die Funkzellen wechselt? Oder die chunks kommen nicht in der richtigen Reihenfolge an?

Diese und viele Fragen mehr müssen für den professionellen Betrieb einer solchen Plattform beantwortet werden. Aber das sind ja die Dinge, die letztendlich am meisten Spaß machen...

Autor

Guido Oelmann

Guido Oelmann arbeitet als freiberuflicher Softwarearchitekt, Berater und Trainer. Zu seinen Schwerpunkten gehören neben agilen Entwicklungsmethoden und Softwarearchitekturen, der Einsatz von Java-/Java-EE-Technologien in...
>> Weiterlesen
botMessage_toctoc_comments_9210