Your First C++ Program
This page uses the repo's built-in get_started sample to walk you through the camera's minimal capture loop. All build, deploy, and run commands below revolve around this sample; its executable is hv_sample_get_started.
First clone the toolkit release repository (gitee and github have identical content; for more download options see Downloads):
# gitee (recommended in China)
git clone https://gitee.com/ShiMetaPi_0/shimetapi_hybrid_vision_toolkit.git
# github
git clone https://github.com/ShiMetaPi/shimetapi_hybrid_vision_toolkit.gitIn v2.0 the three backends (USB / MIPI / Ethernet) share the same Camera API; the backend is selected via DeviceConfig.backend.
Platform overview
The Hybrid Vision Toolkit release (shimetapi_Hybrid_vision_toolkit_release) is distributed as precompiled .so libraries, currently adapted to three platforms: x86_64 (USB), S100, and X5 — the prebuilt libraries ship with the repo (lib/x86_64, lib/s100, lib/x5); building only compiles the samples and links the library for the target architecture:
| Platform | Interface | Prebuilt libs | Build | Status |
|---|---|---|---|---|
| x86_64 (Ubuntu host) | USB camera | lib/x86_64 | ./run.sh build | ✅ Adapted |
| S100 (RDK carrier board) | MIPI module | lib/s100 | ./run.sh build s100 | ✅ Adapted |
| X5 (RDK carrier board) | MIPI module | lib/x5 | ./run.sh build x5 | ✅ Adapted |
| RK3588 | MIPI module | — | — | ⏳ Pending |
- Build artifacts go to
out/<arch>/build(the three architectures don't overwrite each other);./run.sh --listshows the prebuilt-library readiness. - Platform differences only show up in build method and deployment paths — at the API level USB / MIPI are identical except for
DeviceConfig.backendand the event decoder (USB usesEvt2Decoder, MIPI usesMipiRaw8Decoder).
1. USB (x86_64)
APIs involved
| API | Description | Docs |
|---|---|---|
Camera | Unified capture (Init → StartStream → GetFrame → …) | View → |
DeviceConfig | Backend + VID/PID configuration | View → |
Frame | Unified frame (evs event bytes + aps interpreted per Frame.format) | View → |
EventCD | Decoded event (x, y, t, polarity) | View → |
Evt2Decoder | EVT2 byte stream → EventCD | View → |
Prerequisites
sudo apt-get update
sudo apt-get install -y build-essential cmake libusb-1.0-0 libopencv-devCore code
Below is the teaching version of the get_started sample — command-line argument parsing is omitted to focus on the minimal USB-backend flow. The actual source at samples/cpp/get_started/main.cpp in the repo also supports switching backends from the command line (--mipi / --sensor-index N, or passing VID PID as arguments); see that file for the full logic.
The USB backend selects the device by VID/PID; GetFrame synchronously pulls the combined frame (events + APS), then Evt2Decoder decodes it:
#include <shimetapi/hv/camera.h>
#include <shimetapi/hv/device_config.h>
#include <shimetapi/codec/evt2_codec.h>
#include <iostream>
#include <vector>
int main() {
Shimeta::hv::Camera cam;
Shimeta::hv::DeviceConfig cfg;
cfg.backend = Shimeta::hv::Backend::Usb;
cfg.vendor_id = 0x1d6b; // replace with your actual VID/PID
cfg.product_id = 0x0105;
cfg.event_fmt = Shimeta::hv::EventFormat::Evt2;
cam.Init(cfg);
if (!cam.StartStream()) {
std::cerr << "Failed to open the camera; check the USB connection and permissions." << std::endl;
return 1;
}
std::cout << "Camera ready" << std::endl;
// Synchronously pull 10 frames
Shimeta::codec::Evt2Decoder dec;
Shimeta::Frame f;
for (int i = 0; i < 10; ++i) {
if (cam.GetFrame(f, 1000)) {
std::vector<Shimeta::EventCD> events;
dec.Decode(f.evs.data, f.evs.size, events); // raw bytes → EventCD
std::cout << "frame " << i << ": evs=" << f.evs.size
<< " bytes, decoded " << events.size() << " events" << std::endl;
}
}
cam.StopStream();
cam.Destroy();
return 0;
}In v2.0 the camera delivers raw event bytes (Frame.evs), which must be decoded with the matching codec. EventCD fields: x/y (coordinates), t (microsecond timestamp), polarity (true = CD_ON / false = CD_OFF).
Build and run
cd shimetapi_Hybrid_vision_toolkit # the repo directory cloned above
./run.sh build # prebuilt libs ship with the repo; only samples are compiled; on an x86_64 host the default target is x86_64./out/x86_64/build/samples/cpp/get_started/hv_sample_get_started # default 0x1d6b:0x0105
./out/x86_64/build/samples/cpp/get_started/hv_sample_get_started 0x1d6b 0x0105 # explicit VID PIDUSB permissions: if you get LIBUSB_ERROR_ACCESS, the recommended fix is a udev rule (no sudo needed at runtime):
echo 'SUBSYSTEM=="usb", ATTR{idVendor}=="1d6b", ATTR{idProduct}=="0105", MODE="0666"' \
| sudo tee /etc/udev/rules.d/99-hv-camera.rules
sudo udevadm control --reload-rules && sudo udevadm trigger2. S100 (MIPI / cross-compilation)
APIs involved
| API | Description | Docs |
|---|---|---|
Camera | Unified capture (same as USB, backend switched to Mipi) | View → |
DeviceConfig | backend + evs_fps / sensor_index | View → |
Frame | Unified frame (evs RAW8 + aps image) | View → |
MipiRaw8Decoder | RAW8 subframe stream → EventCD | View → |
MIPI is not a USB device: instead of VID/PID, devices are selected by sensor index. On S100, the apx003cc sensor configuration (linear_4096x256_raw8) has index 9 (the sample gets this default injected by CMake per architecture).
Prerequisites
All of the following cross-compiles on an x86_64 host (no build environment needed on the board):
# 1) aarch64 cross toolchain (the Ubuntu system package is enough)
sudo apt-get install -y g++-aarch64-linux-gnu
# 2) S100 board sysroot (evs_device_vendor_sdk repo; gitee and github have identical content)
git clone https://gitee.com/ShiMetaPi_0/evs_device_vendor_sdk.git # recommended in China
# git clone https://github.com/ShiMetaPi/evs_device_vendor_sdk.git # overseas mirror
export S100_SYSROOT=$PWD/evs_device_vendor_sdk/source/hobot-multimedia/debian/usrCore code
Teaching version — the flow that get_started --mipi runs on the board (EVS-only single VC, device selected by sensor index):
#include <shimetapi/hv/camera.h>
#include <shimetapi/hv/device_config.h>
#include <shimetapi/codec/mipi_raw8_codec.h>
#include <iostream>
#include <vector>
int main() {
Shimeta::hv::Camera cam;
Shimeta::hv::DeviceConfig cfg;
cfg.backend = Shimeta::hv::Backend::Mipi; // EVS-only single VC
cfg.sensor_index = 9; // S100: index of apx003cc linear_4096x256_raw8
cam.Init(cfg);
if (!cam.StartStream()) {
std::cerr << "Failed to start the MIPI device" << std::endl;
return 1;
}
std::cout << "MIPI device started" << std::endl;
// MIPI Frame.evs is an apx003 RAW8 subframe stream → use MipiRaw8Decoder (not Evt2Decoder)
Shimeta::codec::MipiRaw8Decoder dec;
Shimeta::Frame f;
for (int i = 0; i < 10; ++i) {
if (cam.GetFrame(f, 1000)) {
std::vector<Shimeta::EventCD> events;
dec.Decode(f.evs.data, f.evs.size, events); // adapts to the subframe count by data length
std::cout << "frame " << i << ": decoded " << events.size() << " events" << std::endl;
}
}
cam.StopStream();
cam.Destroy();
return 0;
}MIPI's
Frame.evsis a RAW8 subframe stream and must be decoded withMipiRaw8Decoder, notEvt2Decoder— this is the only decoding difference between USB and MIPI.
Build and deploy
./run.sh build s100 # the aarch64 toolchain file is injected automatically (toolchains/toolchain-aarch64-linux-gnu.cmake)
file out/s100/build/samples/cpp/get_started/hv_sample_get_started # verify: should be ELF aarch64out/s100/build is a self-contained directory — the build bundles the libshimetapi_*.so libraries from lib/s100 into it, and the sample rpath resolves via $ORIGIN, so copying the whole directory onto the board is enough to run:
# On the host: deploy to the board
scp -r out/s100/build root@<board-IP>:/app/
# Run on the board (this is the mode of the core code above)
export LD_LIBRARY_PATH=/app/build # prebuilt libs and executables live in the same build directory
/app/build/samples/cpp/get_started/hv_sample_get_started --mipi # EVS-only, sensor_index defaults to 9
/app/build/samples/cpp/get_started/hv_sample_get_started --mipi --sensor-index 9 # explicit indexNo cross-compilation environment needed on the board
S100_SYSROOT, the aarch64 toolchain, etc. are only used on the build host; the board runs out of the box. OpenCV-based samples (player / live_record_display) also need the repo's third_party/aarch64_opencv/lib/aarch64-linux-gnu copied onto the board with export LD_LIBRARY_PATH pointing to it.
3. X5 (MIPI / cross-compilation)
X5 works the same way as S100 (same API, same samples); only three things differ: the SDK path environment variable, the sensor_index default, and the APS output format.
Prerequisites
# 1) aarch64 cross toolchain (same as S100)
sudo apt-get install -y g++-aarch64-linux-gnu
# 2) X5 SDK source tree (evs_device_vendor_sdk repo, x5_v3.4.1 branch; gitee and github have identical content)
git clone -b x5_v3.4.1 --single-branch https://gitee.com/ShiMetaPi_0/evs_device_vendor_sdk.git
# or (overseas) git clone -b x5_v3.4.1 --single-branch https://github.com/ShiMetaPi/evs_device_vendor_sdk.git
export X5_SDK_ROOT=$PWD/evs_device_vendor_sdk # tells the build script where the SDK isCore code, build, and deploy
The code is line-for-line identical to the S100 section (same Camera API + MipiRaw8Decoder) and cross-compiles without source changes — X5's differences are only in build configuration and on-board output, so build directly:
./run.sh build x5 # reads X5_SDK_ROOT
file out/x5/build/samples/cpp/get_started/hv_sample_get_started # should be ELF aarch64
# Deploy and run like S100: copy the whole directory onto the board, set LD_LIBRARY_PATH there first
scp -r out/x5/build root@<board-IP>:/app/
export LD_LIBRARY_PATH=/app/build # run on the board
/app/build/samples/cpp/get_started/hv_sample_get_started --mipi # EVS-only, sensor_index defaults to 494. RK3588 (pending)
The RK3588 platform is not yet adapted: the current release ships no lib/rk3588 prebuilt library, ./run.sh has no rk3588 build target yet, and ./run.sh --list won't list this architecture for now.
- No code changes needed in advance: once adapted it will still use
Backend::Mipi(EVS-only) with the sameCameraAPI andMipiRaw8Decoder— you'll only need to rebuild the samples against thelib/rk3588prebuilt library. - Still to come: the
lib/rk3588prebuilt library (aarch64) + cross-compilation integration against the matching board sysroot + on-board validation (including the sensor_index table). - Watch Downloads for release updates, or contact technical support.
5. Creating a new program
Creating a new sample under samples/cpp/ (say my_demo) requires 3 changes, all mandatory — skipping ② or ③ means your sample never gets compiled. These changes are the same for USB, S100, and X5; in main.cpp pick the USB or MIPI core code from this page for your target platform.
① Create the sample
samples/cpp/my_demo/
├── CMakeLists.txt # sample build script
└── main.cpp # sample source (either USB or MIPI backend)CMakeLists.txt can copy get_started's verbatim (link the IMPORTED targets defined by the root CMakeLists; no need to spell out header paths or .so locations):
add_executable(hv_sample_my_demo main.cpp)
target_link_libraries(hv_sample_my_demo PRIVATE
HVToolkit::shimetapi_hv HVToolkit::shimetapi_codec HVToolkit::shimetapi_io)- Multiple source files: append to
add_executable, e.g.add_executable(hv_sample_my_demo main.cpp utils.cpp) - Need OpenCV (display-window samples): copy the conditional block from
samples/cpp/player/CMakeLists.txt; when cross-compiling it automatically usesthird_party/aarch64_opencv, so OpenCV doesn't need to be installed on the board.
② Register the subdirectory
add_subdirectory(cpp/my_demo)Without this line the directory won't be compiled even though it exists.
③ Add the dependency
Find add_dependencies(bundle_libs ...) (around line 119) and append the new target name:
add_dependencies(bundle_libs
hv_sample_get_started hv_sample_callback hv_sample_record hv_sample_viewer
hv_sample_bench_hw hv_sample_live_record_display hv_sample_player
hv_sample_my_demo) # ← addedThis step ensures "bundle the prebuilt libs into the build root" happens after your new sample builds. It compiles without it, but the deploy directory may end up missing libraries.
④ Register the sample (optional)
SAMPLE_NAMES="get_started callback record viewer bench_hw live_record_display player my_demo"This only affects whether ./run.sh samples lists the new sample (OK/MISS status); it has nothing to do with compilation.
Build and run
| Platform | Build | Run |
|---|---|---|
| USB (x86_64) | ./run.sh build | ./out/x86_64/build/samples/cpp/my_demo/hv_sample_my_demo |
| S100 (MIPI) | ./run.sh build s100 | After deploying out/s100/build, run /app/build/samples/cpp/my_demo/hv_sample_my_demo --mipi |
| X5 (MIPI) | ./run.sh build x5 | After deploying out/x5/build, run /app/build/samples/cpp/my_demo/hv_sample_my_demo --mipi |
For S100 and X5, the cross-compilation prerequisites and deployment environment variables follow their respective platform sections; the MIPI sensor_index defaults are 9 and 49 respectively.
If artifacts misbehave after editing CMakeLists
CMake caches old target dependencies. If behavior doesn't match expectations after an edit, delete the corresponding out/<arch>/build and rebuild.
6. More information
S100 vs X5 differences
| S100 | X5 | |
|---|---|---|
| SDK environment var | S100_SYSROOT | X5_SDK_ROOT |
sensor_index default¹ | 9 | 49 |
| APS output format² | NV12 (color) | Gray8 (gray) |
¹ Both are indexes of the apx003cc linear_4096x256_raw8 configuration — X5's SDK sensor list is longer, so the same configuration lands at 49.
² On S100 the APS goes through ISP/PYM and outputs NV12; on X5 the current ISP 2A ioctl is restricted, so the APS takes the VIN direct-read RAW10→Gray8 bypass — this is expected behavior (not a fault); applications just branch on Frame.format when decoding.
7. Further reading
- Full API reference: C++ API
- Task-based deep dives: Programming Guides (open camera → read events → record → denoise → display → tune)
- Samples overview: Samples Overview
- Full board-side workflow (flashing images, hardware connection): RDK S100 Carrier Board / RDK X5 Carrier Board
