Streaming Video With RaspberryPi
Contents
General
Caution: this is a Work in progress, things are being tested. The objective is to provide in the end one or more working solutions for everyone.
Video streaming is a problem
The RaspberryPi camera offers an interesting solution to this problem. It is a very well integrated module of the Pi with one huge advantage: h264 encoding can be performed directly by the CPU as the camera uses the Serial Camera Interface protocol.
So theorically, solving the video problem with the Pi is easy but there are many subtle problems.
Problems
Audio As we use video webstreaming mostly for conferences broadcasting, good audio quality is necessary.
Slides It would be interesting to include slides of conferences while filming.
File It is important to have a file at the end of the filming.
Web It is important to have a large viewer base, therefore a well supported format.
Raspicam basics
http://elinux.org/Rpi_Camera_Module
raspivid is the basic command line used to capture video in h264.
raspivid -t 3 -fps 25 -b 1000000 -w 1920 -h 1080 -o /tmp/video.h264
A very simple tutorial : http://www.raspberrypi-spy.co.uk/2013/05/capturing-hd-video-with-the-pi-camera-module/
Solution
Solution 1 : OGG/VORBIS + Icecast
Basic idea
- Use the PI to capture video as h264, merge audio from usb and use ffmpeg to produce MPEGTS "chunks"
- Rsync the chunks to a laptop or a server (note : the audio mix should be integrated here to ensure a good audio/video synchronization)
- Assemble the chunks and pipe them in ffmpeg
- Ask ffmpeg to convert this into ogg
- Use oggfwd to push the ogg to your icecast server
- Serve m3u from the server
CON ogg does not work for everyone. It is supposed to be HTML5 compatible but icecast doesn't offer that by default.
PRO Icecast is simple, open, and handles authentification. Rsync using SSH is crypto friendly. The file is saved on the server.
How to stream in OGG to Icecast
A. Compile FFMPEG on pi & server (see below)
B. Start capture in a screen
It is advised to run this one liner in a screen
command on the RaspberryPi
[ -d /tmp/capture ] || mkdir /tmp/capture; rm -f /tmp/capture/* && cd /tmp/capture/ && \
raspivid -ih -t 0 -w 1280 -h 720 -b 1000000 -pf baseline -o - | /usr/local/bin/ffmpeg -f alsa -itsoffset 6.5 -ac 1 -i hw:1 -acodec aac -strict -2 \
-i - -vcodec copy -f segment -segment_list out.list -segment_list_flags +live -segment_list_size 5 -segment_time 4 -segment_time_delta 3 %10d.ts
What's happening here
[ -d /tmp/capture ] || mkdir /tmp/capture; rm -f /tmp/capture/* && cd /tmp/capture/
We create a /tmp/capture folder and make sure it's empty when starting capture in itraspivid
Use raspivid to capturing with following parameters:-ih
(inline headers) DONT CHANGE Necessary for technical reasons, as otherwise the "chunking" doesn't work-t 0
(timeout) DONT CHANGE Necessary for technical reasons, as otherwise capture stops after 5s-w 1080 -h 720
(height) and (width) Tweak according to your needs-b 1000000
(bitrate) Tweak according to your needs (only integer numbers in bits are accepted, here <=> 1Mb)-pf baseline
(h264 profile) Tweak according to your needs ( only baseline, main, or high accepted)-o -
(output) DONT CHANGE Necessary in order to use the flux as Standard Output
- We pipe the content into ffmpeg with following parameters:
- ALSA Input
-f alsa
(format) We usealsa
for usb audio capture-itsoffset 6.5
(time offset) This one is a trick We noticed our RPi B+ had a 6.5 seconds delay to start the audio, so this is used to resync audio. Tweak.-ac 1
(number of audio channels) We used a mono input, so1
was the right choice. Tweak-i hw:1
(input) Tweak as your audio card adress may vary. Find more witharecord -l
-acodec aac
(audio codec) AAC works well for TS live.-strict -2
Argument mandatory for AAC format
- Video Input
-i -
(input) DONT CHANGE Use the Standard input-vcodec copy
(video codec) DONT CHANGE Use the video codec from the RPi. Not enough CPU to do anything else.-f segment
(output format) DONT CHANGE Use a "chunked" output-segment_list out.list
(segment file) Defines a file containing the produced files names-segment_list_flags +live
(segment file flags) Defines the way the output file caches files names.-segment_list_size 5
(segment file size)-segment_time 4
(segment time) Defines the capture base duration in seconds. Tweak.-segment_time_delta 3
(segment time delta) Defines a window to modulate chunks duration in seconds to include mandatory inline headers. Tweak.%10d.ts
The format for chunks files names. %10d will start at 0000000000.ts and ffmpeg understand we want MPEGTS format for chunks
- ALSA Input
- ffmpeg saves the files 0000.ts, 0001.ts, etc. and out.list in /tmp/capture
C. Use rsync to infinitely synchronise chunks on server
Some important points to mention here
- The RaspberryPi MUST have access to a <server> using an SSH KEY for an <user>. Password access won't work for infinite rsync.
- This <server> CAN be your laptop. If so it MUST be on the same LAN as the RaspberryPi
- This <server> CAN be a datacenter machie. If so it MUST be accessible on Internet by the RaspberryPi.
- This <server> MUST have FFMPEG installed (see point D below)
- It is advised to run this one liner in a
screen
command on the RaspberryPi
ssh <user>@<server> "[ -d /tmp/capture ] || mkdir /tmp/capture" && \
while true; do rsync -a --files-from=/tmp/capture/out.list /tmp/capture <user>@<server>:/tmp/capture; sleep 1; done
What's happening here
ssh
Use SSH ...<user>@<server>
... to connect to server "server" as user "user""[ -d /tmp/capture ] || mkdir /tmp/capture"
... and create if not exists a folder "/tmp/capture"
while true; do
Run an infinite looprsync
Start rsync file synchronisation-a
(archive mode) Set the right parameters for transfer--files-from=/tmp/capture/out.list
Use the out.list as a list of file to transfer, which avoids scanning the whole folder/tmp/capture
(source) Transfer local folder content...<user>@<server>:/tmp/capture;
(destination) To the server "server"
sleep 1;
Sleep one second
done
Loop end
D. Broadcast from server to icecast
- You MUST install some script on <server> to assemble / concatenate the MPEGTS chunks for you.
This PHP streamer is made for that: https://raw.githubusercontent.com/albancrommer/raspistream/master/stream.php
- You MUST install ffmpeg on <server> with ogg support (see below)
- You MUST install the oggfwd command line tool with
aptitude install oggfwd
- You MUST have access to an icecast server. If you use a datacenter server, everything can run locally
php /usr/local/bin/stream.php | ffmpeg -i - -f ogg - | oggfwd -p -n "My RaspberryPi Stream" <stream.server.com> 8000 mySecretIceCastStreamingPassword /test
What's happening here
php /usr/local/bin/stream.php
Start an infinite stream of assembled chunks received via rsync| ffmpeg
Pipe into FFMPEG-i -
(input) DON'T CHANGE Use Standard In as input-f ogg
(format) DON'T CHANGE Use ogg as output format-
(output) DON'T CHANGE Output to Standard Out
| oggfwd
Pipe into oggfwd-p
(public) Makes the stream public. Tweak-n "My RaspberryPi Stream"
(name) Your stream name. Adapt<stream.server.com>
(address) Your icecast server name. Adapt8000
(port) 8000 is default for icecast. Adapt<mySecretIceCastStreamingPassword>
(password) The icecast input password Adapt/rpi01
(mountpoint) The icecast "mountpoint" ie. the path for your stream
E. Get the m3u from icecast
With the default parameters provided the stream would be accessed on
http://<stream.server.com>:8000/rpi01.m3u
Sources
http://sirlagz.net/2013/01/07/how-to-stream-a-webcam-from-the-raspberry-pi-part-3/
How to get full video from the small chunks
After the streaming you should have chunks both on the RaspberryPi and the server, and could perform the conversion on any of them.
Except that the RaspberryPi is VERY slow and that depending on your budget / stability needs you might not have kept all the chunks on the RaspberryPi.
In other words, make the conversion on the server, be it your laptop or a datacenter server.
A. Clean the last file (optional)
As our last chunk / fragment might be invalid, it's safer to remove it using :
ls /tmp/capture/*ts|tail -n 1|xargs rm
What's happening here
This command retrieves a sorted list of all chunks in the capture folder, extracts the last one and deletes it.
B. Convert to single file (mp4, webm)
It is assumed you have FFMPEG installed on the machine.
It is assumed you want to make minimal changes to your original video input (size, bitrate, etc). Only essential options are provided but you can add more according to your needs, double pass conversion is not included either.
It is recommanded to use a script for files merging, as ffmpeg syntax can be a bit of a mess for that, with little option if you want to use start or end file.
This PHP script is made for that : https://raw.githubusercontent.com/albancrommer/raspistream/master/concat.php
Converting to MP4
This operation can be fast as the MPEGTS chunks are ready for MP4
php concat.php <start> <end> | ffmpeg -i - -movflags +faststart -threads 0 -profile:v high -preset slow <myfile>.mp4
What's happening here
php concat.php
Start concatenation<start>
(optional) an integer designing the first file to include<end>
(optional) an integer designing the last file to include
| ffmpeg
Pipe into FFMPEG with following parameters-i -
(input) DON'T CHANGE use stdin as input-movflags +faststart
DON'T CHANGE Make the file ready for web viewing-threads 0
Require all CPU to work on the conversion. Tweak.-profile:v high
Set the output quality. Tweak.-preset slow
Set the encoding speed.Tweak.<myfile>.mp4
Your output file name. Adapt.
Converting to WEBM
This operation will be slower as audio and video tracks needs to use new codecs
php concat.php <start> <end> | ffmpeg -i - -codec:a libvorbis -codec:v libvpx -threads 0 -quality good -cpu-used 0 -qmin 10 -qmax 42 <myfile>.webm
What's happening here
php concat.php
Start concatenation<start>
(optional) an integer designing the first file to include<end>
(optional) an integer designing the last file to include
| ffmpeg
Pipe into FFMPEG with following parameters-i -
(input) DON'T CHANGE use stdin as input-codec:a libvorbis
(codec) DON'T CHANGE Define the audio codec-codec:v libvpx
(codec) DON'T CHANGE Define the video codec-threads 0
Require all CPU to work on the conversion. Tweak.-quality good
Set the encoding speed. Tweak-cpu-used 1
Set the encoding speed. Tweak-qmin 10 -qmax 42
Set the encoding quality. Tweak<myfile>.webm
Your output file name. Adapt
Sources
https://www.virag.si/2012/01/web-video-encoding-tutorial-with-ffmpeg-0-9/ https://www.virag.si/2012/01/webm-web-video-encoding-tutorial-with-ffmpeg-0-9/
Solution 2 : FLVSTR + PHP Streamer
Basic idea the Octopuce company has a solution to convert live MP4 to F4V. With an USB audio card, we could mux the MP4 and AAC audio and have a standalone solution.
CON authentification is hard, F4V means Flash, requires an USB disk for local backup
PRO the pi can be autonomous
First, authentification. This problem is adressed by solving encryption as well: we use an SSL socket to communicate with the server.
Solution 3 : RTSP
Basic idea Use an RTSP stream with VLC and the V4L driver
CON Non commercial RTSP server are not the norm, requires VLC or Flash player, Quality with v4l is low
PRO Easy to work out
Sources
http://www.ics.com/blog/raspberry-pi-camera-module#.VJFhbyvF-b8
http://ffmpeg.gusari.org/viewtopic.php?f=16&t=1130
Solution 4 : HLS + RSYNC
Basic idea Use HLS segmentation and rsync
CON Not all web players can do HLS
PRO Almost out of the box, robust
Howto
1. Compile fresh ffmpeg on the pi
2. Run a capture : raspivid -ih -pf baseline -t 0 -b 1000000 -w 1280 -h 720 -v -o - | ffmpeg -i - -f alsa -ac 1 -itsoffset 6.5 -i hw:1 -acodec aac -strict -2 -vcodec copy out.m3u8
3. Run a cron rsync to server (todo)
4. Connect a client (todo)
Sources
http://www.ffmpeg.org/ffmpeg-formats.html#hls
FFMPEG compilation
This installation is debian based. Some packages are included by default :
- ffmpeg : Provides a large number of the dependencies required at compilation tim
- yasm : modular assembler (good for compilation)
- pkg-config : info about installed libraries (good for compilation)
- screen : helpful for running compilation in background
For Raspberry
For the Raspberry, we only need the support of h264, AAC and ALSA
sudo -s aptitude install screen yasl libx264-dev libasound2-dev libfdk-aac-dev ffmpeg cd /usr/src git clone --depth 1 git://source.ffmpeg.org/ffmpeg.git cd ffmpeg ./configure --enable-gpl --enable-libx264 --enable-libfdk-aac make make install
For laptop or server
Your default debian might come with sufficent support but if you want total control, compiling is a good idea.
Remove packages and ffmpeg support if you don't need everything.
Ex: to produce ogg format, you only need
- aptitude packages libtheora-dev and libvorbis-dev
- configure options --enable-libtheora --enable-libvorbis
sudo aptitude update && aptitude install screen pkg-config yasm ffmpeg libass-dev libavcodec-extra libfdk-aac-dev libmp3lame-dev libopus-dev libtheora-dev libvorbis-dev libvpx-dev libx264-dev cd /usr/src git clone --depth 1 git://source.ffmpeg.org/ffmpeg.git cd ffmpeg ./configure --enable-gpl --enable-libass --enable-libfdk-aac --enable-libfreetype --enable-libmp3lame --enable-libopus --enable-libtheora --enable-libvorbis --enable-libvpx --enable-libx264 --enable-nonfree --enable-x11grab make make install
References
Here are a number of unsorted links
- http://techzany.com/2013/09/live-streaming-video-using-avconv-and-a-raspberry-pi/
- http://blog.cloudfrancois.fr/category/streaming-video.html
FFMPEG
- http://ffmpeg.org/ffmpeg-all.html#segment_002c-stream_005fsegment_002c-ssegment
- https://trac.ffmpeg.org/wiki/StreamingGuide
Node
- http://phoboslab.org/log/2013/09/html5-live-video-streaming-via-websockets
- https://github.com/phoboslab/jsmpeg
- https://github.com/fluent-ffmpeg/node-fluent-ffmpeg
Raspberry PI
- Raspbian, debian on Raspberry pi http://www.raspbian.org/
- Chose your SD card for your PI : http://elinux.org/RPi_SD_cards