Compare commits
3 Commits
clearpilot
..
fps20
| Author | SHA1 | Date | |
|---|---|---|---|
| 2ce6e8fe0c | |||
| 3bd0c942e8 | |||
| 12da9acfdd |
@@ -2,6 +2,8 @@ prebuilt
|
||||
system/clearpilot/dev/on_start_brian.sh
|
||||
system/clearpilot/dev/id_rsa
|
||||
system/clearpilot/dev/id_rsa.pub
|
||||
system/clearpilot/dev/id_ed25519
|
||||
system/clearpilot/dev/id_ed25519.pub
|
||||
venv/
|
||||
.venv/
|
||||
.ci_cache
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
# CAN-FD Issues to Investigate
|
||||
|
||||
## Cruise Control Desync on Hot Start
|
||||
|
||||
### Problem
|
||||
When the car is already in cruise control mode before openpilot starts (or before controlsd initializes), pressing the cruise control button causes a desync:
|
||||
|
||||
1. Car has active cruise control when openpilot comes online
|
||||
2. Driver presses cruise button — car interprets as "toggle off" (cruise was already on)
|
||||
3. openpilot interprets it as "engage" (from its perspective, it hasn't seen a cruise enable transition)
|
||||
4. Result: car disengages cruise, but openpilot thinks it just engaged lateral mode
|
||||
|
||||
### Proposed Fix
|
||||
When controlsd first reads carState and discovers `cruiseState.enabled` is already true:
|
||||
|
||||
1. Set a flag `cruise_was_active_at_start = True`
|
||||
2. While this flag is set, do NOT allow openpilot to transition to engaged state on button press
|
||||
3. Only clear the flag after seeing `cruiseState.enabled` go to `False` at least once (a full stop of cruise, not just standstill/pause)
|
||||
4. After the first full off cycle, normal engagement logic resumes
|
||||
|
||||
### Status
|
||||
- Could not reproduce on 2026-04-14 — may require specific timing (openpilot restart while driving with cruise active)
|
||||
- Need to capture telemetry data during reproduction to see exact signal sequence
|
||||
- Key telemetry groups to watch: `cruise` (enabled, available, ACCMode, VSetDis), `buttons` (cruise_button)
|
||||
|
||||
### Relevant Code
|
||||
- `selfdrive/controls/controlsd.py` — engagement state machine
|
||||
- `selfdrive/car/hyundai/carstate.py` — CAN-FD cruise state parsing
|
||||
- `selfdrive/car/interfaces.py` — pcm_enable / cruise state transitions
|
||||
@@ -2,115 +2,27 @@
|
||||
|
||||
## Project Overview
|
||||
|
||||
ClearPilot is a custom fork of **FrogPilot** (itself a fork of comma.ai's openpilot), purpose-built for Brian Hanson's **Hyundai Tucson** (HDA2 equipped). The vehicle's HDA2 system has specific quirks around how it synchronizes driving state with openpilot that require careful handling.
|
||||
ClearPilot is a custom fork of **FrogPilot** (itself a fork of comma.ai's openpilot), based on a 2024 release. It is purpose-built for Brian Hanson's **Hyundai Tucson** (HDA2 equipped). The vehicle's HDA2 system has specific quirks around how it synchronizes driving state with openpilot that require careful handling.
|
||||
|
||||
The fork was previously in a state where many features were layered on top but the driving model behavior had regressed in ways that couldn't be traced. On **2026-05-03** the working tree was reset back to a known-clean baseline so features can be re-introduced one at a time with proper testing. Most non-driving-math features have since been ported back.
|
||||
### Key Customizations in This Fork
|
||||
|
||||
## Current State
|
||||
- **UI changes** to the onroad driving interface
|
||||
- **Lane change behavior**: brief disengage when turn signal is active during lane changes
|
||||
- **Lateral control disabled**: the car's own radar cruise control handles lateral; openpilot handles longitudinal only
|
||||
- **Driver monitoring timeouts**: modified safety timeouts for the driver monitoring system
|
||||
- **Custom driving models**: `duck-amigo.thneed`, `farmville.onnx`, `wd-40.thneed` in `selfdrive/clearpilot/models/`
|
||||
- **ClearPilot service**: Node.js service at `selfdrive/clearpilot/` with behavior scripts for lane change and longitudinal control
|
||||
- **Native dashcamd**: C++ process capturing raw camera frames via VisionIPC with OMX H.264 hardware encoding
|
||||
- **Standstill power saving**: model inference throttled to 1fps and fan quieted when car is stopped
|
||||
- **ClearPilot menu**: sidebar settings panel replacing stock home screen (Home, Dashcam, Debug panels)
|
||||
- **Status window**: live system stats (temp, fan, storage, RAM, WiFi, VPN, GPS, telemetry, dashcam status)
|
||||
- **Debug button (LFA)**: steering wheel button repurposed for screen toggle and future UI actions
|
||||
- **Telemetry system**: diff-based CSV logger via ZMQ IPC, toggleable from Debug panel
|
||||
- **Bench mode**: `--bench` flag for onroad UI testing without car connected
|
||||
- **GPS**: custom AT-command based GPS daemon (`system/clearpilot/gpsd.py`) replacing broken qcomgpsd diag interface
|
||||
- **OpenVPN tunnel**: auto-connecting VPN to expose device on remote network for SSH access
|
||||
|
||||
`build_only.sh` succeeds and `launch_openpilot.sh` boots the manager. The following customizations are active in this tree:
|
||||
|
||||
**Boot / build / device infra**
|
||||
- launch chain (`launch_openpilot.sh`, `launch_chffrplus.sh`) with stale-process kill (including `_text` by comm), `on_start.sh` SSH/WiFi setup, OpenVPN auto-connect (`vpn-monitor.sh` + `vpn.ovpn`), `nice-monitor.sh`, `build_only.sh` + `build_preflight.sh`
|
||||
- DongleId-keyed dev SSH identity (`system/clearpilot/dev/id_ed25519.{cpt,pub.cpt}` + `tools/{encrypt,decrypt}`)
|
||||
- Custom startup logo (`bg.jpg`) + custom Qt spinner (`selfdrive/ui/qt/spinner{,.cc,.h}`) — `build.py` atomically swaps the prebuilt spinner binary after each successful build
|
||||
- `BUILD_ONLY=1` env path in `build.py` spawns the failure TextWindow fully detached (own session, /dev/null stdio) so `build_only.sh` exits with a non-blocking error window
|
||||
|
||||
**Logging suppressed**
|
||||
- `loggerd`, `encoderd`, `stream_encoderd` disabled in `process_config.py` — no `rlog`/`qlog` segments or `.hevc` files written to `/data/media/0/realdata/`
|
||||
- `save_bootlog()` skipped in `manager_init()` — no boot logs
|
||||
|
||||
**GPS (Quectel modem replacement)**
|
||||
- `system/clearpilot/gpsd.py` polls Quectel EC25 modem via `mmcli` AT commands at 2 Hz, publishes `gpsLocation`, sets system clock on first fix
|
||||
- NOAA solar-position calc → `IsDaylight` memory param + auto `ScreenDisplayMode` 0↔1 switch (sunrise/sunset)
|
||||
- **`locationd` patched to NOT subscribe to GPS** — `liveLocationKalman.gpsOK` stays false permanently. Self-driving sees GPS as not-present; downstream consumers (controlsd, paramsd, torqued, frogpilot_planner) handle that case naturally. GPS data is still published and used by speed_logicd / UI / dashcamd, just kept out of the kalman filter.
|
||||
|
||||
**UI (full C++ port)**
|
||||
- New ready/splash screen rendered by Qt directly; shown when started but gear=park
|
||||
- ClearPilot offroad menu (Home / Dashcam / Debug panels) replacing the stock home — Status window with live system stats (temp, fan, storage, RAM, WiFi, VPN, GPS, telemetry, dashcam)
|
||||
- Onroad widgets: speed (from `gpsLocation` via `speed_logicd`), speed-limit, cruise-vs-limit warning sign
|
||||
- Nightrider mode: camera suppressed, lane lines/path drawn as 2px outlines (`ScreenDisplayMode` 1 or 4)
|
||||
- Display power: `ScreenDisplayMode` 3 → screen off; ignition off blanks immediately
|
||||
- Qt RPC widget-tree dump server at `ipc:///tmp/clearpilot_ui_rpc`
|
||||
- Crash handler in `main.cc` with stack-trace dump for SIGSEGV/SIGABRT
|
||||
- `screenDisplayMode` is a member of `AnnotatedCameraWidget`, accessed from `OnroadWindow::updateState` as `nvg->screenDisplayMode`
|
||||
- QtWebEngine dependency removed entirely
|
||||
|
||||
**Display modes (LFA debug button)**
|
||||
- 5-state `ScreenDisplayMode` machine in `controlsd.clearpilot_state_control()`:
|
||||
- Drive: 0→4, 1→2, 2→3, 3→4, 4→2 (button never goes back to auto)
|
||||
- Not drive: anything except 3 → 3 (off), 3 → 0 (auto)
|
||||
- Auto-wake from screen-off (mode 3 → 0) on park→drive edge
|
||||
- "Clearpilot Debug Function Executed" alert removed (`clp_debug_notice` and its event registration deleted from `events.py`)
|
||||
|
||||
**Speed / cruise**
|
||||
- `speed_logicd` standalone managed process (`selfdrive/clearpilot/speed_logicd.py` wrapping `speed_logic.SpeedState`) — subscribes to `gpsLocation` + `carState`, ticks at 2 Hz, writes `Clearpilot{Speed,SpeedLimit,CruiseWarning,...}Display` memory params
|
||||
- Cruise-vs-limit warning thresholds: +10 over (limit ≥ 50), +7 over (26-49), +9 over (≤ 25); -5 under
|
||||
- On warning transitions / speed-limit change: writes `ClearpilotPlayDing="1"` (consumed by soundd)
|
||||
- Decoupled from `controlsd` so self-driving timing is unaffected
|
||||
- **Note**: speed-limit widget shows 0 until `CarSpeedLimit` publisher in `hyundai/carstate.py` CAN-FD decode is ported (not done yet). Speed widget works.
|
||||
|
||||
**Sound**
|
||||
- `soundd.py` mixes a single-shot ding alongside the alert stream (`MAX_VOLUME`, independent of alert volume map). Polls `ClearpilotPlayDing` at ~2 Hz, clears on read. `selfdrive/clearpilot/sounds/ding.wav` is the asset.
|
||||
|
||||
**Dashcam**
|
||||
- `selfdrive/clearpilot/dashcamd` (native C++) — VisionIPC frames from camerad → OMX H.264 hardware encoder → 3-min MP4 segments + SRT GPS subtitle sidecars in `/data/media/0/videos/<trip>/`
|
||||
- Trip lifecycle (WAITING → RECORDING → IDLE_TIMEOUT). 10-min idle close from drive→park; immediate close on ignition off; graceful close on `DashcamShutdown`
|
||||
- Publishes `DashcamState` ("waiting"/"recording"/"stopped") and `DashcamFrames` (per-trip count) every 5 s
|
||||
- `omx_encoder.cc` ports broken's version (adds `encode_frame_nv12()`, NEON-only libyuv paths)
|
||||
- **`camerad` gating changed to `always_run`** so the dashcam can record the moment ignition+drive arrives without waiting for camera startup
|
||||
- `system/loggerd/deleter.py` trip-aware cleanup: drops oldest entire trip dir first, falls back to oldest segment within active trip; also enforces 4 GB cap on `/data/log2`
|
||||
|
||||
## Where the Old Code Lives
|
||||
|
||||
| Location | What it is |
|
||||
|---|---|
|
||||
| `/data/openpilot/` | This repo. **Active.** |
|
||||
| `/data/openpilot-broken-2026-05-03/` | Full snapshot (with `.git`) of the prior modified-but-broken tree. Reference for porting features. |
|
||||
| `/data/clearpilot-baseline/` | The original baseline source we copied in. Kept for safety; do not modify. |
|
||||
| `/data/openpilot-features-broken/` | Pre-existing snapshot from an earlier reset attempt — **unverified**, leave alone. |
|
||||
|
||||
| Tag | Commit | What |
|
||||
|---|---|---|
|
||||
| `pre-reset-2026-05-03` | `f7e602c` | Last commit of the pre-reset broken-but-feature-complete tree. |
|
||||
| `working-baseline-2` | `b287fd0` | First commit after the reset where the bare baseline built and launched. |
|
||||
|
||||
Both tags are pushed to `origin/clearpilot`.
|
||||
|
||||
## Pending Feature Port Roadmap
|
||||
|
||||
Items still in `/data/openpilot-broken-2026-05-03/` that haven't been ported. Port in small, testable batches.
|
||||
|
||||
**Driving behavior (HDA2 specifics)** — these touch self-driving code and warrant extra scrutiny:
|
||||
- Lateral disabled (car's radar cruise handles steering; openpilot longitudinal only)
|
||||
- Brief disengage when turn signal is active during lane changes (note: `no_lat_lane_change` infrastructure already wired through `controlsd` → `paramsMemory` → UI/canfd, but the actual disengage trigger may need work)
|
||||
- Driver-monitoring timeout adjustments
|
||||
- Custom driving models — model files (`duck-amigo.thneed`, `farmville.onnx`, `wd-40.thneed`) live in `selfdrive/clearpilot/models/`; selection logic not ported
|
||||
|
||||
**Speed-limit publisher**
|
||||
- `hyundai/carstate.py update_canfd()` writes `CarSpeedLimit` from CAN — until ported, the speed-limit widget shows 0 and cruise warnings never trigger
|
||||
|
||||
**Telemetry**
|
||||
- `selfdrive/clearpilot/telemetry.py` (client) + `telemetryd.py` (collector) — diff-based CSV logger over ZMQ
|
||||
- Toggleable via `TelemetryEnabled` memory param from Debug panel
|
||||
- Auto-disabled if `/data` free < 5 GB; auto-disabled on every manager start
|
||||
- Hyundai CAN-FD data logged from `update_canfd()` groups (car/cruise/speed_limit/buttons)
|
||||
|
||||
**Bench mode (UI testing without a car)**
|
||||
- `--bench` flag → `BENCH_MODE=1` in `launch_openpilot.sh` (already wired) → enables `bench_onroad.py`, blocks real car processes
|
||||
- `bench_cmd.py` for setting fake vehicle state via params; UI dump RPC wired (`bench_cmd dump`)
|
||||
- Param keys (`Bench*`, `ClpUiState`) not yet registered
|
||||
|
||||
**Power/thermal**
|
||||
- Standstill power saving: `modeld` and `dmonitoringmodeld` throttled to 1 fps when stopped
|
||||
- Fan controller uses offroad clamps at standstill
|
||||
- Park CPU savings + virtual battery shutdown fix
|
||||
|
||||
**Session logging**
|
||||
- `/data/log2/current/` per-process stderr capture; aggregate `session.log` of major events
|
||||
- Time-resolved log dir rename via GPS/NTP; 30-day rotation
|
||||
- See `selfdrive/manager/process.py` and `manager.py` changes in the broken tree
|
||||
- `LogDirInitialized` param not yet registered
|
||||
See `GOALS.md` for feature roadmap.
|
||||
|
||||
## Working Rules
|
||||
|
||||
@@ -129,11 +41,12 @@ This is self-driving software. All changes must be deliberate and well-understoo
|
||||
|
||||
**NEVER use `cloudlog`.** It's comma.ai's cloud telemetry pipeline, not ours — writes go to a publisher that's effectively a black hole for us (and the only thing it could do if ever reachable is bother the upstream FrogPilot developer). Our changes must always use **file logging** instead.
|
||||
|
||||
Use `print(..., file=sys.stderr, flush=True)`. Once session-logging is re-ported, manager will redirect each managed process's stderr to `/data/log2/current/{process}.log`. Prefix custom log lines with `CLP ` so they're easy to filter from upstream noise.
|
||||
Use `print(..., file=sys.stderr, flush=True)`. `manager.py` redirects each managed process's stderr to `/data/log2/current/{process}.log`, so these lines land in the per-process log we already grep. Prefix custom log lines with `CLP ` so they're easy to filter out from upstream noise.
|
||||
|
||||
Example:
|
||||
```python
|
||||
import sys
|
||||
print(f"CLP gpsd: ...", file=sys.stderr, flush=True)
|
||||
print(f"CLP frogpilotPlan valid=False: carState_freq_ok={sm.freq_ok['carState']}", file=sys.stderr, flush=True)
|
||||
```
|
||||
|
||||
Do not use `cloudlog.warning`, `cloudlog.info`, `cloudlog.error`, `cloudlog.event`, or `cloudlog.exception` in any CLEARPILOT-added code. Existing upstream/FrogPilot `cloudlog` calls can stay untouched.
|
||||
@@ -149,9 +62,8 @@ chown -R comma:comma /data/openpilot
|
||||
### Git
|
||||
|
||||
- Remote: `git@git.hanson.xyz:brianhansonxyz/clearpilot.git`
|
||||
- Branch: `clearpilot` — this is the canonical/main branch and is what fresh device flashes provision from. **Do not abandon it for another branch.**
|
||||
- Branch: `clearpilot`
|
||||
- Large model files are tracked in git (intentional — this is a backup)
|
||||
- The `clearpilot` branch was force-pushed on 2026-05-03 as part of the reset; the prior history is reachable via the `pre-reset-2026-05-03` tag.
|
||||
|
||||
### Samba Share
|
||||
|
||||
@@ -164,103 +76,526 @@ chown -R comma:comma /data/openpilot
|
||||
|
||||
### Testing Changes
|
||||
|
||||
Use `build_only.sh` to compile, then start the manager separately. Never compile individual targets with scons directly — always use the full build script. Always start the manager after a successful build — don't wait for the user to ask.
|
||||
Always use `build_only.sh` to compile, then start the manager separately. Never compile individual targets with scons directly — always use the full build script. Always use full paths with `su - comma` — the login shell drops into `/home/comma` (volatile tmpfs), not `/data/openpilot`. **Always start the manager after a successful build** — don't wait for the user to ask.
|
||||
|
||||
```bash
|
||||
# 1. Fix ownership
|
||||
# 1. Fix ownership (we edit as root, openpilot runs as comma)
|
||||
chown -R comma:comma /data/openpilot
|
||||
|
||||
# 2. Build (kills running manager, removes prebuilt, compiles, exits)
|
||||
# build_only.sh tees output to /tmp/build.log and propagates the build's
|
||||
# exit code via PIPESTATUS. On failure: error text window stays on screen
|
||||
# fully detached; the script exits non-zero and stderr has the compile error.
|
||||
# Shows progress spinner on screen. On failure, shows error on screen
|
||||
# and prints to stderr. Does NOT start the manager.
|
||||
su - comma -c "bash /data/openpilot/build_only.sh"
|
||||
|
||||
# 3. If build succeeded ($? == 0), start openpilot
|
||||
su - comma -c "bash /data/openpilot/launch_openpilot.sh"
|
||||
|
||||
# 4. Inspect logs
|
||||
ls /data/log2/current/
|
||||
# 4. Review the aggregate session log for errors
|
||||
cat /data/log2/current/session.log
|
||||
tail /tmp/build.log # last build's output
|
||||
|
||||
# 5. Check per-process stdout/stderr logs if needed
|
||||
ls /data/log2/current/
|
||||
cat /data/log2/current/gpsd.log
|
||||
```
|
||||
|
||||
### Changing a Service's Publish Rate
|
||||
|
||||
SubMaster's `freq_ok` check requires observed rate to fall within
|
||||
`[0.8 × min_freq, 1.2 × max_freq]` of the value declared in
|
||||
`cereal/services.py`. Publishing *faster* than declared fails the check
|
||||
just as surely as publishing too slowly — it trips `all_freq_ok()` →
|
||||
`EventName.commIssue` → "TAKE CONTROL IMMEDIATELY / Communication Issue
|
||||
Between Processes".
|
||||
|
||||
If you change how often a process publishes, you **must** update the rate
|
||||
in `cereal/services.py` to match. Example (real bug we hit):
|
||||
|
||||
- Bumped thermald from 2Hz → 4Hz (DT_TRML 0.5→0.25) for faster fan control
|
||||
- Bumped gpsd from 1Hz → 2Hz
|
||||
- manager publishes `managerState` gated by `sm.update()` returning on
|
||||
`deviceState`, so it picks up thermald's rate automatically
|
||||
|
||||
`services.py` still declared `deviceState` and `managerState` as 2Hz
|
||||
(ceiling 2.4Hz) and `gpsLocation` as 1Hz (ceiling 1.2Hz), so controlsd
|
||||
fired `commIssue` on every cycle the moment any of these arrived at the
|
||||
new faster rate.
|
||||
|
||||
Fix: update the second tuple element (Hz) in `cereal/services.py` for the
|
||||
affected service. Cereal will use the updated rate for all consumers.
|
||||
|
||||
### Adding New Params
|
||||
|
||||
The params system uses a C++ whitelist. Adding a new param name without registering it will crash with `UnknownKeyName`. To add one:
|
||||
The params system uses a C++ whitelist. Adding a new param name in `manager.py` alone will crash with `UnknownKeyName`. You must:
|
||||
|
||||
1. Register the key in `common/params.cc` (alphabetically, with `PERSISTENT` or `CLEAR_ON_*` flag)
|
||||
2. For memory params, add a default in `manager_init()`'s memory-params loop (after the persistent default loop)
|
||||
2. Add the default value in `selfdrive/manager/manager.py` in `manager_init()`
|
||||
3. Remove `prebuilt`, `common/params.o`, and `common/libcommon.a` to force rebuild
|
||||
|
||||
### Memory Params (paramsMemory)
|
||||
|
||||
ClearPilot uses memory params (`/dev/shm/params/d/`) for transient state that should reset on boot. Conventions:
|
||||
ClearPilot uses memory params (`/dev/shm/params/d/`) for UI toggles that should reset on boot. Key rules:
|
||||
|
||||
- **Registration**: register in `common/params.cc` as `PERSISTENT` (the registration flag does NOT control which path the param lives at — `/dev/shm` is tmpfs and clears on reboot regardless)
|
||||
- **C++ access**: `Params{"/dev/shm/params"}` (the Params class appends `/d/` internally — `Params("/dev/shm/params/d")` would resolve to `/dev/shm/params/d/d/`)
|
||||
- **Python access**: `Params("/dev/shm/params")`
|
||||
- **UI toggles**: use `ToggleControl` with manual `toggleFlipped` lambda, not `ParamControl` (which only handles persistent params)
|
||||
- **IMPORTANT — method names differ between C++ and Python**: C++ uses camelCase (`putBool`, `getBool`, `getInt`), Python uses snake_case (`put_bool`, `get_bool`, `get_int`). This is a common source of silent failures.
|
||||
- **Registration**: Register the key in `common/params.cc` as `PERSISTENT` (same as FrogPilot pattern — the registration flag does NOT control which path the param lives at)
|
||||
- **C++ access**: Use `Params{"/dev/shm/params"}` — stored as a class member `paramsMemory` in onroad.h. Do NOT use `Params("/dev/shm/params/d")` — the Params class appends `/d/` internally, so that would resolve to `/dev/shm/params/d/d/`
|
||||
- **Python access**: Use `Params("/dev/shm/params")`
|
||||
- **Defaults**: Set in `manager_init()` via `Params("/dev/shm/params").put(key, value)`
|
||||
- **UI toggles**: Use `ToggleControl` with manual `toggleFlipped` lambda that writes via `Params("/dev/shm/params")`. Do NOT use `ParamControl` for memory params — it reads/writes persistent params only
|
||||
- **Current memory params**: `TelemetryEnabled` (default "0"), `VpnEnabled` (default "1"), `ModelStandby` (default "0"), `ScreenDisplayMode`, `DashcamState` (default "stopped"), `DashcamFrames` (default "0"), `DashcamShutdown` (default "0")
|
||||
- **IMPORTANT — method names differ between C++ and Python**: C++ uses camelCase (`putBool`, `getBool`, `getInt`), Python uses snake_case (`put_bool`, `get_bool`, `get_int`). This is a common source of silent failures — the wrong casing compiles/runs but doesn't work.
|
||||
|
||||
Currently registered memory-param keys (in `params.cc`): `ScreenDisplayMode`, `CPTLkasButtonAction`, `CarIsMetric`, `Clearpilot{CruiseWarning,CruiseWarningSpeed,HasSpeed,IsMetric,PlayDing,ShowHealthMetrics,SpeedDisplay,SpeedLimitDisplay,SpeedUnit}`, `Dashcam{Frames,Shutdown,State}`, `IsDaylight`, `ModelFps`, `ModelStandby`, `ModelStandbyTs`, `ScreenRecorderDebug`, `ShutdownTouchReset`, `TelemetryEnabled`, `VpnEnabled`, `no_lat_lane_change`. Bench-mode and `LogDirInitialized` keys not registered yet.
|
||||
### Building Native (C++) Processes
|
||||
|
||||
### Changing a Service's Publish Rate
|
||||
- SCons is the build system. Static libraries (`common`, `messaging`, `cereal`, `visionipc`) must be imported as SCons objects, not `-l` flags
|
||||
- The `--as-needed` linker flag can cause link order issues with static libs — disable it in your SConscript if needed
|
||||
- OMX encoder object (`omx_encoder.o`) is compiled by the UI build — reference the pre-built `.o` file rather than recompiling (avoids "two environments" scons error)
|
||||
- `prebuilt` is recreated after every successful build — always remove it before rebuilding
|
||||
|
||||
SubMaster's `freq_ok` check requires observed rate to fall within `[0.8 × min_freq, 1.2 × max_freq]` of the value declared in `cereal/services.py`. Publishing *faster* than declared trips `commIssue` just as surely as too slow. If you change how often a process publishes, update the rate in `cereal/services.py` to match.
|
||||
## Bench Mode (Onroad UI Testing)
|
||||
|
||||
Bench mode allows testing the onroad UI without a car connected. It runs a fake vehicle simulator (`bench_onroad.py`) as a managed process and disables real car processes (pandad, thermald, controlsd, etc.).
|
||||
|
||||
### Usage
|
||||
|
||||
**IMPORTANT**: Do NOT use `echo` to write bench params — `echo` appends a newline which causes param parsing to fail silently (e.g. gear stays in park). Always use the `bench_cmd.py` tool.
|
||||
|
||||
```bash
|
||||
# 1. Build first
|
||||
chown -R comma:comma /data/openpilot
|
||||
su - comma -c "bash /data/openpilot/build_only.sh"
|
||||
|
||||
# 2. Start in bench mode
|
||||
su - comma -c "bash /data/openpilot/launch_openpilot.sh --bench"
|
||||
|
||||
# 3. Wait for UI to be ready (polls RPC every 1s, up to 20s)
|
||||
su - comma -c "PYTHONPATH=/data/openpilot python3 -m selfdrive.clearpilot.bench_cmd wait_ready"
|
||||
|
||||
# 4. Control vehicle state
|
||||
su - comma -c "PYTHONPATH=/data/openpilot python3 -m selfdrive.clearpilot.bench_cmd gear D"
|
||||
su - comma -c "PYTHONPATH=/data/openpilot python3 -m selfdrive.clearpilot.bench_cmd speed 20"
|
||||
su - comma -c "PYTHONPATH=/data/openpilot python3 -m selfdrive.clearpilot.bench_cmd speedlimit 45"
|
||||
su - comma -c "PYTHONPATH=/data/openpilot python3 -m selfdrive.clearpilot.bench_cmd cruise 55"
|
||||
su - comma -c "PYTHONPATH=/data/openpilot python3 -m selfdrive.clearpilot.bench_cmd engaged 1"
|
||||
|
||||
# 5. Inspect UI widget tree (RPC call, instant response)
|
||||
su - comma -c "PYTHONPATH=/data/openpilot python3 -m selfdrive.clearpilot.bench_cmd dump"
|
||||
```
|
||||
|
||||
### Debugging Crashes
|
||||
|
||||
The UI has a SIGSEGV/SIGABRT crash handler (`selfdrive/ui/main.cc`) that prints a stack trace to stderr, captured in the per-process log:
|
||||
|
||||
```bash
|
||||
# Check for crash traces (use /data/log2/current which is always the active session)
|
||||
grep -A 30 "CRASH" /data/log2/current/ui.log
|
||||
|
||||
# Resolve addresses to source lines
|
||||
addr2line -e /data/openpilot/selfdrive/ui/ui -f 0xADDRESS
|
||||
|
||||
# bench_cmd dump detects crash loops automatically:
|
||||
# if UI process uptime < 5s, it reports "likely crash looping"
|
||||
|
||||
# Check per-process logs
|
||||
ls /data/log2/current/
|
||||
cat /data/log2/current/session.log
|
||||
cat /data/log2/current/gpsd.log
|
||||
```
|
||||
|
||||
### UI Introspection RPC
|
||||
|
||||
The UI process runs a ZMQ REP server at `ipc:///tmp/clearpilot_ui_rpc`. Send `"dump"` to get a recursive widget tree showing class name, visibility, geometry, and stacked layout current indices. This is the primary debugging tool for understanding what the UI is displaying.
|
||||
|
||||
- `bench_cmd dump` — queries the RPC and prints the widget tree
|
||||
- `bench_cmd wait_ready` — polls the RPC every second until `ReadyWindow` is visible (up to 10s)
|
||||
- `ui_dump.py` — standalone dump tool (same as `bench_cmd dump`)
|
||||
|
||||
### Architecture
|
||||
|
||||
- `launch_openpilot.sh --bench` sets `BENCH_MODE=1` env var
|
||||
- `manager.py` reads `BENCH_MODE`, blocks real car processes, enables `bench_onroad` process
|
||||
- `bench_onroad.py` publishes fake `pandaStates` (ignition=true), `carState`, `controlsState` — thermald reads the fake pandaStates to determine ignition and publishes `deviceState.started=true` on its own
|
||||
- thermald and camerad run normally in bench mode (thermald manages CPU cores needed for camerad)
|
||||
- Blocked processes in bench mode: pandad, controlsd, radard, plannerd, calibrationd, torqued, paramsd, locationd, sensord, ubloxd, pigeond, dmonitoringmodeld, dmonitoringd, modeld, soundd, loggerd, micd, dashcamd
|
||||
|
||||
### Key Files
|
||||
|
||||
| File | Role |
|
||||
|------|------|
|
||||
| `selfdrive/clearpilot/bench_onroad.py` | Fake vehicle state publisher |
|
||||
| `selfdrive/clearpilot/bench_cmd.py` | Command tool for setting bench params and querying UI |
|
||||
| `selfdrive/clearpilot/ui_dump.py` | Standalone UI widget tree dump |
|
||||
| `selfdrive/manager/process_config.py` | Registers bench_onroad as managed process (enabled=BENCH_MODE) |
|
||||
| `selfdrive/manager/manager.py` | Blocks conflicting processes in bench mode |
|
||||
| `launch_openpilot.sh` | Accepts `--bench` flag, exports BENCH_MODE env var |
|
||||
| `selfdrive/ui/qt/window.cc` | UI RPC server (`ipc:///tmp/clearpilot_ui_rpc`), widget tree dump |
|
||||
|
||||
### Resolved Issues
|
||||
|
||||
- **SIGSEGV in onroad view (fixed)**: `update_model()` in `ui.cc` dereferenced empty model position data when modeld wasn't running. Fixed by guarding against empty `plan_position.getX()`. The root cause was found using the crash handler + `addr2line`.
|
||||
- **`showDriverView` overriding transitions (fixed)**: was forcing `slayout` to onroad/home every frame at 20Hz, overriding park/drive logic. Fixed to only act when not in started state.
|
||||
- **Sidebar appearing during onroad transition (fixed)**: `MainWindow::closeSettings()` was re-enabling the sidebar. Fixed by not calling `closeSettings` during `offroadTransition`.
|
||||
|
||||
## Performance Profiling
|
||||
|
||||
Use `py-spy` to find CPU hotspots in any Python process. It's installed at `/home/comma/.local/bin/py-spy`. (If missing: `su - comma -c "/usr/local/pyenv/versions/3.11.4/bin/pip install py-spy"`.)
|
||||
|
||||
```bash
|
||||
# Find the target pid
|
||||
ps -eo pid,cmd | grep -E "selfdrive.controls.controlsd" | grep -v grep
|
||||
|
||||
# Record 10s of stacks at 200Hz, raw (folded) format
|
||||
/home/comma/.local/bin/py-spy record -o /tmp/ctrl.txt --pid <PID> --duration 10 --rate 200 --format raw
|
||||
|
||||
# Aggregate: which line of step() is consuming the most samples
|
||||
awk -F';' '{
|
||||
for(i=1;i<=NF;i++) if ($i ~ /step \(selfdrive\/controls\/controlsd.py/) step_line=i;
|
||||
if (step_line && step_line < NF) {
|
||||
n=split($NF, parts, " "); count=parts[n];
|
||||
caller = $(step_line+1);
|
||||
sum[caller] += count;
|
||||
}
|
||||
step_line=0;
|
||||
} END { for (c in sum) printf "%6d %s\n", sum[c], c }' /tmp/ctrl.txt | sort -rn | head -15
|
||||
|
||||
# Aggregate by a source file — shows hottest lines in that file
|
||||
awk -F';' '{
|
||||
for(i=1;i<=NF;i++) if ($i ~ /carstate\.py:/) {
|
||||
match($i, /:[0-9]+/); ln = substr($i, RSTART+1, RLENGTH-1);
|
||||
n=split($NF, parts, " "); count=parts[n];
|
||||
sum[ln] += count;
|
||||
}
|
||||
} END { for (l in sum) printf "%5d line %s\n", sum[l], l }' /tmp/ctrl.txt | sort -rn | head -15
|
||||
|
||||
# Quick stack dump (single sample, no recording)
|
||||
/home/comma/.local/bin/py-spy dump --pid <PID>
|
||||
```
|
||||
|
||||
**Known performance trap — hot `Params` writes**: `Params.put()` does `mkstemp` + `fsync` + `flock` + `rename` + `fsync_dir`. At 100Hz even on tmpfs the `flock` contention is ruinous. Cache the last-written value and skip writes when unchanged. Found this pattern in `carstate.py` and `controlsd.py` — controlsd went from 69% → 28% CPU after gating writes.
|
||||
|
||||
## Session Logging
|
||||
|
||||
Per-process stderr and an aggregate event log are captured in `/data/log2/current/`.
|
||||
|
||||
### Log Directory
|
||||
|
||||
- `/data/log2/current/` is always the active session directory
|
||||
- `init_log_dir()` is called once from `manager_init()` — creates a fresh `/data/log2/current/` real directory
|
||||
- If a previous `current/` real directory exists (unresolved session), it's renamed using its mtime timestamp
|
||||
- If a previous `current` symlink exists, it's removed
|
||||
- Once system time is valid (GPS/NTP), the real directory is renamed to `/data/log2/YYYY-MM-DD-HH-MM-SS/` and `current` becomes a symlink to it
|
||||
- `LogDirInitialized` param: `"0"` until time resolves, then `"1"`
|
||||
- Open file handles survive the rename (same inode, same filesystem)
|
||||
- Session directories older than 30 days are deleted on manager startup
|
||||
|
||||
### Per-Process Logs
|
||||
|
||||
- Every `PythonProcess` and `NativeProcess` has stderr redirected to `{name}.log` in the session directory
|
||||
- `DaemonProcess` (athenad) redirects both stdout+stderr (existing behavior)
|
||||
- Stderr is redirected via `os.dup2` inside the forked child process
|
||||
|
||||
### Aggregate Session Log (`session.log`)
|
||||
|
||||
A single `session.log` in each session directory records major events:
|
||||
|
||||
- Manager start/stop/crash
|
||||
- Process starts, deaths (with exit codes), watchdog restarts
|
||||
- Onroad/offroad transitions
|
||||
|
||||
### Key Files
|
||||
|
||||
| File | Role |
|
||||
|------|------|
|
||||
| `selfdrive/manager/process.py` | Log directory creation, stderr redirection, session_log logger |
|
||||
| `selfdrive/manager/manager.py` | Log rotation cleanup, session event logging |
|
||||
| `build_only.sh` | Build-only script (no manager start) |
|
||||
|
||||
## Dashcam (dashcamd)
|
||||
|
||||
### Architecture
|
||||
|
||||
`dashcamd` is a native C++ process that captures raw camera frames directly from `camerad` via VisionIPC and encodes to MP4 using the Qualcomm OMX H.264 hardware encoder. This replaces the earlier FrogPilot screen recorder approach (`QWidget::grab()` -> OMX).
|
||||
|
||||
- **Codec**: H.264 AVC (hardware accelerated via `OMX.qcom.video.encoder.avc`)
|
||||
- **Resolution**: 1928x1208 (full camera resolution, no downscaling)
|
||||
- **Bitrate**: 2500 kbps
|
||||
- **Container**: MP4 (remuxed via libavformat) + SRT subtitle sidecar
|
||||
- **Segment length**: 3 minutes per file
|
||||
- **Save path**: `/data/media/0/videos/YYYYMMDD-HHMMSS/YYYYMMDD-HHMMSS.mp4` (trip directories)
|
||||
- **GPS subtitles**: companion `.srt` file per segment with 1Hz entries (speed MPH, lat/lon, UTC timestamp)
|
||||
- **Trip lifecycle**: waits in WAITING state until valid system time + GPS fix + car in drive; records until car parked 10 min or ignition off; then returns to WAITING
|
||||
- **Graceful shutdown**: thermald sets `DashcamShutdown` param, dashcamd closes segment and acks within 15s
|
||||
- **Storage**: ~56 MB per 3-minute segment at 2500 kbps (verified: actual bitrate ~2570 kbps)
|
||||
- **Crash handler**: SIGSEGV/SIGABRT handler writes backtrace to `/tmp/dashcamd_crash.log`
|
||||
- **Storage device**: WDC SDINDDH4-128G UFS 2.1 — automotive grade, ~384 TB write endurance, no concern for continuous writes
|
||||
|
||||
### OmxEncoder
|
||||
|
||||
The OMX encoder (`selfdrive/frogpilot/screenrecorder/omx_encoder.cc`) was ported from upstream FrogPilot with the following key properties:
|
||||
|
||||
- Each encoder instance calls `OMX_Init()` in constructor and `OMX_Deinit()` in destructor — manages its own OMX lifecycle
|
||||
- Constructor takes 5 args: `(path, width, height, fps, bitrate)` — no h265/downscale params
|
||||
- `encoder_close()` calls `av_write_trailer` + ffmpeg faststart remux (`-movflags +faststart`)
|
||||
- Destructor has null guards and error handling on all OMX state transitions
|
||||
- ClearPilot addition: `encode_frame_nv12()` for direct NV12 input (dashcamd), alongside original `encode_frame_rgba()` (screen recorder)
|
||||
|
||||
### Key Files
|
||||
|
||||
| File | Role |
|
||||
|------|------|
|
||||
| `selfdrive/clearpilot/dashcamd.cc` | Main dashcam process — VisionIPC -> OMX encoder |
|
||||
| `selfdrive/clearpilot/SConscript` | Build config for dashcamd |
|
||||
| `selfdrive/frogpilot/screenrecorder/omx_encoder.cc` | OMX encoder (upstream FrogPilot port + `encode_frame_nv12`) |
|
||||
| `selfdrive/frogpilot/screenrecorder/omx_encoder.h` | Encoder header |
|
||||
| `selfdrive/manager/process_config.py` | dashcamd registered as NativeProcess, camerad always_run, encoderd disabled |
|
||||
| `system/loggerd/deleter.py` | Trip-aware storage rotation (oldest trip first, then segments within active trip) |
|
||||
|
||||
### Params
|
||||
|
||||
- `DashcamShutdown` (memory param) — set by thermald before power-off, dashcamd acks by clearing it
|
||||
- `DashcamState` (memory param) — "stopped", "waiting", or "recording" — published every 5s
|
||||
- `DashcamFrames` (memory param) — per-trip encoded frame count, resets each trip — published every 5s
|
||||
|
||||
## Standstill Power Saving
|
||||
|
||||
When `carState.standstill` is true:
|
||||
|
||||
- **modeld**: skips GPU inference on 19/20 frames (1fps vs 20fps), reports 0 frame drops to avoid triggering `modeldLagging` in controlsd. Runs full 20fps during calibration (`liveCalibration.calStatus != calibrated`)
|
||||
- **dmonitoringmodeld**: same 1fps throttle, added `carState` subscription
|
||||
- **Fan controller**: uses offroad clamps (0-30%) instead of onroad (30-100%) at standstill; thermal protection still active via feedforward if temp > 60°C
|
||||
|
||||
### Key Files
|
||||
|
||||
| File | Role |
|
||||
|------|------|
|
||||
| `selfdrive/modeld/modeld.py` | Standstill frame skip logic |
|
||||
| `selfdrive/modeld/dmonitoringmodeld.py` | Standstill frame skip logic |
|
||||
| `selfdrive/thermald/fan_controller.py` | Standstill-aware fan clamps |
|
||||
| `selfdrive/thermald/thermald.py` | Passes standstill to fan controller via carState |
|
||||
|
||||
## Display Modes (LFA/LKAS Debug Button)
|
||||
|
||||
The Hyundai Tucson's LFA steering wheel button cycles through 5 display modes via `ScreenDisplayMode` param (`/dev/shm/params`, CLEAR_ON_MANAGER_START, default 0).
|
||||
|
||||
### Display States
|
||||
|
||||
| State | Name | Display | Camera | Path Drawing |
|
||||
|-------|------|---------|--------|-------------|
|
||||
| 0 | auto-normal | on | yes | filled (normal) |
|
||||
| 1 | auto-nightrider | on | black | 2px outline only |
|
||||
| 2 | normal (manual) | on | yes | filled (normal) |
|
||||
| 3 | screen off | GPIO off | n/a | n/a |
|
||||
| 4 | nightrider (manual) | on | black | 2px outline only |
|
||||
|
||||
### Auto Day/Night Switching
|
||||
|
||||
- `gpsd.py` computes `is_daylight(lat, lon, utc_dt)` using NOAA solar position algorithm
|
||||
- First calculation immediately on GPS fix + valid clock, then every 30 seconds
|
||||
- State 0 + sunset → auto-switch to 1; State 1 + sunrise → auto-switch to 0
|
||||
- States 2/3/4 are never touched by auto logic
|
||||
- `IsDaylight` param written to `/dev/shm/params` for reference
|
||||
|
||||
### Button Behavior
|
||||
|
||||
**Onroad (car in drive gear):** 0→4, 1→2, 2→3, 3→4, 4→2 (never back to auto via button)
|
||||
|
||||
**Not in drive (parked/off):** any except 3 → 3 (screen off), state 3 → 0 (auto-normal)
|
||||
|
||||
**Shift to drive from screen off:** auto-resets to mode 0 (auto-normal) via `controlsd`
|
||||
|
||||
**Shift to park from nightrider:** auto-switches to mode 3 (screen off) via `home.cc`
|
||||
|
||||
**Tap screen while screen off:** resets to mode 0 (auto-normal) via `window.cc` touch handler
|
||||
|
||||
### Nightrider Mode
|
||||
|
||||
- Camera feed suppressed (OpenGL clears to black instead of rendering camera texture)
|
||||
- All HUD elements (speed, alerts, telemetry indicator) still visible
|
||||
- Path/lane polygons drawn as 2px outlines only (no gradient fill)
|
||||
- Lane lines, road edges, blind spot paths, adjacent paths all use outline rendering
|
||||
|
||||
### Signal Chain
|
||||
|
||||
```
|
||||
Steering wheel LFA button press
|
||||
-> CAN-FD: cruise_btns_msg_canfd["LFA_BTN"]
|
||||
-> Edge detection → ButtonEvent(type=FrogPilotButtonType.lkas)
|
||||
-> controlsd.clearpilot_state_control()
|
||||
-> Reads ScreenDisplayMode, applies transition table based on driving_gear
|
||||
-> Writes new ScreenDisplayMode to /dev/shm/params
|
||||
-> UI reads ScreenDisplayMode in paintEvent() (for camera/nightrider)
|
||||
and drawHud() (for display power on/off)
|
||||
```
|
||||
|
||||
### Key Files
|
||||
|
||||
| File | Role |
|
||||
|------|------|
|
||||
| `selfdrive/controls/controlsd.py` | Button state machine, writes ScreenDisplayMode |
|
||||
| `selfdrive/ui/qt/onroad.cc` | Nightrider rendering, display power control |
|
||||
| `selfdrive/ui/qt/home.cc` | Display power for park/splash state |
|
||||
| `system/clearpilot/gpsd.py` | Sunset/sunrise calc, auto day/night transitions |
|
||||
| `selfdrive/clearpilot/bench_cmd.py` | `debugbutton` command simulates button press |
|
||||
|
||||
## Screen Timeout / Display Power
|
||||
|
||||
Display power is managed by `Device::updateWakefulness()` in `selfdrive/ui/ui.cc`.
|
||||
|
||||
- **Ignition off (offroad)**: screen blanks after `ScreenTimeout` seconds (default 120) of no touch. Tapping wakes it.
|
||||
- **Ignition on (onroad)**: screen stays on unconditionally — ignition=true short-circuits the timeout check.
|
||||
- **ScreenDisplayMode 3 override**: `updateWakefulness` checks `ScreenDisplayMode` first — if mode 3, calls `setAwake(false)` unconditionally, preventing ignition-on from overriding screen-off.
|
||||
- **Debug button (LFA)**: cycles through display modes including screen off (state 3).
|
||||
- **Park transition**: always shows splash screen; if coming from nightrider mode, auto-switches to screen off (mode 3) via `home.cc`.
|
||||
- **Touch wake**: tapping screen while in mode 3 resets to mode 0 via `window.cc` event filter.
|
||||
|
||||
## Offroad UI (ClearPilot Menu)
|
||||
|
||||
The offroad home screen (`selfdrive/ui/qt/home.cc`) is a sidebar settings panel replacing the stock home screen. The original system settings are no longer accessible — the ClearPilot menu is the only settings interface.
|
||||
|
||||
### Panels
|
||||
|
||||
- **General**: Status window, Reset Calibration, Shutdown Timer, Reboot/Soft Reboot/Power Off
|
||||
- **Network**: WiFi management, tethering, roaming, hidden networks (APN settings removed)
|
||||
- **Dashcam**: placeholder for future dashcam footage viewer
|
||||
- **Debug**: Telemetry logging toggle
|
||||
|
||||
### Navigation
|
||||
|
||||
- Tapping the splash screen (ReadyWindow) opens the ClearPilot menu
|
||||
- "← Back" button returns to splash or onroad view
|
||||
- Sidebar with stock metrics (TEMP, VEHICLE, CONNECT) is hidden
|
||||
|
||||
## Device: comma 3x
|
||||
|
||||
- Qualcomm Snapdragon SoC (aarch64), serial `comma-3889765b`
|
||||
### Hardware
|
||||
|
||||
- Qualcomm Snapdragon SoC (aarch64)
|
||||
- Serial: comma-3889765b
|
||||
- Storage: WDC SDINDDH4-128G, 128 GB UFS 2.1
|
||||
- Ubuntu 20.04.6 LTS on AGNOS 9.7
|
||||
- Kernel 4.9.103+ (custom comma.ai PREEMPT build, vendor-patched Qualcomm)
|
||||
- Python 3.11.4 via pyenv at `/usr/local/pyenv/versions/3.11.4/` (system python 3.8 — do not use)
|
||||
- Display: Weston (Wayland) on tty1
|
||||
- Hardware encoding: OMX (`OMX.qcom.video.encoder.avc` / `.hevc`); V4L2 VIDC exists but is not usable from ffmpeg subprocess
|
||||
- Connects to the car via comma panda (CAN bus interface)
|
||||
|
||||
### Filesystem mount quirks
|
||||
### Operating System
|
||||
|
||||
| Mount | Device | Type | Notes |
|
||||
|---|---|---|---|
|
||||
| `/` | /dev/sda7 | ext4 | rw |
|
||||
| `/data` | /dev/sda12 | ext4 | **persistent** — openpilot lives here |
|
||||
| `/home` | overlay | overlayfs | **volatile** (upper on tmpfs) — changes lost on reboot |
|
||||
| `/tmp` | tmpfs | tmpfs | volatile |
|
||||
| `/persist` | /dev/sda2 | ext4 | persistent config/certs, noexec |
|
||||
| `/dsp` | /dev/sde26 | ext4 | **read-only** Qualcomm DSP firmware |
|
||||
| `/firmware` | /dev/sde4 | vfat | **read-only** firmware blobs |
|
||||
- **Ubuntu 20.04.6 LTS (Focal Fossa)** on aarch64
|
||||
- **Kernel**: 4.9.103+ (custom comma.ai PREEMPT build, Feb 2024) — very old, vendor-patched Qualcomm kernel
|
||||
- **Python**: 3.11.4 via pyenv at `/usr/local/pyenv/versions/3.11.4/` (system python is 3.8, do not use)
|
||||
- **AGNOS version**: 9.7 (comma's custom OS layer on top of Ubuntu)
|
||||
- **Display server**: Weston (Wayland compositor) on tty1
|
||||
- **SELinux**: mounted (enforcement status varies)
|
||||
|
||||
### GPS
|
||||
### Users
|
||||
|
||||
The device has **no u-blox chip** (`/dev/ttyHS0` does not exist). GPS is the **Quectel EC25 LTE modem**'s built-in GPS, accessed via AT commands through `mmcli`. The original `qcomgpsd` is broken on this device because the diag interface hangs after setup. `system/clearpilot/gpsd.py` is the replacement; locationd is patched not to consume `gpsLocation` so the kalman filter isn't tainted.
|
||||
- `comma` (uid=1000) — the user openpilot runs as; member of root, sudo, disk, gpu, gpio groups
|
||||
- `root` — what we SSH in as; files must be chowned back to comma before running openpilot
|
||||
|
||||
### Sidebar / Process Notes
|
||||
### Filesystem / Mount Quirks
|
||||
|
||||
- `camerad` runs `always_run` (was `driverview`) — this is needed for dashcamd. Costs steady-state camera power even when ignition is off.
|
||||
- `loggerd`, `encoderd`, `stream_encoderd` are commented out in `process_config.py` — no segment or video logging
|
||||
- `qcomgpsd` is commented out; `system.clearpilot.gpsd` is registered in its place (gated by `qcomgps` callback = `not ublox_available()`, which is always true on this device)
|
||||
- `speed_logicd` is `only_onroad`; `dashcamd` is `always_run` (manages own trip lifecycle)
|
||||
- `uploader` already commented out in baseline
|
||||
| Mount | Device | Type | Notes |
|
||||
|-------------|-------------|---------|-------|
|
||||
| `/` | /dev/sda7 | ext4 | Root filesystem, read-write |
|
||||
| `/data` | /dev/sda12 | ext4 | **Persistent**. Openpilot lives here. Survives reboots. |
|
||||
| `/home` | overlay | overlayfs | **VOLATILE** — upperdir on tmpfs, changes lost on reboot |
|
||||
| `/tmp` | tmpfs | tmpfs | Volatile, 150 MB |
|
||||
| `/var` | tmpfs | tmpfs | Volatile, 128 MB (fstab) / 1.5 GB (actual) |
|
||||
| `/systemrw` | /dev/sda10 | ext4 | Writable system overlay, noexec |
|
||||
| `/persist` | /dev/sda2 | ext4 | Persistent config/certs, noexec |
|
||||
| `/cache` | /dev/sda11 | ext4 | Android-style cache partition |
|
||||
| `/dsp` | /dev/sde26 | ext4 | **Read-only** Qualcomm DSP firmware |
|
||||
| `/firmware` | /dev/sde4 | vfat | **Read-only** firmware blobs |
|
||||
|
||||
### Hardware Encoding
|
||||
|
||||
- **OMX**: `OMX.qcom.video.encoder.avc` (H.264) and `OMX.qcom.video.encoder.hevc` — used by dashcamd and screen recorder
|
||||
- **V4L2**: Qualcomm VIDC at `/dev/v4l/by-path/platform-aa00000.qcom_vidc-video-index1` — used by encoderd (now disabled). Not accessible from ffmpeg due to permission/driver issues
|
||||
- **ffmpeg**: v4.2.2, has `h264_v4l2m2m` and `h264_omx` listed but neither works from ffmpeg subprocess (OMX port issues, V4L2 device not found). Use OMX directly via the C++ encoder
|
||||
|
||||
### Fan Control
|
||||
|
||||
Software-controlled via `thermald` -> `fan_controller.py` -> panda USB -> PWM. Target temp 70°C, PI+feedforward controller. See Standstill Power Saving section for standstill-aware clamps.
|
||||
|
||||
## Boot Sequence
|
||||
|
||||
```
|
||||
Power On
|
||||
→ systemd: comma.service (runs as comma user)
|
||||
→ /usr/comma/comma.sh (waits for Weston, handles factory reset)
|
||||
→ /data/continue.sh
|
||||
→ /data/openpilot/launch_openpilot.sh
|
||||
→ kill stale instances (launch_openpilot, launch_chffrplus, manager.py, ./ui, _text by comm)
|
||||
→ bash system/clearpilot/on_start.sh (SSH, WiFi, run provision.sh)
|
||||
→ background system/clearpilot/vpn-monitor.sh
|
||||
→ background system/clearpilot/nice-monitor.sh
|
||||
→ exec ./launch_chffrplus.sh
|
||||
→ source launch_env.sh
|
||||
→ run agnos_init
|
||||
→ set PYTHONPATH
|
||||
→ if no `prebuilt`: run build.py (spinner + scons)
|
||||
→ exec selfdrive/manager/manager.py
|
||||
→ manager_init() sets default params (persistent + memory)
|
||||
→ ensure_running() loop starts managed processes
|
||||
-> systemd: comma.service (runs as comma user)
|
||||
-> /usr/comma/comma.sh (waits for Weston, handles factory reset)
|
||||
-> /data/continue.sh (exec bridge to openpilot)
|
||||
-> /data/openpilot/launch_openpilot.sh
|
||||
-> Kills other instances of itself and manager.py
|
||||
-> Runs on_start.sh (logo, reverse SSH)
|
||||
-> exec launch_chffrplus.sh
|
||||
-> Sources launch_env.sh (thread counts, AGNOS_VERSION)
|
||||
-> Runs agnos_init (marks boot slot, GPU perms, checks OS update)
|
||||
-> Sets PYTHONPATH, symlinks /data/pythonpath
|
||||
-> Runs build.py if no `prebuilt` marker
|
||||
-> Launches selfdrive/manager/manager.py
|
||||
-> manager_init() sets default params
|
||||
-> ensure_running() loop starts all managed processes
|
||||
```
|
||||
|
||||
## Openpilot Architecture
|
||||
|
||||
### Process Manager
|
||||
|
||||
`selfdrive/manager/manager.py` orchestrates all processes defined in `selfdrive/manager/process_config.py`.
|
||||
|
||||
### Always-Running Processes (offroad + onroad)
|
||||
|
||||
- `thermald` — thermal management and fan control
|
||||
- `pandad` — panda CAN bus interface
|
||||
- `ui` — Qt-based onroad/offroad UI
|
||||
- `deleter` — storage cleanup (9 GB threshold)
|
||||
- `statsd`, `timed`, `logmessaged`, `tombstoned` — telemetry/logging
|
||||
- `manage_athenad` — comma cloud connectivity
|
||||
- `fleet_manager`, `frogpilot_process` — FrogPilot additions
|
||||
|
||||
### Onroad-Only Processes (when driving)
|
||||
|
||||
- `controlsd` — main vehicle control loop
|
||||
- `plannerd` — path planning
|
||||
- `radard` — radar processing
|
||||
- `modeld` — driving model inference (throttled to 1fps at standstill)
|
||||
- `dmonitoringmodeld` — driver monitoring model (throttled to 1fps at standstill)
|
||||
- `locationd`, `calibrationd`, `paramsd`, `torqued` — localization and calibration
|
||||
- `sensord` — IMU/sensor data
|
||||
- `soundd` — alert sounds
|
||||
- `camerad` — camera capture
|
||||
- `loggerd` — CAN/sensor log recording (video encoding disabled)
|
||||
|
||||
### ClearPilot Processes
|
||||
|
||||
- `dashcamd` — raw camera dashcam recording (always runs; manages its own trip lifecycle)
|
||||
|
||||
### GPS
|
||||
|
||||
- Device has **no u-blox chip** (`/dev/ttyHS0` does not exist) — `ubloxd`/`pigeond` never start
|
||||
- GPS hardware is a **Quectel EC25 LTE modem** (USB, `lsusb: 2c7c:0125`) with built-in GPS
|
||||
- GPS is accessed via AT commands through `mmcli`: `mmcli -m any --command='AT+QGPSLOC=2'`
|
||||
- **`qcomgpsd`** (original openpilot process) uses the modem's diag interface which is broken on this device — the diag recv loop blocks forever after setup. Commented out.
|
||||
- **`system/clearpilot/gpsd.py`** is the replacement — polls GPS via AT commands at 1Hz, publishes `gpsLocation` cereal messages
|
||||
- GPS data flows: `gpsd` → `gpsLocation` → `locationd` → `liveLocationKalman` → `timed` (sets system clock)
|
||||
- `locationd` checks `UbloxAvailable` param (false on this device) to subscribe to `gpsLocation` instead of `gpsLocationExternal`
|
||||
- `mmcli` returns `response: '...'` wrapper — `at_cmd()` strips it before parsing (fixed)
|
||||
- GPS antenna power must be enabled via GPIO: `gpio_set(GPIO.GNSS_PWR_EN, True)`
|
||||
- System `/usr/sbin/gpsd` daemon may respawn and interfere — should be disabled or killed
|
||||
|
||||
### Telemetry
|
||||
|
||||
- **Client**: `selfdrive/clearpilot/telemetry.py` — `tlog(group, data)` sends JSON over ZMQ PUSH
|
||||
- **Collector**: `selfdrive/clearpilot/telemetryd.py` — diffs against previous state, writes changed values to CSV
|
||||
- **Toggle**: `TelemetryEnabled` memory param, controlled from Debug panel in ClearPilot menu
|
||||
- **Auto-disable**: disabled on every manager start; disabled if `/data` free < 5GB
|
||||
- **Hyundai CAN-FD data**: logged from `selfdrive/car/hyundai/carstate.py` `update_canfd()` — groups: `car`, `cruise`, `speed_limit`, `buttons`
|
||||
- **GPS data**: logged directly by telemetryd via cereal `gpsLocation` subscription at 1Hz — group: `gps` (latitude, longitude, speed, altitude, bearing, accuracy)
|
||||
- **CSV location**: `/data/log2/current/telemetry.csv` (or session directory)
|
||||
- **Onroad overlay**: when telemetry enabled, status bar shows temp, fan %, model FPS, and STANDSTILL indicator
|
||||
- **Speed limit**: processed by `selfdrive/clearpilot/speed_logic.py` (SpeedState class), converts m/s to display units, writes to memory params. Cruise warning signs appear when cruise set speed exceeds speed limit by threshold (10 mph if limit >= 50, 7 mph if < 50) or is 5+ mph under. Ding sound plays when warning sign appears or speed limit changes while visible (30s cooldown)
|
||||
|
||||
### Key Dependencies
|
||||
|
||||
- **Python 3.11** (via pyenv) with: numpy, casadi, onnx/onnxruntime, pycapnp, pyzmq, sentry-sdk, sympy, Cython
|
||||
- **capnp (Cap'n Proto)** — IPC message serialization between all processes
|
||||
- **ZeroMQ** — IPC transport layer
|
||||
- **Qt 5** — UI framework (with WebEngine available but not used for rotation reasons)
|
||||
- **OpenMAX (OMX)** — hardware video encoding
|
||||
- **libavformat** — MP4 container muxing
|
||||
- **libyuv** — color space conversion
|
||||
- **SCons** — build system for native C++ components
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
# Dashcam Project
|
||||
|
||||
## Status
|
||||
|
||||
### Completed (2026-04-11)
|
||||
|
||||
- Disabled comma training data video encoding (`encoderd`) — CAN/sensor logs still recorded
|
||||
- Re-enabled FrogPilot OMX screen recorder (H.264 MP4, 1440x720, 2Mbps, hardware encoded)
|
||||
- Auto-start/stop recording tied to car ignition (`scene.started`)
|
||||
- `ScreenRecorderDebug` param for bench testing without car connected
|
||||
- Hidden all recorder UI elements — invisible to driver
|
||||
- Videos saved to `/data/media/0/videos/YYYYMMDD-HHMMSS.mp4`, 3-minute segments
|
||||
- Deleter updated: 9 GB free space threshold, rotates oldest videos first
|
||||
- Cleaned up offroad UI: replaced stock home screen with grid launcher (Settings + Dashcam buttons)
|
||||
|
||||
### Next: Dashcam Footage Viewer
|
||||
|
||||
Build a native Qt widget accessible from the offroad home screen "Dashcam" button.
|
||||
|
||||
**Requirements:**
|
||||
- List MP4 files from `/data/media/0/videos/` sorted newest first
|
||||
- Tap a file to play it back using Qt Multimedia (`QMediaPlayer` + `QVideoWidget`)
|
||||
- Only accessible when offroad (car in park or off)
|
||||
- Back button to return to offroad home
|
||||
- No rotation hacks needed — native Qt widget in existing UI tree handles rotation correctly
|
||||
|
||||
**Architecture:**
|
||||
- New widget class (e.g. `DashcamViewer`) added to `selfdrive/ui/qt/`
|
||||
- Wire into `HomeWindow`'s `QStackedLayout` alongside `home`, `onroad`, `ready`, `driver_view`
|
||||
- Dashcam button in `OffroadHome` switches `slayout` to the viewer
|
||||
- Viewer has a file list view and a playback view (sub-stacked layout)
|
||||
- Back button returns to `OffroadHome`
|
||||
- Build: add to `qt_src` list in `selfdrive/ui/SConscript`, link `Qt5Multimedia`
|
||||
|
||||
**Previous webview attempts (abandoned):**
|
||||
- Brian tried QWebEngineView for browser-based playback but the WebEngine subprocess renders independently of Qt's widget tree
|
||||
- Screen rotation (`view.rotate(90)`, Wayland `wl_surface_set_buffer_transform`) did not work for WebEngine content
|
||||
- Native Qt widget approach avoids this problem entirely
|
||||
|
||||
## Key Files
|
||||
|
||||
| File | Role |
|
||||
|------|------|
|
||||
| `selfdrive/frogpilot/screenrecorder/screenrecorder.cc` | Recording logic, auto-start/stop |
|
||||
| `selfdrive/frogpilot/screenrecorder/omx_encoder.cc` | OMX H.264 hardware encoder |
|
||||
| `selfdrive/ui/qt/onroad.cc` | Timer driving frame capture |
|
||||
| `selfdrive/ui/qt/home.cc` | Offroad home with Dashcam button |
|
||||
| `system/loggerd/deleter.py` | Storage rotation |
|
||||
| `/data/media/0/videos/` | Video output directory |
|
||||
@@ -0,0 +1,46 @@
|
||||
# ClearPilot — Feature Goals
|
||||
|
||||
Each goal below will be discussed in detail before implementation. Background and specifics to be provided by Brian when we get to each item.
|
||||
|
||||
## Dashcam
|
||||
|
||||
- [ ] **Dashcam viewer** — on-screen Qt widget for browsing and playing back recorded footage from the offroad home screen (Dashcam button already placed). See `DASHCAM_PROJECT.md` for architecture notes.
|
||||
- [ ] **Dashcam uploader** — upload footage to a remote server or cloud storage
|
||||
- [ ] **GPS + speed overlay on dashcam footage** — embed GPS coordinates and vehicle speed into the video stream or as metadata
|
||||
- [ ] **Dashcam footage archive** — option to mark a trip's footage as archived (kept in storage but hidden from the main UI)
|
||||
- [ ] **Suspend dashcam / route recording** — on-screen setting to pause recording for a trip and delete footage of the current trip so far (privacy mode)
|
||||
|
||||
## Driving Alerts & Safety
|
||||
|
||||
- [ ] **Chirp on speed change and over speed limit** — audible alert when speed limit changes or vehicle exceeds the limit
|
||||
- [ ] **Warn on traffic standstill ahead** — alert when approaching cars ahead too quickly (closing distance warning)
|
||||
- [ ] **On-screen current speed limit** — display the speed limit as reported by the car's CAN data
|
||||
- [ ] **Fix on-screen speed value for MPH** — correct the speed display to use the proper value for MPH conversion
|
||||
|
||||
## Cruise Control & Driving State
|
||||
|
||||
- [ ] **Auto-set speed via CAN bus** — simulate pressing speed up/down buttons on CAN bus to automatically set cruise to the correct speed via UI interaction (stretch goal)
|
||||
- [ ] **Fix engaged-while-braking state sync** — if openpilot is engaged while brakes are pressed, the system thinks it's active but the car isn't actually in cruise control, causing a sync error that should be recoverable
|
||||
- [ ] **Fix resume-cruise lateral mode bug** — pressing resume cruise control (rather than enable) causes the car to enter a state where it thinks it's in always-on lateral mode and disables driver monitoring
|
||||
- [ ] **Lateral assist in turn lanes** — keep lateral assist active when using turn signal in a turn lane (at low/turning speeds) as opposed to cruising speeds, where lateral currently disengages
|
||||
|
||||
## Driver Monitoring
|
||||
|
||||
- [ ] **Disable driver monitoring on demand** — add a technique to disable DM for 2 minutes via a button or gesture
|
||||
- [ ] **Hands-on-wheel mode** — add a setting requiring the driver to keep hands on the wheel (configurable via settings)
|
||||
|
||||
## UI Modes & Display
|
||||
|
||||
- [ ] **Curves-only UI mode** — show only on-screen curve/path depictions without the camera feed (reduces visual clutter, saves processing)
|
||||
- [ ] **Night mode auto-display-off** — turn off display if device starts at night (determine sunset for current location/datetime or use ambient light levels from car sensors)
|
||||
- [ ] **Update boot/offroad splash logos** — replace the red pac-man ghost with a blue pac-man ghost for boot and offroad full-screen splash
|
||||
- [ ] **Dashcam-only reboot mode** — on-screen option to reboot into a mode that provides no driver assist (reverts to stock OEM lane assist) but keeps dashcam running. For lending the car to friends who shouldn't use comma's driving features.
|
||||
|
||||
## Connectivity & Data
|
||||
|
||||
- [ ] **Offroad weather report screen** — weather display accessible from the offroad home grid (depends on internet connection)
|
||||
- [ ] **Fix GPS tracking** — GPS tracking feature currently broken, processes commented out in process_config
|
||||
|
||||
## Vehicle-Specific (Hyundai Tucson HDA2)
|
||||
|
||||
- [ ] **Warn if panoramic roof is open** — if possible to intercept window/roof state from CAN bus, warn when the top roof is left open when the car is turned off
|
||||
@@ -121,6 +121,7 @@ def objcopy(source, target, env, for_signature):
|
||||
return '$OBJCOPY -O binary %s %s' % (source[0], target[0])
|
||||
|
||||
# Common autogenerated includes
|
||||
os.makedirs("obj", exist_ok=True)
|
||||
git_ver = get_version(BUILDER, BUILD_TYPE)
|
||||
with open("obj/gitversion.h", "w") as f:
|
||||
f.write(f'const uint8_t gitversion[8] = "{git_ver}";\n')
|
||||
|
||||
@@ -10,11 +10,8 @@ BASEDIR="/data/openpilot"
|
||||
# Fix ownership — we edit as root, openpilot builds/runs as comma
|
||||
sudo chown -R comma:comma "$BASEDIR"
|
||||
|
||||
# Kill stale error displays and any running manager/launch/managed processes.
|
||||
# `text` is a shell wrapper that execs `./_text` — after exec the process is named
|
||||
# `_text` (no path), so we kill by exact comm in addition to the path pattern.
|
||||
# Kill stale error displays and any running manager/launch/managed processes
|
||||
pkill -9 -f "selfdrive/ui/text" 2>/dev/null
|
||||
pkill -9 -x _text 2>/dev/null
|
||||
pkill -9 -f 'launch_openpilot.sh' 2>/dev/null
|
||||
pkill -9 -f 'launch_chffrplus.sh' 2>/dev/null
|
||||
pkill -9 -f 'python.*manager.py' 2>/dev/null
|
||||
@@ -38,6 +35,4 @@ source "$BASEDIR/build_preflight.sh"
|
||||
cd "$BASEDIR/selfdrive/manager"
|
||||
rm -f "$BASEDIR/prebuilt"
|
||||
|
||||
# Tee output to /tmp/build.log for inspection after the script exits
|
||||
BUILD_ONLY=1 ./build.py 2>&1 | tee /tmp/build.log
|
||||
exit "${PIPESTATUS[0]}"
|
||||
BUILD_ONLY=1 exec ./build.py
|
||||
|
||||
@@ -12,6 +12,9 @@ struct FrogPilotCarControl @0x81c2f05a394cf4af {
|
||||
alwaysOnLateral @0 :Bool;
|
||||
speedLimitChanged @1 :Bool;
|
||||
trafficModeActive @2 :Bool;
|
||||
# CLEARPILOT: migrated from paramsMemory to avoid file-read syscalls in modeld/UI/carcontroller
|
||||
latRequested @3 :Bool; # controlsd's request to enable lat before ramp-up delay
|
||||
noLatLaneChange @4 :Bool; # lat is temporarily suppressed during a lane change
|
||||
}
|
||||
|
||||
struct FrogPilotCarState @0xaedffd8f31e7b55d {
|
||||
|
||||
@@ -30,7 +30,7 @@ services: dict[str, tuple] = {
|
||||
"temperatureSensor": (True, 2., 200),
|
||||
"temperatureSensor2": (True, 2., 200),
|
||||
"gpsNMEA": (True, 9.),
|
||||
"deviceState": (True, 2., 1),
|
||||
"deviceState": (True, 5., 1), # CLEARPILOT: 5Hz — thermald decimates pandaStates (10Hz) every 2 ticks
|
||||
"can": (True, 100., 1223), # decimation gives ~5 msgs in a full segment
|
||||
"controlsState": (True, 100., 10),
|
||||
"pandaStates": (True, 10., 1),
|
||||
@@ -50,7 +50,7 @@ services: dict[str, tuple] = {
|
||||
"longitudinalPlan": (True, 20., 5),
|
||||
"procLog": (True, 0.5, 15),
|
||||
"gpsLocationExternal": (True, 10., 10),
|
||||
"gpsLocation": (True, 1., 1),
|
||||
"gpsLocation": (True, 2., 1), # CLEARPILOT: 2Hz (was 1Hz) — gpsd polls at 2Hz
|
||||
"ubloxGnss": (True, 10.),
|
||||
"qcomGnss": (True, 2.),
|
||||
"gnssMeasurements": (True, 10., 10),
|
||||
@@ -70,7 +70,7 @@ services: dict[str, tuple] = {
|
||||
"wideRoadEncodeIdx": (False, 20., 1),
|
||||
"wideRoadCameraState": (True, 20., 20),
|
||||
"modelV2": (True, 20., 40),
|
||||
"managerState": (True, 2., 1),
|
||||
"managerState": (True, 5., 1), # CLEARPILOT: 5Hz — gated by deviceState arrival (see thermald)
|
||||
"uploaderState": (True, 0., 1),
|
||||
"navInstruction": (True, 1., 10),
|
||||
"navRoute": (True, 0.),
|
||||
|
||||
@@ -109,6 +109,9 @@ std::unordered_map<std::string, uint32_t> keys = {
|
||||
{"ControlsReady", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
|
||||
{"CurrentBootlog", PERSISTENT},
|
||||
{"CurrentRoute", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
|
||||
{"DashcamFrames", PERSISTENT},
|
||||
{"DashcamState", PERSISTENT},
|
||||
{"DashcamShutdown", CLEAR_ON_MANAGER_START},
|
||||
{"DisableLogging", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
|
||||
{"DisablePowerDown", PERSISTENT},
|
||||
{"DisableUpdates", PERSISTENT},
|
||||
@@ -158,6 +161,7 @@ std::unordered_map<std::string, uint32_t> keys = {
|
||||
{"LastUpdateTime", PERSISTENT},
|
||||
{"LiveParameters", PERSISTENT},
|
||||
{"LiveTorqueParameters", PERSISTENT | DONT_LOG},
|
||||
{"LogDirInitialized", CLEAR_ON_MANAGER_START},
|
||||
{"LongitudinalPersonality", PERSISTENT},
|
||||
{"NavDestination", CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION},
|
||||
{"NavDestinationWaypoints", CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION},
|
||||
@@ -189,12 +193,18 @@ std::unordered_map<std::string, uint32_t> keys = {
|
||||
{"RecordFrontLock", PERSISTENT}, // for the internal fleet
|
||||
{"ReplayControlsState", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
|
||||
{"RouteCount", PERSISTENT},
|
||||
{"ShutdownTouchReset", PERSISTENT},
|
||||
{"SnoozeUpdate", CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION},
|
||||
{"SshEnabled", PERSISTENT},
|
||||
{"TermsVersion", PERSISTENT},
|
||||
{"TelemetryEnabled", PERSISTENT},
|
||||
{"Timezone", PERSISTENT},
|
||||
{"TrainingVersion", PERSISTENT},
|
||||
{"ModelFps", PERSISTENT},
|
||||
{"ModelStandby", PERSISTENT},
|
||||
{"ModelStandbyTs", PERSISTENT},
|
||||
{"UbloxAvailable", PERSISTENT},
|
||||
{"VpnEnabled", PERSISTENT},
|
||||
{"UpdateAvailable", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
|
||||
{"UpdateFailedCount", CLEAR_ON_MANAGER_START},
|
||||
{"UpdaterAvailableBranches", PERSISTENT},
|
||||
@@ -210,6 +220,13 @@ std::unordered_map<std::string, uint32_t> keys = {
|
||||
|
||||
// FrogPilot parameters
|
||||
{"AccelerationPath", PERSISTENT},
|
||||
{"BenchCruiseActive", CLEAR_ON_MANAGER_START},
|
||||
{"BenchCruiseSpeed", CLEAR_ON_MANAGER_START},
|
||||
{"ClpUiState", CLEAR_ON_MANAGER_START},
|
||||
{"BenchEngaged", CLEAR_ON_MANAGER_START},
|
||||
{"BenchGear", CLEAR_ON_MANAGER_START},
|
||||
{"BenchSpeed", CLEAR_ON_MANAGER_START},
|
||||
{"BenchSpeedLimit", CLEAR_ON_MANAGER_START},
|
||||
{"AccelerationProfile", PERSISTENT},
|
||||
{"AdjacentPath", PERSISTENT},
|
||||
{"AdjacentPathMetrics", PERSISTENT},
|
||||
@@ -232,41 +249,27 @@ std::unordered_map<std::string, uint32_t> keys = {
|
||||
{"CarMake", PERSISTENT},
|
||||
{"CarModel", PERSISTENT},
|
||||
|
||||
{"CarCruiseDisplayActual", PERSISTENT},
|
||||
{"CarSpeedLimit", PERSISTENT},
|
||||
|
||||
{"CarSpeedLimitWarning", PERSISTENT},
|
||||
{"CarSpeedLimitLiteral", PERSISTENT},
|
||||
|
||||
{"CarIsMetric", PERSISTENT},
|
||||
|
||||
{"ClearpilotSpeedDisplay", PERSISTENT},
|
||||
{"ClearpilotSpeedLimitDisplay", PERSISTENT},
|
||||
{"ClearpilotHasSpeed", PERSISTENT},
|
||||
{"ClearpilotIsMetric", PERSISTENT},
|
||||
{"ClearpilotSpeedUnit", PERSISTENT},
|
||||
{"ClearpilotCruiseWarning", PERSISTENT},
|
||||
{"ClearpilotCruiseWarningSpeed", PERSISTENT},
|
||||
{"ClearpilotPlayDing", PERSISTENT},
|
||||
|
||||
// {"SpeedLimitLatDesired", PERSISTENT},
|
||||
// {"SpeedLimitVTSC", PERSISTENT},
|
||||
|
||||
{"CPTLkasButtonAction", PERSISTENT},
|
||||
{"ScreenDisplayMode", PERSISTENT},
|
||||
|
||||
// CLEARPILOT memory params (live at /dev/shm/params; tmpfs is volatile so
|
||||
// these reset on reboot regardless of registration flag).
|
||||
{"CarIsMetric", PERSISTENT},
|
||||
{"ClearpilotCruiseWarning", PERSISTENT},
|
||||
{"ClearpilotCruiseWarningSpeed", PERSISTENT},
|
||||
{"ClearpilotHasSpeed", PERSISTENT},
|
||||
{"ClearpilotIsMetric", PERSISTENT},
|
||||
{"ClearpilotPlayDing", PERSISTENT},
|
||||
{"ClearpilotShowHealthMetrics", PERSISTENT},
|
||||
{"ClearpilotSpeedDisplay", PERSISTENT},
|
||||
{"ClearpilotSpeedLimitDisplay", PERSISTENT},
|
||||
{"ClearpilotSpeedUnit", PERSISTENT},
|
||||
{"DashcamFrames", PERSISTENT},
|
||||
{"DashcamShutdown", PERSISTENT},
|
||||
{"DashcamState", PERSISTENT},
|
||||
{"IsDaylight", PERSISTENT},
|
||||
{"ModelFps", PERSISTENT},
|
||||
{"ModelStandby", PERSISTENT},
|
||||
{"ModelStandbyTs", PERSISTENT},
|
||||
{"ScreenRecorderDebug", PERSISTENT},
|
||||
{"ShutdownTouchReset", PERSISTENT},
|
||||
{"TelemetryEnabled", PERSISTENT},
|
||||
{"VpnEnabled", PERSISTENT},
|
||||
{"IsDaylight", CLEAR_ON_MANAGER_START},
|
||||
{"ScreenDisplayMode", CLEAR_ON_MANAGER_START},
|
||||
|
||||
{"RadarDist", PERSISTENT},
|
||||
{"ModelDist", PERSISTENT},
|
||||
@@ -287,6 +290,7 @@ std::unordered_map<std::string, uint32_t> keys = {
|
||||
{"CEStopLights", PERSISTENT},
|
||||
{"CEStopLightsLead", PERSISTENT},
|
||||
{"Compass", PERSISTENT},
|
||||
{"ClearpilotShowHealthMetrics", PERSISTENT},
|
||||
{"ConditionalExperimental", PERSISTENT},
|
||||
{"CrosstrekTorque", PERSISTENT},
|
||||
{"CurrentHolidayTheme", PERSISTENT},
|
||||
@@ -440,6 +444,7 @@ std::unordered_map<std::string, uint32_t> keys = {
|
||||
{"ScreenBrightnessOnroad", PERSISTENT},
|
||||
{"ScreenManagement", PERSISTENT},
|
||||
{"ScreenRecorder", PERSISTENT},
|
||||
{"ScreenRecorderDebug", PERSISTENT},
|
||||
{"ScreenTimeout", PERSISTENT},
|
||||
{"ScreenTimeoutOnroad", PERSISTENT},
|
||||
{"SearchInput", PERSISTENT},
|
||||
@@ -503,8 +508,6 @@ std::unordered_map<std::string, uint32_t> keys = {
|
||||
{"WheelIcon", PERSISTENT},
|
||||
{"WheelSpeed", PERSISTENT},
|
||||
|
||||
// Clearpilot
|
||||
{"no_lat_lane_change", PERSISTENT},
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -12,7 +12,7 @@ from openpilot.system.hardware import PC
|
||||
# time step for each process
|
||||
DT_CTRL = 0.01 # controlsd
|
||||
DT_MDL = 0.05 # model
|
||||
DT_TRML = 0.5 # thermald and manager
|
||||
DT_TRML = 0.25 # thermald and manager — 4 Hz
|
||||
DT_DMON = 0.05 # driver monitoring
|
||||
|
||||
|
||||
|
||||
@@ -1,358 +0,0 @@
|
||||
# Park-mode power savings — design notes & retro
|
||||
|
||||
## Goal
|
||||
|
||||
While ignition is on but the car is in **park**, reduce CPU/power draw by
|
||||
shutting down processes that aren't needed for "watching the gear lever."
|
||||
The constraint: the car's steering ECU must keep seeing the openpilot
|
||||
heartbeat (LFA / LKAS / LKAS_ALT CAN-FD messages, every controlsd cycle)
|
||||
or it drops out of tester mode and throws a steering fault on the next
|
||||
shift to drive.
|
||||
|
||||
Behavior should approximate "ignition off" except:
|
||||
- controlsd stays alive (it's the heartbeat source and the gear watcher)
|
||||
- pandad / boardd / camerad / dashcamd / gpsd / ui / thermald keep running
|
||||
- everything else (modeld, locationd, paramsd, torqued, calibrationd,
|
||||
plannerd, radard, dmonitoringmodeld, dmonitoringd, soundd, sensord,
|
||||
speed_logicd, frogpilot_process) gets paused
|
||||
|
||||
When the car shifts back to drive, all the paused processes spin up cold
|
||||
and openpilot resumes normal operation.
|
||||
|
||||
## Status: reverted
|
||||
|
||||
We attempted this on **2026-05-04** (commits `5d18ad1` → `1e4e95c`,
|
||||
force-pushed away). It built and launched fine on the bench but produced
|
||||
a cascade of false alerts during the park→drive transition in the car —
|
||||
"controlsd unresponsive", commIssue with longitudinalPlan/frogpilotPlan,
|
||||
locationd temporaryError, sensorDataInvalid. Reverted to tag
|
||||
`stable_5_4_26` (commit `0d1cedd`).
|
||||
|
||||
This document captures the design we tried and the problems we ran into
|
||||
so the next attempt doesn't relearn the same mistakes.
|
||||
|
||||
## Design we tried
|
||||
|
||||
### 1. The flag
|
||||
|
||||
A new memory param `ParkMode` (registered in `common/params.cc`,
|
||||
defaulted to `"0"` in `manager_init`'s memory-params loop). Lives at
|
||||
`/dev/shm/params/d/ParkMode`. Written by controlsd, read by
|
||||
process_config gating callbacks.
|
||||
|
||||
### 2. controlsd writes the flag and runs a minimal cycle in park
|
||||
|
||||
In `__init__`:
|
||||
```python
|
||||
self.park_mode = False
|
||||
self.park_exit_frame = -1
|
||||
self.startup_complete_frame = -1
|
||||
self.PARK_GRACE_MAX_FRAMES = int(15.0 / DT_CTRL) # see "Park-grace cap" below
|
||||
self.PARK_STARTUP_DELAY_FRAMES = int(10.0 / DT_CTRL) # see "Startup delay" below
|
||||
self.last_engaged_frame = -1
|
||||
self.last_engage_attempt_frame = -1
|
||||
self.POST_ENGAGE_LAG_GRACE_FRAMES = int(3.0 / DT_CTRL)
|
||||
```
|
||||
|
||||
In `step()` after `data_sample()`:
|
||||
```python
|
||||
self._update_park_mode(CS)
|
||||
if self.park_mode or self._in_park_exit_grace():
|
||||
self._park_mode_tick(CS)
|
||||
self.CS_prev = CS
|
||||
return
|
||||
# ... normal flow
|
||||
```
|
||||
|
||||
Helpers:
|
||||
```python
|
||||
def _park_mode_allowed(self):
|
||||
# Don't allow park mode until init has completed AND we've run at
|
||||
# least PARK_STARTUP_DELAY_FRAMES of normal step() after init.
|
||||
if not self.initialized:
|
||||
return False
|
||||
if self.startup_complete_frame < 0:
|
||||
self.startup_complete_frame = self.sm.frame
|
||||
return (self.sm.frame - self.startup_complete_frame) >= self.PARK_STARTUP_DELAY_FRAMES
|
||||
|
||||
def _update_park_mode(self, CS):
|
||||
if not self._park_mode_allowed():
|
||||
return
|
||||
in_park = CS.gearShifter == GearShifter.park
|
||||
if in_park != self.park_mode:
|
||||
self.park_mode = in_park
|
||||
self.params_memory.put_bool("ParkMode", in_park)
|
||||
if not in_park:
|
||||
self.park_exit_frame = self.sm.frame
|
||||
|
||||
def _in_park_exit_grace(self):
|
||||
if self.park_exit_frame < 0:
|
||||
return False
|
||||
if (self.sm.frame - self.park_exit_frame) >= self.PARK_GRACE_MAX_FRAMES:
|
||||
return False
|
||||
return not (self.sm.all_checks() and not self.card.can_rcv_timeout)
|
||||
|
||||
def _park_mode_tick(self, CS):
|
||||
# Build a do-nothing CarControl; card.controls_update -> CI.apply ->
|
||||
# CarController.update appends create_steering_messages()
|
||||
# unconditionally, so the LFA/LKAS heartbeat keeps flowing.
|
||||
CC = car.CarControl.new_message()
|
||||
self.clearpilot_state_control(CC, CS)
|
||||
self.card.controls_update(CC, self.frogpilot_variables)
|
||||
```
|
||||
|
||||
Why every piece matters:
|
||||
- **`_park_mode_allowed` (10 s startup delay)**: `card.initialize()` is
|
||||
only called inside `data_sample`'s `if not self.initialized` branch.
|
||||
That call wires `CarInterface` up so `controls_update` actually
|
||||
produces CAN sends. If we entered park-mode before init completed,
|
||||
the heartbeat was a silent no-op. The 10 s buffer also lets all
|
||||
`only_onroad_active` processes complete their cold-spawn the very
|
||||
first time, before manager starts pausing them.
|
||||
- **`_in_park_exit_grace`**: when ParkMode flips off, the cold-spawn
|
||||
chain takes time. Stay in keepalive-tick mode until SubMaster reports
|
||||
everything healthy, with a hard cap as a safety net (we used 15 s,
|
||||
saw it still wasn't enough — see "What broke" below).
|
||||
- **`_park_mode_tick` calls `card.controls_update`**: don't call
|
||||
`state_update` or `data_sample` — `step()` already did that. Just
|
||||
publish the empty CC to push the heartbeat CAN messages.
|
||||
|
||||
### 3. Manager gating
|
||||
|
||||
In `selfdrive/manager/process_config.py`:
|
||||
|
||||
```python
|
||||
def _park_mode() -> bool:
|
||||
return Params("/dev/shm/params").get_bool("ParkMode")
|
||||
|
||||
def only_onroad_active(started, params, CP):
|
||||
return started and not _park_mode()
|
||||
|
||||
def driverview_active(started, params, CP):
|
||||
return driverview(started, params, CP) and not _park_mode()
|
||||
|
||||
def always_run_unless_parked(started, params, CP):
|
||||
# Same as always_run, but pauses while ignition is on and parked.
|
||||
# Preserves offroad behavior.
|
||||
return not (started and _park_mode())
|
||||
```
|
||||
|
||||
Re-gated:
|
||||
- `only_onroad` → `only_onroad_active`: modeld, sensord, soundd,
|
||||
locationd, calibrationd, torqued, paramsd, plannerd, radard,
|
||||
speed_logicd
|
||||
- `driverview` → `driverview_active`: dmonitoringmodeld, dmonitoringd
|
||||
- `always_run` → `always_run_unless_parked`: frogpilot_process
|
||||
|
||||
`controlsd` stays plain `only_onroad` — it's the writer + heartbeat source.
|
||||
`camerad`, `pandad`, `thermald`, `ui`, `dashcamd`, `gpsd`, `deleter`,
|
||||
`fleet_manager`, `tombstoned`, `timed`, manager internals stay
|
||||
`always_run` (or whatever they were).
|
||||
|
||||
### 4. Engage-attempt grace (separate from park-grace)
|
||||
|
||||
The state transition into engaged briefly bumps the controlsd loop time
|
||||
over budget. `self.rk.lagging` flips True for a cycle, `update_events`
|
||||
adds `EventName.controlsdLagging`, which has an `ET.NO_ENTRY` alert
|
||||
("Controls Process Lagging: Reboot Your Device"). That alert fires
|
||||
right as the user is taking their hands off the wheel after pressing
|
||||
SET. Same goes for `commIssue` if any service is briefly stale.
|
||||
|
||||
Solution: track two engagement edges — the actual transition into
|
||||
ENABLED (post-success) and the cruise.enabled rising edge (pre-success,
|
||||
covers blocked attempts):
|
||||
|
||||
```python
|
||||
# In __init__:
|
||||
self.last_engaged_frame = -1
|
||||
self.last_engage_attempt_frame = -1
|
||||
|
||||
# In update_events, BEFORE the lag/comm checks:
|
||||
if CS.cruiseState.enabled and not self.CS_prev.cruiseState.enabled:
|
||||
self.last_engage_attempt_frame = self.sm.frame
|
||||
|
||||
# At the engaged-state transition (state_transition):
|
||||
enabled_prev = self.enabled
|
||||
self.enabled = self.state in ENABLED_STATES
|
||||
if self.enabled and not enabled_prev:
|
||||
self.last_engaged_frame = self.sm.frame
|
||||
|
||||
# Gate logic in update_events:
|
||||
in_engage_grace = (
|
||||
(self.last_engaged_frame >= 0
|
||||
and (self.sm.frame - self.last_engaged_frame) < self.POST_ENGAGE_LAG_GRACE_FRAMES)
|
||||
or
|
||||
(self.last_engage_attempt_frame >= 0
|
||||
and (self.sm.frame - self.last_engage_attempt_frame) < self.POST_ENGAGE_LAG_GRACE_FRAMES)
|
||||
)
|
||||
if not REPLAY and self.rk.lagging and not in_engage_grace:
|
||||
self.events.add(EventName.controlsdLagging)
|
||||
# ... and similarly suppress commIssue when in_engage_grace
|
||||
```
|
||||
|
||||
### 5. Fan range rules (port from broken tree)
|
||||
|
||||
Independent of park-mode plumbing. Widen the fan PID range when the car
|
||||
is parked so the device can fully cool while the user can't hear road
|
||||
noise.
|
||||
|
||||
`selfdrive/thermald/fan_controller.py`:
|
||||
```python
|
||||
def update(self, cur_temp, ignition, standstill=False, is_parked=True, cruise_engaged=False):
|
||||
# neg_limit = -max_fan_pct, pos_limit = -min_fan_pct
|
||||
if not ignition:
|
||||
self.controller.neg_limit = -30
|
||||
self.controller.pos_limit = 0
|
||||
elif is_parked:
|
||||
self.controller.neg_limit = -100
|
||||
self.controller.pos_limit = 0 # 0-100% (full range)
|
||||
elif cruise_engaged:
|
||||
self.controller.neg_limit = -100
|
||||
self.controller.pos_limit = -30 # 30-100%
|
||||
elif standstill:
|
||||
self.controller.neg_limit = -100
|
||||
self.controller.pos_limit = -10 # 10-100%
|
||||
else:
|
||||
self.controller.neg_limit = -100
|
||||
self.controller.pos_limit = -30 # 30-100%
|
||||
```
|
||||
|
||||
`selfdrive/thermald/thermald.py`:
|
||||
```python
|
||||
# Add carState to the SubMaster service list
|
||||
sm = messaging.SubMaster([..., "carState"], poll="pandaStates")
|
||||
|
||||
# At the fan_controller.update call site:
|
||||
if sm.seen['carState']:
|
||||
cs = sm['carState']
|
||||
standstill = cs.standstill
|
||||
is_parked = cs.gearShifter == car.CarState.GearShifter.park
|
||||
else:
|
||||
standstill = False
|
||||
is_parked = True # default safe: assume parked, no fan floor
|
||||
cruise_engaged = sm['controlsState'].enabled if sm.seen['controlsState'] else False
|
||||
msg.deviceState.fanSpeedPercentDesired = fan_controller.update(
|
||||
all_comp_temp, onroad_conditions["ignition"], standstill,
|
||||
is_parked=is_parked, cruise_engaged=cruise_engaged)
|
||||
```
|
||||
|
||||
This piece worked fine standalone — it's safe to land on its own without
|
||||
the rest of the park-mode plumbing. (It does require the thermald
|
||||
carState subscription to actually receive carState — which is fine in
|
||||
normal operation.)
|
||||
|
||||
## What broke
|
||||
|
||||
### A. card.initialize() not called → silent CAN
|
||||
|
||||
First attempt entered park-mode immediately whenever `gearShifter ==
|
||||
PARK`, including before controlsd had ever passed through its
|
||||
`if not self.initialized` branch. Symptoms:
|
||||
- UI stuck on splash even after shift to drive
|
||||
- carState never published to cereal (tested by subscribing externally)
|
||||
- presumably the steering ECU was getting no heartbeat, though we
|
||||
hadn't yet tried to engage
|
||||
|
||||
Fix: `_park_mode_allowed` gate, requires `self.initialized` AND a 10 s
|
||||
post-init buffer. **Critical.** Don't skip this in v2.
|
||||
|
||||
### B. Park-grace cap (8 s) too short
|
||||
|
||||
Cold-spawn chain length:
|
||||
1. modeld load thneed weights, init GPU (~3-5 s)
|
||||
2. modeld publishes modelV2
|
||||
3. plannerd consumes modelV2, publishes longitudinalPlan
|
||||
4. frogpilot_process consumes modelV2, publishes frogpilotPlan
|
||||
5. paramsd, torqued, calibrationd estimators accumulate enough samples
|
||||
to set valid=True
|
||||
|
||||
In testing this cumulatively exceeded 8 s. We bumped the cap to 15 s.
|
||||
**Even 15 s wasn't enough in some test runs** — got commIssue alerts
|
||||
with longitudinalPlan / frogpilotPlan still invalid past the cap.
|
||||
|
||||
Possible fixes for v2:
|
||||
- Even longer cap (30 s)
|
||||
- Or condition-only with no cap (live with the risk that a genuinely
|
||||
broken service strands controlsd in keepalive forever)
|
||||
- Or **a different approach**: don't actually pause modeld; throttle it
|
||||
to 1 fps when stopped (this is what the broken tree's CLAUDE.md
|
||||
pending-features list described as "modeld throttled to 1fps when
|
||||
stopped"). Avoids the cold-spawn entirely.
|
||||
|
||||
### C. NO_ENTRY alerts fire on engage attempt, not engage success
|
||||
|
||||
`controlsdLagging` and `commIssue` both have `ET.NO_ENTRY`. NO_ENTRY
|
||||
alerts fire when the user *tries* to engage but the event blocks them.
|
||||
At that moment `self.enabled` hasn't flipped yet, so a grace keyed on
|
||||
`last_engaged_frame` is inert.
|
||||
|
||||
Fix: also track `CS.cruiseState.enabled` rising edge as
|
||||
`last_engage_attempt_frame`. See "Engage-attempt grace" above.
|
||||
|
||||
### D. Cascade of related alerts
|
||||
|
||||
After all the above were patched, in-car testing still produced
|
||||
"locationd temporaryError" and "sensorDataInvalid" alerts after the
|
||||
post-engage grace window expired. We didn't have time to chase these
|
||||
down before reverting. Hypotheses:
|
||||
- locationd's kalman filter needs more than ~15 s to converge after
|
||||
cold spawn, especially without GPS feeding it (we explicitly skip
|
||||
GPS in locationd).
|
||||
- sensord might lose its IMU sample lock and need re-init when
|
||||
killed/restarted.
|
||||
|
||||
These are an argument for the "throttle, don't kill" approach in v2.
|
||||
|
||||
### E. Sensor data invalid
|
||||
|
||||
`sensorDataInvalid` likely stems from sensord being killed and
|
||||
restarted. The IMU init handshake takes time, and during that window
|
||||
`accelerometer` / `gyroscope` services are alive but their data hasn't
|
||||
stabilized — locationd reports invalid, controlsd alerts.
|
||||
|
||||
Same fix family as (D).
|
||||
|
||||
## Recommended approach for v2
|
||||
|
||||
The "kill processes" approach is architecturally clean but creates a
|
||||
big cold-spawn cliff every time the user shifts to drive. The v2 plan
|
||||
should probably look more like:
|
||||
|
||||
1. **Throttle, don't kill.** Send a "standby" memory param that modeld
|
||||
reads and reduces its tick rate to 1 fps. dmonitoringmodeld likewise.
|
||||
Plannerd/radard naturally adapt to slower modelV2 input. paramsd /
|
||||
torqued / calibrationd / locationd keep accumulating slowly.
|
||||
2. **Keep controlsd's main loop running normally** — no park_mode_tick.
|
||||
The state machine already handles "not engaged" → no actuators →
|
||||
passive. The heartbeat flows naturally because `state_control` /
|
||||
`publish_logs` run every cycle.
|
||||
3. **Apply the fan-range widening (Section 5 of "Design")** — that
|
||||
piece is independently valuable and doesn't depend on the rest.
|
||||
4. **Skip dashcamd touchpoints.** dashcamd is already gear-aware via
|
||||
its own state machine and pauses recording in park naturally.
|
||||
5. **Keep the `_park_mode_allowed` startup-delay gate concept** if any
|
||||
form of conditional-shutdown is reintroduced — this guards against
|
||||
`card.initialize()` being skipped.
|
||||
|
||||
### Files involved (for reference)
|
||||
|
||||
| File | Why |
|
||||
|---|---|
|
||||
| `common/params.cc` | register `ParkMode` (or `ModelStandby` etc.) |
|
||||
| `selfdrive/manager/manager.py` | `manager_init` memory-param defaults |
|
||||
| `selfdrive/manager/process_config.py` | gating helpers |
|
||||
| `selfdrive/controls/controlsd.py` | the park branch and engage-grace |
|
||||
| `selfdrive/thermald/fan_controller.py` | fan range rules |
|
||||
| `selfdrive/thermald/thermald.py` | carState sub for fan rules |
|
||||
| `selfdrive/modeld/modeld.py` (v2) | throttle on standby flag |
|
||||
| `selfdrive/modeld/dmonitoringmodeld.py` (v2) | throttle on standby flag |
|
||||
|
||||
### Reference commits (force-pushed away from origin/clearpilot, but the
|
||||
text of this document captures the substance)
|
||||
|
||||
- park-mode initial: bring up flag + gating + controlsd keepalive
|
||||
- park-mode startup gate: 10 s post-init delay before allowing park
|
||||
- engage-grace: 3 s suppression on engage edge for controlsdLagging
|
||||
- engage-attempt grace + grace cap bump to 15 s
|
||||
|
||||
These were all on top of `stable_5_4_26` (`0d1cedd`).
|
||||
@@ -79,11 +79,8 @@ function launch {
|
||||
agnos_init
|
||||
fi
|
||||
|
||||
# CLEARPILOT: kill stale error display from previous build/run.
|
||||
# `text` is a wrapper that execs ./_text — running process is named _text
|
||||
# with no path, so kill by exact comm too.
|
||||
# CLEARPILOT: kill stale error display from previous build/run
|
||||
pkill -f "selfdrive/ui/text" 2>/dev/null
|
||||
pkill -x _text 2>/dev/null
|
||||
|
||||
# write tmux scrollback to a file
|
||||
tmux capture-pane -pq -S-1000 > /tmp/launch_log
|
||||
|
||||
@@ -13,17 +13,8 @@ pkill -9 -f 'selfdrive\.' 2>/dev/null
|
||||
pkill -9 -f 'system\.' 2>/dev/null
|
||||
pkill -9 -f './ui' 2>/dev/null
|
||||
pkill -9 -f 'selfdrive/ui/text' 2>/dev/null
|
||||
# `text` is a shell wrapper that execs `./_text` — after exec the running
|
||||
# process's argv has no path, so kill by exact comm too.
|
||||
pkill -9 -x _text 2>/dev/null
|
||||
sleep 1
|
||||
|
||||
# CLEARPILOT: ensure params persistence dir is owned by comma:comma. Editing
|
||||
# the tree as root leaves files owned by root in /data/params/d_tmp/, and
|
||||
# Params writes done as comma will then EACCES on rename. Reset on every
|
||||
# launch so this never silently breaks again.
|
||||
sudo chown -R comma:comma /data/params
|
||||
|
||||
bash /data/openpilot/system/clearpilot/on_start.sh
|
||||
|
||||
# CLEARPILOT: start VPN monitor (kills previous instances, runs as root)
|
||||
|
||||
@@ -1,17 +1,7 @@
|
||||
-----
|
||||
|
||||
- fix debug notice message
|
||||
- screen mode 1 = no UI, screen mode 2 = off
|
||||
- screen off on lkas
|
||||
- speed limit change chirp on cruise
|
||||
- fix always on detection, change message
|
||||
|
||||
-----
|
||||
|
||||
- Better debugger and data recorder
|
||||
- Represent distance to lead car on primary UI
|
||||
- Start testing for alert when stopped traffic and approaching > 31 mph
|
||||
- or not enough time to slow at 31
|
||||
- special experimental mode override?
|
||||
|
||||
-----
|
||||
|
||||
@@ -21,7 +11,6 @@
|
||||
- better turning logic
|
||||
- new settings UI
|
||||
- experimental mode
|
||||
- resume from stop
|
||||
|
||||
------------
|
||||
|
||||
|
||||
@@ -166,6 +166,7 @@ Export('base_project_f4', 'base_project_h7', 'build_project')
|
||||
|
||||
|
||||
# Common autogenerated includes
|
||||
os.makedirs("board/obj", exist_ok=True)
|
||||
with open("board/obj/gitversion.h", "w") as f:
|
||||
f.write(f'const uint8_t gitversion[] = "{get_version(BUILDER, BUILD_TYPE)}";\n')
|
||||
|
||||
|
||||
@@ -32,7 +32,9 @@ def main():
|
||||
continue
|
||||
|
||||
cloudlog.info("starting athena daemon")
|
||||
proc = Process(name='athenad', target=launcher, args=('selfdrive.athena.athenad', 'athenad'))
|
||||
from openpilot.selfdrive.manager.process import _log_dir
|
||||
log_path = _log_dir + "/athenad.log"
|
||||
proc = Process(name='athenad', target=launcher, args=('selfdrive.athena.athenad', 'athenad', log_path))
|
||||
proc.start()
|
||||
proc.join()
|
||||
cloudlog.event("athenad exited", exitcode=proc.exitcode)
|
||||
|
||||
@@ -8,7 +8,6 @@ from openpilot.selfdrive.car.hyundai import hyundaicanfd, hyundaican
|
||||
from openpilot.selfdrive.car.hyundai.hyundaicanfd import CanBus
|
||||
from openpilot.selfdrive.car.hyundai.values import HyundaiFlags, Buttons, CarControllerParams, CANFD_CAR, CAR
|
||||
from openpilot.selfdrive.car.interfaces import CarControllerBase
|
||||
from openpilot.common.params import Params
|
||||
VisualAlert = car.CarControl.HUDControl.VisualAlert
|
||||
LongCtrlState = car.CarControl.Actuators.LongControlState
|
||||
|
||||
@@ -57,6 +56,7 @@ class CarController(CarControllerBase):
|
||||
self.car_fingerprint = CP.carFingerprint
|
||||
self.last_button_frame = 0
|
||||
|
||||
|
||||
def update(self, CC, CS, now_nanos, frogpilot_variables):
|
||||
actuators = CC.actuators
|
||||
hud_control = CC.hudControl
|
||||
@@ -112,7 +112,9 @@ class CarController(CarControllerBase):
|
||||
hda2_long = hda2 and self.CP.openpilotLongitudinalControl
|
||||
|
||||
# steering control
|
||||
can_sends.extend(hyundaicanfd.create_steering_messages(self.packer, self.CP, self.CAN, CC.enabled, apply_steer_req, apply_steer))
|
||||
# CLEARPILOT: no_lat_lane_change comes via frogpilot_variables (set by controlsd in-process)
|
||||
no_lat_lane_change = int(getattr(frogpilot_variables, 'no_lat_lane_change', False))
|
||||
can_sends.extend(hyundaicanfd.create_steering_messages(self.packer, self.CP, self.CAN, CC.enabled, apply_steer_req, apply_steer, no_lat_lane_change))
|
||||
|
||||
# prevent LFA from activating on HDA2 by sending "no lane lines detected" to ADAS ECU
|
||||
if self.frame % 5 == 0 and hda2:
|
||||
|
||||
@@ -13,6 +13,7 @@ from openpilot.selfdrive.car.hyundai.hyundaicanfd import CanBus
|
||||
from openpilot.selfdrive.car.hyundai.values import HyundaiFlags, CAR, DBC, CAN_GEARS, CAMERA_SCC_CAR, \
|
||||
CANFD_CAR, Buttons, CarControllerParams
|
||||
from openpilot.selfdrive.car.interfaces import CarStateBase
|
||||
from openpilot.selfdrive.clearpilot.telemetry import tlog
|
||||
|
||||
PREV_BUTTON_SAMPLES = 8
|
||||
CLUSTER_SAMPLE_RATE = 20 # frames
|
||||
@@ -47,6 +48,10 @@ class CarState(CarStateBase):
|
||||
self.is_metric = False
|
||||
self.buttons_counter = 0
|
||||
|
||||
# CLEARPILOT: cache to avoid per-cycle atomic writes to /dev/shm (eats CPU via fsync/flock)
|
||||
self._prev_car_speed_limit = None
|
||||
self._prev_car_is_metric = None
|
||||
|
||||
self.cruise_info = {}
|
||||
|
||||
# On some cars, CLU15->CF_Clu_VehicleSpeed can oscillate faster than the dash updates. Sample at 5 Hz
|
||||
@@ -209,10 +214,15 @@ class CarState(CarStateBase):
|
||||
self.lkas_previously_enabled = self.lkas_enabled
|
||||
self.lkas_enabled = cp.vl["BCM_PO_11"]["LFA_Pressed"]
|
||||
|
||||
# self.params_memory.put_int("CarSpeedLimitLiteral", self.calculate_speed_limit(cp, cp_cam))
|
||||
self.params_memory.put_float("CarSpeedLimit", self.calculate_speed_limit(cp, cp_cam) * speed_conv)
|
||||
self.params_memory.put_float("CarCruiseDisplayActual", cp_cruise.vl["SCC11"]["VSetDis"])
|
||||
|
||||
# CLEARPILOT: gate on change — see same fix in update_canfd
|
||||
car_speed_limit = self.calculate_speed_limit(cp, cp_cam) * speed_conv
|
||||
if car_speed_limit != self._prev_car_speed_limit:
|
||||
self.params_memory.put_float("CarSpeedLimit", car_speed_limit)
|
||||
self._prev_car_speed_limit = car_speed_limit
|
||||
if self.is_metric != self._prev_car_is_metric:
|
||||
self.params_memory.put("CarIsMetric", "1" if self.is_metric else "0")
|
||||
self._prev_car_is_metric = self.is_metric
|
||||
|
||||
|
||||
return ret
|
||||
|
||||
@@ -415,10 +425,23 @@ class CarState(CarStateBase):
|
||||
# nonAdaptive = false,
|
||||
# speedCluster = 0 )
|
||||
|
||||
# print("Set limit")
|
||||
# print(self.calculate_speed_limit(cp, cp_cam))
|
||||
# self.params_memory.put_float("CarSpeedLimitLiteral", self.calculate_speed_limit(cp, cp_cam))
|
||||
self.params_memory.put_float("CarSpeedLimit", self.calculate_speed_limit(cp, cp_cam) * speed_factor)
|
||||
# CLEARPILOT: gate on change — these writes run 100Hz, each is an atomic fsync/flock transaction
|
||||
car_speed_limit = self.calculate_speed_limit(cp, cp_cam) * speed_factor
|
||||
if car_speed_limit != self._prev_car_speed_limit:
|
||||
self.params_memory.put_float("CarSpeedLimit", car_speed_limit)
|
||||
self._prev_car_speed_limit = car_speed_limit
|
||||
if self.is_metric != self._prev_car_is_metric:
|
||||
self.params_memory.put("CarIsMetric", "1" if self.is_metric else "0")
|
||||
self._prev_car_is_metric = self.is_metric
|
||||
|
||||
# CLEARPILOT: telemetry logging — disabled, re-enable when needed
|
||||
# speed_limit_bus = cp if self.CP.flags & HyundaiFlags.CANFD_HDA2 else cp_cam
|
||||
# scc = cp_cam.vl["SCC_CONTROL"] if self.CP.flags & HyundaiFlags.CANFD_CAMERA_SCC else cp.vl["SCC_CONTROL"]
|
||||
# cluster = speed_limit_bus.vl["CLUSTER_SPEED_LIMIT"]
|
||||
# tlog("car", { ... })
|
||||
# tlog("cruise", { ... })
|
||||
# tlog("speed_limit", { ... })
|
||||
# tlog("buttons", { ... })
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ from openpilot.common.numpy_fast import clip
|
||||
from openpilot.selfdrive.car import CanBusBase
|
||||
from openpilot.selfdrive.car.hyundai.values import HyundaiFlags
|
||||
|
||||
from openpilot.common.params import Params
|
||||
|
||||
class CanBus(CanBusBase):
|
||||
def __init__(self, CP, hda2=None, fingerprint=None) -> None:
|
||||
@@ -36,12 +35,9 @@ class CanBus(CanBusBase):
|
||||
return self._cam
|
||||
|
||||
|
||||
def create_steering_messages(packer, CP, CAN, enabled, lat_active, apply_steer):
|
||||
|
||||
# Im sure there is a better way to do this
|
||||
params_memory = Params("/dev/shm/params")
|
||||
no_lat_lane_change = params_memory.get_int("no_lat_lane_change", 1)
|
||||
|
||||
def create_steering_messages(packer, CP, CAN, enabled, lat_active, apply_steer, no_lat_lane_change=0):
|
||||
# CLEARPILOT: no_lat_lane_change is passed in by the caller so we can hoist
|
||||
# the Params read out of the 100Hz hot path (was ~5% of carcontroller time)
|
||||
ret = []
|
||||
|
||||
values = {
|
||||
@@ -130,9 +126,7 @@ def create_buttons(packer, CP, CAN, cnt, btn):
|
||||
|
||||
def create_buttons_alt(packer, CP, CAN, cnt, btn, template):
|
||||
return
|
||||
params_memory = Params("/dev/shm/params")
|
||||
CarCruiseDisplayActual = params_memory.get_float("CarCruiseDisplayActual")
|
||||
|
||||
|
||||
values = {
|
||||
"COUNTER": cnt,
|
||||
"NEW_SIGNAL_1": 0.0,
|
||||
|
||||
@@ -484,10 +484,19 @@ class CarInterfaceBase(ABC):
|
||||
self.silent_steer_warning = True
|
||||
events.add(EventName.steerTempUnavailableSilent)
|
||||
else:
|
||||
# CLEARPILOT: log once per instance of this warning
|
||||
if not getattr(self, '_steer_fault_logged', False):
|
||||
import sys
|
||||
print(f"CLP steerTempUnavailable: steerFaultTemporary={cs_out.steerFaultTemporary} "
|
||||
f"steeringPressed={cs_out.steeringPressed} standstill={cs_out.standstill} "
|
||||
f"steering_unpressed={self.steering_unpressed} steeringAngleDeg={cs_out.steeringAngleDeg:.1f} "
|
||||
f"steeringTorque={cs_out.steeringTorque:.1f} vEgo={cs_out.vEgo:.2f}", file=sys.stderr)
|
||||
self._steer_fault_logged = True
|
||||
events.add(EventName.steerTempUnavailable)
|
||||
else:
|
||||
self.no_steer_warning = False
|
||||
self.silent_steer_warning = False
|
||||
self._steer_fault_logged = False
|
||||
if cs_out.steerFaultPermanent:
|
||||
events.add(EventName.steerUnavailable)
|
||||
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
ClearPilot bench command tool. Sets bench params and queries UI state.
|
||||
|
||||
Usage:
|
||||
python3 -m selfdrive.clearpilot.bench_cmd gear D
|
||||
python3 -m selfdrive.clearpilot.bench_cmd speed 20
|
||||
python3 -m selfdrive.clearpilot.bench_cmd speedlimit 45
|
||||
python3 -m selfdrive.clearpilot.bench_cmd cruise 55
|
||||
python3 -m selfdrive.clearpilot.bench_cmd cruiseactive 0|1|2 (0=disabled, 1=active, 2=paused)
|
||||
python3 -m selfdrive.clearpilot.bench_cmd engaged 1
|
||||
python3 -m selfdrive.clearpilot.bench_cmd ding (trigger speed limit ding sound)
|
||||
python3 -m selfdrive.clearpilot.bench_cmd debugbutton (simulate LKAS debug button press)
|
||||
python3 -m selfdrive.clearpilot.bench_cmd dump
|
||||
python3 -m selfdrive.clearpilot.bench_cmd wait_ready
|
||||
"""
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
import zmq
|
||||
from openpilot.common.params import Params
|
||||
|
||||
|
||||
def check_ui_process():
|
||||
"""Check if UI process is running and healthy. Returns error string or None if OK."""
|
||||
try:
|
||||
result = subprocess.run(["pgrep", "-a", "-f", "./ui"], capture_output=True, text=True)
|
||||
if result.returncode != 0:
|
||||
return "ERROR: UI process not running"
|
||||
# Get the PID and check its uptime
|
||||
for line in result.stdout.strip().split("\n"):
|
||||
parts = line.split(None, 1)
|
||||
if len(parts) >= 2 and "./ui" in parts[1]:
|
||||
pid = parts[0]
|
||||
try:
|
||||
stat = os.stat(f"/proc/{pid}")
|
||||
uptime = time.time() - stat.st_mtime
|
||||
if uptime < 5:
|
||||
return f"ERROR: UI process (pid {pid}) uptime {uptime:.1f}s — likely crash looping. Check: tail /data/log2/$(ls -t /data/log2/ | head -1)/session.log"
|
||||
except FileNotFoundError:
|
||||
return "ERROR: UI process disappeared"
|
||||
except Exception:
|
||||
pass
|
||||
return None
|
||||
|
||||
|
||||
def ui_dump():
|
||||
ctx = zmq.Context()
|
||||
sock = ctx.socket(zmq.REQ)
|
||||
sock.setsockopt(zmq.RCVTIMEO, 2000)
|
||||
sock.connect("ipc:///tmp/clearpilot_ui_rpc")
|
||||
sock.send_string("dump")
|
||||
try:
|
||||
return sock.recv_string()
|
||||
except zmq.Again:
|
||||
return None
|
||||
finally:
|
||||
sock.close()
|
||||
ctx.term()
|
||||
|
||||
|
||||
def wait_ready(timeout=20):
|
||||
"""Wait until the UI shows ReadyWindow, up to timeout seconds."""
|
||||
start = time.time()
|
||||
while time.time() - start < timeout:
|
||||
dump = ui_dump()
|
||||
if dump and "ReadyWindow" in dump:
|
||||
# Check it's actually visible
|
||||
for line in dump.split("\n"):
|
||||
if "ReadyWindow" in line and "vis=Y" in line:
|
||||
print("UI ready (ReadyWindow visible)")
|
||||
return True
|
||||
time.sleep(1)
|
||||
print(f"ERROR: UI not ready after {timeout}s")
|
||||
if dump:
|
||||
print(dump)
|
||||
return False
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 2:
|
||||
print(__doc__)
|
||||
return
|
||||
|
||||
cmd = sys.argv[1].lower()
|
||||
params = Params("/dev/shm/params")
|
||||
|
||||
param_map = {
|
||||
"gear": "BenchGear",
|
||||
"speed": "BenchSpeed",
|
||||
"speedlimit": "BenchSpeedLimit",
|
||||
"cruise": "BenchCruiseSpeed",
|
||||
"cruiseactive": "BenchCruiseActive",
|
||||
"engaged": "BenchEngaged",
|
||||
}
|
||||
|
||||
if cmd == "dump":
|
||||
ui_status = check_ui_process()
|
||||
if ui_status:
|
||||
print(ui_status)
|
||||
else:
|
||||
result = ui_dump()
|
||||
if result:
|
||||
print(result)
|
||||
else:
|
||||
print("ERROR: UI not responding")
|
||||
|
||||
elif cmd == "wait_ready":
|
||||
wait_ready()
|
||||
|
||||
elif cmd == "ding":
|
||||
params.put("ClearpilotPlayDing", "1")
|
||||
print("Ding triggered")
|
||||
|
||||
elif cmd == "debugbutton":
|
||||
# Simulate LKAS debug button — same state machine as controlsd.clearpilot_state_control()
|
||||
current = params.get_int("ScreenDisplayMode")
|
||||
gear = (params.get("BenchGear") or b"P").decode().strip().upper()
|
||||
in_drive = gear in ("D", "S", "L")
|
||||
|
||||
if in_drive:
|
||||
transitions = {0: 4, 1: 2, 2: 3, 3: 4, 4: 2}
|
||||
new_mode = transitions.get(current, 0)
|
||||
else:
|
||||
new_mode = 0 if current == 3 else 3
|
||||
|
||||
params.put_int("ScreenDisplayMode", new_mode)
|
||||
mode_names = {0: "auto-normal", 1: "auto-nightrider", 2: "normal", 3: "screen-off", 4: "nightrider"}
|
||||
print(f"ScreenDisplayMode: {current} ({mode_names.get(current, '?')}) → {new_mode} ({mode_names.get(new_mode, '?')})"
|
||||
f" [gear={gear}, in_drive={in_drive}]")
|
||||
|
||||
elif cmd in param_map:
|
||||
if len(sys.argv) < 3:
|
||||
print(f"Usage: bench_cmd {cmd} <value>")
|
||||
return
|
||||
value = sys.argv[2]
|
||||
params.put(param_map[cmd], value)
|
||||
print(f"{param_map[cmd]} = {value}")
|
||||
|
||||
else:
|
||||
print(f"Unknown command: {cmd}")
|
||||
print(__doc__)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,137 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
ClearPilot bench onroad simulator.
|
||||
|
||||
Publishes fake messages to make the UI go onroad and display
|
||||
configurable vehicle state. Control values via params in /dev/shm/params:
|
||||
|
||||
BenchSpeed - vehicle speed in mph (default: 0)
|
||||
BenchSpeedLimit - speed limit in mph (default: 0, 0=hidden)
|
||||
BenchCruiseSpeed - cruise set speed in mph (default: 0, 0=not set)
|
||||
BenchCruiseActive - 0=disabled, 1=active, 2=paused/standstill (default: 0)
|
||||
BenchGear - P, D, R, N (default: P)
|
||||
BenchEngaged - 0 or 1, cruise engaged (default: 0)
|
||||
|
||||
Usage:
|
||||
su - comma -c "PYTHONPATH=/data/openpilot python3 -m selfdrive.clearpilot.bench_onroad"
|
||||
|
||||
To stop: Ctrl+C or kill the process. UI returns to offroad.
|
||||
"""
|
||||
import time
|
||||
|
||||
import cereal.messaging as messaging
|
||||
from cereal import log, car
|
||||
from openpilot.common.params import Params
|
||||
from openpilot.common.conversions import Conversions as CV
|
||||
from openpilot.selfdrive.clearpilot.speed_logic import SpeedState
|
||||
|
||||
|
||||
def main():
|
||||
params = Params()
|
||||
params_mem = Params("/dev/shm/params")
|
||||
|
||||
# Set defaults
|
||||
params_mem.put("BenchSpeed", "0")
|
||||
params_mem.put("BenchSpeedLimit", "0")
|
||||
params_mem.put("BenchCruiseSpeed", "0")
|
||||
params_mem.put("BenchCruiseActive", "0")
|
||||
params_mem.put("BenchGear", "P")
|
||||
params_mem.put("BenchEngaged", "0")
|
||||
|
||||
# ClearPilot speed processing
|
||||
speed_state = SpeedState()
|
||||
|
||||
# thermald handles deviceState (reads our fake pandaStates for ignition)
|
||||
pm = messaging.PubMaster([
|
||||
"pandaStates", "carState", "controlsState",
|
||||
"driverMonitoringState", "liveCalibration",
|
||||
])
|
||||
|
||||
print("Bench onroad simulator started")
|
||||
print("Set values in /dev/shm/params/d/Bench*")
|
||||
print(" BenchSpeed=0 BenchSpeedLimit=0 BenchCruiseSpeed=0 BenchGear=P BenchEngaged=0")
|
||||
|
||||
gear_map = {
|
||||
"P": car.CarState.GearShifter.park,
|
||||
"D": car.CarState.GearShifter.drive,
|
||||
"R": car.CarState.GearShifter.reverse,
|
||||
"N": car.CarState.GearShifter.neutral,
|
||||
}
|
||||
|
||||
frame = 0
|
||||
try:
|
||||
while True:
|
||||
# Read bench params
|
||||
speed_mph = float((params_mem.get("BenchSpeed", encoding="utf-8") or "0").strip())
|
||||
speed_limit_mph = float((params_mem.get("BenchSpeedLimit", encoding="utf-8") or "0").strip())
|
||||
cruise_mph = float((params_mem.get("BenchCruiseSpeed", encoding="utf-8") or "0").strip())
|
||||
gear_str = (params_mem.get("BenchGear", encoding="utf-8") or "P").strip().upper()
|
||||
cruise_active_str = (params_mem.get("BenchCruiseActive", encoding="utf-8") or "0").strip()
|
||||
engaged = (params_mem.get("BenchEngaged", encoding="utf-8") or "0").strip() == "1"
|
||||
|
||||
speed_ms = speed_mph * CV.MPH_TO_MS
|
||||
speed_limit_ms = speed_limit_mph * CV.MPH_TO_MS
|
||||
cruise_ms = cruise_mph * CV.MPH_TO_MS
|
||||
gear = gear_map.get(gear_str, car.CarState.GearShifter.park)
|
||||
|
||||
# Cruise state: 0=disabled, 1=active, 2=paused
|
||||
cruise_active = cruise_active_str == "1"
|
||||
cruise_standstill = cruise_active_str == "2"
|
||||
|
||||
# ClearPilot speed processing (~2 Hz at 10 Hz loop)
|
||||
if frame % 5 == 0:
|
||||
has_speed = speed_mph > 0
|
||||
speed_state.update(speed_ms, has_speed, speed_limit_ms, is_metric=False,
|
||||
cruise_speed_ms=cruise_ms, cruise_active=cruise_active or cruise_standstill,
|
||||
cruise_standstill=cruise_standstill)
|
||||
|
||||
# pandaStates — 10 Hz (thermald reads ignition from this)
|
||||
if frame % 1 == 0:
|
||||
dat = messaging.new_message("pandaStates", 1)
|
||||
dat.pandaStates[0].ignitionLine = True
|
||||
dat.pandaStates[0].pandaType = log.PandaState.PandaType.tres
|
||||
pm.send("pandaStates", dat)
|
||||
|
||||
# carState — 10 Hz
|
||||
dat = messaging.new_message("carState")
|
||||
cs = dat.carState
|
||||
cs.vEgo = speed_ms
|
||||
cs.vEgoCluster = speed_ms
|
||||
cs.gearShifter = gear
|
||||
cs.standstill = speed_ms < 0.1
|
||||
cs.cruiseState.available = True
|
||||
cs.cruiseState.enabled = engaged
|
||||
cs.cruiseState.speed = cruise_mph * CV.MPH_TO_MS if cruise_mph > 0 else 0
|
||||
pm.send("carState", dat)
|
||||
|
||||
# controlsState — 10 Hz
|
||||
dat = messaging.new_message("controlsState")
|
||||
ctl = dat.controlsState
|
||||
ctl.vCruise = cruise_mph * 1.60934 if cruise_mph > 0 else 255 # km/h or 255=not set
|
||||
ctl.vCruiseCluster = ctl.vCruise
|
||||
ctl.enabled = engaged
|
||||
ctl.active = engaged
|
||||
pm.send("controlsState", dat)
|
||||
|
||||
# driverMonitoringState — low freq
|
||||
if frame % 10 == 0:
|
||||
dat = messaging.new_message("driverMonitoringState")
|
||||
dat.driverMonitoringState.isActiveMode = True
|
||||
pm.send("driverMonitoringState", dat)
|
||||
|
||||
# liveCalibration — low freq
|
||||
if frame % 50 == 0:
|
||||
dat = messaging.new_message("liveCalibration")
|
||||
dat.liveCalibration.calStatus = log.LiveCalibrationData.Status.calibrated
|
||||
dat.liveCalibration.rpyCalib = [0.0, 0.0, 0.0]
|
||||
pm.send("liveCalibration", dat)
|
||||
|
||||
frame += 1
|
||||
time.sleep(0.1) # 10 Hz base loop
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print("\nBench simulator stopped")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,121 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
CLEARPILOT dashcamd — records raw camera footage to MP4 using hardware H.264 encoder.
|
||||
|
||||
Connects directly to camerad via VisionIPC, receives NV12 frames, and pipes them
|
||||
to ffmpeg's h264_v4l2m2m encoder. Produces 3-minute MP4 segments in /data/media/0/videos/.
|
||||
|
||||
This replaces the FrogPilot screen recorder approach (QWidget::grab -> OMX) with a
|
||||
direct camera capture that works regardless of UI state (screen off, alternate modes, etc).
|
||||
"""
|
||||
import os
|
||||
import time
|
||||
import subprocess
|
||||
import signal
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
|
||||
from cereal.visionipc import VisionIpcClient, VisionStreamType
|
||||
from openpilot.common.params import Params
|
||||
from openpilot.common.swaglog import cloudlog
|
||||
from openpilot.selfdrive import sentry
|
||||
|
||||
PROCESS_NAME = "selfdrive.clearpilot.dashcamd"
|
||||
VIDEOS_DIR = "/data/media/0/videos"
|
||||
SEGMENT_SECONDS = 180 # 3 minutes
|
||||
CAMERA_FPS = 20
|
||||
FRAMES_PER_SEGMENT = SEGMENT_SECONDS * CAMERA_FPS
|
||||
|
||||
|
||||
def make_filename():
|
||||
return datetime.now().strftime("%Y%m%d-%H%M%S") + ".mp4"
|
||||
|
||||
|
||||
def open_encoder(width, height, filepath):
|
||||
"""Start an ffmpeg subprocess that accepts raw NV12 on stdin and writes MP4."""
|
||||
cmd = [
|
||||
"ffmpeg", "-y", "-nostdin", "-loglevel", "error",
|
||||
"-f", "rawvideo",
|
||||
"-pix_fmt", "nv12",
|
||||
"-s", f"{width}x{height}",
|
||||
"-r", str(CAMERA_FPS),
|
||||
"-i", "pipe:0",
|
||||
"-c:v", "h264_v4l2m2m",
|
||||
"-b:v", "4M",
|
||||
"-f", "mp4",
|
||||
"-movflags", "+faststart",
|
||||
filepath,
|
||||
]
|
||||
return subprocess.Popen(cmd, stdin=subprocess.PIPE)
|
||||
|
||||
|
||||
def main():
|
||||
sentry.set_tag("daemon", PROCESS_NAME)
|
||||
cloudlog.bind(daemon=PROCESS_NAME)
|
||||
|
||||
os.makedirs(VIDEOS_DIR, exist_ok=True)
|
||||
|
||||
params = Params()
|
||||
|
||||
# Connect to camerad road stream
|
||||
cloudlog.info("dashcamd: connecting to camerad road stream")
|
||||
vipc = VisionIpcClient("camerad", VisionStreamType.VISION_STREAM_ROAD, False)
|
||||
while not vipc.connect(False):
|
||||
time.sleep(0.1)
|
||||
|
||||
width, height = vipc.width, vipc.height
|
||||
# NV12 frame: Y plane (w*h) + UV plane (w*h/2)
|
||||
frame_size = width * height * 3 // 2
|
||||
cloudlog.info(f"dashcamd: connected, {width}x{height}, frame_size={frame_size}")
|
||||
|
||||
frame_count = 0
|
||||
encoder = None
|
||||
lock_path = None
|
||||
|
||||
try:
|
||||
while True:
|
||||
buf = vipc.recv()
|
||||
if buf is None:
|
||||
continue
|
||||
|
||||
# Start new segment if needed
|
||||
if encoder is None or frame_count >= FRAMES_PER_SEGMENT:
|
||||
# Close previous segment
|
||||
if encoder is not None:
|
||||
encoder.stdin.close()
|
||||
encoder.wait()
|
||||
if lock_path and os.path.exists(lock_path):
|
||||
os.remove(lock_path)
|
||||
cloudlog.info(f"dashcamd: closed segment, {frame_count} frames")
|
||||
|
||||
# Open new segment
|
||||
filename = make_filename()
|
||||
filepath = os.path.join(VIDEOS_DIR, filename)
|
||||
lock_path = filepath + ".lock"
|
||||
Path(lock_path).touch()
|
||||
|
||||
cloudlog.info(f"dashcamd: opening segment {filename}")
|
||||
encoder = open_encoder(width, height, filepath)
|
||||
frame_count = 0
|
||||
|
||||
# Write raw NV12 frame to ffmpeg stdin
|
||||
try:
|
||||
encoder.stdin.write(buf.data[:frame_size])
|
||||
frame_count += 1
|
||||
except BrokenPipeError:
|
||||
cloudlog.error("dashcamd: encoder pipe broken, restarting segment")
|
||||
encoder = None
|
||||
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
pass
|
||||
finally:
|
||||
if encoder is not None:
|
||||
encoder.stdin.close()
|
||||
encoder.wait()
|
||||
if lock_path and os.path.exists(lock_path):
|
||||
os.remove(lock_path)
|
||||
cloudlog.info("dashcamd: stopped")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,60 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
ClearPilot speed/cruise daemon.
|
||||
|
||||
Subscribes to gpsLocation (for vehicle speed display) and carState (for
|
||||
cruise state), reads CarSpeedLimit from /dev/shm/params, and ticks
|
||||
SpeedState at ~2 Hz. SpeedState writes the display params the onroad UI
|
||||
reads, and asserts ClearpilotPlayDing="1" on speed-limit warning
|
||||
transitions (soundd consumes that flag).
|
||||
|
||||
Decoupled from controlsd so self-driving timing isn't affected.
|
||||
"""
|
||||
import sys
|
||||
|
||||
import cereal.messaging as messaging
|
||||
from openpilot.common.params import Params
|
||||
from openpilot.common.realtime import Ratekeeper
|
||||
from openpilot.selfdrive.clearpilot.speed_logic import SpeedState
|
||||
|
||||
|
||||
def main():
|
||||
print("CLP speed_logicd: starting", file=sys.stderr, flush=True)
|
||||
|
||||
params = Params()
|
||||
params_memory = Params("/dev/shm/params")
|
||||
speed_state = SpeedState()
|
||||
|
||||
is_metric = params.get_bool("IsMetric")
|
||||
|
||||
sm = messaging.SubMaster(['gpsLocation', 'carState'])
|
||||
rk = Ratekeeper(2.0, print_delay_threshold=None)
|
||||
|
||||
while True:
|
||||
sm.update(0)
|
||||
|
||||
gps = sm['gpsLocation']
|
||||
has_gps = sm.valid['gpsLocation'] and gps.hasFix
|
||||
speed_ms = float(gps.speed) if has_gps else 0.0
|
||||
|
||||
cs = sm['carState']
|
||||
cruise_speed_ms = float(cs.cruiseState.speed)
|
||||
cruise_active = bool(cs.cruiseState.enabled)
|
||||
cruise_standstill = bool(cs.cruiseState.standstill)
|
||||
|
||||
# CarSpeedLimit is published by hyundai/carstate.py CAN-FD decode (when
|
||||
# ported). Until then it's 0 / empty and the speed-limit + warning logic
|
||||
# naturally short-circuits.
|
||||
try:
|
||||
speed_limit_ms = float(params_memory.get("CarSpeedLimit") or 0.0)
|
||||
except (TypeError, ValueError):
|
||||
speed_limit_ms = 0.0
|
||||
|
||||
speed_state.update(speed_ms, has_gps, speed_limit_ms, is_metric,
|
||||
cruise_speed_ms, cruise_active, cruise_standstill)
|
||||
|
||||
rk.keep_time()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,53 @@
|
||||
"""
|
||||
ClearPilot telemetry client library.
|
||||
|
||||
Usage from any process:
|
||||
from openpilot.selfdrive.clearpilot.telemetry import tlog
|
||||
tlog("canbus", {"speed": 45.2, "speed_limit": 60})
|
||||
|
||||
Sends JSON packets over ZMQ PUSH to telemetryd, which diffs and writes CSV.
|
||||
Only sends when TelemetryEnabled memory param is set — zero cost when disabled.
|
||||
"""
|
||||
import json
|
||||
import os
|
||||
import time
|
||||
import zmq
|
||||
|
||||
TELEMETRY_SOCK = "ipc:///tmp/clearpilot_telemetry"
|
||||
_PARAM_PATH = "/dev/shm/params/d/TelemetryEnabled"
|
||||
|
||||
_ctx = None
|
||||
_sock = None
|
||||
_last_check = 0
|
||||
_enabled = False
|
||||
|
||||
|
||||
def tlog(group: str, data: dict):
|
||||
"""Log a telemetry packet. Only changed values will be written to CSV by telemetryd."""
|
||||
global _ctx, _sock, _last_check, _enabled
|
||||
|
||||
# Check param at most once per second to avoid filesystem overhead
|
||||
now = time.monotonic()
|
||||
if now - _last_check > 1.0:
|
||||
_last_check = now
|
||||
try:
|
||||
with open(_PARAM_PATH, 'r') as f:
|
||||
_enabled = f.read().strip() == "1"
|
||||
except (FileNotFoundError, IOError):
|
||||
_enabled = False
|
||||
|
||||
if not _enabled:
|
||||
return
|
||||
|
||||
if _sock is None:
|
||||
_ctx = zmq.Context.instance()
|
||||
_sock = _ctx.socket(zmq.PUSH)
|
||||
_sock.setsockopt(zmq.LINGER, 0)
|
||||
_sock.setsockopt(zmq.SNDHWM, 100)
|
||||
_sock.connect(TELEMETRY_SOCK)
|
||||
|
||||
msg = json.dumps({"ts": time.time(), "group": group, "data": data})
|
||||
try:
|
||||
_sock.send_string(msg, zmq.NOBLOCK)
|
||||
except zmq.Again:
|
||||
pass # drop if collector isn't running or queue full
|
||||
@@ -0,0 +1,147 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
ClearPilot telemetry collector.
|
||||
|
||||
Receives telemetry packets from any process via ZMQ, diffs against previous
|
||||
state per group, and writes only changed values to CSV.
|
||||
|
||||
Controlled by TelemetryEnabled param — when toggled on, the first packet
|
||||
from each group writes all values (full dump). When toggled off, stops writing.
|
||||
|
||||
CSV format: timestamp,group,key,value
|
||||
"""
|
||||
import csv
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
import time
|
||||
import zmq
|
||||
|
||||
import cereal.messaging as messaging
|
||||
from openpilot.common.params import Params
|
||||
from openpilot.selfdrive.clearpilot.telemetry import TELEMETRY_SOCK
|
||||
from openpilot.selfdrive.manager.process import _log_dir, session_log
|
||||
|
||||
MIN_DISK_FREE_GB = 5
|
||||
DISK_CHECK_INTERVAL = 10 # seconds
|
||||
|
||||
|
||||
def main():
|
||||
params = Params("/dev/shm/params")
|
||||
ctx = zmq.Context.instance()
|
||||
sock = ctx.socket(zmq.PULL)
|
||||
sock.setsockopt(zmq.RCVHWM, 1000)
|
||||
sock.bind(TELEMETRY_SOCK)
|
||||
|
||||
# GPS subscriber for location telemetry
|
||||
sm = messaging.SubMaster(['gpsLocation'])
|
||||
last_gps_log = 0
|
||||
|
||||
csv_path = os.path.join(_log_dir, "telemetry.csv")
|
||||
state: dict[str, dict] = {} # group -> {key: last_value}
|
||||
was_enabled = False
|
||||
writer = None
|
||||
f = None
|
||||
last_disk_check = 0
|
||||
|
||||
while True:
|
||||
# Check enable state every iteration (cheap param read)
|
||||
enabled = params.get("TelemetryEnabled") == b"1"
|
||||
|
||||
# Check disk space every 10 seconds while enabled
|
||||
if enabled and (time.monotonic() - last_disk_check) > DISK_CHECK_INTERVAL:
|
||||
last_disk_check = time.monotonic()
|
||||
disk = shutil.disk_usage("/data")
|
||||
free_gb = disk.free / (1024 ** 3)
|
||||
if free_gb < MIN_DISK_FREE_GB:
|
||||
session_log.warning("telemetry disabled: disk free %.1f GB < %d GB", free_gb, MIN_DISK_FREE_GB)
|
||||
params.put("TelemetryEnabled", "0")
|
||||
enabled = False
|
||||
|
||||
# Toggled on: open CSV, clear state so first packet is a full dump
|
||||
if enabled and not was_enabled:
|
||||
f = open(csv_path, "a", newline="")
|
||||
writer = csv.writer(f)
|
||||
if os.path.getsize(csv_path) == 0:
|
||||
writer.writerow(["timestamp", "group", "key", "value"])
|
||||
state.clear() # force full dump on first packet per group
|
||||
f.flush()
|
||||
|
||||
# Toggled off: close file
|
||||
if not enabled and was_enabled:
|
||||
if f:
|
||||
f.close()
|
||||
f = None
|
||||
writer = None
|
||||
|
||||
was_enabled = enabled
|
||||
|
||||
# GPS telemetry at most 1Hz — fed through the same diff logic
|
||||
if enabled and writer is not None:
|
||||
sm.update(0)
|
||||
now = time.monotonic()
|
||||
if sm.updated['gpsLocation'] and (now - last_gps_log) >= 1.0:
|
||||
gps = sm['gpsLocation']
|
||||
gps_data = {
|
||||
"latitude": f"{gps.latitude:.7f}",
|
||||
"longitude": f"{gps.longitude:.7f}",
|
||||
"speed": f"{gps.speed:.2f}",
|
||||
"altitude": f"{gps.altitude:.1f}",
|
||||
"bearing": f"{gps.bearingDeg:.1f}",
|
||||
"accuracy": f"{gps.accuracy:.1f}",
|
||||
}
|
||||
ts = time.time()
|
||||
if "gps" not in state:
|
||||
state["gps"] = {}
|
||||
prev_gps = state["gps"]
|
||||
gps_changed = False
|
||||
for key, value in gps_data.items():
|
||||
if key not in prev_gps or prev_gps[key] != value:
|
||||
writer.writerow([f"{ts:.6f}", "gps", key, value])
|
||||
prev_gps[key] = value
|
||||
gps_changed = True
|
||||
if gps_changed:
|
||||
f.flush()
|
||||
last_gps_log = now
|
||||
|
||||
# Always drain the socket (even when disabled) to avoid backpressure
|
||||
try:
|
||||
raw = sock.recv_string(zmq.NOBLOCK)
|
||||
except zmq.Again:
|
||||
time.sleep(0.01)
|
||||
continue
|
||||
except zmq.ZMQError:
|
||||
time.sleep(0.01)
|
||||
continue
|
||||
|
||||
if not enabled or writer is None:
|
||||
continue
|
||||
|
||||
try:
|
||||
pkt = json.loads(raw)
|
||||
except json.JSONDecodeError:
|
||||
continue
|
||||
|
||||
ts = pkt.get("ts", 0)
|
||||
group = pkt.get("group", "")
|
||||
data = pkt.get("data", {})
|
||||
|
||||
if group not in state:
|
||||
state[group] = {}
|
||||
|
||||
prev = state[group]
|
||||
changed = False
|
||||
|
||||
for key, value in data.items():
|
||||
str_val = str(value)
|
||||
if key not in prev or prev[key] != str_val:
|
||||
writer.writerow([f"{ts:.6f}", group, key, str_val])
|
||||
prev[key] = str_val
|
||||
changed = True
|
||||
|
||||
if changed:
|
||||
f.flush()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 50 KiB |
|
After Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 7.5 KiB |
|
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 24 KiB |
@@ -0,0 +1,12 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Query the UI process for its current widget tree. Usage: python3 -m selfdrive.clearpilot.ui_dump"""
|
||||
import zmq
|
||||
ctx = zmq.Context()
|
||||
sock = ctx.socket(zmq.REQ)
|
||||
sock.setsockopt(zmq.RCVTIMEO, 2000)
|
||||
sock.connect("ipc:///tmp/clearpilot_ui_rpc")
|
||||
sock.send_string("dump")
|
||||
try:
|
||||
print(sock.recv_string())
|
||||
except zmq.Again:
|
||||
print("ERROR: UI not responding (timeout)")
|
||||
@@ -37,6 +37,8 @@ from openpilot.system.version import get_short_branch
|
||||
from openpilot.selfdrive.frogpilot.controls.lib.frogpilot_functions import CRUISING_SPEED, PROBABILITY, MovingAverageCalculator
|
||||
|
||||
from openpilot.selfdrive.frogpilot.controls.lib.model_manager import RADARLESS_MODELS
|
||||
from openpilot.selfdrive.clearpilot.telemetry import tlog
|
||||
from openpilot.selfdrive.clearpilot.speed_logic import SpeedState
|
||||
from openpilot.selfdrive.frogpilot.controls.lib.speed_limit_controller import SpeedLimitController
|
||||
|
||||
SOFT_DISABLE_TIME = 3 # seconds
|
||||
@@ -47,7 +49,7 @@ CAMERA_OFFSET = 0.04
|
||||
REPLAY = "REPLAY" in os.environ
|
||||
SIMULATION = "SIMULATION" in os.environ
|
||||
TESTING_CLOSET = "TESTING_CLOSET" in os.environ
|
||||
IGNORE_PROCESSES = {"loggerd", "encoderd", "statsd"}
|
||||
IGNORE_PROCESSES = {"loggerd", "encoderd", "statsd", "telemetryd", "dashcamd"}
|
||||
|
||||
ThermalStatus = log.DeviceState.ThermalStatus
|
||||
State = log.ControlsState.OpenpilotState
|
||||
@@ -78,8 +80,12 @@ class Controls:
|
||||
|
||||
self.params_memory.put_bool("CPTLkasButtonAction", False)
|
||||
self.params_memory.put_int("ScreenDisplayMode", 0)
|
||||
# CLEARPILOT: edge tracking for park→drive auto-wake of screen
|
||||
|
||||
# CLEARPILOT: speed-limit/cruise-warning state machine + park→drive auto-reset
|
||||
self.speed_state = SpeedState()
|
||||
self.speed_state_frame = 0
|
||||
self.was_driving_gear = False
|
||||
self.driving_gear = False
|
||||
|
||||
self.radarless_model = self.params.get("Model", encoding='utf-8') in RADARLESS_MODELS
|
||||
|
||||
@@ -109,8 +115,8 @@ class Controls:
|
||||
self.sm = messaging.SubMaster(['deviceState', 'pandaStates', 'peripheralState', 'modelV2', 'liveCalibration',
|
||||
'carOutput', 'driverMonitoringState', 'longitudinalPlan', 'liveLocationKalman',
|
||||
'managerState', 'liveParameters', 'radarState', 'liveTorqueParameters',
|
||||
'testJoystick', 'frogpilotPlan'] + self.camera_packets + self.sensor_packets,
|
||||
ignore_alive=ignore, ignore_avg_freq=ignore+['radarState', 'testJoystick'], ignore_valid=['testJoystick', ],
|
||||
'testJoystick', 'frogpilotPlan', 'gpsLocation'] + self.camera_packets + self.sensor_packets,
|
||||
ignore_alive=ignore + ['gpsLocation'], ignore_avg_freq=ignore+['radarState', 'testJoystick', 'gpsLocation'], ignore_valid=['testJoystick', 'gpsLocation'],
|
||||
frequency=int(1/DT_CTRL))
|
||||
|
||||
self.joystick_mode = self.params.get_bool("JoystickDebugMode")
|
||||
@@ -443,6 +449,13 @@ class Controls:
|
||||
# All events here should at least have NO_ENTRY and SOFT_DISABLE.
|
||||
num_events = len(self.events)
|
||||
|
||||
# CLEARPILOT: compute model standby suppression early — used by multiple checks below
|
||||
try:
|
||||
standby_ts = float(self.params_memory.get("ModelStandbyTs") or "0")
|
||||
except (ValueError, TypeError):
|
||||
standby_ts = 0
|
||||
model_suppress = (time.monotonic() - standby_ts) < 2.0
|
||||
|
||||
not_running = {p.name for p in self.sm['managerState'].processes if not p.running and p.shouldBeRunning}
|
||||
if self.sm.recv_frame['managerState'] and (not_running - IGNORE_PROCESSES):
|
||||
self.events.add(EventName.processNotRunning)
|
||||
@@ -456,9 +469,11 @@ class Controls:
|
||||
elif not self.sm.all_freq_ok(self.camera_packets):
|
||||
self.events.add(EventName.cameraFrameRate)
|
||||
if not REPLAY and self.rk.lagging:
|
||||
import sys
|
||||
print(f"CLP controlsdLagging: remaining={self.rk.remaining:.4f} standstill={CS.standstill} vEgo={CS.vEgo:.2f}", file=sys.stderr)
|
||||
self.events.add(EventName.controlsdLagging)
|
||||
if not self.radarless_model:
|
||||
if len(self.sm['radarState'].radarErrors) or (not self.rk.lagging and not self.sm.all_checks(['radarState'])):
|
||||
if not model_suppress and (len(self.sm['radarState'].radarErrors) or (not self.rk.lagging and not self.sm.all_checks(['radarState']))):
|
||||
self.events.add(EventName.radarFault)
|
||||
if not self.sm.valid['pandaStates']:
|
||||
self.events.add(EventName.usbError)
|
||||
@@ -470,7 +485,7 @@ class Controls:
|
||||
# generic catch-all. ideally, a more specific event should be added above instead
|
||||
has_disable_events = self.events.contains(ET.NO_ENTRY) and (self.events.contains(ET.SOFT_DISABLE) or self.events.contains(ET.IMMEDIATE_DISABLE))
|
||||
no_system_errors = (not has_disable_events) or (len(self.events) == num_events)
|
||||
if (not self.sm.all_checks() or self.card.can_rcv_timeout) and no_system_errors:
|
||||
if (not self.sm.all_checks() or self.card.can_rcv_timeout) and no_system_errors and not model_suppress:
|
||||
if not self.sm.all_alive():
|
||||
self.events.add(EventName.commIssue)
|
||||
elif not self.sm.all_freq_ok():
|
||||
@@ -491,13 +506,13 @@ class Controls:
|
||||
self.logged_comm_issue = None
|
||||
|
||||
if not (self.CP.notCar and self.joystick_mode):
|
||||
if not self.sm['liveLocationKalman'].posenetOK:
|
||||
if not self.sm['liveLocationKalman'].posenetOK and not model_suppress:
|
||||
self.events.add(EventName.posenetInvalid)
|
||||
if not self.sm['liveLocationKalman'].deviceStable:
|
||||
self.events.add(EventName.deviceFalling)
|
||||
if not self.sm['liveLocationKalman'].inputsOK:
|
||||
if not self.sm['liveLocationKalman'].inputsOK and not model_suppress:
|
||||
self.events.add(EventName.locationdTemporaryError)
|
||||
if not self.sm['liveParameters'].valid and not TESTING_CLOSET and (not SIMULATION or REPLAY):
|
||||
if not self.sm['liveParameters'].valid and not TESTING_CLOSET and (not SIMULATION or REPLAY) and not model_suppress:
|
||||
self.events.add(EventName.paramsdTemporaryError)
|
||||
|
||||
# conservative HW alert. if the data or frequency are off, locationd will throw an error
|
||||
@@ -543,7 +558,7 @@ class Controls:
|
||||
self.distance_traveled = 0
|
||||
self.distance_traveled += CS.vEgo * DT_CTRL
|
||||
|
||||
if self.sm['modelV2'].frameDropPerc > 20:
|
||||
if self.sm['modelV2'].frameDropPerc > 20 and not model_suppress:
|
||||
self.events.add(EventName.modeldLagging)
|
||||
|
||||
|
||||
@@ -630,6 +645,14 @@ class Controls:
|
||||
# Check if openpilot is engaged and actuators are enabled
|
||||
self.enabled = self.state in ENABLED_STATES
|
||||
self.active = self.state in ACTIVE_STATES
|
||||
|
||||
# CLEARPILOT: engagement telemetry disabled — was running at 100Hz, causing CPU load
|
||||
# tlog("engage", {
|
||||
# "state": self.state.name if hasattr(self.state, 'name') else str(self.state),
|
||||
# "enabled": self.enabled, "active": self.active,
|
||||
# "cruise_enabled": CS.cruiseState.enabled, "cruise_available": CS.cruiseState.available,
|
||||
# "brakePressed": CS.brakePressed,
|
||||
# })
|
||||
if self.active:
|
||||
self.current_alert_types.append(ET.WARNING)
|
||||
|
||||
@@ -678,9 +701,9 @@ class Controls:
|
||||
|
||||
if model_v2.meta.laneChangeState == LaneChangeState.laneChangeStarting and clearpilot_disable_lat_on_lane_change:
|
||||
CC.latActive = False
|
||||
self.params_memory.put_bool("no_lat_lane_change", True)
|
||||
self.frogpilot_variables.no_lat_lane_change = True
|
||||
else:
|
||||
self.params_memory.put_bool("no_lat_lane_change", False)
|
||||
self.frogpilot_variables.no_lat_lane_change = False
|
||||
|
||||
if CS.leftBlinker or CS.rightBlinker:
|
||||
self.last_blinker_frame = self.sm.frame
|
||||
@@ -1237,33 +1260,43 @@ class Controls:
|
||||
def update_clearpilot_events(self, CS):
|
||||
if (len(CS.buttonEvents) > 0):
|
||||
print (CS.buttonEvents)
|
||||
if any(be.pressed and be.type == FrogPilotButtonType.lkas for be in CS.buttonEvents):
|
||||
self.events.add(EventName.clpDebug)
|
||||
|
||||
def clearpilot_state_control(self, CC, CS):
|
||||
# CLEARPILOT: pure UI plumbing — does not modify CC/actuators. Maintains
|
||||
# ScreenDisplayMode (5-state machine driven by the LFA/debug button + gear
|
||||
# edges). Speed/cruise overlay state is owned by speed_logicd, not here.
|
||||
driving_gear = CS.gearShifter not in (GearShifter.neutral, GearShifter.park,
|
||||
GearShifter.reverse, GearShifter.unknown)
|
||||
|
||||
# Auto-wake screen when shifting into drive from screen-off
|
||||
if driving_gear and not self.was_driving_gear:
|
||||
# CLEARPILOT: auto-reset display when shifting into drive from screen-off
|
||||
if self.driving_gear and not self.was_driving_gear:
|
||||
if self.params_memory.get_int("ScreenDisplayMode") == 3:
|
||||
self.params_memory.put_int("ScreenDisplayMode", 0)
|
||||
self.was_driving_gear = driving_gear
|
||||
self.was_driving_gear = self.driving_gear
|
||||
|
||||
# LFA/debug button cycles ScreenDisplayMode. Onroad and offroad use
|
||||
# different transition tables.
|
||||
if any(be.pressed and be.type == FrogPilotButtonType.lkas for be in CS.buttonEvents):
|
||||
current = self.params_memory.get_int("ScreenDisplayMode")
|
||||
if driving_gear:
|
||||
|
||||
if self.driving_gear:
|
||||
# Onroad: 0→4, 1→2, 2→3, 3→4, 4→2 (never back to auto via button)
|
||||
transitions = {0: 4, 1: 2, 2: 3, 3: 4, 4: 2}
|
||||
new_mode = transitions.get(current, 0)
|
||||
else:
|
||||
# Not in drive: anything except 3 → 3 (screen off), state 3 → 0 (auto)
|
||||
# Not in drive: any except 3 → 3, state 3 → 0
|
||||
new_mode = 0 if current == 3 else 3
|
||||
|
||||
self.params_memory.put_int("ScreenDisplayMode", new_mode)
|
||||
|
||||
# ClearPilot speed processing (~2 Hz at 100 Hz loop) — drives speed-limit sign
|
||||
# and cruise over/under warning sign via memory params read by the UI.
|
||||
self.speed_state_frame += 1
|
||||
if self.speed_state_frame % 50 == 0:
|
||||
gps = self.sm['gpsLocation']
|
||||
has_gps = self.sm.valid['gpsLocation'] and gps.hasFix
|
||||
speed_ms = gps.speed if has_gps else 0.0
|
||||
speed_limit_ms = self.params_memory.get_float("CarSpeedLimit")
|
||||
is_metric = (self.params_memory.get("CarIsMetric", encoding="utf-8") or "0") == "1"
|
||||
cruise_speed_ms = CS.cruiseState.speed
|
||||
cruise_active = CS.cruiseState.enabled
|
||||
cruise_standstill = CS.cruiseState.standstill
|
||||
self.speed_state.update(speed_ms, has_gps, speed_limit_ms, is_metric,
|
||||
cruise_speed_ms, cruise_active, cruise_standstill)
|
||||
return CC
|
||||
|
||||
def main():
|
||||
|
||||
@@ -262,6 +262,16 @@ def calibration_incomplete_alert(CP: car.CarParams, CS: car.CarState, sm: messag
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOWEST, VisualAlert.none, AudibleAlert.none, .2)
|
||||
|
||||
def clp_debug_notice(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert:
|
||||
if not hasattr(clp_debug_notice, "counter"):
|
||||
clp_debug_notice.counter = 0
|
||||
clp_debug_notice.counter += 1
|
||||
return Alert(
|
||||
f"Clearpilot Debug Function Executed ({clp_debug_notice.counter})",
|
||||
"",
|
||||
AlertStatus.normal, AlertSize.small,
|
||||
Priority.LOWER, VisualAlert.none, AudibleAlert.none, 3.0)
|
||||
|
||||
|
||||
def no_gps_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert:
|
||||
# Clearpilot todo:
|
||||
@@ -768,6 +778,10 @@ EVENTS: dict[int, dict[str, Alert | AlertCallbackType]] = {
|
||||
ET.SOFT_DISABLE: soft_disable_alert("Sensor Data Invalid"),
|
||||
},
|
||||
|
||||
# CLEARPILOT: alert suppressed — event still fires for screen toggle and future actions
|
||||
EventName.clpDebug: {
|
||||
},
|
||||
|
||||
EventName.noGps: {
|
||||
ET.PERMANENT: no_gps_alert,
|
||||
},
|
||||
|
||||
@@ -58,6 +58,9 @@ class FrogPilotPlanner:
|
||||
self.params = Params()
|
||||
self.params_memory = Params("/dev/shm/params")
|
||||
|
||||
# CLEARPILOT: track valid transitions so we only log when it flips, not every cycle
|
||||
self._dbg_prev_valid = True
|
||||
|
||||
self.cem = ConditionalExperimentalMode()
|
||||
self.lead_one = Lead()
|
||||
self.mtsc = MapTurnSpeedController()
|
||||
@@ -242,7 +245,18 @@ class FrogPilotPlanner:
|
||||
|
||||
def publish(self, sm, pm):
|
||||
frogpilot_plan_send = messaging.new_message('frogpilotPlan')
|
||||
frogpilot_plan_send.valid = sm.all_checks(service_list=['carState', 'controlsState'])
|
||||
valid = sm.all_checks(service_list=['carState', 'controlsState'])
|
||||
# CLEARPILOT: log on transition into invalid — stderr goes to our plannerd.log
|
||||
if valid != self._dbg_prev_valid and not valid:
|
||||
import sys
|
||||
print(
|
||||
"CLP frogpilotPlan valid=False: "
|
||||
f"carState(a={sm.alive['carState']},v={sm.valid['carState']},f={sm.freq_ok['carState']}) "
|
||||
f"controlsState(a={sm.alive['controlsState']},v={sm.valid['controlsState']},f={sm.freq_ok['controlsState']})",
|
||||
file=sys.stderr, flush=True
|
||||
)
|
||||
self._dbg_prev_valid = valid
|
||||
frogpilot_plan_send.valid = valid
|
||||
frogpilotPlan = frogpilot_plan_send.frogpilotPlan
|
||||
|
||||
frogpilotPlan.accelerationJerk = A_CHANGE_COST * (float(self.jerk) if self.lead_one.status else 1)
|
||||
|
||||
@@ -27,7 +27,7 @@ ScreenRecorder::ScreenRecorder(QWidget *parent) : QPushButton(parent), image_que
|
||||
|
||||
void ScreenRecorder::initializeEncoder() {
|
||||
const std::string path = "/data/media/0/videos";
|
||||
encoder = std::make_unique<OmxEncoder>(path.c_str(), recording_width, recording_height, 60, 2 * 1024 * 1024, false, false);
|
||||
encoder = std::make_unique<OmxEncoder>(path.c_str(), recording_width, recording_height, 60, 2 * 1024 * 1024);
|
||||
}
|
||||
|
||||
ScreenRecorder::~ScreenRecorder() {
|
||||
@@ -132,13 +132,20 @@ void ScreenRecorder::stop() {
|
||||
}
|
||||
|
||||
void ScreenRecorder::update_screen() {
|
||||
if (!uiState()->scene.started) {
|
||||
bool car_on = uiState()->scene.started || uiState()->scene.screen_recorder_debug;
|
||||
|
||||
if (!car_on) {
|
||||
if (recording) {
|
||||
stop();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!recording) return;
|
||||
|
||||
// CLEARPILOT: auto-start recording when car is on (or debug flag set)
|
||||
if (!recording) {
|
||||
start();
|
||||
return;
|
||||
}
|
||||
|
||||
if (milliseconds() - started > 1000 * 60 * 3) {
|
||||
stop();
|
||||
|
||||
@@ -594,15 +594,10 @@ void Localizer::handle_msg(const cereal::Event::Reader& log) {
|
||||
this->handle_sensor(t, log.getAccelerometer());
|
||||
} else if (log.isGyroscope()) {
|
||||
this->handle_sensor(t, log.getGyroscope());
|
||||
// CLEARPILOT: GPS branches removed — locationd no longer subscribes to
|
||||
// gpsLocation/gpsLocationExternal, so these would be dead code regardless.
|
||||
// Self-driving treats GPS as not present: gpsOK stays false (last_gps_msg
|
||||
// never updates), and other liveLocationKalman fields stay at the kalman
|
||||
// filter's IMU+camera-odometry-only state.
|
||||
// } else if (log.isGpsLocation()) {
|
||||
// this->handle_gps(t, log.getGpsLocation(), GPS_QUECTEL_SENSOR_TIME_OFFSET);
|
||||
// } else if (log.isGpsLocationExternal()) {
|
||||
// this->handle_gps(t, log.getGpsLocationExternal(), GPS_UBLOX_SENSOR_TIME_OFFSET);
|
||||
} else if (log.isGpsLocation()) {
|
||||
this->handle_gps(t, log.getGpsLocation(), GPS_QUECTEL_SENSOR_TIME_OFFSET);
|
||||
} else if (log.isGpsLocationExternal()) {
|
||||
this->handle_gps(t, log.getGpsLocationExternal(), GPS_UBLOX_SENSOR_TIME_OFFSET);
|
||||
//} else if (log.isGnssMeasurements()) {
|
||||
// this->handle_gnss(t, log.getGnssMeasurements());
|
||||
} else if (log.isCarState()) {
|
||||
@@ -681,16 +676,22 @@ void Localizer::configure_gnss_source(const LocalizerGnssSource &source) {
|
||||
}
|
||||
|
||||
int Localizer::locationd_thread() {
|
||||
// CLEARPILOT: do not subscribe to GPS. Our gpsd publishes gpsLocation for
|
||||
// UI/clock/dashcam, but feeding it to the kalman filter screws up the
|
||||
// self-driving math. liveLocationKalman.gpsOK stays false; downstream
|
||||
// self-driving consumers (controlsd, paramsd, torqued, frogpilot_planner)
|
||||
// already handle that case.
|
||||
this->configure_gnss_source(LocalizerGnssSource::QCOM);
|
||||
const std::initializer_list<const char *> service_list = {"cameraOdometry", "liveCalibration",
|
||||
Params params;
|
||||
LocalizerGnssSource source;
|
||||
const char* gps_location_socket;
|
||||
if (params.getBool("UbloxAvailable")) {
|
||||
source = LocalizerGnssSource::UBLOX;
|
||||
gps_location_socket = "gpsLocationExternal";
|
||||
} else {
|
||||
source = LocalizerGnssSource::QCOM;
|
||||
gps_location_socket = "gpsLocation";
|
||||
}
|
||||
|
||||
this->configure_gnss_source(source);
|
||||
const std::initializer_list<const char *> service_list = {gps_location_socket, "cameraOdometry", "liveCalibration",
|
||||
"carState", "accelerometer", "gyroscope"};
|
||||
|
||||
SubMaster sm(service_list, {}, nullptr, {});
|
||||
SubMaster sm(service_list, {}, nullptr, {gps_location_socket});
|
||||
PubMaster pm({"liveLocationKalman"});
|
||||
|
||||
uint64_t cnt = 0;
|
||||
@@ -729,14 +730,12 @@ int Localizer::locationd_thread() {
|
||||
kj::ArrayPtr<capnp::byte> bytes = this->get_message_bytes(msg_builder, inputsOK, sensorsOK, gpsOK, filterInitialized);
|
||||
pm.send("liveLocationKalman", bytes.begin(), bytes.size());
|
||||
|
||||
// CLEARPILOT: dead code now that gpsOK is permanently false (we don't
|
||||
// subscribe to gpsLocation). Was: write LastGPSPosition once a minute.
|
||||
// if (cnt % 1200 == 0 && gpsOK) {
|
||||
// VectorXd posGeo = this->get_position_geodetic();
|
||||
// std::string lastGPSPosJSON = util::string_format(
|
||||
// "{\"latitude\": %.15f, \"longitude\": %.15f, \"altitude\": %.15f}", posGeo(0), posGeo(1), posGeo(2));
|
||||
// params.putNonBlocking("LastGPSPosition", lastGPSPosJSON);
|
||||
// }
|
||||
if (cnt % 1200 == 0 && gpsOK) { // once a minute
|
||||
VectorXd posGeo = this->get_position_geodetic();
|
||||
std::string lastGPSPosJSON = util::string_format(
|
||||
"{\"latitude\": %.15f, \"longitude\": %.15f, \"altitude\": %.15f}", posGeo(0), posGeo(1), posGeo(2));
|
||||
params.putNonBlocking("LastGPSPosition", lastGPSPosJSON);
|
||||
}
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,19 +56,12 @@ def build(spinner: Spinner, dirty: bool = False, minimal: bool = False) -> None:
|
||||
if scons.returncode == 0:
|
||||
Path('/data/openpilot/prebuilt').touch()
|
||||
|
||||
# CLEARPILOT: update prebuilt spinner if the new build is newer.
|
||||
# Write to a temp path then os.replace so we can swap a binary that's
|
||||
# currently executing (the in-process spinner holds the old one open).
|
||||
# CLEARPILOT: update prebuilt spinner if the new build is newer
|
||||
new_spinner = Path(BASEDIR) / "selfdrive/ui/_spinner"
|
||||
old_spinner = Path(BASEDIR) / "selfdrive/ui/qt/spinner"
|
||||
if new_spinner.exists() and (not old_spinner.exists() or new_spinner.stat().st_mtime > old_spinner.stat().st_mtime):
|
||||
import shutil
|
||||
try:
|
||||
tmp_spinner = old_spinner.with_name(old_spinner.name + ".new")
|
||||
shutil.copy2(str(new_spinner), str(tmp_spinner))
|
||||
os.replace(str(tmp_spinner), str(old_spinner))
|
||||
except OSError as e:
|
||||
print(f"CLP failed to update prebuilt spinner: {e}", file=sys.stderr)
|
||||
shutil.copy2(str(new_spinner), str(old_spinner))
|
||||
|
||||
break
|
||||
|
||||
@@ -85,23 +78,13 @@ def build(spinner: Spinner, dirty: bool = False, minimal: bool = False) -> None:
|
||||
# Show TextWindow
|
||||
spinner.close()
|
||||
if not os.getenv("CI"):
|
||||
msg = "openpilot failed to build\n \n" + error_s
|
||||
# CLEARPILOT: BUILD_ONLY mode shows error on screen but doesn't block
|
||||
t = TextWindow("openpilot failed to build\n \n" + error_s)
|
||||
if os.getenv("BUILD_ONLY"):
|
||||
# CLEARPILOT: BUILD_ONLY mode — spawn the text window fully detached
|
||||
# (own session, /dev/null stdio) so it stays on screen after this
|
||||
# script exits and doesn't hold our stdout/stderr pipes open.
|
||||
print(error_s, file=sys.stderr)
|
||||
devnull = open(os.devnull, 'r+b')
|
||||
subprocess.Popen(
|
||||
["./text", msg],
|
||||
cwd=os.path.join(BASEDIR, "selfdrive", "ui"),
|
||||
stdin=devnull, stdout=devnull, stderr=devnull,
|
||||
start_new_session=True,
|
||||
close_fds=True,
|
||||
)
|
||||
else:
|
||||
with TextWindow(msg) as t:
|
||||
t.wait_for_exit()
|
||||
t.wait_for_exit()
|
||||
t.close()
|
||||
exit(1)
|
||||
|
||||
# enforce max cache size
|
||||
|
||||
@@ -16,7 +16,7 @@ from openpilot.common.text_window import TextWindow
|
||||
from openpilot.common.time import system_time_valid
|
||||
from openpilot.system.hardware import HARDWARE, PC
|
||||
from openpilot.selfdrive.manager.helpers import unblock_stdout, write_onroad_params, save_bootlog
|
||||
from openpilot.selfdrive.manager.process import ensure_running
|
||||
from openpilot.selfdrive.manager.process import ensure_running, init_log_dir, update_log_dir_timestamp, session_log
|
||||
from openpilot.selfdrive.manager.process_config import managed_processes
|
||||
from openpilot.selfdrive.athena.registration import register, UNREGISTERED_DONGLE_ID
|
||||
from openpilot.common.swaglog import cloudlog, add_file_handler
|
||||
@@ -51,16 +51,58 @@ def frogpilot_boot_functions(frogpilot_functions):
|
||||
except Exception as e:
|
||||
print(f"An unexpected error occurred: {e}")
|
||||
|
||||
def cleanup_old_logs(max_age_days=30):
|
||||
"""CLEARPILOT: delete session log directories older than max_age_days."""
|
||||
import shutil
|
||||
log_base = "/data/log2"
|
||||
if not os.path.exists(log_base):
|
||||
return
|
||||
cutoff = time.time() - (max_age_days * 86400)
|
||||
for entry in os.listdir(log_base):
|
||||
if entry == "current":
|
||||
continue
|
||||
path = os.path.join(log_base, entry)
|
||||
if os.path.isdir(path):
|
||||
if os.path.getmtime(path) < cutoff:
|
||||
try:
|
||||
shutil.rmtree(path)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
|
||||
def manager_init(frogpilot_functions) -> None:
|
||||
init_log_dir()
|
||||
cleanup_old_logs()
|
||||
|
||||
frogpilot_boot = threading.Thread(target=frogpilot_boot_functions, args=(frogpilot_functions,))
|
||||
frogpilot_boot.start()
|
||||
|
||||
# CLEARPILOT: skip writing boot logs to /data/media/0/realdata/boot/
|
||||
# save_bootlog()
|
||||
save_bootlog()
|
||||
|
||||
params = Params()
|
||||
params_storage = Params("/persist/params")
|
||||
params.clear_all(ParamKeyType.CLEAR_ON_MANAGER_START)
|
||||
|
||||
# CLEARPILOT: always start with telemetry disabled, VPN enabled (memory params)
|
||||
params_memory = Params("/dev/shm/params")
|
||||
params_memory.put("TelemetryEnabled", "0")
|
||||
params_memory.put("VpnEnabled", "1")
|
||||
params_memory.put("DashcamFrames", "0")
|
||||
params_memory.put("DashcamState", "stopped")
|
||||
params_memory.put("DashcamShutdown", "0")
|
||||
params_memory.put("ModelFps", "20")
|
||||
params_memory.put("ModelStandby", "0")
|
||||
params_memory.put("ShutdownTouchReset", "0")
|
||||
params_memory.put("ModelStandbyTs", "0")
|
||||
params_memory.put("CarIsMetric", "0")
|
||||
params_memory.put("ClearpilotSpeedDisplay", "")
|
||||
params_memory.put("ClearpilotSpeedLimitDisplay", "0")
|
||||
params_memory.put("ClearpilotHasSpeed", "0")
|
||||
params_memory.put("ClearpilotIsMetric", "0")
|
||||
params_memory.put("ClearpilotSpeedUnit", "mph")
|
||||
params_memory.put("ClearpilotCruiseWarning", "")
|
||||
params_memory.put("ClearpilotCruiseWarningSpeed", "")
|
||||
params_memory.put("ClearpilotPlayDing", "0")
|
||||
params.clear_all(ParamKeyType.CLEAR_ON_ONROAD_TRANSITION)
|
||||
params.clear_all(ParamKeyType.CLEAR_ON_OFFROAD_TRANSITION)
|
||||
if is_release_branch():
|
||||
@@ -136,6 +178,7 @@ def manager_init(frogpilot_functions) -> None:
|
||||
("DisableOpenpilotLongitudinal", "0"),
|
||||
("DisableVTSCSmoothing", "0"),
|
||||
("DisengageVolume", "100"),
|
||||
("TelemetryEnabled", "0"),
|
||||
("DragonPilotTune", "0"),
|
||||
("DriverCamera", "0"),
|
||||
("DynamicPathWidth", "0"),
|
||||
@@ -230,6 +273,7 @@ def manager_init(frogpilot_functions) -> None:
|
||||
("ScreenBrightnessOnroad", "101"),
|
||||
("ScreenManagement", "1"),
|
||||
("ScreenRecorder", "1"),
|
||||
("ScreenRecorderDebug", "1"),
|
||||
("ScreenTimeout", "30"),
|
||||
("ScreenTimeoutOnroad", "30"),
|
||||
("SearchInput", "0"),
|
||||
@@ -301,32 +345,6 @@ def manager_init(frogpilot_functions) -> None:
|
||||
else:
|
||||
params_storage.put(k, params.get(k))
|
||||
|
||||
# CLEARPILOT memory-param defaults. /dev/shm/params is on tmpfs so these
|
||||
# reset on every boot anyway; we still set them so first-readers don't see
|
||||
# missing keys before the writer process has spun up.
|
||||
params_memory = Params("/dev/shm/params")
|
||||
for k, v in [
|
||||
("CarIsMetric", "0"),
|
||||
("ClearpilotCruiseWarning", ""),
|
||||
("ClearpilotCruiseWarningSpeed", ""),
|
||||
("ClearpilotHasSpeed", "0"),
|
||||
("ClearpilotIsMetric", "0"),
|
||||
("ClearpilotPlayDing", "0"),
|
||||
("ClearpilotSpeedDisplay", ""),
|
||||
("ClearpilotSpeedLimitDisplay", "0"),
|
||||
("ClearpilotSpeedUnit", "mph"),
|
||||
("DashcamFrames", "0"),
|
||||
("DashcamShutdown", "0"),
|
||||
("DashcamState", "stopped"),
|
||||
("ModelFps", "20"),
|
||||
("ModelStandby", "0"),
|
||||
("ModelStandbyTs", "0"),
|
||||
("ShutdownTouchReset", "0"),
|
||||
("TelemetryEnabled", "0"),
|
||||
("VpnEnabled", "1"),
|
||||
]:
|
||||
params_memory.put(k, v)
|
||||
|
||||
# Create folders needed for msgq
|
||||
try:
|
||||
os.mkdir("/dev/shm")
|
||||
@@ -392,6 +410,7 @@ def manager_thread(frogpilot_functions) -> None:
|
||||
cloudlog.bind(daemon="manager")
|
||||
cloudlog.info("manager start")
|
||||
cloudlog.info({"environ": os.environ})
|
||||
session_log.info("manager starting")
|
||||
|
||||
params = Params()
|
||||
params_memory = Params("/dev/shm/params")
|
||||
@@ -401,6 +420,14 @@ def manager_thread(frogpilot_functions) -> None:
|
||||
ignore += ["manage_athenad", "uploader"]
|
||||
if os.getenv("NOBOARD") is not None:
|
||||
ignore.append("pandad")
|
||||
# CLEARPILOT: bench mode — disable real car processes, enable bench simulator
|
||||
if os.getenv("BENCH_MODE") is not None:
|
||||
ignore += ["pandad", "controlsd", "radard", "plannerd",
|
||||
"calibrationd", "torqued", "paramsd", "locationd", "sensord",
|
||||
"ubloxd", "pigeond", "dmonitoringmodeld", "dmonitoringd",
|
||||
"modeld", "soundd", "loggerd", "micd",
|
||||
"dashcamd"]
|
||||
session_log.info("bench mode enabled")
|
||||
ignore += [x for x in os.getenv("BLOCK", "").split(",") if len(x) > 0]
|
||||
|
||||
sm = messaging.SubMaster(['deviceState', 'carParams'], poll='deviceState')
|
||||
@@ -422,6 +449,7 @@ def manager_thread(frogpilot_functions) -> None:
|
||||
|
||||
if started and not started_prev:
|
||||
params.clear_all(ParamKeyType.CLEAR_ON_ONROAD_TRANSITION)
|
||||
session_log.info("onroad transition")
|
||||
|
||||
if openpilot_crashed:
|
||||
os.remove(os.path.join(sentry.CRASHES_DIR, 'error.txt'))
|
||||
@@ -429,6 +457,7 @@ def manager_thread(frogpilot_functions) -> None:
|
||||
elif not started and started_prev:
|
||||
params.clear_all(ParamKeyType.CLEAR_ON_OFFROAD_TRANSITION)
|
||||
params_memory.clear_all(ParamKeyType.CLEAR_ON_OFFROAD_TRANSITION)
|
||||
session_log.info("offroad transition")
|
||||
|
||||
# update onroad params, which drives boardd's safety setter thread
|
||||
if started != started_prev:
|
||||
@@ -438,6 +467,9 @@ def manager_thread(frogpilot_functions) -> None:
|
||||
|
||||
ensure_running(managed_processes.values(), started, params=params, CP=sm['carParams'], not_run=ignore)
|
||||
|
||||
# CLEARPILOT: rename log directory once system time is valid
|
||||
update_log_dir_timestamp()
|
||||
|
||||
running = ' '.join("{}{}\u001b[0m".format("\u001b[32m" if p.proc.is_alive() else "\u001b[31m", p.name)
|
||||
for p in managed_processes.values() if p.proc)
|
||||
print(running)
|
||||
@@ -455,6 +487,7 @@ def manager_thread(frogpilot_functions) -> None:
|
||||
shutdown = True
|
||||
params.put("LastManagerExitReason", f"{param} {datetime.datetime.now()}")
|
||||
cloudlog.warning(f"Shutting down manager - {param} set")
|
||||
session_log.info("manager shutting down: %s", param)
|
||||
|
||||
if shutdown:
|
||||
break
|
||||
@@ -506,6 +539,7 @@ if __name__ == "__main__":
|
||||
except Exception:
|
||||
add_file_handler(cloudlog)
|
||||
cloudlog.exception("Manager failed to start")
|
||||
session_log.critical("manager failed to start: %s", traceback.format_exc())
|
||||
|
||||
try:
|
||||
managed_processes['ui'].stop()
|
||||
|
||||
@@ -2,6 +2,7 @@ import importlib
|
||||
import os
|
||||
import signal
|
||||
import struct
|
||||
import sys
|
||||
import datetime
|
||||
import time
|
||||
import subprocess
|
||||
@@ -17,17 +18,117 @@ import openpilot.selfdrive.sentry as sentry
|
||||
from openpilot.common.basedir import BASEDIR
|
||||
from openpilot.common.params import Params
|
||||
from openpilot.common.swaglog import cloudlog
|
||||
from openpilot.common.time import system_time_valid
|
||||
|
||||
WATCHDOG_FN = "/dev/shm/wd_"
|
||||
ENABLE_WATCHDOG = os.getenv("NO_WATCHDOG") is None
|
||||
|
||||
timestamp = datetime.datetime.now().strftime("%Y-%m-%d-%H-%M-%S")
|
||||
_log_dir = f"/data/log2/{timestamp}"
|
||||
os.makedirs(_log_dir, exist_ok=True)
|
||||
# CLEARPILOT: logging directory and session log
|
||||
# init_log_dir() must be called once from manager_init() before any process starts.
|
||||
# Until then, _log_dir and session_log are usable but write to a NullHandler.
|
||||
import logging
|
||||
|
||||
_log_dir = "/data/log2/current"
|
||||
_time_resolved = False
|
||||
_session_handler = None
|
||||
|
||||
session_log = logging.getLogger("clearpilot.session")
|
||||
session_log.setLevel(logging.DEBUG)
|
||||
session_log.addHandler(logging.NullHandler())
|
||||
|
||||
|
||||
def init_log_dir():
|
||||
"""Create /data/log2/current as a real directory for this session.
|
||||
Called once from manager_init(). Previous current (if a real dir) is
|
||||
renamed to a timestamp or boot-monotonic name before we create a fresh one."""
|
||||
global _log_dir, _time_resolved, _session_handler
|
||||
|
||||
log_base = "/data/log2"
|
||||
current = os.path.join(log_base, "current")
|
||||
os.makedirs(log_base, exist_ok=True)
|
||||
|
||||
# If 'current' is a symlink, just remove the symlink
|
||||
if os.path.islink(current):
|
||||
os.unlink(current)
|
||||
# If 'current' is a real directory (leftover from previous session that
|
||||
# never got time-resolved), rename it out of the way
|
||||
elif os.path.isdir(current):
|
||||
# Use mtime of session.log (or the dir itself) for the rename
|
||||
session_file = os.path.join(current, "session.log")
|
||||
mtime = os.path.getmtime(session_file) if os.path.exists(session_file) else os.path.getmtime(current)
|
||||
ts = datetime.datetime.fromtimestamp(mtime).strftime('%Y-%m-%d-%H-%M-%S')
|
||||
dest = os.path.join(log_base, ts)
|
||||
# Avoid collision
|
||||
if os.path.exists(dest):
|
||||
dest = dest + f"-{int(time.monotonic())}"
|
||||
try:
|
||||
os.rename(current, dest)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
os.makedirs(current, exist_ok=True)
|
||||
_log_dir = current
|
||||
_time_resolved = False
|
||||
|
||||
# Set up session log file handler
|
||||
_session_handler = logging.FileHandler(os.path.join(_log_dir, "session.log"))
|
||||
_session_handler.setFormatter(logging.Formatter("%(asctime)s %(levelname)s %(message)s"))
|
||||
# Remove NullHandler and add file handler
|
||||
session_log.handlers.clear()
|
||||
session_log.addHandler(_session_handler)
|
||||
|
||||
session_log.info("session started, log dir: %s", _log_dir)
|
||||
|
||||
|
||||
def update_log_dir_timestamp():
|
||||
"""Rename /data/log2/current to a real timestamp and replace with a symlink
|
||||
once system time is valid."""
|
||||
global _log_dir, _time_resolved, _session_handler
|
||||
if _time_resolved:
|
||||
return
|
||||
if not system_time_valid():
|
||||
return
|
||||
|
||||
log_base = "/data/log2"
|
||||
current = os.path.join(log_base, "current")
|
||||
ts_name = datetime.datetime.now().strftime('%Y-%m-%d-%H-%M-%S')
|
||||
new_dir = os.path.join(log_base, ts_name)
|
||||
|
||||
try:
|
||||
os.rename(current, new_dir)
|
||||
# Create symlink: current -> YYYY-MM-DD-HH-MM-SS
|
||||
os.symlink(ts_name, current)
|
||||
_log_dir = new_dir
|
||||
_time_resolved = True
|
||||
# Re-point session log handler (open files follow the inode, but
|
||||
# new opens should go through the symlink — update handler for clarity)
|
||||
session_log.removeHandler(_session_handler)
|
||||
_session_handler.close()
|
||||
_session_handler = logging.FileHandler(os.path.join(_log_dir, "session.log"))
|
||||
_session_handler.setFormatter(logging.Formatter("%(asctime)s %(levelname)s %(message)s"))
|
||||
session_log.addHandler(_session_handler)
|
||||
session_log.info("log directory renamed to %s", _log_dir)
|
||||
|
||||
# Signal via param that the directory has been time-resolved
|
||||
try:
|
||||
from openpilot.common.params import Params
|
||||
Params().put("LogDirInitialized", "1")
|
||||
except Exception:
|
||||
pass
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
|
||||
|
||||
def launcher(proc: str, name: str) -> None:
|
||||
def launcher(proc: str, name: str, log_path: str) -> None:
|
||||
# CLEARPILOT: redirect stderr to per-process log file
|
||||
try:
|
||||
log_file = open(log_path, 'a')
|
||||
os.dup2(log_file.fileno(), sys.stderr.fileno())
|
||||
os.dup2(log_file.fileno(), sys.stdout.fileno())
|
||||
except Exception as e:
|
||||
print(f"CLEARPILOT: stderr redirect failed for {name}: {e}", file=sys.stderr)
|
||||
|
||||
try:
|
||||
# import the process
|
||||
mod = importlib.import_module(proc)
|
||||
@@ -53,9 +154,17 @@ def launcher(proc: str, name: str) -> None:
|
||||
raise
|
||||
|
||||
|
||||
def nativelauncher(pargs: list[str], cwd: str, name: str) -> None:
|
||||
def nativelauncher(pargs: list[str], cwd: str, name: str, log_path: str) -> None:
|
||||
os.environ['MANAGER_DAEMON'] = name
|
||||
|
||||
# CLEARPILOT: redirect stderr and stdout to per-process log file
|
||||
try:
|
||||
log_file = open(log_path, 'a')
|
||||
os.dup2(log_file.fileno(), sys.stderr.fileno())
|
||||
os.dup2(log_file.fileno(), sys.stdout.fileno())
|
||||
except Exception as e:
|
||||
print(f"CLEARPILOT: stderr redirect failed for {name}: {e}", file=sys.stderr)
|
||||
|
||||
# exec the process
|
||||
os.chdir(cwd)
|
||||
os.execvp(pargs[0], pargs)
|
||||
@@ -110,6 +219,7 @@ class ManagerProcess(ABC):
|
||||
if dt > self.watchdog_max_dt:
|
||||
if (self.watchdog_seen or self.always_watchdog and self.proc.exitcode is not None) and ENABLE_WATCHDOG:
|
||||
cloudlog.error(f"Watchdog timeout for {self.name} (exitcode {self.proc.exitcode}) restarting ({started=})")
|
||||
session_log.warning("watchdog timeout for %s (exitcode %s), restarting", self.name, self.proc.exitcode)
|
||||
self.restart()
|
||||
else:
|
||||
self.watchdog_seen = True
|
||||
@@ -139,6 +249,10 @@ class ManagerProcess(ABC):
|
||||
|
||||
ret = self.proc.exitcode
|
||||
cloudlog.info(f"{self.name} is dead with {ret}")
|
||||
if ret is not None and ret != 0:
|
||||
session_log.error("process %s died with exit code %s", self.name, ret)
|
||||
elif ret == 0:
|
||||
session_log.info("process %s stopped (exit 0)", self.name)
|
||||
|
||||
if self.proc.exitcode is not None:
|
||||
self.shutting_down = False
|
||||
@@ -198,9 +312,13 @@ class NativeProcess(ManagerProcess):
|
||||
global _log_dir
|
||||
log_path = _log_dir+"/"+self.name+".log"
|
||||
|
||||
# CLEARPILOT: ensure log file exists even if child redirect fails
|
||||
open(log_path, 'a').close()
|
||||
|
||||
cwd = os.path.join(BASEDIR, self.cwd)
|
||||
cloudlog.info(f"starting process {self.name}")
|
||||
self.proc = Process(name=self.name, target=self.launcher, args=(self.cmdline, cwd, self.name))
|
||||
session_log.info("starting %s", self.name)
|
||||
self.proc = Process(name=self.name, target=self.launcher, args=(self.cmdline, cwd, self.name, log_path))
|
||||
self.proc.start()
|
||||
self.watchdog_seen = False
|
||||
self.shutting_down = False
|
||||
@@ -231,8 +349,12 @@ class PythonProcess(ManagerProcess):
|
||||
|
||||
global _log_dir
|
||||
log_path = _log_dir+"/"+self.name+".log"
|
||||
# CLEARPILOT: ensure log file exists even if child redirect fails
|
||||
open(log_path, 'a').close()
|
||||
|
||||
cloudlog.info(f"starting python {self.module}")
|
||||
self.proc = Process(name=self.name, target=self.launcher, args=(self.module, self.name))
|
||||
session_log.info("starting %s", self.name)
|
||||
self.proc = Process(name=self.name, target=self.launcher, args=(self.module, self.name, log_path))
|
||||
self.proc.start()
|
||||
self.watchdog_seen = False
|
||||
self.shutting_down = False
|
||||
@@ -276,6 +398,7 @@ class DaemonProcess(ManagerProcess):
|
||||
pass
|
||||
|
||||
cloudlog.info(f"starting daemon {self.name}")
|
||||
session_log.info("starting daemon %s", self.name)
|
||||
proc = subprocess.Popen(['python', '-m', self.module],
|
||||
stdin=open('/dev/null'),
|
||||
stdout=open(log_path, 'a'),
|
||||
|
||||
@@ -7,6 +7,7 @@ from openpilot.selfdrive.manager.process import PythonProcess, NativeProcess, Da
|
||||
|
||||
|
||||
WEBCAM = os.getenv("USE_WEBCAM") is not None
|
||||
BENCH_MODE = os.getenv("BENCH_MODE") is not None
|
||||
|
||||
def driverview(started: bool, params: Params, CP: car.CarParams) -> bool:
|
||||
return started or params.get_bool("IsDriverViewEnabled")
|
||||
@@ -51,11 +52,13 @@ def allow_uploads(started, params, CP: car.CarParams) -> bool:
|
||||
allow_uploads = not (params.get_bool("DeviceManagement") and params.get_bool("NoUploads"))
|
||||
return allow_uploads
|
||||
|
||||
# ClearPilot functions
|
||||
def dashcam_should_run(started, params, CP: car.CarParams) -> bool:
|
||||
return True # CLEARPILOT: dashcamd manages its own trip lifecycle
|
||||
|
||||
procs = [
|
||||
DaemonProcess("manage_athenad", "selfdrive.athena.manage_athenad", "AthenadPid"),
|
||||
|
||||
# CLEARPILOT: camerad runs always (was driverview) so dashcamd can record
|
||||
# the moment ignition+drive-gear arrives without waiting for camera startup.
|
||||
NativeProcess("camerad", "system/camerad", ["./camerad"], always_run),
|
||||
NativeProcess("logcatd", "system/logcatd", ["./logcatd"], allow_logging),
|
||||
NativeProcess("proclogd", "system/proclogd", ["./proclogd"], allow_logging),
|
||||
@@ -64,12 +67,10 @@ procs = [
|
||||
PythonProcess("timed", "system.timed", always_run, enabled=not PC),
|
||||
|
||||
PythonProcess("dmonitoringmodeld", "selfdrive.modeld.dmonitoringmodeld", driverview, enabled=(not PC or WEBCAM)),
|
||||
# CLEARPILOT: disabled segment + camera logging — no rlog/qlog or .hevc
|
||||
# files written to /data/media/0/realdata. We don't use comma's upload/
|
||||
# replay pipeline. Keep deleter running for any leftover cleanup.
|
||||
# CLEARPILOT: disabled video encoding (camera .hevc files) — CAN/sensor logs still recorded via loggerd
|
||||
# NativeProcess("encoderd", "system/loggerd", ["./encoderd"], allow_logging),
|
||||
# NativeProcess("stream_encoderd", "system/loggerd", ["./encoderd", "--stream"], notcar),
|
||||
# NativeProcess("loggerd", "system/loggerd", ["./loggerd"], allow_logging),
|
||||
NativeProcess("loggerd", "system/loggerd", ["./loggerd"], allow_logging),
|
||||
NativeProcess("modeld", "selfdrive/modeld", ["./modeld"], only_onroad),
|
||||
#NativeProcess("mapsd", "selfdrive/navd", ["./mapsd"], only_onroad),
|
||||
#PythonProcess("navmodeld", "selfdrive.modeld.navmodeld", only_onroad),
|
||||
@@ -83,17 +84,8 @@ procs = [
|
||||
PythonProcess("controlsd", "selfdrive.controls.controlsd", only_onroad),
|
||||
PythonProcess("deleter", "system.loggerd.deleter", always_run),
|
||||
PythonProcess("dmonitoringd", "selfdrive.monitoring.dmonitoringd", driverview, enabled=(not PC or WEBCAM)),
|
||||
# PythonProcess("qcomgpsd", "system.qcomgpsd.qcomgpsd", qcomgps, enabled=TICI), # Fixme
|
||||
# CLEARPILOT: replacement for qcomgpsd (whose diag interface is broken on this device).
|
||||
# Uses Quectel modem AT commands via mmcli. Self-driving does NOT consume this; locationd
|
||||
# is patched to skip gpsLocation. Used only for system clock + UI speed + dashcam metadata.
|
||||
# PythonProcess("qcomgpsd", "system.qcomgpsd.qcomgpsd", qcomgps, enabled=TICI),
|
||||
PythonProcess("gpsd", "system.clearpilot.gpsd", qcomgps, enabled=TICI),
|
||||
# CLEARPILOT: speed/cruise overlay daemon. Reads gpsLocation + carState, writes display
|
||||
# params for the onroad UI; asserts ClearpilotPlayDing on speed-limit warning transitions.
|
||||
PythonProcess("speed_logicd", "selfdrive.clearpilot.speed_logicd", only_onroad),
|
||||
# CLEARPILOT: dashcam — VisionIPC frames → OMX H.264 → 3-min MP4 segments + SRT
|
||||
# GPS subtitles in /data/media/0/videos/. Manages its own trip lifecycle.
|
||||
NativeProcess("dashcamd", "selfdrive/clearpilot", ["./dashcamd"], always_run),
|
||||
# PythonProcess("ugpsd", "system.ugpsd", only_onroad, enabled=TICI),
|
||||
#PythonProcess("navd", "selfdrive.navd.navd", only_onroad),
|
||||
PythonProcess("pandad", "selfdrive.boardd.pandad", always_run),
|
||||
@@ -116,6 +108,11 @@ procs = [
|
||||
# FrogPilot processes
|
||||
PythonProcess("fleet_manager", "selfdrive.frogpilot.fleetmanager.fleet_manager", always_run),
|
||||
PythonProcess("frogpilot_process", "selfdrive.frogpilot.frogpilot_process", always_run),
|
||||
|
||||
# ClearPilot processes
|
||||
NativeProcess("dashcamd", "selfdrive/clearpilot", ["./dashcamd"], dashcam_should_run),
|
||||
PythonProcess("telemetryd", "selfdrive.clearpilot.telemetryd", always_run),
|
||||
PythonProcess("bench_onroad", "selfdrive.clearpilot.bench_onroad", always_run, enabled=BENCH_MODE),
|
||||
]
|
||||
|
||||
managed_processes = {p.name: p for p in procs}
|
||||
@@ -134,11 +134,15 @@ def main(demo=False):
|
||||
setproctitle(PROCESS_NAME)
|
||||
config_realtime_process(7, 54)
|
||||
|
||||
import time as _time
|
||||
cloudlog.warning("setting up CL context")
|
||||
_t0 = _time.monotonic()
|
||||
cl_context = CLContext()
|
||||
cloudlog.warning("CL context ready; loading model")
|
||||
_t1 = _time.monotonic()
|
||||
cloudlog.warning("CL context ready in %.3fs; loading model", _t1 - _t0)
|
||||
model = ModelState(cl_context)
|
||||
cloudlog.warning("models loaded, modeld starting")
|
||||
_t2 = _time.monotonic()
|
||||
cloudlog.warning("model loaded in %.3fs (total init %.3fs), modeld starting", _t2 - _t1, _t2 - _t0)
|
||||
|
||||
# visionipc clients
|
||||
while True:
|
||||
@@ -233,6 +237,12 @@ def main(demo=False):
|
||||
meta_extra = meta_main
|
||||
|
||||
sm.update(0)
|
||||
|
||||
# CLEARPILOT: constant 20fps. Variable-rate + standby logic removed — the
|
||||
# variable-rate path caused freq_ok cascades in downstream consumers
|
||||
# (calibrationd/locationd/paramsd). Running at the camera's native rate is
|
||||
# simpler and keeps the full-stack localization chain happy.
|
||||
|
||||
desire = DH.desire
|
||||
is_rhd = sm["driverMonitoringState"].isRHD
|
||||
frame_id = sm["roadCameraState"].frameId
|
||||
|
||||
@@ -21,6 +21,7 @@ def dmonitoringd_thread():
|
||||
|
||||
v_cruise_last = 0
|
||||
driver_engaged = False
|
||||
dbg_prev_valid = True # CLEARPILOT: track valid transitions
|
||||
|
||||
# 10Hz <- dmonitoringmodeld
|
||||
while True:
|
||||
@@ -43,7 +44,18 @@ def dmonitoringd_thread():
|
||||
# Get data from dmonitoringmodeld
|
||||
events = Events()
|
||||
|
||||
if sm.all_checks() and len(sm['liveCalibration'].rpyCalib):
|
||||
# CLEARPILOT: narrow update_states gate. The original sm.all_checks() also
|
||||
# required modelV2 fresh (stops at standstill in two-state modeld) and
|
||||
# liveCalibration.valid (calibrationd cascades its own freq_ok to valid, which
|
||||
# flaps). Both made DM freeze pose → face_detected stuck False → awareness
|
||||
# decayed to 0 within 6s of engagement. Narrow the gate to the subs
|
||||
# update_states actually reads, and only to alive+valid (skip freq_ok and
|
||||
# skip liveCalibration.valid). rpyCalib presence is sufficient to know
|
||||
# calibration has produced output.
|
||||
if (sm.alive['driverStateV2'] and sm.valid['driverStateV2'] and
|
||||
sm.alive['carState'] and sm.valid['carState'] and
|
||||
sm.alive['controlsState'] and sm.valid['controlsState'] and
|
||||
sm.alive['liveCalibration'] and len(sm['liveCalibration'].rpyCalib) > 0):
|
||||
driver_status.update_states(sm['driverStateV2'], sm['liveCalibration'].rpyCalib, sm['carState'].vEgo, sm['controlsState'].enabled)
|
||||
|
||||
# Block engaging after max number of distrations
|
||||
@@ -54,8 +66,16 @@ def dmonitoringd_thread():
|
||||
# Update events from driver state
|
||||
driver_status.update_events(events, driver_engaged, sm['controlsState'].enabled, sm['carState'].standstill)
|
||||
|
||||
# CLEARPILOT: log on transition to invalid so we can see which sub caused the cascade
|
||||
dm_valid = sm.all_checks()
|
||||
if dm_valid != dbg_prev_valid and not dm_valid:
|
||||
import sys
|
||||
bad = [s for s in sm.alive if not (sm.alive[s] and sm.valid[s] and sm.freq_ok.get(s, True))]
|
||||
details = [f"{s}(a={sm.alive[s]},v={sm.valid[s]},f={sm.freq_ok[s]})" for s in bad]
|
||||
print(f"CLP driverMonitoringState valid=False: {' '.join(details)}", file=sys.stderr, flush=True)
|
||||
dbg_prev_valid = dm_valid
|
||||
# build driverMonitoringState packet
|
||||
dat = messaging.new_message('driverMonitoringState', valid=sm.all_checks())
|
||||
dat = messaging.new_message('driverMonitoringState', valid=dm_valid)
|
||||
dat.driverMonitoringState = {
|
||||
"events": events.to_msg(),
|
||||
"faceDetected": driver_status.face_detected,
|
||||
|
||||
@@ -6,9 +6,11 @@ from openpilot.common.numpy_fast import interp
|
||||
from openpilot.common.swaglog import cloudlog
|
||||
from openpilot.selfdrive.controls.lib.pid import PIDController
|
||||
|
||||
|
||||
class BaseFanController(ABC):
|
||||
@abstractmethod
|
||||
def update(self, cur_temp: float, ignition: bool) -> int:
|
||||
def update(self, cur_temp: float, ignition: bool, standstill: bool = False,
|
||||
is_parked: bool = True, cruise_engaged: bool = False) -> int:
|
||||
pass
|
||||
|
||||
|
||||
@@ -20,9 +22,27 @@ class TiciFanController(BaseFanController):
|
||||
self.last_ignition = False
|
||||
self.controller = PIDController(k_p=0, k_i=4e-3, k_f=1, rate=(1 / DT_TRML))
|
||||
|
||||
def update(self, cur_temp: float, ignition: bool) -> int:
|
||||
self.controller.neg_limit = -(100 if ignition else 30)
|
||||
self.controller.pos_limit = -(30 if ignition else 0)
|
||||
def update(self, cur_temp: float, ignition: bool, standstill: bool = False,
|
||||
is_parked: bool = True, cruise_engaged: bool = False) -> int:
|
||||
# CLEARPILOT fan range rules:
|
||||
# parked → 0-100% (full, no floor)
|
||||
# in drive + cruise engaged (any speed, inc standstill) → 30-100%
|
||||
# in drive + cruise off + standstill → 10-100%
|
||||
# in drive + cruise off + moving → 30-100%
|
||||
# In the PID output, neg_limit is how negative it can go (= max fan as %),
|
||||
# pos_limit is how positive (= negative of min fan %).
|
||||
if is_parked:
|
||||
self.controller.neg_limit = -100
|
||||
self.controller.pos_limit = 0
|
||||
elif cruise_engaged:
|
||||
self.controller.neg_limit = -100
|
||||
self.controller.pos_limit = -30
|
||||
elif standstill:
|
||||
self.controller.neg_limit = -100
|
||||
self.controller.pos_limit = -10
|
||||
else:
|
||||
self.controller.neg_limit = -100
|
||||
self.controller.pos_limit = -30
|
||||
|
||||
if ignition != self.last_ignition:
|
||||
self.controller.reset()
|
||||
|
||||
@@ -36,12 +36,9 @@ class PowerMonitoring:
|
||||
# Reset capacity if it's low
|
||||
self.car_battery_capacity_uWh = max((CAR_BATTERY_CAPACITY_uWh / 10), int(car_battery_capacity_uWh))
|
||||
|
||||
# FrogPilot variables
|
||||
device_management = self.params.get_bool("DeviceManagement")
|
||||
device_shutdown_setting = self.params.get_int("DeviceShutdown") if device_management else 33
|
||||
# If the toggle is set for < 1 hour, configure by 15 minute increments
|
||||
self.device_shutdown_time = (device_shutdown_setting - 3) * 3600 if device_shutdown_setting >= 4 else device_shutdown_setting * (60 * 15)
|
||||
self.low_voltage_shutdown = self.params.get_float("LowVoltageShutdown") if device_management else VBATT_PAUSE_CHARGING
|
||||
# CLEARPILOT: hardcoded 30 minute shutdown timer (not user-configurable)
|
||||
self.device_shutdown_time = 1800
|
||||
self.low_voltage_shutdown = VBATT_PAUSE_CHARGING
|
||||
|
||||
# Calculation tick
|
||||
def calculate(self, voltage: int | None, ignition: bool):
|
||||
@@ -125,7 +122,13 @@ class PowerMonitoring:
|
||||
offroad_time > VOLTAGE_SHUTDOWN_MIN_OFFROAD_TIME_S)
|
||||
should_shutdown |= offroad_time > self.device_shutdown_time
|
||||
should_shutdown |= low_voltage_shutdown
|
||||
should_shutdown |= (self.car_battery_capacity_uWh <= 0)
|
||||
# CLEARPILOT: removed `car_battery_capacity_uWh <= 0` trigger. That's a virtual
|
||||
# capacity counter floor-limited to 3e6 µWh on boot which drains in ~12 min at
|
||||
# typical device power. With a short drive that doesn't fully recharge (charges
|
||||
# at 45W, cap 30e6 µWh = 36 min to full), a quick store stop could trip shutdown
|
||||
# well before the intended 30-min idle timer. The real protection we want here
|
||||
# is the car battery voltage check (kept above) — the virtual counter is now
|
||||
# retained only for telemetry.
|
||||
should_shutdown &= not ignition
|
||||
should_shutdown &= (not self.params.get_bool("DisablePowerDown"))
|
||||
should_shutdown &= in_car
|
||||
|
||||
@@ -10,7 +10,7 @@ from pathlib import Path
|
||||
import psutil
|
||||
|
||||
import cereal.messaging as messaging
|
||||
from cereal import log
|
||||
from cereal import car, log
|
||||
from cereal.services import SERVICE_LIST
|
||||
from openpilot.common.dict_helpers import strip_deprecated_keys
|
||||
from openpilot.common.filter_simple import FirstOrderFilter
|
||||
@@ -164,7 +164,7 @@ def hw_state_thread(end_event, hw_queue):
|
||||
|
||||
def thermald_thread(end_event, hw_queue) -> None:
|
||||
pm = messaging.PubMaster(['deviceState', 'frogpilotDeviceState'])
|
||||
sm = messaging.SubMaster(["peripheralState", "gpsLocationExternal", "controlsState", "pandaStates"], poll="pandaStates")
|
||||
sm = messaging.SubMaster(["peripheralState", "gpsLocationExternal", "controlsState", "pandaStates", "carState"], poll="pandaStates")
|
||||
|
||||
count = 0
|
||||
|
||||
@@ -197,7 +197,9 @@ def thermald_thread(end_event, hw_queue) -> None:
|
||||
engaged_prev = False
|
||||
|
||||
params = Params()
|
||||
params_memory = Params("/dev/shm/params")
|
||||
power_monitor = PowerMonitoring()
|
||||
last_touch_reset = "0" # CLEARPILOT: track last seen touch reset value
|
||||
|
||||
HARDWARE.initialize_hardware()
|
||||
thermal_config = HARDWARE.get_thermal_config()
|
||||
@@ -290,7 +292,18 @@ def thermald_thread(end_event, hw_queue) -> None:
|
||||
msg.deviceState.maxTempC = all_comp_temp
|
||||
|
||||
if fan_controller is not None:
|
||||
msg.deviceState.fanSpeedPercentDesired = fan_controller.update(all_comp_temp, onroad_conditions["ignition"])
|
||||
# CLEARPILOT: fan rules based on gear (parked vs drive) and cruise-engaged state
|
||||
if sm.seen['carState']:
|
||||
cs = sm['carState']
|
||||
standstill = cs.standstill
|
||||
is_parked = cs.gearShifter == car.CarState.GearShifter.park
|
||||
else:
|
||||
standstill = False
|
||||
is_parked = True # default safe: assume parked, no fan floor
|
||||
cruise_engaged = sm['controlsState'].enabled if sm.seen['controlsState'] else False
|
||||
msg.deviceState.fanSpeedPercentDesired = fan_controller.update(
|
||||
all_comp_temp, onroad_conditions["ignition"], standstill,
|
||||
is_parked=is_parked, cruise_engaged=cruise_engaged)
|
||||
|
||||
is_offroad_for_5_min = (started_ts is None) and ((not started_seen) or (off_ts is None) or (time.monotonic() - off_ts > 60 * 5))
|
||||
if is_offroad_for_5_min and offroad_comp_temp > OFFROAD_DANGER_TEMP:
|
||||
@@ -408,9 +421,26 @@ def thermald_thread(end_event, hw_queue) -> None:
|
||||
statlog.sample("som_power_draw", som_power_draw)
|
||||
msg.deviceState.somPowerDrawW = som_power_draw
|
||||
|
||||
# CLEARPILOT: screen tap resets shutdown timer (off_ts) while offroad
|
||||
touch_reset = params_memory.get("ShutdownTouchReset")
|
||||
if touch_reset is not None and touch_reset != last_touch_reset and off_ts is not None:
|
||||
off_ts = time.monotonic()
|
||||
cloudlog.info("shutdown timer reset by screen touch")
|
||||
last_touch_reset = touch_reset
|
||||
|
||||
# Check if we need to shut down
|
||||
if power_monitor.should_shutdown(onroad_conditions["ignition"], in_car, off_ts, started_seen):
|
||||
cloudlog.warning(f"shutting device down, offroad since {off_ts}")
|
||||
# CLEARPILOT: signal dashcamd to close recording gracefully before power-off
|
||||
params_memory.put_bool("DashcamShutdown", True)
|
||||
deadline = time.monotonic() + 15.0
|
||||
while time.monotonic() < deadline:
|
||||
if not params_memory.get_bool("DashcamShutdown"):
|
||||
cloudlog.info("dashcamd shutdown ack received")
|
||||
break
|
||||
time.sleep(0.5)
|
||||
else:
|
||||
cloudlog.warning("dashcamd shutdown ack timeout, proceeding")
|
||||
params.put_bool("DoShutdown", True)
|
||||
|
||||
msg.deviceState.started = started_ts is not None
|
||||
|
||||
@@ -79,8 +79,8 @@ void OnroadWindow::updateState(const UIState &s) {
|
||||
nvg->update(); // CLEARPILOT: force repaint every frame for HUD elements
|
||||
|
||||
QColor bgColor = bg_colors[s.status];
|
||||
// CLEARPILOT: read from paramsMemory; controlsd writes "no_lat_lane_change".
|
||||
if (paramsMemory.getBool("no_lat_lane_change")) {
|
||||
// CLEARPILOT: read from frogpilotCarControl cereal message (was paramsMemory)
|
||||
if ((*s.sm)["frogpilotCarControl"].getFrogpilotCarControl().getNoLatLaneChange()) {
|
||||
bgColor = bg_colors[STATUS_DISENGAGED];
|
||||
}
|
||||
|
||||
@@ -851,15 +851,8 @@ void AnnotatedCameraWidget::drawLaneLines(QPainter &painter, const UIState *s) {
|
||||
// CLEARPILOT: nightrider mode — outline only, no fill
|
||||
bool outlineOnly = nightriderMode;
|
||||
|
||||
// CLEARPILOT: read here so the nightrider hide-when-disengaged check below
|
||||
// can let lane-change frames through (controlsd forces edgeColor to
|
||||
// STATUS_DISENGAGED while no_lat_lane_change is true).
|
||||
bool is_no_lat_lane_change = paramsMemory.getBool("no_lat_lane_change");
|
||||
|
||||
// CLEARPILOT: in nightrider mode, hide all lines when not engaged — except
|
||||
// during a lane change, where we still want lane lines + road edges drawn
|
||||
// alongside the yellow lane-change outline.
|
||||
if (outlineOnly && edgeColor == bg_colors[STATUS_DISENGAGED] && !is_no_lat_lane_change) {
|
||||
// CLEARPILOT: in nightrider mode, hide all lines when not engaged
|
||||
if (outlineOnly && edgeColor == bg_colors[STATUS_DISENGAGED]) {
|
||||
painter.restore();
|
||||
return;
|
||||
}
|
||||
@@ -900,7 +893,8 @@ void AnnotatedCameraWidget::drawLaneLines(QPainter &painter, const UIState *s) {
|
||||
// paint center lane path
|
||||
// QColor bg_colors[CHANGE_LANE_PATH_COLOR];
|
||||
|
||||
// is_no_lat_lane_change was read at the top of this function.
|
||||
// CLEARPILOT: read from frogpilotCarControl cereal message (was paramsMemory)
|
||||
bool is_no_lat_lane_change = sm["frogpilotCarControl"].getFrogpilotCarControl().getNoLatLaneChange();
|
||||
|
||||
QColor center_lane_color;
|
||||
|
||||
@@ -961,17 +955,11 @@ void AnnotatedCameraWidget::drawLaneLines(QPainter &painter, const UIState *s) {
|
||||
}
|
||||
|
||||
if (outlineOnly) {
|
||||
// CLEARPILOT: in nightrider, the tire path is rendered as an outline.
|
||||
// - Normal: light blue 3px (status-neutral guide)
|
||||
// - Lane change: 4px outline of CHANGE_LANE_PATH_COLOR (the same yellow
|
||||
// used to fill the polygon in normal mode), so the nightrider lane
|
||||
// change reads as the same visual cue, just hollow.
|
||||
if (is_no_lat_lane_change) {
|
||||
painter.setPen(QPen(bg_colors[CHANGE_LANE_PATH_COLOR], 4));
|
||||
} else {
|
||||
QColor lightBlue(153, 204, 255, 220); // #99CCFF light blue, mostly opaque
|
||||
painter.setPen(QPen(lightBlue, 3));
|
||||
}
|
||||
// CLEARPILOT: in nightrider, the tire path outline is light blue at 3px.
|
||||
// Uses a fixed light-blue instead of center_lane_color (which is status-tinted) so
|
||||
// the path reads as a neutral guide, not as engagement/status feedback.
|
||||
QColor lightBlue(153, 204, 255, 220); // #99CCFF light blue, mostly opaque
|
||||
painter.setPen(QPen(lightBlue, 3));
|
||||
painter.setBrush(Qt::NoBrush);
|
||||
} else {
|
||||
painter.setPen(Qt::NoPen);
|
||||
|
||||
@@ -93,8 +93,7 @@ class Soundd:
|
||||
|
||||
self.spl_filter_weighted = FirstOrderFilter(0, 2.5, FILTER_DT, initialized=False)
|
||||
|
||||
# CLEARPILOT ding (plays independently of alerts; triggered by writers
|
||||
# setting ClearpilotPlayDing="1" in /dev/shm/params).
|
||||
# ClearPilot ding (plays independently of alerts)
|
||||
self.ding_sound = None
|
||||
self.ding_frame = 0
|
||||
self.ding_playing = False
|
||||
@@ -102,7 +101,6 @@ class Soundd:
|
||||
self._load_ding()
|
||||
|
||||
def _load_ding(self):
|
||||
import sys
|
||||
ding_path = BASEDIR + "/selfdrive/clearpilot/sounds/ding.wav"
|
||||
try:
|
||||
wavefile = wave.open(ding_path, 'r')
|
||||
@@ -111,9 +109,9 @@ class Soundd:
|
||||
assert wavefile.getframerate() == SAMPLE_RATE
|
||||
length = wavefile.getnframes()
|
||||
self.ding_sound = np.frombuffer(wavefile.readframes(length), dtype=np.int16).astype(np.float32) / (2**16/2)
|
||||
print(f"CLP soundd: ClearPilot ding loaded: {length} frames", file=sys.stderr, flush=True)
|
||||
cloudlog.info(f"ClearPilot ding loaded: {length} frames")
|
||||
except Exception as e:
|
||||
print(f"CLP soundd: failed to load ding sound: {e}", file=sys.stderr, flush=True)
|
||||
cloudlog.error(f"Failed to load ding sound: {e}")
|
||||
self.ding_sound = None
|
||||
|
||||
def load_sounds(self):
|
||||
@@ -162,7 +160,7 @@ class Soundd:
|
||||
|
||||
ret = ret * self.current_volume
|
||||
|
||||
# CLEARPILOT: mix in ding (independent of alerts, always max volume)
|
||||
# Mix in ClearPilot ding (independent of alerts, always max volume)
|
||||
if self.ding_playing and self.ding_sound is not None:
|
||||
ding_remaining = len(self.ding_sound) - self.ding_frame
|
||||
if ding_remaining > 0:
|
||||
@@ -233,6 +231,14 @@ class Soundd:
|
||||
|
||||
self.get_audible_alert(sm)
|
||||
|
||||
# ClearPilot: check for ding trigger at ~2Hz
|
||||
self.ding_check_counter += 1
|
||||
if self.ding_check_counter % 10 == 0 and self.ding_sound is not None:
|
||||
if self.params_memory.get("ClearpilotPlayDing") == b"1":
|
||||
self.params_memory.put("ClearpilotPlayDing", "0")
|
||||
self.ding_playing = True
|
||||
self.ding_frame = 0
|
||||
|
||||
rk.keep_time()
|
||||
|
||||
assert stream.active
|
||||
@@ -241,14 +247,6 @@ class Soundd:
|
||||
if self.params_memory.get_bool("FrogPilotTogglesUpdated"):
|
||||
self.update_frogpilot_params()
|
||||
|
||||
# CLEARPILOT: poll ClearpilotPlayDing at ~2Hz; clear it on read.
|
||||
self.ding_check_counter += 1
|
||||
if self.ding_check_counter % 10 == 0 and self.ding_sound is not None:
|
||||
if self.params_memory.get("ClearpilotPlayDing") == b"1":
|
||||
self.params_memory.put("ClearpilotPlayDing", "0")
|
||||
self.ding_playing = True
|
||||
self.ding_frame = 0
|
||||
|
||||
def update_frogpilot_params(self):
|
||||
self.alert_volume_control = self.params.get_bool("AlertVolumeControl")
|
||||
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
#!/bin/sh
|
||||
|
||||
# CLEARPILOT: prefer freshly built _spinner over prebuilt qt/spinner
|
||||
if [ -f ./_spinner ]; then
|
||||
exec ./_spinner "$1"
|
||||
fi
|
||||
|
||||
if [ -f /TICI ] && [ ! -f qt/spinner ]; then
|
||||
cp qt/spinner_larch64 qt/spinner
|
||||
fi
|
||||
|
||||
@@ -118,9 +118,8 @@ void update_model(UIState *s,
|
||||
}
|
||||
|
||||
// update path
|
||||
// CLEARPILOT: read from paramsMemory; controlsd writes "no_lat_lane_change"
|
||||
// there. (Broken used a custom cereal field; we keep the param-based wiring.)
|
||||
bool no_lat_lane_change = paramsMemory.getBool("no_lat_lane_change");
|
||||
// CLEARPILOT: read from frogpilotCarControl cereal message (was paramsMemory)
|
||||
bool no_lat_lane_change = (*s->sm)["frogpilotCarControl"].getFrogpilotCarControl().getNoLatLaneChange();
|
||||
float path;
|
||||
if (no_lat_lane_change) {
|
||||
path = (float)LANE_CHANGE_NO_LAT_PATH_WIDTH / 20; // Release: better calc for EU users
|
||||
|
||||
@@ -7,17 +7,27 @@
|
||||
#include "system/hardware/hw.h"
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
fprintf(stderr, "camerad: starting\n");
|
||||
|
||||
if (Hardware::PC()) {
|
||||
printf("exiting, camerad is not meant to run on PC\n");
|
||||
fprintf(stderr, "camerad: exiting, not meant to run on PC\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ret;
|
||||
fprintf(stderr, "camerad: setting realtime priority 53\n");
|
||||
ret = util::set_realtime_priority(53);
|
||||
fprintf(stderr, "camerad: set_realtime_priority ret=%d\n", ret);
|
||||
assert(ret == 0);
|
||||
ret = util::set_core_affinity({6});
|
||||
assert(ret == 0 || Params().getBool("IsOffroad")); // failure ok while offroad due to offlining cores
|
||||
|
||||
fprintf(stderr, "camerad: setting core affinity to cpu6\n");
|
||||
ret = util::set_core_affinity({6});
|
||||
bool isOffroad = Params().getBool("IsOffroad");
|
||||
fprintf(stderr, "camerad: set_core_affinity ret=%d, IsOffroad=%d\n", ret, isOffroad);
|
||||
assert(ret == 0 || isOffroad); // failure ok while offroad due to offlining cores
|
||||
|
||||
fprintf(stderr, "camerad: starting camerad_thread\n");
|
||||
camerad_thread();
|
||||
fprintf(stderr, "camerad: camerad_thread returned\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,22 +1,13 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
ClearPilot GPS daemon — reads GPS from Quectel EC25 modem via AT commands
|
||||
and publishes gpsLocation messages.
|
||||
and publishes gpsLocation messages for locationd/timed.
|
||||
|
||||
Replaces qcomgpsd (which uses the diag interface — broken on this device).
|
||||
|
||||
Used solely for: setting system clock on first fix, an on-screen UI
|
||||
speed indicator, per-segment GPS metadata for the dashcam, and driving
|
||||
the auto day/night display-mode switch (ScreenDisplayMode 0 ↔ 1) via
|
||||
NOAA solar-position calc against the current fix.
|
||||
|
||||
Self-driving code does NOT consume this output — locationd is patched
|
||||
to not subscribe to gpsLocation, so liveLocationKalman.gpsOK stays false.
|
||||
Replaces qcomgpsd which uses the diag interface (broken on this device).
|
||||
"""
|
||||
import math
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
import datetime
|
||||
|
||||
@@ -24,6 +15,7 @@ from cereal import log
|
||||
import cereal.messaging as messaging
|
||||
from openpilot.common.gpio import gpio_init, gpio_set
|
||||
from openpilot.common.params import Params
|
||||
from openpilot.common.swaglog import cloudlog
|
||||
from openpilot.common.time import system_time_valid
|
||||
from openpilot.system.hardware.tici.pins import GPIO
|
||||
|
||||
@@ -92,7 +84,8 @@ def at_cmd(cmd: str) -> str:
|
||||
f"mmcli -m any --timeout 10 --command='{cmd}'",
|
||||
shell=True, encoding='utf8', stderr=subprocess.DEVNULL
|
||||
).strip()
|
||||
# mmcli wraps AT responses: response: '+QGPSLOC: ...' — strip the wrapper
|
||||
# mmcli wraps AT responses: response: '+QGPSLOC: ...'
|
||||
# Strip the wrapper to get just the AT response
|
||||
if result.startswith("response: '") and result.endswith("'"):
|
||||
result = result[len("response: '"):-1]
|
||||
return result
|
||||
@@ -101,7 +94,7 @@ def at_cmd(cmd: str) -> str:
|
||||
|
||||
|
||||
def wait_for_modem():
|
||||
print("CLP gpsd: waiting for modem", file=sys.stderr, flush=True)
|
||||
cloudlog.warning("gpsd: waiting for modem")
|
||||
while True:
|
||||
ret = subprocess.call(
|
||||
"mmcli -m any --timeout 10 --command='AT+QGPS?'",
|
||||
@@ -132,10 +125,12 @@ def parse_qgpsloc(response: str):
|
||||
fix = int(fields[5]) # 2=2D, 3=3D
|
||||
cog = float(fields[6]) # course over ground
|
||||
spkm = float(fields[7]) # speed km/h
|
||||
spkn = float(fields[8]) # speed knots
|
||||
date = fields[9] # DDMMYY
|
||||
nsat = int(fields[10])
|
||||
|
||||
# Build unix timestamp from UTC + date
|
||||
# utc: "HHMMSS.S", date: "DDMMYY"
|
||||
hh, mm = int(utc[0:2]), int(utc[2:4])
|
||||
ss = float(utc[4:])
|
||||
dd, mo, yy = int(date[0:2]), int(date[2:4]), 2000 + int(date[4:6])
|
||||
@@ -146,51 +141,52 @@ def parse_qgpsloc(response: str):
|
||||
"latitude": lat,
|
||||
"longitude": lon,
|
||||
"altitude": alt,
|
||||
"speed": spkm / 3.6, # km/h -> m/s
|
||||
"speed": spkm / 3.6, # convert km/h to m/s
|
||||
"bearing": cog,
|
||||
"accuracy": hdop * 5, # rough HDOP -> meters
|
||||
"accuracy": hdop * 5, # rough conversion from HDOP to meters
|
||||
"timestamp_ms": dt.timestamp() * 1e3,
|
||||
"satellites": nsat,
|
||||
"fix": fix,
|
||||
}
|
||||
except (ValueError, IndexError) as e:
|
||||
print(f"CLP gpsd: parse error: {e}", file=sys.stderr, flush=True)
|
||||
cloudlog.error(f"gpsd: parse error: {e}")
|
||||
return None
|
||||
|
||||
|
||||
def main():
|
||||
print("CLP gpsd: starting", file=sys.stderr, flush=True)
|
||||
import sys
|
||||
print("gpsd: starting", file=sys.stderr, flush=True)
|
||||
|
||||
# Kill system gpsd which may respawn and interfere with modem access
|
||||
subprocess.run("pkill -f /usr/sbin/gpsd", shell=True,
|
||||
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
|
||||
wait_for_modem()
|
||||
print("CLP gpsd: modem ready", file=sys.stderr, flush=True)
|
||||
print("gpsd: modem ready", file=sys.stderr, flush=True)
|
||||
|
||||
# Enable GPS antenna power
|
||||
gpio_init(GPIO.GNSS_PWR_EN, True)
|
||||
gpio_set(GPIO.GNSS_PWR_EN, True)
|
||||
print("CLP gpsd: GPIO power enabled", file=sys.stderr, flush=True)
|
||||
print("gpsd: GPIO power enabled", file=sys.stderr, flush=True)
|
||||
|
||||
# Don't restart GPS if already running (preserve existing fix)
|
||||
resp = at_cmd("AT+QGPS?")
|
||||
print(f"CLP gpsd: QGPS status: {resp}", file=sys.stderr, flush=True)
|
||||
print(f"gpsd: QGPS status: {resp}", file=sys.stderr, flush=True)
|
||||
if "QGPS: 1" not in resp:
|
||||
at_cmd('AT+QGPSCFG="dpoenable",0')
|
||||
at_cmd('AT+QGPSCFG="outport","none"')
|
||||
at_cmd("AT+QGPS=1")
|
||||
print("CLP gpsd: GPS started fresh", file=sys.stderr, flush=True)
|
||||
print("gpsd: GPS started fresh", file=sys.stderr, flush=True)
|
||||
else:
|
||||
print("CLP gpsd: GPS already running, keeping fix", file=sys.stderr, flush=True)
|
||||
print("gpsd: GPS already running, keeping fix", file=sys.stderr, flush=True)
|
||||
|
||||
pm = messaging.PubMaster(["gpsLocation"])
|
||||
clock_set = system_time_valid()
|
||||
params_memory = Params("/dev/shm/params")
|
||||
last_daylight_check = 0.0
|
||||
daylight_computed = False
|
||||
prev_daylight = None # gate IsDaylight write on change (twice/day, no point rewriting every 30s)
|
||||
print("CLP gpsd: entering main loop", file=sys.stderr, flush=True)
|
||||
prev_daylight = None # CLEARPILOT: gate IsDaylight write on change
|
||||
print("gpsd: entering main loop", file=sys.stderr, flush=True)
|
||||
|
||||
while True:
|
||||
resp = at_cmd("AT+QGPSLOC=2")
|
||||
@@ -205,9 +201,10 @@ def main():
|
||||
capture_output=True)
|
||||
if ret.returncode == 0:
|
||||
clock_set = True
|
||||
print(f"CLP gpsd: system clock set from GPS: {gps_dt}", file=sys.stderr, flush=True)
|
||||
cloudlog.warning("gpsd: system clock set from GPS: %s", gps_dt)
|
||||
print(f"gpsd: system clock set from GPS: {gps_dt}", file=sys.stderr, flush=True)
|
||||
else:
|
||||
print(f"CLP gpsd: failed to set clock: {ret.stderr.decode().strip()}", file=sys.stderr, flush=True)
|
||||
cloudlog.error("gpsd: failed to set clock: %s", ret.stderr.decode().strip())
|
||||
|
||||
msg = messaging.new_message("gpsLocation", valid=True)
|
||||
gps = msg.gpsLocation
|
||||
@@ -227,8 +224,7 @@ def main():
|
||||
gps.speedAccuracy = 1.0
|
||||
pm.send("gpsLocation", msg)
|
||||
|
||||
# Daylight calc + auto display-mode switch (only touches modes 0 and 1).
|
||||
# First calc happens immediately once clock is set; thereafter every 30s.
|
||||
# CLEARPILOT: daylight calculation for auto display mode switching
|
||||
if clock_set:
|
||||
now_mono = time.monotonic()
|
||||
interval = 1.0 if not daylight_computed else 30.0
|
||||
@@ -236,23 +232,24 @@ def main():
|
||||
last_daylight_check = now_mono
|
||||
utc_now = datetime.datetime.utcfromtimestamp(fix["timestamp_ms"] / 1000)
|
||||
daylight = is_daylight(fix["latitude"], fix["longitude"], utc_now)
|
||||
# CLEARPILOT: gate on change — daylight flips twice a day, don't rewrite every 30s
|
||||
if daylight != prev_daylight:
|
||||
params_memory.put_bool("IsDaylight", daylight)
|
||||
prev_daylight = daylight
|
||||
|
||||
if not daylight_computed:
|
||||
daylight_computed = True
|
||||
print(f"CLP gpsd: initial daylight calc: {'day' if daylight else 'night'}",
|
||||
file=sys.stderr, flush=True)
|
||||
cloudlog.warning("gpsd: initial daylight calc: %s", "day" if daylight else "night")
|
||||
print(f"gpsd: initial daylight calc: {'day' if daylight else 'night'}", file=sys.stderr, flush=True)
|
||||
|
||||
# Auto-transition: only touch states 0 and 1 (manual modes 2/3/4 stay)
|
||||
# Auto-transition: only touch states 0 and 1
|
||||
current_mode = params_memory.get_int("ScreenDisplayMode")
|
||||
if current_mode == 0 and not daylight:
|
||||
params_memory.put_int("ScreenDisplayMode", 1)
|
||||
print("CLP gpsd: auto-switch to nightrider (sunset)", file=sys.stderr, flush=True)
|
||||
cloudlog.warning("gpsd: auto-switch to nightrider (sunset)")
|
||||
elif current_mode == 1 and daylight:
|
||||
params_memory.put_int("ScreenDisplayMode", 0)
|
||||
print("CLP gpsd: auto-switch to normal (sunrise)", file=sys.stderr, flush=True)
|
||||
cloudlog.warning("gpsd: auto-switch to normal (sunrise)")
|
||||
|
||||
time.sleep(0.5) # 2 Hz polling
|
||||
|
||||
|
||||
@@ -50,7 +50,6 @@ public:
|
||||
}
|
||||
|
||||
static void reboot() { std::system("sudo reboot"); }
|
||||
|
||||
static void soft_reboot() {
|
||||
const std::vector<std::string> commands = {
|
||||
"rm -f /tmp/safe_staging_overlay.lock",
|
||||
@@ -68,9 +67,7 @@ public:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void poweroff() { std::system("sudo poweroff"); }
|
||||
|
||||
static void set_brightness(int percent) {
|
||||
std::string max = util::read_file("/sys/class/backlight/panel0-backlight/max_brightness");
|
||||
|
||||
@@ -80,7 +77,6 @@ public:
|
||||
brightness_control.close();
|
||||
}
|
||||
}
|
||||
|
||||
static void set_display_power(bool on) {
|
||||
std::ofstream bl_power_control("/sys/class/backlight/panel0-backlight/bl_power");
|
||||
if (bl_power_control.is_open()) {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
import threading
|
||||
from openpilot.system.hardware.hw import Paths
|
||||
from openpilot.common.swaglog import cloudlog
|
||||
@@ -9,16 +8,14 @@ from openpilot.system.loggerd.config import get_available_bytes, get_available_p
|
||||
from openpilot.system.loggerd.uploader import listdir_by_creation
|
||||
from openpilot.system.loggerd.xattr_cache import getxattr
|
||||
|
||||
# CLEARPILOT: bumped from 5 GB to 9 GB so dashcam footage has headroom
|
||||
# CLEARPILOT: increased from 5 GB to 9 GB to reserve space for screen recordings
|
||||
MIN_BYTES = 9 * 1024 * 1024 * 1024
|
||||
MIN_PERCENT = 10
|
||||
|
||||
DELETE_LAST = ['boot', 'crash']
|
||||
|
||||
# CLEARPILOT: dashcam footage directory (trip dirs YYYYMMDD-HHMMSS/ with .mp4 segments)
|
||||
# CLEARPILOT: screen recorder video directory
|
||||
VIDEOS_DIR = '/data/media/0/videos'
|
||||
# CLEARPILOT: max total size for /data/log2 session logs
|
||||
LOG2_MAX_BYTES = 4 * 1024 * 1024 * 1024
|
||||
|
||||
PRESERVE_ATTR_NAME = 'user.preserve'
|
||||
PRESERVE_ATTR_VALUE = b'1'
|
||||
@@ -52,16 +49,15 @@ def get_preserved_segments(dirs_by_creation: list[str]) -> list[str]:
|
||||
|
||||
|
||||
def delete_oldest_video():
|
||||
"""CLEARPILOT: prune dashcam footage when disk space is low.
|
||||
Trip directories are /data/media/0/videos/YYYYMMDD-HHMMSS/ containing .mp4
|
||||
segments. Deletes entire oldest trip directory first. If only one trip
|
||||
remains (the active one), deletes individual segments oldest-first within
|
||||
it. Also cleans up legacy flat .mp4 files.
|
||||
Returns True if something was deleted."""
|
||||
"""CLEARPILOT: delete oldest dashcam footage when disk space is low.
|
||||
Trip directories are /data/media/0/videos/YYYYMMDD-HHMMSS/ containing .mp4 segments.
|
||||
Deletes entire oldest trip directory first. If only one trip remains (active),
|
||||
deletes individual segments oldest-first within it. Also cleans up legacy flat .mp4 files."""
|
||||
try:
|
||||
if not os.path.isdir(VIDEOS_DIR):
|
||||
return False
|
||||
|
||||
# Collect legacy flat mp4 files and trip directories
|
||||
legacy_files = []
|
||||
trip_dirs = []
|
||||
for entry in os.listdir(VIDEOS_DIR):
|
||||
@@ -71,72 +67,71 @@ def delete_oldest_video():
|
||||
elif os.path.isdir(path):
|
||||
trip_dirs.append(entry)
|
||||
|
||||
# Delete legacy flat files first (oldest by name)
|
||||
if legacy_files:
|
||||
legacy_files.sort()
|
||||
delete_path = os.path.join(VIDEOS_DIR, legacy_files[0])
|
||||
print(f"CLP deleter: deleting legacy video {delete_path}", file=sys.stderr, flush=True)
|
||||
cloudlog.info(f"deleting legacy video {delete_path}")
|
||||
os.remove(delete_path)
|
||||
return True
|
||||
|
||||
if not trip_dirs:
|
||||
return False
|
||||
|
||||
trip_dirs.sort() # timestamp names = chronological order
|
||||
trip_dirs.sort() # sorted by timestamp name = chronological order
|
||||
|
||||
# If more than one trip, delete the oldest entire trip directory
|
||||
if len(trip_dirs) > 1:
|
||||
delete_path = os.path.join(VIDEOS_DIR, trip_dirs[0])
|
||||
print(f"CLP deleter: deleting trip {delete_path}", file=sys.stderr, flush=True)
|
||||
cloudlog.info(f"deleting trip {delete_path}")
|
||||
shutil.rmtree(delete_path)
|
||||
return True
|
||||
|
||||
# Only one trip left (likely active) — drop its oldest segment
|
||||
# Only one trip left (likely active) — delete oldest segment within it
|
||||
trip_path = os.path.join(VIDEOS_DIR, trip_dirs[0])
|
||||
segments = sorted(f for f in os.listdir(trip_path) if f.endswith('.mp4'))
|
||||
if not segments:
|
||||
return False
|
||||
delete_path = os.path.join(trip_path, segments[0])
|
||||
print(f"CLP deleter: deleting segment {delete_path}", file=sys.stderr, flush=True)
|
||||
cloudlog.info(f"deleting segment {delete_path}")
|
||||
os.remove(delete_path)
|
||||
return True
|
||||
except OSError as e:
|
||||
print(f"CLP deleter: issue deleting video from {VIDEOS_DIR}: {e}", file=sys.stderr, flush=True)
|
||||
except OSError:
|
||||
cloudlog.exception(f"issue deleting video from {VIDEOS_DIR}")
|
||||
return False
|
||||
|
||||
|
||||
# CLEARPILOT: max total size for /data/log2 session logs
|
||||
LOG2_MAX_BYTES = 4 * 1024 * 1024 * 1024
|
||||
|
||||
def cleanup_log2():
|
||||
"""CLEARPILOT: keep /data/log2 session logs under LOG2_MAX_BYTES total.
|
||||
Deletes oldest dated session directories first (the 'current' symlink/dir
|
||||
is preserved). Runs even when disk space is fine."""
|
||||
"""Delete oldest session log directories until /data/log2 is under LOG2_MAX_BYTES."""
|
||||
log_base = "/data/log2"
|
||||
if not os.path.isdir(log_base):
|
||||
return
|
||||
# Get all session dirs sorted oldest first (by name = timestamp)
|
||||
dirs = []
|
||||
for entry in sorted(os.listdir(log_base)):
|
||||
if entry == "current":
|
||||
continue
|
||||
path = os.path.join(log_base, entry)
|
||||
if os.path.isdir(path) and not os.path.islink(path):
|
||||
try:
|
||||
size = sum(f.stat().st_size for f in os.scandir(path) if f.is_file())
|
||||
except OSError:
|
||||
size = 0
|
||||
size = sum(f.stat().st_size for f in os.scandir(path) if f.is_file())
|
||||
dirs.append((entry, path, size))
|
||||
total = sum(s for _, _, s in dirs)
|
||||
# Also count current session
|
||||
current = os.path.join(log_base, "current")
|
||||
if os.path.isdir(current):
|
||||
try:
|
||||
total += sum(f.stat().st_size for f in os.scandir(current) if f.is_file())
|
||||
except OSError:
|
||||
pass
|
||||
total += sum(f.stat().st_size for f in os.scandir(current) if f.is_file())
|
||||
# Delete oldest until under quota
|
||||
while total > LOG2_MAX_BYTES and dirs:
|
||||
entry, path, size = dirs.pop(0)
|
||||
try:
|
||||
print(f"CLP deleter: deleting log session {path} ({size // 1024 // 1024} MB)",
|
||||
file=sys.stderr, flush=True)
|
||||
cloudlog.info(f"deleting log session {path} ({size // 1024 // 1024} MB)")
|
||||
shutil.rmtree(path)
|
||||
total -= size
|
||||
except OSError as e:
|
||||
print(f"CLP deleter: issue deleting log {path}: {e}", file=sys.stderr, flush=True)
|
||||
except OSError:
|
||||
cloudlog.exception(f"issue deleting log {path}")
|
||||
|
||||
|
||||
def deleter_thread(exit_event):
|
||||
@@ -145,7 +140,7 @@ def deleter_thread(exit_event):
|
||||
out_of_percent = get_available_percent(default=MIN_PERCENT + 1) < MIN_PERCENT
|
||||
|
||||
if out_of_percent or out_of_bytes:
|
||||
# CLEARPILOT: drop oldest dashcam footage first, fall back to log segments
|
||||
# CLEARPILOT: try deleting oldest video first, then fall back to log segments
|
||||
if delete_oldest_video():
|
||||
exit_event.wait(.1)
|
||||
continue
|
||||
@@ -173,7 +168,7 @@ def deleter_thread(exit_event):
|
||||
cloudlog.exception(f"issue deleting {delete_path}")
|
||||
exit_event.wait(.1)
|
||||
else:
|
||||
# CLEARPILOT: keep /data/log2 quota even when disk space is fine
|
||||
# CLEARPILOT: enforce log2 size quota even when disk space is fine
|
||||
cleanup_log2()
|
||||
exit_event.wait(30)
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
libqpOASES_e.so.3.1
|
||||
@@ -1 +0,0 @@
|
||||
libqpOASES_e.so.3.1
|
||||
@@ -1 +0,0 @@
|
||||
../include
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2011 The LibYuv Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef INCLUDE_LIBYUV_H_
|
||||
#define INCLUDE_LIBYUV_H_
|
||||
|
||||
#include "libyuv/basic_types.h"
|
||||
#include "libyuv/compare.h"
|
||||
#include "libyuv/convert.h"
|
||||
#include "libyuv/convert_argb.h"
|
||||
#include "libyuv/convert_from.h"
|
||||
#include "libyuv/convert_from_argb.h"
|
||||
#include "libyuv/cpu_id.h"
|
||||
#include "libyuv/mjpeg_decoder.h"
|
||||
#include "libyuv/planar_functions.h"
|
||||
#include "libyuv/rotate.h"
|
||||
#include "libyuv/rotate_argb.h"
|
||||
#include "libyuv/row.h"
|
||||
#include "libyuv/scale.h"
|
||||
#include "libyuv/scale_argb.h"
|
||||
#include "libyuv/scale_row.h"
|
||||
#include "libyuv/version.h"
|
||||
#include "libyuv/video_common.h"
|
||||
|
||||
#endif // INCLUDE_LIBYUV_H_
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright 2011 The LibYuv Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef INCLUDE_LIBYUV_BASIC_TYPES_H_
|
||||
#define INCLUDE_LIBYUV_BASIC_TYPES_H_
|
||||
|
||||
#include <stddef.h> // for NULL, size_t
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER < 1600)
|
||||
#include <sys/types.h> // for uintptr_t on x86
|
||||
#else
|
||||
#include <stdint.h> // for uintptr_t
|
||||
#endif
|
||||
|
||||
#ifndef GG_LONGLONG
|
||||
#ifndef INT_TYPES_DEFINED
|
||||
#define INT_TYPES_DEFINED
|
||||
#ifdef COMPILER_MSVC
|
||||
typedef unsigned __int64 uint64;
|
||||
typedef __int64 int64;
|
||||
#ifndef INT64_C
|
||||
#define INT64_C(x) x ## I64
|
||||
#endif
|
||||
#ifndef UINT64_C
|
||||
#define UINT64_C(x) x ## UI64
|
||||
#endif
|
||||
#define INT64_F "I64"
|
||||
#else // COMPILER_MSVC
|
||||
#if defined(__LP64__) && !defined(__OpenBSD__) && !defined(__APPLE__)
|
||||
typedef unsigned long uint64; // NOLINT
|
||||
typedef long int64; // NOLINT
|
||||
#ifndef INT64_C
|
||||
#define INT64_C(x) x ## L
|
||||
#endif
|
||||
#ifndef UINT64_C
|
||||
#define UINT64_C(x) x ## UL
|
||||
#endif
|
||||
#define INT64_F "l"
|
||||
#else // defined(__LP64__) && !defined(__OpenBSD__) && !defined(__APPLE__)
|
||||
typedef unsigned long long uint64; // NOLINT
|
||||
typedef long long int64; // NOLINT
|
||||
#ifndef INT64_C
|
||||
#define INT64_C(x) x ## LL
|
||||
#endif
|
||||
#ifndef UINT64_C
|
||||
#define UINT64_C(x) x ## ULL
|
||||
#endif
|
||||
#define INT64_F "ll"
|
||||
#endif // __LP64__
|
||||
#endif // COMPILER_MSVC
|
||||
typedef unsigned int uint32;
|
||||
typedef int int32;
|
||||
typedef unsigned short uint16; // NOLINT
|
||||
typedef short int16; // NOLINT
|
||||
typedef unsigned char uint8;
|
||||
typedef signed char int8;
|
||||
#endif // INT_TYPES_DEFINED
|
||||
#endif // GG_LONGLONG
|
||||
|
||||
// Detect compiler is for x86 or x64.
|
||||
#if defined(__x86_64__) || defined(_M_X64) || \
|
||||
defined(__i386__) || defined(_M_IX86)
|
||||
#define CPU_X86 1
|
||||
#endif
|
||||
// Detect compiler is for ARM.
|
||||
#if defined(__arm__) || defined(_M_ARM)
|
||||
#define CPU_ARM 1
|
||||
#endif
|
||||
|
||||
#ifndef ALIGNP
|
||||
#ifdef __cplusplus
|
||||
#define ALIGNP(p, t) \
|
||||
(reinterpret_cast<uint8*>(((reinterpret_cast<uintptr_t>(p) + \
|
||||
((t) - 1)) & ~((t) - 1))))
|
||||
#else
|
||||
#define ALIGNP(p, t) \
|
||||
((uint8*)((((uintptr_t)(p) + ((t) - 1)) & ~((t) - 1)))) /* NOLINT */
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !defined(LIBYUV_API)
|
||||
#if defined(_WIN32) || defined(__CYGWIN__)
|
||||
#if defined(LIBYUV_BUILDING_SHARED_LIBRARY)
|
||||
#define LIBYUV_API __declspec(dllexport)
|
||||
#elif defined(LIBYUV_USING_SHARED_LIBRARY)
|
||||
#define LIBYUV_API __declspec(dllimport)
|
||||
#else
|
||||
#define LIBYUV_API
|
||||
#endif // LIBYUV_BUILDING_SHARED_LIBRARY
|
||||
#elif defined(__GNUC__) && (__GNUC__ >= 4) && !defined(__APPLE__) && \
|
||||
(defined(LIBYUV_BUILDING_SHARED_LIBRARY) || \
|
||||
defined(LIBYUV_USING_SHARED_LIBRARY))
|
||||
#define LIBYUV_API __attribute__ ((visibility ("default")))
|
||||
#else
|
||||
#define LIBYUV_API
|
||||
#endif // __GNUC__
|
||||
#endif // LIBYUV_API
|
||||
|
||||
#define LIBYUV_BOOL int
|
||||
#define LIBYUV_FALSE 0
|
||||
#define LIBYUV_TRUE 1
|
||||
|
||||
// Visual C x86 or GCC little endian.
|
||||
#if defined(__x86_64__) || defined(_M_X64) || \
|
||||
defined(__i386__) || defined(_M_IX86) || \
|
||||
defined(__arm__) || defined(_M_ARM) || \
|
||||
(defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
|
||||
#define LIBYUV_LITTLE_ENDIAN
|
||||
#endif
|
||||
|
||||
#endif // INCLUDE_LIBYUV_BASIC_TYPES_H_
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2011 The LibYuv Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef INCLUDE_LIBYUV_COMPARE_H_
|
||||
#define INCLUDE_LIBYUV_COMPARE_H_
|
||||
|
||||
#include "libyuv/basic_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
namespace libyuv {
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Compute a hash for specified memory. Seed of 5381 recommended.
|
||||
LIBYUV_API
|
||||
uint32 HashDjb2(const uint8* src, uint64 count, uint32 seed);
|
||||
|
||||
// Scan an opaque argb image and return fourcc based on alpha offset.
|
||||
// Returns FOURCC_ARGB, FOURCC_BGRA, or 0 if unknown.
|
||||
LIBYUV_API
|
||||
uint32 ARGBDetect(const uint8* argb, int stride_argb, int width, int height);
|
||||
|
||||
// Sum Square Error - used to compute Mean Square Error or PSNR.
|
||||
LIBYUV_API
|
||||
uint64 ComputeSumSquareError(const uint8* src_a,
|
||||
const uint8* src_b, int count);
|
||||
|
||||
LIBYUV_API
|
||||
uint64 ComputeSumSquareErrorPlane(const uint8* src_a, int stride_a,
|
||||
const uint8* src_b, int stride_b,
|
||||
int width, int height);
|
||||
|
||||
static const int kMaxPsnr = 128;
|
||||
|
||||
LIBYUV_API
|
||||
double SumSquareErrorToPsnr(uint64 sse, uint64 count);
|
||||
|
||||
LIBYUV_API
|
||||
double CalcFramePsnr(const uint8* src_a, int stride_a,
|
||||
const uint8* src_b, int stride_b,
|
||||
int width, int height);
|
||||
|
||||
LIBYUV_API
|
||||
double I420Psnr(const uint8* src_y_a, int stride_y_a,
|
||||
const uint8* src_u_a, int stride_u_a,
|
||||
const uint8* src_v_a, int stride_v_a,
|
||||
const uint8* src_y_b, int stride_y_b,
|
||||
const uint8* src_u_b, int stride_u_b,
|
||||
const uint8* src_v_b, int stride_v_b,
|
||||
int width, int height);
|
||||
|
||||
LIBYUV_API
|
||||
double CalcFrameSsim(const uint8* src_a, int stride_a,
|
||||
const uint8* src_b, int stride_b,
|
||||
int width, int height);
|
||||
|
||||
LIBYUV_API
|
||||
double I420Ssim(const uint8* src_y_a, int stride_y_a,
|
||||
const uint8* src_u_a, int stride_u_a,
|
||||
const uint8* src_v_a, int stride_v_a,
|
||||
const uint8* src_y_b, int stride_y_b,
|
||||
const uint8* src_u_b, int stride_u_b,
|
||||
const uint8* src_v_b, int stride_v_b,
|
||||
int width, int height);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
} // namespace libyuv
|
||||
#endif
|
||||
|
||||
#endif // INCLUDE_LIBYUV_COMPARE_H_
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright 2013 The LibYuv Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef INCLUDE_LIBYUV_COMPARE_ROW_H_
|
||||
#define INCLUDE_LIBYUV_COMPARE_ROW_H_
|
||||
|
||||
#include "libyuv/basic_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
namespace libyuv {
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if defined(__pnacl__) || defined(__CLR_VER) || \
|
||||
(defined(__i386__) && !defined(__SSE2__))
|
||||
#define LIBYUV_DISABLE_X86
|
||||
#endif
|
||||
// MemorySanitizer does not support assembly code yet. http://crbug.com/344505
|
||||
#if defined(__has_feature)
|
||||
#if __has_feature(memory_sanitizer)
|
||||
#define LIBYUV_DISABLE_X86
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Visual C 2012 required for AVX2.
|
||||
#if defined(_M_IX86) && !defined(__clang__) && \
|
||||
defined(_MSC_VER) && _MSC_VER >= 1700
|
||||
#define VISUALC_HAS_AVX2 1
|
||||
#endif // VisualStudio >= 2012
|
||||
|
||||
// clang >= 3.4.0 required for AVX2.
|
||||
#if defined(__clang__) && (defined(__x86_64__) || defined(__i386__))
|
||||
#if (__clang_major__ > 3) || (__clang_major__ == 3 && (__clang_minor__ >= 4))
|
||||
#define CLANG_HAS_AVX2 1
|
||||
#endif // clang >= 3.4
|
||||
#endif // __clang__
|
||||
|
||||
#if !defined(LIBYUV_DISABLE_X86) && \
|
||||
defined(_M_IX86) && (defined(VISUALC_HAS_AVX2) || defined(CLANG_HAS_AVX2))
|
||||
#define HAS_HASHDJB2_AVX2
|
||||
#endif
|
||||
|
||||
// The following are available for Visual C and GCC:
|
||||
#if !defined(LIBYUV_DISABLE_X86) && \
|
||||
(defined(__x86_64__) || (defined(__i386__) || defined(_M_IX86)))
|
||||
#define HAS_HASHDJB2_SSE41
|
||||
#define HAS_SUMSQUAREERROR_SSE2
|
||||
#endif
|
||||
|
||||
// The following are available for Visual C and clangcl 32 bit:
|
||||
#if !defined(LIBYUV_DISABLE_X86) && defined(_M_IX86) && \
|
||||
(defined(VISUALC_HAS_AVX2) || defined(CLANG_HAS_AVX2))
|
||||
#define HAS_HASHDJB2_AVX2
|
||||
#define HAS_SUMSQUAREERROR_AVX2
|
||||
#endif
|
||||
|
||||
// The following are available for Neon:
|
||||
#if !defined(LIBYUV_DISABLE_NEON) && \
|
||||
(defined(__ARM_NEON__) || defined(LIBYUV_NEON) || defined(__aarch64__))
|
||||
#define HAS_SUMSQUAREERROR_NEON
|
||||
#endif
|
||||
|
||||
uint32 SumSquareError_C(const uint8* src_a, const uint8* src_b, int count);
|
||||
uint32 SumSquareError_SSE2(const uint8* src_a, const uint8* src_b, int count);
|
||||
uint32 SumSquareError_AVX2(const uint8* src_a, const uint8* src_b, int count);
|
||||
uint32 SumSquareError_NEON(const uint8* src_a, const uint8* src_b, int count);
|
||||
|
||||
uint32 HashDjb2_C(const uint8* src, int count, uint32 seed);
|
||||
uint32 HashDjb2_SSE41(const uint8* src, int count, uint32 seed);
|
||||
uint32 HashDjb2_AVX2(const uint8* src, int count, uint32 seed);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
} // namespace libyuv
|
||||
#endif
|
||||
|
||||
#endif // INCLUDE_LIBYUV_COMPARE_ROW_H_
|
||||
@@ -0,0 +1,259 @@
|
||||
/*
|
||||
* Copyright 2011 The LibYuv Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef INCLUDE_LIBYUV_CONVERT_H_
|
||||
#define INCLUDE_LIBYUV_CONVERT_H_
|
||||
|
||||
#include "libyuv/basic_types.h"
|
||||
|
||||
#include "libyuv/rotate.h" // For enum RotationMode.
|
||||
|
||||
// TODO(fbarchard): fix WebRTC source to include following libyuv headers:
|
||||
#include "libyuv/convert_argb.h" // For WebRTC I420ToARGB. b/620
|
||||
#include "libyuv/convert_from.h" // For WebRTC ConvertFromI420. b/620
|
||||
#include "libyuv/planar_functions.h" // For WebRTC I420Rect, CopyPlane. b/618
|
||||
|
||||
#ifdef __cplusplus
|
||||
namespace libyuv {
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Convert I444 to I420.
|
||||
LIBYUV_API
|
||||
int I444ToI420(const uint8* src_y, int src_stride_y,
|
||||
const uint8* src_u, int src_stride_u,
|
||||
const uint8* src_v, int src_stride_v,
|
||||
uint8* dst_y, int dst_stride_y,
|
||||
uint8* dst_u, int dst_stride_u,
|
||||
uint8* dst_v, int dst_stride_v,
|
||||
int width, int height);
|
||||
|
||||
// Convert I422 to I420.
|
||||
LIBYUV_API
|
||||
int I422ToI420(const uint8* src_y, int src_stride_y,
|
||||
const uint8* src_u, int src_stride_u,
|
||||
const uint8* src_v, int src_stride_v,
|
||||
uint8* dst_y, int dst_stride_y,
|
||||
uint8* dst_u, int dst_stride_u,
|
||||
uint8* dst_v, int dst_stride_v,
|
||||
int width, int height);
|
||||
|
||||
// Convert I411 to I420.
|
||||
LIBYUV_API
|
||||
int I411ToI420(const uint8* src_y, int src_stride_y,
|
||||
const uint8* src_u, int src_stride_u,
|
||||
const uint8* src_v, int src_stride_v,
|
||||
uint8* dst_y, int dst_stride_y,
|
||||
uint8* dst_u, int dst_stride_u,
|
||||
uint8* dst_v, int dst_stride_v,
|
||||
int width, int height);
|
||||
|
||||
// Copy I420 to I420.
|
||||
#define I420ToI420 I420Copy
|
||||
LIBYUV_API
|
||||
int I420Copy(const uint8* src_y, int src_stride_y,
|
||||
const uint8* src_u, int src_stride_u,
|
||||
const uint8* src_v, int src_stride_v,
|
||||
uint8* dst_y, int dst_stride_y,
|
||||
uint8* dst_u, int dst_stride_u,
|
||||
uint8* dst_v, int dst_stride_v,
|
||||
int width, int height);
|
||||
|
||||
// Convert I400 (grey) to I420.
|
||||
LIBYUV_API
|
||||
int I400ToI420(const uint8* src_y, int src_stride_y,
|
||||
uint8* dst_y, int dst_stride_y,
|
||||
uint8* dst_u, int dst_stride_u,
|
||||
uint8* dst_v, int dst_stride_v,
|
||||
int width, int height);
|
||||
|
||||
#define J400ToJ420 I400ToI420
|
||||
|
||||
// Convert NV12 to I420.
|
||||
LIBYUV_API
|
||||
int NV12ToI420(const uint8* src_y, int src_stride_y,
|
||||
const uint8* src_uv, int src_stride_uv,
|
||||
uint8* dst_y, int dst_stride_y,
|
||||
uint8* dst_u, int dst_stride_u,
|
||||
uint8* dst_v, int dst_stride_v,
|
||||
int width, int height);
|
||||
|
||||
// Convert NV21 to I420.
|
||||
LIBYUV_API
|
||||
int NV21ToI420(const uint8* src_y, int src_stride_y,
|
||||
const uint8* src_vu, int src_stride_vu,
|
||||
uint8* dst_y, int dst_stride_y,
|
||||
uint8* dst_u, int dst_stride_u,
|
||||
uint8* dst_v, int dst_stride_v,
|
||||
int width, int height);
|
||||
|
||||
// Convert YUY2 to I420.
|
||||
LIBYUV_API
|
||||
int YUY2ToI420(const uint8* src_yuy2, int src_stride_yuy2,
|
||||
uint8* dst_y, int dst_stride_y,
|
||||
uint8* dst_u, int dst_stride_u,
|
||||
uint8* dst_v, int dst_stride_v,
|
||||
int width, int height);
|
||||
|
||||
// Convert UYVY to I420.
|
||||
LIBYUV_API
|
||||
int UYVYToI420(const uint8* src_uyvy, int src_stride_uyvy,
|
||||
uint8* dst_y, int dst_stride_y,
|
||||
uint8* dst_u, int dst_stride_u,
|
||||
uint8* dst_v, int dst_stride_v,
|
||||
int width, int height);
|
||||
|
||||
// Convert M420 to I420.
|
||||
LIBYUV_API
|
||||
int M420ToI420(const uint8* src_m420, int src_stride_m420,
|
||||
uint8* dst_y, int dst_stride_y,
|
||||
uint8* dst_u, int dst_stride_u,
|
||||
uint8* dst_v, int dst_stride_v,
|
||||
int width, int height);
|
||||
|
||||
// Convert Android420 to I420.
|
||||
LIBYUV_API
|
||||
int Android420ToI420(const uint8* src_y, int src_stride_y,
|
||||
const uint8* src_u, int src_stride_u,
|
||||
const uint8* src_v, int src_stride_v,
|
||||
int pixel_stride_uv,
|
||||
uint8* dst_y, int dst_stride_y,
|
||||
uint8* dst_u, int dst_stride_u,
|
||||
uint8* dst_v, int dst_stride_v,
|
||||
int width, int height);
|
||||
|
||||
// ARGB little endian (bgra in memory) to I420.
|
||||
LIBYUV_API
|
||||
int ARGBToI420(const uint8* src_frame, int src_stride_frame,
|
||||
uint8* dst_y, int dst_stride_y,
|
||||
uint8* dst_u, int dst_stride_u,
|
||||
uint8* dst_v, int dst_stride_v,
|
||||
int width, int height);
|
||||
|
||||
// BGRA little endian (argb in memory) to I420.
|
||||
LIBYUV_API
|
||||
int BGRAToI420(const uint8* src_frame, int src_stride_frame,
|
||||
uint8* dst_y, int dst_stride_y,
|
||||
uint8* dst_u, int dst_stride_u,
|
||||
uint8* dst_v, int dst_stride_v,
|
||||
int width, int height);
|
||||
|
||||
// ABGR little endian (rgba in memory) to I420.
|
||||
LIBYUV_API
|
||||
int ABGRToI420(const uint8* src_frame, int src_stride_frame,
|
||||
uint8* dst_y, int dst_stride_y,
|
||||
uint8* dst_u, int dst_stride_u,
|
||||
uint8* dst_v, int dst_stride_v,
|
||||
int width, int height);
|
||||
|
||||
// RGBA little endian (abgr in memory) to I420.
|
||||
LIBYUV_API
|
||||
int RGBAToI420(const uint8* src_frame, int src_stride_frame,
|
||||
uint8* dst_y, int dst_stride_y,
|
||||
uint8* dst_u, int dst_stride_u,
|
||||
uint8* dst_v, int dst_stride_v,
|
||||
int width, int height);
|
||||
|
||||
// RGB little endian (bgr in memory) to I420.
|
||||
LIBYUV_API
|
||||
int RGB24ToI420(const uint8* src_frame, int src_stride_frame,
|
||||
uint8* dst_y, int dst_stride_y,
|
||||
uint8* dst_u, int dst_stride_u,
|
||||
uint8* dst_v, int dst_stride_v,
|
||||
int width, int height);
|
||||
|
||||
// RGB big endian (rgb in memory) to I420.
|
||||
LIBYUV_API
|
||||
int RAWToI420(const uint8* src_frame, int src_stride_frame,
|
||||
uint8* dst_y, int dst_stride_y,
|
||||
uint8* dst_u, int dst_stride_u,
|
||||
uint8* dst_v, int dst_stride_v,
|
||||
int width, int height);
|
||||
|
||||
// RGB16 (RGBP fourcc) little endian to I420.
|
||||
LIBYUV_API
|
||||
int RGB565ToI420(const uint8* src_frame, int src_stride_frame,
|
||||
uint8* dst_y, int dst_stride_y,
|
||||
uint8* dst_u, int dst_stride_u,
|
||||
uint8* dst_v, int dst_stride_v,
|
||||
int width, int height);
|
||||
|
||||
// RGB15 (RGBO fourcc) little endian to I420.
|
||||
LIBYUV_API
|
||||
int ARGB1555ToI420(const uint8* src_frame, int src_stride_frame,
|
||||
uint8* dst_y, int dst_stride_y,
|
||||
uint8* dst_u, int dst_stride_u,
|
||||
uint8* dst_v, int dst_stride_v,
|
||||
int width, int height);
|
||||
|
||||
// RGB12 (R444 fourcc) little endian to I420.
|
||||
LIBYUV_API
|
||||
int ARGB4444ToI420(const uint8* src_frame, int src_stride_frame,
|
||||
uint8* dst_y, int dst_stride_y,
|
||||
uint8* dst_u, int dst_stride_u,
|
||||
uint8* dst_v, int dst_stride_v,
|
||||
int width, int height);
|
||||
|
||||
#ifdef HAVE_JPEG
|
||||
// src_width/height provided by capture.
|
||||
// dst_width/height for clipping determine final size.
|
||||
LIBYUV_API
|
||||
int MJPGToI420(const uint8* sample, size_t sample_size,
|
||||
uint8* dst_y, int dst_stride_y,
|
||||
uint8* dst_u, int dst_stride_u,
|
||||
uint8* dst_v, int dst_stride_v,
|
||||
int src_width, int src_height,
|
||||
int dst_width, int dst_height);
|
||||
|
||||
// Query size of MJPG in pixels.
|
||||
LIBYUV_API
|
||||
int MJPGSize(const uint8* sample, size_t sample_size,
|
||||
int* width, int* height);
|
||||
#endif
|
||||
|
||||
// Convert camera sample to I420 with cropping, rotation and vertical flip.
|
||||
// "src_size" is needed to parse MJPG.
|
||||
// "dst_stride_y" number of bytes in a row of the dst_y plane.
|
||||
// Normally this would be the same as dst_width, with recommended alignment
|
||||
// to 16 bytes for better efficiency.
|
||||
// If rotation of 90 or 270 is used, stride is affected. The caller should
|
||||
// allocate the I420 buffer according to rotation.
|
||||
// "dst_stride_u" number of bytes in a row of the dst_u plane.
|
||||
// Normally this would be the same as (dst_width + 1) / 2, with
|
||||
// recommended alignment to 16 bytes for better efficiency.
|
||||
// If rotation of 90 or 270 is used, stride is affected.
|
||||
// "crop_x" and "crop_y" are starting position for cropping.
|
||||
// To center, crop_x = (src_width - dst_width) / 2
|
||||
// crop_y = (src_height - dst_height) / 2
|
||||
// "src_width" / "src_height" is size of src_frame in pixels.
|
||||
// "src_height" can be negative indicating a vertically flipped image source.
|
||||
// "crop_width" / "crop_height" is the size to crop the src to.
|
||||
// Must be less than or equal to src_width/src_height
|
||||
// Cropping parameters are pre-rotation.
|
||||
// "rotation" can be 0, 90, 180 or 270.
|
||||
// "format" is a fourcc. ie 'I420', 'YUY2'
|
||||
// Returns 0 for successful; -1 for invalid parameter. Non-zero for failure.
|
||||
LIBYUV_API
|
||||
int ConvertToI420(const uint8* src_frame, size_t src_size,
|
||||
uint8* dst_y, int dst_stride_y,
|
||||
uint8* dst_u, int dst_stride_u,
|
||||
uint8* dst_v, int dst_stride_v,
|
||||
int crop_x, int crop_y,
|
||||
int src_width, int src_height,
|
||||
int crop_width, int crop_height,
|
||||
enum RotationMode rotation,
|
||||
uint32 format);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
} // namespace libyuv
|
||||
#endif
|
||||
|
||||
#endif // INCLUDE_LIBYUV_CONVERT_H_
|
||||
@@ -0,0 +1,319 @@
|
||||
/*
|
||||
* Copyright 2012 The LibYuv Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef INCLUDE_LIBYUV_CONVERT_ARGB_H_
|
||||
#define INCLUDE_LIBYUV_CONVERT_ARGB_H_
|
||||
|
||||
#include "libyuv/basic_types.h"
|
||||
|
||||
#include "libyuv/rotate.h" // For enum RotationMode.
|
||||
|
||||
// TODO(fbarchard): This set of functions should exactly match convert.h
|
||||
// TODO(fbarchard): Add tests. Create random content of right size and convert
|
||||
// with C vs Opt and or to I420 and compare.
|
||||
// TODO(fbarchard): Some of these functions lack parameter setting.
|
||||
|
||||
#ifdef __cplusplus
|
||||
namespace libyuv {
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Alias.
|
||||
#define ARGBToARGB ARGBCopy
|
||||
|
||||
// Copy ARGB to ARGB.
|
||||
LIBYUV_API
|
||||
int ARGBCopy(const uint8* src_argb, int src_stride_argb,
|
||||
uint8* dst_argb, int dst_stride_argb,
|
||||
int width, int height);
|
||||
|
||||
// Convert I420 to ARGB.
|
||||
LIBYUV_API
|
||||
int I420ToARGB(const uint8* src_y, int src_stride_y,
|
||||
const uint8* src_u, int src_stride_u,
|
||||
const uint8* src_v, int src_stride_v,
|
||||
uint8* dst_argb, int dst_stride_argb,
|
||||
int width, int height);
|
||||
|
||||
// Duplicate prototype for function in convert_from.h for remoting.
|
||||
LIBYUV_API
|
||||
int I420ToABGR(const uint8* src_y, int src_stride_y,
|
||||
const uint8* src_u, int src_stride_u,
|
||||
const uint8* src_v, int src_stride_v,
|
||||
uint8* dst_argb, int dst_stride_argb,
|
||||
int width, int height);
|
||||
|
||||
// Convert I422 to ARGB.
|
||||
LIBYUV_API
|
||||
int I422ToARGB(const uint8* src_y, int src_stride_y,
|
||||
const uint8* src_u, int src_stride_u,
|
||||
const uint8* src_v, int src_stride_v,
|
||||
uint8* dst_argb, int dst_stride_argb,
|
||||
int width, int height);
|
||||
|
||||
// Convert I444 to ARGB.
|
||||
LIBYUV_API
|
||||
int I444ToARGB(const uint8* src_y, int src_stride_y,
|
||||
const uint8* src_u, int src_stride_u,
|
||||
const uint8* src_v, int src_stride_v,
|
||||
uint8* dst_argb, int dst_stride_argb,
|
||||
int width, int height);
|
||||
|
||||
// Convert J444 to ARGB.
|
||||
LIBYUV_API
|
||||
int J444ToARGB(const uint8* src_y, int src_stride_y,
|
||||
const uint8* src_u, int src_stride_u,
|
||||
const uint8* src_v, int src_stride_v,
|
||||
uint8* dst_argb, int dst_stride_argb,
|
||||
int width, int height);
|
||||
|
||||
// Convert I444 to ABGR.
|
||||
LIBYUV_API
|
||||
int I444ToABGR(const uint8* src_y, int src_stride_y,
|
||||
const uint8* src_u, int src_stride_u,
|
||||
const uint8* src_v, int src_stride_v,
|
||||
uint8* dst_abgr, int dst_stride_abgr,
|
||||
int width, int height);
|
||||
|
||||
// Convert I411 to ARGB.
|
||||
LIBYUV_API
|
||||
int I411ToARGB(const uint8* src_y, int src_stride_y,
|
||||
const uint8* src_u, int src_stride_u,
|
||||
const uint8* src_v, int src_stride_v,
|
||||
uint8* dst_argb, int dst_stride_argb,
|
||||
int width, int height);
|
||||
|
||||
// Convert I420 with Alpha to preattenuated ARGB.
|
||||
LIBYUV_API
|
||||
int I420AlphaToARGB(const uint8* src_y, int src_stride_y,
|
||||
const uint8* src_u, int src_stride_u,
|
||||
const uint8* src_v, int src_stride_v,
|
||||
const uint8* src_a, int src_stride_a,
|
||||
uint8* dst_argb, int dst_stride_argb,
|
||||
int width, int height, int attenuate);
|
||||
|
||||
// Convert I420 with Alpha to preattenuated ABGR.
|
||||
LIBYUV_API
|
||||
int I420AlphaToABGR(const uint8* src_y, int src_stride_y,
|
||||
const uint8* src_u, int src_stride_u,
|
||||
const uint8* src_v, int src_stride_v,
|
||||
const uint8* src_a, int src_stride_a,
|
||||
uint8* dst_abgr, int dst_stride_abgr,
|
||||
int width, int height, int attenuate);
|
||||
|
||||
// Convert I400 (grey) to ARGB. Reverse of ARGBToI400.
|
||||
LIBYUV_API
|
||||
int I400ToARGB(const uint8* src_y, int src_stride_y,
|
||||
uint8* dst_argb, int dst_stride_argb,
|
||||
int width, int height);
|
||||
|
||||
// Convert J400 (jpeg grey) to ARGB.
|
||||
LIBYUV_API
|
||||
int J400ToARGB(const uint8* src_y, int src_stride_y,
|
||||
uint8* dst_argb, int dst_stride_argb,
|
||||
int width, int height);
|
||||
|
||||
// Alias.
|
||||
#define YToARGB I400ToARGB
|
||||
|
||||
// Convert NV12 to ARGB.
|
||||
LIBYUV_API
|
||||
int NV12ToARGB(const uint8* src_y, int src_stride_y,
|
||||
const uint8* src_uv, int src_stride_uv,
|
||||
uint8* dst_argb, int dst_stride_argb,
|
||||
int width, int height);
|
||||
|
||||
// Convert NV21 to ARGB.
|
||||
LIBYUV_API
|
||||
int NV21ToARGB(const uint8* src_y, int src_stride_y,
|
||||
const uint8* src_vu, int src_stride_vu,
|
||||
uint8* dst_argb, int dst_stride_argb,
|
||||
int width, int height);
|
||||
|
||||
// Convert M420 to ARGB.
|
||||
LIBYUV_API
|
||||
int M420ToARGB(const uint8* src_m420, int src_stride_m420,
|
||||
uint8* dst_argb, int dst_stride_argb,
|
||||
int width, int height);
|
||||
|
||||
// Convert YUY2 to ARGB.
|
||||
LIBYUV_API
|
||||
int YUY2ToARGB(const uint8* src_yuy2, int src_stride_yuy2,
|
||||
uint8* dst_argb, int dst_stride_argb,
|
||||
int width, int height);
|
||||
|
||||
// Convert UYVY to ARGB.
|
||||
LIBYUV_API
|
||||
int UYVYToARGB(const uint8* src_uyvy, int src_stride_uyvy,
|
||||
uint8* dst_argb, int dst_stride_argb,
|
||||
int width, int height);
|
||||
|
||||
// Convert J420 to ARGB.
|
||||
LIBYUV_API
|
||||
int J420ToARGB(const uint8* src_y, int src_stride_y,
|
||||
const uint8* src_u, int src_stride_u,
|
||||
const uint8* src_v, int src_stride_v,
|
||||
uint8* dst_argb, int dst_stride_argb,
|
||||
int width, int height);
|
||||
|
||||
// Convert J422 to ARGB.
|
||||
LIBYUV_API
|
||||
int J422ToARGB(const uint8* src_y, int src_stride_y,
|
||||
const uint8* src_u, int src_stride_u,
|
||||
const uint8* src_v, int src_stride_v,
|
||||
uint8* dst_argb, int dst_stride_argb,
|
||||
int width, int height);
|
||||
|
||||
// Convert J420 to ABGR.
|
||||
LIBYUV_API
|
||||
int J420ToABGR(const uint8* src_y, int src_stride_y,
|
||||
const uint8* src_u, int src_stride_u,
|
||||
const uint8* src_v, int src_stride_v,
|
||||
uint8* dst_abgr, int dst_stride_abgr,
|
||||
int width, int height);
|
||||
|
||||
// Convert J422 to ABGR.
|
||||
LIBYUV_API
|
||||
int J422ToABGR(const uint8* src_y, int src_stride_y,
|
||||
const uint8* src_u, int src_stride_u,
|
||||
const uint8* src_v, int src_stride_v,
|
||||
uint8* dst_abgr, int dst_stride_abgr,
|
||||
int width, int height);
|
||||
|
||||
// Convert H420 to ARGB.
|
||||
LIBYUV_API
|
||||
int H420ToARGB(const uint8* src_y, int src_stride_y,
|
||||
const uint8* src_u, int src_stride_u,
|
||||
const uint8* src_v, int src_stride_v,
|
||||
uint8* dst_argb, int dst_stride_argb,
|
||||
int width, int height);
|
||||
|
||||
// Convert H422 to ARGB.
|
||||
LIBYUV_API
|
||||
int H422ToARGB(const uint8* src_y, int src_stride_y,
|
||||
const uint8* src_u, int src_stride_u,
|
||||
const uint8* src_v, int src_stride_v,
|
||||
uint8* dst_argb, int dst_stride_argb,
|
||||
int width, int height);
|
||||
|
||||
// Convert H420 to ABGR.
|
||||
LIBYUV_API
|
||||
int H420ToABGR(const uint8* src_y, int src_stride_y,
|
||||
const uint8* src_u, int src_stride_u,
|
||||
const uint8* src_v, int src_stride_v,
|
||||
uint8* dst_abgr, int dst_stride_abgr,
|
||||
int width, int height);
|
||||
|
||||
// Convert H422 to ABGR.
|
||||
LIBYUV_API
|
||||
int H422ToABGR(const uint8* src_y, int src_stride_y,
|
||||
const uint8* src_u, int src_stride_u,
|
||||
const uint8* src_v, int src_stride_v,
|
||||
uint8* dst_abgr, int dst_stride_abgr,
|
||||
int width, int height);
|
||||
|
||||
// BGRA little endian (argb in memory) to ARGB.
|
||||
LIBYUV_API
|
||||
int BGRAToARGB(const uint8* src_frame, int src_stride_frame,
|
||||
uint8* dst_argb, int dst_stride_argb,
|
||||
int width, int height);
|
||||
|
||||
// ABGR little endian (rgba in memory) to ARGB.
|
||||
LIBYUV_API
|
||||
int ABGRToARGB(const uint8* src_frame, int src_stride_frame,
|
||||
uint8* dst_argb, int dst_stride_argb,
|
||||
int width, int height);
|
||||
|
||||
// RGBA little endian (abgr in memory) to ARGB.
|
||||
LIBYUV_API
|
||||
int RGBAToARGB(const uint8* src_frame, int src_stride_frame,
|
||||
uint8* dst_argb, int dst_stride_argb,
|
||||
int width, int height);
|
||||
|
||||
// Deprecated function name.
|
||||
#define BG24ToARGB RGB24ToARGB
|
||||
|
||||
// RGB little endian (bgr in memory) to ARGB.
|
||||
LIBYUV_API
|
||||
int RGB24ToARGB(const uint8* src_frame, int src_stride_frame,
|
||||
uint8* dst_argb, int dst_stride_argb,
|
||||
int width, int height);
|
||||
|
||||
// RGB big endian (rgb in memory) to ARGB.
|
||||
LIBYUV_API
|
||||
int RAWToARGB(const uint8* src_frame, int src_stride_frame,
|
||||
uint8* dst_argb, int dst_stride_argb,
|
||||
int width, int height);
|
||||
|
||||
// RGB16 (RGBP fourcc) little endian to ARGB.
|
||||
LIBYUV_API
|
||||
int RGB565ToARGB(const uint8* src_frame, int src_stride_frame,
|
||||
uint8* dst_argb, int dst_stride_argb,
|
||||
int width, int height);
|
||||
|
||||
// RGB15 (RGBO fourcc) little endian to ARGB.
|
||||
LIBYUV_API
|
||||
int ARGB1555ToARGB(const uint8* src_frame, int src_stride_frame,
|
||||
uint8* dst_argb, int dst_stride_argb,
|
||||
int width, int height);
|
||||
|
||||
// RGB12 (R444 fourcc) little endian to ARGB.
|
||||
LIBYUV_API
|
||||
int ARGB4444ToARGB(const uint8* src_frame, int src_stride_frame,
|
||||
uint8* dst_argb, int dst_stride_argb,
|
||||
int width, int height);
|
||||
|
||||
#ifdef HAVE_JPEG
|
||||
// src_width/height provided by capture
|
||||
// dst_width/height for clipping determine final size.
|
||||
LIBYUV_API
|
||||
int MJPGToARGB(const uint8* sample, size_t sample_size,
|
||||
uint8* dst_argb, int dst_stride_argb,
|
||||
int src_width, int src_height,
|
||||
int dst_width, int dst_height);
|
||||
#endif
|
||||
|
||||
// Convert camera sample to ARGB with cropping, rotation and vertical flip.
|
||||
// "src_size" is needed to parse MJPG.
|
||||
// "dst_stride_argb" number of bytes in a row of the dst_argb plane.
|
||||
// Normally this would be the same as dst_width, with recommended alignment
|
||||
// to 16 bytes for better efficiency.
|
||||
// If rotation of 90 or 270 is used, stride is affected. The caller should
|
||||
// allocate the I420 buffer according to rotation.
|
||||
// "dst_stride_u" number of bytes in a row of the dst_u plane.
|
||||
// Normally this would be the same as (dst_width + 1) / 2, with
|
||||
// recommended alignment to 16 bytes for better efficiency.
|
||||
// If rotation of 90 or 270 is used, stride is affected.
|
||||
// "crop_x" and "crop_y" are starting position for cropping.
|
||||
// To center, crop_x = (src_width - dst_width) / 2
|
||||
// crop_y = (src_height - dst_height) / 2
|
||||
// "src_width" / "src_height" is size of src_frame in pixels.
|
||||
// "src_height" can be negative indicating a vertically flipped image source.
|
||||
// "crop_width" / "crop_height" is the size to crop the src to.
|
||||
// Must be less than or equal to src_width/src_height
|
||||
// Cropping parameters are pre-rotation.
|
||||
// "rotation" can be 0, 90, 180 or 270.
|
||||
// "format" is a fourcc. ie 'I420', 'YUY2'
|
||||
// Returns 0 for successful; -1 for invalid parameter. Non-zero for failure.
|
||||
LIBYUV_API
|
||||
int ConvertToARGB(const uint8* src_frame, size_t src_size,
|
||||
uint8* dst_argb, int dst_stride_argb,
|
||||
int crop_x, int crop_y,
|
||||
int src_width, int src_height,
|
||||
int crop_width, int crop_height,
|
||||
enum RotationMode rotation,
|
||||
uint32 format);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
} // namespace libyuv
|
||||
#endif
|
||||
|
||||
#endif // INCLUDE_LIBYUV_CONVERT_ARGB_H_
|
||||
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
* Copyright 2011 The LibYuv Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef INCLUDE_LIBYUV_CONVERT_FROM_H_
|
||||
#define INCLUDE_LIBYUV_CONVERT_FROM_H_
|
||||
|
||||
#include "libyuv/basic_types.h"
|
||||
#include "libyuv/rotate.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
namespace libyuv {
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// See Also convert.h for conversions from formats to I420.
|
||||
|
||||
// I420Copy in convert to I420ToI420.
|
||||
|
||||
LIBYUV_API
|
||||
int I420ToI422(const uint8* src_y, int src_stride_y,
|
||||
const uint8* src_u, int src_stride_u,
|
||||
const uint8* src_v, int src_stride_v,
|
||||
uint8* dst_y, int dst_stride_y,
|
||||
uint8* dst_u, int dst_stride_u,
|
||||
uint8* dst_v, int dst_stride_v,
|
||||
int width, int height);
|
||||
|
||||
LIBYUV_API
|
||||
int I420ToI444(const uint8* src_y, int src_stride_y,
|
||||
const uint8* src_u, int src_stride_u,
|
||||
const uint8* src_v, int src_stride_v,
|
||||
uint8* dst_y, int dst_stride_y,
|
||||
uint8* dst_u, int dst_stride_u,
|
||||
uint8* dst_v, int dst_stride_v,
|
||||
int width, int height);
|
||||
|
||||
LIBYUV_API
|
||||
int I420ToI411(const uint8* src_y, int src_stride_y,
|
||||
const uint8* src_u, int src_stride_u,
|
||||
const uint8* src_v, int src_stride_v,
|
||||
uint8* dst_y, int dst_stride_y,
|
||||
uint8* dst_u, int dst_stride_u,
|
||||
uint8* dst_v, int dst_stride_v,
|
||||
int width, int height);
|
||||
|
||||
// Copy to I400. Source can be I420, I422, I444, I400, NV12 or NV21.
|
||||
LIBYUV_API
|
||||
int I400Copy(const uint8* src_y, int src_stride_y,
|
||||
uint8* dst_y, int dst_stride_y,
|
||||
int width, int height);
|
||||
|
||||
LIBYUV_API
|
||||
int I420ToNV12(const uint8* src_y, int src_stride_y,
|
||||
const uint8* src_u, int src_stride_u,
|
||||
const uint8* src_v, int src_stride_v,
|
||||
uint8* dst_y, int dst_stride_y,
|
||||
uint8* dst_uv, int dst_stride_uv,
|
||||
int width, int height);
|
||||
|
||||
LIBYUV_API
|
||||
int I420ToNV21(const uint8* src_y, int src_stride_y,
|
||||
const uint8* src_u, int src_stride_u,
|
||||
const uint8* src_v, int src_stride_v,
|
||||
uint8* dst_y, int dst_stride_y,
|
||||
uint8* dst_vu, int dst_stride_vu,
|
||||
int width, int height);
|
||||
|
||||
LIBYUV_API
|
||||
int I420ToYUY2(const uint8* src_y, int src_stride_y,
|
||||
const uint8* src_u, int src_stride_u,
|
||||
const uint8* src_v, int src_stride_v,
|
||||
uint8* dst_frame, int dst_stride_frame,
|
||||
int width, int height);
|
||||
|
||||
LIBYUV_API
|
||||
int I420ToUYVY(const uint8* src_y, int src_stride_y,
|
||||
const uint8* src_u, int src_stride_u,
|
||||
const uint8* src_v, int src_stride_v,
|
||||
uint8* dst_frame, int dst_stride_frame,
|
||||
int width, int height);
|
||||
|
||||
LIBYUV_API
|
||||
int I420ToARGB(const uint8* src_y, int src_stride_y,
|
||||
const uint8* src_u, int src_stride_u,
|
||||
const uint8* src_v, int src_stride_v,
|
||||
uint8* dst_argb, int dst_stride_argb,
|
||||
int width, int height);
|
||||
|
||||
LIBYUV_API
|
||||
int I420ToBGRA(const uint8* src_y, int src_stride_y,
|
||||
const uint8* src_u, int src_stride_u,
|
||||
const uint8* src_v, int src_stride_v,
|
||||
uint8* dst_argb, int dst_stride_argb,
|
||||
int width, int height);
|
||||
|
||||
LIBYUV_API
|
||||
int I420ToABGR(const uint8* src_y, int src_stride_y,
|
||||
const uint8* src_u, int src_stride_u,
|
||||
const uint8* src_v, int src_stride_v,
|
||||
uint8* dst_argb, int dst_stride_argb,
|
||||
int width, int height);
|
||||
|
||||
LIBYUV_API
|
||||
int I420ToRGBA(const uint8* src_y, int src_stride_y,
|
||||
const uint8* src_u, int src_stride_u,
|
||||
const uint8* src_v, int src_stride_v,
|
||||
uint8* dst_rgba, int dst_stride_rgba,
|
||||
int width, int height);
|
||||
|
||||
LIBYUV_API
|
||||
int I420ToRGB24(const uint8* src_y, int src_stride_y,
|
||||
const uint8* src_u, int src_stride_u,
|
||||
const uint8* src_v, int src_stride_v,
|
||||
uint8* dst_frame, int dst_stride_frame,
|
||||
int width, int height);
|
||||
|
||||
LIBYUV_API
|
||||
int I420ToRAW(const uint8* src_y, int src_stride_y,
|
||||
const uint8* src_u, int src_stride_u,
|
||||
const uint8* src_v, int src_stride_v,
|
||||
uint8* dst_frame, int dst_stride_frame,
|
||||
int width, int height);
|
||||
|
||||
LIBYUV_API
|
||||
int I420ToRGB565(const uint8* src_y, int src_stride_y,
|
||||
const uint8* src_u, int src_stride_u,
|
||||
const uint8* src_v, int src_stride_v,
|
||||
uint8* dst_frame, int dst_stride_frame,
|
||||
int width, int height);
|
||||
|
||||
// Convert I420 To RGB565 with 4x4 dither matrix (16 bytes).
|
||||
// Values in dither matrix from 0 to 7 recommended.
|
||||
// The order of the dither matrix is first byte is upper left.
|
||||
|
||||
LIBYUV_API
|
||||
int I420ToRGB565Dither(const uint8* src_y, int src_stride_y,
|
||||
const uint8* src_u, int src_stride_u,
|
||||
const uint8* src_v, int src_stride_v,
|
||||
uint8* dst_frame, int dst_stride_frame,
|
||||
const uint8* dither4x4, int width, int height);
|
||||
|
||||
LIBYUV_API
|
||||
int I420ToARGB1555(const uint8* src_y, int src_stride_y,
|
||||
const uint8* src_u, int src_stride_u,
|
||||
const uint8* src_v, int src_stride_v,
|
||||
uint8* dst_frame, int dst_stride_frame,
|
||||
int width, int height);
|
||||
|
||||
LIBYUV_API
|
||||
int I420ToARGB4444(const uint8* src_y, int src_stride_y,
|
||||
const uint8* src_u, int src_stride_u,
|
||||
const uint8* src_v, int src_stride_v,
|
||||
uint8* dst_frame, int dst_stride_frame,
|
||||
int width, int height);
|
||||
|
||||
// Convert I420 to specified format.
|
||||
// "dst_sample_stride" is bytes in a row for the destination. Pass 0 if the
|
||||
// buffer has contiguous rows. Can be negative. A multiple of 16 is optimal.
|
||||
LIBYUV_API
|
||||
int ConvertFromI420(const uint8* y, int y_stride,
|
||||
const uint8* u, int u_stride,
|
||||
const uint8* v, int v_stride,
|
||||
uint8* dst_sample, int dst_sample_stride,
|
||||
int width, int height,
|
||||
uint32 format);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
} // namespace libyuv
|
||||
#endif
|
||||
|
||||
#endif // INCLUDE_LIBYUV_CONVERT_FROM_H_
|
||||
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
* Copyright 2012 The LibYuv Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef INCLUDE_LIBYUV_CONVERT_FROM_ARGB_H_
|
||||
#define INCLUDE_LIBYUV_CONVERT_FROM_ARGB_H_
|
||||
|
||||
#include "libyuv/basic_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
namespace libyuv {
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Copy ARGB to ARGB.
|
||||
#define ARGBToARGB ARGBCopy
|
||||
LIBYUV_API
|
||||
int ARGBCopy(const uint8* src_argb, int src_stride_argb,
|
||||
uint8* dst_argb, int dst_stride_argb,
|
||||
int width, int height);
|
||||
|
||||
// Convert ARGB To BGRA.
|
||||
LIBYUV_API
|
||||
int ARGBToBGRA(const uint8* src_argb, int src_stride_argb,
|
||||
uint8* dst_bgra, int dst_stride_bgra,
|
||||
int width, int height);
|
||||
|
||||
// Convert ARGB To ABGR.
|
||||
LIBYUV_API
|
||||
int ARGBToABGR(const uint8* src_argb, int src_stride_argb,
|
||||
uint8* dst_abgr, int dst_stride_abgr,
|
||||
int width, int height);
|
||||
|
||||
// Convert ARGB To RGBA.
|
||||
LIBYUV_API
|
||||
int ARGBToRGBA(const uint8* src_argb, int src_stride_argb,
|
||||
uint8* dst_rgba, int dst_stride_rgba,
|
||||
int width, int height);
|
||||
|
||||
// Convert ARGB To RGB24.
|
||||
LIBYUV_API
|
||||
int ARGBToRGB24(const uint8* src_argb, int src_stride_argb,
|
||||
uint8* dst_rgb24, int dst_stride_rgb24,
|
||||
int width, int height);
|
||||
|
||||
// Convert ARGB To RAW.
|
||||
LIBYUV_API
|
||||
int ARGBToRAW(const uint8* src_argb, int src_stride_argb,
|
||||
uint8* dst_rgb, int dst_stride_rgb,
|
||||
int width, int height);
|
||||
|
||||
// Convert ARGB To RGB565.
|
||||
LIBYUV_API
|
||||
int ARGBToRGB565(const uint8* src_argb, int src_stride_argb,
|
||||
uint8* dst_rgb565, int dst_stride_rgb565,
|
||||
int width, int height);
|
||||
|
||||
// Convert ARGB To RGB565 with 4x4 dither matrix (16 bytes).
|
||||
// Values in dither matrix from 0 to 7 recommended.
|
||||
// The order of the dither matrix is first byte is upper left.
|
||||
// TODO(fbarchard): Consider pointer to 2d array for dither4x4.
|
||||
// const uint8(*dither)[4][4];
|
||||
LIBYUV_API
|
||||
int ARGBToRGB565Dither(const uint8* src_argb, int src_stride_argb,
|
||||
uint8* dst_rgb565, int dst_stride_rgb565,
|
||||
const uint8* dither4x4, int width, int height);
|
||||
|
||||
// Convert ARGB To ARGB1555.
|
||||
LIBYUV_API
|
||||
int ARGBToARGB1555(const uint8* src_argb, int src_stride_argb,
|
||||
uint8* dst_argb1555, int dst_stride_argb1555,
|
||||
int width, int height);
|
||||
|
||||
// Convert ARGB To ARGB4444.
|
||||
LIBYUV_API
|
||||
int ARGBToARGB4444(const uint8* src_argb, int src_stride_argb,
|
||||
uint8* dst_argb4444, int dst_stride_argb4444,
|
||||
int width, int height);
|
||||
|
||||
// Convert ARGB To I444.
|
||||
LIBYUV_API
|
||||
int ARGBToI444(const uint8* src_argb, int src_stride_argb,
|
||||
uint8* dst_y, int dst_stride_y,
|
||||
uint8* dst_u, int dst_stride_u,
|
||||
uint8* dst_v, int dst_stride_v,
|
||||
int width, int height);
|
||||
|
||||
// Convert ARGB To I422.
|
||||
LIBYUV_API
|
||||
int ARGBToI422(const uint8* src_argb, int src_stride_argb,
|
||||
uint8* dst_y, int dst_stride_y,
|
||||
uint8* dst_u, int dst_stride_u,
|
||||
uint8* dst_v, int dst_stride_v,
|
||||
int width, int height);
|
||||
|
||||
// Convert ARGB To I420. (also in convert.h)
|
||||
LIBYUV_API
|
||||
int ARGBToI420(const uint8* src_argb, int src_stride_argb,
|
||||
uint8* dst_y, int dst_stride_y,
|
||||
uint8* dst_u, int dst_stride_u,
|
||||
uint8* dst_v, int dst_stride_v,
|
||||
int width, int height);
|
||||
|
||||
// Convert ARGB to J420. (JPeg full range I420).
|
||||
LIBYUV_API
|
||||
int ARGBToJ420(const uint8* src_argb, int src_stride_argb,
|
||||
uint8* dst_yj, int dst_stride_yj,
|
||||
uint8* dst_u, int dst_stride_u,
|
||||
uint8* dst_v, int dst_stride_v,
|
||||
int width, int height);
|
||||
|
||||
// Convert ARGB to J422.
|
||||
LIBYUV_API
|
||||
int ARGBToJ422(const uint8* src_argb, int src_stride_argb,
|
||||
uint8* dst_yj, int dst_stride_yj,
|
||||
uint8* dst_u, int dst_stride_u,
|
||||
uint8* dst_v, int dst_stride_v,
|
||||
int width, int height);
|
||||
|
||||
// Convert ARGB To I411.
|
||||
LIBYUV_API
|
||||
int ARGBToI411(const uint8* src_argb, int src_stride_argb,
|
||||
uint8* dst_y, int dst_stride_y,
|
||||
uint8* dst_u, int dst_stride_u,
|
||||
uint8* dst_v, int dst_stride_v,
|
||||
int width, int height);
|
||||
|
||||
// Convert ARGB to J400. (JPeg full range).
|
||||
LIBYUV_API
|
||||
int ARGBToJ400(const uint8* src_argb, int src_stride_argb,
|
||||
uint8* dst_yj, int dst_stride_yj,
|
||||
int width, int height);
|
||||
|
||||
// Convert ARGB to I400.
|
||||
LIBYUV_API
|
||||
int ARGBToI400(const uint8* src_argb, int src_stride_argb,
|
||||
uint8* dst_y, int dst_stride_y,
|
||||
int width, int height);
|
||||
|
||||
// Convert ARGB to G. (Reverse of J400toARGB, which replicates G back to ARGB)
|
||||
LIBYUV_API
|
||||
int ARGBToG(const uint8* src_argb, int src_stride_argb,
|
||||
uint8* dst_g, int dst_stride_g,
|
||||
int width, int height);
|
||||
|
||||
// Convert ARGB To NV12.
|
||||
LIBYUV_API
|
||||
int ARGBToNV12(const uint8* src_argb, int src_stride_argb,
|
||||
uint8* dst_y, int dst_stride_y,
|
||||
uint8* dst_uv, int dst_stride_uv,
|
||||
int width, int height);
|
||||
|
||||
// Convert ARGB To NV21.
|
||||
LIBYUV_API
|
||||
int ARGBToNV21(const uint8* src_argb, int src_stride_argb,
|
||||
uint8* dst_y, int dst_stride_y,
|
||||
uint8* dst_vu, int dst_stride_vu,
|
||||
int width, int height);
|
||||
|
||||
// Convert ARGB To NV21.
|
||||
LIBYUV_API
|
||||
int ARGBToNV21(const uint8* src_argb, int src_stride_argb,
|
||||
uint8* dst_y, int dst_stride_y,
|
||||
uint8* dst_vu, int dst_stride_vu,
|
||||
int width, int height);
|
||||
|
||||
// Convert ARGB To YUY2.
|
||||
LIBYUV_API
|
||||
int ARGBToYUY2(const uint8* src_argb, int src_stride_argb,
|
||||
uint8* dst_yuy2, int dst_stride_yuy2,
|
||||
int width, int height);
|
||||
|
||||
// Convert ARGB To UYVY.
|
||||
LIBYUV_API
|
||||
int ARGBToUYVY(const uint8* src_argb, int src_stride_argb,
|
||||
uint8* dst_uyvy, int dst_stride_uyvy,
|
||||
int width, int height);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
} // namespace libyuv
|
||||
#endif
|
||||
|
||||
#endif // INCLUDE_LIBYUV_CONVERT_FROM_ARGB_H_
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright 2011 The LibYuv Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef INCLUDE_LIBYUV_CPU_ID_H_
|
||||
#define INCLUDE_LIBYUV_CPU_ID_H_
|
||||
|
||||
#include "libyuv/basic_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
namespace libyuv {
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Internal flag to indicate cpuid requires initialization.
|
||||
static const int kCpuInitialized = 0x1;
|
||||
|
||||
// These flags are only valid on ARM processors.
|
||||
static const int kCpuHasARM = 0x2;
|
||||
static const int kCpuHasNEON = 0x4;
|
||||
// 0x8 reserved for future ARM flag.
|
||||
|
||||
// These flags are only valid on x86 processors.
|
||||
static const int kCpuHasX86 = 0x10;
|
||||
static const int kCpuHasSSE2 = 0x20;
|
||||
static const int kCpuHasSSSE3 = 0x40;
|
||||
static const int kCpuHasSSE41 = 0x80;
|
||||
static const int kCpuHasSSE42 = 0x100;
|
||||
static const int kCpuHasAVX = 0x200;
|
||||
static const int kCpuHasAVX2 = 0x400;
|
||||
static const int kCpuHasERMS = 0x800;
|
||||
static const int kCpuHasFMA3 = 0x1000;
|
||||
static const int kCpuHasAVX3 = 0x2000;
|
||||
// 0x2000, 0x4000, 0x8000 reserved for future X86 flags.
|
||||
|
||||
// These flags are only valid on MIPS processors.
|
||||
static const int kCpuHasMIPS = 0x10000;
|
||||
static const int kCpuHasDSPR2 = 0x20000;
|
||||
static const int kCpuHasMSA = 0x40000;
|
||||
|
||||
// Internal function used to auto-init.
|
||||
LIBYUV_API
|
||||
int InitCpuFlags(void);
|
||||
|
||||
// Internal function for parsing /proc/cpuinfo.
|
||||
LIBYUV_API
|
||||
int ArmCpuCaps(const char* cpuinfo_name);
|
||||
|
||||
// Detect CPU has SSE2 etc.
|
||||
// Test_flag parameter should be one of kCpuHas constants above.
|
||||
// returns non-zero if instruction set is detected
|
||||
static __inline int TestCpuFlag(int test_flag) {
|
||||
LIBYUV_API extern int cpu_info_;
|
||||
return (!cpu_info_ ? InitCpuFlags() : cpu_info_) & test_flag;
|
||||
}
|
||||
|
||||
// For testing, allow CPU flags to be disabled.
|
||||
// ie MaskCpuFlags(~kCpuHasSSSE3) to disable SSSE3.
|
||||
// MaskCpuFlags(-1) to enable all cpu specific optimizations.
|
||||
// MaskCpuFlags(1) to disable all cpu specific optimizations.
|
||||
LIBYUV_API
|
||||
void MaskCpuFlags(int enable_flags);
|
||||
|
||||
// Low level cpuid for X86. Returns zeros on other CPUs.
|
||||
// eax is the info type that you want.
|
||||
// ecx is typically the cpu number, and should normally be zero.
|
||||
LIBYUV_API
|
||||
void CpuId(uint32 eax, uint32 ecx, uint32* cpu_info);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
} // namespace libyuv
|
||||
#endif
|
||||
|
||||
#endif // INCLUDE_LIBYUV_CPU_ID_H_
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 2016 The LibYuv Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef INCLUDE_LIBYUV_MACROS_MSA_H_
|
||||
#define INCLUDE_LIBYUV_MACROS_MSA_H_
|
||||
|
||||
#if !defined(LIBYUV_DISABLE_MSA) && defined(__mips_msa)
|
||||
#include <stdint.h>
|
||||
#include <msa.h>
|
||||
|
||||
#define LD_B(RTYPE, psrc) *((RTYPE*)(psrc)) /* NOLINT */
|
||||
#define LD_UB(...) LD_B(v16u8, __VA_ARGS__)
|
||||
|
||||
#define ST_B(RTYPE, in, pdst) *((RTYPE*)(pdst)) = (in) /* NOLINT */
|
||||
#define ST_UB(...) ST_B(v16u8, __VA_ARGS__)
|
||||
|
||||
/* Description : Load two vectors with 16 'byte' sized elements
|
||||
Arguments : Inputs - psrc, stride
|
||||
Outputs - out0, out1
|
||||
Return Type - as per RTYPE
|
||||
Details : Load 16 byte elements in 'out0' from (psrc)
|
||||
Load 16 byte elements in 'out1' from (psrc + stride)
|
||||
*/
|
||||
#define LD_B2(RTYPE, psrc, stride, out0, out1) { \
|
||||
out0 = LD_B(RTYPE, (psrc)); \
|
||||
out1 = LD_B(RTYPE, (psrc) + stride); \
|
||||
}
|
||||
#define LD_UB2(...) LD_B2(v16u8, __VA_ARGS__)
|
||||
|
||||
#define LD_B4(RTYPE, psrc, stride, out0, out1, out2, out3) { \
|
||||
LD_B2(RTYPE, (psrc), stride, out0, out1); \
|
||||
LD_B2(RTYPE, (psrc) + 2 * stride , stride, out2, out3); \
|
||||
}
|
||||
#define LD_UB4(...) LD_B4(v16u8, __VA_ARGS__)
|
||||
|
||||
/* Description : Store two vectors with stride each having 16 'byte' sized
|
||||
elements
|
||||
Arguments : Inputs - in0, in1, pdst, stride
|
||||
Details : Store 16 byte elements from 'in0' to (pdst)
|
||||
Store 16 byte elements from 'in1' to (pdst + stride)
|
||||
*/
|
||||
#define ST_B2(RTYPE, in0, in1, pdst, stride) { \
|
||||
ST_B(RTYPE, in0, (pdst)); \
|
||||
ST_B(RTYPE, in1, (pdst) + stride); \
|
||||
}
|
||||
#define ST_UB2(...) ST_B2(v16u8, __VA_ARGS__)
|
||||
#
|
||||
#define ST_B4(RTYPE, in0, in1, in2, in3, pdst, stride) { \
|
||||
ST_B2(RTYPE, in0, in1, (pdst), stride); \
|
||||
ST_B2(RTYPE, in2, in3, (pdst) + 2 * stride, stride); \
|
||||
}
|
||||
#define ST_UB4(...) ST_B4(v16u8, __VA_ARGS__)
|
||||
#
|
||||
/* Description : Shuffle byte vector elements as per mask vector
|
||||
Arguments : Inputs - in0, in1, in2, in3, mask0, mask1
|
||||
Outputs - out0, out1
|
||||
Return Type - as per RTYPE
|
||||
Details : Byte elements from 'in0' & 'in1' are copied selectively to
|
||||
'out0' as per control vector 'mask0'
|
||||
*/
|
||||
#define VSHF_B2(RTYPE, in0, in1, in2, in3, mask0, mask1, out0, out1) { \
|
||||
out0 = (RTYPE) __msa_vshf_b((v16i8) mask0, (v16i8) in1, (v16i8) in0); \
|
||||
out1 = (RTYPE) __msa_vshf_b((v16i8) mask1, (v16i8) in3, (v16i8) in2); \
|
||||
}
|
||||
#define VSHF_B2_UB(...) VSHF_B2(v16u8, __VA_ARGS__)
|
||||
|
||||
#endif /* !defined(LIBYUV_DISABLE_MSA) && defined(__mips_msa) */
|
||||
|
||||
#endif // INCLUDE_LIBYUV_MACROS_MSA_H_
|
||||
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
* Copyright 2012 The LibYuv Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef INCLUDE_LIBYUV_MJPEG_DECODER_H_
|
||||
#define INCLUDE_LIBYUV_MJPEG_DECODER_H_
|
||||
|
||||
#include "libyuv/basic_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
// NOTE: For a simplified public API use convert.h MJPGToI420().
|
||||
|
||||
struct jpeg_common_struct;
|
||||
struct jpeg_decompress_struct;
|
||||
struct jpeg_source_mgr;
|
||||
|
||||
namespace libyuv {
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
LIBYUV_BOOL ValidateJpeg(const uint8* sample, size_t sample_size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
static const uint32 kUnknownDataSize = 0xFFFFFFFF;
|
||||
|
||||
enum JpegSubsamplingType {
|
||||
kJpegYuv420,
|
||||
kJpegYuv422,
|
||||
kJpegYuv411,
|
||||
kJpegYuv444,
|
||||
kJpegYuv400,
|
||||
kJpegUnknown
|
||||
};
|
||||
|
||||
struct Buffer {
|
||||
const uint8* data;
|
||||
int len;
|
||||
};
|
||||
|
||||
struct BufferVector {
|
||||
Buffer* buffers;
|
||||
int len;
|
||||
int pos;
|
||||
};
|
||||
|
||||
struct SetJmpErrorMgr;
|
||||
|
||||
// MJPEG ("Motion JPEG") is a pseudo-standard video codec where the frames are
|
||||
// simply independent JPEG images with a fixed huffman table (which is omitted).
|
||||
// It is rarely used in video transmission, but is common as a camera capture
|
||||
// format, especially in Logitech devices. This class implements a decoder for
|
||||
// MJPEG frames.
|
||||
//
|
||||
// See http://tools.ietf.org/html/rfc2435
|
||||
class LIBYUV_API MJpegDecoder {
|
||||
public:
|
||||
typedef void (*CallbackFunction)(void* opaque,
|
||||
const uint8* const* data,
|
||||
const int* strides,
|
||||
int rows);
|
||||
|
||||
static const int kColorSpaceUnknown;
|
||||
static const int kColorSpaceGrayscale;
|
||||
static const int kColorSpaceRgb;
|
||||
static const int kColorSpaceYCbCr;
|
||||
static const int kColorSpaceCMYK;
|
||||
static const int kColorSpaceYCCK;
|
||||
|
||||
MJpegDecoder();
|
||||
~MJpegDecoder();
|
||||
|
||||
// Loads a new frame, reads its headers, and determines the uncompressed
|
||||
// image format.
|
||||
// Returns LIBYUV_TRUE if image looks valid and format is supported.
|
||||
// If return value is LIBYUV_TRUE, then the values for all the following
|
||||
// getters are populated.
|
||||
// src_len is the size of the compressed mjpeg frame in bytes.
|
||||
LIBYUV_BOOL LoadFrame(const uint8* src, size_t src_len);
|
||||
|
||||
// Returns width of the last loaded frame in pixels.
|
||||
int GetWidth();
|
||||
|
||||
// Returns height of the last loaded frame in pixels.
|
||||
int GetHeight();
|
||||
|
||||
// Returns format of the last loaded frame. The return value is one of the
|
||||
// kColorSpace* constants.
|
||||
int GetColorSpace();
|
||||
|
||||
// Number of color components in the color space.
|
||||
int GetNumComponents();
|
||||
|
||||
// Sample factors of the n-th component.
|
||||
int GetHorizSampFactor(int component);
|
||||
|
||||
int GetVertSampFactor(int component);
|
||||
|
||||
int GetHorizSubSampFactor(int component);
|
||||
|
||||
int GetVertSubSampFactor(int component);
|
||||
|
||||
// Public for testability.
|
||||
int GetImageScanlinesPerImcuRow();
|
||||
|
||||
// Public for testability.
|
||||
int GetComponentScanlinesPerImcuRow(int component);
|
||||
|
||||
// Width of a component in bytes.
|
||||
int GetComponentWidth(int component);
|
||||
|
||||
// Height of a component.
|
||||
int GetComponentHeight(int component);
|
||||
|
||||
// Width of a component in bytes with padding for DCTSIZE. Public for testing.
|
||||
int GetComponentStride(int component);
|
||||
|
||||
// Size of a component in bytes.
|
||||
int GetComponentSize(int component);
|
||||
|
||||
// Call this after LoadFrame() if you decide you don't want to decode it
|
||||
// after all.
|
||||
LIBYUV_BOOL UnloadFrame();
|
||||
|
||||
// Decodes the entire image into a one-buffer-per-color-component format.
|
||||
// dst_width must match exactly. dst_height must be <= to image height; if
|
||||
// less, the image is cropped. "planes" must have size equal to at least
|
||||
// GetNumComponents() and they must point to non-overlapping buffers of size
|
||||
// at least GetComponentSize(i). The pointers in planes are incremented
|
||||
// to point to after the end of the written data.
|
||||
// TODO(fbarchard): Add dst_x, dst_y to allow specific rect to be decoded.
|
||||
LIBYUV_BOOL DecodeToBuffers(uint8** planes, int dst_width, int dst_height);
|
||||
|
||||
// Decodes the entire image and passes the data via repeated calls to a
|
||||
// callback function. Each call will get the data for a whole number of
|
||||
// image scanlines.
|
||||
// TODO(fbarchard): Add dst_x, dst_y to allow specific rect to be decoded.
|
||||
LIBYUV_BOOL DecodeToCallback(CallbackFunction fn, void* opaque,
|
||||
int dst_width, int dst_height);
|
||||
|
||||
// The helper function which recognizes the jpeg sub-sampling type.
|
||||
static JpegSubsamplingType JpegSubsamplingTypeHelper(
|
||||
int* subsample_x, int* subsample_y, int number_of_components);
|
||||
|
||||
private:
|
||||
void AllocOutputBuffers(int num_outbufs);
|
||||
void DestroyOutputBuffers();
|
||||
|
||||
LIBYUV_BOOL StartDecode();
|
||||
LIBYUV_BOOL FinishDecode();
|
||||
|
||||
void SetScanlinePointers(uint8** data);
|
||||
LIBYUV_BOOL DecodeImcuRow();
|
||||
|
||||
int GetComponentScanlinePadding(int component);
|
||||
|
||||
// A buffer holding the input data for a frame.
|
||||
Buffer buf_;
|
||||
BufferVector buf_vec_;
|
||||
|
||||
jpeg_decompress_struct* decompress_struct_;
|
||||
jpeg_source_mgr* source_mgr_;
|
||||
SetJmpErrorMgr* error_mgr_;
|
||||
|
||||
// LIBYUV_TRUE iff at least one component has scanline padding. (i.e.,
|
||||
// GetComponentScanlinePadding() != 0.)
|
||||
LIBYUV_BOOL has_scanline_padding_;
|
||||
|
||||
// Temporaries used to point to scanline outputs.
|
||||
int num_outbufs_; // Outermost size of all arrays below.
|
||||
uint8*** scanlines_;
|
||||
int* scanlines_sizes_;
|
||||
// Temporary buffer used for decoding when we can't decode directly to the
|
||||
// output buffers. Large enough for just one iMCU row.
|
||||
uint8** databuf_;
|
||||
int* databuf_strides_;
|
||||
};
|
||||
|
||||
} // namespace libyuv
|
||||
|
||||
#endif // __cplusplus
|
||||
#endif // INCLUDE_LIBYUV_MJPEG_DECODER_H_
|
||||
@@ -0,0 +1,529 @@
|
||||
/*
|
||||
* Copyright 2011 The LibYuv Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef INCLUDE_LIBYUV_PLANAR_FUNCTIONS_H_
|
||||
#define INCLUDE_LIBYUV_PLANAR_FUNCTIONS_H_
|
||||
|
||||
#include "libyuv/basic_types.h"
|
||||
|
||||
// TODO(fbarchard): Remove the following headers includes.
|
||||
#include "libyuv/convert.h"
|
||||
#include "libyuv/convert_argb.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
namespace libyuv {
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Copy a plane of data.
|
||||
LIBYUV_API
|
||||
void CopyPlane(const uint8* src_y, int src_stride_y,
|
||||
uint8* dst_y, int dst_stride_y,
|
||||
int width, int height);
|
||||
|
||||
LIBYUV_API
|
||||
void CopyPlane_16(const uint16* src_y, int src_stride_y,
|
||||
uint16* dst_y, int dst_stride_y,
|
||||
int width, int height);
|
||||
|
||||
// Set a plane of data to a 32 bit value.
|
||||
LIBYUV_API
|
||||
void SetPlane(uint8* dst_y, int dst_stride_y,
|
||||
int width, int height,
|
||||
uint32 value);
|
||||
|
||||
// Split interleaved UV plane into separate U and V planes.
|
||||
LIBYUV_API
|
||||
void SplitUVPlane(const uint8* src_uv, int src_stride_uv,
|
||||
uint8* dst_u, int dst_stride_u,
|
||||
uint8* dst_v, int dst_stride_v,
|
||||
int width, int height);
|
||||
|
||||
// Merge separate U and V planes into one interleaved UV plane.
|
||||
LIBYUV_API
|
||||
void MergeUVPlane(const uint8* src_u, int src_stride_u,
|
||||
const uint8* src_v, int src_stride_v,
|
||||
uint8* dst_uv, int dst_stride_uv,
|
||||
int width, int height);
|
||||
|
||||
// Copy I400. Supports inverting.
|
||||
LIBYUV_API
|
||||
int I400ToI400(const uint8* src_y, int src_stride_y,
|
||||
uint8* dst_y, int dst_stride_y,
|
||||
int width, int height);
|
||||
|
||||
#define J400ToJ400 I400ToI400
|
||||
|
||||
// Copy I422 to I422.
|
||||
#define I422ToI422 I422Copy
|
||||
LIBYUV_API
|
||||
int I422Copy(const uint8* src_y, int src_stride_y,
|
||||
const uint8* src_u, int src_stride_u,
|
||||
const uint8* src_v, int src_stride_v,
|
||||
uint8* dst_y, int dst_stride_y,
|
||||
uint8* dst_u, int dst_stride_u,
|
||||
uint8* dst_v, int dst_stride_v,
|
||||
int width, int height);
|
||||
|
||||
// Copy I444 to I444.
|
||||
#define I444ToI444 I444Copy
|
||||
LIBYUV_API
|
||||
int I444Copy(const uint8* src_y, int src_stride_y,
|
||||
const uint8* src_u, int src_stride_u,
|
||||
const uint8* src_v, int src_stride_v,
|
||||
uint8* dst_y, int dst_stride_y,
|
||||
uint8* dst_u, int dst_stride_u,
|
||||
uint8* dst_v, int dst_stride_v,
|
||||
int width, int height);
|
||||
|
||||
// Convert YUY2 to I422.
|
||||
LIBYUV_API
|
||||
int YUY2ToI422(const uint8* src_yuy2, int src_stride_yuy2,
|
||||
uint8* dst_y, int dst_stride_y,
|
||||
uint8* dst_u, int dst_stride_u,
|
||||
uint8* dst_v, int dst_stride_v,
|
||||
int width, int height);
|
||||
|
||||
// Convert UYVY to I422.
|
||||
LIBYUV_API
|
||||
int UYVYToI422(const uint8* src_uyvy, int src_stride_uyvy,
|
||||
uint8* dst_y, int dst_stride_y,
|
||||
uint8* dst_u, int dst_stride_u,
|
||||
uint8* dst_v, int dst_stride_v,
|
||||
int width, int height);
|
||||
|
||||
LIBYUV_API
|
||||
int YUY2ToNV12(const uint8* src_yuy2, int src_stride_yuy2,
|
||||
uint8* dst_y, int dst_stride_y,
|
||||
uint8* dst_uv, int dst_stride_uv,
|
||||
int width, int height);
|
||||
|
||||
LIBYUV_API
|
||||
int UYVYToNV12(const uint8* src_uyvy, int src_stride_uyvy,
|
||||
uint8* dst_y, int dst_stride_y,
|
||||
uint8* dst_uv, int dst_stride_uv,
|
||||
int width, int height);
|
||||
|
||||
// Convert I420 to I400. (calls CopyPlane ignoring u/v).
|
||||
LIBYUV_API
|
||||
int I420ToI400(const uint8* src_y, int src_stride_y,
|
||||
const uint8* src_u, int src_stride_u,
|
||||
const uint8* src_v, int src_stride_v,
|
||||
uint8* dst_y, int dst_stride_y,
|
||||
int width, int height);
|
||||
|
||||
// Alias
|
||||
#define J420ToJ400 I420ToI400
|
||||
#define I420ToI420Mirror I420Mirror
|
||||
|
||||
// I420 mirror.
|
||||
LIBYUV_API
|
||||
int I420Mirror(const uint8* src_y, int src_stride_y,
|
||||
const uint8* src_u, int src_stride_u,
|
||||
const uint8* src_v, int src_stride_v,
|
||||
uint8* dst_y, int dst_stride_y,
|
||||
uint8* dst_u, int dst_stride_u,
|
||||
uint8* dst_v, int dst_stride_v,
|
||||
int width, int height);
|
||||
|
||||
// Alias
|
||||
#define I400ToI400Mirror I400Mirror
|
||||
|
||||
// I400 mirror. A single plane is mirrored horizontally.
|
||||
// Pass negative height to achieve 180 degree rotation.
|
||||
LIBYUV_API
|
||||
int I400Mirror(const uint8* src_y, int src_stride_y,
|
||||
uint8* dst_y, int dst_stride_y,
|
||||
int width, int height);
|
||||
|
||||
// Alias
|
||||
#define ARGBToARGBMirror ARGBMirror
|
||||
|
||||
// ARGB mirror.
|
||||
LIBYUV_API
|
||||
int ARGBMirror(const uint8* src_argb, int src_stride_argb,
|
||||
uint8* dst_argb, int dst_stride_argb,
|
||||
int width, int height);
|
||||
|
||||
// Convert NV12 to RGB565.
|
||||
LIBYUV_API
|
||||
int NV12ToRGB565(const uint8* src_y, int src_stride_y,
|
||||
const uint8* src_uv, int src_stride_uv,
|
||||
uint8* dst_rgb565, int dst_stride_rgb565,
|
||||
int width, int height);
|
||||
|
||||
// I422ToARGB is in convert_argb.h
|
||||
// Convert I422 to BGRA.
|
||||
LIBYUV_API
|
||||
int I422ToBGRA(const uint8* src_y, int src_stride_y,
|
||||
const uint8* src_u, int src_stride_u,
|
||||
const uint8* src_v, int src_stride_v,
|
||||
uint8* dst_bgra, int dst_stride_bgra,
|
||||
int width, int height);
|
||||
|
||||
// Convert I422 to ABGR.
|
||||
LIBYUV_API
|
||||
int I422ToABGR(const uint8* src_y, int src_stride_y,
|
||||
const uint8* src_u, int src_stride_u,
|
||||
const uint8* src_v, int src_stride_v,
|
||||
uint8* dst_abgr, int dst_stride_abgr,
|
||||
int width, int height);
|
||||
|
||||
// Convert I422 to RGBA.
|
||||
LIBYUV_API
|
||||
int I422ToRGBA(const uint8* src_y, int src_stride_y,
|
||||
const uint8* src_u, int src_stride_u,
|
||||
const uint8* src_v, int src_stride_v,
|
||||
uint8* dst_rgba, int dst_stride_rgba,
|
||||
int width, int height);
|
||||
|
||||
// Alias
|
||||
#define RGB24ToRAW RAWToRGB24
|
||||
|
||||
LIBYUV_API
|
||||
int RAWToRGB24(const uint8* src_raw, int src_stride_raw,
|
||||
uint8* dst_rgb24, int dst_stride_rgb24,
|
||||
int width, int height);
|
||||
|
||||
// Draw a rectangle into I420.
|
||||
LIBYUV_API
|
||||
int I420Rect(uint8* dst_y, int dst_stride_y,
|
||||
uint8* dst_u, int dst_stride_u,
|
||||
uint8* dst_v, int dst_stride_v,
|
||||
int x, int y, int width, int height,
|
||||
int value_y, int value_u, int value_v);
|
||||
|
||||
// Draw a rectangle into ARGB.
|
||||
LIBYUV_API
|
||||
int ARGBRect(uint8* dst_argb, int dst_stride_argb,
|
||||
int x, int y, int width, int height, uint32 value);
|
||||
|
||||
// Convert ARGB to gray scale ARGB.
|
||||
LIBYUV_API
|
||||
int ARGBGrayTo(const uint8* src_argb, int src_stride_argb,
|
||||
uint8* dst_argb, int dst_stride_argb,
|
||||
int width, int height);
|
||||
|
||||
// Make a rectangle of ARGB gray scale.
|
||||
LIBYUV_API
|
||||
int ARGBGray(uint8* dst_argb, int dst_stride_argb,
|
||||
int x, int y, int width, int height);
|
||||
|
||||
// Make a rectangle of ARGB Sepia tone.
|
||||
LIBYUV_API
|
||||
int ARGBSepia(uint8* dst_argb, int dst_stride_argb,
|
||||
int x, int y, int width, int height);
|
||||
|
||||
// Apply a matrix rotation to each ARGB pixel.
|
||||
// matrix_argb is 4 signed ARGB values. -128 to 127 representing -2 to 2.
|
||||
// The first 4 coefficients apply to B, G, R, A and produce B of the output.
|
||||
// The next 4 coefficients apply to B, G, R, A and produce G of the output.
|
||||
// The next 4 coefficients apply to B, G, R, A and produce R of the output.
|
||||
// The last 4 coefficients apply to B, G, R, A and produce A of the output.
|
||||
LIBYUV_API
|
||||
int ARGBColorMatrix(const uint8* src_argb, int src_stride_argb,
|
||||
uint8* dst_argb, int dst_stride_argb,
|
||||
const int8* matrix_argb,
|
||||
int width, int height);
|
||||
|
||||
// Deprecated. Use ARGBColorMatrix instead.
|
||||
// Apply a matrix rotation to each ARGB pixel.
|
||||
// matrix_argb is 3 signed ARGB values. -128 to 127 representing -1 to 1.
|
||||
// The first 4 coefficients apply to B, G, R, A and produce B of the output.
|
||||
// The next 4 coefficients apply to B, G, R, A and produce G of the output.
|
||||
// The last 4 coefficients apply to B, G, R, A and produce R of the output.
|
||||
LIBYUV_API
|
||||
int RGBColorMatrix(uint8* dst_argb, int dst_stride_argb,
|
||||
const int8* matrix_rgb,
|
||||
int x, int y, int width, int height);
|
||||
|
||||
// Apply a color table each ARGB pixel.
|
||||
// Table contains 256 ARGB values.
|
||||
LIBYUV_API
|
||||
int ARGBColorTable(uint8* dst_argb, int dst_stride_argb,
|
||||
const uint8* table_argb,
|
||||
int x, int y, int width, int height);
|
||||
|
||||
// Apply a color table each ARGB pixel but preserve destination alpha.
|
||||
// Table contains 256 ARGB values.
|
||||
LIBYUV_API
|
||||
int RGBColorTable(uint8* dst_argb, int dst_stride_argb,
|
||||
const uint8* table_argb,
|
||||
int x, int y, int width, int height);
|
||||
|
||||
// Apply a luma/color table each ARGB pixel but preserve destination alpha.
|
||||
// Table contains 32768 values indexed by [Y][C] where 7 it 7 bit luma from
|
||||
// RGB (YJ style) and C is an 8 bit color component (R, G or B).
|
||||
LIBYUV_API
|
||||
int ARGBLumaColorTable(const uint8* src_argb, int src_stride_argb,
|
||||
uint8* dst_argb, int dst_stride_argb,
|
||||
const uint8* luma_rgb_table,
|
||||
int width, int height);
|
||||
|
||||
// Apply a 3 term polynomial to ARGB values.
|
||||
// poly points to a 4x4 matrix. The first row is constants. The 2nd row is
|
||||
// coefficients for b, g, r and a. The 3rd row is coefficients for b squared,
|
||||
// g squared, r squared and a squared. The 4rd row is coefficients for b to
|
||||
// the 3, g to the 3, r to the 3 and a to the 3. The values are summed and
|
||||
// result clamped to 0 to 255.
|
||||
// A polynomial approximation can be dirived using software such as 'R'.
|
||||
|
||||
LIBYUV_API
|
||||
int ARGBPolynomial(const uint8* src_argb, int src_stride_argb,
|
||||
uint8* dst_argb, int dst_stride_argb,
|
||||
const float* poly,
|
||||
int width, int height);
|
||||
|
||||
// Convert plane of 16 bit shorts to half floats.
|
||||
// Source values are multiplied by scale before storing as half float.
|
||||
LIBYUV_API
|
||||
int HalfFloatPlane(const uint16* src_y, int src_stride_y,
|
||||
uint16* dst_y, int dst_stride_y,
|
||||
float scale,
|
||||
int width, int height);
|
||||
|
||||
// Quantize a rectangle of ARGB. Alpha unaffected.
|
||||
// scale is a 16 bit fractional fixed point scaler between 0 and 65535.
|
||||
// interval_size should be a value between 1 and 255.
|
||||
// interval_offset should be a value between 0 and 255.
|
||||
LIBYUV_API
|
||||
int ARGBQuantize(uint8* dst_argb, int dst_stride_argb,
|
||||
int scale, int interval_size, int interval_offset,
|
||||
int x, int y, int width, int height);
|
||||
|
||||
// Copy ARGB to ARGB.
|
||||
LIBYUV_API
|
||||
int ARGBCopy(const uint8* src_argb, int src_stride_argb,
|
||||
uint8* dst_argb, int dst_stride_argb,
|
||||
int width, int height);
|
||||
|
||||
// Copy Alpha channel of ARGB to alpha of ARGB.
|
||||
LIBYUV_API
|
||||
int ARGBCopyAlpha(const uint8* src_argb, int src_stride_argb,
|
||||
uint8* dst_argb, int dst_stride_argb,
|
||||
int width, int height);
|
||||
|
||||
// Extract the alpha channel from ARGB.
|
||||
LIBYUV_API
|
||||
int ARGBExtractAlpha(const uint8* src_argb, int src_stride_argb,
|
||||
uint8* dst_a, int dst_stride_a,
|
||||
int width, int height);
|
||||
|
||||
// Copy Y channel to Alpha of ARGB.
|
||||
LIBYUV_API
|
||||
int ARGBCopyYToAlpha(const uint8* src_y, int src_stride_y,
|
||||
uint8* dst_argb, int dst_stride_argb,
|
||||
int width, int height);
|
||||
|
||||
typedef void (*ARGBBlendRow)(const uint8* src_argb0, const uint8* src_argb1,
|
||||
uint8* dst_argb, int width);
|
||||
|
||||
// Get function to Alpha Blend ARGB pixels and store to destination.
|
||||
LIBYUV_API
|
||||
ARGBBlendRow GetARGBBlend();
|
||||
|
||||
// Alpha Blend ARGB images and store to destination.
|
||||
// Source is pre-multiplied by alpha using ARGBAttenuate.
|
||||
// Alpha of destination is set to 255.
|
||||
LIBYUV_API
|
||||
int ARGBBlend(const uint8* src_argb0, int src_stride_argb0,
|
||||
const uint8* src_argb1, int src_stride_argb1,
|
||||
uint8* dst_argb, int dst_stride_argb,
|
||||
int width, int height);
|
||||
|
||||
// Alpha Blend plane and store to destination.
|
||||
// Source is not pre-multiplied by alpha.
|
||||
LIBYUV_API
|
||||
int BlendPlane(const uint8* src_y0, int src_stride_y0,
|
||||
const uint8* src_y1, int src_stride_y1,
|
||||
const uint8* alpha, int alpha_stride,
|
||||
uint8* dst_y, int dst_stride_y,
|
||||
int width, int height);
|
||||
|
||||
// Alpha Blend YUV images and store to destination.
|
||||
// Source is not pre-multiplied by alpha.
|
||||
// Alpha is full width x height and subsampled to half size to apply to UV.
|
||||
LIBYUV_API
|
||||
int I420Blend(const uint8* src_y0, int src_stride_y0,
|
||||
const uint8* src_u0, int src_stride_u0,
|
||||
const uint8* src_v0, int src_stride_v0,
|
||||
const uint8* src_y1, int src_stride_y1,
|
||||
const uint8* src_u1, int src_stride_u1,
|
||||
const uint8* src_v1, int src_stride_v1,
|
||||
const uint8* alpha, int alpha_stride,
|
||||
uint8* dst_y, int dst_stride_y,
|
||||
uint8* dst_u, int dst_stride_u,
|
||||
uint8* dst_v, int dst_stride_v,
|
||||
int width, int height);
|
||||
|
||||
// Multiply ARGB image by ARGB image. Shifted down by 8. Saturates to 255.
|
||||
LIBYUV_API
|
||||
int ARGBMultiply(const uint8* src_argb0, int src_stride_argb0,
|
||||
const uint8* src_argb1, int src_stride_argb1,
|
||||
uint8* dst_argb, int dst_stride_argb,
|
||||
int width, int height);
|
||||
|
||||
// Add ARGB image with ARGB image. Saturates to 255.
|
||||
LIBYUV_API
|
||||
int ARGBAdd(const uint8* src_argb0, int src_stride_argb0,
|
||||
const uint8* src_argb1, int src_stride_argb1,
|
||||
uint8* dst_argb, int dst_stride_argb,
|
||||
int width, int height);
|
||||
|
||||
// Subtract ARGB image (argb1) from ARGB image (argb0). Saturates to 0.
|
||||
LIBYUV_API
|
||||
int ARGBSubtract(const uint8* src_argb0, int src_stride_argb0,
|
||||
const uint8* src_argb1, int src_stride_argb1,
|
||||
uint8* dst_argb, int dst_stride_argb,
|
||||
int width, int height);
|
||||
|
||||
// Convert I422 to YUY2.
|
||||
LIBYUV_API
|
||||
int I422ToYUY2(const uint8* src_y, int src_stride_y,
|
||||
const uint8* src_u, int src_stride_u,
|
||||
const uint8* src_v, int src_stride_v,
|
||||
uint8* dst_frame, int dst_stride_frame,
|
||||
int width, int height);
|
||||
|
||||
// Convert I422 to UYVY.
|
||||
LIBYUV_API
|
||||
int I422ToUYVY(const uint8* src_y, int src_stride_y,
|
||||
const uint8* src_u, int src_stride_u,
|
||||
const uint8* src_v, int src_stride_v,
|
||||
uint8* dst_frame, int dst_stride_frame,
|
||||
int width, int height);
|
||||
|
||||
// Convert unattentuated ARGB to preattenuated ARGB.
|
||||
LIBYUV_API
|
||||
int ARGBAttenuate(const uint8* src_argb, int src_stride_argb,
|
||||
uint8* dst_argb, int dst_stride_argb,
|
||||
int width, int height);
|
||||
|
||||
// Convert preattentuated ARGB to unattenuated ARGB.
|
||||
LIBYUV_API
|
||||
int ARGBUnattenuate(const uint8* src_argb, int src_stride_argb,
|
||||
uint8* dst_argb, int dst_stride_argb,
|
||||
int width, int height);
|
||||
|
||||
// Internal function - do not call directly.
|
||||
// Computes table of cumulative sum for image where the value is the sum
|
||||
// of all values above and to the left of the entry. Used by ARGBBlur.
|
||||
LIBYUV_API
|
||||
int ARGBComputeCumulativeSum(const uint8* src_argb, int src_stride_argb,
|
||||
int32* dst_cumsum, int dst_stride32_cumsum,
|
||||
int width, int height);
|
||||
|
||||
// Blur ARGB image.
|
||||
// dst_cumsum table of width * (height + 1) * 16 bytes aligned to
|
||||
// 16 byte boundary.
|
||||
// dst_stride32_cumsum is number of ints in a row (width * 4).
|
||||
// radius is number of pixels around the center. e.g. 1 = 3x3. 2=5x5.
|
||||
// Blur is optimized for radius of 5 (11x11) or less.
|
||||
LIBYUV_API
|
||||
int ARGBBlur(const uint8* src_argb, int src_stride_argb,
|
||||
uint8* dst_argb, int dst_stride_argb,
|
||||
int32* dst_cumsum, int dst_stride32_cumsum,
|
||||
int width, int height, int radius);
|
||||
|
||||
// Multiply ARGB image by ARGB value.
|
||||
LIBYUV_API
|
||||
int ARGBShade(const uint8* src_argb, int src_stride_argb,
|
||||
uint8* dst_argb, int dst_stride_argb,
|
||||
int width, int height, uint32 value);
|
||||
|
||||
// Interpolate between two images using specified amount of interpolation
|
||||
// (0 to 255) and store to destination.
|
||||
// 'interpolation' is specified as 8 bit fraction where 0 means 100% src0
|
||||
// and 255 means 1% src0 and 99% src1.
|
||||
LIBYUV_API
|
||||
int InterpolatePlane(const uint8* src0, int src_stride0,
|
||||
const uint8* src1, int src_stride1,
|
||||
uint8* dst, int dst_stride,
|
||||
int width, int height, int interpolation);
|
||||
|
||||
// Interpolate between two ARGB images using specified amount of interpolation
|
||||
// Internally calls InterpolatePlane with width * 4 (bpp).
|
||||
LIBYUV_API
|
||||
int ARGBInterpolate(const uint8* src_argb0, int src_stride_argb0,
|
||||
const uint8* src_argb1, int src_stride_argb1,
|
||||
uint8* dst_argb, int dst_stride_argb,
|
||||
int width, int height, int interpolation);
|
||||
|
||||
// Interpolate between two YUV images using specified amount of interpolation
|
||||
// Internally calls InterpolatePlane on each plane where the U and V planes
|
||||
// are half width and half height.
|
||||
LIBYUV_API
|
||||
int I420Interpolate(const uint8* src0_y, int src0_stride_y,
|
||||
const uint8* src0_u, int src0_stride_u,
|
||||
const uint8* src0_v, int src0_stride_v,
|
||||
const uint8* src1_y, int src1_stride_y,
|
||||
const uint8* src1_u, int src1_stride_u,
|
||||
const uint8* src1_v, int src1_stride_v,
|
||||
uint8* dst_y, int dst_stride_y,
|
||||
uint8* dst_u, int dst_stride_u,
|
||||
uint8* dst_v, int dst_stride_v,
|
||||
int width, int height, int interpolation);
|
||||
|
||||
#if defined(__pnacl__) || defined(__CLR_VER) || \
|
||||
(defined(__i386__) && !defined(__SSE2__))
|
||||
#define LIBYUV_DISABLE_X86
|
||||
#endif
|
||||
// MemorySanitizer does not support assembly code yet. http://crbug.com/344505
|
||||
#if defined(__has_feature)
|
||||
#if __has_feature(memory_sanitizer)
|
||||
#define LIBYUV_DISABLE_X86
|
||||
#endif
|
||||
#endif
|
||||
// The following are available on all x86 platforms:
|
||||
#if !defined(LIBYUV_DISABLE_X86) && \
|
||||
(defined(_M_IX86) || defined(__x86_64__) || defined(__i386__))
|
||||
#define HAS_ARGBAFFINEROW_SSE2
|
||||
#endif
|
||||
|
||||
// Row function for copying pixels from a source with a slope to a row
|
||||
// of destination. Useful for scaling, rotation, mirror, texture mapping.
|
||||
LIBYUV_API
|
||||
void ARGBAffineRow_C(const uint8* src_argb, int src_argb_stride,
|
||||
uint8* dst_argb, const float* uv_dudv, int width);
|
||||
LIBYUV_API
|
||||
void ARGBAffineRow_SSE2(const uint8* src_argb, int src_argb_stride,
|
||||
uint8* dst_argb, const float* uv_dudv, int width);
|
||||
|
||||
// Shuffle ARGB channel order. e.g. BGRA to ARGB.
|
||||
// shuffler is 16 bytes and must be aligned.
|
||||
LIBYUV_API
|
||||
int ARGBShuffle(const uint8* src_bgra, int src_stride_bgra,
|
||||
uint8* dst_argb, int dst_stride_argb,
|
||||
const uint8* shuffler, int width, int height);
|
||||
|
||||
// Sobel ARGB effect with planar output.
|
||||
LIBYUV_API
|
||||
int ARGBSobelToPlane(const uint8* src_argb, int src_stride_argb,
|
||||
uint8* dst_y, int dst_stride_y,
|
||||
int width, int height);
|
||||
|
||||
// Sobel ARGB effect.
|
||||
LIBYUV_API
|
||||
int ARGBSobel(const uint8* src_argb, int src_stride_argb,
|
||||
uint8* dst_argb, int dst_stride_argb,
|
||||
int width, int height);
|
||||
|
||||
// Sobel ARGB effect w/ Sobel X, Sobel, Sobel Y in ARGB.
|
||||
LIBYUV_API
|
||||
int ARGBSobelXY(const uint8* src_argb, int src_stride_argb,
|
||||
uint8* dst_argb, int dst_stride_argb,
|
||||
int width, int height);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
} // namespace libyuv
|
||||
#endif
|
||||
|
||||
#endif // INCLUDE_LIBYUV_PLANAR_FUNCTIONS_H_
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright 2011 The LibYuv Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef INCLUDE_LIBYUV_ROTATE_H_
|
||||
#define INCLUDE_LIBYUV_ROTATE_H_
|
||||
|
||||
#include "libyuv/basic_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
namespace libyuv {
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Supported rotation.
|
||||
typedef enum RotationMode {
|
||||
kRotate0 = 0, // No rotation.
|
||||
kRotate90 = 90, // Rotate 90 degrees clockwise.
|
||||
kRotate180 = 180, // Rotate 180 degrees.
|
||||
kRotate270 = 270, // Rotate 270 degrees clockwise.
|
||||
|
||||
// Deprecated.
|
||||
kRotateNone = 0,
|
||||
kRotateClockwise = 90,
|
||||
kRotateCounterClockwise = 270,
|
||||
} RotationModeEnum;
|
||||
|
||||
// Rotate I420 frame.
|
||||
LIBYUV_API
|
||||
int I420Rotate(const uint8* src_y, int src_stride_y,
|
||||
const uint8* src_u, int src_stride_u,
|
||||
const uint8* src_v, int src_stride_v,
|
||||
uint8* dst_y, int dst_stride_y,
|
||||
uint8* dst_u, int dst_stride_u,
|
||||
uint8* dst_v, int dst_stride_v,
|
||||
int src_width, int src_height, enum RotationMode mode);
|
||||
|
||||
// Rotate NV12 input and store in I420.
|
||||
LIBYUV_API
|
||||
int NV12ToI420Rotate(const uint8* src_y, int src_stride_y,
|
||||
const uint8* src_uv, int src_stride_uv,
|
||||
uint8* dst_y, int dst_stride_y,
|
||||
uint8* dst_u, int dst_stride_u,
|
||||
uint8* dst_v, int dst_stride_v,
|
||||
int src_width, int src_height, enum RotationMode mode);
|
||||
|
||||
// Rotate a plane by 0, 90, 180, or 270.
|
||||
LIBYUV_API
|
||||
int RotatePlane(const uint8* src, int src_stride,
|
||||
uint8* dst, int dst_stride,
|
||||
int src_width, int src_height, enum RotationMode mode);
|
||||
|
||||
// Rotate planes by 90, 180, 270. Deprecated.
|
||||
LIBYUV_API
|
||||
void RotatePlane90(const uint8* src, int src_stride,
|
||||
uint8* dst, int dst_stride,
|
||||
int width, int height);
|
||||
|
||||
LIBYUV_API
|
||||
void RotatePlane180(const uint8* src, int src_stride,
|
||||
uint8* dst, int dst_stride,
|
||||
int width, int height);
|
||||
|
||||
LIBYUV_API
|
||||
void RotatePlane270(const uint8* src, int src_stride,
|
||||
uint8* dst, int dst_stride,
|
||||
int width, int height);
|
||||
|
||||
LIBYUV_API
|
||||
void RotateUV90(const uint8* src, int src_stride,
|
||||
uint8* dst_a, int dst_stride_a,
|
||||
uint8* dst_b, int dst_stride_b,
|
||||
int width, int height);
|
||||
|
||||
// Rotations for when U and V are interleaved.
|
||||
// These functions take one input pointer and
|
||||
// split the data into two buffers while
|
||||
// rotating them. Deprecated.
|
||||
LIBYUV_API
|
||||
void RotateUV180(const uint8* src, int src_stride,
|
||||
uint8* dst_a, int dst_stride_a,
|
||||
uint8* dst_b, int dst_stride_b,
|
||||
int width, int height);
|
||||
|
||||
LIBYUV_API
|
||||
void RotateUV270(const uint8* src, int src_stride,
|
||||
uint8* dst_a, int dst_stride_a,
|
||||
uint8* dst_b, int dst_stride_b,
|
||||
int width, int height);
|
||||
|
||||
// The 90 and 270 functions are based on transposes.
|
||||
// Doing a transpose with reversing the read/write
|
||||
// order will result in a rotation by +- 90 degrees.
|
||||
// Deprecated.
|
||||
LIBYUV_API
|
||||
void TransposePlane(const uint8* src, int src_stride,
|
||||
uint8* dst, int dst_stride,
|
||||
int width, int height);
|
||||
|
||||
LIBYUV_API
|
||||
void TransposeUV(const uint8* src, int src_stride,
|
||||
uint8* dst_a, int dst_stride_a,
|
||||
uint8* dst_b, int dst_stride_b,
|
||||
int width, int height);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
} // namespace libyuv
|
||||
#endif
|
||||
|
||||
#endif // INCLUDE_LIBYUV_ROTATE_H_
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2012 The LibYuv Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef INCLUDE_LIBYUV_ROTATE_ARGB_H_
|
||||
#define INCLUDE_LIBYUV_ROTATE_ARGB_H_
|
||||
|
||||
#include "libyuv/basic_types.h"
|
||||
#include "libyuv/rotate.h" // For RotationMode.
|
||||
|
||||
#ifdef __cplusplus
|
||||
namespace libyuv {
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Rotate ARGB frame
|
||||
LIBYUV_API
|
||||
int ARGBRotate(const uint8* src_argb, int src_stride_argb,
|
||||
uint8* dst_argb, int dst_stride_argb,
|
||||
int src_width, int src_height, enum RotationMode mode);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
} // namespace libyuv
|
||||
#endif
|
||||
|
||||
#endif // INCLUDE_LIBYUV_ROTATE_ARGB_H_
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright 2013 The LibYuv Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef INCLUDE_LIBYUV_ROTATE_ROW_H_
|
||||
#define INCLUDE_LIBYUV_ROTATE_ROW_H_
|
||||
|
||||
#include "libyuv/basic_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
namespace libyuv {
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if defined(__pnacl__) || defined(__CLR_VER) || \
|
||||
(defined(__i386__) && !defined(__SSE2__))
|
||||
#define LIBYUV_DISABLE_X86
|
||||
#endif
|
||||
// MemorySanitizer does not support assembly code yet. http://crbug.com/344505
|
||||
#if defined(__has_feature)
|
||||
#if __has_feature(memory_sanitizer)
|
||||
#define LIBYUV_DISABLE_X86
|
||||
#endif
|
||||
#endif
|
||||
// The following are available for Visual C and clangcl 32 bit:
|
||||
#if !defined(LIBYUV_DISABLE_X86) && defined(_M_IX86)
|
||||
#define HAS_TRANSPOSEWX8_SSSE3
|
||||
#define HAS_TRANSPOSEUVWX8_SSE2
|
||||
#endif
|
||||
|
||||
// The following are available for GCC 32 or 64 bit but not NaCL for 64 bit:
|
||||
#if !defined(LIBYUV_DISABLE_X86) && \
|
||||
(defined(__i386__) || (defined(__x86_64__) && !defined(__native_client__)))
|
||||
#define HAS_TRANSPOSEWX8_SSSE3
|
||||
#endif
|
||||
|
||||
// The following are available for 64 bit GCC but not NaCL:
|
||||
#if !defined(LIBYUV_DISABLE_X86) && !defined(__native_client__) && \
|
||||
defined(__x86_64__)
|
||||
#define HAS_TRANSPOSEWX8_FAST_SSSE3
|
||||
#define HAS_TRANSPOSEUVWX8_SSE2
|
||||
#endif
|
||||
|
||||
#if !defined(LIBYUV_DISABLE_NEON) && !defined(__native_client__) && \
|
||||
(defined(__ARM_NEON__) || defined(LIBYUV_NEON) || defined(__aarch64__))
|
||||
#define HAS_TRANSPOSEWX8_NEON
|
||||
#define HAS_TRANSPOSEUVWX8_NEON
|
||||
#endif
|
||||
|
||||
#if !defined(LIBYUV_DISABLE_MIPS) && !defined(__native_client__) && \
|
||||
defined(__mips__) && \
|
||||
defined(__mips_dsp) && (__mips_dsp_rev >= 2)
|
||||
#define HAS_TRANSPOSEWX8_DSPR2
|
||||
#define HAS_TRANSPOSEUVWX8_DSPR2
|
||||
#endif // defined(__mips__)
|
||||
|
||||
void TransposeWxH_C(const uint8* src, int src_stride,
|
||||
uint8* dst, int dst_stride, int width, int height);
|
||||
|
||||
void TransposeWx8_C(const uint8* src, int src_stride,
|
||||
uint8* dst, int dst_stride, int width);
|
||||
void TransposeWx8_NEON(const uint8* src, int src_stride,
|
||||
uint8* dst, int dst_stride, int width);
|
||||
void TransposeWx8_SSSE3(const uint8* src, int src_stride,
|
||||
uint8* dst, int dst_stride, int width);
|
||||
void TransposeWx8_Fast_SSSE3(const uint8* src, int src_stride,
|
||||
uint8* dst, int dst_stride, int width);
|
||||
void TransposeWx8_DSPR2(const uint8* src, int src_stride,
|
||||
uint8* dst, int dst_stride, int width);
|
||||
void TransposeWx8_Fast_DSPR2(const uint8* src, int src_stride,
|
||||
uint8* dst, int dst_stride, int width);
|
||||
|
||||
void TransposeWx8_Any_NEON(const uint8* src, int src_stride,
|
||||
uint8* dst, int dst_stride, int width);
|
||||
void TransposeWx8_Any_SSSE3(const uint8* src, int src_stride,
|
||||
uint8* dst, int dst_stride, int width);
|
||||
void TransposeWx8_Fast_Any_SSSE3(const uint8* src, int src_stride,
|
||||
uint8* dst, int dst_stride, int width);
|
||||
void TransposeWx8_Any_DSPR2(const uint8* src, int src_stride,
|
||||
uint8* dst, int dst_stride, int width);
|
||||
|
||||
void TransposeUVWxH_C(const uint8* src, int src_stride,
|
||||
uint8* dst_a, int dst_stride_a,
|
||||
uint8* dst_b, int dst_stride_b,
|
||||
int width, int height);
|
||||
|
||||
void TransposeUVWx8_C(const uint8* src, int src_stride,
|
||||
uint8* dst_a, int dst_stride_a,
|
||||
uint8* dst_b, int dst_stride_b, int width);
|
||||
void TransposeUVWx8_SSE2(const uint8* src, int src_stride,
|
||||
uint8* dst_a, int dst_stride_a,
|
||||
uint8* dst_b, int dst_stride_b, int width);
|
||||
void TransposeUVWx8_NEON(const uint8* src, int src_stride,
|
||||
uint8* dst_a, int dst_stride_a,
|
||||
uint8* dst_b, int dst_stride_b, int width);
|
||||
void TransposeUVWx8_DSPR2(const uint8* src, int src_stride,
|
||||
uint8* dst_a, int dst_stride_a,
|
||||
uint8* dst_b, int dst_stride_b, int width);
|
||||
|
||||
void TransposeUVWx8_Any_SSE2(const uint8* src, int src_stride,
|
||||
uint8* dst_a, int dst_stride_a,
|
||||
uint8* dst_b, int dst_stride_b, int width);
|
||||
void TransposeUVWx8_Any_NEON(const uint8* src, int src_stride,
|
||||
uint8* dst_a, int dst_stride_a,
|
||||
uint8* dst_b, int dst_stride_b, int width);
|
||||
void TransposeUVWx8_Any_DSPR2(const uint8* src, int src_stride,
|
||||
uint8* dst_a, int dst_stride_a,
|
||||
uint8* dst_b, int dst_stride_b, int width);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
} // namespace libyuv
|
||||
#endif
|
||||
|
||||
#endif // INCLUDE_LIBYUV_ROTATE_ROW_H_
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright 2011 The LibYuv Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef INCLUDE_LIBYUV_SCALE_H_
|
||||
#define INCLUDE_LIBYUV_SCALE_H_
|
||||
|
||||
#include "libyuv/basic_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
namespace libyuv {
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Supported filtering.
|
||||
typedef enum FilterMode {
|
||||
kFilterNone = 0, // Point sample; Fastest.
|
||||
kFilterLinear = 1, // Filter horizontally only.
|
||||
kFilterBilinear = 2, // Faster than box, but lower quality scaling down.
|
||||
kFilterBox = 3 // Highest quality.
|
||||
} FilterModeEnum;
|
||||
|
||||
// Scale a YUV plane.
|
||||
LIBYUV_API
|
||||
void ScalePlane(const uint8* src, int src_stride,
|
||||
int src_width, int src_height,
|
||||
uint8* dst, int dst_stride,
|
||||
int dst_width, int dst_height,
|
||||
enum FilterMode filtering);
|
||||
|
||||
LIBYUV_API
|
||||
void ScalePlane_16(const uint16* src, int src_stride,
|
||||
int src_width, int src_height,
|
||||
uint16* dst, int dst_stride,
|
||||
int dst_width, int dst_height,
|
||||
enum FilterMode filtering);
|
||||
|
||||
// Scales a YUV 4:2:0 image from the src width and height to the
|
||||
// dst width and height.
|
||||
// If filtering is kFilterNone, a simple nearest-neighbor algorithm is
|
||||
// used. This produces basic (blocky) quality at the fastest speed.
|
||||
// If filtering is kFilterBilinear, interpolation is used to produce a better
|
||||
// quality image, at the expense of speed.
|
||||
// If filtering is kFilterBox, averaging is used to produce ever better
|
||||
// quality image, at further expense of speed.
|
||||
// Returns 0 if successful.
|
||||
|
||||
LIBYUV_API
|
||||
int I420Scale(const uint8* src_y, int src_stride_y,
|
||||
const uint8* src_u, int src_stride_u,
|
||||
const uint8* src_v, int src_stride_v,
|
||||
int src_width, int src_height,
|
||||
uint8* dst_y, int dst_stride_y,
|
||||
uint8* dst_u, int dst_stride_u,
|
||||
uint8* dst_v, int dst_stride_v,
|
||||
int dst_width, int dst_height,
|
||||
enum FilterMode filtering);
|
||||
|
||||
LIBYUV_API
|
||||
int I420Scale_16(const uint16* src_y, int src_stride_y,
|
||||
const uint16* src_u, int src_stride_u,
|
||||
const uint16* src_v, int src_stride_v,
|
||||
int src_width, int src_height,
|
||||
uint16* dst_y, int dst_stride_y,
|
||||
uint16* dst_u, int dst_stride_u,
|
||||
uint16* dst_v, int dst_stride_v,
|
||||
int dst_width, int dst_height,
|
||||
enum FilterMode filtering);
|
||||
|
||||
#ifdef __cplusplus
|
||||
// Legacy API. Deprecated.
|
||||
LIBYUV_API
|
||||
int Scale(const uint8* src_y, const uint8* src_u, const uint8* src_v,
|
||||
int src_stride_y, int src_stride_u, int src_stride_v,
|
||||
int src_width, int src_height,
|
||||
uint8* dst_y, uint8* dst_u, uint8* dst_v,
|
||||
int dst_stride_y, int dst_stride_u, int dst_stride_v,
|
||||
int dst_width, int dst_height,
|
||||
LIBYUV_BOOL interpolate);
|
||||
|
||||
// Legacy API. Deprecated.
|
||||
LIBYUV_API
|
||||
int ScaleOffset(const uint8* src_i420, int src_width, int src_height,
|
||||
uint8* dst_i420, int dst_width, int dst_height, int dst_yoffset,
|
||||
LIBYUV_BOOL interpolate);
|
||||
|
||||
// For testing, allow disabling of specialized scalers.
|
||||
LIBYUV_API
|
||||
void SetUseReferenceImpl(LIBYUV_BOOL use);
|
||||
#endif // __cplusplus
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
} // namespace libyuv
|
||||
#endif
|
||||
|
||||
#endif // INCLUDE_LIBYUV_SCALE_H_
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2012 The LibYuv Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef INCLUDE_LIBYUV_SCALE_ARGB_H_
|
||||
#define INCLUDE_LIBYUV_SCALE_ARGB_H_
|
||||
|
||||
#include "libyuv/basic_types.h"
|
||||
#include "libyuv/scale.h" // For FilterMode
|
||||
|
||||
#ifdef __cplusplus
|
||||
namespace libyuv {
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
LIBYUV_API
|
||||
int ARGBScale(const uint8* src_argb, int src_stride_argb,
|
||||
int src_width, int src_height,
|
||||
uint8* dst_argb, int dst_stride_argb,
|
||||
int dst_width, int dst_height,
|
||||
enum FilterMode filtering);
|
||||
|
||||
// Clipped scale takes destination rectangle coordinates for clip values.
|
||||
LIBYUV_API
|
||||
int ARGBScaleClip(const uint8* src_argb, int src_stride_argb,
|
||||
int src_width, int src_height,
|
||||
uint8* dst_argb, int dst_stride_argb,
|
||||
int dst_width, int dst_height,
|
||||
int clip_x, int clip_y, int clip_width, int clip_height,
|
||||
enum FilterMode filtering);
|
||||
|
||||
// Scale with YUV conversion to ARGB and clipping.
|
||||
LIBYUV_API
|
||||
int YUVToARGBScaleClip(const uint8* src_y, int src_stride_y,
|
||||
const uint8* src_u, int src_stride_u,
|
||||
const uint8* src_v, int src_stride_v,
|
||||
uint32 src_fourcc,
|
||||
int src_width, int src_height,
|
||||
uint8* dst_argb, int dst_stride_argb,
|
||||
uint32 dst_fourcc,
|
||||
int dst_width, int dst_height,
|
||||
int clip_x, int clip_y, int clip_width, int clip_height,
|
||||
enum FilterMode filtering);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
} // namespace libyuv
|
||||
#endif
|
||||
|
||||
#endif // INCLUDE_LIBYUV_SCALE_ARGB_H_
|
||||
@@ -0,0 +1,503 @@
|
||||
/*
|
||||
* Copyright 2013 The LibYuv Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef INCLUDE_LIBYUV_SCALE_ROW_H_
|
||||
#define INCLUDE_LIBYUV_SCALE_ROW_H_
|
||||
|
||||
#include "libyuv/basic_types.h"
|
||||
#include "libyuv/scale.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
namespace libyuv {
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if defined(__pnacl__) || defined(__CLR_VER) || \
|
||||
(defined(__i386__) && !defined(__SSE2__))
|
||||
#define LIBYUV_DISABLE_X86
|
||||
#endif
|
||||
// MemorySanitizer does not support assembly code yet. http://crbug.com/344505
|
||||
#if defined(__has_feature)
|
||||
#if __has_feature(memory_sanitizer)
|
||||
#define LIBYUV_DISABLE_X86
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// GCC >= 4.7.0 required for AVX2.
|
||||
#if defined(__GNUC__) && (defined(__x86_64__) || defined(__i386__))
|
||||
#if (__GNUC__ > 4) || (__GNUC__ == 4 && (__GNUC_MINOR__ >= 7))
|
||||
#define GCC_HAS_AVX2 1
|
||||
#endif // GNUC >= 4.7
|
||||
#endif // __GNUC__
|
||||
|
||||
// clang >= 3.4.0 required for AVX2.
|
||||
#if defined(__clang__) && (defined(__x86_64__) || defined(__i386__))
|
||||
#if (__clang_major__ > 3) || (__clang_major__ == 3 && (__clang_minor__ >= 4))
|
||||
#define CLANG_HAS_AVX2 1
|
||||
#endif // clang >= 3.4
|
||||
#endif // __clang__
|
||||
|
||||
// Visual C 2012 required for AVX2.
|
||||
#if defined(_M_IX86) && !defined(__clang__) && \
|
||||
defined(_MSC_VER) && _MSC_VER >= 1700
|
||||
#define VISUALC_HAS_AVX2 1
|
||||
#endif // VisualStudio >= 2012
|
||||
|
||||
// The following are available on all x86 platforms:
|
||||
#if !defined(LIBYUV_DISABLE_X86) && \
|
||||
(defined(_M_IX86) || defined(__x86_64__) || defined(__i386__))
|
||||
#define HAS_FIXEDDIV1_X86
|
||||
#define HAS_FIXEDDIV_X86
|
||||
#define HAS_SCALEARGBCOLS_SSE2
|
||||
#define HAS_SCALEARGBCOLSUP2_SSE2
|
||||
#define HAS_SCALEARGBFILTERCOLS_SSSE3
|
||||
#define HAS_SCALEARGBROWDOWN2_SSE2
|
||||
#define HAS_SCALEARGBROWDOWNEVEN_SSE2
|
||||
#define HAS_SCALECOLSUP2_SSE2
|
||||
#define HAS_SCALEFILTERCOLS_SSSE3
|
||||
#define HAS_SCALEROWDOWN2_SSSE3
|
||||
#define HAS_SCALEROWDOWN34_SSSE3
|
||||
#define HAS_SCALEROWDOWN38_SSSE3
|
||||
#define HAS_SCALEROWDOWN4_SSSE3
|
||||
#define HAS_SCALEADDROW_SSE2
|
||||
#endif
|
||||
|
||||
// The following are available on all x86 platforms, but
|
||||
// require VS2012, clang 3.4 or gcc 4.7.
|
||||
// The code supports NaCL but requires a new compiler and validator.
|
||||
#if !defined(LIBYUV_DISABLE_X86) && (defined(VISUALC_HAS_AVX2) || \
|
||||
defined(CLANG_HAS_AVX2) || defined(GCC_HAS_AVX2))
|
||||
#define HAS_SCALEADDROW_AVX2
|
||||
#define HAS_SCALEROWDOWN2_AVX2
|
||||
#define HAS_SCALEROWDOWN4_AVX2
|
||||
#endif
|
||||
|
||||
// The following are available on Neon platforms:
|
||||
#if !defined(LIBYUV_DISABLE_NEON) && !defined(__native_client__) && \
|
||||
(defined(__ARM_NEON__) || defined(LIBYUV_NEON) || defined(__aarch64__))
|
||||
#define HAS_SCALEARGBCOLS_NEON
|
||||
#define HAS_SCALEARGBROWDOWN2_NEON
|
||||
#define HAS_SCALEARGBROWDOWNEVEN_NEON
|
||||
#define HAS_SCALEFILTERCOLS_NEON
|
||||
#define HAS_SCALEROWDOWN2_NEON
|
||||
#define HAS_SCALEROWDOWN34_NEON
|
||||
#define HAS_SCALEROWDOWN38_NEON
|
||||
#define HAS_SCALEROWDOWN4_NEON
|
||||
#define HAS_SCALEARGBFILTERCOLS_NEON
|
||||
#endif
|
||||
|
||||
// The following are available on Mips platforms:
|
||||
#if !defined(LIBYUV_DISABLE_MIPS) && !defined(__native_client__) && \
|
||||
defined(__mips__) && defined(__mips_dsp) && (__mips_dsp_rev >= 2)
|
||||
#define HAS_SCALEROWDOWN2_DSPR2
|
||||
#define HAS_SCALEROWDOWN4_DSPR2
|
||||
#define HAS_SCALEROWDOWN34_DSPR2
|
||||
#define HAS_SCALEROWDOWN38_DSPR2
|
||||
#endif
|
||||
|
||||
// Scale ARGB vertically with bilinear interpolation.
|
||||
void ScalePlaneVertical(int src_height,
|
||||
int dst_width, int dst_height,
|
||||
int src_stride, int dst_stride,
|
||||
const uint8* src_argb, uint8* dst_argb,
|
||||
int x, int y, int dy,
|
||||
int bpp, enum FilterMode filtering);
|
||||
|
||||
void ScalePlaneVertical_16(int src_height,
|
||||
int dst_width, int dst_height,
|
||||
int src_stride, int dst_stride,
|
||||
const uint16* src_argb, uint16* dst_argb,
|
||||
int x, int y, int dy,
|
||||
int wpp, enum FilterMode filtering);
|
||||
|
||||
// Simplify the filtering based on scale factors.
|
||||
enum FilterMode ScaleFilterReduce(int src_width, int src_height,
|
||||
int dst_width, int dst_height,
|
||||
enum FilterMode filtering);
|
||||
|
||||
// Divide num by div and return as 16.16 fixed point result.
|
||||
int FixedDiv_C(int num, int div);
|
||||
int FixedDiv_X86(int num, int div);
|
||||
// Divide num - 1 by div - 1 and return as 16.16 fixed point result.
|
||||
int FixedDiv1_C(int num, int div);
|
||||
int FixedDiv1_X86(int num, int div);
|
||||
#ifdef HAS_FIXEDDIV_X86
|
||||
#define FixedDiv FixedDiv_X86
|
||||
#define FixedDiv1 FixedDiv1_X86
|
||||
#else
|
||||
#define FixedDiv FixedDiv_C
|
||||
#define FixedDiv1 FixedDiv1_C
|
||||
#endif
|
||||
|
||||
// Compute slope values for stepping.
|
||||
void ScaleSlope(int src_width, int src_height,
|
||||
int dst_width, int dst_height,
|
||||
enum FilterMode filtering,
|
||||
int* x, int* y, int* dx, int* dy);
|
||||
|
||||
void ScaleRowDown2_C(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* dst, int dst_width);
|
||||
void ScaleRowDown2_16_C(const uint16* src_ptr, ptrdiff_t src_stride,
|
||||
uint16* dst, int dst_width);
|
||||
void ScaleRowDown2Linear_C(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* dst, int dst_width);
|
||||
void ScaleRowDown2Linear_16_C(const uint16* src_ptr, ptrdiff_t src_stride,
|
||||
uint16* dst, int dst_width);
|
||||
void ScaleRowDown2Box_C(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* dst, int dst_width);
|
||||
void ScaleRowDown2Box_Odd_C(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* dst, int dst_width);
|
||||
void ScaleRowDown2Box_16_C(const uint16* src_ptr, ptrdiff_t src_stride,
|
||||
uint16* dst, int dst_width);
|
||||
void ScaleRowDown4_C(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* dst, int dst_width);
|
||||
void ScaleRowDown4_16_C(const uint16* src_ptr, ptrdiff_t src_stride,
|
||||
uint16* dst, int dst_width);
|
||||
void ScaleRowDown4Box_C(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* dst, int dst_width);
|
||||
void ScaleRowDown4Box_16_C(const uint16* src_ptr, ptrdiff_t src_stride,
|
||||
uint16* dst, int dst_width);
|
||||
void ScaleRowDown34_C(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* dst, int dst_width);
|
||||
void ScaleRowDown34_16_C(const uint16* src_ptr, ptrdiff_t src_stride,
|
||||
uint16* dst, int dst_width);
|
||||
void ScaleRowDown34_0_Box_C(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* d, int dst_width);
|
||||
void ScaleRowDown34_0_Box_16_C(const uint16* src_ptr, ptrdiff_t src_stride,
|
||||
uint16* d, int dst_width);
|
||||
void ScaleRowDown34_1_Box_C(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* d, int dst_width);
|
||||
void ScaleRowDown34_1_Box_16_C(const uint16* src_ptr, ptrdiff_t src_stride,
|
||||
uint16* d, int dst_width);
|
||||
void ScaleCols_C(uint8* dst_ptr, const uint8* src_ptr,
|
||||
int dst_width, int x, int dx);
|
||||
void ScaleCols_16_C(uint16* dst_ptr, const uint16* src_ptr,
|
||||
int dst_width, int x, int dx);
|
||||
void ScaleColsUp2_C(uint8* dst_ptr, const uint8* src_ptr,
|
||||
int dst_width, int, int);
|
||||
void ScaleColsUp2_16_C(uint16* dst_ptr, const uint16* src_ptr,
|
||||
int dst_width, int, int);
|
||||
void ScaleFilterCols_C(uint8* dst_ptr, const uint8* src_ptr,
|
||||
int dst_width, int x, int dx);
|
||||
void ScaleFilterCols_16_C(uint16* dst_ptr, const uint16* src_ptr,
|
||||
int dst_width, int x, int dx);
|
||||
void ScaleFilterCols64_C(uint8* dst_ptr, const uint8* src_ptr,
|
||||
int dst_width, int x, int dx);
|
||||
void ScaleFilterCols64_16_C(uint16* dst_ptr, const uint16* src_ptr,
|
||||
int dst_width, int x, int dx);
|
||||
void ScaleRowDown38_C(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* dst, int dst_width);
|
||||
void ScaleRowDown38_16_C(const uint16* src_ptr, ptrdiff_t src_stride,
|
||||
uint16* dst, int dst_width);
|
||||
void ScaleRowDown38_3_Box_C(const uint8* src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8* dst_ptr, int dst_width);
|
||||
void ScaleRowDown38_3_Box_16_C(const uint16* src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint16* dst_ptr, int dst_width);
|
||||
void ScaleRowDown38_2_Box_C(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* dst_ptr, int dst_width);
|
||||
void ScaleRowDown38_2_Box_16_C(const uint16* src_ptr, ptrdiff_t src_stride,
|
||||
uint16* dst_ptr, int dst_width);
|
||||
void ScaleAddRow_C(const uint8* src_ptr, uint16* dst_ptr, int src_width);
|
||||
void ScaleAddRow_16_C(const uint16* src_ptr, uint32* dst_ptr, int src_width);
|
||||
void ScaleARGBRowDown2_C(const uint8* src_argb,
|
||||
ptrdiff_t src_stride,
|
||||
uint8* dst_argb, int dst_width);
|
||||
void ScaleARGBRowDown2Linear_C(const uint8* src_argb,
|
||||
ptrdiff_t src_stride,
|
||||
uint8* dst_argb, int dst_width);
|
||||
void ScaleARGBRowDown2Box_C(const uint8* src_argb, ptrdiff_t src_stride,
|
||||
uint8* dst_argb, int dst_width);
|
||||
void ScaleARGBRowDownEven_C(const uint8* src_argb, ptrdiff_t src_stride,
|
||||
int src_stepx,
|
||||
uint8* dst_argb, int dst_width);
|
||||
void ScaleARGBRowDownEvenBox_C(const uint8* src_argb,
|
||||
ptrdiff_t src_stride,
|
||||
int src_stepx,
|
||||
uint8* dst_argb, int dst_width);
|
||||
void ScaleARGBCols_C(uint8* dst_argb, const uint8* src_argb,
|
||||
int dst_width, int x, int dx);
|
||||
void ScaleARGBCols64_C(uint8* dst_argb, const uint8* src_argb,
|
||||
int dst_width, int x, int dx);
|
||||
void ScaleARGBColsUp2_C(uint8* dst_argb, const uint8* src_argb,
|
||||
int dst_width, int, int);
|
||||
void ScaleARGBFilterCols_C(uint8* dst_argb, const uint8* src_argb,
|
||||
int dst_width, int x, int dx);
|
||||
void ScaleARGBFilterCols64_C(uint8* dst_argb, const uint8* src_argb,
|
||||
int dst_width, int x, int dx);
|
||||
|
||||
// Specialized scalers for x86.
|
||||
void ScaleRowDown2_SSSE3(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* dst_ptr, int dst_width);
|
||||
void ScaleRowDown2Linear_SSSE3(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* dst_ptr, int dst_width);
|
||||
void ScaleRowDown2Box_SSSE3(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* dst_ptr, int dst_width);
|
||||
void ScaleRowDown2_AVX2(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* dst_ptr, int dst_width);
|
||||
void ScaleRowDown2Linear_AVX2(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* dst_ptr, int dst_width);
|
||||
void ScaleRowDown2Box_AVX2(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* dst_ptr, int dst_width);
|
||||
void ScaleRowDown4_SSSE3(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* dst_ptr, int dst_width);
|
||||
void ScaleRowDown4Box_SSSE3(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* dst_ptr, int dst_width);
|
||||
void ScaleRowDown4_AVX2(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* dst_ptr, int dst_width);
|
||||
void ScaleRowDown4Box_AVX2(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* dst_ptr, int dst_width);
|
||||
|
||||
void ScaleRowDown34_SSSE3(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* dst_ptr, int dst_width);
|
||||
void ScaleRowDown34_1_Box_SSSE3(const uint8* src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8* dst_ptr, int dst_width);
|
||||
void ScaleRowDown34_0_Box_SSSE3(const uint8* src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8* dst_ptr, int dst_width);
|
||||
void ScaleRowDown38_SSSE3(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* dst_ptr, int dst_width);
|
||||
void ScaleRowDown38_3_Box_SSSE3(const uint8* src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8* dst_ptr, int dst_width);
|
||||
void ScaleRowDown38_2_Box_SSSE3(const uint8* src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8* dst_ptr, int dst_width);
|
||||
void ScaleRowDown2_Any_SSSE3(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* dst_ptr, int dst_width);
|
||||
void ScaleRowDown2Linear_Any_SSSE3(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* dst_ptr, int dst_width);
|
||||
void ScaleRowDown2Box_Any_SSSE3(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* dst_ptr, int dst_width);
|
||||
void ScaleRowDown2Box_Odd_SSSE3(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* dst_ptr, int dst_width);
|
||||
void ScaleRowDown2_Any_AVX2(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* dst_ptr, int dst_width);
|
||||
void ScaleRowDown2Linear_Any_AVX2(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* dst_ptr, int dst_width);
|
||||
void ScaleRowDown2Box_Any_AVX2(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* dst_ptr, int dst_width);
|
||||
void ScaleRowDown2Box_Odd_AVX2(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* dst_ptr, int dst_width);
|
||||
void ScaleRowDown4_Any_SSSE3(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* dst_ptr, int dst_width);
|
||||
void ScaleRowDown4Box_Any_SSSE3(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* dst_ptr, int dst_width);
|
||||
void ScaleRowDown4_Any_AVX2(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* dst_ptr, int dst_width);
|
||||
void ScaleRowDown4Box_Any_AVX2(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* dst_ptr, int dst_width);
|
||||
|
||||
void ScaleRowDown34_Any_SSSE3(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* dst_ptr, int dst_width);
|
||||
void ScaleRowDown34_1_Box_Any_SSSE3(const uint8* src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8* dst_ptr, int dst_width);
|
||||
void ScaleRowDown34_0_Box_Any_SSSE3(const uint8* src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8* dst_ptr, int dst_width);
|
||||
void ScaleRowDown38_Any_SSSE3(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* dst_ptr, int dst_width);
|
||||
void ScaleRowDown38_3_Box_Any_SSSE3(const uint8* src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8* dst_ptr, int dst_width);
|
||||
void ScaleRowDown38_2_Box_Any_SSSE3(const uint8* src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8* dst_ptr, int dst_width);
|
||||
|
||||
void ScaleAddRow_SSE2(const uint8* src_ptr, uint16* dst_ptr, int src_width);
|
||||
void ScaleAddRow_AVX2(const uint8* src_ptr, uint16* dst_ptr, int src_width);
|
||||
void ScaleAddRow_Any_SSE2(const uint8* src_ptr, uint16* dst_ptr, int src_width);
|
||||
void ScaleAddRow_Any_AVX2(const uint8* src_ptr, uint16* dst_ptr, int src_width);
|
||||
|
||||
void ScaleFilterCols_SSSE3(uint8* dst_ptr, const uint8* src_ptr,
|
||||
int dst_width, int x, int dx);
|
||||
void ScaleColsUp2_SSE2(uint8* dst_ptr, const uint8* src_ptr,
|
||||
int dst_width, int x, int dx);
|
||||
|
||||
|
||||
// ARGB Column functions
|
||||
void ScaleARGBCols_SSE2(uint8* dst_argb, const uint8* src_argb,
|
||||
int dst_width, int x, int dx);
|
||||
void ScaleARGBFilterCols_SSSE3(uint8* dst_argb, const uint8* src_argb,
|
||||
int dst_width, int x, int dx);
|
||||
void ScaleARGBColsUp2_SSE2(uint8* dst_argb, const uint8* src_argb,
|
||||
int dst_width, int x, int dx);
|
||||
void ScaleARGBFilterCols_NEON(uint8* dst_argb, const uint8* src_argb,
|
||||
int dst_width, int x, int dx);
|
||||
void ScaleARGBCols_NEON(uint8* dst_argb, const uint8* src_argb,
|
||||
int dst_width, int x, int dx);
|
||||
void ScaleARGBFilterCols_Any_NEON(uint8* dst_argb, const uint8* src_argb,
|
||||
int dst_width, int x, int dx);
|
||||
void ScaleARGBCols_Any_NEON(uint8* dst_argb, const uint8* src_argb,
|
||||
int dst_width, int x, int dx);
|
||||
|
||||
// ARGB Row functions
|
||||
void ScaleARGBRowDown2_SSE2(const uint8* src_argb, ptrdiff_t src_stride,
|
||||
uint8* dst_argb, int dst_width);
|
||||
void ScaleARGBRowDown2Linear_SSE2(const uint8* src_argb, ptrdiff_t src_stride,
|
||||
uint8* dst_argb, int dst_width);
|
||||
void ScaleARGBRowDown2Box_SSE2(const uint8* src_argb, ptrdiff_t src_stride,
|
||||
uint8* dst_argb, int dst_width);
|
||||
void ScaleARGBRowDown2_NEON(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* dst, int dst_width);
|
||||
void ScaleARGBRowDown2Linear_NEON(const uint8* src_argb, ptrdiff_t src_stride,
|
||||
uint8* dst_argb, int dst_width);
|
||||
void ScaleARGBRowDown2Box_NEON(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* dst, int dst_width);
|
||||
void ScaleARGBRowDown2_Any_SSE2(const uint8* src_argb, ptrdiff_t src_stride,
|
||||
uint8* dst_argb, int dst_width);
|
||||
void ScaleARGBRowDown2Linear_Any_SSE2(const uint8* src_argb,
|
||||
ptrdiff_t src_stride,
|
||||
uint8* dst_argb, int dst_width);
|
||||
void ScaleARGBRowDown2Box_Any_SSE2(const uint8* src_argb, ptrdiff_t src_stride,
|
||||
uint8* dst_argb, int dst_width);
|
||||
void ScaleARGBRowDown2_Any_NEON(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* dst, int dst_width);
|
||||
void ScaleARGBRowDown2Linear_Any_NEON(const uint8* src_argb,
|
||||
ptrdiff_t src_stride,
|
||||
uint8* dst_argb, int dst_width);
|
||||
void ScaleARGBRowDown2Box_Any_NEON(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* dst, int dst_width);
|
||||
|
||||
void ScaleARGBRowDownEven_SSE2(const uint8* src_argb, ptrdiff_t src_stride,
|
||||
int src_stepx, uint8* dst_argb, int dst_width);
|
||||
void ScaleARGBRowDownEvenBox_SSE2(const uint8* src_argb, ptrdiff_t src_stride,
|
||||
int src_stepx,
|
||||
uint8* dst_argb, int dst_width);
|
||||
void ScaleARGBRowDownEven_NEON(const uint8* src_argb, ptrdiff_t src_stride,
|
||||
int src_stepx,
|
||||
uint8* dst_argb, int dst_width);
|
||||
void ScaleARGBRowDownEvenBox_NEON(const uint8* src_argb, ptrdiff_t src_stride,
|
||||
int src_stepx,
|
||||
uint8* dst_argb, int dst_width);
|
||||
void ScaleARGBRowDownEven_Any_SSE2(const uint8* src_argb, ptrdiff_t src_stride,
|
||||
int src_stepx,
|
||||
uint8* dst_argb, int dst_width);
|
||||
void ScaleARGBRowDownEvenBox_Any_SSE2(const uint8* src_argb,
|
||||
ptrdiff_t src_stride,
|
||||
int src_stepx,
|
||||
uint8* dst_argb, int dst_width);
|
||||
void ScaleARGBRowDownEven_Any_NEON(const uint8* src_argb, ptrdiff_t src_stride,
|
||||
int src_stepx,
|
||||
uint8* dst_argb, int dst_width);
|
||||
void ScaleARGBRowDownEvenBox_Any_NEON(const uint8* src_argb,
|
||||
ptrdiff_t src_stride,
|
||||
int src_stepx,
|
||||
uint8* dst_argb, int dst_width);
|
||||
|
||||
// ScaleRowDown2Box also used by planar functions
|
||||
// NEON downscalers with interpolation.
|
||||
|
||||
// Note - not static due to reuse in convert for 444 to 420.
|
||||
void ScaleRowDown2_NEON(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* dst, int dst_width);
|
||||
void ScaleRowDown2Linear_NEON(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* dst, int dst_width);
|
||||
void ScaleRowDown2Box_NEON(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* dst, int dst_width);
|
||||
|
||||
void ScaleRowDown4_NEON(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* dst_ptr, int dst_width);
|
||||
void ScaleRowDown4Box_NEON(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* dst_ptr, int dst_width);
|
||||
|
||||
// Down scale from 4 to 3 pixels. Use the neon multilane read/write
|
||||
// to load up the every 4th pixel into a 4 different registers.
|
||||
// Point samples 32 pixels to 24 pixels.
|
||||
void ScaleRowDown34_NEON(const uint8* src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8* dst_ptr, int dst_width);
|
||||
void ScaleRowDown34_0_Box_NEON(const uint8* src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8* dst_ptr, int dst_width);
|
||||
void ScaleRowDown34_1_Box_NEON(const uint8* src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8* dst_ptr, int dst_width);
|
||||
|
||||
// 32 -> 12
|
||||
void ScaleRowDown38_NEON(const uint8* src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8* dst_ptr, int dst_width);
|
||||
// 32x3 -> 12x1
|
||||
void ScaleRowDown38_3_Box_NEON(const uint8* src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8* dst_ptr, int dst_width);
|
||||
// 32x2 -> 12x1
|
||||
void ScaleRowDown38_2_Box_NEON(const uint8* src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8* dst_ptr, int dst_width);
|
||||
|
||||
void ScaleRowDown2_Any_NEON(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* dst, int dst_width);
|
||||
void ScaleRowDown2Linear_Any_NEON(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* dst, int dst_width);
|
||||
void ScaleRowDown2Box_Any_NEON(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* dst, int dst_width);
|
||||
void ScaleRowDown2Box_Odd_NEON(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* dst, int dst_width);
|
||||
void ScaleRowDown4_Any_NEON(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* dst_ptr, int dst_width);
|
||||
void ScaleRowDown4Box_Any_NEON(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* dst_ptr, int dst_width);
|
||||
void ScaleRowDown34_Any_NEON(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* dst_ptr, int dst_width);
|
||||
void ScaleRowDown34_0_Box_Any_NEON(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* dst_ptr, int dst_width);
|
||||
void ScaleRowDown34_1_Box_Any_NEON(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* dst_ptr, int dst_width);
|
||||
// 32 -> 12
|
||||
void ScaleRowDown38_Any_NEON(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* dst_ptr, int dst_width);
|
||||
// 32x3 -> 12x1
|
||||
void ScaleRowDown38_3_Box_Any_NEON(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* dst_ptr, int dst_width);
|
||||
// 32x2 -> 12x1
|
||||
void ScaleRowDown38_2_Box_Any_NEON(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* dst_ptr, int dst_width);
|
||||
|
||||
void ScaleAddRow_NEON(const uint8* src_ptr, uint16* dst_ptr, int src_width);
|
||||
void ScaleAddRow_Any_NEON(const uint8* src_ptr, uint16* dst_ptr, int src_width);
|
||||
|
||||
void ScaleFilterCols_NEON(uint8* dst_ptr, const uint8* src_ptr,
|
||||
int dst_width, int x, int dx);
|
||||
|
||||
void ScaleFilterCols_Any_NEON(uint8* dst_ptr, const uint8* src_ptr,
|
||||
int dst_width, int x, int dx);
|
||||
|
||||
void ScaleRowDown2_DSPR2(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* dst, int dst_width);
|
||||
void ScaleRowDown2Box_DSPR2(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* dst, int dst_width);
|
||||
void ScaleRowDown4_DSPR2(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* dst, int dst_width);
|
||||
void ScaleRowDown4Box_DSPR2(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* dst, int dst_width);
|
||||
void ScaleRowDown34_DSPR2(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* dst, int dst_width);
|
||||
void ScaleRowDown34_0_Box_DSPR2(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* d, int dst_width);
|
||||
void ScaleRowDown34_1_Box_DSPR2(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* d, int dst_width);
|
||||
void ScaleRowDown38_DSPR2(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* dst, int dst_width);
|
||||
void ScaleRowDown38_2_Box_DSPR2(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* dst_ptr, int dst_width);
|
||||
void ScaleRowDown38_3_Box_DSPR2(const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
uint8* dst_ptr, int dst_width);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
} // namespace libyuv
|
||||
#endif
|
||||
|
||||
#endif // INCLUDE_LIBYUV_SCALE_ROW_H_
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright 2012 The LibYuv Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef INCLUDE_LIBYUV_VERSION_H_
|
||||
#define INCLUDE_LIBYUV_VERSION_H_
|
||||
|
||||
#define LIBYUV_VERSION 1622
|
||||
|
||||
#endif // INCLUDE_LIBYUV_VERSION_H_
|
||||
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
* Copyright 2011 The LibYuv Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
// Common definitions for video, including fourcc and VideoFormat.
|
||||
|
||||
#ifndef INCLUDE_LIBYUV_VIDEO_COMMON_H_
|
||||
#define INCLUDE_LIBYUV_VIDEO_COMMON_H_
|
||||
|
||||
#include "libyuv/basic_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
namespace libyuv {
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Definition of FourCC codes
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Convert four characters to a FourCC code.
|
||||
// Needs to be a macro otherwise the OS X compiler complains when the kFormat*
|
||||
// constants are used in a switch.
|
||||
#ifdef __cplusplus
|
||||
#define FOURCC(a, b, c, d) ( \
|
||||
(static_cast<uint32>(a)) | (static_cast<uint32>(b) << 8) | \
|
||||
(static_cast<uint32>(c) << 16) | (static_cast<uint32>(d) << 24))
|
||||
#else
|
||||
#define FOURCC(a, b, c, d) ( \
|
||||
((uint32)(a)) | ((uint32)(b) << 8) | /* NOLINT */ \
|
||||
((uint32)(c) << 16) | ((uint32)(d) << 24)) /* NOLINT */
|
||||
#endif
|
||||
|
||||
// Some pages discussing FourCC codes:
|
||||
// http://www.fourcc.org/yuv.php
|
||||
// http://v4l2spec.bytesex.org/spec/book1.htm
|
||||
// http://developer.apple.com/quicktime/icefloe/dispatch020.html
|
||||
// http://msdn.microsoft.com/library/windows/desktop/dd206750.aspx#nv12
|
||||
// http://people.xiph.org/~xiphmont/containers/nut/nut4cc.txt
|
||||
|
||||
// FourCC codes grouped according to implementation efficiency.
|
||||
// Primary formats should convert in 1 efficient step.
|
||||
// Secondary formats are converted in 2 steps.
|
||||
// Auxilliary formats call primary converters.
|
||||
enum FourCC {
|
||||
// 9 Primary YUV formats: 5 planar, 2 biplanar, 2 packed.
|
||||
FOURCC_I420 = FOURCC('I', '4', '2', '0'),
|
||||
FOURCC_I422 = FOURCC('I', '4', '2', '2'),
|
||||
FOURCC_I444 = FOURCC('I', '4', '4', '4'),
|
||||
FOURCC_I411 = FOURCC('I', '4', '1', '1'),
|
||||
FOURCC_I400 = FOURCC('I', '4', '0', '0'),
|
||||
FOURCC_NV21 = FOURCC('N', 'V', '2', '1'),
|
||||
FOURCC_NV12 = FOURCC('N', 'V', '1', '2'),
|
||||
FOURCC_YUY2 = FOURCC('Y', 'U', 'Y', '2'),
|
||||
FOURCC_UYVY = FOURCC('U', 'Y', 'V', 'Y'),
|
||||
|
||||
// 2 Secondary YUV formats: row biplanar.
|
||||
FOURCC_M420 = FOURCC('M', '4', '2', '0'),
|
||||
FOURCC_Q420 = FOURCC('Q', '4', '2', '0'), // deprecated.
|
||||
|
||||
// 9 Primary RGB formats: 4 32 bpp, 2 24 bpp, 3 16 bpp.
|
||||
FOURCC_ARGB = FOURCC('A', 'R', 'G', 'B'),
|
||||
FOURCC_BGRA = FOURCC('B', 'G', 'R', 'A'),
|
||||
FOURCC_ABGR = FOURCC('A', 'B', 'G', 'R'),
|
||||
FOURCC_24BG = FOURCC('2', '4', 'B', 'G'),
|
||||
FOURCC_RAW = FOURCC('r', 'a', 'w', ' '),
|
||||
FOURCC_RGBA = FOURCC('R', 'G', 'B', 'A'),
|
||||
FOURCC_RGBP = FOURCC('R', 'G', 'B', 'P'), // rgb565 LE.
|
||||
FOURCC_RGBO = FOURCC('R', 'G', 'B', 'O'), // argb1555 LE.
|
||||
FOURCC_R444 = FOURCC('R', '4', '4', '4'), // argb4444 LE.
|
||||
|
||||
// 4 Secondary RGB formats: 4 Bayer Patterns. deprecated.
|
||||
FOURCC_RGGB = FOURCC('R', 'G', 'G', 'B'),
|
||||
FOURCC_BGGR = FOURCC('B', 'G', 'G', 'R'),
|
||||
FOURCC_GRBG = FOURCC('G', 'R', 'B', 'G'),
|
||||
FOURCC_GBRG = FOURCC('G', 'B', 'R', 'G'),
|
||||
|
||||
// 1 Primary Compressed YUV format.
|
||||
FOURCC_MJPG = FOURCC('M', 'J', 'P', 'G'),
|
||||
|
||||
// 5 Auxiliary YUV variations: 3 with U and V planes are swapped, 1 Alias.
|
||||
FOURCC_YV12 = FOURCC('Y', 'V', '1', '2'),
|
||||
FOURCC_YV16 = FOURCC('Y', 'V', '1', '6'),
|
||||
FOURCC_YV24 = FOURCC('Y', 'V', '2', '4'),
|
||||
FOURCC_YU12 = FOURCC('Y', 'U', '1', '2'), // Linux version of I420.
|
||||
FOURCC_J420 = FOURCC('J', '4', '2', '0'),
|
||||
FOURCC_J400 = FOURCC('J', '4', '0', '0'), // unofficial fourcc
|
||||
FOURCC_H420 = FOURCC('H', '4', '2', '0'), // unofficial fourcc
|
||||
|
||||
// 14 Auxiliary aliases. CanonicalFourCC() maps these to canonical fourcc.
|
||||
FOURCC_IYUV = FOURCC('I', 'Y', 'U', 'V'), // Alias for I420.
|
||||
FOURCC_YU16 = FOURCC('Y', 'U', '1', '6'), // Alias for I422.
|
||||
FOURCC_YU24 = FOURCC('Y', 'U', '2', '4'), // Alias for I444.
|
||||
FOURCC_YUYV = FOURCC('Y', 'U', 'Y', 'V'), // Alias for YUY2.
|
||||
FOURCC_YUVS = FOURCC('y', 'u', 'v', 's'), // Alias for YUY2 on Mac.
|
||||
FOURCC_HDYC = FOURCC('H', 'D', 'Y', 'C'), // Alias for UYVY.
|
||||
FOURCC_2VUY = FOURCC('2', 'v', 'u', 'y'), // Alias for UYVY on Mac.
|
||||
FOURCC_JPEG = FOURCC('J', 'P', 'E', 'G'), // Alias for MJPG.
|
||||
FOURCC_DMB1 = FOURCC('d', 'm', 'b', '1'), // Alias for MJPG on Mac.
|
||||
FOURCC_BA81 = FOURCC('B', 'A', '8', '1'), // Alias for BGGR.
|
||||
FOURCC_RGB3 = FOURCC('R', 'G', 'B', '3'), // Alias for RAW.
|
||||
FOURCC_BGR3 = FOURCC('B', 'G', 'R', '3'), // Alias for 24BG.
|
||||
FOURCC_CM32 = FOURCC(0, 0, 0, 32), // Alias for BGRA kCMPixelFormat_32ARGB
|
||||
FOURCC_CM24 = FOURCC(0, 0, 0, 24), // Alias for RAW kCMPixelFormat_24RGB
|
||||
FOURCC_L555 = FOURCC('L', '5', '5', '5'), // Alias for RGBO.
|
||||
FOURCC_L565 = FOURCC('L', '5', '6', '5'), // Alias for RGBP.
|
||||
FOURCC_5551 = FOURCC('5', '5', '5', '1'), // Alias for RGBO.
|
||||
|
||||
// 1 Auxiliary compressed YUV format set aside for capturer.
|
||||
FOURCC_H264 = FOURCC('H', '2', '6', '4'),
|
||||
|
||||
// Match any fourcc.
|
||||
FOURCC_ANY = -1,
|
||||
};
|
||||
|
||||
enum FourCCBpp {
|
||||
// Canonical fourcc codes used in our code.
|
||||
FOURCC_BPP_I420 = 12,
|
||||
FOURCC_BPP_I422 = 16,
|
||||
FOURCC_BPP_I444 = 24,
|
||||
FOURCC_BPP_I411 = 12,
|
||||
FOURCC_BPP_I400 = 8,
|
||||
FOURCC_BPP_NV21 = 12,
|
||||
FOURCC_BPP_NV12 = 12,
|
||||
FOURCC_BPP_YUY2 = 16,
|
||||
FOURCC_BPP_UYVY = 16,
|
||||
FOURCC_BPP_M420 = 12,
|
||||
FOURCC_BPP_Q420 = 12,
|
||||
FOURCC_BPP_ARGB = 32,
|
||||
FOURCC_BPP_BGRA = 32,
|
||||
FOURCC_BPP_ABGR = 32,
|
||||
FOURCC_BPP_RGBA = 32,
|
||||
FOURCC_BPP_24BG = 24,
|
||||
FOURCC_BPP_RAW = 24,
|
||||
FOURCC_BPP_RGBP = 16,
|
||||
FOURCC_BPP_RGBO = 16,
|
||||
FOURCC_BPP_R444 = 16,
|
||||
FOURCC_BPP_RGGB = 8,
|
||||
FOURCC_BPP_BGGR = 8,
|
||||
FOURCC_BPP_GRBG = 8,
|
||||
FOURCC_BPP_GBRG = 8,
|
||||
FOURCC_BPP_YV12 = 12,
|
||||
FOURCC_BPP_YV16 = 16,
|
||||
FOURCC_BPP_YV24 = 24,
|
||||
FOURCC_BPP_YU12 = 12,
|
||||
FOURCC_BPP_J420 = 12,
|
||||
FOURCC_BPP_J400 = 8,
|
||||
FOURCC_BPP_H420 = 12,
|
||||
FOURCC_BPP_MJPG = 0, // 0 means unknown.
|
||||
FOURCC_BPP_H264 = 0,
|
||||
FOURCC_BPP_IYUV = 12,
|
||||
FOURCC_BPP_YU16 = 16,
|
||||
FOURCC_BPP_YU24 = 24,
|
||||
FOURCC_BPP_YUYV = 16,
|
||||
FOURCC_BPP_YUVS = 16,
|
||||
FOURCC_BPP_HDYC = 16,
|
||||
FOURCC_BPP_2VUY = 16,
|
||||
FOURCC_BPP_JPEG = 1,
|
||||
FOURCC_BPP_DMB1 = 1,
|
||||
FOURCC_BPP_BA81 = 8,
|
||||
FOURCC_BPP_RGB3 = 24,
|
||||
FOURCC_BPP_BGR3 = 24,
|
||||
FOURCC_BPP_CM32 = 32,
|
||||
FOURCC_BPP_CM24 = 24,
|
||||
|
||||
// Match any fourcc.
|
||||
FOURCC_BPP_ANY = 0, // 0 means unknown.
|
||||
};
|
||||
|
||||
// Converts fourcc aliases into canonical ones.
|
||||
LIBYUV_API uint32 CanonicalFourCC(uint32 fourcc);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
} // namespace libyuv
|
||||
#endif
|
||||
|
||||
#endif // INCLUDE_LIBYUV_VIDEO_COMMON_H_
|
||||
@@ -1 +0,0 @@
|
||||
larch64/
|
||||
@@ -0,0 +1 @@
|
||||
#include "export_core.hpp"
|
||||
@@ -0,0 +1 @@
|
||||
#include "layer_parameter.hpp"
|
||||
@@ -0,0 +1 @@
|
||||
#include "map.hpp"
|
||||
@@ -0,0 +1 @@
|
||||
#include "qmaplibre"
|
||||
@@ -0,0 +1 @@
|
||||
#include "settings.hpp"
|
||||
@@ -0,0 +1 @@
|
||||
#include "source_parameter.hpp"
|
||||
@@ -0,0 +1 @@
|
||||
#include "style_parameter.hpp"
|
||||
@@ -0,0 +1 @@
|
||||
#include "types.hpp"
|
||||
@@ -0,0 +1 @@
|
||||
#include "utils.hpp"
|
||||
@@ -1 +0,0 @@
|
||||
aarch64-ubuntu-gcc7.5
|
||||