diff --git a/picamera2/devices/__init__.py b/picamera2/devices/__init__.py new file mode 100644 index 00000000..6157cbb4 --- /dev/null +++ b/picamera2/devices/__init__.py @@ -0,0 +1 @@ +from .imx708 import IMX708 diff --git a/picamera2/devices/imx708/__init__.py b/picamera2/devices/imx708/__init__.py new file mode 100644 index 00000000..6157cbb4 --- /dev/null +++ b/picamera2/devices/imx708/__init__.py @@ -0,0 +1 @@ +from .imx708 import IMX708 diff --git a/picamera2/devices/imx708/imx708.py b/picamera2/devices/imx708/imx708.py new file mode 100644 index 00000000..281d0b54 --- /dev/null +++ b/picamera2/devices/imx708/imx708.py @@ -0,0 +1,67 @@ +import fcntl +import os + +from v4l2 import VIDIOC_S_CTRL, v4l2_control + +from picamera2 import Picamera2 + +HDR_CTRL_ID = 0x009a0915 + + +class IMX708: + def __init__(self, camera_num=None): + self.device_fd = None + + camera_info = Picamera2.global_camera_info() + if camera_num is None: + camera_id = next((c['Id'] for c in camera_info if c['Model'] == 'imx708'), None) + else: + camera_id = next((c['Id'] for c in camera_info if c['Num'] == camera_num), None) + + if camera_id is None: + raise RuntimeError('IMX708: Requested IMX708 camera device not be found') + + for i in range(16): + test_dir = f'/sys/class/video4linux/v4l-subdev{i}/device' + module_dir = f'{test_dir}/driver/module' + id_dir = f'{test_dir}/of_node' + if os.path.exists(module_dir) and os.path.islink(module_dir) and 'imx708' in os.readlink(module_dir): + if os.path.islink(id_dir) and camera_id in os.readlink(id_dir): + self.device_fd = open(f'/dev/v4l-subdev{i}', 'rb+', buffering=0) + break + + if self.device_fd is None: + raise RuntimeError('IMX708: Requested camera v4l2 device node not found') + + def __del__(self): + self.close() + + def close(self): + if self.device_fd: + self.device_fd.close() + self.device_fd = None + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, tb): + self.close() + + def set_sensor_hdr_mode(self, enable: bool): + """ + Set the sensor HDR mode (True/False) on the IMX708 device. + + Note that after changing the HDR mode, you must + re-initialise the Picamera2 object to cache the updated sensor modes. + """ + ctrl = v4l2_control() + ctrl.id = HDR_CTRL_ID + ctrl.value = int(enable) + + try: + fcntl.ioctl(self.device_fd, VIDIOC_S_CTRL, ctrl) + except OSError as err: + print(f'IMX708: Unable to set HDR control in the device node: {err}') + + # Must reset the camera manager so that cached sensor modes can be refreshed. + Picamera2._cm.reset() diff --git a/picamera2/picamera2.py b/picamera2/picamera2.py index cabfbb16..d3117d37 100644 --- a/picamera2/picamera2.py +++ b/picamera2/picamera2.py @@ -69,6 +69,11 @@ def cms(self): self._cms = libcamera.CameraManager.singleton() return self._cms + def reset(self): + with self._lock: + self._cms = None + self._cms = libcamera.CameraManager.singleton() + def add(self, index, camera): with self._lock: self.cameras[index] = camera diff --git a/setup.py b/setup.py index 423fccf8..9b4f6ae9 100644 --- a/setup.py +++ b/setup.py @@ -30,7 +30,8 @@ "Programming Language :: Python :: 3.9", "Topic :: Multimedia :: Graphics :: Capture :: Digital Camera", ], - packages=['picamera2', 'picamera2.encoders', 'picamera2.outputs', 'picamera2.previews', 'picamera2.allocators'], + packages=['picamera2', 'picamera2.devices', 'picamera2.encoders', 'picamera2.outputs', 'picamera2.previews', + 'picamera2.allocators'], python_requires='>=3.9', licence='BSD 2-Clause License', install_requires=['numpy', 'PiDNG', 'piexif', 'pillow', 'simplejpeg', 'v4l2-python3', 'python-prctl', 'av'], diff --git a/tests/imx708_device.py b/tests/imx708_device.py new file mode 100755 index 00000000..4ee32787 --- /dev/null +++ b/tests/imx708_device.py @@ -0,0 +1,28 @@ +#!/bin/python3 + +from picamera2 import Picamera2 +from picamera2.devices.imx708 import IMX708 + +camera_info = Picamera2.global_camera_info() +camera_num = next((c['Num'] for c in camera_info if c['Model'] == 'imx708'), None) + +if camera_num is not None: + with IMX708(camera_num) as cam: + cam.set_sensor_hdr_mode(True) + picam2 = Picamera2(camera_num) + if len(picam2.sensor_modes) != 1: + print("ERROR: We should only report 1 sensor HDR mode") + picam2.close() + + cam.set_sensor_hdr_mode(False) + picam2 = Picamera2(camera_num) + if len(picam2.sensor_modes) <= 1: + print("ERROR: We should report > 1 sensor non-HDR modes") + picam2.close() + + cam = IMX708(camera_num) + cam.set_sensor_hdr_mode(True) + picam2 = Picamera2(camera_num) + if len(picam2.sensor_modes) != 1: + print("ERROR: We should only report 1 sensor HDR mode") + picam2.close() diff --git a/tests/test_list.txt b/tests/test_list.txt index cd2cafaf..4b9725f3 100644 --- a/tests/test_list.txt +++ b/tests/test_list.txt @@ -68,6 +68,7 @@ tests/easy_video2.py tests/egl_leak.py tests/encoder_start_stop.py tests/ffmpeg_abort.py +tests/imx708_device.py tests/large_datagram.py tests/mjpeg_server.py tests/no_raw.py