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.