Vers Unreal : HLS
Les deux autres types d'utilisateurs qui nous intéressent sont Unreal PC (Windows) et Unreal VR (Android).
Unreal propose un objet MediaPlayer, utilisé pour lire des flux et les diffuser dans l'environnement virtuel. Il en existe plusieurs, supportant chacun différents formats. Nous basant sur le travail réalisé pour les utilisateurs Web, nous souhaitons d'abord récupérer les flux qu'ils émettent.
Unreal PC
HTTP-FLV n'est pas utilisable avec Unreal. Pour lire les flux RTMP directement, nous importons et utilisons le plugin VlcMedia : il intègre à Unreal un lecteur VLC qui peut lire les flux RTMP. Le plugin fonctionne mais a des défauts visuels notables (e.g. écran gris/vert au démarrage). De plus, il n'est pas supporté sur Android, OS du casque VR qu'on utilise (Quest 2). Il nous a donc fallu trouver une alternative à RTMP+VlcMedia.
Unreal VR
Notre attention se porte sur le MediaPlayer Android qu'utilise le casque. Le seul format de streaming qu'il supporte est HLS (HTTP Live Streaming).
HLS une technologie adaptée pour diffuser un flux vidéo à la demande avec des propriétés variables (bitrate, codecs, résolution). Comme son nom l'indique, elle peut être utilisée pour du live streaming, mais elle nécessite beaucoup d'opérations intermédiaires qui impliquent une latence minimale non négligeable (les meilleures estimations donnent minimum 15 secondes). De plus, minimiser sa latence se fait au coup de la stabilité ou de la qualité. Nous essayons cette solution en favorisant latence et stabilité.
En configurant Ffmpeg, Node-Media-Server converti le flux Rtmp en fichiers Hls (.m3u8, .ts) auquels les utilisateurs peuvent accéder.
Configuration de Ffmpeg via Node-Media-Server pour la diffusion HLS :
//config rtmp & http
// ...
trans: {
ffmpeg: FfmpegPath,
tasks: [
{
app: 'live',
hls: true,
hlsFlags: '['+
'hls_init_time=2'+
':hls_time=2'+
':hls_list_size=50'+
':hls_flags=independent_segments+delete_segments+program_date_time'+
':hls_segment_type=mpegts'+
']',
hlsKeep: false,
vc: "libx264",
vcParam: ['-b', '2000k'],
ac: "aac",
acParam: ['-ab', '128k', '-ac', '1', '-ar', '44100'],
}
]
},
note
L'option hls_segment_type a pour valeur par défaut mepgts (segments en .ts). A noter que l'autre type, fmp4 (en .m4v) n'est pas supporté par Android Media Player.
Android Media Player parvient à lire correctement le flux HLS. Ils sont lus avec un décalage de 15s et sans défaut apparent. Nous n'avons pas de levier pour améliorer cette latence. Elle est bien au dessus de 5 secondes, à partir desquelles on ne peut plus vraiment parler d'interaction en temps réel.
Améliorer la lecture PC
Etant donné la qualité des flux obtenus en HLS et les défauts visuels du lecteur VLC, nous nous intéressons à l'utilisation de HLS sur Unreal PC.
Aucun lecteur par défaut ne prend en charge HLS. Nous utilisons Electra Media Player (issu d'un plugin officiel). Si ce lecteur lit les flux HLS, il nécessite pour ce faire que ceux-ci soient configurés d'une manière spécifique (voir la documentation en lien plus haut).
Malheureusement, Node-Media-Server abstrait une bonne partie de la configuration d'Ffmpeg et nous sommes très limités dans la configuration de HLS.
Avec la configuration suivante, nous parvenons à produire un flux hls lisible par le lecteur Electra (avec une latence de 15s comme attendu).
hlsFlags: [hls_init_time=2:hls_time=2:hls_list_size=50:hls_flags=independent_segments+delete_segments+program_date_time:hls_segment_type=fmp4:var_stream_map=\\'a:0,agroup:audio v:0,agroup:audio\\':master_pl_name=master.m3u8]
Equivalent, une fois traité par NodeMediaServer, à :
ffmpeg -y -i <inPath> -c:v libx264 -b 2000k -c:a aac -ab 128k -ac 1 -ar 44100 -f tee -map 0:a? -map 0:v? [hls_init_time=2:hls_time=2:hls_list_size=50:hls_flags=independent_segments+delete_segments+program_date_time:hls_segment_type=fmp4:var_stream_map=\\'a:0,agroup:audio v:0,agroup:audio\\':master_pl_name=master.m3u8].media/live/<streamName>/index.m3u8
Ou, pour l'écriture commande plus habituelle :
ffmpeg -y -i <inPath> -c:v libx264 -b 2000k -c:a aac -ab 128k -ac 1 -ar 44100 -f tee -map 0:a? -map 0:v? -hls_init_time 2 -hls_time 2 -hls_list_size-50 -hls_flags independent_segments+delete_segments+program_date_time -hls_segment_type fmp4 -var_stream_map '"a:0,agroup:audio v:0,agroup:audio"' -master_pl_name master.m3u8 .media/live/<streamName>/index.m3u8
Attention
Attention aux caractères qui sont interprétés par les différents processus qui traitent ces commandes et ces chaînes.
- Les " " ne sont pas nécessaire pour Ffmpeg, mais servent de quote à l'interpréteur de commande.
- Les : sont utilisés pour séparer les arguments dans la forme entre crochet, il faut quote les expressions qui en utilisent.
- Les ' ' servent de quote pour Ffmpeg, il n'interprète pas les : contenus.
note
Le nom du stream doit comporter %v (ou %25v si interprété, comme pour OBS) pour que l'opération fonctionne. C'est dû à la séparation des données en plusieurs variantes (audio et vidéo) avec var_stream_map.
Cependant, la rigidité imposée par NodeMediaServer empêche de publier plusieurs flux HLS en même temps. En effet, nous n'avons pas le contrôle sur la disposition des fichiers générés, et master.m3u8 (fichier à lire) ne peut pas être distingué pour chaque stream.
Améliorer la latence Hls
Il existe une version plus rapide d'HLS : Low Latency HLS (LHLS). Elle permet d'atteindre une latence inférieure à 5 secondes. Cela pourrait être une piste intéressante à explorer, il faut notamment savoir :
- si nous pouvons configurer Ffmpeg (au travers de Node-Media-Server ou séparément si nécessaire) pour qu'il produise un flux LHLS ;
- si les lecteurs d'Unreal prennent LHLS en charge.
Résultats : Réception
Nous n'avons abordé que la réception depuis Unreal. Pour la VR, HLS nous assure une latence de 15 secondes et une qualité régulière de vidéo. Pour la version PC, on note seulement 6 secondes de latence pour les flux simples (e.g. WebCam) et quelques défauts visuels en début de lecture. Mais la lecture du flux prétraité n'est pas fiable (e.g. BackgroundRemoval), avec de fréquents arrêts sur image et des défauts visuels exacerbés. Nous supposons que cet écart est dû à la lecture directe du flux RTMP, qui implique d'être beaucoup plus susceptible aux écarts d'un paquet de donnée à l'autre.
Suite : Emission
Maintenant en mesure de recevoir les flux sur Unreal, il faut pouvoir en émettre en retour. Nous nous intéressons surtout à la capture et l'émission de l'audio du micro de l'utilisateur. Savoir émettre du contenu vidéo depuis Unreal peut être intéressant mais n'a pas vraiment d'application dans notre cas.