-
-
Save mwvent/c9adf1f0f4466ed2a188965565e1fcc1 to your computer and use it in GitHub Desktop.
Play Top Zoneminder Recordings as a loopback video device
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
future==0.18.2 | |
mysql-connector==2.2.9 | |
pkg-resources==0.0.0 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
cd "$(dirname "$0")" | |
source ./bin/activate | |
modprobe v4l2loopback | |
#while : ; do | |
nice -n 19 python zmReviewLoopBack3.py | |
sleep 5 | |
#done |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import mysql.connector | |
import os | |
import ffmpeg | |
import subprocess | |
import threading | |
from datetime import timedelta | |
from datetime import datetime | |
from subprocess import Popen, PIPE | |
import time | |
# Settings | |
tempvidsfolder = "/mnt/HDD2/cctvreplayvids" | |
dbHost = "localhost" #MySql Zoneminder Database server | |
dbUser = "zmuser" #Mysql Zoneminder Database user ( TODO load from /etc/zm ) | |
dbPass = "zmpass" #MySql Zoneminder Database pass ( TODO load from /etc/zm ) | |
# Output Thread - ffmpeg subprocess accepting RAW video and outputting to v4l2 loopback | |
# TODO Detect v4l2 loopback device instead of hard coded video0 | |
output_proc_cmd = [ "ffmpeg" , | |
"-v", "error" , | |
"-f", "rawvideo" , | |
"-s", "640x480" , | |
"-pix_fmt", "yuv420p" , | |
"-r", "10", "-re", | |
"-i", "-" , | |
"-vcodec", "rawvideo" , | |
"-pix_fmt", "yuv420p" , | |
"-vf", "scale=640x480", | |
"-threads", "0" , | |
"-f", "v4l2" , | |
"/dev/video0" | |
] | |
output_proc = subprocess.Popen(output_proc_cmd, stdin=PIPE) | |
# Processing thread - keep tempvidsfolder up to date with current videos | |
def processVideos() : | |
db = mysql.connector.connect(host=dbHost, user=dbUser, passwd=dbPass, database="zm", autocommit=True) | |
while True : | |
SQL = """SELECT | |
Frames.Delta, | |
Concat( Path, '/', MonitorId, '/', Date(StartTime), '/', Events.Id, '/', Events.Id, '-video.mp4') , | |
StartTime | |
FROM Events | |
INNER JOIN | |
Storage ON StorageId=Storage.Id | |
INNER JOIN | |
Frames ON Frames.EventId = Events.Id | |
WHERE | |
Frames.Type='Alarm' AND | |
( | |
( Notes LIKE '%HasPerson: 1%' AND (MonitorId = 11 OR MonitorId = 12 OR MonitorId = 15) ) OR | |
( Notes LIKE '%PIR%' OR NOTES LIKE '%Doorbell%' ) | |
) | |
AND StartTime > DATE_SUB(NOW(), INTERVAL 1 DAY) | |
ORDER BY Events.Id, Frames.Delta ASC;""" | |
dbcursor = db.cursor() | |
dbcursor.execute(SQL) | |
dbresult = dbcursor.fetchall() | |
videosegs_index_file = 0 | |
videosegs_index_start = 1 | |
videosegs_index_end = 2 | |
videosegs = [] | |
next_videosegs_entry = [] | |
last_videofile = "" | |
last_delta = -100 | |
for row in dbresult : | |
row_delta, row_videofile, row_starttime = row | |
next_videoseg_started = len(next_videosegs_entry) == 3 | |
new_video = not row_videofile == last_videofile | |
# New Video File Started | |
if new_video : | |
# no previous data start fresh | |
if not next_videoseg_started : | |
next_videosegs_entry= [ row_starttime, row_videofile, row_delta ] | |
else : | |
# finish last video segment and start next | |
next_videosegs_entry.append( last_delta ) | |
videosegs.append(next_videosegs_entry) | |
next_videosegs_entry= [ row_starttime, row_videofile, row_delta ] | |
# if time is more than 2 second away from the previous time | |
# we have a gap signifiant enougth to start a new entry | |
deltajumped = row_delta >= (last_delta + 2) | |
if not new_video and deltajumped and next_videoseg_started: | |
next_videosegs_entry.append( last_delta ) | |
videosegs.append(next_videosegs_entry) | |
next_videosegs_entry= [ row_starttime, row_videofile, row_delta ] | |
# finish | |
last_delta, last_videofile, last_starttime = row | |
# clean old temp videos | |
# Get List Of Valid Tempfiles | |
validTempFiles=[] | |
for segment in videosegs : | |
starttime, videofile, ss, to = segment | |
tempFile = tempvidsfolder + "/" + os.path.basename(videofile) + "-" + str(ss) + ".raw" | |
validTempFiles.append(os.path.basename(tempFile)) | |
for root, dirs, files in os.walk(tempvidsfolder) : | |
for file in files: | |
if not file in validTempFiles : | |
print("Removing " + file) | |
os.remove(tempvidsfolder + "/" + file) | |
# start writing new videos | |
for segment in videosegs : | |
starttime, videofile, ss, to = segment | |
if ss == to : | |
continue | |
if starttime.day == datetime.now().day : | |
drawtextFilterText = "TODAY " | |
else : | |
drawtextFilterText = "YESTERDAY " | |
starttime += timedelta(seconds=int(ss)) | |
drawtextFilterText += starttime.strftime("%H\\:%M\\:%S") | |
drawtextFilterA = [ | |
"fontfile=/usr/share/fonts/truetype/ubuntu/Ubuntu-B.ttf", | |
"text='" + drawtextFilterText +"' ", | |
"fontcolor=white", | |
"fontsize=16", | |
"box=1", | |
"boxcolor=black", | |
"x=(w-text_w)", | |
"y=(h-text_h-line_h)" | |
] | |
drawTextFilter = "drawtext=" + ": ".join(drawtextFilterA) | |
boxfilter=",[0:v]scale=ih*16/9:-1,boxblur=luma_radius=min(h\\,w)/ 20:luma_power=1:chroma_radius=min(cw\\,ch)/20:chroma_power=1[bg];[bg][0:v]overlay=(W-w)/2:(H-h)/2,crop=h=iw*9/16" | |
tempFile = tempvidsfolder + "/" + os.path.basename(videofile) + "-" + str(ss) + ".raw" | |
if not os.path.exists( tempFile ) : | |
print("Creating " + tempFile ) | |
cmd = [ "ffmpeg", "-v", "error", "-probesize", "32", "-analyzeduration", "32", | |
"-i", videofile, | |
"-ss", str(ss), | |
"-to", str(to), | |
"-filter_complex", "scale=640:480,"+drawTextFilter, | |
"-f", "rawvideo", | |
"-r", "2", | |
"-pix_fmt", "yuv420p", | |
tempFile | |
] | |
print(cmd) | |
subprocess.call(cmd) | |
time.sleep(10) | |
# Play thread - read video data from temp files and send to output subprocess | |
def readVideos() : | |
while True : | |
for root, dirs, files in os.walk(tempvidsfolder) : | |
files.sort() | |
for file in files : | |
cmd = [ "cat", tempvidsfolder + "/" + file ] | |
segment_proc = subprocess.Popen(cmd, stdout=output_proc.stdin) | |
segment_proc.communicate() | |
break; | |
t = threading.Thread( target = processVideos ) | |
t.start() | |
readVideos() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment