diff --git a/frigate/config.py b/frigate/config.py index 78f25cf18..3a5d0067a 100644 --- a/frigate/config.py +++ b/frigate/config.py @@ -1,5 +1,4 @@ from __future__ import annotations -from email.policy import default import json import logging @@ -628,7 +627,7 @@ class CameraConfig(FrigateBaseModel): else: for input in self.gstreamer.inputs: gst_cmd = self._get_gstreamer_cmd(self.gstreamer, input) - logger.error("gstreamer command[%s] %s", self.name, gst_cmd) + logger.info("gstreamer command[%s] %s", self.name, gst_cmd) self._decoder_cmds.append({"roles": input.roles, "cmd": gst_cmd}) def _get_gstreamer_cmd( diff --git a/frigate/gstreamer.py b/frigate/gstreamer.py index cb1b89d91..ce9e37b5b 100644 --- a/frigate/gstreamer.py +++ b/frigate/gstreamer.py @@ -17,6 +17,7 @@ VIDEO_CODEC_CAP_NAME = "video codec" logger = logging.getLogger(__name__) + @lru_cache def gst_discover( source: str, cam_name: str, keys: List[str] @@ -53,12 +54,12 @@ def gst_discover( ), cam_name, ) - return None + return {} except: logger.error( "gst-discoverer-1.0 failed with the message: %s", traceback.format_exc() ) - return None + return {} @lru_cache @@ -81,9 +82,7 @@ def gst_inspect_find_codec(codec: Optional[str]) -> List[str]: for line in data.split("\n") if codec is None or codec in line ] - return [ - item[1].strip() for item in data if len(item) > 1 - ] + return [item[1].strip() for item in data if len(item) > 1] except: logger.error( "gst-inspect-1.0 failed with the message: %s", traceback.format_exc() @@ -228,7 +227,7 @@ class GstreamerBaseBuilder: self.audio_pipeline is not None and len(self.audio_pipeline) > 0 ) - split_mux = f"splitmuxsink async-handling=true " + split_mux = f"splitmuxsink async-finalize=true send-keyframe-requests=true max-size-bytes=0 " if use_audio_pipeline: split_mux = split_mux + "name=mux muxer=mp4mux " @@ -283,6 +282,14 @@ class GstreamerBaseBuilder: ) depay_element = f"rtp{self.video_format}depay" + # add rtpjitterbuffer into the input pipeline for reord role if no rtpjitterbuffer has been added already + if use_record: + has_rtpjitterbuffer = "rtpjitterbuffer" in " ".join(self.input_pipeline) + if not has_rtpjitterbuffer: + self.input_pipeline.append( + "rtpjitterbuffer do-lost=true drop-on-latency=true" + ) + pipeline = [*self.input_pipeline, depay_element] # if both detect and record used, split the stream after the depay element # to avoid encoding for recording diff --git a/frigate/record.py b/frigate/record.py index 4964aae7c..734394dd2 100644 --- a/frigate/record.py +++ b/frigate/record.py @@ -22,7 +22,6 @@ from frigate.const import ( CACHE_DIR, RECORD_DIR, GSTREAMER_RECORD_SUFFIX, - RECORD_SEGMENT_TIME_SECONDS, ) from frigate.models import Event, Recordings from frigate.util import area @@ -98,7 +97,7 @@ class RecordingMaintainer(threading.Thread): if camera.endswith(GSTREAMER_RECORD_SUFFIX): camera = camera.split(GSTREAMER_RECORD_SUFFIX)[0] creation_time = ( - os.path.getmtime(cache_path) - RECORD_SEGMENT_TIME_SECONDS + os.path.getctime(cache_path) ) start_time = datetime.datetime.utcfromtimestamp(creation_time) else: diff --git a/frigate/test/test_gstreamer.py b/frigate/test/test_gstreamer.py index 959c98a6c..61c443fd5 100644 --- a/frigate/test/test_gstreamer.py +++ b/frigate/test/test_gstreamer.py @@ -284,6 +284,10 @@ class TestGstreamerNvidia(TestCase): "latency=0", "do-timestamp=true", "!", + "rtpjitterbuffer", + "do-lost=true", + "drop-on-latency=true", + "!", "rtph264depay", "!", "tee", @@ -308,7 +312,9 @@ class TestGstreamerNvidia(TestCase): "h264parse", "!", "splitmuxsink", - "async-handling=true", + "async-finalize=true", + "send-keyframe-requests=true", + "max-size-bytes=0", "location=/tmp/cache/cam_name-gstsplitmuxchunk-%05d.mp4", "max-size-time=10000000000", ] @@ -332,6 +338,10 @@ class TestGstreamerNvidia(TestCase): "latency=0", "do-timestamp=true", "!", + "rtpjitterbuffer", + "do-lost=true", + "drop-on-latency=true", + "!", "rtph264depay", "!", "queue", @@ -339,7 +349,9 @@ class TestGstreamerNvidia(TestCase): "h264parse", "!", "splitmuxsink", - "async-handling=true", + "async-finalize=true", + "send-keyframe-requests=true", + "max-size-bytes=0", "location=/tmp/cache/cam_name-gstsplitmuxchunk-%05d.mp4", "max-size-time=10000000000", ] @@ -367,6 +379,10 @@ class TestGstreamerNvidia(TestCase): "latency=0", "do-timestamp=true", "!", + "rtpjitterbuffer", + "do-lost=true", + "drop-on-latency=true", + "!", "rtph265depay", "!", "tee", @@ -391,7 +407,9 @@ class TestGstreamerNvidia(TestCase): "h265parse", "!", "splitmuxsink", - "async-handling=true", + "async-finalize=true", + "send-keyframe-requests=true", + "max-size-bytes=0", "name=mux", "muxer=mp4mux", "location=/tmp/cache/cam_name-gstsplitmuxchunk-%05d.mp4", @@ -432,6 +450,10 @@ class TestGstreamerNvidia(TestCase): "latency=0", "do-timestamp=true", "!", + "rtpjitterbuffer", + "do-lost=true", + "drop-on-latency=true", + "!", "rtph264depay", "!", "queue", @@ -439,7 +461,9 @@ class TestGstreamerNvidia(TestCase): "h264parse", "!", "splitmuxsink", - "async-handling=true", + "async-finalize=true", + "send-keyframe-requests=true", + "max-size-bytes=0", "name=mux", "muxer=mp4mux", "location=/tmp/cache/cam_name-gstsplitmuxchunk-%05d.mp4",