Streamer un flux audio PCM

On veut diffuser sur le réseau un flux audio linéaire non compressé, voici une méthode ainsi que les différentes pistes explorées pour arriver au résultat.

Les outils utilisés sont ffmpeg pour le diffuseur et ffplay le lecteur.

  • On utilise le paramètre realtime -re
  • Si on a pas de source live, on peut streamer fichier audio local en boucle avec stream_loop -1

1. Stream PCM en UDP

1ffmpeg -re -stream_loop -1 -i test.wav udp://127.0.0.1:1234
2  Stream #0:0: Audio: pcm_s16le ([1][0][0][0] / 0x0001), 44100 Hz, stereo, s16, 1411 kb/s
3[NULL @ 0x142705c60] Unable to find a suitable output format for 'udp://127.0.0.1:1234'
4udp://127.0.0.1:1234: Invalid argument

Ça ne stream pas du tout, il faut un conteneur ?

2. Stream PCM en UDP dans conteneur MPEGTS

1ffmpeg -re -stream_loop -1 -i test.wav -f mpegts udp://127.0.0.1:1234
2  Stream #0:0 -> #0:0 (pcm_s16le (native) -> mp2 (native))

Ça stream mais ffmpeg transcode le flux en mp2 (MPEG 1 Layer II), ce qu'on ne veut pas.

Côté lecteur, après un léger buffering, le flux est bien lu.

1ffplay rtp://127.0.0.1:1234

3. Stream PCM en RTP

1ffmpeg -re -stream_loop -1 -i test.wav rtp://127.0.0.1:1234
2  Stream #0:0 -> #0:0 (pcm_s16le (native) -> pcm_mulaw (native))

Ça stream mais ffmpeg transcode le flux en pcm_mulaw, ce qu'on ne veut pas.

Côté lecteur, ça ne marche pas du tout.

1ffplay rtp://127.0.0.1:1234
2[rtp @ 0x121a056f0] Unable to receive RTP payload type 97 without an SDP file describing it
3rtp://127.0.0.1:1234: Invalid data found when processing input

4. Stream PCM en RTP et gros-boutisme

Cette page m'a aidé à comprendre le problème.

Le protocole RTP, avec les charges utiles 10 ou 11, n'est compatible qu'avec les données en gros-boutisme. OK... Or notre flux PCM est en petit-boutiste (pcm_s16le), "le" pour "little-endian". Faisons donc ce transcodage sans perte, et théoriquement peu couteux car c'est juste une inversion de l'ordre des octets.

1ffmpeg -re -stream_loop -1 -i test.wav -acodec pcm_s16be -ar 44100 -ac 2 -payload_type 10 -f rtp rtp://127.0.0.1:1234
2  Stream #0:0 -> #0:0 (pcm_s16le (native) -> pcm_s16be (native))

Ça stream toujours, lançons le lecteur :

1ffplay rtp://127.0.0.1:1234
2[rtp @ 0x13b907630] Guessing on RTP content - if not received properly you need an SDP file describing it
3Input #0, rtp, from 'rtp://127.0.0.1:1234':
4  Duration: N/A, start: 0.000000, bitrate: 1411 kb/s
5  Stream #0:0: Audio: pcm_s16be, 44100 Hz, 2 channels, s16, 1411 kb/s

Ça lit. Parfait, on a notre méthode.

En revanche si on désire enregistrer localement et sans aucun transcodage le flux transporté on aura l'erreur suivante :

1ffmpeg -i rtp://127.0.0.1:1234 -c copy -y recording.wav
2[rtp @ 0x13f7049b0] Guessing on RTP content - if not received properly you need an SDP file describing it
3Guessed Channel Layout for Input Stream #0.0 : stereo
4Input #0, rtp, from 'rtp://127.0.0.1:1234':
5  Duration: N/A, start: 0.000000, bitrate: 1411 kb/s
6  Stream #0:0: Audio: pcm_s16be, 44100 Hz, stereo, s16, 1411 kb/s
7[wav @ 0x13f7060a0] Codec pcm_s16be not supported in WAVE format
8Could not write header for output file #0 (incorrect codec parameters ?): Function not implemented
9Error initializing output stream 0:0 --

En retirant le -c copy qui désactive le transcodage, ffmpeg va convertir automatiquement le flux en little endian et on aura un fichier wav valide.

1ffmpeg -i rtp://127.0.0.1:1234 recording.wav
2  Stream #0:0 -> #0:0 (pcm_s16be (native) -> pcm_s16le (native))

Conclusion

Voilà, on a une méthode qui marche pour streamer un flux audio sans compression sur le réseau. J'avais également pensé à la piste du codec FLAC mais il ne peut être inséré dans un conteneur MPEG-TS. enfin, d'après Wikipedia, le payload RTP 10 nécesite une fréquence d'échantillonage à 44.1kHz. Je n'ai pas essayé avec d'autres fréquences.

Ressources

comments powered by Disqus