Live Streaming

Below you will find a few tips on how to do live video streaming with inexpensive consumer electronics.

What you need is:

  1. A DV-Camera. I use JVC GR-D720E
  2. A standard labtop with Ubuntu. I use an IBM ThinkPad T60 with a PCMCIA Fireward card.
  3. GSM modem. I have a 4 Mbit/s down and 512 Kbit/s up line
  4. An account at Amazon, AWS, to set up the streaming video server

The setup is:

  1. Connect the DV-Camera to your labtop
  2. Capture video from DV-Camera to a file with dvgrab
  3. Start a streaming video server at AWS
  4. `tail` content of video file through ffmpeg to ffserver running at the video server at AWS
  5. Serve a simple HTML page holding a flash player that gets it’s video input from the video server at AWS

Now, your video is going out to all the users on the Internet. With a limited budget this allows you to stream video of your favourite live event.

When I capture video I encode to MPEG2 right away with this command:

Grab video to file


`dvgrab -format dv1 - | ffmpeg -deinterlace -f dv -i - -target pal-dvd -aspect 16:9 video.mpg`

I encode to pal-dvd alias MPEG2 so I will be able to create a DVD after the event as well as streaming live. The `dvgrab` command above runs in a bash shell.

The streaming server you set up at AWS is a standard Ubuntu image including ffmpeg package with ffserver. The image with ffserver is in a S3 bucket so the streaming server can be started right away when needed. A small bash script, startStreamingServer.sh, has been created to start the streaming server in a new bash shell.

Below you will find references to FIRSTSERVER. The idea is that if bandwidth of the your AWS video server gets exhausted then you can start another server and let the first server feed the second server. Your HTML page should be adjusted users get video from all your servers in a round robin setup. This idea has not been fully implemented.

Script to start a video server at AWS


#! /bin/sh
# Script to start video server on Amazon EC2 server.
#
# If script fails try to run like this `bash ./startStreamingServer.sh`
#
# change log

# Are we starting the first server in the ec2 server farm?
ec2-describe-instances | grep running > /dev/null
if [ "$?" == "0" ]; then
	echo Not first ec2 server in ec2 server farm
	FIRSTSERVER=0
else
	echo First server in ec2 server farm
	FIRSTSERVER=1
fi

# Start new instance and grep instance id
INSTANCEID=`ec2-run-instances -v ami-28f31941 -k ec2-keypair | grep ^INSTANCE | awk '{print $2;}'`
echo Started instance ID: $INSTANCEID

# Get ip of new instance. Loop to allow new instance to come up.
INSTANCEIP=""
while [ true ];
do
	INSTANCEIP=`ec2-describe-instances $INSTANCEID | grep running | awk '{print $4;}'`
	if [ "$INSTANCEIP" == "" ]; then
		echo Sleep 10 seconds waiting for instance $INSTANCEID to come up!
		sleep 10
	else
		break
	fi
done
echo Started instance IP: $INSTANCEIP

# We have seen that if we scp to soon after ec2 server comes up scp fails
# Here we sleep 5 seconds to see if that helps!
sleep 5

# If first server then we must ask from where server gets its feed
# because we will put feed location into the ffserver configuration file that we copy to 
# server. That is, ACL for feed must be adjusted!
# We assume that feed location is local host. Call getip.php to get local host address.
FEEDIP=`wget -q -O - http://www.netmaster.dk/getip.php`
if [ "$FIRSTSERVER" == "1" ]
then
	read -e -p "Enter ip with video source. Enter to accept default ip - edit to change : " -i $FEEDIP FEEDIP
fi

# Create ffserver.conf based on ffserver.base.conf but with ACL adjusted.
sed "s/ACL allow 127.0.0.1/ACL allow $FEEDIP/g" ffserver.tps.conf > ffserver.conf

# Copy ffserver.conf to new instance.
echo Copy ffserver.conf to new instance.
scp -i ec2-keypair ffserver.conf ubuntu@$INSTANCEIP:/home/ubuntu

# Start ffserver in the background on instance.
ssh -i ec2-keypair ubuntu@$INSTANCEIP 'ffserver -f ffserver.conf &/dev/null &'
echo OK, ffserver runs on $INSTANCEIP!

# If this is our first ec2 server then we get feed for the server from outside of our
# ec2 server farm. Otherwise, we get feed for the server from another ec2 server in our ec2 
# server farm.

if [ "$FIRSTSERVER" == "1" ]; then
	echo Run this command to feed the ec2 server:
	echo tail -f NAME-OF-CAPTURE-MPG-FILE \| /HomeRolledVersion/ffmpeg -i - http://$INSTANCEIP:8090/feed1.ffm
	# This is our first server. Overwrite file with list of servers and add the server to the list.
	echo `date +%Y-%m-%d-%H:%M:%S` $INSTANCEID $INSTANCEIP > streamingservers
#else
#	echo Not first ec2 server in server farm
#	VIDEOSOURCEINSTANCEIP=`tail -n 1 streamingservers | awk '{print $3;}'`
#	# Redirection - see http://www.slac.stanford.edu/comp/unix/ssh.html
#	ssh -i ec2-keypair ubuntu@$INSTANCEIP "ffmpeg -i http://$VIDEOSOURCEINSTANCEIP:8090/test1.flv http://127.0.0.1:8090/feed1.ffm &/dev/null &"
#	echo `date +%Y-%m-%d-%H:%M:%S` $INSTANCEID $INSTANCEIP >> streamingservers
fi

echo .
echo Status for ffserver on ec2 server:
ssh -i ec2-keypair ubuntu@$INSTANCEIP 'netstat -lp'

echo .
echo Status for ffserver on ec2 server - copy to browser:
echo http://$INSTANCEIP:8090/stat.html

echo .
echo ssh against the server:
echo ssh -i ec2-keypair ubuntu@$INSTANCEIP

echo .
echo EC2 status for Amazon server farm - copy to browser:
echo http://aws.amazon.com/ 
echo .

Below you see the output from startStreamingServer.sh.

Script output


First server in ec2 server farm
Started instance ID: i-2f9cd541
Sleep 10 seconds waiting for instance i-2f9cd541 to come up!
Sleep 10 seconds waiting for instance i-2f9cd541 to come up!
Sleep 10 seconds waiting for instance i-2f9cd541 to come up!
Started instance IP: ec2-50-16-57-108.compute-1.amazonaws.com
Enter ip with video source. Enter to accept default ip - edit to change : 217.157.54.18
Copy ffserver.conf to new instance.
The authenticity of host 'ec2-50-16-57-108.compute-1.amazonaws.com (50.16.57.108)' can't be established.
RSA key fingerprint is fc:95:c0:d5.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'ec2-50-16-57-108.compute-1.amazonaws.com,50.16.57.108' (RSA) to the list of known hosts.
ffserver.conf                                                                                                                             100% 1366     1.3KB/s   00:00    
OK, ffserver runs on ec2-50-16-57-108.compute-1.amazonaws.com!
Run this command to feed the ec2 server:
tail -f NAME-OF-CAPTURE-MPG-FILE | /HomeRolledVersion/ffmpeg -r 25 -i - http://ec2-50-16-57-108.compute-1.amazonaws.com:8090/feed1.ffm
.
Status for ffserver on ec2 server:
(Not all processes could be identified, non-owned process info
 will not be shown, you would have to be root to see it all.)
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 *:ssh                   *:*                     LISTEN      -               
tcp        0      0 *:8090                  *:*                     LISTEN      723/ffserver    
tcp6       0      0 [::]:ssh                [::]:*                  LISTEN      -               
udp        0      0 *:bootpc                *:*                                 -               
Active UNIX domain sockets (only servers)
Proto RefCnt Flags       Type       State         I-Node   PID/Program name    Path
unix  2      [ ACC ]     STREAM     LISTENING     1407     -                   @/com/ubuntu/upstart
unix  2      [ ACC ]     STREAM     LISTENING     4010     -                   /var/run/dbus/system_bus_socket
.
Status for ffserver on ec2 server - copy to browser:
http://ec2-50-16-57-108.compute-1.amazonaws.com:8090/stat.html
.
ssh against the server:
ssh -i ec2-keypair ubuntu@ec2-50-16-57-108.compute-1.amazonaws.com
.
EC2 status for Amazon server farm - copy to browser:
http://aws.amazon.com/

Now that the streaming server is up and running you are ready to send your video file to the server. Use this command supplied above in the output from startStreamingServer.sh

Send video to video server


`tail -f video.mpg | ffmpeg -r 25 -i - http://ec2-67-202-42-9.compute-1.amazonaws.com:8090/feed1.ffm`

The last piece in the puzzle is a HTML page with a flash player that reads video from the streaming server. The HTML page is stored on a public web server. I have this HTML:

HTML to server video


<script src='AC_RunActiveContent.js' language='javascript'></script>
<script language='javascript'>
AC_FL_RunContent('codebase', 'http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,0,0', 'width', '520', 'height', '390', 'src', ((!DetectFlashVer(9, 0, 0) && DetectFlashVer(8, 0, 0)) ? 'player8' : 'player'), 'pluginspage', 'http://www.macromedia.com/go/getflashplayer', 'id', 'flvPlayer', 'allowFullScreen', 'true', 'movie', ((!DetectFlashVer(9, 0, 0) && DetectFlashVer(8, 0, 0)) ? 'player8' : 'player'), 'FlashVars', 'movie=http://ec2-184-73-150-138.compute-1.amazonaws.com:8090/test1.flv&bgcolor=0x051615&fgcolor=0x13ABEC&volume=&autoplay=off&autoload=on&autorewind=on&clickurl=&clicktarget=&postimage=');
</script>
<noscript>
<object width='520' height='390' id='flvPlayer'>
<param name='allowFullScreen' value='true'>
<param name='movie' value='player.swf?movie=http://ec2-184-73-150-138.compute-1.amazonaws.com:8090/test1.flv&bgcolor=0x051615&fgcolor=0x13ABEC&volume=&autoplay=off&autoload=on&autorewind=on&clickurl=&clicktarget=&postimage='>
<embed src='player.swf?movie=http://ec2-184-73-150-138.compute-1.amazonaws.com:8090/test1.flv&bgcolor=0x051615&fgcolor=0x13ABEC&volume=&autoplay=off&autoload=on&autorewind=on&clickurl=&clicktarget=&postimage=' width='520' height='390' allowFullScreen='true' type='application/x-shockwave-flash'>
</object>
</noscript>

Now, a user on the Internet is able to browse to your HTML page and see live video.

Thanks

Thomas S