Friday, November 29, 2013

Pulling H264 video from an IP camera using Python

These cameras record 1080p wide-angle video at 30 frames per second,
use power
over ethernet (PoE), can see when it's dark using builtin
infrared LEDs
and are weather-proof. The video quality is impressive and they are
surprisingly inexpensive. The camera can deliver two streams at once,
so you can pull a lower resolution stream for preview, motion
detection, etc., and simultaneously pull the higher resolution stream
to simply record it for later scrutinizing.

After buying a few of these cameras I needed a simple way to pull the
raw H264
video from them, and with some digging I discovered the cameras
speak RTSP
and RTP
which are standard protocols for streaming video and audio from IP
cameras. Many IP cameras have adopted these standards.

Both VLC
and MPlayer
can play RTSP/RTP video streams; for the Lorex cameras the default URL
is:

rtsp://admin:000000@<hostname>/PSIA/Streaming/channels/1.

After more digging I found the nice open-source
(LGPL
license) Live555
project, which is a C++ library for all sorts of media related
protocols, including RTSP, RTP and
RTCP. VLC
and MPlayer
use this library for their RTSP support. Perfect!

My C++ is a bit rusty, and I really don't understand all of Live555's
numerous APIs, but I managed to cobble together a simple Python
extension module, derived from
Live555's testRTSPClient.cpp example, that seems to work
well.

I've posted my
current source code in a new Google code project
named pylive555. It
provides a very simple API (only 3 functions!) to pull frames from a
remote camera via
RTSP/RTP; Live555 has
many, many other APIs that I haven't exposed.

I've included a
simple example.py
Python program, that shows how to load H264 video frames from the
camera and save them to a local file. You could start from this
example and modify it to do other things, for example use the
ffmpeg H264 codec to decode
individual frames, use a motion detection library to trigger recording,
parse each frame's metadata to find the keyframes, etc. Here's the current example.py:

I downloaded the live555 CPP library, then installed it and everything worked fine. Then I downloaded your python library, put the files inside my "live" folder, and when I run the "python3 setup.py build" it says that the file "liveMedia.hh" does not exist (even though it does) and then gives me an error. How can I fix that?

You hit that compilation error when running "python3 setup.py build"? That's strange: setup.py takes care of setting up the flags for the compiler. What compile lines are actually executed? Where is Python.h on your system?

I still can't fix it even though I have python-dev already. I don't know if it might be a compatibility issue between pylive555 and the newest version of live555 ( live.2014.02.04.tar.gz - 04-Feb-2014 <--- date)?Maybe you can email me your live555 version?

Also, let me make sure from the instructions:Download pylive555, then download live555 and put the folder "live" inside the pylive555 folder? Or is the other way around?

Hi Michael,Finally got it installed without any errors. Checked my RTSP string using VLC to my Grandstream camera before changing example.py. Get the video file created but unfortunately cannot open it with VLC.Below is a copy of 1st line from video file:tns1:AudioEncoder

Hmm, I'm able to play my file using mplayer. When I try to use VNC, it does have trouble, unless I force it to open a raw H264 file ... these instructions worked for me: https://forum.videolan.org/viewtopic.php?f=14&t=12530

I used mplayer and managed to play the files, unfortunately footage is nearly all green with some form of image in the background. At least now I know I am getting stream from the camera and will play a little more with the RTSP string as there are a number available for the Grandstream cameras.

I don't have any experience with Raspberry Pi nor Foscam, so I'm really not sure. This is only useful if the Foscam speaks RTSP/RTP, which I believe is more and more common in IP cameras these days. But I think the more typically approach with the Pi is to pull frames directly from an attached camera (not via an ethernet connection)? I.e.: http://www.raspberrypi.org/camera

This is definitely possible; I think there are python bindings around ffmpeg so you could go that route. In my usage, I just spawn a sub-process and invoke ffmpeg from the command line, something like:

ffmpeg -i - -vcodec copy -f mp4 video.mp4

Then I send the bytes to that pipe, close it, and video.mp4 holds the H264 in an MP4 container.

Dear friends, may I ask for help regarding this topic? I tried to run the example and it does something but after all the result file is 0byte sized... Do you think where could be the problem? The output of the script is below.

root@pb4540s:/home/karl/Downloads# python3 example.py 88.101.39.191 1 10 test12.avi[URL:"rtsp://admin:admin@88.101.39.191/cam/realmonitor?channel=1&subtype=0/"]: Initiated the "video/H264" subsession (client ports 49430-49431)[URL:"rtsp://admin:admin@88.101.39.191/cam/realmonitor?channel=1&subtype=0/"]: Set up the "video/H264" subsession (client ports 49430-49431)[URL:"rtsp://admin:admin@88.101.39.191/cam/realmonitor?channel=1&subtype=0/"]: Created a data sink for the "video/H264" subsession[URL:"rtsp://admin:admin@88.101.39.191/cam/realmonitor?channel=1&subtype=0/"]: Started playing session...

Had to add "BasicUsageEnvironment" to library_dirs in setup.py (I had unpacked the cpp/py outside of the live dir), otherwise works great. Now I need to wrap the data into a playable container... Thanks!

Thank you for your answer Mike. I was wondering if you could get me started so that I can run pylive555 with Python 2. I'm assuming that the reason I'm getting these errors is that when I #include Python.h, the Python 3 header file is what gets included and not the Python 2 header file. Do you know what changes I need to make (maybe to my system path?) in order for the Python 2 header to be the one included? And can you think of anything else that I need to do in order to make pylive555 work with Python 2?

I don't believe it to be possible to use this extension module with Python2 since many of the variables used in module.cpp are PyModuleDef variables which are only defined in Python3's moduleobject.h, but not in Python 2. I just thought I would give you an update of what I found and see if maybe you knew a way to get around this. These are the errors I get when I try to run python setup.py build:

Is it possible to connect to 2 different IP cameras simultaneously, each on a separate thread, using this module extension? And if so, how would you change the example.py code to do so? I tried to do it on a python script but I got a segmentation fault as soon as I tried to log in to the second camera.

P.S. When trying to find "vcvarsall.bat" with "msvc9compiler.py" from c:\Python35\Lib\distutils\ - everything is ok. So, I guess that it is YOUR program can't find it. Where is it looking for the file? Where should I put it or which part of what file should I change?

thanks for your inspiring idea. Based on your code I would like to decode the video frames with ffmpeg andthen render the images. I have the h264 decoder ffmpeg initialized and ready, however when I callffmpegs decoding function with the buffer (fReceiveBuffer) in

Mike.This is awesome !My question as a new comer to the field: The live555 seems to be a great C++ library. To build video streaming servers when would you use Python and when would you stay with C++.For a new comer what is the best learning path ?Thanks !Andy

ffmpeg takes care of "remembering" the last i frame and doing the delta decode for me ... I don't in any way see the deltas, because I just pull out a fully decoded bitmap on each frame. But maybe there are alternative ffmpeg APIs letting you see the deltas?

Hey there (I'm a friend of Winston). I got this running and love it. I am using https://github.com/mikemccand/pylive555 to connect to an rtsp stream exactly as you describe here.

However, I am noticing that there are only 3 methods exposed:- StartRTSP- runEventLoop- StopEventLoop

I'm then decoding using GStreamer (Live555 is faster at connecting than gstreamer's built in uri connector, plus provides better access to the raw data).

Question is--is there a way to connect to multiple RTSP endpoints and then disconnect from one? Or is this a 1-per process library (i.e., spin up a separate process to connect to another feed and the event loop must be started and killed for that camera 1:1 in its process?

Hey there, I'm reasonably ( 12 months) new to Python at any level of depth (used it 20 years ago, but that doesn't really count), so working on the cPython stuff was a little out of my league for starters.

Instead of exposing more, I ended up running the pylive555 libraries in a separate subprocess using Popen and then passing the data back and forth using a queue. In this way, each connection uses the global scheduler reasonably well, and can be started/stopped using the process methods (a little heavy-handed but it works for now).

I will take a look at the comments and if I get a little time on Friday, I'll take a look at working on a PR. Would love to be helpful and contribute if I can. The library is very solid--FFMpeg-level quality from what I can see and lightning fast when I've compared with others.

I've found single-process works pretty well too ... I was able to get to ~10 RTSP cameras in a single Python process on a wimpyish cpu (Intel(R) Core(TM) i5-2405S CPU @ 2.50GHz) before it seemed to saturate. (Plus, this app is doing a fair amount of python work besides just pulling bytes from the cameras).

Exposing a stopRTSP should be pretty simple ... just copy what I did for startRTSP, I think...

Mike, hey there. So, it turns out python's multiprocess library works great. MOST of the time. That said, it has been giving me a little bit of trouble working with FLASK, so I went ahead and I'm actually working on a pull request for you right now. https://github.com/daveselinger/pylive555/tree/add_stop_rtsp

CAVEAT: It has been almost 15-20 years since I've done any real C++ programming (had to google 'create array of object pointers in C++'), but it was kinda fun getting back into it.

I'll test this over the next little bit. If you have any initial feedback (like "Hey you're an idiot because of X, Y or Z") please feel free to share. Otherwise, I'll keep plugging along and will let you know once I've got it reasonably tested. Cheerio.

I can't run pylive555 in my system (Windows 8.1, Python 3.5 (Anaconda), VS 2012).I've read Leo's question, but I don't know how to solve my problem.I've browse in internet for my problem, for hours, but I don't get its solution, yet.Can you tell me how to solve my problem, in detail?

Hi! MichaelI need to grab IP camera h.264 encoded stream frame by frame.And then on each and every frame i want to do the followings1. Decode the frame 2. Hard Sub some data 3. Encode back 4. Save in avi container

Is there any way to grab the h.264 encoded frames from rtsp stream and then write on those frames and encode again ??Thanks Shivraj

Hi MichealBeginner here....Do you have a step by step install for this?I have only Lorex Cams here on the island and our controller got flooded by hurricane Irma. I want to setup a Ubuntu 16.04 system .. Please help

Subscribe To

About Me

Michael loves building software; he's been building search engines for more than a decade. In 1999 he co-founded iPhrase Technologies, a startup providing a user-centric enterprise search application, written primarily in Python and C. After IBM acquired iPhrase in 2005, Michael fell in love with Lucene, becoming a committer in 2006 and PMC member in 2008. Michael has remained an active committer, helping to push Lucene to new places in recent years. He's co-author of Lucene in Action, 2nd edition. In his spare time Michael enjoys building his own computers, writing software to control his house (mostly in Python), encoding videos and tinkering with all sorts of other things.