Aller au contenu principal

Stream Camera

Stream Camera est prototype qui transmet un flux WebRTC dans un MediaServer pour le récupérer sur Unreal Engine ou sur un client Web.

Introduction

Le partage d'écran nous a montré qu'utiliser les composants de l'ordinateur pour afficher des choses sur Unreal Engine n'était pas simple et surtout pas intéressant sur le plan d'intégration. C'est ce pourquoi nous devons trouver une solution à commencer par partager un flux vidéo sur différentes plateformes. Cela répondra au problème d'intégration et de performance consommée.

De la même façon, cela pourra nous permettre de préparer une architecture intéressante par la suite.

[TODO => Image]

Etapes

Web et API

Pour répondre au besoin d'intégration du sujet, on a trouvé intéressant l'idée de faire un partage de caméra d'un client Web à un client Unreal Engine. L'inverse n'étant pas possible car il n'y aurait pas beaucoup d'intérêt à ce qu'une personne portant un casque VR partage sa caméra.

Pour ce faire, nous avons crée une petite interface Web classique en VueJS. Langage choisi par préférence personnelle et parce que cela permet de créer un site Web rapidement.

De façon à ce que le client web puisse communiquer avec d'autres clients, il est nécessaire de créer une interface en réseau, une API. Nous avons donc crée grâce à ExpressJS une petite API reliée au Client Web. Grâce à ce qu'on appelle des WebSockets nous pouvons aisément envoyer le flux WebRTC, l'API web qui permet d'interagir entre Web et PC, sur notre API personnalisée. Toutes les secondes, le flux enregistré est envoyé sur notre API créée.

Fichiers et streaming

Désormais que nous recevons le flux vidéo correctement traité, une question technique survient : comment transformer ce flux en quelque chose d'exploitable par d'autres clients ?

Cette question est celle qui nous a donné le plus de fil à retordre.

1 - Nous avons premièrement crée un fichier lorsque le stream était initialisé. Chaque seconde le fichier recevait les nouvelles informations écrasant les anciennes. Malheureusement, cela rendait le fichier corrompu, illisible et inexploitable. Un fichier se termine par une fin connue par la machine, une vidéo doit être finie, en écrasant sans cesse le fichier, déjà il y avait un problème d'actualisation, ensuite le fichier n'avait aucune fin pour l'ordinateur qui est incapable de traiter le fichier ainsi.

2 - Nous avons donc tenté une autre approche, au lieu d'écraser le fichier chaque seconde, de lui ajouter la nouvelle partie à la fin de la précédente, faisant un fichier correct qui aurait une vraie fin. Concrètement, cela fonctionne mais qu'avec certains types de vidéo et que dans des conditions techniques précises. Le fichier était parfois corrompu. Ce manque de stabilité a fait que nous ne pouvions pas nous contenter de cette technique. Et qui plus est de la taille du fichier, imaginons un appel qui dure une heure, ça ferait un fichier immense de plusieurs gigas probablement. Cette technique est tout sauf intéressante dans notre cas.

Suite à ces échecs, nous avons dû chercher d'autres solutions.

Media Servers

C'est là qu'intervient les Media Servers. Ce sont des serveurs média qui gèrent entièrement ce que nous avons besoin de faire. Après moult recherches, nous avons fini par comprendre leur concept et leur utilité, ils sont exactement ce que nous avons besoin pour diffuser un flux vidéo en streaming.

Tout d'abord, il en existe plein, nous avons dû en tester plusieurs. Voici une liste de ceux testés : OSSRS, MediaSoup, GStreamer et Icecast.

  • OSSRS a été abandonné car la documentation n'est pas très bien traduite, initialement ce projet est en chinois. Puis le projet est assez lourd à mettre en place, donc nous avons décidé de ne pas continuer là-dessus.

  • MediaSoup n'était pas assez modulaire pour ce que nous voulions faire, nous avons donc opté pour autre chose.

  • GStreamer est un serveur média assez bas niveau puisqu'il est en langage C, or nous, on veut quelque chose d'assez simple à intégrer avec une API en JavaScript.

Nous avons fini par choisir Icecast2 grâce à sa simplicité et ses configurations possibles.

Un serveur média est un serveur qui tourne et qui attend de recevoir un flux vidéo. Une fois le choix fait, notre prochain objectif était donc naturellement de lui envoyer un flux vidéo.

Grâce à un outil, traitement vidéo / audio, qui s'appelle Ffmpeg, on a commencé à chercher pour envoyer le flux sur un serveur Icecast2. En réalité, très peu de personnes ont fait ce que nous voulions faire, il fallait donc trouver une inspiration pour avancer vite. Voici le projet sur lequel nous nous sommes basés : https://gitlab.com/jamie/icecream/

Encodage et format

Après avoir suivi l'exemple et l'avoir arrangé comme nous voulions, nous nous sommes confrontés à un problème : le format vidéo. Unreal Engine a besoin d'un format assez strict pour pouvoir les lire. Avec la documentation technique, nous voulions faire du mp4 car c'est le format le plus souple sur Unreal Engine, mais le flux récupéré par le web ne prend pas en charge le mp4 sur certains navigateurs, il prend le flux et le transforme en Webm.

De ce fait, nous avons tenté de modifier le flux webm pour le convertir en flux MP4 mais la conversion d'une extension à une autre créait une latence trop importante et parfois une perte des données. Nous avons donc préféré conservé les codecs d'origine, audio et vidéo et l'extension en .webm.

Nous avons obtenu les premiers résultats satisfaisants.

On a crée un MediaPlayer sur Unreal Engine, celui-ci possède plusieurs encodeurs. L'un de ces encodeurs est un plugin VlcMedia que nous avons téléchargé à part, on pensait que Vlc prendrait plus facilement en charge le flux vidéo reçu.

Cependant, on s'est trompé puisqu'en fait l'encodeur qui fonctionne est WmfMedia qui est capable de lire le flux Webm.

Résultats

Assez globalement, nous sommes satisfaits des premiers résultats même si on peut noter plusieurs problèmes majeurs :

  • La latence est trop importante, il faut environ 10 secondes pour que le flux charge puis une fois chargé, il y a 10 secondes de latence entre la source et le destinataire.
  • Le flux vidéo ne charge pas toujours. En général un comportement répétitif a pu être observé, quand on charge le stream peu de temps après qu'il ait été lancé, il charge correctement. Néanmoins quand on charge le stream et que celui-ci est lancé depuis plusieurs minutes, alors le client n'arrivera pas à charger le stream.

Afin de vérifier si la latence provenait du Media Server ou du Client Unreal, nous avons ajouté sur l'interface Web une page qui permet de charger les streams. On a noté qu'il y avait toujours 5 secondes de latence, ce qui reste acceptable. Nous avons comme référence Microsoft Teams où la latence est plus ou moins équivalente.

[TODO => Image]

Améliorations possibles

Nous avons fini le principal de ce prototype. Il nous a demandé beaucoup de recherches et d'essais avant d'avoir une première démonstration utilisable. Néanmoins, il reste toujours des choses améliorables :

  • La latence est un énorme souci, il est probable que l'une partie de la latence provienne du traitement vidéo Ffmpeg et une autre du chargement d'Unreal Engine. Unreal Engine n'est pas très optimisé pour lire dynamiquement des flux vidéos et encore moins un flux en temps direct. L'une des solutions seraient d'imbriquer une bibliothèque externe à Unreal qui se chargerait entièrement de cela.

  • Gestion du multi streaming. Un des axes d'amélioration serait de supporter plusieurs streams à la fois, web et Unreal.

Documentation technique