Hello all. Prompted by a request from a customer, I've put together a PoC on using the Meraki API, a simple script, and FFMPEG to request snapshots from a camera every x seconds, and them convert the images into a video I'm going to use Pycharm as my dev environment 0. Create a new project in Pycharm So, to keep things simple, we are going to create a new project, using File > New project in Pycharm Once you've done this, create: a folder called ffmpeg a folder called snapshots a file called MV-Timelapse.py So, you should have : 1. Libraries We are going to use two libraries that need to be installed, a handful of included libraries, and the FFMPEG command line utility Open Terminal within Pycharm and type: pip install meraki
pip install ffmpeg-python Next, download FFMPEG from https://ffmpeg.org/download.html Move the resulting binary from within the .zip into the folder called ffmpeg within the Pycharm project NOTE: I have to CRTL and right click the file and run it first before I could use it, ignoring the prompt that it was from an unknown developer. Do this at your own risk. Whilst I've been using ffmpeg for as long as I can remember, there's always a risk 2. The script # this script is quite simple: It pulls a snapshot from a given camera,
# which is sent to the script via command line arguments
# it needs:
# Meraki API key
# Camera serial number
# 3rd party libraries needed
# pip install meraki
# pip install ffmpeg-python
# you also need to install the ffmpeg binary from https://ffmpeg.org/download.html
import meraki
import getopt, sys, requests, time, os, ffmpeg, datetime
output_directory = 'snapshots' # this is where you'll save your snapshots, relative to the script
periodBetweenShots = 5 # this is an integer in seconds. It should be NO less than 5 seconds
numbersOfSnapshots = 10 # this is the number of snapshots to be taken. No more than 998
pathtoFFMPEG = "/Users/pfidler/ffmpeg/ffmpeg"
def main(argv):
print("Meraki Library version: ")
print(meraki.__version__)
# validate that the script was called with the required paramaters and call printhelp() if not
try:
opts, args = getopt.getopt(argv, 'k:s:')
except getopt.GetoptError:
printhelp(argv)
sys.exit(2)
# assign command line params to variables
for opt, arg in opts:
if opt == '-k':
arg_apikey = arg
elif opt == '-s':
arg_camera_serial = arg
# Create Meraki Client Object and initialise
client = meraki.DashboardAPI(api_key=arg_apikey)
#loop
for a in range(numbersOfSnapshots):
#make API call to request snapshot
snapshot_response = client.camera.generateDeviceCameraSnapshot(serial=arg_camera_serial)
# get resulting URL from response
snappyURL = snapshot_response["url"]
#imagename = "image_" + datetime.datetime.now().strftime("%d%m%Y-%H:%M:%S") + ".jpeg"
# we are going to save the file in the format of xxx.jpeg
numwithzeros = str(a).zfill(3)
# append .jpeg to end of filename
imagename = numwithzeros + ".jpeg"
time.sleep(periodBetweenShots) # this is needed as the camera needs time to be told to take a snapshot
# download image to required location
filedownloader1(imagename, snappyURL)
# there's a tonne of customisation for ffmpeg, including video codecs and resolution.
# To keep things and understandable, I'm using the bareminimum
# %03d is a way of specifying images in the range of 000 to 999.jpeg
# testing has shown this to be:
# Stream #0:0: Video: h264 (avc1 / 0x31637661), yuvj420p(pc, bt470bg/unknown/unknown, progressive),
# 1920x1080, q=2-31, 25 fps, 12800 tbn
# file name format for video
timelapseVideoname = "timelapse_" + datetime.datetime.now().strftime("%d%m%Y-%H:%M:%S") + ".mp4"
(
ffmpeg
.input("snapshots/%03d.jpeg") # %03d
.output(timelapseVideoname)
.run(cmd=pathtoFFMPEG, capture_stdout=True)
#.run()
)
def filedownloader1(sent_name, sent_URL):
# this function receives the name of the file
# and users the 'output_directory' variable
# to save a file from a given URL
# Create a file path by joining the directory name with the desired file name
file_path = os.path.join(output_directory, sent_name)
# get the image from the URL
response = requests.get(sent_URL, allow_redirects=True)
# save the resulting file
open(file_path, "wb").write(response.content)
def printhelp(receivedArg):
# prints help information
print('# this script is quite simple: It pulls a snapshot from a given camera')
print('# which is sent to the script via command line arguments')
print('# it needs:')
print(' Meraki API key')
print(' Camera serial number)')
if __name__ == '__main__':
main(sys.argv[1:]) There's a lot to get through here, but it's actually quite a simple script. NOTE: There's no error checking in here, apologies Script Parameters output_directory = 'snapshots' # this is where you'll save your snapshots, relative to the script
periodBetweenShots = 5 # this is an integer in seconds. It should be NO less than 5 seconds
numbersOfSnapshots = 10 # this is the number of snapshots to be taken. No more than 998
pathtoFFMPEG = "/Users/pfidler/ffmpeg/ffmpeg" output_directory is where your snapshots will be saved and is relative to where the script is being run periodBetweenShots is the delay, in seconds, between each snapshot requested. As it takes a few seconds for the snapshot to be taken and uploaded to Meraki Dashboard, it should not be any less than 5 numbersOfSnapshots this is, well, the number of snapshots you wish to take pathToFFMPEG this is the FULL path to the FFMPEG binary Command Line Parameters In order to keep the script generic, we pass 2 parameters when calling the script: -k which is the Meraki API Key -s which is the serial number of the camera you want to get snapshots from 3. The structure The structure is really simple: Create a client using the Meraki API key Loop x times Request a snapshot: snapshot_response = client.camera.generateDeviceCameraSnapshot(serial=arg_camera_serial) Get the URL from the response Save the resulting image from the URL call ffmpeg to convert the snapshots into a video 4. Notes I created a function to save the image from a given URL: def filedownloader1(sent_name, sent_URL):
# this function receives the name of the file
# and users the 'output_directory' variable
# to save a file from a given URL
# Create a file path by joining the directory name with the desired file name
file_path = os.path.join(output_directory, sent_name)
# get the image from the URL
response = requests.get(sent_URL, allow_redirects=True)
# save the resulting file
open(file_path, "wb").write(response.content) The FFMPEG call is incredibly sparse: (
ffmpeg
.input("snapshots/%03d.jpeg") # %03d
.output(timelapseVideoname)
.run(cmd=pathtoFFMPEG, capture_stdout=True)
#.run()
) You can give it various parameters such as frame rate, resolution, codec, etc. I decided not to to keep the code simple, and, well, because the resulting video with the default parameters of: # Stream #0:0: Video: h264 (avc1 / 0x31637661), yuvj420p(pc, bt470bg/unknown/unknown, progressive), 1920x1080, q=2-31, 25 fps, 12800 tbn resulted in a decent enough video for testing 5. Running the script The script is called with: python3 MV-Timelapse.py -k YOURMERAKIAPIKEY -s 'XXXX-XXXX-XXXX' Where XXXX-XXXX-XXXX is the serial number of the camera in question You'll get some output like this: (venv) pfidler@PFIDLER-M-TWXW Meraki % python3 MV-Timelapse.py -k XXXX -s 'XXXX'
/Users/pfidler/Developer/Meraki/venv/lib/python3.9/site-packages/urllib3/__init__.py:35: NotOpenSSLWarning: urllib3 v2 only supports OpenSSL 1.1.1+, currently the 'ssl' module is compiled with 'LibreSSL 2.8.3'. See: https://github.com/urllib3/urllib3/issues/3020
warnings.warn(
Meraki Library version:
1.43.0
2024-03-26 10:50:25 meraki: INFO > Meraki dashboard API session initialized with these parameters: {'version': '1.43.0', 'api_key': '************************************5f92', 'base_url': 'https://api.meraki.com/api/v1', 'single_request_timeout': 60, 'certificate_path': '', 'requests_proxy': '', 'wait_on_rate_limit': True, 'nginx_429_retry_wait_time': 60, 'action_batch_retry_wait_time': 60, 'network_delete_retry_wait_time': 240, 'retry_4xx_error': False, 'retry_4xx_error_wait_time': 60, 'maximum_retries': 2, 'simulate': False, 'be_geo_id': None, 'caller': None, 'use_iterator_for_get_pages': False}
2024-03-26 10:50:25 meraki: INFO > POST https://api.meraki.com/api/v1/devices/XXXX/camera/generateSnapshot
2024-03-26 10:50:26 meraki: INFO > camera, generateDeviceCameraSnapshot - 202 Accepted
2024-03-26 10:50:32 meraki: INFO > POST https://api.meraki.com/api/v1/devices/XXXX/camera/generateSnapshot
2024-03-26 10:50:32 meraki: INFO > camera, generateDeviceCameraSnapshot - 202 Accepted And then when FFMPEG does its thing: And this results in a file called timelapse_<THE DATE AND TIME>.mp4 6. Caveats and references Don't wait to long to download the image from the URL: There's only a finite time before the link expires This code is given without warranty, support etc, but is free to use Using the FFMPEG Python Library: https://www.bannerbear.com/blog/how-to-use-ffmpeg-in-python-with-examples/#basic-usage-convert-video-format Combining images into a video file using FFMPEG: https://www.abyssale.com/generate-video/ffmpeg-combine-images-into-video Meraki API Generate Device Camera Snapshot: https://developer.cisco.com/meraki/api/generate-device-camera-snapshot/ If there' an errors or omissions, please DM me and I'll update the guide above.
... View more