I spent whole day to implement this code. I think there will be many people who would like to use Raspberry Pi as a IP Camera recorder so I am sharing it here.
This is a shell script that uses GStream 1.0 library, which is not an official Wheezy dist yet. The reason why I am using it is that it was only one way to use hardware accelerated H.264 encoder. Currently only gstream-0.10 is officially available not 1.0 yet.
If you are not comfortable using unverified package, you can also use VLC without hardware acceleration. I haven't tested VLC way thoroughly but it shouldn't be too hard for anybody to use it.
Also my script uses "wget" to retrieve some information from Foscam IP Camera such as "alias name" and "alarm_upload_interval". If you don't want to install wget and you know those values for sure, you can simply modify the script and hard-code it.
#!/bin/sh if [ ! $# = 3 ]; then echo "*** input argument error: IP ID PW ***" exit 1 fi IP=$1 ID=$2 PW=$3 record_basedir=/home/pi/camera file_ext=.mkv WGET=/usr/bin/wget GREP=/bin/grep CUT=/usr/bin/cut MKDIR=/bin/mkdir DATE=/bin/date GSTLAUNCH=/usr/bin/gst-launch-1.0 if [ ! -x $WGET -o ! -x $GREP -o ! -x $CUT -o ! -x $MKDIR -o ! -x $DATE -o ! -x $GSTLAUNCH ]; then echo "*** some of utilities not found ***" exit 2 fi alias_name=`$WGET -q -S -O - http://$ID\@$IP/get_params.cgi\?user=$ID\&pwd=$PW 2> /dev/null | $GREP var\ alias= | $CUT -d"'" -f 2` if [ "$alias_name" = "" ]; then echo "*** alias_name not found ***" exit 3 fi record_dir=$record_basedir/$alias_name if [ ! -d $record_dir ]; then $MKDIR -p $record_dir fi if [ ! -d $record_dir ]; then echo "*** Cannot make a folder: $record_dir ***" exit 4 fi alarm_upload_interval=`$WGET -q -S -O - http://$ID\@$IP/get_params.cgi\?user=$ID\&pwd=$PW 2> /dev/null | $GREP var\ alarm_upload_interval= | $CUT -d"=" -f 2 | $CUT -d";" -f 1` echo alarm_upload_interval=$alarm_upload_interval timestamp=`$DATE +%Y_%m_%d-%H_%M_%S` file_name=$timestamp$file_ext fullpath=$record_dir/$file_name echo file_name=$file_name echo fullpath=$fullpath $GSTLAUNCH souphttpsrc location="http://$ID\@$IP/videostream.asf\?user=$ID\&pwd=$PW" ! decodebin ! videoconvert ! omxh264enc ! "video/x-h264,profile=high" ! h264parse ! matroskamux ! filesink location=$fullpath 2> /dev/null 1> /dev/null echo pid=$!
The last line is the core of the script. The logic is that it retrieve ASF video streaming data from Foscam IP Camera with "souphttpsrc". We need to decode the video with "decodebin". Then it becomes "raw video". The raw video is piped into "videoconvert". Now it is passed to the hardware accelerated H.264 encoder, "omxh264enc". I don't know about "h264parse" but without it, it didn't go through so you need the step as well. Then the encoded H.264 video is stored as MKV with "matroskamux" plug-in. Finally the file name for the output data is specified with "filesink" plug-in.
I found that two steps, souphttpsrc and decodebin, can be merged with "uridecodebin". It seems that uridecodebin can handle "buffering" feature and probably it would work better in different cases.
Another thing I want to mention is that stderr of gst-launch-1.0 should be redirected to /dev/null. Otherwise, it will make the script process "defunct". I think a child process of gst-launch-1.0 is holding the stderr of the parents' and it causes defunct processors when those parents are dead.
In order to install gstream1.0, you will need to follow these steps:
Then you can verify that you have omxh264enc, which is the most important one, by this command:$ echo "deb http://vontaene.de/raspbian-updates/ . main" >> /etc/apt/sources.list $ apt-get update $ apt-get install libgstreamer1.0-0 libgstreamer1.0-0-dbg libgstreamer1.0-dev liborc-0.4-0 liborc-0.4-0-dbg liborc-0.4-dev liborc-0.4-doc gir1.2-gst-plugins-base-1.0 gir1.2-gstreamer-1.0 gstreamer1.0-alsa gstreamer1.0-doc gstreamer1.0-omx gstreamer1.0-plugins-bad gstreamer1.0-plugins-bad-dbg gstreamer1.0-plugins-bad-doc gstreamer1.0-plugins-base gstreamer1.0-plugins-base-apps gstreamer1.0-plugins-base-dbg gstreamer1.0-plugins-base-doc gstreamer1.0-plugins-good gstreamer1.0-plugins-good-dbg gstreamer1.0-plugins-good-doc gstreamer1.0-plugins-ugly gstreamer1.0-plugins-ugly-dbg gstreamer1.0-plugins-ugly-doc gstreamer1.0-pulseaudio gstreamer1.0-tools gstreamer1.0-x libgstreamer-plugins-bad1.0-0 libgstreamer-plugins-bad1.0-dev libgstreamer-plugins-base1.0-0 libgstreamer-plugins-base1.0-dev
Now in case you want to use VLC, you can use this command:$ gst-inspect-1.0 | grep omxh264enc omx: omxh264enc: OpenMAX H.264 Video Encoder
I think it will be better to do -Irc without "--run-time=10 -Idummy" and control the time with remote control process. But I didn't go deep into the step.vlc http://$ID\@$IP/videostream.asf\?user=$ID\&pwd=$PW\&res=8\&rate=6 --run-time=10 -Idummy --sout=#transcode{vcodec=h264}:standard{dst="a.mp4"} vlc://quit
As a reference of GStream, I found this page the most useful.
With more tweaks, I will be able to append time stamp on the corner of the video and I will also be able to include audio. But I haven't been gone that far yet.
17 comments:
I figured how to add audio. I wasn't sure which encoder can give me the best compression but it didn't seem like I had many choices.
$GSTLAUNCH souphttpsrc location="http://$ID\@$IP/videostream.asf\?user=$ID\&pwd=$PW" ! decodebin name=decoder \
decoder. ! videoconvert ! omxh264enc ! "video/x-h264,profile=high" ! h264parse \
! matroskamux name=mux ! filesink location=$fullpath \
decoder. ! audioconvert ! lamemp3enc ! mux.
I figured that a plug-in, "clockoverlay", gives me a nice clock on the side.
It should be placed right before videoconvert plug-in.
It will be looking like this:
$GSTLAUNCH souphttpsrc location="http://$ID\@$IP/videostream.asf\?user=$ID\&pwd=$PW" ! decodebin name=decoder \
decoder. ! clockoverlay ! videoconvert ! omxh264enc ! "video/x-h264,profile=high" ! h264parse \
! matroskamux name=mux ! filesink location=$fullpath \
decoder. ! audioconvert ! lamemp3enc ! mux. 2> /dev/null 1> /dev/null
Hi, quite cool! will try your method and see how it works.
Awesome post. I'll try this.
Do you think its possible to setup more than one camera recording on the Pi ?
Regards
Fabio
I think so but I haven't had a chance to try. lol
Hi, I tried your command but gst-launcher-1.0 will get stuck and the output file seems to be empty. Do you know anything I should tweak? I'm using 1.0.8
pi@raspberrypi ~/video $ gst-launch-1.0 --version
gst-launch-1.0 version 1.0.9
GStreamer 1.0.9
http://packages.qa.debian.org/gstreamer1.0
I hope u have sorted it out by now. I have no further info about gstreamer. It is not the part of official port so I guess we will have to suffer from non standard builds.
Is there a way of limiting recording time to, let's say, 10 seconds and upload the resulting file to box.net?
If you use VLC, it is very easy to limit the recording time.
vlc http://$ID\@$IP/videostream.asf\?user=$ID\&pwd=$PW\&res=8\&rate=6 --run-time=10 -Idummy --sout=#transcode{vcodec=h264}:standard{dst="a.mp4"} vlc://quit
Hi Jay,
Trying to use VLC got a few interface errors that are probably fine (Failed to connect to the D-Bus session daemon: Unable to autolaunch a dbus-daemon without a $DISPLAY for X11).
But it fails with:
mux_mp4 mux error: unsupported codec ms in mp4
It works with vcodec=mp4v, but image quality is not good. What muxer should I use with h264?
Interesting.. I think I had the issue but I can barely remember. I think I had to download and install something manually in order to use h264 in VLC. I couldn't get it working with H/W encoder for h264 in VLC. I am not sure why you have quality issue with mp4 tho. You may want to check out VLC options for mp4 visual quality.
Going back to the original issue, wouldn't it be possible to limit recording time with GStream?
Could not limit the time length, but number of frames can be set with num-buffers. If set like num-buffers=2000 it will record about 10 seconds of video. Now the problem is that the script won't end, a CTRL+C is needed to return to the prompt.
Hi Jay,
Just in case anyone is trying to do the same thing, found a way of setting recording time length using Python with the help of the GStreamer-devel group at http://gstreamer-devel.966125.n4.nabble.com/timeout-not-working-td4665437.html#a4665445
Thanks!
Hi Gilson/Jay,
I tried the python version and cannot get it to work. My files collect and the size grows with the length of recording. I cannot play the file on any player. Any help you could provide would be greatly appreciated...
When I use
#!/usr/bin/python3
I receive:
Traceback (most recent call last):
File "./VideoCapture2.py", line 3, in
import gi
So I changed to:
#!/usr/bin/python
Now, I receive the errors after the timeout period:
Traceback (most recent call last):
File "/usr/lib/python2.7/dist-packages/gi/overrides/GLib.py", line 629, in
return (lambda data: callback(*data), user_data)
File "./VideoCapture2.py", line 27, in _timeout
sink.send_event(Gst.Event.new_eos())
AttributeError: 'NoneType' object has no attribute 'send_event'
My file script is:
#!/usr/bin/python
import gi
gi.require_version('Gst', '1.0')
from gi.repository import GObject, Gst
GObject.threads_init()
Gst.init(None)
class Encode:
def __init__(self):
self.mainloop = GObject.MainLoop()
self.pipeline = Gst.parse_launch('souphttpsrc location=http://192.168.xxx.xxx/videostream.asf?user=aaaa&pwd=bbbb! decodebin ! videoconvert ! omxh264enc ! video/x-h264,profile=high ! h264parse ! matroskamux ! filesink location=/home/pi/video.mkv')
self.bus = self.pipeline.get_bus()
self.bus.add_signal_watch()
self.bus.connect('message::eos', self.on_eos)
self.bus.connect('message::error', self.on_error)
def run(self):
self.pipeline.set_state(Gst.State.PLAYING)
tid = GObject.timeout_add(10000, self._timeout)
self.mainloop.run()
def _timeout(self):
print('timeout')
#self.pipeline.send_event(Gst.Event.new_eos()
sink = self.pipeline.get_by_name('matroskamux')
sink.send_event(Gst.Event.new_eos())
def kill(self):
self.pipeline.set_state(Gst.State.NULL)
self.mainloop.quit()
def on_eos(self, bus, msg):
print('on_eos()')
self.kill()
def on_error(self, bus, msg):
print('on_error():', msg.parse_error())
self.kill()
encode = Encode()
encode.run()
This blog post is very informative. wireless burglar alarm is best way to protect your home and family from thieves and burglaries. It easy to install and cost effective. You can monitor your home premises 24/7 day and night from anywhere from your smartphone. It fills all your security needs.
Post a Comment