# Building timps is a small, dependency-light C11 codebase built with a plain top-level `Makefile` — no autotools/CMake/meson. Two build modes exist: - **`make sim`** — a host (x86/whatever `cc` targets) build using the simulation HAL backend (`src/hal/hal_sim.c`), which feeds pre-recorded files instead of talking to real ISP/encoder hardware. No cross toolchain or vendor SDK headers needed. Produces `timpsd-sim`. - **`make PLATFORM=... CROSS_COMPILE=... IMP_INC=...`** — a cross-compiled build against the real Ingenic `libimp` SDK for a MIPS target. Produces `timpsd`. ## Host simulation build ```sh make sim # builds ./timpsd-sim with the host cc ./timpsd-sim -c timps.conf # feeds files instead of the ISP ffplay http://127.0.0.1:8880/stream.mp4 ``` `hal_sim.c` reads `sim.video0` / `sim.video1` (raw H.264/H.265 Annex-B files, looped), `sim.audio` (an ADTS AAC file), and `sim.jpeg` (a still JPEG) and republishes them into the [hub](Architecture.md) at the configured fps/sample rate, so the entire RTSP/HTTP/control/events stack can be exercised without hardware. `hal_isp_total_gain()` / `hal_isp_ae_luma()` always report "unavailable" in this backend, so [day/night](Day-Night.md) falls back to its brightness-proc-scrape path (which is also absent on a host, so the day/night thread simply idles). `make sim` links `src/hal/hal_sim.c` in place of `src/hal/hal_ingenic.c` and does not need `IMP_INC`/vendor libraries at all — this is the fastest way to iterate on protocol/control/muxer code. ## Cross-compiling for a real camera ```sh git clone --recurse-submodules https://github.com/Lu-Fi/timps cd timps make PLATFORM=T31 CROSS_COMPILE=mipsel-linux- make strip PLATFORM=T31 CROSS_COMPILE=mipsel-linux- ``` - `PLATFORM` selects one of `T10 T20 T21 T23 T30 T31 T40 T41 C100`. - `CROSS_COMPILE` is the toolchain prefix (default `mipsel-linux-`). - `IMP_INC` points at the vendored Ingenic IMP SDK headers; it defaults to a version pinned per platform under `./include///` (a git submodule sourced from [gtxaspec/ingenic-headers](https://github.com/gtxaspec/ingenic-headers)): | Platform | Default `IMP_INC` | | --- | --- | | T31 | `include/T31/1.1.6/en` | | C100 | `include/C100/2.1.0/en` | | T21 | `include/T21/1.0.33/zh` | | T23 | `include/T23/1.3.0/en` (must be ≥1.1.2 — the 1.1.0 header is missing the trailing `fcrop` member of `IMPFSChnAttr`; with libimp 1.3.0 that mismatch makes the framesource silently deliver no frames. `fs_create()` has a compile-time tripwire against this.) | | T30 | `include/T30/1.0.5/zh` | | T40 | `include/T40/1.2.0/zh` | | T41 | `include/T41/1.2.0/zh` | | T20, T10 | `include/T20/3.12.0/zh` (T10 builds against the T20 headers) | - `IMP_LIB` optionally adds a `-L` search directory for `libimp.a`/`.so`. - `SYSROOT` optionally passes `--sysroot` to the cross toolchain. - `IMPLIBS` (default `-l:libimp.a -l:libalog.a -l:libsysutils.a`) controls how the vendor libraries are linked — static archives by default for a single self-contained drop-in binary; adjust to match your SDK (e.g. add `libmuslshim`/`libaudioProcess`). - The result is a single `timpsd` binary. `make strip` strips debug symbols after building. Small-RAM targets (e.g. T10, 64 MB) can tune buffer sizes via extra `CFLAGS`, e.g.: ```sh make PLATFORM=T10 CFLAGS="-DMS_AU_BUF_MAX=524288 -DMS_JPEG_BUF_MAX=262144 \ -DMS_RTSP_QCAP=32 -DMS_MP4_QCAP=32 -DRTSP_MAX_CLIENTS=4 -DHTTP_MAX_CLIENTS=4" ``` (AU/JPEG buffers already auto-scale with the configured resolution; these overrides lower the ceiling further for very constrained boards.) ## Build hardening `HARDEN=1` (default) adds `-fstack-protector-strong` (+ `-D_FORTIFY_SOURCE=2` unless `FORTIFY=0`) plus linker RELRO/`noexecstack`. Set `HARDEN=0` for a bare build if the target toolchain lacks `libssp`/fortified libc wrappers (`build.sh`, the scripted MIPS cross-build driver, carries its own libc-aware copy of this logic — see `docs/M14-build-hardening.md` for the full rationale). The thingino package (`package/timps/timps.mk`) passes its own `CFLAGS`/`LDFLAGS` and so bypasses this block: a firmware build gets only the hardening buildroot's `TARGET_CFLAGS`/`TARGET_LDFLAGS` carry (`BR2_SSP_*`, `BR2_RELRO_*`, `BR2_FORTIFY_SOURCE_*`). ## Feature flags (`USE_*`) Every optional feature is gated by a `USE_*` Make variable, each turning into a `-DUSE_*` (or a differently-named macro, noted below) preprocessor define. A feature that is off compiles its code out entirely — the resulting binary is byte-identical to a build that never had the feature. | Flag | Default | Effect | | --- | --- | --- | | `USE_CONTROL` | **1 (on)** | Builds `src/control.c` + `src/events.c`: the `/control` live-settings API and the `/events` SSE push stream. See [HTTP /control API Reference](HTTP-Control-API.md). `USE_CONTROL=0` removes it entirely (saves ~15 KB, reduces attack surface). | | `USE_DAYNIGHT` | **1 (on)** | Builds `src/daynight.c`: the native automatic day/night detection thread. See [Day/Night](Day-Night.md). | | `USE_RECORD` | **1 (on)** | Builds `src/record.c`: local SD recording (continuous/motion-triggered fMP4 segments) + the `/control` clip/record endpoints. `USE_RECORD=0` saves ~11 KB. | | `USE_TIMELAPSE` | **1 (on)** | Builds `src/timelapse.c`: periodic JPEG capture to SD. `USE_TIMELAPSE=0` saves ~4 KB. | | `USE_OPENIMP` | 0 (off) | Built against OpenIMP (the open libimp) instead of the vendor libimp. Selects the OpenIMP capability set: `isp_caps.h` (colorfx/scene on T10/T20/T21/T23/T31, DPC on T10/T20/T21, defog/DRC on T20, `ae_compensation`/sinter/temper and the `GetAeExprInfo` AE reserve on T41), `rotate_caps.h` (FrameSource 90/270 on T10/T20/T21 sub streams, with `USE_ROTATE`), `fs_ref.h` (`general.fs_keepalive` auto = on for T23/T40/T41) and OpenIMP motion v2 in `imp_motion.c` (weakly bound, used only when the libimp exports it). thingino sets it when `BR2_PACKAGE_OPENIMP` is selected. Off = byte-identical to a vendor build. The OpenIMP-only ISP setters (DPC, defog/DRC on T20, colorfx/scene) are weak imports: on a vendor libimp the key logs once, is persisted only and drops out of `caps.image` instead of crashing. Still not a supported mix: this flavour links only `-limp`, and a vendor libimp needs `libalog.so`; since v1.10.0 `timpsd` refuses it at start (exit 78, see [below](#which-libimp-a-binary-needs---check-libs)). `scripts/check-weak-imports.sh ` lists strong imports a libimp lacks. | | `USE_FAAC` | 0 (off) | Software AAC audio encoding via `libfaac` (`FAACLIB`, default static `-l:libfaac.a`). Needed for AAC on RTSP/browser preview; without it `audio.codec` should be `pcmu`/`pcma`. | | `USE_TLS` | 0 (off) | HTTPS (`http.https`) + RTSPS (`rtsp.tls`, port 322 by default) via mbedTLS (`src/tls.c`). Needs `-lmbedtls`/`-lmbedx509`/`-lmbedcrypto` available; auto-enabled by the thingino package build when `libmbedtls` is linked. | | `USE_SRT` | 0 (off) | MPEG-TS over SRT output (`src/srt.c`), needs `libsrt`. Serves either as a listener (`srt.mode=listener`, the default) or as a caller dialling `srt.host` (`srt.mode=caller`) — see [Streaming Protocols](Streaming-Protocols.md#srt-srcsrtc). Because SRT links C++ (`libstdc++`), the final link step switches from `gcc` to `g++` (`LINK_DRV`) when this is on. | | `USE_ROTATE` | 0 (off) | Image rotation (`videoN.rotation = 0\|90\|270`, plus `180` on T40/T41): hardware I2D on T40/T41, FrameSource rotate on T31 and, with `USE_OPENIMP`, on T10/T20/T21 sub streams (needs an OpenIMP with T1x rotation, `6b7f5ae`; checked at start), software on T23 with `USE_SW_ROTATE`. Off by default = zero `ROT_HAS_*` macros defined, so all rotation code compiles out. See [Platform & SDK Support](Platform-SDK-Support.md) and `docs/rotation.md`. | | `USE_SW_ROTATE` | 0 (off) | Opt-in **software** 90/270 rotation on T23 only (CPU NV12 transpose + unbound `IMP_Encoder_YuvEncode`; no hardware OSD/privacy on the rotated stream). Implies `USE_ROTATE=1`. Defines `-DMS_ENABLE_SW_ROTATE` (not `USE_SW_ROTATE` itself) in the actual compile. | | `USE_BACKCHANNEL` | 0 (off) | ONVIF-style two-way audio backchannel (client → camera speaker) via native `IMP_AO`. Pure-C G.711 decode built in; see [Audio](Audio.md). | | `USE_BC_AAC` | 0 (off) | Also accept AAC on the backchannel (`libhelix-aac`, `HELIXLIB`/`HELIX_INC`). Implies `USE_BACKCHANNEL=1`. | | `USE_BC_WS` | 0 (off) | Browser-microphone backchannel over an RFC 6455 WebSocket at `/talk` on the http port (`src/ws.c`, `src/sha1.c`, `src/rtsp/talk_ws.c`) — 20 ms binary frames of G.711 mu-law, decoded by the `g711.c` the backchannel already builds, so no new library. Implies `USE_BACKCHANNEL=1` **and** `USE_CONTROL=1` (the `?token=` machinery lives there — a `WebSocket` constructor cannot set request headers). `USE_TLS` is **optional**, not required: without it only `audio.talk_ws=2` (plain `ws://`) is usable. See [Audio](Audio.md#browser-push-to-talk-talk-use_bc_ws). | | `USE_PLAY` | 0 (off) | System-sound play queue: a FIFO at `/run/timps/audio_out` accepting the same `PLAY`/`STOP` protocol prudynt/raptor's `/usr/sbin/play` speaks, driving `IMP_AO` natively. Decodes WAV, raw PCM16, G.711 out of the box. | | `USE_PLAY_OPUS` | 0 (off) | Also decode Ogg-Opus in the play queue (`opusfile`, `OPUSLIB`/`OPUS_INC`). Implies `USE_PLAY=1`. Unrelated to `USE_STREAM_OPUS` below (that is the encode/stream side). | | `USE_STREAM_OPUS` | 0 (off) | Offer **Opus as a live RTSP/RTP streaming audio codec** (RFC 7587): makes `audio.codec = opus` a valid choice alongside `aac`/`pcmu`/`pcma`. The mic is encoded at its capture rate (16 kHz default) in `OPUS_APPLICATION_VOIP` mode; the RTP track is always SDP-signalled as `opus/48000/2` per the RFC (real mono/rate carried out-of-band via `sprop-stereo=0`). RTSP-only, exactly like G.711 — Opus is **not** carried by the HTTP fMP4 preview or the SRT TS mux. Links the bare `libopus` **encoder** (~337 KB, `OPUS_ENC_LIB`, default `-lopus`), **not** `opusfile`/`libogg` — RTP carries raw Opus frames with no Ogg container. Fully independent of `USE_PLAY_OPUS` (local `.opus` file playback); a board may enable either, both, or neither. Off = no Opus stream code compiled in and `opus` is not an accepted `audio.codec` value. See [Audio](Audio.md) and [Streaming Protocols](Streaming-Protocols.md). | | `USE_OSD_HINTING` | 0 (off) | Compile in the opt-in geometric OSD-text autohinter (`autohint_glyph()` and friends in `src/hal/msttf.c`). Still gated at runtime by the `osd.hinting` config key either way; off measures ~2.1 KB smaller `.text` (T31/GCC 16.1.0/`-Os`) and makes `osd.hinting=1` in `timps.conf` accepted but a no-op. | | `USE_TRACE` | 0 (off) | **Developer tool, not for production images.** Compiles in `src/trace.c`, the opt-in send-pipeline latency instrumentation (see `src/trace.h`). Off means `trace.c` is not compiled at all, `ms_trace_on()` and friends become static-false inline stubs the compiler dead-code-eliminates at `-Os`, and `general.trace`/`general.trace_ms` in `timps.conf` are accepted but a no-op — the same pattern as `USE_OSD_HINTING`. Deliberately **not** a menuconfig option (see below); turn it on with `make USE_TRACE=1 ...` for a debug build. | Any combination of `USE_BACKCHANNEL`/`USE_PLAY` pulls in `src/rtsp/speaker.c` + `src/codec/resample.c` (the shared `IMP_AO` owner + resampler) — this is handled automatically by the Makefile's `USE_AUDIO_OUT` derived variable. ### Build recipe internals worth knowing - `BASE` lists the platform-independent sources shared by both `target` and `sim`; `TARGET_SRC`/`SIM_SRC` add the HAL-specific and `USE_CONTROL`-only sources (`control.c`, `events.c`, `daynight.c`) — note the sim build **also** links `control.c`/`events.c`/`daynight.c` unconditionally (so `/control` works against `timpsd-sim` too) and `imp_motion.c` (motion detection is exercised in sim as well, via the `MOTION_AVAILABLE` host-sim fallback in `motion_caps.h`). - The real target build compiles then links in two steps so vendor static libs (`IMPLIBS`) can be linked last; the object files are removed after a successful link (`@rm -f $(TARGET_OBJS)`). - `VERSION` is baked into the binary (`timps -h` / the startup log line) via `git describe --tags --always --dirty`; the buildroot package overrides it with `VERSION=$(TIMPS_VERSION)`. ### Which libimp a binary needs (`--check-libs`) A target `timpsd` fits exactly one libimp stack: `USE_OPENIMP=1` = OpenIMP (thingino `BR2_PACKAGE_OPENIMP`, the `aperto` open stack), `USE_OPENIMP=0` = the Ingenic vendor libimp (linked `-limp -lalog -lsysutils`). **Since v1.10.0** `timpsd` checks the loaded `libimp.so` before it reads the config, takes the lock or calls into IMP (`src/hal/libimp_check.c`), and exits | Exit | Cause | | --- | --- | | **78** | Flavour mismatch. OpenIMP is recognised by `OpenIMP_P0_GetState` / `OpenIMP_AVC_Create` / `OpenIMP_Tuning_Create`; no vendor libimp exports an `OpenIMP_*` symbol. | | **79** | The libimp lacks one of the 24 functions every start calls (`LIBIMP_REQUIRED` in `src/hal/libimp_check.h`). | | **80** | `--check-libs` only: the check could not run (`libimp.so` not openable by name, or none of the 24 names resolves). The daemon itself starts in that case. | with a log line that names both sides and the remedy, e.g. `libimp mismatch: this timpsd is built for OpenIMP (USE_OPENIMP=1) but /usr/lib/libimp.so is the Ingenic vendor libimp - install the vendor-flavour timpsd (USE_OPENIMP=0) or the OpenIMP libimp`. If `libimp.so` cannot be opened or none of the names resolves, it only warns and starts as before. Command line: `timpsd [-c ] [-v] [-V|--version] [--check-libs]`. `--check-libs` runs only this check (no config, no lock, no device) and prints `timpsd: libimp OK (vendor build, /usr/lib/libimp.so)` / exit 0, or one line per problem on stderr and 78/79 (80: could not check). `-V`/`--version` prints `timps `. `-v` is verbose logging, not the version. thingino's `S95timps` runs `--check-libs` before `start-stop-daemon` and does not start a daemon that would exit 78/79 (it logs the lines under tag `timps`); any other result, 80 included, starts as before. A dynamic-loader error (a missing `NEEDED` library) happens before `main()` and is outside what `timpsd` itself can report. The 24 names are checked offline against every libimp at hand: ```sh scripts/check-libimp-required.sh -H # IMP headers of every SoC scripts/check-libimp-required.sh --expect vendor ... scripts/check-libimp-required.sh --expect openimp ... make test-libimp-check # decision + dlopen glue (host) scripts/test_s95timps_preflight.sh /package/timps/files/S95timps ``` A new libimp version must pass `check-libimp-required.sh` before it ships, or it would be refused with exit 79. The table also compiles against the SoC's IMP headers (`sizeof(&fn)`), so a name a header lacks is a build error. ### Self-test target ```sh make test-auth ``` Builds `timpsd-sim` with `USE_CONTROL=1`, starts it on unprivileged ports, runs `scripts/test_auth.sh` against it (RTSP fully exercised; HTTP negative tests are skipped over loopback since the HTTP server trusts `127.0.0.0/8` — point it at a real LAN IP to exercise those), then stops it and propagates the exit code. ## Building as part of thingino-firmware A full camera firmware image builds timps through a buildroot package in the sibling `thingino-firmware` (or `thingino-firmware-LuFi`) tree, at `package/timps/`: ```sh make menuconfig # Streamer Packages → Streamer → timps (deselect prudynt/raptor) make # build the firmware, flash as usual ``` `package/timps/timps.mk` invokes this project's `Makefile` with `PLATFORM=` and the cross toolchain, translating each `BR2_PACKAGE_TIMPS_*` Kconfig boolean into the matching `USE_*` flag, e.g.: | Kconfig option | `USE_*` flag | Default | | --- | --- | --- | | `BR2_PACKAGE_TIMPS_FAAC` | `USE_FAAC` | y | | `BR2_PACKAGE_TIMPS_STREAM_OPUS` (`select`s `BR2_PACKAGE_OPUS`) | `USE_STREAM_OPUS` | n | | `BR2_PACKAGE_TIMPS_CONTROL` | `USE_CONTROL` | y | | `BR2_PACKAGE_TIMPS_DAYNIGHT` | `USE_DAYNIGHT` | y | | `BR2_PACKAGE_TIMPS_RECORD` | `USE_RECORD` | y | | `BR2_PACKAGE_TIMPS_TIMELAPSE` | `USE_TIMELAPSE` | y | | `BR2_PACKAGE_TIMPS_TLS` | `USE_TLS` | y | | `BR2_PACKAGE_TIMPS_SRT` | `USE_SRT` | n | | `BR2_PACKAGE_TIMPS_ROTATE` | `USE_ROTATE` | n | | `BR2_PACKAGE_TIMPS_SW_ROTATE` (depends on `ROTATE`) | `USE_SW_ROTATE` | n | | `BR2_PACKAGE_TIMPS_BACKCHANNEL` | `USE_BACKCHANNEL` | n | | `BR2_PACKAGE_TIMPS_BC_AAC` (depends on `BACKCHANNEL`) | `USE_BC_AAC` | n | | `BR2_PACKAGE_TIMPS_BC_WS` (depends on `BACKCHANNEL` + `CONTROL`) | `USE_BC_WS` | **y on TLS builds**, else n | | `BR2_PACKAGE_TIMPS_PLAY` | `USE_PLAY` | n | | `BR2_PACKAGE_TIMPS_PLAY_OPUS` (depends on `PLAY`) | `USE_PLAY_OPUS` | n | | `BR2_PACKAGE_TIMPS_OSD_HINTING` | `USE_OSD_HINTING` | n | `USE_TRACE` has **no** Kconfig option on purpose — it must not be reachable from menuconfig. For a one-off debug build, pass `TIMPS_TRACE=1` as a plain make-variable override on the firmware build invocation (`make ... TIMPS_TRACE=1 rebuild-timps`); unset, the package builds `USE_TRACE=0` like every other build. The package also exposes an **"Audio backchannel preset"** choice that bundles the interdependent audio options for convenience: `MANUAL` (default, set the above individually), `FULL` (backchannel + AAC + Opus play queue, confirmed to fit T31-family flash, too large for a ~5 MB T20 image), and `MINIMAL` (backchannel + G.711 play queue only, no extra codec libraries — confirmed on a 5 MB-rootfs T20 board). Beyond the Makefile translation, the package's finalize hooks handle cross-package integration: swapping in timps-native WebUI bridge CGIs and a fMP4/MSE preview page when `BR2_PACKAGE_THINGINO_WEBUI` and `BR2_PACKAGE_TIMPS_CONTROL` are both enabled, disabling thingino's standalone `S97daynightd` init script when `USE_DAYNIGHT` is on (timps is the detector), and installing timps's own `S96onvif_discovery` (which sources ONVIF credentials from `/etc/timps.conf`). The package ships a default `/etc/timps.conf` (`package/timps/files/timps.conf`) with `rtsp.user`/`rtsp.pass` and `http.user`/`http.pass` both set to `thingino`/`thingino` — the same default-credential convention prudynt-t uses on thingino; leaving `rtsp.user` empty makes RTSP open (no auth). See [Configuration Reference](Configuration-Reference.md) and [HTTP /control API Reference](HTTP-Control-API.md) for the full authentication model. This wiki's focus is the timps daemon itself; consult `package/timps/timps.mk`/`Config.in` in the firmware repo directly for the authoritative, up-to-date buildroot integration.