Aug 072013
 
VLC streaming with GPIO control thumbnail

Having managed to get VLC streaming from Pi Camera to Nexus 7 Android tablet working, it was only a matter of time before I wanted to stream from the RasPiCamcorder. But when I tried it, I came across an annoying problem. RPi.GPIO needs to run as root (that’s why we always use sudo for our GPIO stuff)

But VLC refuses to run as root. This means you can’t run them both from the same program. There’s all sorts of clever solutions people proposed (and I wasted a lot of time unsuccessfully trying them all out) about changing IDs within a script. VLC seemed to be immune to all that. So, for a time, I gave up. But the mind didn’t switch off and a workaround came to me while I was doing something else.

I needed a way to separate out the GPIO part from the VLC streaming part. But how to get the two parts to talk to each other?

And then it hit me. “Everything in Linux is a file.”

So, what’s to stop me writing to and reading from a file (not owned by root) to check GPIO status? Nothing at all! It’s a bit of a ‘cludgy’ workaround, but it works – and here’s a picture of the RasPiCamcorder streaming to my phone and Nexus 7 simultaneously, while I took the photo with my main camcorder…

RasPiCamcorder streaming to Phone and Nexus 7 with GPIO control

Here’s a short video of it working

OK let’s back up and explain

So basically, I’m running the (sudo) GPIO part as a second script. Each time the status of the button inputs changes, it writes the new status (1 or 0) to a file that can be read by the first script (non-root user, VLC streaming control).

I think I’ve made that sound much more difficult than it really is, so I’ll try and summarise it.

  1. You run the non-sudo script (controls VLC and camera)
  2. This script creates a file (owns it) which the other one will write to [script 1, line 49]
  3. Then it starts the second script as root (the GPIO part) [script 1, line 54]
  4. Second script reads the buttons (input ports) [script 2, lines 83 & 91]
  5. If start button pressed, it writes 1 to the file [script 2, line 53], if stop button pressed, 0 [script 2, line 98]
  6. Script 1 reads this file 10 times per second and switches streaming on or off in response to whether the file contains a 1 or a 0 [script 1, lines 60-77]

So, yes. It’s a ‘cludgy’ workaround, but it works, and it made me feel very clever for a while because I made it do what I wanted it to do. ;)

If you want to see the scripts that use this trick, they are picamstreamer.py and picamstream-sudo.py below…

Script 1 picamstreamer.py

#!/usr/bin/env python2.7
# script by Alex Eames http://RasPi.tv

from subprocess import call
import sys
import time

front_led_status = sys.argv[-1]
if front_led_status == "0":
    print "front LED off"
    front_led_status = 0

streaming_on = 0
streaming_file = "/home/pi/streaming.txt"
if front_led_status == 0:
    sudo_file = "sudo python /home/pi/picamstream-sudo.py 0 &"
else:
    sudo_file = "sudo python /home/pi/picamstream-sudo.py &"

stream = "raspivid -o - -t 9999999 -w 640 -h 360 -fps 25|cvlc stream:///dev/stdin --sout '#standard{access=http,mux=ts,dst=:8090}' :demux=h264 &"

# you can change -w and -h numbers for whatever suits your network.
# Depending on what I'm streaming to, mine can cope with 720p, 
# which is -w 1280 -h 720

def stream_video():
    print "starting streaming\n%s" % stream
    call ([stream], shell=True)

def stop_stream():
    print "stopping streaming"
    call (["pkill raspivid"], shell=True)
    call (["pkill vlc"], shell=True)

def check_streaming_status():
# read file streaming_file, make into int() set streaming_on equal to it
    vrn = open(streaming_file, 'r')
    streaming_on = int(vrn.readline())
    #print "streaming_on is %d" % streaming_on
    vrn.close()
    return streaming_on

def write_streaming_status(streaming_on):
    vrnw = open(streaming_file, 'w')
    vrnw.write(str(streaming_on))
    vrnw.close()

try:
    write_streaming_status(0)
except:
    print "couldn't write streaming status"
    sys.exit()

call ([sudo_file], shell=True)
   
previous_streaming_on = 0
counter = 0

#have a file containing the streaming_on variable value
while True:
    streaming_on = check_streaming_status()
    #print "streaming status = %d" % streaming_on

    if streaming_on != previous_streaming_on:
        if streaming_on == 1:
            stream_video()
        elif streaming_on == 0:
            stop_stream()
        else:
            stop_stream()
            print "Closing picamstreamer.py"
            sys.exit()
    if counter % 50 == 0:
        print "streaming status = %d" % streaming_on  
    previous_streaming_on = streaming_on
    counter += 1
    time.sleep(0.1)

Script 2 picamstream-sudo.py

#!/usr/bin/env python2.7
# script by Alex Eames http://RasPi.tv
import RPi.GPIO as GPIO
import sys
import os
import time
from time import sleep

front_led_status = sys.argv[-1]
if front_led_status == "0":
    print "front LED off"
    front_led_status = 0

streaming_on = 0
streaming_file = "/home/pi/streaming.txt"
time_off = 0

GPIO.setmode(GPIO.BCM)

# GPIO 23 set up as input, pulled up to avoid false detection.
# wired to connect to GND on button press.
# So we'll be setting up falling edge detection 
GPIO.setup(23, GPIO.IN, pull_up_down=GPIO.PUD_UP)

# GPIO 24 set up as an input, pulled down, connected to 3V3 on button press
GPIO.setup(24, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)

# Set up GPIO 5 for camera LED control and rear LED control
GPIO.setup(5, GPIO.OUT)
GPIO.setup(22, GPIO.OUT)

def write_streaming_status(streaming_on):
    vrnw = open(streaming_file, 'w')
    vrnw.write(str(streaming_on))
    vrnw.close()

def check_streaming_status():
# read file streaming_file, make into int() set streaming_on equal to it
    vrn = open(streaming_file, 'r')
    streaming_on = int(vrn.readline())
    #print "streaming_on is %d" % streaming_on
    vrn.close()
    return streaming_on

# threaded callback function runs in another thread when event is detected
# this increments variable rec_num for filename and starts recording
def stream_button(channel):
    global time_off
    time_now = time.time()
    if (time_now - time_off) >= 0.3:
        streaming_status = check_streaming_status()
        if streaming_status == 0:
            write_streaming_status(1)
            print "stream button pressed"
            if front_led_status != 0:
                GPIO.output(5, 1)
            GPIO.output(22, 1)
    time_off = time.time()

def flash(interval,reps):
    for i in range(reps):
        GPIO.output(5, 1)
        GPIO.output(22, 1)
        sleep(interval)
        GPIO.output(5, 0)
        GPIO.output(22, 0)
        sleep(interval)

def shutdown():
    print "shutting down now"
    flash(0.05,50)
    GPIO.cleanup()
    os.system("sudo halt")
    sys.exit()

try:
    write_streaming_status(0)
except:
    print "couldn't write streaming status"
    sys.exit()

# when a falling edge is detected on blue stream button port 23 stream_button() will be run
GPIO.add_event_detect(23, GPIO.FALLING, callback=stream_button)

try:
    while True:
        # this will run until black button attached to 24 is pressed, then 
        # if pressed long, shut program, if pressed very long shutdown Pi
        # stop recording and shutdown gracefully
        print "Waiting for button press" # rising edge on port 24"
        GPIO.wait_for_edge(24, GPIO.RISING)
        #print "Stop button pressed"

        time_now = time.time()
        if (time_now - time_off) >= 0.3:
            streaming_status = check_streaming_status()
            if streaming_status == 1:
                write_streaming_status(0)
                print "Stop button pressed"
                GPIO.output(5, 0)
                GPIO.output(22, 0)
        time_off = time.time()

        # poll GPIO 24 button at 20 Hz continuously for 2 seconds
        # if at the end of that time button is still pressed, shut down
        # if it's released at all, break
        for i in range(60):
            if not GPIO.input(24):
                break
            sleep(0.05)

        if 25 <= i < 58:
            print "Closing program"
            flash(0.02,50) # interval,reps
            write_streaming_status(2)   # 2 will close the host program
            GPIO.cleanup()
            sys.exit()

        if GPIO.input(24):
            if i >= 59:
                shutdown()

finally:
    write_streaming_status(0)
    GPIO.cleanup()       # clean up GPIO on exit

I hope to release the entire suite of RasPiCamcorder software soon – I just need to do a little work on the documentation before I can unleash it on the world.

I hope you found this little trick useful. It’s a bit convoluted, but it gets the job done.

  14 Responses to “How to stream vlc from a Raspberry Pi with GPIO control but without being root”

  1. The video is still marked as “private” ?

  2. Brilliant stuff!
    I’m looking into doing something along similar lines at my squash club.

    I’d like to record matches, but also want the ability to stream the live play to a nearby TV.
    The recording should be remote controlled.
    This all looks very doable and affordable, using a Pi, from what I’ve seen on your site.

    • You won’t even need the VLC GPIO fix for that if you’re going to control it by ssh or bluetooth. You could just remotely record and hook up monitors to HDMI. Of course, there’s loads of other ways to do it. :)

      • TV is about 40-50 meter from court. Probably rules out hdmi?
        I would be keen to weigh up different options.
        Care to elaborate on some of the other ways.

        • In that case the options are more limited. I haven’t yet figured out how to stream AND record at the same time. But it must be doable. But if not, you could record the stream with another device. You’ll need a good router, either fast wifi or better yet ethernet.

          You’ll have to try and see what throughput your network can handle. No reliable way of judging that in advance.

          50m HDMI? Sounds a bit long.No idea what the specs are though.

          • I haven’t yet figured out how to stream AND record at the same time. But it must be doable.

            It’s actually dead easy – just use the ‘tee’ command in the middle of your pipe, i.e. change the streaming command to:
            raspivid -o - -t 9999999 -w 640 -h 360 -fps 25 | tee videofilename.h264 | cvlc stream:///dev/stdin --sout '#standard{access=http,mux=ts,dst=:8090}' :demux=h264

          • Excellent. Thanks Andrew:)

  3. RPi.GPIO needs to run as root. But VLC refuses to run as root. This means you can’t run them both from the same program. There’s all sorts of clever solutions people proposed…

    I’ve finally got round to having a bit of a fiddle with this, and came up with two approaches to getting this working (tested in a Bash script being run as root).
    The first (easier) approach simply runs the whole streaming command as the ‘pi’ user:

    sudo -u pi bash -c "raspivid -o - -t 20000 -w 640 -h 360 -fps 25 -rot 270 -n | cvlc stream:///dev/stdin --sout '#standard{access=http,mux=ts,dst=:8090}' :demux=h264"

    And the second (more flexible) approach continues to run raspivid as ‘root’, but just runs the vlc part as ‘pi’:

    FIFO=/tmp/vidfifo$$
    mkfifo $FIFO
    sudo -u pi bash -c "cat $FIFO | cvlc stream:///dev/stdin --sout '#standard{access=http,mux=ts,dst=:8090}' :demux=h264 &"
    raspivid -o - -t 20000 -w 640 -h 360 -fps 25 -rot 270 -n > $FIFO
    pkill vlc
    rm -f $FIFO

    Sorry I never investigated this earlier, to save you from all this extra hassle!

    Andrew ‘Linux’ Scheller ;-)

  4. You can operate the GPIO as a non-root user. Just use WiringPi instead of RPi.GPIO.

  5. I am look for a program/code to setup a Live Stream for my video production company …

    With network cable for indoors
    and
    3G Dongle for outdoors

    the live stream will run on my web site, using YOUTUBE live events http://www.photosofafrica.com/live-broadcast-youtube

    so Live stream will be in HD using my HD Professional video Camera..not the Raspberry 5mp Camera…

    any ideas ? PLEASE ADVISE…

    • You won’t be able to use the RaspberryPi for this then, it’s not fast enough to process video transmitted over USB (which must go via the CPU). It’s only able to process video from the Pi Camera because it connects directly to the GPU via the CSI connector, bypassing the CPU. (the GPU and CPU are both part of the same BCM2835 SoC, but the GPU is much more powerful than the CPU)

Leave a Reply