Implement dual output for camera using QVideoWidget and QVideoSink, add camera permission handling, and enhance overlay error messaging
This commit is contained in:
126
notes/02-mvp-mac.md
Normal file
126
notes/02-mvp-mac.md
Normal file
@@ -0,0 +1,126 @@
|
||||
# macOS — Camera on macOS with PySide6
|
||||
|
||||
## Problem
|
||||
|
||||
Aplikacja uruchomiona w interpreted mode (`python -m duck_preview`) na macOS z kamerą Elgato nie wyświetla obrazu.
|
||||
|
||||
## Przyczyna
|
||||
|
||||
Oficjalny przykład PySide6 (`camera.qml` / examples/multimedia) zawiera kod:
|
||||
|
||||
```python
|
||||
if sys.platform == "darwin":
|
||||
is_nuitka = "__compiled__" in globals()
|
||||
if not is_nuitka and sys.platform == "darwin":
|
||||
print("This example does not work on macOS when Python is run "
|
||||
"in interpreted mode. For this example to work on macOS, "
|
||||
"package the example using pyside6-deploy")
|
||||
sys.exit(0)
|
||||
```
|
||||
|
||||
**macOS → AVFoundation → wymaga spakowania przez Nuitka/pyside6-deploy.**
|
||||
|
||||
`QCamera` na macOS potrzebuje properly bundled app structure (Info.plist, entitlements, code signing). W interpreted mode Python nie ma tego contextu.
|
||||
|
||||
## Rozwiązanie
|
||||
|
||||
Pakować aplikację przez `pyside6-deploy` (Nuitka) na macOS. Na Windows działa bez pakowania.
|
||||
|
||||
---
|
||||
|
||||
## Qt Permission API (`QCameraPermission`)
|
||||
|
||||
Od Qt 6.5+ dostępne jest `QCameraPermission` — nowoczesne API do proszenia o zgodę kamery.
|
||||
|
||||
### API
|
||||
|
||||
```python
|
||||
from PySide6.QtCore import QCameraPermission, Qt
|
||||
from PySide6.QtWidgets import QApplication
|
||||
|
||||
perm = QCameraPermission()
|
||||
match QApplication.checkPermission(perm):
|
||||
case Qt.PermissionStatus.Undetermined:
|
||||
# Prosimy o zgodę → callback wywoła init ponownie
|
||||
QApplication.requestPermission(perm, parent, callback)
|
||||
return
|
||||
case Qt.PermissionStatus.Denied:
|
||||
# overlay: "Camera permission denied"
|
||||
case Qt.PermissionStatus.Granted:
|
||||
# uruchom kamerę
|
||||
```
|
||||
|
||||
### Permission flow
|
||||
|
||||
```
|
||||
User → wybiera kamerę w menu
|
||||
→ MainWindow._on_camera_selected(device)
|
||||
→ checkPermission(QCameraPermission)
|
||||
├─ Undetermined → requestPermission(camera, parent, callback)
|
||||
│ └─ callback → _on_camera_selected ponownie
|
||||
├─ Denied → overlay: "Camera permission denied. Grant access in System Settings > Privacy & Security > Camera"
|
||||
└─ Granted → CameraService.start(device)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## QVideoWidget + QVideoSink — dual output
|
||||
|
||||
PySide6 6.11 ma osobne metody w `QMediaCaptureSession`:
|
||||
|
||||
| Metoda | Przeznaczenie |
|
||||
|--------|---------------|
|
||||
| `setVideoOutput(QVideoWidget*)` | Natywne renderowanie (GPU) |
|
||||
| `setVideoSink(QVideoSink*)` | Dostęp do klatek (telemetria, AI) |
|
||||
|
||||
Oba działają **równolegle** — nie ma konfliktu.
|
||||
|
||||
```
|
||||
QMediaCaptureSession
|
||||
├─ setVideoOutput(QVideoWidget) → natywne renderowanie
|
||||
└─ setVideoSink(QVideoSink) → frame access
|
||||
└─ videoFrameChanged → FrameDispatcher
|
||||
```
|
||||
|
||||
QVideoSink dostarcza sygnał `videoFrameChanged(QVideoFrame)` — na to podpina się FrameDispatcher, a ten rozsyła do TelemetryCollector i przyszłych AI subscriberów.
|
||||
|
||||
---
|
||||
|
||||
## macOS — packaging
|
||||
|
||||
### pyside6-deploy
|
||||
|
||||
```bash
|
||||
pip install nuitka
|
||||
pyside6-deploy duck_preview/__main__.py --name "Duck Preview"
|
||||
```
|
||||
|
||||
### pyside6-deploy.toml
|
||||
|
||||
```toml
|
||||
[app]
|
||||
script = "duck_preview/__main__.py"
|
||||
name = "Duck Preview"
|
||||
bundle_identifier = "com.bartool.duck-preview"
|
||||
categories = "public.app-category.photography"
|
||||
platforms = ["macos"]
|
||||
```
|
||||
|
||||
Po spakowaniu powstaje `Duck Preview.app` — standalone bundle z dostępem do AVFoundation.
|
||||
|
||||
### Windows
|
||||
|
||||
Na Windows działa bez pakowania — `python -m duck_preview` w venv.
|
||||
|
||||
---
|
||||
|
||||
## Font fallback
|
||||
|
||||
`Consolas` nie istnieje na macOS. Używać `"monospace"` (generic font family — Qt mapuje na Menlo na macOS, Consolas na Windows).
|
||||
|
||||
## Error state w Overlay
|
||||
|
||||
Overlay wspiera wyświetlanie błędów (np. brak permisji):
|
||||
- Normalny stan → zielone metryki (FPS, frame times)
|
||||
- Error state → czerwony komunikat
|
||||
- Przełączanie przez `overlay.set_metrics({"error": "message"})`
|
||||
Reference in New Issue
Block a user