No blocking portals
This commit is contained in:
parent
ca3f339cbe
commit
e7f669cb56
4 changed files with 13 additions and 28 deletions
|
|
@ -70,18 +70,17 @@ class OpenCVCamera(BaseCamera):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def _capture_frames(self) -> None:
|
def _capture_frames(self) -> None:
|
||||||
portal = lt.get_blocking_portal(self)
|
|
||||||
while self._capture_enabled:
|
while self._capture_enabled:
|
||||||
ret, frame = self.cap.read()
|
ret, frame = self.cap.read()
|
||||||
if not ret:
|
if not ret:
|
||||||
LOGGER.error(f"Failed to capture frame from camera {self.camera_index}")
|
LOGGER.error(f"Failed to capture frame from camera {self.camera_index}")
|
||||||
break
|
break
|
||||||
jpeg = cv2.imencode(".jpg", frame)[1].tobytes()
|
jpeg = cv2.imencode(".jpg", frame)[1].tobytes()
|
||||||
self.mjpeg_stream.add_frame(jpeg, portal)
|
self.mjpeg_stream.add_frame(jpeg)
|
||||||
jpeg_lores = cv2.imencode(".jpg", cv2.resize(frame, (320, 240)))[
|
jpeg_lores = cv2.imencode(".jpg", cv2.resize(frame, (320, 240)))[
|
||||||
1
|
1
|
||||||
].tobytes()
|
].tobytes()
|
||||||
self.lores_mjpeg_stream.add_frame(jpeg_lores, portal)
|
self.lores_mjpeg_stream.add_frame(jpeg_lores)
|
||||||
|
|
||||||
@lt.action
|
@lt.action
|
||||||
def discard_frames(self) -> None:
|
def discard_frames(self) -> None:
|
||||||
|
|
|
||||||
|
|
@ -68,9 +68,7 @@ class MissingCalibrationError(RuntimeError):
|
||||||
class PicameraStreamOutput(Output):
|
class PicameraStreamOutput(Output):
|
||||||
"""An Output class that sends frames to a stream."""
|
"""An Output class that sends frames to a stream."""
|
||||||
|
|
||||||
def __init__(
|
def __init__(self, stream: lt.outputs.MJPEGStream) -> None:
|
||||||
self, stream: lt.outputs.MJPEGStream, portal: lt.deps.BlockingPortal
|
|
||||||
) -> None:
|
|
||||||
"""Create an output that puts frames in an MJPEGStream.
|
"""Create an output that puts frames in an MJPEGStream.
|
||||||
|
|
||||||
We need to pass the stream object, and also the blocking portal, because
|
We need to pass the stream object, and also the blocking portal, because
|
||||||
|
|
@ -80,7 +78,6 @@ class PicameraStreamOutput(Output):
|
||||||
"""
|
"""
|
||||||
Output.__init__(self)
|
Output.__init__(self)
|
||||||
self.stream = stream
|
self.stream = stream
|
||||||
self.portal = portal
|
|
||||||
|
|
||||||
def outputframe(
|
def outputframe(
|
||||||
self,
|
self,
|
||||||
|
|
@ -91,7 +88,7 @@ class PicameraStreamOutput(Output):
|
||||||
_audio: bool = False,
|
_audio: bool = False,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Add a frame to the stream's ringbuffer."""
|
"""Add a frame to the stream's ringbuffer."""
|
||||||
self.stream.add_frame(frame, self.portal)
|
self.stream.add_frame(frame)
|
||||||
|
|
||||||
|
|
||||||
class SensorMode(BaseModel):
|
class SensorMode(BaseModel):
|
||||||
|
|
@ -480,18 +477,12 @@ class StreamingPiCamera2(BaseCamera):
|
||||||
stream_name = "lores" if main_resolution[0] > 1280 else "main"
|
stream_name = "lores" if main_resolution[0] > 1280 else "main"
|
||||||
picam.start_recording(
|
picam.start_recording(
|
||||||
MJPEGEncoder(self.mjpeg_bitrate),
|
MJPEGEncoder(self.mjpeg_bitrate),
|
||||||
PicameraStreamOutput(
|
PicameraStreamOutput(self.mjpeg_stream),
|
||||||
self.mjpeg_stream,
|
|
||||||
lt.get_blocking_portal(self),
|
|
||||||
),
|
|
||||||
name=stream_name,
|
name=stream_name,
|
||||||
)
|
)
|
||||||
picam.start_encoder(
|
picam.start_encoder(
|
||||||
MJPEGEncoder(100000000),
|
MJPEGEncoder(100000000),
|
||||||
PicameraStreamOutput(
|
PicameraStreamOutput(self.lores_mjpeg_stream),
|
||||||
self.lores_mjpeg_stream,
|
|
||||||
lt.get_blocking_portal(self),
|
|
||||||
),
|
|
||||||
name="lores",
|
name="lores",
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
@ -515,9 +506,8 @@ class StreamingPiCamera2(BaseCamera):
|
||||||
else:
|
else:
|
||||||
self.stream_active = False
|
self.stream_active = False
|
||||||
if stop_web_stream:
|
if stop_web_stream:
|
||||||
portal = lt.get_blocking_portal(self)
|
self.mjpeg_stream.stop()
|
||||||
self.mjpeg_stream.stop(portal)
|
self.lores_mjpeg_stream.stop()
|
||||||
self.lores_mjpeg_stream.stop(portal)
|
|
||||||
LOGGER.info("Stopped MJPEG stream.")
|
LOGGER.info("Stopped MJPEG stream.")
|
||||||
|
|
||||||
# Adding a sleep to prevent camera getting confused by rapid commands
|
# Adding a sleep to prevent camera getting confused by rapid commands
|
||||||
|
|
|
||||||
|
|
@ -290,7 +290,6 @@ class SimulatedCamera(BaseCamera):
|
||||||
noise_level: float = lt.property(default=2.0)
|
noise_level: float = lt.property(default=2.0)
|
||||||
|
|
||||||
def _capture_frames(self) -> None:
|
def _capture_frames(self) -> None:
|
||||||
portal = lt.get_blocking_portal(self)
|
|
||||||
last_frame_t = time.time()
|
last_frame_t = time.time()
|
||||||
while self._capture_enabled:
|
while self._capture_enabled:
|
||||||
wait_time = last_frame_t - time.time() - self.frame_interval
|
wait_time = last_frame_t - time.time() - self.frame_interval
|
||||||
|
|
@ -299,9 +298,9 @@ class SimulatedCamera(BaseCamera):
|
||||||
last_frame_t = time.time()
|
last_frame_t = time.time()
|
||||||
try:
|
try:
|
||||||
frame = self.generate_frame()
|
frame = self.generate_frame()
|
||||||
self.mjpeg_stream.add_frame(_frame2bytes(frame), portal)
|
self.mjpeg_stream.add_frame(_frame2bytes(frame))
|
||||||
ds_frame = frame.resize((320, 240), resample=Image.NEAREST)
|
ds_frame = frame.resize((320, 240), resample=Image.NEAREST)
|
||||||
self.lores_mjpeg_stream.add_frame(_frame2bytes(ds_frame), portal)
|
self.lores_mjpeg_stream.add_frame(_frame2bytes(ds_frame))
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
LOGGER.exception(f"Failed to capture frame: {e}, retrying...")
|
LOGGER.exception(f"Failed to capture frame: {e}, retrying...")
|
||||||
|
|
|
||||||
|
|
@ -51,8 +51,6 @@ def test_handle_broken_frame():
|
||||||
|
|
||||||
camera.mjpeg_stream.grab_frame = flaky_grabber
|
camera.mjpeg_stream.grab_frame = flaky_grabber
|
||||||
with camera_server(camera):
|
with camera_server(camera):
|
||||||
portal = lt.get_blocking_portal(camera)
|
|
||||||
|
|
||||||
# Check that this does cause broken frames.
|
# Check that this does cause broken frames.
|
||||||
# The noqa is because we don't know exactly when the error is thrown so we
|
# The noqa is because we don't know exactly when the error is thrown so we
|
||||||
# can't have a single simple statement in the pytest raises.
|
# can't have a single simple statement in the pytest raises.
|
||||||
|
|
@ -60,13 +58,13 @@ def test_handle_broken_frame():
|
||||||
OSError, match="broken data stream when reading image file"
|
OSError, match="broken data stream when reading image file"
|
||||||
):
|
):
|
||||||
for _i in range(15):
|
for _i in range(15):
|
||||||
jpeg = camera.grab_jpeg(portal)
|
jpeg = camera.grab_jpeg()
|
||||||
np.asarray(Image.open(jpeg.open()))
|
np.asarray(Image.open(jpeg.open()))
|
||||||
|
|
||||||
# Check that grab_as_array handles the broken frames and completes without
|
# Check that grab_as_array handles the broken frames and completes without
|
||||||
# the same error.
|
# the same error.
|
||||||
for _i in range(15):
|
for _i in range(15):
|
||||||
array = camera.grab_as_array(portal)
|
array = camera.grab_as_array()
|
||||||
assert isinstance(array, np.ndarray)
|
assert isinstance(array, np.ndarray)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -74,8 +72,7 @@ def test_simulation_cam_calibration():
|
||||||
"""Test that the simulated camera can be calibrated and reports calibration correctly."""
|
"""Test that the simulated camera can be calibrated and reports calibration correctly."""
|
||||||
camera = SimulatedCamera()
|
camera = SimulatedCamera()
|
||||||
with camera_server(camera):
|
with camera_server(camera):
|
||||||
portal = lt.get_blocking_portal(camera)
|
|
||||||
assert camera.calibration_required
|
assert camera.calibration_required
|
||||||
camera.full_auto_calibrate(portal)
|
camera.full_auto_calibrate()
|
||||||
assert not camera.calibration_required
|
assert not camera.calibration_required
|
||||||
assert camera.background_detector_status.ready
|
assert camera.background_detector_status.ready
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue