I was in a similar situation and I especially didn't like the order of actions in the "classic" raspivid -> netcat -> netcat -> mplayer
streaming example because it requires entering the client IP on the Pi side. I wanted something that allows me to
- request a stream from the Pi
- to a single client only
- without excess load when idle
So let's start with the
classic way (not what we want)
First, set up a server on the "viewer system" (IP address 192.168.0.1) that waits for a connection on port 5001, using netcat
:
netcat -l 5001 | mplayer -fps 30 -cache 1024 - 2>&1 &
Then, start the stream on the Pi and pipe the output into netcat:
raspivid -fps 25 -w 640 -h 480 -t 0 -o - | nc 192.168.0.1 5001
I call this the "classic" way because I found it in many places when I was trying to use the camera for the first time. It certainly is very useful for testing, as it is so simple.
However, to get the desired behavior, the overall order of things must be reversed. This can be done by wrapping the two lines above in two simple server and client scripts.
Turning things around
The Pi must act as a kind of server and the viewer system must act as a kind of client. So we need a second flow of information from the viewer system to the Pi, that transports information about the viewer to the Pi.
server.sh
(run on the Pi):
#/bin/bash
PORT=51718
if [ "$#" -eq 1 ]
then
PORT=$1
fi
while [ true ]
do
echo "Starting Video Server on port $PORT"
IP_AND_PORT=$(nc -l $PORT)
echo "Streaming to $IP_AND_PORT"
raspivid -fps 25 -w 640 -h 480 -t 0 -o - | nc $IP_AND_PORT
echo "Stream terminated"
done
server.sh
listens for the client's IP address and port on port 51718 or the one specified in the first command line argument.
- When it has received that information, it starts streaming to that IP/port just like in the "classic" way.
- This is done in a loop, so it's possible to view the stream, terminate it from the client, and come back later.
The client must do what is expected by the Pi. The Pi's assumed IP address is 192.168.0.2 in this case.
client.sh
(run on the viewer system):
#!/bin/bash
if [ "$#" -ne 3 ]
then
echo " usage: client.sh pi_ip pi_video_server_port pi_video_stream_port"
echo "example: client.sh 192.168.0.2 51718 5001"
exit 1
fi
OWN_IP=$(ip route get 8.8.8.8 | head -1 | cut -d' ' -f8)
PI_IP=$1
PI_VIDEO_SERVER_PORT=$2
PI_VIDEO_STREAM_PORT=$3
echo "requesting stream to $OWN_IP:$PI_VIDEO_STREAM_PORT"
# start mplayer which waits for a stream from the pi
nc -l $PI_VIDEO_STREAM_PORT | mplayer -fps 30 -cache 1024 - 2>&1 &
echo "$OWN_IP $PI_VIDEO_STREAM_PORT" | nc $PI_IP $PI_VIDEO_SERVER_PORT
while [ true ]
do
sleep 1s;
done
This script needs the Pi's IP address, the port it uses to gather the viewer's address and port, and the streaming port as arguments. A suitable example is given in the help text at the beginning of the script.
client.sh
first starts netcat
, waiting for a video stream, ready to pipe the output into mplayer
(in the background, note the trailing &
). These won't do much until the Pi starts streaming.
- It then sends the viewer's address and port to the Pi, where
netcat
is listening for that information.
- As soon as the Pi starts streaming, you should see
mplayer
filling the cache and then showing output.
- The player can be killed and started again later, the server should allow this.
Some remarks:
- Most of this is probably bad style, bash-, networking- or otherwise. I really don't know.
- you specifically asked for
vlc
. I don't use it and I don't have any experience with it, but it should be possible to use it for sending or for receiving the stream.
server.sh
can be run as a daemon without any interaction required. There are many way to do that.
- If it doesn't work, the first thing is to check for unset variables. I actually use slightly different scripts that gather the Pi's IP and ports from the environment, as well as settings for
raspivid
and mplayer
. Please add a comment here in that case!
- While we needed to tell the Pi about the viewer's IP address in the "classic" example, we now need to tell the viewer where the Pi is.
- We need an extra port here (51718 in this case).
You'll also see a lot of frequent "empty cache" warnings from mplayer
. That has a practical reason: reading from the cache faster than it is filled results in less lag between the camera and the shown video. There is some lag after mplayer
is started, but that goes down and almost vanishes as soon as the cache is empty. The lag from this effect depends on streaming fps, cache size and viewer fps. I get it down to about 100 ms in my network (WiFi).