Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Recordings visible in the WebUI, but not present in the configured directory #837

Open
jin-eld opened this issue Nov 18, 2024 · 65 comments · Fixed by #865
Open

Recordings visible in the WebUI, but not present in the configured directory #837

jin-eld opened this issue Nov 18, 2024 · 65 comments · Fixed by #865

Comments

@jin-eld
Copy link

jin-eld commented Nov 18, 2024

Hi,

I am not quite sure if this is similar to #797 but I simply can't find my recordings, although they are shown in the web interface.

I am using the dev version, the relevant docker compose config:

services:
  viseron:
    image: roflcoopter/viseron:dev
    container_name: viseron
    volumes:
      - /mnt/data/nvr/viseron/recordings:/recordings
      - /mnt/data/nvr/viseron/config:/config
      - /mnt/data/nvr/viseron/models:/models
      - /etc/localtime:/etc/localtime:ro

In my viseron config (I know it's default) I specified the recordings path anyway:

ffmpeg:
  camera:
    camera_1:  # This value has to be unique across all cameras
      name: east
      host: xxx.xxx.xxx.xxx
      port: 554
      path: /stream=0
      protocol: rtsp
      rtsp_transport: tcp
      username: xxxx
      password: xxxx
      substream:
        port: 80
        path: /mjpeg
        stream_format: mjpeg
        protocol: http
        fps: 5
      recorder:
        filename_pattern: east_%Y-%m-%d-%H:%M:%S
        folder: /recordings

## Then add an object detector
edgetpu:
  object_detector:
    cameras:
      camera_1:  # Attach detector to the configured camera_1 above
        fps: 5
        scan_on_motion_only: false  # Scan for objects even when there is no motion
        labels:
          - label: person
            confidence: 0.7
            trigger_recorder: true

nvr:
  camera_1:  # Run NVR for camera_1

logger:
  default_level: debug

On the host the directories recordings/camera_1/2024-11-18/ get created, but there are not files there.

I do not see any errors in the logs:

[2024-11-18 15:08:47] [DEBUG   ] [viseron.components.edgetpu.object_detector.camera_1] - Objects: [], message repeated 4 times
viseron  | [2024-11-18 15:08:47] [DEBUG   ] [viseron.domains.camera.fragmenter.camera_1] - Checking for new segments to fragment in /tmp/viseron/segments/camera_1
viseron  | [2024-11-18 15:08:47] [DEBUG   ] [viseron.domains.camera.fragmenter.camera_1] - Processing 1731938921.m4s
viseron  | [2024-11-18 15:08:47] [DEBUG   ] [viseron.components.storage.tier_handler.camera_1.tier_0] - File created: /segments/camera_1/1731938921.m4s
viseron  | [2024-11-18 15:08:47] [DEBUG   ] [viseron.components.edgetpu.object_detector.camera_1] - Objects: []
viseron  | [2024-11-18 15:08:47] [DEBUG   ] [viseron.components.edgetpu.object_detector.camera_1] - Objects: [{'label': 'person', 'confidence': 0.719, 'rel_width': 0.031, 'rel_height': 0.184, 'rel_x1': 0.966, 'rel_y1': 0.347, 'rel_x2': 0.997, 'rel_y2': 0.531}]
viseron  | [2024-11-18 15:08:47] [DEBUG   ] [viseron.states] - Setting state of binary_sensor.camera_1_object_detected_person to state: on, attributes {'name': 'east Object Detected Person', 'domain': 'binary_sensor', 'count': 1, 'objects': [<viseron.domains.object_detector.detected_object.DetectedObject object at 0x7f520631c9d0>]}
viseron  | [2024-11-18 15:08:47] [DEBUG   ] [viseron.states] - Setting state of binary_sensor.camera_1_object_detected to state: on, attributes {'name': 'east Object Detected', 'domain': 'binary_sensor', 'count': 1, 'objects': [<viseron.domains.object_detector.detected_object.DetectedObject object at 0x7f520631c9d0>]}
viseron  | [2024-11-18 15:08:47] [DEBUG   ] [viseron.components.ffmpeg.camera.camera_1] - Saving snapshot to /snapshots/object_detector/camera_1/2024-11-18-14-08-47-66ef6063-67f3-47c2-a354-b0b94834b0b5.jpg
viseron  | [2024-11-18 15:08:47] [DEBUG   ] [viseron.components.storage.tier_handler.camera_1.tier_0] - File created: /snapshots/object_detector/camera_1/2024-11-18-14-08-47-66ef6063-67f3-47c2-a354-b0b94834b0b5.jpg
viseron  | [2024-11-18 15:08:47] [INFO    ] [viseron.components.ffmpeg.recorder.camera_1] - Starting recorder
viseron  | [2024-11-18 15:08:47] [DEBUG   ] [viseron.components.storage.queries] - Files to move query bindparms: category(snapshots), subcategory(object_detector), tier_id(0), camera_identifier(camera_1), max_bytes(0), min_age_timestamp(1731938922.745313), min_bytes(0), max_age_timestamp(1731334127.745313)
viseron  | [2024-11-18 15:08:47] [DEBUG   ] [viseron.components.ffmpeg.recorder.camera_1] - Saving thumbnail in /thumbnails/camera_1
viseron  | [2024-11-18 15:08:47] [DEBUG   ] [viseron.components.storage.tier_handler.camera_1.tier_0] - File created: /thumbnails/camera_1/81.jpg
viseron  | [2024-11-18 15:08:47] [DEBUG   ] [viseron.states] - Setting state of binary_sensor.camera_1_recorder to state: on, attributes {'name': 'east Recorder', 'domain': 'binary_sensor', 'id': 81, 'start_time': datetime.datetime(2024, 11, 18, 14, 8, 47, 745477, tzinfo=datetime.timezone.utc), 'start_timestamp': 1731938927.745477, 'end_time': None, 'end_timestamp': None, 'date': '2024-11-18', 'path': '/recordings/camera_1/2024-11-18/east_2024-11-18-14:08:47.mp4', 'filename': 'east_2024-11-18-14:08:47.mp4', 'thumbnail_path': '/thumbnails/camera_1/81.jpg', 'objects': [<viseron.domains.object_detector.detected_object.DetectedObject object at 0x7f520631c9d0>]}
viseron  | [2024-11-18 15:08:47] [DEBUG   ] [viseron.states] - Setting state of image.camera_1_latest_thumbnail to state: unknown, attributes {'name': 'east Latest Thumbnail', 'domain': 'image', 'start_time': '2024-11-18T14:08:47.745477+00:00', 'path': '/recordings/camera_1/2024-11-18/east_2024-11-18-14:08:47.mp4', 'thumbnail_path': '/thumbnails/camera_1/81.jpg'}

However, the file: /recordings/camera_1/2024-11-18/east_2024-11-18-14:08:47.mp4 is nowhere to be found. I tried attaching to a shell inside the docker container and the recordings directory is there as empty as on the host:

root@b3ed43059b4e:/recordings# find .
.
./camera_1
./camera_1/2024-11-18

I tried searching for the file in the container root, also in the host system root and I do not see it anywhere. Yet http://nvr:8888/#/recordings/camera_1/2024-11-18 shows quite a number of files which can be played back in the browser.

So... where are they located? :)

@jin-eld
Copy link
Author

jin-eld commented Nov 18, 2024

I think I might know what I did wrong, so it seems in the dev version the configuration has changed and the folder option is obsolete, one has to use a storage section.

In docker compose I now changed my tmpfs to:

    tmpfs:
      - /ramdisk:rw,size=2g,mode=1777

And in Viseron I added:

storage:
  recorder:
    tiers:
      - path: /ramdisk
        events:
          max_age:
            days: 1
          max_size:
            gb: 1.5
        move_on_shutdown: true

      - path: /recordings
        events:
          max_age:
            days: 7
          max_size:
            gb: 80
  snapshots:
    tiers:
      - path: /ramdisk
        move_on_shutdown: true
        max_age:
          days: 1
        max_size:
          gb: 0.5

Will need to make a few tests where something gets detected and recorded to see if the recordings finally end up where they should (for example if they will get moved after a shutdown).

@jin-eld
Copy link
Author

jin-eld commented Nov 18, 2024

Retested with the new storage settings, something is still off, the log shows:

viseron  | [2024-11-18 19:49:54] [INFO    ] [viseron.components.nvr.nvr.camera_1] - Stopping recording in: 1
viseron  | [2024-11-18 19:49:55] [DEBUG   ] [viseron.components.edgetpu.object_detector.camera_1] - Objects: []
viseron  | [2024-11-18 19:49:55] [DEBUG   ] [viseron.domains.camera.fragmenter.camera_1] - Checking for new segments to fragment in /tmp/viseron/ramdisk/segments/camera_1
viseron  | [2024-11-18 19:49:55] [DEBUG   ] [viseron.components.edgetpu.object_d[2024-11-18 19:49:56] [DEBUG   ] [viseron.components.edgetpu.object_detector.camera_1] - Objects: [], message repeated 2 times
viseron  | [2024-11-18 19:49:56] [DEBUG   ] [viseron.domains.camera.fragmenter.camera_1] - Checking for new segments to fragment in /tmp/viseron/ramdisk/segments/camera_1
viseron  | [2024-11-18 19:49:56] [DEBUG   ] [viseron.domains.camera.fragmenter.camera_1] - Processing 1731955790.m4s
viseron  | [2024-11-18 19:49:56] [DEBUG   ] [viseron.components.storage.tier_handler.camera_1.tier_0] - File created: /ramdisk/segments/camera_1/1731955790.m4s
viseron  | [2024-11-18 19:49:57] [DEBUG   ] [viseron.components.edgetpu.object_detector.camera_1] - Objects: []
viseron  | [2024-11-18 19:49:57] [DEBUG   ] [viseron.domains.camera.fragmenter.camera_1] - Checking for new segments to fragment in /tmp/viseron/ramdisk/segments/camera_1
viseron  | [2024-11-18 19:49:57] [DEBUG   ] [viseron.components.storage.tier_handler.camera_1.tier_0] - File modified (delayed event): /ramdisk/segments/camera_1/1731955790.m4s
viseron  | [2024-11-18 19:49:57] [DEBUG   ] [viseron.components.edgetpu.object_detector.camera_1] - Objects: []
viseron  | [2024-11-18 19:49:57] [INFO    ] [viseron.components.nvr.nvr.camera_1] - Stopping recording in: 0
viseron  | [2024-11-18 19:49:57] [INFO    ] [viseron.components.ffmpeg.recorder.camera_1] - Stopping recorder
viseron  | [2024-11-18 19:49:57] [DEBUG   ] [viseron.states] - Setting state of binary_sensor.camera_1_recorder to state: off, attributes {'name': 'east Recorder', 'domain': 'binary_sensor', 'id': 96, 'start_time': datetime.datetime(2024, 11, 18, 18, 49, 28, 374093, tzinfo=datetime.timezone.utc), 'start_timestamp': 1731955768.374093, 'end_time': datetime.datetime(2024, 11, 18, 18, 49, 57, 627797, tzinfo=datetime.timezone.utc), 'end_timestamp': 1731955797.627797, 'date': '2024-11-18', 'path': '/ramdisk/recordings/camera_1/2024-11-18/east_2024-11-18-18:49:28.mp4', 'filename': 'east_2024-11-18-18:49:28.mp4', 'thumbnail_path': '/ramdisk/thumbnails/camera_1/96.jpg', 'objects': [<viseron.domains.object_detector.detected_object.DetectedObject object at 0x7f39891ec460>]}
viseron  | [2024-11-18 19:49:57] [DEBUG   ] [viseron.states] - Setting state of sensor.camera_1_operation_state to state: scanning_for_objects, attributes {'name': 'east Operation State', 'domain': 'sensor'}

Note the log entry: 'path': '/ramdisk/recordings/camera_1/2024-11-18/east_2024-11-18-18:49:28.mp4'

Now looking inside of the container (docker exec -it container_id /bin/bash) - nothing there:

root@d461e1aa7951:/ramdisk/recordings# find .
.
./camera_1
./camera_1/2024-11-18

Now, gracefully stopping the container, hoping that move_on_shutdown: true would move it to the /recordings mount point which maps to the host emmc:

viseron  | [2024-11-18 20:17:43] [DEBUG   ] [viseron.domains.camera.fragmenter.camera_1] - Checking for new segments to fragment in /tmp/viseron/ramdisk/segments/camera_1
[2024-11-18 20:17:44] [DEBUG   ] [viseron.components.edgetpu.object_detector.camera_1] - Objects: [], message repeated 2 times
viseron  | [2024-11-18 20:17:44] [DEBUG   ] [viseron.domains.camera.fragmenter.camera_1] - Checking for new segments to fragment in /tmp/viseron/ramdisk/segments/camera_1
viseron  | [2024-11-18 20:17:44] [DEBUG   ] [viseron.domains.camera.fragmenter.camera_1] - Processing 1731957460.m4s
viseron  | [2024-11-18 20:17:44] [DEBUG   ] [viseron.components.storage.tier_handler.camera_1.tier_0] - File created: /ramdisk/segments/camera_1/1731957460.m4s
viseron  | [2024-11-18 20:17:44] [DEBUG   ] [viseron.components.edgetpu.object_detector.camera_1] - Objects: []
viseron  | [2024-11-18 20:17:45] [DEBUG   ] [viseron.domains.camera.fragmenter.camera_1] - Checking for new segments to fragment in /tmp/viseron/ramdisk/segments/camera_1
viseron  | [2024-11-18 20:17:45] [DEBUG   ] [viseron.components.storage.tier_handler.camera_1.tier_0] - File modified (delayed event): /ramdisk/segments/camera_1/1731957460.m4s
viseron  | [2024-11-18 20:17:45] [DEBUG   ] [viseron.components.edgetpu.object_detector.camera_1] - Objects: []
^CGracefully stopping... (press Ctrl+C again to force)
[+] Stopping 1/1
 ✔ Container viseron  Stopped 

Nothing in the logs indicates it was going to move the recording anywhere?

The recordings directory on the host shows the correct subdirectory structure, but has not .mp4 files, i.e. the file east_2024-11-18-18:49:28.mp4 is not there.

Is this a bug or what am I missing?

@roflcoopter
Copy link
Owner

Sorry for the confusion! The dev branch still has some kinks i need to workout.

In v3, the cameras recordings are stored as short m4s segments to enable 24/7 recordings.
They are stored in the /segments folder by default.
If you check the folder /ramdisk/segments you should be seeing files there.

If you are in need of having the events as mp4 files, just like v2 did, you can set the create_event_clip: true config option under the recorder section for each camera.
That will create the files you are looking for in the recordings folder.

The logline and the folder creation is missleading since the file is never created, will fix that right away

@jin-eld
Copy link
Author

jin-eld commented Nov 18, 2024

Thank you for getting back to me! Yes indeed I saw the segments on ramdisk, but assumed they were something internal, especially seeing in the logs how they get regularly deleted.

I was indeed looking for one complete clip for the whole event, expecting it to end up in /recordings at some point, allowing local high res review and evidence collecting if need be.

I now found it in the sources and have a question regarding this remark:

WARNING: Will store both the fragments AND the MP4 file, using more storage space.

Does that mean, that after the event fragments get concatenated into one event mp4, they will not get cleaned up? If so, why? My naive assumption would be, that it is not necessary to keep the same data twice or is there a technical limitation?

Thanks again for helping out, I'll test it shortly. My main issue at this point is the terrible detection quality of the Coral TPU regardless of the model used, but that is not a Viseron problem :)

@jin-eld
Copy link
Author

jin-eld commented Nov 19, 2024

Did a short test with the suggested setting and the complete .mp4 videos of individual events got created, however when I did a graceful shutdown, the .mp4 files got lost.

My assumption was, that move_on_shutdown: true would move the videos to the next tier:

storage:
  recorder:
    tiers:
      - path: /ramdisk
        events:
          max_age:
            days: 1
          max_size:
            gb: 1.5
        move_on_shutdown: true

      - path: /recordings
        events:
          max_age:
            days: 7
          max_size:
            gb: 80

Does this work differently or is my configuration not correct?

@roflcoopter
Copy link
Owner

Thank you for getting back to me! Yes indeed I saw the segments on ramdisk, but assumed they were something internal, especially seeing in the logs how they get regularly deleted.

I was indeed looking for one complete clip for the whole event, expecting it to end up in /recordings at some point, allowing local high res review and evidence collecting if need be.

I now found it in the sources and have a question regarding this remark:

WARNING: Will store both the fragments AND the MP4 file, using more storage space.

Does that mean, that after the event fragments get concatenated into one event mp4, they will not get cleaned up? If so, why? My naive assumption would be, that it is not necessary to keep the same data twice or is there a technical limitation?

Thanks again for helping out, I'll test it shortly. My main issue at this point is the terrible detection quality of the Coral TPU regardless of the model used, but that is not a Viseron problem :)

Correct, the fragments are not cleaned up since they are what Viseron uses mainly.

The Viseron UI does not utilize the MP4 recordings at all, it only displays HLS playlists using the m4s files.
The event clip is there just to allow easy browsing outside of the Viseron UI.
I could possibly allow the user to choose wether to use the MP4 files or the m4s files for playback in the browser, but i feel that would get very complex and its not something i have time to look into right now sadly.

@roflcoopter
Copy link
Owner

Did a short test with the suggested setting and the complete .mp4 videos of individual events got created, however when I did a graceful shutdown, the .mp4 files got lost.

My assumption was, that move_on_shutdown: true would move the videos to the next tier:

storage:
  recorder:
    tiers:
      - path: /ramdisk
        events:
          max_age:
            days: 1
          max_size:
            gb: 1.5
        move_on_shutdown: true

      - path: /recordings
        events:
          max_age:
            days: 7
          max_size:
            gb: 80

Does this work differently or is my configuration not correct?

That should be working, let me investigate.

Just a note on your configuration, the second tier pointing to /recordings will result in subfolders like /recordings/segments, /recordings/recordings etc

@jin-eld
Copy link
Author

jin-eld commented Nov 19, 2024

Correct, the fragments are not cleaned up since they are what Viseron uses mainly.

The Viseron UI does not utilize the MP4 recordings at all, it only displays HLS playlists using the m4s files. The event clip is there just to allow easy browsing outside of the Viseron UI. I could possibly allow the user to choose wether to use the MP4 files or the m4s files for playback in the browser, but i feel that would get very complex and its not something i have time to look into right now sadly.

OK, floating some ideas:
perhaps a simpler workaround would be to have an option which forces the creation of .mp4 files on the next tier? As I understand the segments are being rotated frequently, so having them on a ramdisk makes sense in order to be kinder to the eMMC. The use case of the mp4 files is based on the need to store them for later review or evidence outside of Viseron, so it does not sound wrong creating them directly in the long-term i.e. tier2 storage?
Goal is to offload the ramdisk spacewise, to ensure that it can continue handling new events without running out of space due to same data that will be present twice.

Just a note on your configuration, the second tier pointing to /recordings will result in subfolders like /recordings/segments, /recordings/recordings etc

Yes, thanks for the hint, I noticed 😅

@jin-eld
Copy link
Author

jin-eld commented Nov 19, 2024

I have more logs and a somewhat strange behaviour, I should probably update my dev image to make sure I am running the latest code.

So, after making some changes to the configuration (enabled more detection labels) I hit the restart button in the UI, which resulted in the following backtrace:

viseron  | [2024-11-19 18:34:43] [INFO    ] [viseron.core] - Initiating shutdown
viseron  | [2024-11-19 18:34:43] [INFO    ] [viseron.components.nvr.nvr.camera_1] - Stopping NVR thread
viseron  | [2024-11-19 18:34:47] [ERROR   ] [root] - Uncaught thread exception in thread Thread-105 (_shutdown)
viseron  | Traceback (most recent call last):
viseron  |   File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/engine/result.py", line 199, in _key_not_found
viseron  |     self._key_fallback(key, None)
viseron  |   File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/engine/result.py", line 138, in _key_fallback
viseron  |     raise KeyError(key) from err
viseron  | KeyError: 'path'
viseron  | 
viseron  | The above exception was the direct cause of the following exception:
viseron  | 
viseron  | Traceback (most recent call last):
viseron  |   File "/usr/lib/python3.10/threading.py", line 1016, in _bootstrap_inner
viseron  |     self.run()
viseron  |   File "/usr/lib/python3.10/threading.py", line 953, in run
viseron  |     self._target(*self._args, **self._kwargs)
viseron  |   File "/src/viseron/components/storage/tier_handler.py", line 346, in _shutdown
viseron  |     force_move_files(
viseron  |   File "/src/viseron/components/storage/tier_handler.py", line 1030, in force_move_files
viseron  |     file.path,
viseron  |   File "lib/sqlalchemy/cyextension/resultproxy.pyx", line 66, in sqlalchemy.cyextension.resultproxy.BaseRow.__getattr__
viseron  |   File "lib/sqlalchemy/cyextension/resultproxy.pyx", line 63, in sqlalchemy.cyextension.resultproxy.BaseRow._get_by_key_impl
viseron  |   File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/engine/result.py", line 201, in _key_not_found
viseron  |     raise AttributeError(ke.args[0]) from ke
viseron  | AttributeError: path
viseron  | [2024-11-19 18:34:47] [ERROR   ] [root] - Uncaught thread exception in thread Thread-104 (_shutdown)
viseron  | Traceback (most recent call last):
viseron  |   File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/engine/result.py", line 199, in _key_not_found
viseron  |     self._key_fallback(key, None)
viseron  |   File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/engine/result.py", line 138, in _key_fallback
viseron  |     raise KeyError(key) from err
viseron  | KeyError: 'path'
viseron  | 
viseron  | The above exception was the direct cause of the following exception:
viseron  | 
viseron  | Traceback (most recent call last):
viseron  |   File "/usr/lib/python3.10/threading.py", line 1016, in _bootstrap_inner
viseron  |     self.run()
viseron  |   File "/usr/lib/python3.10/threading.py", line 953, in run
viseron  |     self._target(*self._args, **self._kwargs)
viseron  |   File "/src/viseron/components/storage/tier_handler.py", line 346, in _shutdown
viseron  |     force_move_files(
viseron  |   File "/src/viseron/components/storage/tier_handler.py", line 1030, in force_move_files
viseron  |     file.path,
viseron  |   File "lib/sqlalchemy/cyextension/resultproxy.pyx", line 66, in sqlalchemy.cyextension.resultproxy.BaseRow.__getattr__
viseron  |   File "lib/sqlalchemy/cyextension/resultproxy.pyx", line 63, in sqlalchemy.cyextension.resultproxy.BaseRow._get_by_key_impl
viseron  |   File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/engine/result.py", line 201, in _key_not_found
viseron  |     raise AttributeError(ke.args[0]) from ke
viseron  | AttributeError: path
viseron  | [2024-11-19 18:34:47] [ERROR   ] [root] - Uncaught thread exception in thread Thread-106 (_shutdown)
viseron  | Traceback (most recent call last):
viseron  |   File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/engine/result.py", line 199, in _key_not_found
viseron  |     self._key_fallback(key, None)
viseron  |   File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/engine/result.py", line 138, in _key_fallback
viseron  |     raise KeyError(key) from err
viseron  | KeyError: 'path'
viseron  | 
viseron  | The above exception was the direct cause of the following exception:
viseron  | 
viseron  | Traceback (most recent call last):
viseron  |   File "/usr/lib/python3.10/threading.py", line 1016, in _bootstrap_inner
viseron  |     self.run()
viseron  |   File "/usr/lib/python3.10/threading.py", line 953, in run
viseron  |     self._target(*self._args, **self._kwargs)
viseron  |   File "/src/viseron/components/storage/tier_handler.py", line 346, in _shutdown
viseron  |     force_move_files(
viseron  |   File "/src/viseron/components/storage/tier_handler.py", line 1030, in force_move_files
viseron  |     file.path,
viseron  |   File "lib/sqlalchemy/cyextension/resultproxy.pyx", line 66, in sqlalchemy.cyextension.resultproxy.BaseRow.__getattr__
viseron  |   File "lib/sqlalchemy/cyextension/resultproxy.pyx", line 63, in sqlalchemy.cyextension.resultproxy.BaseRow._get_by_key_impl
viseron  |   File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/engine/result.py", line 201, in _key_not_found
viseron  |     raise AttributeError(ke.args[0]) from ke
viseron  | AttributeError: path
viseron  | [2024-11-19 18:34:47] [ERROR   ] [root] - Uncaught thread exception in thread Thread-110 (_shutdown)
viseron  | Traceback (most recent call last):
viseron  |   File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/engine/result.py", line 199, in _key_not_found
viseron  |     self._key_fallback(key, None)
viseron  |   File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/engine/result.py", line 138, in _key_fallback
viseron  |     raise KeyError(key) from err
viseron  | KeyError: 'path'
viseron  | 
viseron  | The above exception was the direct cause of the following exception:
viseron  | 
viseron  | Traceback (most recent call last):
viseron  |   File "/usr/lib/python3.10/threading.py", line 1016, in _bootstrap_inner
viseron  |     self.run()
viseron  |   File "/usr/lib/python3.10/threading.py", line 953, in run
viseron  |     self._target(*self._args, **self._kwargs)
viseron  |   File "/src/viseron/components/storage/tier_handler.py", line 346, in _shutdown
viseron  |     force_move_files(
viseron  |   File "/src/viseron/components/storage/tier_handler.py", line 1030, in force_move_files
viseron  |     file.path,
viseron  |   File "lib/sqlalchemy/cyextension/resultproxy.pyx", line 66, in sqlalchemy.cyextension.resultproxy.BaseRow.__getattr__
viseron  |   File "lib/sqlalchemy/cyextension/resultproxy.pyx", line 63, in sqlalchemy.cyextension.resultproxy.BaseRow._get_by_key_impl
viseron  |   File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/engine/result.py", line 201, in _key_not_found
viseron  |     raise AttributeError(ke.args[0]) from ke
viseron  | AttributeError: path
viseron  | [2024-11-19 18:34:47] [ERROR   ] [root] - Uncaught thread exception in thread Thread-111 (_shutdown)
viseron  | Traceback (most recent call last):
viseron  |   File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/engine/result.py", line 199, in _key_not_found
viseron  |     self._key_fallback(key, None)
viseron  |   File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/engine/result.py", line 138, in _key_fallback
viseron  |     raise KeyError(key) from err
viseron  | KeyError: 'path'
viseron  | 
viseron  | The above exception was the direct cause of the following exception:
viseron  | 
viseron  | Traceback (most recent call last):
viseron  |   File "/usr/lib/python3.10/threading.py", line 1016, in _bootstrap_inner
viseron  |     self.run()
viseron  |   File "/usr/lib/python3.10/threading.py", line 953, in run
viseron  |     self._target(*self._args, **self._kwargs)
viseron  |   File "/src/viseron/components/storage/tier_handler.py", line 346, in _shutdown
viseron  |     force_move_files(
viseron  |   File "/src/viseron/components/storage/tier_handler.py", line 1030, in force_move_files
viseron  |     file.path,
viseron  |   File "lib/sqlalchemy/cyextension/resultproxy.pyx", line 66, in sqlalchemy.cyextension.resultproxy.BaseRow.__getattr__
viseron  |   File "lib/sqlalchemy/cyextension/resultproxy.pyx", line 63, in sqlalchemy.cyextension.resultproxy.BaseRow._get_by_key_impl
viseron  |   File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/engine/result.py", line 201, in _key_not_found
viseron  |     raise AttributeError(ke.args[0]) from ke
viseron  | AttributeError: path
viseron  | [2024-11-19 18:34:47] [ERROR   ] [root] - Uncaught thread exception in thread Thread-112 (_shutdown)
viseron  | Traceback (most recent call last):
viseron  |   File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/engine/result.py", line 199, in _key_not_found
viseron  |     self._key_fallback(key, None)
viseron  |   File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/engine/result.py", line 138, in _key_fallback
viseron  |     raise KeyError(key) from err
viseron  | KeyError: 'path'
viseron  | 
viseron  | The above exception was the direct cause of the following exception:
viseron  | 
viseron  | Traceback (most recent call last):
viseron  |   File "/usr/lib/python3.10/threading.py", line 1016, in _bootstrap_inner
viseron  |     self.run()
viseron  |   File "/usr/lib/python3.10/threading.py", line 953, in run
viseron  |     self._target(*self._args, **self._kwargs)
viseron  |   File "/src/viseron/components/storage/tier_handler.py", line 346, in _shutdown
viseron  |     force_move_files(
viseron  |   File "/src/viseron/components/storage/tier_handler.py", line 1030, in force_move_files
viseron  |     file.path,
viseron  |   File "lib/sqlalchemy/cyextension/resultproxy.pyx", line 66, in sqlalchemy.cyextension.resultproxy.BaseRow.__getattr__
viseron  |   File "lib/sqlalchemy/cyextension/resultproxy.pyx", line 63, in sqlalchemy.cyextension.resultproxy.BaseRow._get_by_key_impl
viseron  |   File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/engine/result.py", line 201, in _key_not_found
viseron  |     raise AttributeError(ke.args[0]) from ke
viseron  | AttributeError: path
viseron  | [2024-11-19 18:34:47] [ERROR   ] [root] - Uncaught thread exception in thread Thread-113 (_shutdown)
viseron  | Traceback (most recent call last):
viseron  |   File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/engine/result.py", line 199, in _key_not_found
viseron  |     self._key_fallback(key, None)
viseron  |   File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/engine/result.py", line 138, in _key_fallback
viseron  |     raise KeyError(key) from err
viseron  | KeyError: 'path'
viseron  | 
viseron  | The above exception was the direct cause of the following exception:
viseron  | 
viseron  | Traceback (most recent call last):
viseron  |   File "/usr/lib/python3.10/threading.py", line 1016, in _bootstrap_inner
viseron  |     self.run()
viseron  |   File "/usr/lib/python3.10/threading.py", line 953, in run
viseron  |     self._target(*self._args, **self._kwargs)
viseron  |   File "/src/viseron/components/storage/tier_handler.py", line 346, in _shutdown
viseron  |     force_move_files(
viseron  |   File "/src/viseron/components/storage/tier_handler.py", line 1030, in force_move_files
viseron  |     file.path,
viseron  |   File "lib/sqlalchemy/cyextension/resultproxy.pyx", line 66, in sqlalchemy.cyextension.resultproxy.BaseRow.__getattr__
viseron  |   File "lib/sqlalchemy/cyextension/resultproxy.pyx", line 63, in sqlalchemy.cyextension.resultproxy.BaseRow._get_by_key_impl
viseron  |   File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/engine/result.py", line 201, in _key_not_found
viseron  |     raise AttributeError(ke.args[0]) from ke
viseron  | AttributeError: path
viseron  | [2024-11-19 18:34:49] [INFO    ] [viseron.core] - Shutdown complete
viseron  | [viseron-finish] Viseron exit code 100
viseron  | /var/run/postgresql:5432 - accepting connections
viseron  | PostgreSQL Server has started!
viseron  | [2024-11-19 18:34:50] [INFO    ] [viseron.core] - -------------------------------------------
viseron  | [2024-11-19 18:34:50] [INFO    ] [viseron.core] - Initializing Viseron dev

the startup after that looked fine, so I assume all is good, but when I returned to check the events I saw that nothing new has been recorded, although I was outside to test detection.

I checked the logs and saw the following errors:

viseron  | [2024-11-19 19:49:27] [INFO    ] [viseron.components.ffmpeg.recorder.camera_1] - Starting recorder
viseron  | [2024-11-19 19:49:27] [ERROR   ] [viseron.components.storage.tier_handler.camera_1.tier_0] - Failed to delete file /ramdisk/snapshots/object_detector/camera_1/2024-11-18-17-02-28-917682d3-e657-4e84-b422-60645729b722.jpg: [Errno 2] No such file or directory: '/ramdisk/snapshots/object_detector/camera_1/2024-11-18-17-02-28-917682d3-e657-4e84-b422-60645729b722.jpg'
viseron  | [2024-11-19 19:49:27] [ERROR   ] [viseron.components.storage.tier_handler.camera_1.tier_0] - Failed to delete file /ramdisk/snapshots/object_detector/camera_1/2024-11-18-17-03-28-43abae53-3d51-41e1-83e2-bc228c7716f2.jpg: [Errno 2] No such file or directory: '/ramdisk/snapshots/object_detector/camera_1/2024-11-18-17-03-28-43abae53-3d51-41e1-83e2-bc228c7716f2.jpg'
viseron  | [2024-11-19 19:49:27] [ERROR   ] [viseron.components.storage.tier_handler.camera_1.tier_0] - Failed to delete file /ramdisk/snapshots/object_detector/camera_1/2024-11-18-18-48-55-d335c1b5-cc77-4e6c-afc8-d6499b12010e.jpg: [Errno 2] No such file or directory: '/ramdisk/snapshots/object_detector/camera_1/2024-11-18-18-48-55-d335c1b5-cc77-4e6c-afc8-d6499b12010e.jpg'
viseron  | [2024-11-19 19:49:32] [INFO    ] [viseron.components.nvr.nvr.camera_1] - Stopping recording in: 9
viseron  | [2024-11-19 19:49:35] [INFO    ] [viseron.components.nvr.nvr.camera_1] - Stopping recording in: 8
viseron  | [2024-11-19 19:49:38] [INFO    ] [viseron.components.nvr.nvr.camera_1] - Stopping recording in: 7
viseron  | [2024-11-19 19:49:41] [INFO    ] [viseron.components.nvr.nvr.camera_1] - Stopping recording in: 6
viseron  | [2024-11-19 19:49:43] [INFO    ] [viseron.components.nvr.nvr.camera_1] - Stopping recording in: 5
viseron  | [2024-11-19 19:49:46] [INFO    ] [viseron.components.nvr.nvr.camera_1] - Stopping recording in: 4
viseron  | [2024-11-19 19:49:49] [INFO    ] [viseron.components.nvr.nvr.camera_1] - Stopping recording in: 3
viseron  | [2024-11-19 19:49:52] [INFO    ] [viseron.components.nvr.nvr.camera_1] - Stopping recording in: 2
viseron  | [2024-11-19 19:49:52] [ERROR   ] [viseron.components.storage.tier_handler.camera_1.tier_0] - Failed to move file /ramdisk/segments/camera_1/1731955730.m4s to /recordings/segments/camera_1/1731955730.m4s: [Errno 2] No such file or directory: '/ramdisk/segments/camera_1/1731955730.m4s'
viseron  | [2024-11-19 19:49:52] [ERROR   ] [viseron.components.storage.tier_handler.camera_1.tier_0] - Failed to move file /ramdisk/segments/camera_1/1731955735.m4s to /recordings/segments/camera_1/1731955735.m4s: [Errno 2] No such file or directory: '/ramdisk/segments/camera_1/1731955735.m4s'
viseron  | [2024-11-19 19:49:52] [ERROR   ] [viseron.components.storage.tier_handler.camera_1.tier_0] - Failed to move file /ramdisk/segments/camera_1/1731955738.m4s to /recordings/segments/camera_1/1731955738.m4s: [Errno 2] No such file or directory: '/ramdisk/segments/camera_1/1731955738.m4s'
viseron  | [2024-11-19 19:49:52] [ERROR   ] [viseron.components.storage.tier_handler.camera_1.tier_0] - Failed to move file /ramdisk/segments/camera_1/1731955744.m4s to /recordings/segments/camera_1/1731955744.m4s: [Errno 2] No such file or directory: '/ramdisk/segments/camera_1/1731955744.m4s'
viseron  | [2024-11-19 19:49:52] [ERROR   ] [viseron.components.storage.tier_handler.camera_1.tier_0] - Failed to move file /ramdisk/segments/camera_1/1731955750.m4s to /recordings/segments/camera_1/1731955750.m4s: [Errno 2] No such file or directory: '/ramdisk/segments/camera_1/1731955750.m4s'
viseron  | [2024-11-19 19:49:52] [ERROR   ] [viseron.components.storage.tier_handler.camera_1.tier_0] - Failed to move file /ramdisk/segments/camera_1/1731955756.m4s to /recordings/segments/camera_1/1731955756.m4s: [Errno 2] No such file or directory: '/ramdisk/segments/camera_1/1731955756.m4s'
viseron  | [2024-11-19 19:49:52] [ERROR   ] [viseron.components.storage.tier_handler.camera_1.tier_0] - Failed to move file /ramdisk/segments/camera_1/1731955759.m4s to /recordings/segments/camera_1/1731955759.m4s: [Errno 2] No such file or directory: '/ramdisk/segments/camera_1/1731955759.m4s'
viseron  | [2024-11-19 19:49:52] [ERROR   ] [viseron.components.storage.tier_handler.camera_1.tier_0] - Failed to move file /ramdisk/segments/camera_1/1731955764.m4s to /recordings/segments/camera_1/1731955764.m4s: [Errno 2] No such file or directory: '/ramdisk/segments/camera_1/1731955764.m4s'
viseron  | [2024-11-19 19:49:52] [ERROR   ] [viseron.components.storage.tier_handler.camera_1.tier_0] - Failed to move file /ramdisk/segments/camera_1/1731955770.m4s to /recordings/segments/camera_1/1731955770.m4s: [Errno 2] No such file or directory: '/ramdisk/segments/camera_1/1731955770.m4s'
viseron  | [2024-11-19 19:49:52] [ERROR   ] [viseron.components.storage.tier_handler.camera_1.tier_0] - Failed to move file /ramdisk/segments/camera_1/1731955773.m4s to /recordings/segments/camera_1/1731955773.m4s: [Errno 2] No such file or directory: '/ramdisk/segments/camera_1/1731955773.m4s'
viseron  | [2024-11-19 19:49:52] [ERROR   ] [viseron.components.storage.tier_handler.camera_1.tier_0] - Failed to move file /ramdisk/segments/camera_1/1731955779.m4s to /recordings/segments/camera_1/1731955779.m4s: [Errno 2] No such file or directory: '/ramdisk/segments/camera_1/1731955779.m4s'
viseron  | [2024-11-19 19:49:52] [ERROR   ] [viseron.components.storage.tier_handler.camera_1.tier_0] - Failed to move file /ramdisk/segments/camera_1/1731955784.m4s to /recordings/segments/camera_1/1731955784.m4s: [Errno 2] No such file or directory: '/ramdisk/segments/camera_1/1731955784.m4s'
viseron  | [2024-11-19 19:49:52] [ERROR   ] [viseron.components.storage.tier_handler.camera_1.tier_0] - Failed to move file /ramdisk/segments/camera_1/1731955790.m4s to /recordings/segments/camera_1/1731955790.m4s: [Errno 2] No such file or directory: '/ramdisk/segments/camera_1/1731955790.m4s'
viseron  | [2024-11-19 19:49:52] [ERROR   ] [viseron.components.storage.tier_handler.camera_1.tier_0] - Failed to move file /ramdisk/segments/camera_1/1731955796.m4s to /recordings/segments/camera_1/1731955796.m4s: [Errno 2] No such file or directory: '/ramdisk/segments/camera_1/1731955796.m4s'
viseron  | [2024-11-19 19:49:52] [ERROR   ] [viseron.components.storage.tier_handler.camera_1.tier_0] - Failed to move file /ramdisk/segments/camera_1/1731955799.m4s to /recordings/segments/camera_1/1731955799.m4s: [Errno 2] No such file or directory: '/ramdisk/segments/camera_1/1731955799.m4s'
viseron  | [2024-11-19 19:49:52] [ERROR   ] [viseron.components.storage.tier_handler.camera_1.tier_0] - Failed to move file /ramdisk/thumbnails/camera_1/95.jpg to /recordings/thumbnails/camera_1/95.jpg: [Errno 2] No such file or directory: '/ramdisk/thumbnails/camera_1/95.jpg'
viseron  | [2024-11-19 19:49:52] [ERROR   ] [viseron.components.storage.tier_handler.camera_1.tier_0] - Failed to move file /ramdisk/thumbnails/camera_1/96.jpg to /recordings/thumbnails/camera_1/96.jpg: [Errno 2] No such file or directory: '/ramdisk/thumbnails/camera_1/96.jpg'
viseron  | [2024-11-19 19:49:55] [INFO    ] [viseron.components.nvr.nvr.camera_1] - Stopping recording in: 1
viseron  | [2024-11-19 19:49:57] [INFO    ] [viseron.components.nvr.nvr.camera_1] - Stopping recording in: 0
viseron  | [2024-11-19 19:49:57] [INFO    ] [viseron.components.ffmpeg.recorder.camera_1] - Stopping recorder
viseron  | [2024-11-19 19:49:58] [ERROR   ] [root] - Uncaught thread exception in thread tier_handler_camera_1
viseron  | Traceback (most recent call last):
viseron  |   File "/usr/lib/python3.10/threading.py", line 1016, in _bootstrap_inner
viseron  |     self.run()
viseron  |   File "/usr/lib/python3.10/threading.py", line 953, in run
viseron  |     self._target(*self._args, **self._kwargs)
viseron  |   File "/src/viseron/components/storage/tier_handler.py", line 236, in _process_events
viseron  |     self._on_created(event)
viseron  |   File "/src/viseron/components/storage/tier_handler.py", line 768, in _on_created
viseron  |     super()._on_created(event)
viseron  |   File "/src/viseron/components/storage/tier_handler.py", line 262, in _on_created
viseron  |     session.execute(stmt)
viseron  |   File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/orm/session.py", line 2351, in execute
viseron  |     return self._execute_internal(
viseron  |   File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/orm/session.py", line 2236, in _execute_internal
viseron  |     result: Result[Any] = compile_state_cls.orm_execute_statement(
viseron  |   File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/orm/bulk_persistence.py", line 1283, in orm_execute_statement
viseron  |     result = conn.execute(
viseron  |   File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/engine/base.py", line 1418, in execute
viseron  |     return meth(
viseron  |   File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/sql/elements.py", line 515, in _execute_on_connection
viseron  |     return connection._execute_clauseelement(
viseron  |   File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/engine/base.py", line 1609, in _execute_clauseelement
viseron  |     ) = self._invoke_before_exec_event(
viseron  |   File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/engine/base.py", line 1568, in _invoke_before_exec_event
viseron  |     elem, event_multiparams, event_params = fn(
viseron  |   File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/engine/events.py", line 160, in wrap_before_execute
viseron  |     orig_fn(
viseron  |   File "/src/viseron/components/storage/triggers.py", line 24, in insert_into_files_meta
viseron  |     conn.execute(
viseron  |   File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/engine/base.py", line 1418, in execute
viseron  |     return meth(
viseron  |   File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/sql/elements.py", line 515, in _execute_on_connection
viseron  |     return connection._execute_clauseelement(
viseron  |   File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/engine/base.py", line 1640, in _execute_clauseelement
viseron  |     ret = self._execute_context(
viseron  |   File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/engine/base.py", line 1846, in _execute_context
viseron  |     return self._exec_single_context(
viseron  |   File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/engine/base.py", line 1986, in _exec_single_context
viseron  |     self._handle_dbapi_exception(
viseron  |   File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/engine/base.py", line 2356, in _handle_dbapi_exception
viseron  |     raise exc_info[1].with_traceback(exc_info[2])
viseron  |   File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/engine/base.py", line 1967, in _exec_single_context
viseron  |     self.dialect.do_execute(
viseron  |   File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/engine/default.py", line 924, in do_execute
viseron  |     cursor.execute(statement, parameters)
viseron  | UnicodeEncodeError: 'ascii' codec can't encode characters in position 40-45: ordinal not in range(128)
viseron  | [2024-11-19 19:49:58] [ERROR   ] [root] - Uncaught thread exception in thread Thread-10404 (_concatenate_fragments)
viseron  | Traceback (most recent call last):
viseron  |   File "/usr/lib/python3.10/threading.py", line 1016, in _bootstrap_inner
viseron  |     self.run()
viseron  |   File "/usr/lib/python3.10/threading.py", line 953, in run
viseron  |     self._target(*self._args, **self._kwargs)
viseron  |   File "/src/viseron/domains/camera/recorder.py", line 405, in _concatenate_fragments
viseron  |     session.execute(stmt)
viseron  |   File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/orm/session.py", line 2351, in execute
viseron  |     return self._execute_internal(
viseron  |   File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/orm/session.py", line 2236, in _execute_internal
viseron  |     result: Result[Any] = compile_state_cls.orm_execute_statement(
viseron  |   File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/orm/bulk_persistence.py", line 1624, in orm_execute_statement
viseron  |     return super().orm_execute_statement(
viseron  |   File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/orm/context.py", line 293, in orm_execute_statement
viseron  |     result = conn.execute(
viseron  |   File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/engine/base.py", line 1418, in execute
viseron  |     return meth(
viseron  |   File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/sql/elements.py", line 515, in _execute_on_connection
viseron  |     return connection._execute_clauseelement(
viseron  |   File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/engine/base.py", line 1640, in _execute_clauseelement
viseron  |     ret = self._execute_context(
viseron  |   File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/engine/base.py", line 1846, in _execute_context
viseron  |     return self._exec_single_context(
viseron  |   File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/engine/base.py", line 1986, in _exec_single_context
viseron  |     self._handle_dbapi_exception(
viseron  |   File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/engine/base.py", line 2356, in _handle_dbapi_exception
viseron  |     raise exc_info[1].with_traceback(exc_info[2])
viseron  |   File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/engine/base.py", line 1967, in _exec_single_context
viseron  |     self.dialect.do_execute(
viseron  |   File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/engine/default.py", line 924, in do_execute
viseron  |     cursor.execute(statement, parameters)
viseron  | UnicodeEncodeError: 'ascii' codec can't encode characters in position 40-45: ordinal not in range(128)
viseron  | [2024-11-19 19:50:05] [ERROR   ] [viseron.watchdog.thread_watchdog] - Thread tier_handler_camera_1 is dead, restarting
viseron  | [2024-11-19 19:50:06] [ERROR   ] [root] - Uncaught thread exception in thread Thread-10429
viseron  | Traceback (most recent call last):
viseron  |   File "/usr/lib/python3.10/threading.py", line 1016, in _bootstrap_inner
viseron  |     self.run()
viseron  |   File "/usr/lib/python3.10/threading.py", line 1378, in run
viseron  |     self.function(*self.args, **self.kwargs)
viseron  |   File "/src/viseron/components/storage/tier_handler.py", line 307, in _update_size
viseron  |     session.execute(stmt)
viseron  |   File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/orm/session.py", line 2351, in execute
viseron  |     return self._execute_internal(
viseron  |   File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/orm/session.py", line 2236, in _execute_internal
viseron  |     result: Result[Any] = compile_state_cls.orm_execute_statement(
viseron  |   File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/orm/bulk_persistence.py", line 1624, in orm_execute_statement
viseron  |     return super().orm_execute_statement(
viseron  |   File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/orm/context.py", line 293, in orm_execute_statement
viseron  |     result = conn.execute(
viseron  |   File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/engine/base.py", line 1418, in execute
viseron  |     return meth(
viseron  |   File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/sql/elements.py", line 515, in _execute_on_connection
viseron  |     return connection._execute_clauseelement(
viseron  |   File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/engine/base.py", line 1640, in _execute_clauseelement
viseron  |     ret = self._execute_context(
viseron  |   File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/engine/base.py", line 1846, in _execute_context
viseron  |     return self._exec_single_context(
viseron  |   File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/engine/base.py", line 1986, in _exec_single_context
viseron  |     self._handle_dbapi_exception(
viseron  |   File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/engine/base.py", line 2356, in _handle_dbapi_exception
viseron  |     raise exc_info[1].with_traceback(exc_info[2])
viseron  |   File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/engine/base.py", line 1967, in _exec_single_context
viseron  |     self.dialect.do_execute(
viseron  |   File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/engine/default.py", line 924, in do_execute
viseron  |     cursor.execute(statement, parameters)
viseron  | UnicodeEncodeError: 'ascii' codec can't encode characters in position 40-45: ordinal not in range(128)

Not sure what happened there and why the files were missing. I did not restart the whole container, I only restarted Viseron via the UI.

The encoding error is also quite strange, I do not use anything except UTF-8.

@roflcoopter
Copy link
Owner

OK, floating some ideas:
perhaps a simpler workaround would be to have an option which forces the creation of .mp4 files on the next tier? As I understand the segments are being rotated frequently, so having them on a ramdisk makes sense in order to be kinder to the eMMC. The use case of the mp4 files is based on the need to store them for later review or evidence outside of Viseron, so it does not sound wrong creating them directly in the long-term i.e. tier2 storage?
Goal is to offload the ramdisk spacewise, to ensure that it can continue handling new events without running out of space due to same data that will be present twice.

The segments are stored for as long as they are wanted.
In your case, since you are only storing events, only the segments that relate to an event (triggered by trigger_recorder: true will be kept.
I realize the naming here between events and recordings is a bit confusing, but it stems from v2 which functioned in a very different way.

I do see the need for storing the MP4 files, and you are not the first to have similar requirements.
What i could do is introduce event_clips as a separate section in the tier config which would allow for more control of where they end up.
Something like this:

storage:
  recorder:
    tiers:
      - path: /config/tier1
        continuous:
          max_size:
            mb: 50
        events:
          max_size:
            mb: 100
        event_clips:
          max_size:
            mb: 1 # Setting a low value would essentially mean they get moved instantly
        move_on_shutdown: true
      - path: /config/tier2 # Store only event clips in this tier
        event_clips:
          max_size:
            gb: 100   
      - path: /config/tier3
        continuous:
          max_size:
            mb: 50
        events:
          max_size:
            mb: 100

I will have a look at how complicated that would be. v3 has been in beta for ages now and i would like to get it released soon and i can work on polishing it further later on.

@roflcoopter
Copy link
Owner

I have more logs and a somewhat strange behaviour, I should probably update my dev image to make sure I am running the latest code.

So, after making some changes to the configuration (enabled more detection labels) I hit the restart button in the UI, which resulted in the following backtrace:

Yes i noticed that as well, move_on_shutdown is not working correctly.
I have it fixed locally, just need to clean it up before pushing to dev.

Not sure what happened there and why the files were missing. I did not restart the whole container, I only restarted Viseron via the UI.

The missing files most likely a side effect of the unclean shutdown.

The encoding error is also quite strange, I do not use anything except UTF-8.

Indeed, never seen that before. Can you enable debug logging and see if the error keeps appearing?

logger:
  default_level: debug

@jin-eld
Copy link
Author

jin-eld commented Nov 19, 2024

The segments are stored for as long as they are wanted.
In your case, since you are only storing events, only the segments that relate to an event (triggered by trigger_recorder: true will be kept.
I realize the naming here between events and recordings is a bit confusing, but it stems from v2 which functioned in a very different way.

I guess it is :) So, my understanding was that an "event" is something that triggers a recording when it occurs, so essentially in the end you get a recording of the event. Now, seeing your config proposal I think I did not get that right, it seems there are "events" and "event clips" which is not the same? Ah, I think I know what you mean, an "event clip" is the complete mp4? I am not sure if these names are not too confusing...

From the user perspective I consider the gazillion of segments something internal to how the application works, focus of those is to show up and be playable in the UI. They should get rotated according to the configured rules to ensure the system does not run out of space, but otherwise I do not think that anyone or anything except for the application itself is doing anything with those files?

The complete mp4 files, as you called them "event clips" are actually what a user would want to grab and copy out of the Viseron storage and save them permanently, the assumption here is, that these files are needed outside of Viseron.

Let me float yet another idea :) So, as long as I am within Viseron (i.e. using the UI) I do not really care what format the files are in, the UI plays everything nicely and I can look through the events to check what happened. The format only becomes interesting as soon as I see something caught on camera, that I need to store for myself permanently. This is actually the use case for having an mp4 clip of the event in the first place.

So, perhaps a more intuitive way would be to offer some sort of an export button in the UI, allowing to convert selected events to "event clips" and have them saved in a dedicated storage tier.

This way there is no automatic duplication as there is no real need for bulk-saving the mp4 clips - they are only needed when the user saw an event that needs to be saved permanently and since reviewing the events is anyway a manual step, hitting a button in the UI to generate an mp4 clip should be totally fine from a user's perspective. What do you think?

v3 has been in beta for ages now and i would like to get it released soon and i can work on polishing it further later on.

I totally get that, as a developer I also do not like when something does not come to an end and just drags on and on, better to get out a stable release with lesser features and then follow up by smaller releases, than polishing a scope-creeped beta release for years :>

Indeed, never seen that before. Can you enable debug logging and see if the error keeps appearing?

Unfortunately I was not able to reproduce it, i.e. the exceptions during the shutdown are there, but the exception at start did not occur anymore. Perhaps some UTF-8 characters got truncated when saving due to the shutdown exceptions and produced illegal UTF-8, which in turn caused an exception on startup? Just guessing..

@roflcoopter
Copy link
Owner

I guess it is :) So, my understanding was that an "event" is something that triggers a recording when it occurs, so essentially in the end you get a recording of the event. Now, seeing your config proposal I think I did not get that right, it seems there are "events" and "event clips" which is not the same? Ah, I think I know what you mean, an "event clip" is the complete mp4? I am not sure if these names are not too confusing...

I think placing the segments in the /recordings folder and storing the "event clips" in a new folder called /clips would make more sense.
The problem is that v2 uses /recordings to store "event clip" (the only kind of recording in v2) so for easier transition to v3 i had to go with different naming.
Do you think /recordings for the short segments and /clips for clips created by create_event_clip: true would make sense?
Manually created clips probably has to live outside of the Viseron tiers so that they are not cleaned up by the system.

From the user perspective I consider the gazillion of segments something internal to how the application works, focus of those is to show up and be playable in the UI. They should get rotated according to the configured rules to ensure the system does not run out of space, but otherwise I do not think that anyone or anything except for the application itself is doing anything with those files?

Yes that is correct.

The complete mp4 files, as you called them "event clips" are actually what a user would want to grab and copy out of the Viseron storage and save them permanently, the assumption here is, that these files are needed outside of Viseron.

Let me float yet another idea :) So, as long as I am within Viseron (i.e. using the UI) I do not really care what format the files are in, the UI plays everything nicely and I can look through the events to check what happened. The format only becomes interesting as soon as I see something caught on camera, that I need to store for myself permanently. This is actually the use case for having an mp4 clip of the event in the first place.

So, perhaps a more intuitive way would be to offer some sort of an export button in the UI, allowing to convert selected events to "event clips" and have them saved in a dedicated storage tier.

This way there is no automatic duplication as there is no real need for bulk-saving the mp4 clips - they are only needed when the user saw an event that needs to be saved permanently and since reviewing the events is anyway a manual step, hitting a button in the UI to generate an mp4 clip should be totally fine from a user's perspective. What do you think?

Yes having a way to selectively export seems to be a good resolution.
It is already on the to-do list in the v3 PR actually: #619
Hopefully i can have a look at that soon! You are not the first to point out that its a needed feature.

@jin-eld
Copy link
Author

jin-eld commented Nov 21, 2024

The problem is that v2 uses /recordings to store "event clip" (the only kind of recording in v2) so for easier transition to v3 i had to go with different naming.
Do you think /recordings for the short segments and /clips for clips created by create_event_clip: true would make sense?

Hmm, as a user who is new to Viseron, "recordings" was the name where I expected to find the actual coherent mp4 recordings. One has to actually dive a bit deeper to understand the difference between "recordings", "clips" and "segments", actually if short segments are stored in "recordings" and if we also conclude, that those short segments are something internal to Viseron, then why isn't the directory where they are stored called accordingly?

If you have to keep the names as they are now, then indeed having /clips (which are intuitively - recordings) would make sense in conjunction with create_event_clip: true

My later argument was, that if we can get the export feature, then having create_event_clip: true isn't even needed, unless there is another use case which I am missing?

Yes having a way to selectively export seems to be a good resolution.
It is already on the to-do list in the v3 PR actually: #619

Oh it's on the list? I did not see it mentioned anywhere in #619 which is already merged?

Hopefully i can have a look at that soon! You are not the first to point out that its a needed feature.

Awesome! If there is anything to test, please ping me. I am currently setting things up and waiting for the remaining cameras to arrive, so doing a lot of tests and experiments at the moment.

@roflcoopter
Copy link
Owner

Hmm, as a user who is new to Viseron, "recordings" was the name where I expected to find the actual coherent mp4 recordings. One has to actually dive a bit deeper to understand the difference between "recordings", "clips" and "segments", actually if short segments are stored in "recordings" and if we also conclude, that those short segments are something internal to Viseron, then why isn't the directory where they are stored called accordingly?

If you have to keep the names as they are now, then indeed having /clips (which are intuitively - recordings) would make sense in conjunction with create_event_clip: true

My later argument was, that if we can get the export feature, then having create_event_clip: true isn't even needed, unless there is another use case which I am missing?

Yeah a lot of people seem to have the same expectations as you have.
I think writing a proper glossary and also documentation on how Viseron handles recordings would clear up a lot of confusion, and also highlight to myself what to improve.
I am the sole developer on this project and sometimes what makes sense to me might not make sense to others!
So i appreciate you taking the time and writing down your thoughts.

Oh it's on the list? I did not see it mentioned anywhere in #619 which is already merged?

Sorry my bad, its this PR: #716

@jin-eld
Copy link
Author

jin-eld commented Nov 23, 2024

Yeah a lot of people seem to have the same expectations as you have.
I think writing a proper glossary and also documentation on how Viseron handles recordings would clear up a lot of confusion, and also highlight to myself what to improve.

Indeed, I'd be interested to read and understand how you see these things, because the separation of "Events" and "Recordings", where both are playable videos of the same occurrence is not quite clear to me 😅

I am the sole developer on this project and sometimes what makes sense to me might not make sense to others!
So i appreciate you taking the time and writing down your thoughts.

No problem, as a user I'm happy that you are interested in feedback and are considering to tune a thing or two. I've been running an open source project too back in the day, so I know how much work it actually is :)

Sorry my bad, its this PR: #716

Ah, this must be the point "Allow export of timespan"?
How is this planned, export "all recordings within a given timespan"? If possible, please also add a possibility to "export this event/recording", i.e. the one that is being viewed by the user at the moment, that'd be really practical.

@jin-eld
Copy link
Author

jin-eld commented Nov 25, 2024

I am still battling the "clips" issue, so my config has:

      recorder:
        filename_pattern: east_%Y-%m-%d-%H:%M:%S
        create_event_clip: true

So my expectation was that it would now create those mp4 clips that we have been talking about.

I see the event in the UI, but there are no mp4 files corresponding to this event. A few mp4 files with earlier timestamps have been created, but last I see is from 14:00 while I am looking for an event after 15:08
image

I assume it is available in the segments format since I can play it in the UI, but dozens of files named as 1732553822.m4s are not easy to match :(

Also, when I go to "Recordings" in the UI, I only see one video from today, although I have dozens of events which I expected to end up as a "recording", I guess this brings me back to the earlier question on what the difference between "Events" and "Recordings" is and what it is good for?

I do not see any errors in the log, so I really have no clue why no mp4 "clips" have been created for these events, but I'd really like to download them. Any hints on how to figure out what segments belong to which event, without having to go through everything manually?

@roflcoopter
Copy link
Owner

Ah, this must be the point "Allow export of timespan"?
How is this planned, export "all recordings within a given timespan"? If possible, please also add a possibility to "export this event/recording", i.e. the one that is being viewed by the user at the moment, that'd be really practical.

Yes my plan is to allow export of individual events or by specifying a time range.

@roflcoopter
Copy link
Owner

Could you paste your config?

I tried (with your filename_pattern just to be sure) and i get clips created properly.

abc@458814191c5f:/workspaces/viseron/config/tier3/recordings/viseron_vscode_camera/2024-11-25$ ls -al
total 5576
drwxr-xr-x 2 abc abc    4096 Nov 25 22:05 .
drwxr-xr-x 9 abc abc    4096 Nov 25 21:55 ..
-rw-r--r-- 1 abc abc 1977969 Nov 25 21:55 21:53:44.mp4
-rw-r--r-- 1 abc abc 1887373 Nov 25 22:01 22:01:04.mp4
-rw-r--r-- 1 abc abc 1831587 Nov 25 22:05 east_2024-11-25-22:05:04.mp4

Note that they are stored in UTC so depending on your timezone they might not appear where you think they are.

@jin-eld
Copy link
Author

jin-eld commented Nov 26, 2024

Note that they are stored in UTC so depending on your timezone they might not appear where you think they are.

OK, sorry, no need to check the config - this was exactly it, the recordings were made, but time was off, and since I did not see any recordings for after 3pm. I thought that they were missing. Which brings me to another interesting question: how can I, as a user, map the time I see in the UI to the timestamp of the filename (of course knowing this nuance one could "manually" convert the desired time to UTC and figure out what the clip name might be)? I would expect them to be the same, since I am interested in a recording of a particular event and the time when it happened is important. Is the issue with my pattern %Y-%m-%d-%H:%M:%S or is it always UTC?

From a software engineering point of view I understand, that using UTC eliminates all sorts of problems (especially the daylight saving time jump horrors), but when exporting the clip I would expect it to be saved in the same time format that is being displayed in the UI, otherwise it's just confusing... Would you be open to the thought of tuning this (or perhaps adding an option for this) at some point?

@roflcoopter
Copy link
Owner

I agree, it should definitely be in the servers timezone. Will fix for the next beta release

@roflcoopter
Copy link
Owner

Just released 3.0.0b12 which changes the timezone used for the naming of clips.

It contains some fixes for the move_on_shutdown option as well so that should work better now.

@jin-eld
Copy link
Author

jin-eld commented Nov 27, 2024

Cool, thank you for the update, will pull it right away.

Going back to the question of "Events" vs "Recordings" - I was hoping you could explain the idea between these two? I see various recorded events and when I then go to "recordings" there's maybe only one recording and I have no idea what the criteria for making this recording was and how different it is from what was recorded for the event?

I understand, that for 24/7 recordings it becomes unrelated to events as it's just a continuous recording no matter what, but I do not think that I have that enabled and I'm still having a hard time figuring out what the difference between an "event" recording and a "recording" recording is?

EDIT: need to revisit the statement about missing recordings, may have been a user error :)

@jin-eld
Copy link
Author

jin-eld commented Nov 28, 2024

I noticed one more issue which I have been following previously and which is still there in the latest dev version:

after midnight I check the events, naturally it shows only the ones from the current (new) day. I go to the calendar icon, so that I can select the previous date (i.e. before midnight), but I can't click the previous date - I guess, because it thinks, that there are no events there. I know however, that there have been various events during the day and they have been visible before midnight. Has anyone noticed this? @roflcoopter please let me know if I should rather open a new issue about this particular topic.

EDIT: today is the first time that I saw this work - not sure why it did not work previously, but today I was able to select the previous day after midnight.

@jin-eld
Copy link
Author

jin-eld commented Nov 29, 2024

Tested the feature which should move the recordings after shutdown from ramdisk to the next tier. According to the log this seems to work now:

Nov 29 02:31:16 nvr docker-compose[1321]: viseron  | [2024-11-29 02:31:16] [DEBUG   ] [viseron.components.storage.tier_handler.camera_1.tier_0.recorder.segments] - Moving file from /ramdisk/segments/camera_1/1732799807.m4s to /recordings/segments/camera_1/1732799807.m4s
Nov 29 02:31:16 nvr docker-compose[1321]: viseron  | [2024-11-29 02:31:16] [DEBUG   ] [viseron.components.storage.tier_handler.camera_1.tier_0.recorder.segments] - File deleted: /ramdisk/segments/camera_1/1732799802.m4s

@jin-eld
Copy link
Author

jin-eld commented Dec 3, 2024

You can find information on that here: https://viseron.netlify.app/docs/developers/docker

What you need to build is the amd64-viseron container (or any other arch if that is what you are running on)

You mean when I want to test the dev version? I thought the CI is building those anyway and a docker pull of the :dev image would give me the latest and greatest?

@roflcoopter
Copy link
Owner

Oh crap i replied in the wrong issue!
Sorry about that 😄

@roflcoopter
Copy link
Owner

I noticed one more issue which I have been following previously and which is still there in the latest dev version:

after midnight I check the events, naturally it shows only the ones from the current (new) day. I go to the calendar icon, so that I can select the previous date (i.e. before midnight), but I can't click the previous date - I guess, because it thinks, that there are no events there. I know however, that there have been various events during the day and they have been visible before midnight. Has anyone noticed this? @roflcoopter please let me know if I should rather open a new issue about this particular topic.

EDIT: today is the first time that I saw this work - not sure why it did not work previously, but today I was able to select the previous day after midnight.

If you experience that issue again, could you take a screenshot and paste it here?
And also check if a refresh solves the issue.

@roflcoopter
Copy link
Owner

Cool, thank you for the update, will pull it right away.

Going back to the question of "Events" vs "Recordings" - I was hoping you could explain the idea between these two? I see various recorded events and when I then go to "recordings" there's maybe only one recording and I have no idea what the criteria for making this recording was and how different it is from what was recorded for the event?

I understand, that for 24/7 recordings it becomes unrelated to events as it's just a continuous recording no matter what, but I do not think that I have that enabled and I'm still having a hard time figuring out what the difference between an "event" recording and a "recording" recording is?

EDIT: need to revisit the statement about missing recordings, may have been a user error :)

I am working on some documentation on this.
My plan is to write down how i want it to work, not how it is currently working.

Would be great if i could get your feedback on that when it is done.
Mayeb we can agree on some terminology before i proceed with that.

First i would like to explain how i ended up where we are now.

Viseron v2

In v2 there are only recordings that are triggered by the trigger_recorder: true config option for object or motion detectors.
And these are what you find under the Recordings page in the UI.
They are stored as full MP4 files in /recordings

Viseron v3

But now in v3 we also have the continuous recordings, so now there are two types of recordings.
Both the event recordings (triggered by trigger_recorder: true) and the continuous recordings ar stored in the /segments folder as short 5s m4s files in order to support streaming using HLS.
The /recordings folder is reused to store the full MP4 files for an event recording if create_event_clip: true is set (which is not used by the frontend anymore).

The Events page also displays other events which does not trigger recordings, like face recognition for instance.
You will not find these on the Recordings page.

Going forward

These are the changes that i see are needed to get everything to make more sense:

  • Rename trigger_recorder to something like trigger_event_recording
  • Replace the /segments folder with /recordings
  • Store MP4 files from create_event_clip: true in a new folder called /clips in case someone wants to have all their recordings exported as MP4
  • Allow exporting a specific event recording or a given timeframe to an MP4 from the web interface

What i cant decide is what to do with the Recordings page and the Events page.
What should these be called to make sense? Any ideas?
I am reluctant to remove the Recordings page since users are used to that layout and would like to keep it in addition the Events page.

Anything else that you think should be considered?

@jin-eld
Copy link
Author

jin-eld commented Dec 3, 2024

If you experience that issue again, could you take a screenshot and paste it here?
And also check if a refresh solves the issue.

OK, I'll be monitoring this, so far there is for sure something fishy going on with events. I might have had an unclean shutdown now and then, there I understand that something could get lost, but when I shutdown -h (Viseron docker runs as a systemd service) it should not lose anything, but it appears that something happens to events/recordings. For instance, recordings are shown with their thumbnails, but are not playable (video files seem to be gone).

It may be related to my ramdisk setup, but I need to specifically sit down and test some scenarios to make sure I understand what is happening, before I open an issue on this.

In v2 there are only recordings that are triggered by the trigger_recorder: true config option for object or motion detectors.
...
But now in v3 we also have the continuous recordings, so now there are two types of recordings.

Both types are still recordings though, so I do not necessarily see any contradiction, i.e. as a user I would expect both of them to be just "recordings".

The /recordings folder is reused to store the full MP4 files for an event recording if create_event_clip: true is set (which is not used by the frontend anymore).

About create_event_clip: true - after gaining a bit of understanding how things work in regard to segments, my earlier proposal was to get rid of that option completely. My argument was, that there is no need to have the data present twice generally (i.e. twice, but in different formats), but only generate the .mp4 clips on an as needed basis. Would be interesting to learn how other users see it, but the only use case that I see for the clips, is the desire to save them away for later use outside of Viseron. And this "saving away" is always a conscious decision with manual intervention: i.e. a user is reviewing what got caught on camera in the Viseron UI (via HLS streaming from segments), as soon as they see something that is worth saving permanently (i.e. outside of tiers/rotated files) - this is the moment where the .mp4 clip is actually needed, hence the suggestion for an Export button which would live-generate the .mp4 out of the segments and allow to download it right away (or maybe store it in some permanent storage on the nvr which could be configured). This on-demand clip generation also makes sense from the disk space usage perspective. I think this would generally simplify things in regard to clips.

The Events page also displays other events which does not trigger recordings, like face recognition for instance.
You will not find these on the Recordings page.

OK, this was the main point that was missing in order to understand the actual difference between "Recordings" and "Events", so, basically not each event has a corresponding recording? Or is there still always some sort of video associated with the event (for instance, recording of the face that got recognized)?

Without knowing this subtle difference, the "Recoridngs" page seemed like a duplication of the "Events" page, just in a different layout.

Although, if we ignore v2 and the argument that people got used to "Recordings" - one actually does not need both. I guess this is why it was so confusing to a new user like me. And since in the end each recording shows up in the "Events" view anyway, I usually do not bother to look at the "Recordings" page at all. Or am I still misunderstanding things and in reality there is a saved video/segments of an "Event" and then another saved video/segments of the same event, but saved as a "Recording"?

  • Rename trigger_recorder to something like trigger_event_recording

Please confirm, so apart from the 24/7 option, there are no recordings that happen "on their own", a recording of video data is always triggered by an event, correct? If this is the case, then trigger_event_recording is indeed more intuitive.

Maybe we can agree on some terminology before i proceed with that.

This would be good, we have a confusing mix of "segments", "events", "clips", "recordings" and almost all of them result in video data saved on disk in one form or another :)

  • Replace the /segments folder with /recordings

In the sense that segment files will go to /recordings?

  • Store MP4 files from create_event_clip: true in a new folder called /clips in case someone wants to have all their recordings exported as MP4

  • Allow exporting a specific event recording or a given timeframe to an MP4 from the web interface

As mentioned earlier, I would simplify things, does unconditionally storing the same recording in an .mp4 format in addition to the segments make sense at all? I'd replace that point with the export feature, imho the .mp4 files are in fact only needed "on demand" and the exporting would fully cover this use case. I'd be interested to hear use cases from others, perhaps I am not seeing something.

What i cant decide is what to do with the Recordings page and the Events page.
What should these be called to make sense? Any ideas?
I am reluctant to remove the Recordings page since users are used to that layout and would like to keep it in addition the Events page.

My personal opinion: drop the "Recordings" page completely and rather allow an additional filter in "Events" to show only show events which resulted in stored video data (i.e. filter on events which resulted in something being recorded on disk).

My question to the above mentioned users would be: do they use the "Events" page, or do they just continue using "Recordings" coming from v2 and basically ignore "Events"? The "Events" page provides a bit more info, but in reality "Recordings" is now just a duplication of a subset of what is anyway available in "Events", perhaps with a different layout. For new users who start right with v3 these two pages are just confusing, because one starts to interpret, that they show different data/different videos, which is not the case.

Anything else that you think should be considered?

Hmm, especially for starters it'd be good to keep things simple in a "less is more" like approach and make sure the basic surveillance use case is rock solid.

What basic functionality does someone expect from a surveillance software?

  • identify when to record something
  • store the recording based on configured rules for later review by the user
  • make sure nothing gets lost
  • allow to export "evidence" into a format that is easily playable by external players (i.e. .mp4)

Advanced:

  • allow events to trigger external alarms (I think this is covered via MQTT already?)

The above would be my personal surveillance software minimum requirement list. I think Viseron covers a lot of it already, but I have the feeling that you got a bit entangled with "clips", "segments", "events", "recordings" during the migration from v2 to v3 and this entanglement is also leaking into configuration a bit, which makes things that should be simple somewhat confusing :)

Once the basic stuff is covered, there are of course tons of nice things that could be tuned, improved and added, but in my view these absolute simple and basic must haves is the essence of it all.

@jin-eld
Copy link
Author

jin-eld commented Dec 16, 2024

I re-downloaded the dev docker image (thought maybe something got corrupt as I once had a power outage while pulling), but it had no effect, with the latest image I reliably get a crash when trying to access the web interface.

What exactly has changed in the last dev image update? The previous one was working just fine (minus the known bugs).

@roflcoopter
Copy link
Owner

Your crash indicates that there is a problem with dlib, probably some CPU instruction that is unsupported like AVX2.
What CPU do you have?

Do you have dlib configured? Can i see your config?

@jin-eld
Copy link
Author

jin-eld commented Dec 16, 2024

Well, I did not change anything on that machine between the previous and the last :dev tag update... and the previous one worked just fine.

I tried to go back, but did not know how, I am not too good with docker, but I did not find a way to return to the previous dev pull.

Your crash indicates that there is a problem with dlib, probably some CPU instruction that is unsupported like AVX2.

This CPU does not support AVX2 nor AVX, again, wasn't a problem with the previous docker image version...

What CPU do you have?

Intel(R) Celeron(R) N5105 @ 2.00GHz

Do you have dlib configured?

I have no idea what you are talking about :) I had no clue that something extra needed to be configured? I just did a docker pull to get the latest fixes and nothing else. Otherwise it's a regular Fedora 41 host installation.

Can i see your config?

I renamed it from .yaml to .txt to make github happy, attached. Should be pretty much the same as the one I pasted originally.

config.txt

@roflcoopter
Copy link
Owner

dlib is just another component that provides face recognition, and it seems to be the cause of your crash.
But since you are not using it there must be something causing an import of it which leads to the crash.

I cant find anything obvious in the most recent changes, or the code base as a whole.

I am not familiar with Fedora but on Ubuntu i can get the crash logs by mounting /var/crash into the container.
Do you know if something like that is available for Fedora? Would be of great help since it would point out the offending code

@jin-eld
Copy link
Author

jin-eld commented Dec 20, 2024

Well, if it's the docker container which runs Ubuntu, which is supposed to dump the logs to /var/crash, then I guess it should be enough to mount a host directory as /var/crash inside the container? Should not really be related to Fedora... I did that, however I did not get any files there after the crash.

Do you know how I could get to the dev version prior to the last update? Is it tagged in docker somehow?

@jin-eld
Copy link
Author

jin-eld commented Dec 20, 2024

Help me out please, what do I need to run to start Viseron within the container? I thought it would be /init, but it does not work when started manually, gives me weird error output. Basically, I wanted to start the container without launching the main application automatically, then attach to it and then run Viseron from the shell (perhaps within gdb) to figure out what crashes and why.

However, I am somehow not able to figure out what or where the Viseron script is and how to manually start the backend from an interactive shell inside the docker image?

I'd really like to solve this issue, because for over a week I am basically not able to use Viseron anymore due to the crash, which is happening each time I try to access the web UI.

@roflcoopter
Copy link
Owner

Well, if it's the docker container which runs Ubuntu, which is supposed to dump the logs to /var/crash, then I guess it should be enough to mount a host directory as /var/crash inside the container? Should not really be related to Fedora... I did that, however I did not get any files there after the crash.

Do you know how I could get to the dev version prior to the last update? Is it tagged in docker somehow?

From my understanding the segfaults are handled by the host and not Docker but i could be wrong here.

The previous dev build is sadly no longer available, it is overwritten by the current one.

@roflcoopter
Copy link
Owner

roflcoopter commented Dec 20, 2024

Help me out please, what do I need to run to start Viseron within the container? I thought it would be /init, but it does not work when started manually, gives me weird error output. Basically, I wanted to start the container without launching the main application automatically, then attach to it and then run Viseron from the shell (perhaps within gdb) to figure out what crashes and why.

However, I am somehow not able to figure out what or where the Viseron script is and how to manually start the backend from an interactive shell inside the docker image?

I'd really like to solve this issue, because for over a week I am basically not able to use Viseron anymore due to the crash, which is happening each time I try to access the web UI.

You can start the container like this to stop the init scripts from running:
docker run -it --rm --entrypoint /bin/bash roflcoopter/amd64-viseron:dev

And then to run Viseron you cd to src and run python3 -u -m viseron

Edit: You might end up with other issues with that approach tho, let me run some tests and see if we can get it working

@roflcoopter
Copy link
Owner

roflcoopter commented Dec 20, 2024

Okay so the only way i could think of to run all init scripts except Viseron is to do this:

  • Start the container normally
  • Exec into the container and run `rm -r /etc/services.d/viseron
  • Restart the container

Now you can exec into it again and run python3 -u -m viseron manually from the src directory.

I guess this is what you were planning to do? https://stackoverflow.com/a/10035594
Would be of great help!

@jin-eld
Copy link
Author

jin-eld commented Dec 21, 2024

Okay so the only way i could think of to run all init scripts except Viseron is to do this:

Thank you for the instructions, this worked as expected.

Now you can exec into it again and run python3 -u -m viseron manually from the src directory.

It had some issues with ffmpeg when launched like that from within gdb, but this did not matter for this particular test case - when I tried to open the webpage, python crashed as expected.

I guess this is what you were planning to do? https://stackoverflow.com/a/10035594 Would be of great help!

Pretty much, yes, the backtrace is huge, so I am not pasting all of it, but I traced it up to where the call is being made in Viseron:

#233 0x0000556d3d4cce56 in _PyEval_EvalFrame (throwflag=0, f=Frame 0x7fed28b2b640, for file /src/viseron/components/compreface/face_recognition.py, line 11, in <module> (), tstate=0x556d5adb0360) at ../Include/internal/pycore_ceval.h:46
#234 _PyEval_Vector (tstate=0x556d5adb0360, con=<optimized out>, locals=<optimized out>, args=<optimized out>, argcount=<optimized out>, kwnames=<optimized out>) at ../Python/ceval.c:5067
#235 0x0000556d3d4ccd26 in PyEval_EvalCode (co=<code at remote 0x7fed28057260>, globals={'__name__': 'viseron.components.compreface.face_recognition', '__doc__': 'CompreFace face recognition.', '__package__': 'viseron.components.compreface', '__loader__': <SourceFileLoader(name='viseron.components.compreface.face_recognition', path='/src/viseron/components/compreface/face_recognition.py') at remote 0x7fed282dc430>, '__spec__': <ModuleSpec(name='viseron.components.compreface.face_recognition', loader=<...>, origin='/src/viseron/components/compreface/face_recognition.py', loader_state=None, submodule_search_locations=None, _set_fileattr=True, _cached='/src/viseron/components/compreface/__pycache__/face_recognition.cpython-310.pyc', _initializing=True) at remote 0x7fed282dc4f0>, '__file__': '/src/viseron/components/compreface/face_recognition.py', '__cached__': '/src/viseron/components/compreface/__pycache__/face_recognition.cpython-310.pyc', '__builtins__': {'__name__': 'builtins', '__doc__': "Built-in functions, exceptions, and other objects.\n\nNoteworthy: None is the `nil' object; Ellipsis repr...(truncated), locals=<optimized out>) at ../Python/ceval.c:1134

It happens when importing the face_recognition_api so it's really easy to reproduce now:

root@1ea5570160f9:/src# python3
Python 3.10.12 (main, Nov  6 2024, 20:22:13) [GCC 11.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import face_recognition
Illegal instruction (core dumped)

Or, if we mimic what Viseron is importing:

>>> from face_recognition.face_recognition_cli import image_files_in_folder
Illegal instruction (core dumped)

Now... I do not use face recognition, so I would prefer not to end up debugging the face recognition module which is probably compiled with optimizations not available on this particular CPU.

What I hacked for now just for testing - I removed compreface completely and also made sure it's not being imported in /src/viseron/components/webserver/api/v1/__init__.py and now everything works fine again.

I do not know if it's easily doable on your side, but if only the components that are enabled in the config would be imported - that would solve the problem. Or perhaps you have better ideas?

@roflcoopter
Copy link
Owner

Nice work!

Components are only imported when they are used, but the problem here is the new API endpoint for compreface which was recently introduced.

When the webserver gets a request this import chain starts:

  • viseron.components.webserver.api.v1.__init__.py
  • viseron.components.webserver.api.v1.compreface.py
  • viseron.components.compreface
  • viseron.components.compreface.face_recognition
  • face_recognition.face_recognition_cli
  • dlib

When looking for this i just searched for dlib imports, didnt think it would go this deep...

Gonna see if i can redesign how the API imports its handlers so that it only imports the endpoints being hit.

@bsyomov
Copy link

bsyomov commented Dec 21, 2024

Many low end systems that can be used as NVR (e.g. Celeron and Pentium before 2020!) do not support AVX instructions. Building DLIB in a container with appropriate optimizations leads to compatibility problems.
Rebuilding containers on the target system is not always convenient. And not every user will be able to do it.
A set of prebuild images without AVX would be a nice addition.

@roflcoopter
Copy link
Owner

Many low end systems that can be used as NVR (e.g. Celeron and Pentium before 2020!) do not support AVX instructions. Building DLIB in a container with appropriate optimizations leads to compatibility problems. Rebuilding containers on the target system is not always convenient. And not every user will be able to do it. A set of prebuild images without AVX would be a nice addition.

Agreed, a lot of tools are built for Viseron tho so need to work out how to disable AVX but most tools auto discovers it and enables the instructions.
Builds are run on the Azure agents so would probably have to get them to build in a VM or something

@bsyomov
Copy link

bsyomov commented Dec 21, 2024

AVX can be disabled on build:

python3 setup.py --set DLIB_USE_CUDA_COMPUTE_CAPABILITIES:STRING=50,52,53,60,61,70,72,75,80,86,87,89,90 --no USE_AVX_INSTRUCTIONS -- bdist_wheel --dist-dir=/wheels && \

@roflcoopter roflcoopter reopened this Dec 21, 2024
@roflcoopter
Copy link
Owner

roflcoopter commented Dec 21, 2024

AVX can be disabled on build:

python3 setup.py --set DLIB_USE_CUDA_COMPUTE_CAPABILITIES:STRING=50,52,53,60,61,70,72,75,80,86,87,89,90 --no USE_AVX_INSTRUCTIONS -- bdist_wheel --dist-dir=/wheels && \

Yes but need to find appropriate flags for ffmpeg, opencv, darknet etc etc.

Would be nice if i could just globally disable AVX but i dont know if that is even possible.

Edit: On the other hand other tools seem to already work, i guess by check CPU instructions at runtime.
So maybe it only is dlib that is causing issues.

@roflcoopter
Copy link
Owner

Pushed a fix and builds are now running, you can follow the progress here: https://dev.azure.com/jespernilsson93/Viseron%20Pipelines/_build/results?buildId=748&view=results

@bsyomov
Copy link

bsyomov commented Dec 21, 2024

Would be nice if i could just globally disable AVX but i dont know if that is even possible

Instruction sets can be disabled for virtual machine on hypervisor side. I think it can't be done on Azure or similiar services.
So configure aruments look more promising.

@bsyomov
Copy link

bsyomov commented Dec 21, 2024

--disable-avx for ffmpeg, but not needed in this case: avx support detected in runtime.
I get invalid opcode errors only in DLIB on Celeron J4105 based system. But I haven't tried using darknet.

@jin-eld
Copy link
Author

jin-eld commented Dec 22, 2024

Pushed a fix and builds are now running, you can follow the progress here: https://dev.azure.com/jespernilsson93/Viseron%20Pipelines/_build/results?buildId=748&view=results

Latest pull works for me again, thank you for fixing this issue so quickly!

@roflcoopter
Copy link
Owner

The export/download feature is now available in dev
You can download recordings, snapshots or specify a timeframe which will be concatenated to an MP4 and then downloaded.

Have not had the time to update the docs with screenshots yet tho

@jin-eld
Copy link
Author

jin-eld commented Jan 1, 2025

Awesome, I did not expect that this feature would be available so quickly, really nice, thank you!

You can download recordings, snapshots or specify a timeframe which will be concatenated to an MP4 and then downloaded.

Hmm, how can I download a specific event?

When I select and play an event, I only have the new download icon on the bottom right, which offers a timeframe, but I don't see where I could download the current event clip which I selected in the list?

Is this not yet in dev or am I not seeing something obvious?

@roflcoopter
Copy link
Owner

roflcoopter commented Jan 2, 2025

You hover over the event icon to view each individual event and there you have the download button.
image

There is no download button inside the player

@jin-eld
Copy link
Author

jin-eld commented Jan 2, 2025

Hmm, I don't see that, I only have the "global" download button, but my individual hover looks quite different
viseron

@roflcoopter
Copy link
Owner

So you dont have any text or anything below the thumbnails?
Have you tried clearing cache?

@jin-eld
Copy link
Author

jin-eld commented Jan 2, 2025

So you dont have any text or anything below the thumbnails?

Nope, it only shows the image as in my screenshot, I do not have the view as shown in yours, puzzling...

Have you tried clearing cache?

I tried shift-reload which I think does the same, also tried from a new private window - always looks the same, as in my screenshot.

I am using Firefox 133.0.3 (64-bit) on Linux.

Ooh now I see :) I retested in Chromium and there I noticed, that I have a scrollbar on the right side and if I scroll down - then I do see the text you mentioned and the download button. So, this is Chromium:

image

Now that I actually know, that there is a scrollbar, I tried scrolling in Firefox and indeed it shows the text and the button too, but the scrollbar itself is not visible.

Question is - why it so huge in my case, the thumbnails are much smaller on your screenshot and fit nicely without the need to scroll, but well, web stuff is a nightmare 😅

@jin-eld
Copy link
Author

jin-eld commented Jan 4, 2025

Now that I know that there is a scrollbar and that recordings can be downloaded (would be nice to get a fix for the UI though), I did some further testing. I noticed that even though I can play the entire event clip in the UI, it only allows me to download small chunks of 12 seconds, which is somewhat confusing. I thought the idea was to be able to download the entire event clip as a whole?
image

Am I missing some configuration option?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants