Friday, November 29, 2013

Pulling H264 video from an IP camera using Python

IP cameras have come a long ways, and recently I upgraded some old cameras to these new Lorex cameras (model LNB2151/LNB2153) and I'm very impressed.

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:


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.

The code is thread-friendly (releases the global interpreter lock when invoking the Live555 APIs).

I've included a simple 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

import time
import sys
import live555
import threading

# Shows how to use live555 module to pull frames from an RTSP/RTP
# source.  Run this (likely first customizing the URL below:

# Example: python3 1 10 out.264 
if len(sys.argv) != 5:
  print('Usage: python3 cameraIP channel seconds fileOut')
cameraIP = sys.argv[1]
channel = sys.argv[2]
seconds = float(sys.argv[3])
fileOut = sys.argv[4]

# NOTE: the username & password, and the URL path, will vary from one
# camera to another!  This URL path works with the Lorex LNB2153:
url = 'rtsp://admin:000000@%s/PSIA/Streaming/channels/%s' % (cameraIP, channel)

fOut = open(fileOut, 'wb')

def oneFrame(codecName, bytes, sec, usec, durUSec):
  print('frame for %s: %d bytes' % (codecName, len(bytes)))
  fOut.write(b'\0\0\0\1' + bytes)

# Starts pulling frames from the URL, with the provided callback:
useTCP = False
live555.startRTSP(url, oneFrame, useTCP)

# Run Live555's event loop in a background thread:
t = threading.Thread(target=live555.runEventLoop, args=())

endTime = time.time() + seconds
while time.time() < endTime:

# Tell Live555's event loop to stop:

# Wait for the background thread to finish:

Installation is very easy; see the README.txt. I've only tested on Linux with Python3.2 and with the Lorex LNB2151 cameras.

I'm planning on installing one of these Lorex cameras inside a bat house that I'll build with the kids this winter. If we're lucky we'll be able to view live bats in the summer!


  1. This is awesome. Great job. I have been using live555 with ctypes in Python. I like what you did, it's a better solution.

  2. This is one of the best security camera. I am also using ip camera.

  3. 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 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?

    Thanks for your help in advanced!

  4. well, I guess this error when I try to compile it with python3:

    module.cpp:22:20: fatal error: Python.h: No such file or directory

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

    Maybe you don't have the python-dev package installed?

  6. 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?

    Thank you!

    1. Hi Luis,

      Can you post the compile lines that is running? Somehow it's not finding your Python.h header.

      You had it right the first time: download/checkout pylive555, then download live555 and unpack it into the "live" sub-directory.

      My version of live555 is from 11/28/2013, but I don't think that's your problem (yet). You need to solve the missing Python.h header first.

  7. Hi Michael,

    Really looking forward to trying your code out but......

    I seem to be having the same problem with not finding python.h

    root@raspbx:~/live# python3 build
    running build
    running build_ext
    building 'live555' extension
    creating build
    creating build/temp.linux-armv6l-3.2
    gcc -pthread -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -D_FORTIFY_SOURCE =2 -g -fstack-protector --param=ssp-buffer-size=4 -Wformat -Werror=format-securi ty -fPIC -I./live/liveMedia/include -I./live/BasicUsageEnvironment/include -I./l ive/UsageEnvironment/include -I./live/groupsock/include -I/usr/include/python3.2 mu -c module.cpp -o build/temp.linux-armv6l-3.2/module.o
    cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for Ada/C/O bjC but not for C++ [enabled by default]
    module.cpp:22:20: fatal error: Python.h: No such file or directory
    compilation terminated.
    error: command 'gcc' failed with exit status 1

    Hope you can help.

    Thanks Gerry

    1. Hi Gerry,

      I think you need to install the python-dev package on your box?

  8. Michael,

    I did install python-dev but got same error. I think I have been playing around with some many different applications, it's best I do a clean install.

    Get back with my results later

  9. Hi Michael,

    I think I have misread your instructions somewhere as now I have a bigger problem.

    My directory structure is as follows:
    home/pi/pylive555 (here are readme.txt,, module.cpp, and live555-latest.tar.gz)
    including the other directories:- live, build and git

    I started from
    /pylive555/live $ ./genMakefiles linux
    /pylive555/live $ export CPPFLAGS=-fPIC CFLAGS=-fPIC
    /pylive555/live $ make install
    After many lines.....
    I got these errors
    install -d /usr/local/include/liveMedia /usr/local/lib
    install: cannot change permissions of ' /usr/local(include/liveMedia' : No such file or directory
    install: cannot change permissions of ' /usr/local/lib' : Operation not permitted
    make[1] : *** [install1] Error 1
    make[1] : Leaving directory ' /home/pi/pylive555/live/liveMedia'
    make: *** [install] Error 2
    back at /pylive555/live

    Not sure where to go from here.


    1. You should do "sudo make install", ie that step must be done as root. See if that works?

  10. If I run python3, I get in traceback ImportError: No module named live555

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

    Not sure what I have done wrong.


    1. 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:

      Try that?

  12. sorry but this blog has removed my copied text from video file

  13. 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.

  14. Could this be adapted to run on a Raspberry Pi and Foscam cameras?

    1. Hi Gilson,

      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.:

  15. Hi Michael,

    I understand that script produces a binary file with H264 frames...
    Is it possible to create some proper container like AVI or MP4 or MKV so the file is playable with media player?

    1. Hi Anton,

      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.

  16. Hi Michael,

    I can't solve this problem in raspberry pi env..
    please comment..

    pi@raspberrypi ~/pylive555 $ python3 1 10 out.264
    Traceback (most recent call last):
    File "", line 3, in
    import live555
    ImportError: /usr/local/lib/python3.2/dist-packages/ undefined symbol: _ZN18BasicTaskScheduler9createNewEj
    pi@raspberrypi ~/pylive555 $

    1. Something went wrong when you build the, because this symbol is defined in libBasicUsageEnvironment.a, which is one of the libraries we link against (in

      When I run "nm" in my build I can see that symbol is defined.

    2. Thanks for your quick reply..

      I run 'nm' command but that simbol is 'undefinded'

      pi@raspberrypi /usr/local/lib/python3.2/dist-packages $ nm | grep _ZN18BasicTaskScheduler9createNewEj
      U _ZN18BasicTaskScheduler9createNewEj

      what can i do?

    3. Can you remove your "build" subdirectory, run python3 build, and post the commands here? Somehow it's not linking properly against the live555 libs.

    4. pi@raspberrypi ~ $ cd pylive555
      pi@raspberrypi ~/pylive555 $ ls
      build live module.cpp README.txt
      pi@raspberrypi ~/pylive555 $ sudo rm -rf build
      pi@raspberrypi ~/pylive555 $ ls live module.cpp README.txt
      pi@raspberrypi ~/pylive555 $ python3 build
      running build
      running build_ext
      building 'live555' extension
      creating build
      creating build/temp.linux-armv6l-3.2
      gcc -pthread -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -D_FORTIFY_SOURCE=2 -g -fstack-protector --param=ssp-buffer-size=4 -Wformat -Werror=format-security -fPIC -I./live/liveMedia/include -I./live/BasicUsageEnvironment/include -I./live/UsageEnvironment/include -I./live/groupsock/include -I/usr/include/python3.2mu -c module.cpp -o build/temp.linux-armv6l-3.2/module.o
      cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for Ada/C/ObjC but not for C++ [enabled by default]
      creating build/lib.linux-armv6l-3.2
      g++ -pthread -shared -Wl,-O1 -Wl,-Bsymbolic-functions -Wl,-z,relro build/temp.linux-armv6l-3.2/module.o -L./live/liveMedia -L./live/UsageEnvironment -L./live/groupsock -lliveMedia -lgroupsock -lBasicUsageEnvironment -lUsageEnvironment -o build/lib.linux-armv6l-3.2/
      pi@raspberrypi ~/pylive555 $

    5. That looks correct; I can see in the last line that it's linking against BasicUsageEnvironment. Can you confirm you have a libBasicUsageEnvironment.a, and that it does in fact define that symbol?

    6. ok, I run this..

      pi@raspberrypi ~ $ sudo find / -name 'libBasicUsageEnvironment.a'

      and then open editor and search '_ZN18BasicTaskScheduler9createNewEj'.
      capture that result screen.

      pi@raspberrypi ~ $ sudo nano /home/pi/pylive555/live/BasicUsageEnvironment/libBasicUsageEnvironment.a

      pi@raspberrypi ~ $ sudo nano /usr/local/lib/libBasicUsageEnvironment.a

      pi@raspberrypi ~ $ sudo nano /usr/lib/libBasicUsageEnvironment.a

    7. so I rename '/usr/lib/libBasicUsageEnvironment.a' as /usr/lib/libBasicUsageEnvironment.a.bak.
      and then I run build command.
      Now runs successfully!

      Thank you for your kind and quick response

    8. Ahh, so you had an older libBasicUsageEnvironment.a in your path? Phew, thanks for bringing closure :) I'm glad you solved it.

    9. Adding this comment since Blogger's spam filters seem to have removed it:

      Hi Michael

      I have another problem.

      My ip camera is ATTN ops-bip720p like this (
      How can I find camera's rtsp url like example code (rtsp://admin:ecofence@%s/PSIA/Streaming/channels/%s)

      Buyer don't know about this and I can't find manufacturer ATTN web site or Technical Information.

      my question is that
      Where do you find rtsp url path of your camera Lorex LNB2153?

      Is it possible to get only from manufacturer?


    10. I don't know much about RTSP; perhaps the URL path that the camera must accept is part of the standard?

      In my case, after some Googling, I found documents that listed these paths for my camera ...

  17. 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 1 10 test12.avi
    [URL:"rtsp://admin:admin@"]: Initiated the "video/H264" subsession (client ports 49430-49431)
    [URL:"rtsp://admin:admin@"]: Set up the "video/H264" subsession (client ports 49430-49431)
    [URL:"rtsp://admin:admin@"]: Created a data sink for the "video/H264" subsession
    [URL:"rtsp://admin:admin@"]: Started playing session...

  18. Will this work with Lorex HR118000? the cameras don't have an IP just the DRV. Running Linux Mint.

  19. Sorry, I don't know anything about the Lorex HR118000.

  20. I get some problem linking: /usr/bin/ld: ./live/liveMedia/libliveMedia.a(Media.o): relocation R_X86_64_32S against `_ZTV6Medium' can not be used when making a shared object; recompile with -fPIC

    I look inside your code and uncommented extra_compile_args = ['-fPIC'], but not compiled.
    Thanks for share this piece of code with us though.

    1. Oh, did the -fPIC fix your issue or not? Or you hit different errors?

    2. Sorry, I had not seen the text 'export CPPFLAGS=-fPIC CFLAGS=-fPIC' in the README file. Worked after I exported these flags. Thanks.

  21. I'm getting this 'error' for this command on this camera. (AirCam Dome firmw v3.0.9)

    python3 1 10 asd.mp4
    [URL:"rtsp://"]: Initiated the "video/H264" subsession (client ports 52244-52245)
    [URL:"rtsp://"]: Failed to set up the "video/H264" subsession: 461 Unsupported Transport
    [URL:"rtsp://"]: Initiated the "audio/MPEG4-GENERIC" subsession (client ports 55122-55123)
    [URL:"rtsp://"]: Failed to set up the "audio/MPEG4-GENERIC" subsession: 461 Unsupported Transport
    [URL:"rtsp://"]: Failed to start playing session: No RTSP session is currently in progress

    1. I think you should ask on the live devel list?

  22. Hi Micheal, can you help me solve the following problem:

    running build
    running build_ext
    building 'live555' extension
    creating build/temp.linux-i686-2.7
    i686-linux-gnu-gcc -pthread -fno-strict-aliasing -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -fPIC -I./live/liveMedia/include -I./live/BasicUsageEnvironment/include -I./live/UsageEnvironment/include -I./live/groupsock/include -I/usr/include/python2.7 -c module.cpp -o build/temp.linux-i686-2.7/module.o
    cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++ [enabled by default]
    module.cpp:1180:27: error: variable ‘PyModuleDef module’ has initializer but incomplete type
    static struct PyModuleDef module = {
    module.cpp:1182:2: error: ‘PyModuleDef_HEAD_INIT’ was not declared in this scope
    module.cpp: In function ‘void PyInit_live555()’:
    module.cpp:1208:29: error: ‘PyModule_Create’ was not declared in this scope
    m = PyModule_Create(&module);
    In file included from /usr/include/wchar.h:51:0,
    from /usr/include/python2.7/unicodeobject.h:120,
    from /usr/include/python2.7/Python.h:85,
    from module.cpp:43:
    module.cpp:1212:10: error: return-statement with a value, in function returning 'void' [-fpermissive]
    return NULL;
    module.cpp:1218:56: warning: deprecated conversion from string constant to ‘char*’ [-Wwrite-strings]
    error = PyErr_NewException("live555.error", NULL, NULL);
    module.cpp:1234:9: error: return-statement with a value, in function returning 'void' [-fpermissive]
    return m;
    error: command 'i686-linux-gnu-gcc' failed with exit status 1

    1. Sorry, I'm not sure offhand; maybe this is a Python 2 vs 3 issue?