forked from Github/frigate
Add ability to submit frames from recordings (#11212)
* add ability to parse and upload image from recording to frigate+ * Show dialog with current frame to be uploaded * Implement uploading image in frontend * Cleanup * Update title
This commit is contained in:
@@ -26,6 +26,7 @@ from frigate.const import (
|
||||
)
|
||||
from frigate.models import Event, Previews, Recordings, Regions, ReviewSegment
|
||||
from frigate.util.builtin import get_tz_modifiers
|
||||
from frigate.util.image import get_image_from_recording
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -205,30 +206,20 @@ def get_snapshot_from_recording(camera_name: str, frame_time: str):
|
||||
try:
|
||||
recording: Recordings = recording_query.get()
|
||||
time_in_segment = frame_time - recording.start_time
|
||||
image_data = get_image_from_recording(recording.path, time_in_segment)
|
||||
|
||||
ffmpeg_cmd = [
|
||||
"ffmpeg",
|
||||
"-hide_banner",
|
||||
"-loglevel",
|
||||
"warning",
|
||||
"-ss",
|
||||
f"00:00:{time_in_segment}",
|
||||
"-i",
|
||||
recording.path,
|
||||
"-frames:v",
|
||||
"1",
|
||||
"-c:v",
|
||||
"png",
|
||||
"-f",
|
||||
"image2pipe",
|
||||
"-",
|
||||
]
|
||||
if not image_data:
|
||||
return make_response(
|
||||
jsonify(
|
||||
{
|
||||
"success": False,
|
||||
"message": f"Unable to parse frame at time {frame_time}",
|
||||
}
|
||||
),
|
||||
404,
|
||||
)
|
||||
|
||||
process = sp.run(
|
||||
ffmpeg_cmd,
|
||||
capture_output=True,
|
||||
)
|
||||
response = make_response(process.stdout)
|
||||
response = make_response(image_data)
|
||||
response.headers["Content-Type"] = "image/png"
|
||||
return response
|
||||
except DoesNotExist:
|
||||
@@ -243,6 +234,71 @@ def get_snapshot_from_recording(camera_name: str, frame_time: str):
|
||||
)
|
||||
|
||||
|
||||
@MediaBp.route("/<camera_name>/plus/<frame_time>", methods=("POST",))
|
||||
def submit_recording_snapshot_to_plus(camera_name: str, frame_time: str):
|
||||
if camera_name not in current_app.frigate_config.cameras:
|
||||
return make_response(
|
||||
jsonify({"success": False, "message": "Camera not found"}),
|
||||
404,
|
||||
)
|
||||
|
||||
frame_time = float(frame_time)
|
||||
recording_query = (
|
||||
Recordings.select(
|
||||
Recordings.path,
|
||||
Recordings.start_time,
|
||||
)
|
||||
.where(
|
||||
(
|
||||
(frame_time >= Recordings.start_time)
|
||||
& (frame_time <= Recordings.end_time)
|
||||
)
|
||||
)
|
||||
.where(Recordings.camera == camera_name)
|
||||
.order_by(Recordings.start_time.desc())
|
||||
.limit(1)
|
||||
)
|
||||
|
||||
try:
|
||||
recording: Recordings = recording_query.get()
|
||||
time_in_segment = frame_time - recording.start_time
|
||||
image_data = get_image_from_recording(recording.path, time_in_segment)
|
||||
|
||||
if not image_data:
|
||||
return make_response(
|
||||
jsonify(
|
||||
{
|
||||
"success": False,
|
||||
"message": f"Unable to parse frame at time {frame_time}",
|
||||
}
|
||||
),
|
||||
404,
|
||||
)
|
||||
|
||||
nd = cv2.imdecode(np.frombuffer(image_data, dtype=np.int8), cv2.IMREAD_COLOR)
|
||||
current_app.plus_api.upload_image(nd, camera_name)
|
||||
|
||||
return make_response(
|
||||
jsonify(
|
||||
{
|
||||
"success": True,
|
||||
"message": "Successfully submitted image.",
|
||||
}
|
||||
),
|
||||
200,
|
||||
)
|
||||
except DoesNotExist:
|
||||
return make_response(
|
||||
jsonify(
|
||||
{
|
||||
"success": False,
|
||||
"message": "Recording not found at {}".format(frame_time),
|
||||
}
|
||||
),
|
||||
404,
|
||||
)
|
||||
|
||||
|
||||
@MediaBp.route("/recordings/storage", methods=["GET"])
|
||||
def get_recordings_storage_usage():
|
||||
recording_stats = current_app.stats_emitter.get_latest_stats()["service"][
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import datetime
|
||||
import logging
|
||||
import subprocess as sp
|
||||
from abc import ABC, abstractmethod
|
||||
from multiprocessing import shared_memory
|
||||
from string import printable
|
||||
@@ -746,3 +747,37 @@ def add_mask(mask: str, mask_img: np.ndarray):
|
||||
]
|
||||
)
|
||||
cv2.fillPoly(mask_img, pts=[contour], color=(0))
|
||||
|
||||
|
||||
def get_image_from_recording(
|
||||
file_path: str, relative_frame_time: float
|
||||
) -> Optional[any]:
|
||||
"""retrieve a frame from given time in recording file."""
|
||||
|
||||
ffmpeg_cmd = [
|
||||
"ffmpeg",
|
||||
"-hide_banner",
|
||||
"-loglevel",
|
||||
"warning",
|
||||
"-ss",
|
||||
f"00:00:{relative_frame_time}",
|
||||
"-i",
|
||||
file_path,
|
||||
"-frames:v",
|
||||
"1",
|
||||
"-c:v",
|
||||
"png",
|
||||
"-f",
|
||||
"image2pipe",
|
||||
"-",
|
||||
]
|
||||
|
||||
process = sp.run(
|
||||
ffmpeg_cmd,
|
||||
capture_output=True,
|
||||
)
|
||||
|
||||
if process.returncode == 0:
|
||||
return process.stdout
|
||||
else:
|
||||
return None
|
||||
|
||||
Reference in New Issue
Block a user