recording for GStreamer

This commit is contained in:
YS 2022-01-07 12:23:08 +03:00
parent d5dd82fa47
commit 1aa6ccaa6d
4 changed files with 31 additions and 11 deletions

View File

@ -12,7 +12,7 @@ import yaml
from pydantic import BaseModel, Extra, Field, validator, root_validator from pydantic import BaseModel, Extra, Field, validator, root_validator
from pydantic.fields import PrivateAttr from pydantic.fields import PrivateAttr
from frigate.const import BASE_DIR, CACHE_DIR, YAML_EXT from frigate.const import BASE_DIR, CACHE_DIR, YAML_EXT, RECORD_SEGMENT_TIME_SECONDS
from frigate.util import ( from frigate.util import (
create_mask, create_mask,
deep_merge, deep_merge,
@ -315,7 +315,7 @@ RECORD_FFMPEG_OUTPUT_ARGS_DEFAULT = [
"-f", "-f",
"segment", "segment",
"-segment_time", "-segment_time",
"10", str(RECORD_SEGMENT_TIME_SECONDS),
"-segment_format", "-segment_format",
"mp4", "mp4",
"-reset_timestamps", "-reset_timestamps",
@ -616,7 +616,7 @@ class CameraConfig(FrigateBaseModel):
) )
builder = GstreamerBuilder( builder = GstreamerBuilder(
gstreamer_input.path, self.detect.width, self.detect.height gstreamer_input.path, self.detect.width, self.detect.height, self.name
) )
if caps is None or len(caps) == 0: if caps is None or len(caps) == 0:
logger.warn("gsreamer was not able to detect the input stream format") logger.warn("gsreamer was not able to detect the input stream format")

View File

@ -3,3 +3,5 @@ CLIPS_DIR = f"{BASE_DIR}/clips"
RECORD_DIR = f"{BASE_DIR}/recordings" RECORD_DIR = f"{BASE_DIR}/recordings"
CACHE_DIR = "/tmp/cache" CACHE_DIR = "/tmp/cache"
YAML_EXT = (".yaml", ".yml") YAML_EXT = (".yaml", ".yml")
RECORD_SEGMENT_TIME_SECONDS = 10
GSTREAMER_RECORD_SUFFIX = "-gstsplitmuxchunk"

View File

@ -3,7 +3,11 @@ import logging
import traceback import traceback
import subprocess as sp import subprocess as sp
from typing import Dict, List, Optional from typing import Dict, List, Optional
from frigate.const import CACHE_DIR from frigate.const import (
CACHE_DIR,
GSTREAMER_RECORD_SUFFIX,
RECORD_SEGMENT_TIME_SECONDS,
)
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -62,6 +66,7 @@ def gst_inspect_find_codec(codec: str) -> List[str]:
) )
return None return None
def autodetect_decoder_pipeline( def autodetect_decoder_pipeline(
codec: Optional[str], codec: Optional[str],
) -> List[str]: ) -> List[str]:
@ -118,10 +123,11 @@ CODECS = {
class GstreamerBuilder: class GstreamerBuilder:
def __init__(self, uri, width, height, format="I420"): def __init__(self, uri, width, height, name, format="I420"):
self.uri = uri self.uri = uri
self.width = width self.width = width
self.height = height self.height = height
self.name = name
self.video_format = f"video/x-raw,width=(int){width},height=(int){height},format=(string){format}" self.video_format = f"video/x-raw,width=(int){width},height=(int){height},format=(string){format}"
self.input_pipeline = [f'rtspsrc location="{uri}" latency=0'] self.input_pipeline = [f'rtspsrc location="{uri}" latency=0']
self.destination_format_pipeline = [self.video_format, "videoconvert"] self.destination_format_pipeline = [self.video_format, "videoconvert"]
@ -141,7 +147,7 @@ class GstreamerBuilder:
self.decoder_pipeline = autodetect_decoder_pipeline(codec) self.decoder_pipeline = autodetect_decoder_pipeline(codec)
return self return self
def with_source_format_pipeline(self, source_format_pipeline): def with_source_format_pipeline(self, source_format_pipeline):
source_format_pipeline = ( source_format_pipeline = (
source_format_pipeline source_format_pipeline
@ -176,7 +182,7 @@ class GstreamerBuilder:
"queue2", "queue2",
"x264enc key-int-max=10", "x264enc key-int-max=10",
"h264parse", "h264parse",
f"splitmuxsink async-handling=true location={os.path.join(CACHE_DIR, self.name)}-gst-%05d.mp4 max-size-time=10000000000", f"splitmuxsink async-handling=true location={os.path.join(CACHE_DIR, self.name)}{GSTREAMER_RECORD_SUFFIX}-%05d.mp4 max-size-time={RECORD_SEGMENT_TIME_SECONDS*1000000000}",
] ]
if use_record if use_record
else [] else []
@ -188,4 +194,3 @@ class GstreamerBuilder:
] ]
pipeline_args = [item for sublist in pipeline_args for item in sublist] pipeline_args = [item for sublist in pipeline_args for item in sublist]
return ["gst-launch-1.0", "-q", *pipeline_args][:-1] return ["gst-launch-1.0", "-q", *pipeline_args][:-1]

View File

@ -1,4 +1,5 @@
import datetime import datetime
from dateutil import tz
import itertools import itertools
import logging import logging
import multiprocessing as mp import multiprocessing as mp
@ -17,7 +18,12 @@ import psutil
from peewee import JOIN, DoesNotExist from peewee import JOIN, DoesNotExist
from frigate.config import RetainModeEnum, FrigateConfig from frigate.config import RetainModeEnum, FrigateConfig
from frigate.const import CACHE_DIR, RECORD_DIR from frigate.const import (
CACHE_DIR,
RECORD_DIR,
GSTREAMER_RECORD_SUFFIX,
RECORD_SEGMENT_TIME_SECONDS,
)
from frigate.models import Event, Recordings from frigate.models import Event, Recordings
from frigate.util import area from frigate.util import area
@ -69,7 +75,7 @@ class RecordingMaintainer(threading.Thread):
files_in_use = [] files_in_use = []
for process in psutil.process_iter(): for process in psutil.process_iter():
try: try:
if process.name() != "ffmpeg": if process.name() not in ["ffmpeg", "gst-launch-1.0"]:
continue continue
flist = process.open_files() flist = process.open_files()
if flist: if flist:
@ -89,7 +95,14 @@ class RecordingMaintainer(threading.Thread):
cache_path = os.path.join(CACHE_DIR, f) cache_path = os.path.join(CACHE_DIR, f)
basename = os.path.splitext(f)[0] basename = os.path.splitext(f)[0]
camera, date = basename.rsplit("-", maxsplit=1) camera, date = basename.rsplit("-", maxsplit=1)
start_time = datetime.datetime.strptime(date, "%Y%m%d%H%M%S") if camera.endswith(GSTREAMER_RECORD_SUFFIX):
camera = camera.split(GSTREAMER_RECORD_SUFFIX)[0]
creation_time = (
os.path.getmtime(cache_path) - RECORD_SEGMENT_TIME_SECONDS
)
start_time = datetime.datetime.utcfromtimestamp(creation_time)
else:
start_time = datetime.datetime.strptime(date, "%Y%m%d%H%M%S")
grouped_recordings[camera].append( grouped_recordings[camera].append(
{ {