Faire du streaming live avec une Raspberry Pi et les ressources de l'ENS-IFÉ

Motivations

Les raspberries Pi sont proposées avec une caméra vidéo connectable avec une nappe qui évite d'occuper un connecteur USB. cette caméra est de bonne qualité et de très petite taille ce qui peut permettre de construire des outils d'exploration très maniables et très performants ou de fabriquer des dispotifs de veille discrets et peu encombrants.

Il existe une bibliothèque dédiée à la caméra de la raspberry qui a été installée dans les images fournies par l'IFÉ-ENS de Lyon mais il est aussi possible d'utiliser des outils linux génériques qui permettent d'acquérir un savoir faire réutilisable dans toutes les conditions ou d'insérer la raspberry et sa caméra dans un univers linux (ou autre) préexistant en utilisant des solutions standard. Nous n'aborderons pas ici les "chaînes toutes prêtes" pour aborder plutôt la compréhension du fonctionnement d'un service de streaming vidéo live.

Remerciements

Charles-Henri Eyraud.


Sommaire

  1. Principes de la vidéodiffusion

  2. Configuration de l'encodeur

    1. Informations sur le matériel utilisé
    2. Configuration de la caméra
    3. Les commandes d'acquisition et d'encodage raspivid et ffmpeg
  3. Configuration du serveur et format du flux transporté
    1. Le serveur web nginx rtmp
      1. Configuration d'un serveur hls avec ffmpeg
    2. Le serveur web nginx HLS
    3. Le serveur web nginx DASH
    4. Raspberry Pi comme encodeur et serveur
  4. Configuration du player dans une page web

Téléchargements

  1. image proposée par le projet Tremplin de l'ENS-IFÉ
  2. g_vidal rpi23-gen-image
  3. Images raspberryPi du projet Tremplin

Résumé

Si vous êtes pressé et souhaitez immédiatement faire de la vidéodiffusion sans forcément en connaître les tenants et les aboutissants voici la procédure :

* Installer les paquets ǹginx` `ffmpeg`, `v4l2`, `nginx-extras`, `libnginx-mod-rtmp`.
    1. `sudo apt install nginx-extras ffmpeg libnginx-mod-rtmp v4l-utils qv4l2`
* ajouter à la fin du  fichier `/etc/nginx/nginx.conf` de votre raspberry  le contenu du bloc ci-dessous
rtmp {
    server {
        listen 1935;
        chunk_size 4096;

        application live {
             live on;
            record off;
            allow publish all;
            allow play all;
            }

        application hls {
            live on;
            hls on;
            hls_cleanup on;
            hls_sync 100ms;
            hls_fragment 2s;
            hls_path /tmp/hls;
            allow play all;
        }

        application dash {
            live on;
            dash on;
            dash_path /tmp/dash;
            allow play all;
        }
    }
}       
* relancer nginx `sudo systemctl restart nginx`
* lancer l'encodage `ffmpeg -f v4l2 -r 25 -pix_fmt h264 -video_size 640x360 -i /dev/video0 -c:v libx264  -an -b:v 1600k -preset veryfast  -f flv -r 25  rtmp://127.0.0.1:1935/live`
* se connecter sur l'adresse de la raspberry, sur le port 1935 sur le répertoire live

1. Principes de la vidéodiffusion

Un système de vidéodiffusion (live streaming) est composé de 3 segments :

  encodage  -> serveur vidéo -> décodeur/visualiseur 

Les deux premiers segments peuvent être assumés par la même machine mais dans ce cas la fonction serveur est limitée et on privilégie cet usage plutôt dans une logique point à point plutôt que pour un service de vidéodiffusion. Que l'on utilise une seule machine ou une série de machines dédiées plusieurs problèmes se posent :

* Compatibilité des formats entre segments
* choix des protocoles de transfert
* configuration de chacun des segments

Il n'existe pas de solution unique générale, il est nécessaire d'établir le besoin, de comprendre les contraintes que cela entraine et de faire des choix en conséquence.

Aujourd'hui la vidéodiffusion en direct se fait selon deux protocoles principaux, soit une famille de protocoles dédiés : rtp://, rtmp://, rtsp:// ; soit plus "simplement" en http. Il existe bien sûr aussi un certain nombre de protocoles de communication

2. Configuration de l'encodeur

la configuration de l'encodeur repose sur une bonne connaissance du matériel utilisé. L'exemple ci-dessous utilise la raspicam mais il fonctionne avec toute caméra connectée à la raspberry et reconnue par le système

2.1. Informations sur le matériel utilisé

S'ils ne sont pas déjà installés sur le système ajouter les outils video for linux (v4l v4l2), pour cela installer les paquets suivants :

sudo apt install v4l-utils qv4l2 

Une fois ces outils installés il est possible d'obtenir de nombreuses informations sur l'équipement que l'on utilise, le bloc ci-dessous propose une liste non exhaustive de commandes utiles. les commandes v4l2-ctl permettent de contrôler les paramètres d'acquisition de la caméra, les commandes ffmpeg permettent d'obtenir les informations sur les capacités de l'encodeur.

# liste des caméras disponibles 

ens-ife[~] raspi9-€ :  v4l2-ctl --list-devices
bcm2835-codec-decode (platform:bcm2835-codec):
    /dev/video10
    /dev/video11
    /dev/video12

mmal service 16.1 (platform:bcm2835-v4l2):
    /dev/video0

# liste de l'état des paramètres de la caméra
ens-ife[~] raspi9-€ :  v4l2-ctl --log-status

Status Log:

   [10566.765068] bcm2835-v4l2: =================  START STATUS  =================
   [10566.765082] bcm2835-v4l2: Saturation: 0
   [10566.765093] bcm2835-v4l2: Sharpness: 0
   [10566.765102] bcm2835-v4l2: Contrast: 0
   [10566.765112] bcm2835-v4l2: Brightness: 50
   [10566.765121] bcm2835-v4l2: ISO Sensitivity: 0
   [10566.765131] bcm2835-v4l2: ISO Sensitivity, Auto: Auto
   [10566.765141] bcm2835-v4l2: Image Stabilization: false
   [10566.765150] bcm2835-v4l2: Auto Exposure: Auto Mode
   [10566.765160] bcm2835-v4l2: Exposure Time, Absolute: 1000
   [10566.765169] bcm2835-v4l2: Auto Exposure, Bias: 0
   [10566.765179] bcm2835-v4l2: Exposure, Dynamic Framerate: false
   [10566.765188] bcm2835-v4l2: Exposure, Metering Mode: Average
   [10566.765198] bcm2835-v4l2: White Balance, Auto & Preset: Auto
   [10566.765207] bcm2835-v4l2: Red Balance: 1000
   [10566.765216] bcm2835-v4l2: Blue Balance: 1000
   [10566.765226] bcm2835-v4l2: Color Effects: None
   [10566.765235] bcm2835-v4l2: Color Effects, CbCr: 32896
   [10566.765244] bcm2835-v4l2: Rotate: 0
   [10566.765254] bcm2835-v4l2: Horizontal Flip: false
   [10566.765263] bcm2835-v4l2: Vertical Flip: false
   [10566.765272] bcm2835-v4l2: Video Bitrate Mode: Variable Bitrate
   [10566.765282] bcm2835-v4l2: Video Bitrate: 10000000
   [10566.765291] bcm2835-v4l2: Compression Quality: 30
   [10566.765301] bcm2835-v4l2: Power Line Frequency: 50 Hz
   [10566.765310] bcm2835-v4l2: Repeat Sequence Header: false
   [10566.765319] bcm2835-v4l2: H264 Profile: High
   [10566.765328] bcm2835-v4l2: H264 Level: 4
   [10566.765337] bcm2835-v4l2: Scene Mode: None
   [10566.765347] bcm2835-v4l2: H264 I-Frame Period: 60
   [10566.765356] bcm2835-v4l2: ==================  END STATUS  ==================


# liste des formats disponibles en utilisant ffmpeg
ens-ife[~] raspi9-€ :  ffmpeg -f v4l2 -list_formats all -i /dev/video0
.../...
[video4linux2,v4l2 @ 0x12121c0] Raw       :     yuv420p :     Planar YUV 4:2:0 : {32-3280, 2}x{32-2464, 2}
[video4linux2,v4l2 @ 0x12121c0] Raw       :     yuyv422 :           YUYV 4:2:2 : {32-3280, 2}x{32-2464, 2}
[video4linux2,v4l2 @ 0x12121c0] Raw       :       rgb24 :     24-bit RGB 8-8-8 : {32-3280, 2}x{32-2464, 2}
[video4linux2,v4l2 @ 0x12121c0] Compressed:       mjpeg :            JFIF JPEG : {32-3280, 2}x{32-2464, 2}
[video4linux2,v4l2 @ 0x12121c0] Compressed:        h264 :                H.264 : {32-3280, 2}x{32-2464, 2}
[video4linux2,v4l2 @ 0x12121c0] Compressed:       mjpeg :          Motion-JPEG : {32-3280, 2}x{32-2464, 2}
[video4linux2,v4l2 @ 0x12121c0] Raw       : Unsupported :           YVYU 4:2:2 : {32-3280, 2}x{32-2464, 2}
[video4linux2,v4l2 @ 0x12121c0] Raw       : Unsupported :           VYUY 4:2:2 : {32-3280, 2}x{32-2464, 2}
[video4linux2,v4l2 @ 0x12121c0] Raw       :     uyvy422 :           UYVY 4:2:2 : {32-3280, 2}x{32-2464, 2}
[video4linux2,v4l2 @ 0x12121c0] Raw       :        nv12 :         Y/CbCr 4:2:0 : {32-3280, 2}x{32-2464, 2}
[video4linux2,v4l2 @ 0x12121c0] Raw       :       bgr24 :     24-bit BGR 8-8-8 : {32-3280, 2}x{32-2464, 2}
[video4linux2,v4l2 @ 0x12121c0] Raw       :     yuv420p :     Planar YVU 4:2:0 : {32-3280, 2}x{32-2464, 2}
[video4linux2,v4l2 @ 0x12121c0] Raw       : Unsupported :         Y/CrCb 4:2:0 : {32-3280, 2}x{32-2464, 2}
[video4linux2,v4l2 @ 0x12121c0] Raw       :        bgr0 : 32-bit BGRA/X 8-8-8-8 : {32-3280, 2}x{32-2464, 2}

2.2. Configuration de la caméra

Cette configuration s'effectue avec l'outil v4l2-ctl plusieursoptions de la commande permettent de choisir la stratégie d'acquisition ; le nombre de paramètres et de menus est important la commande v4l2-ctl --log-status vue ci-dessus fournit l'état des réglages, la commande v4l2-ctl -L fournit le détail des options et des menus la commande ci-dessous permet d'avoir une version simplifiée.

ens-ife[~] raspi9-€ :  v4l2-ctl -l

User Controls

                     brightness 0x00980900 (int)    : min=0 max=100 step=1 default=50 value=50 flags=slider
                       contrast 0x00980901 (int)    : min=-100 max=100 step=1 default=0 value=0 flags=slider
                     saturation 0x00980902 (int)    : min=-100 max=100 step=1 default=0 value=0 flags=slider
                    red_balance 0x0098090e (int)    : min=1 max=7999 step=1 default=1000 value=1000 flags=slider
                   blue_balance 0x0098090f (int)    : min=1 max=7999 step=1 default=1000 value=1000 flags=slider
                horizontal_flip 0x00980914 (bool)   : default=0 value=0
                  vertical_flip 0x00980915 (bool)   : default=0 value=0
           power_line_frequency 0x00980918 (menu)   : min=0 max=3 default=1 value=1
                      sharpness 0x0098091b (int)    : min=-100 max=100 step=1 default=0 value=0 flags=slider
                  color_effects 0x0098091f (menu)   : min=0 max=15 default=0 value=0
                         rotate 0x00980922 (int)    : min=0 max=360 step=90 default=0 value=0 flags=modify-layout
             color_effects_cbcr 0x0098092a (int)    : min=0 max=65535 step=1 default=32896 value=32896

Codec Controls

             video_bitrate_mode 0x009909ce (menu)   : min=0 max=1 default=0 value=0 flags=update
                  video_bitrate 0x009909cf (int)    : min=25000 max=25000000 step=25000 default=10000000 value=10000000
         repeat_sequence_header 0x009909e2 (bool)   : default=0 value=0
            h264_i_frame_period 0x00990a66 (int)    : min=0 max=2147483647 step=1 default=60 value=60
                     h264_level 0x00990a67 (menu)   : min=0 max=11 default=11 value=11
                   h264_profile 0x00990a6b (menu)   : min=0 max=4 default=4 value=4

Camera Controls

                  auto_exposure 0x009a0901 (menu)   : min=0 max=3 default=0 value=0
         exposure_time_absolute 0x009a0902 (int)    : min=1 max=10000 step=1 default=1000 value=1000
     exposure_dynamic_framerate 0x009a0903 (bool)   : default=0 value=0
             auto_exposure_bias 0x009a0913 (intmenu): min=0 max=24 default=12 value=12
      white_balance_auto_preset 0x009a0914 (menu)   : min=0 max=10 default=1 value=1
            image_stabilization 0x009a0916 (bool)   : default=0 value=0
                iso_sensitivity 0x009a0917 (intmenu): min=0 max=4 default=0 value=0
           iso_sensitivity_auto 0x009a0918 (menu)   : min=0 max=1 default=1 value=1
         exposure_metering_mode 0x009a0919 (menu)   : min=0 max=2 default=0 value=0
                     scene_mode 0x009a091a (menu)   : min=0 max=13 default=0 value=0

JPEG Compression Controls

            compression_quality 0x009d0903 (int)    : min=1 max=100 step=1 default=30 value=30

Le choix de ces paramètres est plus important et délicat qu'il n'y parait et les valeurs par défaut ne conviennent pas forcément à ce que l'on souhaite faire. On notera par exemple que l'intervalle des Iframes est par défaut à 60 ce qui convient parffaitement pour un découpage de segments en secondes pour un encodage à 60 images/seconde mais si on encode à 25 images/seconde les choses deviennent compliquées car un segment requiert impérativement de débuter par une Iframe.... Ce point est crucial pour la vidéodiffusion en HLS.

Exemple de modification des paramètres avec la commande v4l2-ctl -c permettant le passage d'un flux variable à un flux fixe à 1500000 b/s (1.5 Mb/s) :

ens-ife[~] raspi9-€ :  v4l2-ctl -c video_bitrate_mode=1
ens-ife[~] raspi9-€ :  v4l2-ctl -c video_bitrate=1500000

# Visualisation des effets
ens-ife[~] raspi9-€ :  v4l2-ctl --log-status

Status Log:

   [21317.086681] bcm2835-v4l2: =================  START STATUS  =================
   .../...
   [21317.086886] bcm2835-v4l2: Video Bitrate Mode: Constant Bitrate
   [21317.086896] bcm2835-v4l2: Video Bitrate: 1500000
   .../...
   [21317.086970] bcm2835-v4l2: ==================  END STATUS  ==================

2.3. Les commandes d'acquisition et d'encodage raspivid et ffmpeg

raspivid est le logiciel construit spécialement pour la raspicam et la raspberry, l'avantage est une optimisation forte mais l'inconvénient est la non portabilité des ce que l'on produit. ffmpeg est le couteau suisse de la vidéo, de nombreux logiciels reposent sur ce code (vlc,...), l'inconvénient est l'extrême flexibilité de cet outil proposée pour optimiser au maximum les performances ce qui se traduit par énormément d'options et parfois des pertes de performances dues à de mauvais choix de paramètres; l'avantage est est la portabilité totale et la capacité d'évolution au cours du temps.

Nous privilégions ici l'utilisation de ffmpeg à des fins d'interopérabilité et pour permettre de prendre en compte le maximum de matériels. Le bloc ci-dessous présente quelques exemples d'utilisation de raspivid dans un mode production de fichier local ou streaming.

raspivid -t 0 -w 240 -h 135 -b 512000 -fps 15 -g 5/15 -o - -pf main -v | ffmpeg  -i - -an -vcodec copy -f rtp_mpegts -r 15  rtp://192.168.221.52:1935/live  
raspivid -t 0 -w 480 -h 270 -b 512000 -fps 15 -g 5/15 -o - -pf main -v | ffmpeg  -i - -an -vcodec copy -f rtp_mpegts -r 15  rtp://192.168.221.32:1935/live  
raspivid -w 960 -h 540 -b 2000000 -v -t 0 -fps 25.0 -k -o test.mp4
raspivid -t 0 -w 960 -h 540  -o - | ffmpeg -i -   -f rtp rtmp://192.168.221.118:1935/live
raspivid -t 10000 -w 480 -h 270 -b 512000 -fps 25 -g 5/25 -pf main -o test.mp4 -v
raspivid -t 20000 -w 480 -h 270 -b 512000 -fps 25 -g 5/25 -pf main -o test.mp4 -v
raspivid -t 20000 -w 480 -h 270 -b 512000 -fps 25 -g 5/25 -pf main -o - -v | ffmpeg -i - -vcodec copy -an -f rtp rtp://192.168.221.118/live

raspivid -t 0 -ex night -w 480 -h 270 -b 512000 -fps 25 -g 5/25 -pf main -o - -v | ffmpeg -re -thread_queue_size 4 -i -  -strict -2 -vcodec copy -an -f 

raspivid -t 0 -ex night -w 960 -h 540 -b 1024000 -fps 25 -g 5/25 -pf main -o - -v | ffmpeg  -i -  -f rtp_mpegts -r 25  rtp://192.168.221.118:1935/live

Les exemples ci-dessous sont opérationnels et démontrent l'utilisation de quelques paramètres. Le protocole rtp:// ne permet apparemment pas de faire du multicast (regarder le flux vidéo sur plusieurs postes par contre le flux rtmp:// le permet. Dans l'exemple suivant on utilise je format flv (flash) en sortie qui est le plus accessible ainsi que le format v4l2 qui permet la plus large interopérabilité. De nombreuses autres configurations sont possibles, nous n'explorerons pas cet univers mais toutes les contributions sont bienvenues ici.

ffmpeg -f v4l2 -r 25 -pix_fmt h264 -video_size 640x360 -i /dev/video0 -c:v libx264  -an -b:v 1600k -preset veryfast  -f flv -r 25  rtmp://192.168.221.37:1935/live
ffmpeg -f v4l2 -r 25 -pix_fmt h264 -video_size 1280x720 -i /dev/video0 -c:v libx264  -an -b:v 1600k -preset ultrafast  -f flv -r 25  rtmp://192.168.221.37:1935/live
ffmpeg -f v4l2 -r 25 -pix_fmt h264 -video_size 640x360 -i /dev/video0 -c:v libx264  -an -b:v 1600k -preset fast  -f flv -r 25  rtmp://192.168.221.37:1935/live

3. Configuration du serveur et format du flux transporté

L'évolution importante de la vidéo sur le web a conduit à une simplification radicale de la chaine de vidéodiffusion, il y a une dizaine d'années les outils d'encodage, les serveurs et les visionneurs étaient des logiciels dédiés souvent construits sur un format ou un protocole particulier. Aujourd'hui l'encodage peut se faire via un outil généraliste (ffmpeg) les serveurs web modernes (nginx) proposent les protocoles de vidéodiffusion (rtmp, hls) au même titre que l'hypertexte (http), et les navigateurs web ou les logiciels de visualisation de films ordinaires acceptent de restituer vidéo à la demande ou vidéodiffusion, la contrepartie est l'utilisation de standards.

Ces standards s'appliquent de bout en bout et du fait de la pression du marché économique autour de la vidéo il n'existe pas de solution "clef en main" en logiciel libre, la communauté ayant mis en place une grande variété de solutions. Le seul point invariant de tout le système est le flux transporté. Nous avons déjà vu ci-dessus le flux au format flv transporté via le protocole rtmp; nous allons aborder maintenant un flux au format HLS transporté via le protocole http.

3.1. Le serveur web nginx rtmp

nginx est le logiciel de serveurs web qui tend à se répendre rapidement aux dépens d'apache, de nombreux serveurs nodejs apparaissent aussi du fait de la simplicité de la mise en oeuvre de sites web avec nodeJS. Nous utilisons ici la version nginx-extras du serveur nginx.

Pour mettre en place un serveur vidéo il suffit de rajouter la configuration du protocole rtmp au fichier de configuration /etc/nginx/nginx.conf comme indiqué ci dessous. Toutes les précisions se trouvent sur la documentation nginx-rtmp.

rtmp {
    server {
    listen 1935;
    chunk_size 4096;

    application live {
        live on;
        record off;
        allow publish all;
        allow play all;
        }
    }
}
* `rtmp` au même titre que `http` désigne le protocole
* `server` imposé par la syntaxe de la configuration nginx indique que l'on décrit un serveur en ligne
* `listen` imposé par la syntaxe de la configuration nginx désigne le port d'écoute -> 1935
* `chunk_size` décrit la taille des morceaux découpés pour le multiplexage
* `application` déclaration d'un flux `rtmp`
* `live` activation/désactivation  du mode vidéodiffusion en direct
* `record` activation/désactivation de l'enregistrement 
* `allow` (`deny`) `publish` / `play` gestion de la publication  et de la diffusion

De nombreuses options sont disponibles pour gérer tous les paramètres du serveur, se reporter à la documentation nginx-rtmp pour toutes les précisions possibles.

3.2. Le serveur web nginx HLS

La mise en place d'un flux hls est un peu plus compexe et requiert à la fois la configuration d'une application hls dans le serveur rtmp et la mise en place d'un point de diffusion dans le serveur http. Le bloc ci-dessous propose une configuration simple via le fichier /etc/nginx/nginx.conf qui peut servir de point de départ à des configurations plus élaborées (prenant en compte les flux les tailles les codecs). Attention les fragments doivent impérativement commencer par ine Iframe bien ajuster les paramètres de l'encodeur et du serveur.

#For good HLS experience we recommend using 3 seconds fragments with 60 seconds playlist.

rtmp {
    server {
        listen 1935; # Listen on standard RTMP port
        chunk_size 4000;

        application hls {
            live on;
            # Turn on HLS
            hls on;
            hls_path /mnt/hls/;
            hls_fragment 3;
            hls_playlist_length 60;
            # disable consuming the stream from nginx as rtmp
            deny play all;
        }
    }
}

Le bloc suivant est une proposition de point d'accès hls sur le serveur http. cat > /etc/nginx/sites-available/10-hls

#Since HLS consists of static files, a simple http server can be set up with two additions, correct MIME types and CORS headers.

server {
    listen 8080;

    location /hls {
        # Disable cache
        add_header Cache-Control no-cache;

        # CORS setup
        add_header 'Access-Control-Allow-Origin' '*' always;
        add_header 'Access-Control-Expose-Headers' 'Content-Length';

        # allow CORS preflight requests
        if ($request_method = 'OPTIONS') {
            add_header 'Access-Control-Allow-Origin' '*';
            add_header 'Access-Control-Max-Age' 1728000;
            add_header 'Content-Type' 'text/plain charset=UTF-8';
            add_header 'Content-Length' 0;
            return 204;
        }

        types {
            application/vnd.apple.mpegurl m3u8;
            video/mp2t ts;
        }

        root /mnt/;
    }
}

Lecture du stream rtmp pour hls.

http://192.168.221.38:1935/hls/test

3.2.1. Configuration d'un serveur HLS avec ffmpeg

Comme cela a été dit en introduction de ce chapître l'encodage hls est un peu plus compliqué, nous allons procéder étape par étape et d'abord considérer les paramètres de l'encoder ffmpeg avec la commande :

ens-ife[~] raspi9-€ :  ffmpeg -h muxer=hls

.../...

Muxer hls [Apple HTTP Live Streaming]:
    Common extensions: m3u8.
    Default video codec: h264.
    Default audio codec: aac.
    Default subtitle codec: webvtt.
hls muxer AVOptions:
  -start_number      <int64>      E........ set first number in the sequence (from 0 to I64_MAX) (default 0)
  -hls_time          <float>      E........ set segment length in seconds (from 0 to FLT_MAX) (default 2)
  -hls_init_time     <float>      E........ set segment length in seconds at init list (from 0 to FLT_MAX) (default 0)
  -hls_list_size     <int>        E........ set maximum number of playlist entries (from 0 to INT_MAX) (default 5)
  -hls_delete_threshold <int>        E........ set number of unreferenced segments to keep before deleting (from 1 to INT_MAX) (default 1)
  -hls_ts_options    <string>     E........ set hls mpegts list of options for the container format used for hls
  -hls_vtt_options   <string>     E........ set hls vtt list of options for the container format used for hls
  -hls_wrap          <int>        E........ set number after which the index wraps (will be deprecated) (from 0 to INT_MAX) (default 0)
  -hls_allow_cache   <int>        E........ explicitly set whether the client MAY (1) or MUST NOT (0) cache media segments (from INT_MIN to INT_MAX) (default -1)
  -hls_base_url      <string>     E........ url to prepend to each playlist entry
  -hls_segment_filename <string>     E........ filename template for segment files
  -hls_segment_size  <int>        E........ maximum size per segment file, (in bytes) (from 0 to INT_MAX) (default 0)
  -hls_key_info_file <string>     E........ file with key URI and key file path
  -hls_enc           <boolean>    E........ enable AES128 encryption support (default false)
  -hls_enc_key       <string>     E........ hex-coded 16 byte key to encrypt the segments
  -hls_enc_key_url   <string>     E........ url to access the key to decrypt the segments
  -hls_enc_iv        <string>     E........ hex-coded 16 byte initialization vector
  -hls_subtitle_path <string>     E........ set path of hls subtitles
  -hls_segment_type  <int>        E........ set hls segment files type (from 0 to 1) (default mpegts)
     mpegts                       E........ make segment file to mpegts files in m3u8
     fmp4                         E........ make segment file to fragment mp4 files in m3u8
  -hls_fmp4_init_filename <string>     E........ set fragment mp4 file init filename (default "init.mp4")
  -hls_flags         <flags>      E........ set flags affecting HLS playlist and media file generation (default 0)
     single_file                  E........ generate a single media file indexed with byte ranges
     temp_file                    E........ write segment to temporary file and rename when complete
     delete_segments              E........ delete segment files that are no longer part of the playlist
     round_durations              E........ round durations in m3u8 to whole numbers
     discont_start                E........ start the playlist with a discontinuity tag
     omit_endlist                 E........ Do not append an endlist when ending stream
     split_by_time                E........ split the hls segment by time which user set by hls_time
     append_list                  E........ append the new segments into old hls segment list
     program_date_time              E........ add EXT-X-PROGRAM-DATE-TIME
     second_level_segment_index              E........ include segment index in segment filenames when use_localtime
     second_level_segment_duration              E........ include segment duration in segment filenames when use_localtime
     second_level_segment_size              E........ include segment size in segment filenames when use_localtime
     periodic_rekey               E........ reload keyinfo file periodically for re-keying
     independent_segments              E........ add EXT-X-INDEPENDENT-SEGMENTS, whenever applicable
  -use_localtime     <boolean>    E........ set filename expansion with strftime at segment creation(will be deprecated ) (default false)
  -strftime          <boolean>    E........ set filename expansion with strftime at segment creation (default false)
  -use_localtime_mkdir <boolean>    E........ create last directory component in strftime-generated filename(will be deprecated) (default false)
  -strftime_mkdir    <boolean>    E........ create last directory component in strftime-generated filename (default false)
  -hls_playlist_type <int>        E........ set the HLS playlist type (from 0 to 2) (default 0)
     event                        E........ EVENT playlist
     vod                          E........ VOD playlist
  -method            <string>     E........ set the HTTP method(default: PUT)
  -hls_start_number_source <int>        E........ set source of first number in sequence (from 0 to 2) (default generic)
     generic                      E........ start_number value (default)
     epoch                        E........ seconds since epoch
     datetime                     E........ current datetime as YYYYMMDDhhmmss
  -http_user_agent   <string>     E........ override User-Agent field in HTTP header
  -var_stream_map    <string>     E........ Variant stream map string
  -cc_stream_map     <string>     E........ Closed captions stream map string
  -master_pl_name    <string>     E........ Create HLS master playlist with this name
  -master_pl_publish_rate <int>        E........ Publish master play list every after this many segment intervals (from 0 to UINT32_MAX) (default 0)
  -http_persistent   <boolean>    E........ Use persistent HTTP connections (default false)
  -timeout           <duration>   E........ set timeout for socket I/O operations (default -0.000001)

Instruction pour un streaming direct depuis la raspberry sur le serveur vidéo 192.168.221.37:1935 ou dans le fichier out.m3u8 :

ffmpeg -f v4l2 -r 25 -pix_fmt h264 -video_size 640x360 -i /dev/video0 -f alsa -ac 1 -i hw:1,0 -c:v libx264 -b:v 800k -preset ultrafast -b:a 64k  -f flv -r 25  rtmp://192.168.221.37:1935/hls/live


ffmpeg -f v4l2 -r 25 -pix_fmt h264 -video_size 640x360 -i /dev/video0 -c:v h264 -flags +cgop -g 25 -hls_time 1 out.m3u8

3.3. Le serveur web nginx DASH

La configuration du mode DASH est un petit peu plus simple que celle du HLS mais requiert tout de même un peu d'attention pour le paramétrisation.

ffmpeg -h muxer=dash
.../...

Muxer dash [DASH Muxer]:
    Common extensions: mpd.
    Default video codec: h264.
    Default audio codec: aac.
dash muxer AVOptions:
  -adaptation_sets   <string>     E........ Adaptation sets. Syntax: id=0,streams=0,1,2 id=1,streams=3,4 and so on
  -window_size       <int>        E........ number of segments kept in the manifest (from 0 to INT_MAX) (default 0)
  -extra_window_size <int>        E........ number of segments kept outside of the manifest before removing from disk (from 0 to INT_MAX) (default 5)
  -min_seg_duration  <int>        E........ minimum segment duration (in microseconds) (will be deprecated) (from 0 to INT_MAX) (default 5e+06)
  -seg_duration      <duration>   E........ segment duration (in seconds, fractional value can be set) (default 5)
  -remove_at_exit    <boolean>    E........ remove all segments when finished (default false)
  -use_template      <boolean>    E........ Use SegmentTemplate instead of SegmentList (default true)
  -use_timeline      <boolean>    E........ Use SegmentTimeline in SegmentTemplate (default true)
  -single_file       <boolean>    E........ Store all segments in one file, accessed using byte ranges (default false)
  -single_file_name  <string>     E........ DASH-templated name to be used for baseURL. Implies storing all segments in one file, accessed using byte ranges
  -init_seg_name     <string>     E........ DASH-templated name to used for the initialization segment (default "init-stream$RepresentationID$.m4s")
  -media_seg_name    <string>     E........ DASH-templated name to used for the media segments (default "chunk-stream$RepresentationID$-$Number%05d$.m4s")
  -utc_timing_url    <string>     E........ URL of the page that will return the UTC timestamp in ISO format
  -method            <string>     E........ set the HTTP method
  -http_user_agent   <string>     E........ override User-Agent field in HTTP header
  -http_persistent   <boolean>    E........ Use persistent HTTP connections (default false)
  -hls_playlist      <boolean>    E........ Generate HLS playlist files(master.m3u8, media_%d.m3u8) (default false)
  -streaming         <boolean>    E........ Enable/Disable streaming mode of output. Each frame will be moof fragment (default false)
  -timeout           <duration>   E........ set timeout for socket I/O operations (default -0.000001)
  -index_correction  <boolean>    E........ Enable/Disable segment index correction logic (default false)
  -format_options    <string>     E........ set list of options for the container format (mp4/webm) used for dash
  -dash_segment_type <int>        E........ set dash segment files type (from 0 to 1) (default mp4)
     mp4                          E........ make segment file in ISOBMFF format
     webm                         E........ make segment file in WebM format

Encodage de la video seule avec un seul flux. les paramètres de la vidéo sont fournis avec la variable externe VP9_LIVE_PARAMS="-speed 6 -tile-columns 4 -frame-parallel 1 -threads 8 -static-thresh 0 -max-intra-rate 300 -deadline realtime -lag-in-frames 0 -error-resilient 1"

ffmpeg \
  -f v4l2 -input_format mjpeg -r 30 -s 1280x720 -i /dev/video0 \
  -pix_fmt yuv420p \
  -c:v libvpx-vp9 \
    -s 1280x720 -keyint_min 60 -g 60 ${VP9_LIVE_PARAMS} \
    -b:v 3000k \
  -f webm_chunk \
    -header "/var/www/webm_live/glass_360.hdr" \
    -chunk_start_index 1 \
  /var/www/webm_live/glass_360_%d.chk \

Encodage audio video avec un seul flux

ffmpeg \
  -f v4l2 -input_format mjpeg -r 30 -s 1280x720 -i /dev/video0 \
  -f alsa -ar 44100 -ac 2 -i hw:2 \
  -map 0:0 \
  -pix_fmt yuv420p \
  -c:v libvpx-vp9 \
    -s 1280x720 -keyint_min 60 -g 60 ${VP9_LIVE_PARAMS} \
    -b:v 3000k \
  -f webm_chunk \
    -header "/var/www/webm_live/glass_360.hdr" \
    -chunk_start_index 1 \
  /var/www/webm_live/glass_360_%d.chk \
  -map 1:0 \
  -c:a libvorbis \
    -b:a 128k -ar 44100 \
  -f webm_chunk \
    -audio_chunk_duration 2000 \
    -header /var/www/webm_live/glass_171.hdr \
    -chunk_start_index 1 \
  /var/www/webm_live/glass_171_%d.chk```


Instruction pour un streaming direct depuis la raspberry sur le serveur vidéo `192.168.221.37:1935`  :

```shell
ffmpeg -f v4l2 -r 25 -pix_fmt h264 -video_size 640x360 -i /dev/video0 -f alsa -ac 1 -i hw:1,0 -c:v libx264 -b:v 800k -preset ultrafast -b:a 64k  -f flv -r 25  rtmp://192.168.221.37:1935/hls/test

3.4. Raspberry Pi comme encodeur et serveur

Comme expliqué au début la chaîne de vidéodiffusion est composé d'éléments indépendants il est donc possible de donner à la Raspberry Pi les deux fonctions : encodeur et serveur. Il suffit pour cela de remplacer l'adresse du serveur par l'adresse locale de la raspberry 127.0.0.1. Il suffit alors de se connecter avec un lecteur de flux vidéo et le tour est joué.

ffmpeg -f v4l2 -r 25 -pix_fmt h264 -video_size 640x360 -i /dev/video0 -c:v libx264  -an -b:v 1500k -preset veryfast  -f flv -r 25  rtmp://127.0.0.1:1935/live

Pour le hls

ffmpeg -f v4l2 -r 25 -pix_fmt h264 -video_size 640x360 -i /dev/video0 -c:v libx264  -an -b:v 1500k -preset veryfast  -f flv -r 25  rtmp://127.0.0.1:1935/hls/test

Il est impératif aussi de modifier le configuration du serveur nginx de la raspberry pour qu'il soit capable de servir le rtmp, le hls et le dash on regroupe donc les configurations vues précédemment :

rtmp {
    server {
        listen 1935;
        chunk_size 4096;

        application live {
             live on;
            record off;
            allow publish all;
            allow play all;
            }

        application hls {
            live on;
            hls on;
            hls_cleanup on;
            hls_sync 100ms;
            hls_fragment 2s;
            hls_path /tmp/hls;
            allow play all;
        }

        application dash {
            live on;
            dash on;
            dash_path /tmp/dash;
            allow play all;
        }
    }
}       

variantes

Utilisation de la commande exec_pull pour convertir le format rtmp avec ffmpeg.

rtmp {
   access_log /var/log/nginx/rtmp_access.log;
   server {
       listen 1935;
       ping 30s;
       notify_method get;
       application camera1 {
           live on;
           exec_pull ffmpeg -i rtsp://admin:admin@10.10.10.11/axis-media/media.amp -threads 2 -f flv -r 25 -s 1280x720 -an rtmp://localhost:1935/cam1/stream 2>>/var/log/nginx/ffmpeg.log;
       }
   }
}
rtmp {
    server {
            listen 1935;
            chunk_size 4096;
            application live {
                live on;
                record off;
                hls on;
                hls_nested on;
                hls_path /usr/local/nginx/html/hls/;
                hls_fragment 3;
                hls_playlist_length 60;
                exec_pull /usr/bin/ffmpeg -i rtsp://10.103.0.77:8050/$name -vcodec copy -acodec copy -f flv rtmp://10.103.2.106:1935/live/$name;
           }
    }

}

4. Configuration du player dans une page web

Ajout d'un player nodejs via sudo npm install -g --upgrade video.js

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Live Streaming</title>
    <link href="//vjs.zencdn.net/5.8/video-js.min.css" rel="stylesheet">
    <script src="//vjs.zencdn.net/5.8/video.min.js"></script>
</head>
<body>
<video id="player" class="video-js vjs-default-skin" height="360" width="640" controls preload="none">
    <source src="http://localhost:8080/hls/stream.m3u8" type="application/x-mpegURL" />
</video>
<script>
    var player = videojs('#player');
</script>
</body>
</html>

Autre possibilité

<script src="dash-old.all.js"></script>

        <script>
            function getUrlVars() {
                var vars = {};
                var parts = window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function(m,key,value) {
                    vars[key] = value;
                });
                return vars;
            }
            function startVideo() {
                var vars = getUrlVars(),
                    url = "http://192.168.100.107:80/dash/Screen.mpd",
                    video,
                    context,
                    player;
                if (vars && vars.hasOwnProperty("url")) {
                    url = vars.url;
                }
                video = document.querySelector(".dash-video-player video");
                context = new Dash.di.DashContext();
                player = new MediaPlayer(context);
                player.startup();
                player.attachView(video);
                player.setAutoPlay(true);
                player.attachSource(url);
            }
        </script>

Webographie

  1. Création de l'image
  2. présentation des images disponibles
  3. weewx
  4. article HLS
  5. capture de la webcam
  6. documentation nginx-rtmp
  7. documentation HLS
  8. documentation DASH

Commentaires