title: How to efficiently read and save video from an IP camera?
body: <p>I have a python script I use to grab images from an ip camera through my home network and add date time information. In a 12 hour period it grabs about 200,000 pictures. But when using <a href="http://www.zoneminder.com/" rel="nofollow">zoneminder</a> (camera monitoring software) the camera manages 250,000 in a 7 hour period.</p>
  
  <p>I was wondering if anyone could help me improve my script efficiency I have tried using the threading module to create 2 threads but it has not helped i am not sure if I have implemented it wrong or not. Below is code I am currently using:</p>
  
  <pre><code>#!/usr/bin/env python
  
  # My First python script to grab images from an ip camera
  
  import requests
  import time
  import urllib2
  import sys
  import os
  import PIL
  from PIL import ImageFont
  from PIL import Image
  from PIL import ImageDraw
  import datetime
  from datetime import datetime
  import threading
  
  timecount = 43200
  lock = threading.Lock()
  
  wdir = "/workdir/"
  
  y = len([f for f in os.listdir(wdir) 
       if f.startswith('Cam1') and os.path.isfile(os.path.join(wdir, f))])
  
  def looper(timeCount):
     global y
     start = time.time()
     keepLooping = True
     while keepLooping:
      with lock:
          y += 1
      now = datetime.now()
      dte = str(now.day) + ":" +  str(now.month) + ":" + str(now.year)
      dte1 = str(now.hour) + ":" + str(now.minute) + ":" + str(now.second) + "." + str(now.microsecond)
      cname = "Cam1:"
      dnow = """Date: %s """ % (dte)
      dnow1 = """Time: %s""" % (dte1)
      buffer = urllib2.urlopen('http://(ip address)/snapshot.cgi?user=uname&amp;pwd=password').read()
      img = str(wdir) + "Cam1-" + str('%010d' % y) + ".jpg"
      f = open(img, 'wb')
      f.write(buffer) 
      f.close()
      if time.time()-start &gt; timeCount:
             keepLooping = False
      font = ImageFont.truetype("/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf",10)
      img=Image.open(img)
      draw = ImageDraw.Draw(img)
      draw.text((0, 0),cname,fill="white",font=font)
      draw.text((0, 10),dnow,fill="white",font=font)
      draw.text((0, 20),dnow1,fill="white",font=font)
      draw = ImageDraw.Draw(img)
      draw = ImageDraw.Draw(img)
      img.save(str(wdir) + "Cam1-" + str('%010d' % y) + ".jpg")
  
  for i in range(2):
          thread = threading.Thread(target=looper,args=(timecount,))
          thread.start()
          thread.join()
  </code></pre>
  
  <p>how could i improve this script or how do i open a stream from the camera then grab images from the stream? would that even increase the efficiency / capture rate?</p>
  
  <p><strong>Edit:</strong></p>
  
  <p>Thanks to kobejohn's help i have come up with the following implementation. running for a 12 hour period it has gotten over 420,000 pictures from 2 seperate cameras (at the same tme) each running on their own thread at the same time compared to about 200,000 from my origional implementation above. The following code will run 2 camera's in parallel (or close enough to it) and add text to them:</p>
  
  <pre><code>import base64
  from datetime import datetime
  import httplib
  import io
  import os
  import time
  
  from PIL import ImageFont
  from PIL import Image
  from PIL import ImageDraw
  
  import multiprocessing
  
  wdir = "/workdir/"
  stream_urlA = '192.168.3.21'
  stream_urlB = '192.168.3.23'
  usernameA = ''
  usernameB = ''
  password = ''
  
  y = sum(1 for f in os.listdir(wdir) if f.startswith('CamA') and os.path.isfile(os.path.join(wdir, f)))
  x = sum(1 for f in os.listdir(wdir) if f.startswith('CamB') and os.path.isfile(os.path.join(wdir, f)))
  
  def main():
      time_count = 43200
  #    time_count = 1
      procs = list()
      for i in range(1):
          p = multiprocessing.Process(target=CameraA, args=(time_count, y,))
          q = multiprocessing.Process(target=CameraB, args=(time_count, x,))
          procs.append(p)
          procs.append(q)
          p.start()
          q.start()
      for p in procs:
          p.join()
  
  def CameraA(time_count, y):
      y = y
      h = httplib.HTTP(stream_urlA)
      h.putrequest('GET', '/videostream.cgi')
      h.putheader('Authorization', 'Basic %s' % base64.encodestring('%s:%s' % (usernameA, password))[:-1])
      h.endheaders()
      errcode, errmsg, headers = h.getreply()
      stream_file = h.getfile()
      start = time.time()
      end = start + time_count
      while time.time() &lt;= end:
      y += 1
          now = datetime.now()
          dte = str(now.day) + "-" + str(now.month) + "-" + str(now.year)
          dte1 = str(now.hour) + ":" + str(now.minute) + ":" + str(now.second) + "." + str(now.microsecond)
          cname = "Cam#: CamA"
          dnow = """Date: %s """ % dte
          dnow1 = """Time: %s""" % dte1
          # your camera may have a different streaming format
          # but I think you can figure it out from the debug style below
          source_name = stream_file.readline()    # '--ipcamera'
          content_type = stream_file.readline()    # 'Content-Type: image/jpeg'
          content_length = stream_file.readline()   # 'Content-Length: 19565'
          #print 'confirm/adjust content (source?): ' + source_name
          #print 'confirm/adjust content (type?): ' + content_type
          #print 'confirm/adjust content (length?): ' + content_length
          # find the beginning of the jpeg data BEFORE pulling the jpeg framesize
          # there must be a more efficient way, but hopefully this is not too bad
          b1 = b2 = b''
          while True:
              b1 = stream_file.read(1)
              while b1 != chr(0xff):
                  b1 = stream_file.read(1)
              b2 = stream_file.read(1)
              if b2 == chr(0xd8):
                  break
          # pull the jpeg data
          framesize = int(content_length[16:])
          jpeg_stripped = b''.join((b1, b2, stream_file.read(framesize - 2)))
          # throw away the remaining stream data. Sorry I have no idea what it is
          junk_for_now = stream_file.readline()
          # convert directly to an Image instead of saving / reopening
          # thanks to SO: http://stackoverflow.com/a/12020860/377366
          image_as_file = io.BytesIO(jpeg_stripped)
          image_as_pil = Image.open(image_as_file)
          draw = ImageDraw.Draw(image_as_pil)
          draw.text((0, 0), cname, fill="white")
          draw.text((0, 10), dnow, fill="white")
          draw.text((0, 20), dnow1, fill="white")
          img_name = "CamA-" + str('%010d' % y) + ".jpg"
          img_path = os.path.join(wdir, img_name)
          image_as_pil.save(img_path)
  
  def CameraB(time_count, x):
      x = x
      h = httplib.HTTP(stream_urlB)
      h.putrequest('GET', '/videostream.cgi')
      h.putheader('Authorization', 'Basic %s' % base64.encodestring('%s:%s' % (usernameB, password))[:-1])
      h.endheaders()
      errcode, errmsg, headers = h.getreply()
      stream_file = h.getfile()
      start = time.time()
      end = start + time_count
      while time.time() &lt;= end:
      x += 1
          now = datetime.now()
          dte = str(now.day) + "-" + str(now.month) + "-" + str(now.year)
          dte1 = str(now.hour) + ":" + str(now.minute) + ":" + str(now.second) + "." + str(now.microsecond)
          cname = "Cam#: CamB"
          dnow = """Date: %s """ % dte
          dnow1 = """Time: %s""" % dte1
          # your camera may have a different streaming format
          # but I think you can figure it out from the debug style below
          source_name = stream_file.readline()    # '--ipcamera'
          content_type = stream_file.readline()    # 'Content-Type: image/jpeg'
          content_length = stream_file.readline()   # 'Content-Length: 19565'
          #print 'confirm/adjust content (source?): ' + source_name
          #print 'confirm/adjust content (type?): ' + content_type
          #print 'confirm/adjust content (length?): ' + content_length
          # find the beginning of the jpeg data BEFORE pulling the jpeg framesize
          # there must be a more efficient way, but hopefully this is not too bad
          b1 = b2 = b''
          while True:
              b1 = stream_file.read(1)
              while b1 != chr(0xff):
                  b1 = stream_file.read(1)
              b2 = stream_file.read(1)
              if b2 == chr(0xd8):
                  break
          # pull the jpeg data
          framesize = int(content_length[16:])
          jpeg_stripped = b''.join((b1, b2, stream_file.read(framesize - 2)))
          # throw away the remaining stream data. Sorry I have no idea what it is
          junk_for_now = stream_file.readline()
          # convert directly to an Image instead of saving / reopening
          # thanks to SO: http://stackoverflow.com/a/12020860/377366
          image_as_file = io.BytesIO(jpeg_stripped)
          image_as_pil = Image.open(image_as_file)
          draw = ImageDraw.Draw(image_as_pil)
          draw.text((0, 0), cname, fill="white")
          draw.text((0, 10), dnow, fill="white")
          draw.text((0, 20), dnow1, fill="white")
          img_name = "CamB-" + str('%010d' % x) + ".jpg"
          img_path = os.path.join(wdir, img_name)
          image_as_pil.save(img_path)
  
  if __name__ == '__main__':
      main()
  </code></pre>
  
  <p><strong>EDIT (26/05/2014):</strong></p>
  
  <p>I have spent the better part of 2 months trying to update this script / program to work with python 3 but have been completely unable to get it to do anything. would anyone be able to point me in the right direction?</p>
  
  <p>I have tried the 2to3 script but it just changed a couple of entries and I still was unable to get it to function at all.</p>