LD_PRELOAD hook for Tuya-like cameras based on Ingenic T31X.
It intercepts IMP_Encoder_GetStream() from libimp.so and mirrors encoded video to a local TCP socket, while keeping the original fw process running normally.
This allows RTSP/WebRTC/HLS/MP4 streaming on your local network via go2rtc without Tuya cloud dependency, while preserving its original functionality.
Based on: https://github.com/FiveLeavesLeft/WyzeCameraLiveStream
- Original app still calls Ingenic encoder APIs as usual.
- Hooked
IMP_Encoder_GetStream()forwards the call to the reallibimp.so. - On success, encoded packs are copied to one TCP client (
0.0.0.0:12345by default). - If needed, the hook requests an IDR frame (
IMP_Encoder_RequestIDR) when a client connects or when backpressure is detected. - Stream starts only after a keyframe to avoid decoder startup issues.
- Pack payload is normalized for decoder compatibility:
- Annex-B packets are sent as-is.
- AVCC-length packets are converted to Annex-B (
00 00 00 01prefixes).
ROOT access to the camera is required.
Download libimp.so from releases page, copy to the camera (e.g., to the SD card) and launch the main fw app with LD_PRELOAD set accordingly.
Example:
LD_PRELOAD=/mnt/extsd/lib/libimp.so /tmp/jsapp/baby_monitor &With logging enabled:
STREAM_HACK_LOG=/mnt/extsd/stream_hack.log LD_PRELOAD=/mnt/extsd/lib/libimp.so /tmp/jsapp/baby_monitor &An external go2rtc instance is required to display the stream via RTSP, WebRTC, MP4, HLS, etc.
go2rtc config example:
streams:
my_camera_local: tcp://192.168.1.25:12345/Replace 192.168.1.25 with your camera IP.
-
STREAM_HACK_LOG0or unset: logging disabled1: log to default path (/mnt/extsd/stream_hack.log)- any other non-empty value: treated as full log file path
-
STREAM_HACK_PORT- TCP listen port (default:
12345)
- TCP listen port (default:
-
STREAM_HACK_CHANNEL- Fixed encoder channel to stream (default:
0) -1enables auto channel detection from first matching stream callback
- Fixed encoder channel to stream (default:
-
STREAM_HACK_MAX_DROP_STREAK- Max consecutive
would-blockframe drops before disconnecting slow client (default:90)
- Max consecutive
-
STREAM_HACK_LIBIMP- Path to real Ingenic library (default:
/usr/lib/libimp.so)
- Path to real Ingenic library (default:
- Single-client model: only one TCP consumer at a time;
- New client connection replaces the previous one;
- Non-blocking socket send is used to avoid stalling fw app;
- On backpressure, frames may be dropped intentionally to keep the main process responsive;
- Connection is dropped only on persistent slowness or hard socket errors.
- Video only (no audio forwarding);
- One TCP client at a time;
- Raw elementary stream transport (no RTSP/HTTP framing in hook itself).
Check:
- Hook loaded (
LD_PRELOADpath correct); - Port open (
STREAM_HACK_PORT); - Correct encoder channel (
STREAM_HACK_CHANNEL, usually0); - Log file for connect/disconnect and socket errors.
Common causes:
- Incompatible toolchain;
- Blocking operations inside hook path;
- Too heavy logging;
- ABI mismatch with
IMPEncoderStreamlayout.
Current code avoids blocking sends and uses T31X-compatible imp/imp_encoder.h layout.
Usually means stream did not begin with a decodable keyframe or packet header format mismatch. Current implementation already mitigates this by:
- Waiting for a keyframe before sending;
- Forcing IDR on connect;
- Converting AVCC packets to Annex-B when needed.
Toolchain used: https://github.com/Dafang-Hacks/mips-gcc472-glibc216-64bit
make clean && make buildResult: dist/libimp.so in project root.
These points should be kept in mind for future changes:
-
Header/ABI must match camera firmware.
IMPEncoderStreamlayout differs across SDK/header versions. Wrong struct layout causes random failures/crashes. -
Do not block in
IMP_Encoder_GetStreamhook path. Any blocking I/O can trigger watchdog. -
Keyframe-first startup is required. Sending P-frames first often breaks decoder startup.
-
Annex-B vs AVCC can vary. Some payloads arrive without Annex-B start codes; normalize to Annex-B for better player compatibility.
-
Drop frames under pressure, do not stall producer. It is better to drop and recover via IDR than to block encoder callback flow.
-
Keep logging optional and lightweight. High-frequency logs can destabilize low-power camera environments.
-
Prefer minimal complexity in hook code. This code runs in a critical process. Simpler is safer.
Inspired by FiveLeavesLeft and WyzeCameraLiveStream project.
Thanks to AlexxIT for go2rtc project, Dafang-Hacks / EliasKotlyar for toolchain and gtxaspec for clues.
Thanks to Codex for helping with refactoring.