3

Although I don't know much about video encoding and such I know how to stream video from the camera board with vlc by piping the output of raspivid into cvlc. Despite the horrific lag, it works well.

However, my other concern is streaming video like that makes the Pi work really hard, around the clock, even when nobody is watching it, which seems pointless to me.

Is there a way to make video from the camera available remotely but only actually start decoding/streaming when someone is watching the stream? Ideally I want to achieve this without running some command first via SSH

1

Have a look at the example script in the "recording to a network stream" recipe in the picamera docs. The script at the end of that section listens on a network port and only starts recording video when someone connects. It shouldn't be terribly difficult to modify that recipe so that 1) it's more than a one-shot affair (in the recipe it streams for 60 seconds then quits), and 2) so that it only initializes the camera object after connection (the camera draws quite a bit of power even when its not recording, simply because things like auto-exposure and AWB are constantly running).

1

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).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.