test on mac

This commit is contained in:
2026-05-14 16:08:17 +02:00
parent 6c401b62bb
commit b13c468df9
42 changed files with 7932 additions and 2 deletions

363
test_qcamera.py Normal file
View File

@@ -0,0 +1,363 @@
import sys
import logging
from pathlib import Path
from PySide6.QtCore import Qt, QTimer
from PySide6.QtGui import QAction
from PySide6.QtWidgets import QApplication, QMainWindow, QLabel
from PySide6.QtMultimedia import (
QMediaDevices,
QCamera,
QCameraDevice,
QMediaFormat,
QVideoFrameFormat,
QMediaCaptureSession
)
from PySide6.QtMultimediaWidgets import QVideoWidget
# ============================================================
# LOGGER
# ============================================================
LOG_DIR = Path("logs")
LOG_DIR.mkdir(exist_ok=True)
logging.basicConfig(
level=logging.DEBUG,
format="%(asctime)s [%(levelname)s] %(message)s",
handlers=[
logging.FileHandler(LOG_DIR / "camera_debug.log", encoding="utf-8"),
logging.StreamHandler(sys.stdout),
],
)
logger = logging.getLogger("camera_app")
# ============================================================
# CAMERA WINDOW
# ============================================================
class CameraWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Camera Preview")
# Widget wyświetlający wyłącznie obraz
self.video_widget = QVideoWidget()
self.setCentralWidget(self.video_widget)
self.camera = None
self.camera_device = None
self.capture_session = QMediaCaptureSession()
self.init_camera()
# ========================================================
# CAMERA INIT
# ========================================================
def init_camera(self):
devices = QMediaDevices.videoInputs()
logger.info("==========================================")
logger.info("VIDEO DEVICES ENUMERATION")
logger.info("==========================================")
if not devices:
logger.error("Nie znaleziono kamer.")
return
for idx, device in enumerate(devices):
self.log_camera_device(device, idx)
# wybór pierwszej kamery
self.camera_device = devices[0]
logger.info(f"Wybrano kamerę: {self.camera_device.description()}")
self.camera = QCamera(self.camera_device)
# sygnały
self.camera.errorOccurred.connect(self.on_camera_error)
self.camera.activeChanged.connect(self.on_active_changed)
# video sink
# self.camera.setVideoOutput(self.video_widget)
self.capture_session.setCamera(self.camera)
self.capture_session.setVideoOutput(self.video_widget)
# przykładowe ustawienia
self.set_camera_format(
width=1280,
height=720,
fps=30,
preferred_pixel_format=QVideoFrameFormat.PixelFormat.Format_Jpeg
)
self.log_runtime_camera_capabilities()
self.camera.start()
logger.info("Kamera uruchomiona.")
# ========================================================
# FORMAT SELECTION
# ========================================================
def set_camera_format(
self,
width: int,
height: int,
fps: int,
preferred_pixel_format=QVideoFrameFormat.PixelFormat.Format_Jpeg,
):
# def set_camera_format(self, width: int, height: int, fps: int):
"""
Prototyp funkcji ustawiającej:
- rozdzielczość
- FPS
Wybiera najbliższy pasujący format.
"""
logger.info(
f"Próba ustawienia formatu: {width}x{height} @ {fps} FPS"
)
best_match = None
for fmt in self.camera_device.videoFormats():
resolution = fmt.resolution()
min_fps = fmt.minFrameRate()
max_fps = fmt.maxFrameRate()
pixel_format = fmt.pixelFormat()
logger.debug(
f"Sprawdzam format -> "
f"{resolution.width()}x{resolution.height()} "
f"FPS:{min_fps}-{max_fps} "
f"PIX:{pixel_format}"
)
if (
resolution.width() == width
and resolution.height() == height
and min_fps <= fps <= max_fps
and pixel_format == preferred_pixel_format
):
best_match = fmt
break
if best_match:
self.camera.setCameraFormat(best_match)
logger.info("Ustawiono format kamery:")
logger.info(
f"Resolution: "
f"{best_match.resolution().width()}x"
f"{best_match.resolution().height()}"
)
logger.info(
f"FPS range: "
f"{best_match.minFrameRate()} - "
f"{best_match.maxFrameRate()}"
)
logger.info(
f"Pixel format: {best_match.pixelFormat()}"
)
else:
logger.warning("Nie znaleziono pasującego formatu.")
# ========================================================
# DEVICE LOGGER
# ========================================================
def log_camera_device(self, device: QCameraDevice, idx: int):
logger.info("------------------------------------------")
logger.info(f"KAMERA #{idx}")
logger.info("------------------------------------------")
logger.info(f"Description: {device.description()}")
logger.info(f"ID: {device.id().data().decode(errors='ignore')}")
try:
logger.info(f"Is default: {device.isDefault()}")
except Exception as e:
logger.warning(f"isDefault() unsupported: {e}")
formats = device.videoFormats()
logger.info(f"Liczba formatów: {len(formats)}")
for i, fmt in enumerate(formats):
resolution = fmt.resolution()
logger.info(f"")
logger.info(f"FORMAT #{i}")
logger.info(
f"Resolution: "
f"{resolution.width()}x{resolution.height()}"
)
logger.info(
f"FPS min/max: "
f"{fmt.minFrameRate()} / {fmt.maxFrameRate()}"
)
logger.info(
f"Pixel format enum: {fmt.pixelFormat()}"
)
logger.info(
f"Pixel format name: "
f"{self.pixel_format_to_string(fmt.pixelFormat())}"
)
# ========================================================
# RUNTIME CAMERA CAPABILITIES
# ========================================================
def log_runtime_camera_capabilities(self):
logger.info("")
logger.info("==========================================")
logger.info("QCAMERA RUNTIME CAPABILITIES")
logger.info("==========================================")
try:
logger.info(f"Camera active: {self.camera.isActive()}")
except Exception as e:
logger.warning(e)
# Dostępne w zależności od backendu/platformy
properties = [
"focusMode",
"exposureMode",
"whiteBalanceMode",
"flashMode",
"torchMode",
]
for prop in properties:
try:
value = getattr(self.camera, prop)()
logger.info(f"{prop}: {value}")
except Exception as e:
logger.warning(f"{prop} unsupported: {e}")
# backend/platform info
logger.info("")
logger.info("QT MULTIMEDIA INFO")
try:
logger.info(f"Qt version: {QApplication.qtVersion()}")
except Exception:
pass
logger.info(
"Backend zależy od platformy:"
)
logger.info(
"- Windows -> MediaFoundation"
)
logger.info(
"- Linux -> GStreamer / PipeWire / V4L2"
)
logger.info(
"- macOS -> AVFoundation"
)
# ========================================================
# PIXEL FORMAT NAME
# ========================================================
def pixel_format_to_string(self, pixel_format):
mapping = {
QVideoFrameFormat.PixelFormat.Format_YUV420P: "YUV420P",
QVideoFrameFormat.PixelFormat.Format_NV12: "NV12",
QVideoFrameFormat.PixelFormat.Format_NV21: "NV21",
QVideoFrameFormat.PixelFormat.Format_UYVY: "UYVY",
QVideoFrameFormat.PixelFormat.Format_YUYV: "YUYV",
QVideoFrameFormat.PixelFormat.Format_Jpeg: "MJPG/JPEG",
}
return mapping.get(pixel_format, str(pixel_format))
# ========================================================
# SIGNALS
# ========================================================
def on_camera_error(self, error, error_string):
logger.error(f"CAMERA ERROR: {error}")
logger.error(f"ERROR STRING: {error_string}")
def on_active_changed(self, active):
logger.info(f"Camera active changed: {active}")
# ========================================================
# CLEANUP
# ========================================================
# def closeEvent(self, event):
# logger.info("Zamykanie aplikacji.")
# if self.camera:
# self.camera.stop()
# super().closeEvent(event)
def closeEvent(self, event):
logger.info("Zamykanie aplikacji.")
try:
if self.camera:
logger.info("Stopping camera...")
self.camera.stop()
if self.capture_session:
logger.info("Detaching camera from capture session...")
self.capture_session.setCamera(None)
self.capture_session.setVideoOutput(None)
except Exception as e:
logger.exception(e)
super().closeEvent(event)
# ============================================================
# MAIN
# ============================================================
def main():
app = QApplication(sys.argv)
window = CameraWindow()
# tylko obraz
window.resize(1280, 720)
# fullscreen:
# window.showFullScreen()
window.show()
sys.exit(app.exec())
if __name__ == "__main__":
main()