initial commit

This commit is contained in:
Jason Hunter
2021-07-09 16:14:16 -04:00
committed by Blake Blackshear
parent dc759a3e56
commit a476bc9885
6 changed files with 402 additions and 278 deletions

View File

@@ -6,11 +6,13 @@ import glob
import logging
import os
import re
import subprocess as sp
import time
from functools import reduce
from pathlib import Path
import cv2
from flask.helpers import send_file
import numpy as np
from flask import (
@@ -223,6 +225,32 @@ def event_snapshot(id):
return response
@bp.route("/events/<id>/clip.mp4")
def event_clip(id):
event: Event = Event.get(Event.id == id)
if event is None:
return "Event not found.", 404
if not event.has_clip:
return "Clip not available", 404
event_config = current_app.frigate_config.cameras[event.camera].record.events
start_ts = event.start_time - event_config.pre_capture
end_ts = event.end_time + event_config.post_capture
clip_path = os.path.join(CLIPS_DIR, f"{event.camera}-{id}.mp4")
if not os.path.isfile(clip_path):
return recording_clip(event.camera, start_ts, end_ts)
return send_file(
clip_path,
mimetype="video/mp4",
as_attachment=True,
attachment_filename=f"{event.camera}_{start_ts}-{end_ts}.mp4",
)
@bp.route("/events")
def events():
limit = request.args.get("limit", 100)
@@ -517,14 +545,84 @@ def recordings(camera_name):
)
@bp.route("/vod/<year_month>/<day>/<hour>/<camera>")
def vod(year_month, day, hour, camera):
start_date = datetime.strptime(f"{year_month}-{day} {hour}", "%Y-%m-%d %H")
end_date = start_date + timedelta(hours=1) - timedelta(milliseconds=1)
start_ts = start_date.timestamp()
end_ts = end_date.timestamp()
@bp.route("/<camera>/start/<int:start_ts>/end/<int:end_ts>/clip.mp4")
@bp.route("/<camera>/start/<float:start_ts>/end/<float:end_ts>/clip.mp4")
def recording_clip(camera, start_ts, end_ts):
recordings = (
Recordings.select()
.where(
(Recordings.start_time.between(start_ts, end_ts))
| (Recordings.end_time.between(start_ts, end_ts))
)
.where(Recordings.camera == camera)
.order_by(Recordings.start_time.asc())
)
# Select all recordings where either the start or end dates fall in the requested hour
playlist_lines = []
clip: Recordings
for clip in recordings:
playlist_lines.append(f"file '{clip.path}'")
# if this is the starting clip, add an inpoint
if clip.start_time < start_ts:
playlist_lines.append(f"inpoint {int(start_ts - clip.start_time)}")
# if this is the ending clip, add an outpoint
if clip.end_time > end_ts:
playlist_lines.append(f"outpoint {int(end_ts - clip.start_time)}")
path = f"/tmp/cache/tmp_clip_{camera}_{start_ts}-{end_ts}.mp4"
ffmpeg_cmd = [
"ffmpeg",
"-y",
"-protocol_whitelist",
"pipe,file",
"-f",
"concat",
"-safe",
"0",
"-i",
"-",
"-c",
"copy",
"-f",
"mp4",
"-movflags",
"+faststart",
path,
]
p = sp.run(
ffmpeg_cmd,
input="\n".join(playlist_lines),
encoding="ascii",
capture_output=True,
)
if p.returncode != 0:
logger.error(p.stderr)
return f"Could not create clip from recordings for {camera}.", 500
mp4_bytes = None
try:
# read clip from disk
with open(path, "rb") as mp4_file:
mp4_bytes = mp4_file.read()
# delete after we have the bytes
os.remove(path)
except DoesNotExist:
return f"Could not create clip from recordings for {camera}.", 500
response = make_response(mp4_bytes)
response.mimetype = "video/mp4"
response.headers[
"Content-Disposition"
] = f"attachment; filename={camera}_{start_ts}-{end_ts}.mp4"
return response
@bp.route("/vod/<camera>/start/<int:start_ts>/end/<int:end_ts>")
@bp.route("/vod/<camera>/start/<float:start_ts>/end/<float:end_ts>")
def vod_ts(camera, start_ts, end_ts):
recordings = (
Recordings.select()
.where(
@@ -553,9 +651,13 @@ def vod(year_month, day, hour, camera):
clips.append(clip)
durations.append(duration)
if not clips:
return "No recordings found.", 404
hour_ago = datetime.now() - timedelta(hours=1)
return jsonify(
{
"cache": datetime.now() - timedelta(hours=1) > start_date,
"cache": hour_ago.timestamp() > start_ts,
"discontinuity": False,
"durations": durations,
"sequences": [{"clips": clips}],
@@ -563,6 +665,45 @@ def vod(year_month, day, hour, camera):
)
@bp.route("/vod/<year_month>/<day>/<hour>/<camera>")
def vod_hour(year_month, day, hour, camera):
start_date = datetime.strptime(f"{year_month}-{day} {hour}", "%Y-%m-%d %H")
end_date = start_date + timedelta(hours=1) - timedelta(milliseconds=1)
start_ts = start_date.timestamp()
end_ts = end_date.timestamp()
return vod_ts(camera, start_ts, end_ts)
@bp.route("/vod/event/<id>")
def vod_event(id):
event: Event = Event.get(Event.id == id)
if event is None:
return "Event not found.", 404
if not event.has_clip:
return "Clip not available", 404
event_config = current_app.frigate_config.cameras[event.camera].record.events
start_ts = event.start_time - event_config.pre_capture
end_ts = event.end_time + event_config.post_capture
clip_path = os.path.join(CLIPS_DIR, f"{event.camera}-{id}.mp4")
if not os.path.isfile(clip_path):
return vod_ts(event.camera, start_ts, end_ts)
duration = int((end_ts - start_ts) * 1000)
return jsonify(
{
"cache": True,
"discontinuity": False,
"durations": [duration],
"sequences": [{"clips": [{"type": "source", "path": clip_path}]}],
}
)
def imagestream(detected_frames_processor, camera_name, fps, height, draw_options):
while True:
# max out at specified FPS