birdseye.py 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. import multiprocessing as mp
  2. import numpy as np
  3. import subprocess as sp
  4. import logging
  5. import threading
  6. from frigate.util import SharedMemoryFrameManager
  7. logger = logging.getLogger(__name__)
  8. # methods for maintaining the birdseyeframe in the object processing thread
  9. # avoids work when no clients are listening
  10. class BirdsEyeFrameManager:
  11. def __init__(self):
  12. # self.config = config
  13. self.frame_manager = SharedMemoryFrameManager()
  14. self._frame_shape = (1080, 1920)
  15. self.frame_shape_yuv = (self._frame_shape[0] * 3 // 2, self._frame_shape[1])
  16. self.frame_shm = mp.shared_memory.SharedMemory(
  17. name=f"birdseye-frame",
  18. create=True,
  19. size=self.frame_shape_yuv[0] * self.frame_shape_yuv[1],
  20. )
  21. self.frame = np.ndarray(
  22. self.frame_shape_yuv, dtype=np.uint8, buffer=self.frame_shm.buf
  23. )
  24. # initialize the frame as black and with the frigate logo
  25. self.blank_frame = np.zeros((1080 * 3 // 2, 1920), np.uint8)
  26. self.blank_frame[:] = 128
  27. self.blank_frame[0:1080, 0:1920] = 16
  28. self.frame[:] = self.blank_frame
  29. def update_frame(self, camera, object_count, motion_count, frame_time, frame):
  30. # determine how many cameras are tracking objects (or recently were)
  31. # decide on a layout for the birdseye view (try to avoid too much churn)
  32. # calculate position of each camera
  33. # calculate resolution of each position in the layout
  34. # if layout is changing, wipe the frame black again
  35. # For each camera currently tracking objects (alphabetical):
  36. # - resize the current frame and copy into the birdseye view
  37. # signal to birdseye process that the frame is ready to send
  38. self.frame[:] = frame
  39. # separate process for managing the external ffmpeg process and sending frame
  40. # bytes to ffmpeg
  41. class BirdsEyeFrameOutputter(threading.Thread):
  42. def __init__(self, stop_event):
  43. threading.Thread.__init__(self)
  44. self.stop_event = stop_event
  45. self.frame_shm = mp.shared_memory.SharedMemory(
  46. name=f"birdseye-frame",
  47. create=False,
  48. )
  49. def start_ffmpeg(self):
  50. ffmpeg_cmd = "ffmpeg -f rawvideo -pix_fmt yuv420p -video_size 1920x1080 -i pipe: -f mpegts -codec:v mpeg1video -b:v 1000k -bf 0 http://localhost:8081/birdseye".split(
  51. " "
  52. )
  53. self.process = sp.Popen(
  54. ffmpeg_cmd,
  55. stdout=sp.DEVNULL,
  56. # TODO: logging
  57. stderr=sp.DEVNULL,
  58. stdin=sp.PIPE,
  59. start_new_session=True,
  60. )
  61. def run(self):
  62. self.start_ffmpeg()
  63. while not self.stop_event.wait(1):
  64. if self.process.poll() != None:
  65. logger.info(f"ffmpeg process is not running. restarting ...")
  66. self.start_ffmpeg()
  67. self.process.stdin.write(self.frame_shm.buf.tobytes())
  68. # separate process for passing jsmpeg packets over websockets
  69. # signals to the frame manager when a client is listening
  70. # class JSMpegSocketServer: