One more update. I built minimal sample demonstrating the issue. See it in the end of the post. It depends only on Nvidia libraries and STL. And also I noticed that in order to reproduce issue we don’t even need any reboot or systemd service, it is enough to build it and re-run several times within the same boot. After 2-3 attempts usually it fails because cannot obtain any frames after successful init of SIPL.
What can be a root cause of the problem?
// Minimal raw SIPL camera init test - zero dependencies.
// Tests whether NvSIPL camera capture works at the current optimization level.
// Exit code: 0 = frames received, 1 = no frames (init or capture failure).
#include <chrono>
#include <cstdarg>
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <ctime>
#include <fstream>
#include <map>
#include <optional>
#include <string>
#include <thread>
#include <vector>
#include "NvSIPLCamera.hpp"
#include "NvSIPLClient.hpp"
#include "NvSIPLPipelineMgr.hpp"
#include "NvSIPLQuery.hpp"
#include "nvscibuf.h"
#include "nvscisync.h"
// --- Simple logging (no external deps) ---
static FILE* g_log_file = nullptr;
__attribute__((format(printf, 2, 3))) static void log_msg(const char* level, const char* fmt, ...)
{
auto now = std::chrono::system_clock::now();
auto t = std::chrono::system_clock::to_time_t(now);
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()) % 1000;
struct tm tm_buf{};
localtime_r(&t, &tm_buf);
char ts[32];
std::snprintf(ts, sizeof(ts), "%04d-%02d-%02d %02d:%02d:%02d.%03d", tm_buf.tm_year + 1900, tm_buf.tm_mon + 1,
tm_buf.tm_mday, tm_buf.tm_hour, tm_buf.tm_min, tm_buf.tm_sec, static_cast<int>(ms.count()));
va_list args;
va_start(args, fmt);
std::fprintf(stderr, "[%s] [%s] ", ts, level);
std::vfprintf(stderr, fmt, args);
std::fprintf(stderr, "\n");
std::fflush(stderr);
va_end(args);
if (g_log_file != nullptr)
{
va_start(args, fmt);
std::fprintf(g_log_file, "[%s] [%s] ", ts, level);
std::vfprintf(g_log_file, fmt, args);
std::fprintf(g_log_file, "\n");
std::fflush(g_log_file);
va_end(args);
}
}
#define LOG_INFO(...) log_msg("info", __VA_ARGS__)
#define LOG_ERROR(...) log_msg("error", __VA_ARGS__)
#define LOG_WARN(...) log_msg("warn", __VA_ARGS__)
// --- Configuration ---
static constexpr const char* PLATFORM_CFG_NAME = "IMX728_96724_RGGB_CPHY_x4";
static const std::vector<std::uint32_t> LINK_MASKS = {0x1111, 0x0000, 0x0000, 0x1101};
static constexpr std::uint32_t NUM_ICP_BUFFERS = 6;
static constexpr std::uint32_t NUM_ISP0_BUFFERS = 4;
static constexpr const char* NITO_FOLDER = "/usr/share/camera/";
static constexpr int INIT_POLL_ITERATIONS = 50;
static constexpr int INIT_POLL_INTERVAL_MS = 100;
static constexpr std::uint32_t FRAME_TIMEOUT_US = 1'000'000; // 1s blocking timeout per sensor
static constexpr int FPS_LOG_INTERVAL_S = 5;
// --- Helpers ---
static std::optional<std::vector<std::uint8_t>> read_file(const std::string& path)
{
std::ifstream file(path, std::ios::binary | std::ios::ate);
if (!file.is_open())
{
return std::nullopt;
}
const auto size = file.tellg();
file.seekg(0, std::ios::beg);
std::vector<std::uint8_t> data(static_cast<std::size_t>(size));
if (!file.read(reinterpret_cast<char*>(data.data()), size))
{
return std::nullopt;
}
return data;
}
template <typename T>
static void set_buf_attr(NvSciBufAttrList list, NvSciBufAttrKey key, T value)
{
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
NvSciBufAttrKeyValuePair kvp = {key, const_cast<void*>(static_cast<const void*>(&value)), sizeof(T)};
auto err = NvSciBufAttrListSetAttrs(list, &kvp, 1);
if (err != NvSciError_Success)
{
LOG_ERROR("NvSciBufAttrListSetAttrs failed: key=%d, err=%d", static_cast<int>(key), static_cast<int>(err));
}
}
template <typename T>
static void set_sync_attr(NvSciSyncAttrList list, NvSciSyncAttrKey key, T value)
{
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
NvSciSyncAttrKeyValuePair kvp = {key, const_cast<void*>(static_cast<const void*>(&value)), sizeof(T)};
auto err = NvSciSyncAttrListSetAttrs(list, &kvp, 1);
if (err != NvSciError_Success)
{
LOG_ERROR("NvSciSyncAttrListSetAttrs failed: key=%d, err=%d", static_cast<int>(key), static_cast<int>(err));
}
}
// --- Per-sensor state for cleanup ---
struct SensorState
{
std::string module_name;
nvsipl::INvSIPLFrameCompletionQueue* isp0_queue = nullptr;
nvsipl::INvSIPLFrameCompletionQueue* icp_queue = nullptr;
// ICP buffers
NvSciBufAttrList icp_attr_list = nullptr;
NvSciBufAttrList icp_reconciled = nullptr;
NvSciBufAttrList icp_conflict = nullptr;
std::vector<NvSciBufObj> icp_bufs;
// ISP0 buffers
NvSciBufAttrList isp0_attr_list = nullptr;
NvSciBufAttrList isp0_reconciled = nullptr;
NvSciBufAttrList isp0_conflict = nullptr;
std::vector<NvSciBufObj> isp0_bufs;
// ISP0 EOF sync
NvSciSyncAttrList sync_signaler = nullptr;
NvSciSyncAttrList sync_waiter = nullptr;
NvSciSyncAttrList sync_reconciled = nullptr;
NvSciSyncAttrList sync_conflict = nullptr;
NvSciSyncObj sync_obj = nullptr;
};
static void cleanup_sensor(SensorState& s)
{
for (auto& buf : s.icp_bufs)
{
if (buf != nullptr)
{
NvSciBufObjFree(buf);
}
}
for (auto& buf : s.isp0_bufs)
{
if (buf != nullptr)
{
NvSciBufObjFree(buf);
}
}
if (s.icp_attr_list != nullptr)
{
NvSciBufAttrListFree(s.icp_attr_list);
}
if (s.icp_reconciled != nullptr)
{
NvSciBufAttrListFree(s.icp_reconciled);
}
if (s.icp_conflict != nullptr)
{
NvSciBufAttrListFree(s.icp_conflict);
}
if (s.isp0_attr_list != nullptr)
{
NvSciBufAttrListFree(s.isp0_attr_list);
}
if (s.isp0_reconciled != nullptr)
{
NvSciBufAttrListFree(s.isp0_reconciled);
}
if (s.isp0_conflict != nullptr)
{
NvSciBufAttrListFree(s.isp0_conflict);
}
if (s.sync_signaler != nullptr)
{
NvSciSyncAttrListFree(s.sync_signaler);
}
if (s.sync_waiter != nullptr)
{
NvSciSyncAttrListFree(s.sync_waiter);
}
if (s.sync_reconciled != nullptr)
{
NvSciSyncAttrListFree(s.sync_reconciled);
}
if (s.sync_conflict != nullptr)
{
NvSciSyncAttrListFree(s.sync_conflict);
}
if (s.sync_obj != nullptr)
{
NvSciSyncObjFree(s.sync_obj);
}
}
static std::string parse_log_file(int argc, char** argv)
{
for (int i = 1; i < argc - 1; i++)
{
if (std::string(argv[i]) == "--log-file")
{
return argv[i + 1]; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
}
}
return "";
}
int main(int argc, char** argv)
{
std::string log_file = parse_log_file(argc, argv);
if (!log_file.empty())
{
g_log_file = std::fopen(log_file.c_str(), "w");
if (g_log_file != nullptr)
{
LOG_INFO("Logging to file: %s", log_file.c_str());
}
}
LOG_INFO("=== SIPL Init Test starting ===");
NvSciBufModule buf_module = nullptr;
NvSciSyncModule sync_module = nullptr;
std::unique_ptr<nvsipl::INvSIPLCamera> sipl_camera;
std::map<std::uint32_t, SensorState> sensors;
bool success = false;
bool started = false;
auto cleanup = [&]()
{
if (sipl_camera)
{
if (started)
{
LOG_INFO("Stopping...");
sipl_camera->Stop();
}
LOG_INFO("Deinitializing...");
sipl_camera->Deinit();
sipl_camera.reset();
}
for (auto& [id, s] : sensors)
{
cleanup_sensor(s);
}
if (sync_module != nullptr)
{
NvSciSyncModuleClose(sync_module);
}
if (buf_module != nullptr)
{
NvSciBufModuleClose(buf_module);
}
if (g_log_file != nullptr)
{
std::fclose(g_log_file);
g_log_file = nullptr;
}
};
try
{
// 1. Open NvSci modules
LOG_INFO("Opening NvSciBuf module...");
auto err = NvSciBufModuleOpen(&buf_module);
if (err != NvSciError_Success)
{
LOG_ERROR("NvSciBufModuleOpen failed: %d", static_cast<int>(err));
cleanup();
return 1;
}
LOG_INFO("Opening NvSciSync module...");
err = NvSciSyncModuleOpen(&sync_module);
if (err != NvSciError_Success)
{
LOG_ERROR("NvSciSyncModuleOpen failed: %d", static_cast<int>(err));
cleanup();
return 1;
}
// 2. Load platform config
LOG_INFO("Loading platform config: %s", PLATFORM_CFG_NAME);
auto p_query = nvsipl::INvSIPLQuery::GetInstance();
if (p_query == nullptr)
{
LOG_ERROR("Failed to get INvSIPLQuery instance");
cleanup();
return 1;
}
auto status = p_query->ParseDatabase();
if (status != nvsipl::NVSIPL_STATUS_OK)
{
LOG_ERROR("ParseDatabase failed: %d", static_cast<int>(status));
cleanup();
return 1;
}
nvsipl::PlatformCfg platform_cfg{};
status = p_query->GetPlatformCfg(PLATFORM_CFG_NAME, platform_cfg);
if (status != nvsipl::NVSIPL_STATUS_OK)
{
LOG_ERROR("GetPlatformCfg failed: %d", static_cast<int>(status));
cleanup();
return 1;
}
status = p_query->ApplyMask(platform_cfg, LINK_MASKS);
if (status != nvsipl::NVSIPL_STATUS_OK)
{
LOG_ERROR("ApplyMask failed: %d", static_cast<int>(status));
cleanup();
return 1;
}
// Set resetAll on device blocks and deserializers
for (std::uint32_t d = 0; d < platform_cfg.numDeviceBlocks; d++)
{
platform_cfg.deviceBlockList[d].resetAll = true;
platform_cfg.deviceBlockList[d].deserInfo.resetAll = true;
}
LOG_INFO("Platform: %s, numDeviceBlocks: %u", platform_cfg.platform.c_str(), platform_cfg.numDeviceBlocks);
// 3. Create SIPL camera
LOG_INFO("Creating INvSIPLCamera...");
sipl_camera = nvsipl::INvSIPLCamera::GetInstance();
if (sipl_camera == nullptr)
{
LOG_ERROR("Failed to get INvSIPLCamera instance");
cleanup();
return 1;
}
nvsipl::NvSIPLDeviceBlockQueues device_block_queues{};
status = sipl_camera->SetPlatformCfg(&platform_cfg, device_block_queues);
if (status != nvsipl::NVSIPL_STATUS_OK)
{
LOG_ERROR("SetPlatformCfg failed: %d", static_cast<int>(status));
cleanup();
return 1;
}
LOG_INFO("SetPlatformCfg OK");
// 4. Configure pipelines (ISP0 + ICP only)
for (std::uint32_t d = 0; d < platform_cfg.numDeviceBlocks; d++)
{
const auto& db = platform_cfg.deviceBlockList[d];
for (std::uint32_t m = 0; m < db.numCameraModules; m++)
{
const auto& module = db.cameraModuleInfoList[m];
const std::uint32_t sensor_id = module.sensorInfo.id;
nvsipl::NvSIPLPipelineConfiguration pipeline_cfg{};
pipeline_cfg.captureOutputRequested = true;
pipeline_cfg.isp0OutputRequested = true;
pipeline_cfg.isp1OutputRequested = false;
pipeline_cfg.isp2OutputRequested = false;
pipeline_cfg.disableSubframe = false;
nvsipl::NvSIPLPipelineQueues pipeline_queues{};
status = sipl_camera->SetPipelineCfg(sensor_id, pipeline_cfg, pipeline_queues);
if (status != nvsipl::NVSIPL_STATUS_OK)
{
LOG_ERROR("SetPipelineCfg failed for sensor %u: %d", sensor_id, static_cast<int>(status));
cleanup();
return 1;
}
SensorState ss;
ss.module_name = module.name;
ss.isp0_queue = pipeline_queues.isp0CompletionQueue;
ss.icp_queue = pipeline_queues.captureCompletionQueue;
sensors[sensor_id] = std::move(ss);
LOG_INFO("Sensor %u: pipeline configured, module='%s'", sensor_id, module.name.c_str());
}
}
// 5. Allocate buffers (ICP + ISP0 for each sensor)
for (auto& [sensor_id, ss] : sensors)
{
// --- ICP buffers ---
err = NvSciBufAttrListCreate(buf_module, &ss.icp_attr_list);
if (err != NvSciError_Success)
{
LOG_ERROR("NvSciBufAttrListCreate(ICP) failed for sensor %u: %d", sensor_id, static_cast<int>(err));
cleanup();
return 1;
}
set_buf_attr(ss.icp_attr_list, NvSciBufGeneralAttrKey_Types, NvSciBufType_Image);
set_buf_attr(ss.icp_attr_list, NvSciBufGeneralAttrKey_RequiredPerm, NvSciBufAccessPerm_ReadWrite);
status = sipl_camera->GetImageAttributes(sensor_id, nvsipl::INvSIPLClient::ConsumerDesc::OutputType::ICP,
ss.icp_attr_list);
if (status != nvsipl::NVSIPL_STATUS_OK)
{
LOG_ERROR("GetImageAttributes(ICP) failed for sensor %u: %d", sensor_id, static_cast<int>(status));
cleanup();
return 1;
}
NvSciBufAttrList icp_input[] = {ss.icp_attr_list};
err = NvSciBufAttrListReconcile(icp_input, 1, &ss.icp_reconciled, &ss.icp_conflict);
if (err != NvSciError_Success)
{
LOG_ERROR("NvSciBufAttrListReconcile(ICP) failed for sensor %u: %d", sensor_id, static_cast<int>(err));
cleanup();
return 1;
}
ss.icp_bufs.resize(NUM_ICP_BUFFERS, nullptr);
for (std::uint32_t i = 0; i < NUM_ICP_BUFFERS; i++)
{
err = NvSciBufObjAlloc(ss.icp_reconciled, &ss.icp_bufs[i]);
if (err != NvSciError_Success)
{
LOG_ERROR("NvSciBufObjAlloc(ICP) failed for sensor %u buf %u: %d", sensor_id, i, static_cast<int>(err));
cleanup();
return 1;
}
}
LOG_INFO("Sensor %u: allocated %u ICP buffers", sensor_id, NUM_ICP_BUFFERS);
// --- ISP0 buffers ---
err = NvSciBufAttrListCreate(buf_module, &ss.isp0_attr_list);
if (err != NvSciError_Success)
{
LOG_ERROR("NvSciBufAttrListCreate(ISP0) failed for sensor %u: %d", sensor_id, static_cast<int>(err));
cleanup();
return 1;
}
set_buf_attr(ss.isp0_attr_list, NvSciBufGeneralAttrKey_Types, NvSciBufType_Image);
set_buf_attr(ss.isp0_attr_list, NvSciBufGeneralAttrKey_RequiredPerm, NvSciBufAccessPerm_ReadWrite);
status = sipl_camera->GetImageAttributes(sensor_id, nvsipl::INvSIPLClient::ConsumerDesc::OutputType::ISP0,
ss.isp0_attr_list);
if (status != nvsipl::NVSIPL_STATUS_OK)
{
LOG_ERROR("GetImageAttributes(ISP0) failed for sensor %u: %d", sensor_id, static_cast<int>(status));
cleanup();
return 1;
}
NvSciBufAttrList isp0_input[] = {ss.isp0_attr_list};
err = NvSciBufAttrListReconcile(isp0_input, 1, &ss.isp0_reconciled, &ss.isp0_conflict);
if (err != NvSciError_Success)
{
LOG_ERROR("NvSciBufAttrListReconcile(ISP0) failed for sensor %u: %d", sensor_id, static_cast<int>(err));
cleanup();
return 1;
}
ss.isp0_bufs.resize(NUM_ISP0_BUFFERS, nullptr);
for (std::uint32_t i = 0; i < NUM_ISP0_BUFFERS; i++)
{
err = NvSciBufObjAlloc(ss.isp0_reconciled, &ss.isp0_bufs[i]);
if (err != NvSciError_Success)
{
LOG_ERROR("NvSciBufObjAlloc(ISP0) failed for sensor %u buf %u: %d", sensor_id, i, static_cast<int>(err));
cleanup();
return 1;
}
}
LOG_INFO("Sensor %u: allocated %u ISP0 buffers", sensor_id, NUM_ISP0_BUFFERS);
}
// 5b. Allocate EOF sync objects (ISP0 only)
for (auto& [sensor_id, ss] : sensors)
{
// Signaler attrs (SIPL fills these)
err = NvSciSyncAttrListCreate(sync_module, &ss.sync_signaler);
if (err != NvSciError_Success)
{
LOG_ERROR("NvSciSyncAttrListCreate(signaler) failed for sensor %u: %d", sensor_id, static_cast<int>(err));
cleanup();
return 1;
}
status = sipl_camera->FillNvSciSyncAttrList(
sensor_id, nvsipl::INvSIPLClient::ConsumerDesc::OutputType::ISP0, ss.sync_signaler, nvsipl::SIPL_SIGNALER);
if (status != nvsipl::NVSIPL_STATUS_OK)
{
LOG_ERROR("FillNvSciSyncAttrList(signaler) failed for sensor %u: %d", sensor_id, static_cast<int>(status));
cleanup();
return 1;
}
// Waiter attrs (our requirements)
err = NvSciSyncAttrListCreate(sync_module, &ss.sync_waiter);
if (err != NvSciError_Success)
{
LOG_ERROR("NvSciSyncAttrListCreate(waiter) failed for sensor %u: %d", sensor_id, static_cast<int>(err));
cleanup();
return 1;
}
set_sync_attr(ss.sync_waiter, NvSciSyncAttrKey_NeedCpuAccess, false);
set_sync_attr(ss.sync_waiter, NvSciSyncAttrKey_RequiredPerm, NvSciSyncAccessPerm_WaitOnly);
// Reconcile signaler + waiter
NvSciSyncAttrList sync_input[] = {ss.sync_signaler, ss.sync_waiter};
err = NvSciSyncAttrListReconcile(sync_input, 2, &ss.sync_reconciled, &ss.sync_conflict);
if (err != NvSciError_Success)
{
LOG_ERROR("NvSciSyncAttrListReconcile failed for sensor %u: %d", sensor_id, static_cast<int>(err));
cleanup();
return 1;
}
err = NvSciSyncObjAlloc(ss.sync_reconciled, &ss.sync_obj);
if (err != NvSciError_Success)
{
LOG_ERROR("NvSciSyncObjAlloc failed for sensor %u: %d", sensor_id, static_cast<int>(err));
cleanup();
return 1;
}
LOG_INFO("Sensor %u: allocated EOF sync object", sensor_id);
}
// 6. Init
LOG_INFO("Calling Init()...");
status = sipl_camera->Init();
if (status != nvsipl::NVSIPL_STATUS_OK)
{
LOG_ERROR("Init() failed: %d", static_cast<int>(status));
cleanup();
return 1;
}
LOG_INFO("Init() OK");
// 7. Register buffers
for (auto& [sensor_id, ss] : sensors)
{
// Register ICP
status = sipl_camera->RegisterImages(sensor_id, nvsipl::INvSIPLClient::ConsumerDesc::OutputType::ICP,
ss.icp_bufs);
if (status != nvsipl::NVSIPL_STATUS_OK)
{
LOG_ERROR("RegisterImages(ICP) failed for sensor %u: %d", sensor_id, static_cast<int>(status));
cleanup();
return 1;
}
// Register ISP0
status = sipl_camera->RegisterImages(sensor_id, nvsipl::INvSIPLClient::ConsumerDesc::OutputType::ISP0,
ss.isp0_bufs);
if (status != nvsipl::NVSIPL_STATUS_OK)
{
LOG_ERROR("RegisterImages(ISP0) failed for sensor %u: %d", sensor_id, static_cast<int>(status));
cleanup();
return 1;
}
LOG_INFO("Sensor %u: buffers registered", sensor_id);
}
// 8. Register EOF sync objects
for (auto& [sensor_id, ss] : sensors)
{
status = sipl_camera->RegisterNvSciSyncObj(sensor_id, nvsipl::INvSIPLClient::ConsumerDesc::OutputType::ISP0,
nvsipl::NVSIPL_EOFSYNCOBJ, ss.sync_obj);
if (status != nvsipl::NVSIPL_STATUS_OK)
{
LOG_ERROR("RegisterNvSciSyncObj failed for sensor %u: %d", sensor_id, static_cast<int>(status));
cleanup();
return 1;
}
LOG_INFO("Sensor %u: EOF sync registered", sensor_id);
}
// 9. Register auto control plugins (NITO files)
for (auto& [sensor_id, ss] : sensors)
{
std::string nito_path = std::string(NITO_FOLDER) + ss.module_name + ".nito";
auto nito_blob = read_file(nito_path);
if (!nito_blob)
{
LOG_ERROR("Failed to read NITO file: %s", nito_path.c_str());
cleanup();
return 1;
}
LOG_INFO("Sensor %u: loaded NITO %s (%zu bytes)", sensor_id, nito_path.c_str(), nito_blob->size());
status = sipl_camera->RegisterAutoControlPlugin(sensor_id, nvsipl::NV_PLUGIN, nullptr, *nito_blob);
if (status != nvsipl::NVSIPL_STATUS_OK)
{
LOG_ERROR("RegisterAutoControlPlugin failed for sensor %u: %d", sensor_id, static_cast<int>(status));
cleanup();
return 1;
}
}
// 10. Start
LOG_INFO("Calling Start()...");
status = sipl_camera->Start();
if (status != nvsipl::NVSIPL_STATUS_OK)
{
LOG_ERROR("Start() failed: %d", static_cast<int>(status));
cleanup();
return 1;
}
started = true;
LOG_INFO("Start() OK - waiting for first frame...");
// 11. Initial check: poll for first frame (5s timeout, 100ms intervals)
for (int i = 0; i < INIT_POLL_ITERATIONS && !success; i++)
{
for (auto& [sensor_id, ss] : sensors)
{
// Drain ICP queue to prevent buffer starvation
if (ss.icp_queue != nullptr)
{
nvsipl::INvSIPLClient::INvSIPLBuffer* icp_buf = nullptr;
while (ss.icp_queue->Get(icp_buf, 0U) == nvsipl::NVSIPL_STATUS_OK && icp_buf != nullptr)
{
icp_buf->Release();
}
}
if (ss.isp0_queue == nullptr)
{
continue;
}
nvsipl::INvSIPLClient::INvSIPLBuffer* p_buffer = nullptr;
auto q_status = ss.isp0_queue->Get(p_buffer, 0U); // non-blocking
if (q_status == nvsipl::NVSIPL_STATUS_OK && p_buffer != nullptr)
{
LOG_INFO("SUCCESS: first frame from sensor %u on iteration %d", sensor_id, i);
p_buffer->Release();
success = true;
break;
}
}
if (!success)
{
std::this_thread::sleep_for(std::chrono::milliseconds(INIT_POLL_INTERVAL_MS));
}
}
if (!success)
{
LOG_ERROR("FAILED: no frames received after %dms init period", INIT_POLL_ITERATIONS * INIT_POLL_INTERVAL_MS);
cleanup();
return 1;
}
// 12. Infinite round-robin frame drain loop
LOG_INFO("Draining frames (round-robin)...");
std::vector<std::uint32_t> active_sensors;
for (const auto& [sensor_id, ss] : sensors)
{
if (ss.isp0_queue != nullptr)
{
active_sensors.push_back(sensor_id);
}
}
std::size_t next_idx = 0;
std::uint64_t total_frames = 0;
auto last_log_time = std::chrono::steady_clock::now();
while (true)
{
const std::uint32_t sid = active_sensors[next_idx];
auto& ss = sensors[sid];
// Drain ICP queue to prevent buffer starvation
if (ss.icp_queue != nullptr)
{
nvsipl::INvSIPLClient::INvSIPLBuffer* icp_buf = nullptr;
while (ss.icp_queue->Get(icp_buf, 0U) == nvsipl::NVSIPL_STATUS_OK && icp_buf != nullptr)
{
icp_buf->Release();
}
}
nvsipl::INvSIPLClient::INvSIPLBuffer* p_buffer = nullptr;
auto q_status = ss.isp0_queue->Get(p_buffer, FRAME_TIMEOUT_US);
if (q_status == nvsipl::NVSIPL_STATUS_OK && p_buffer != nullptr)
{
total_frames++;
p_buffer->Release();
}
else if (q_status == nvsipl::NVSIPL_STATUS_EOF)
{
LOG_WARN("Sensor %u: got EOF, stopping", sid);
break;
}
next_idx = (next_idx + 1) % active_sensors.size();
// Periodic FPS logging
auto now = std::chrono::steady_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::seconds>(now - last_log_time).count();
if (elapsed >= FPS_LOG_INTERVAL_S)
{
double fps = static_cast<double>(total_frames) / static_cast<double>(elapsed);
LOG_INFO("Frames: %llu total, %.1f fps (over %llds)", static_cast<unsigned long long>(total_frames), fps,
static_cast<long long>(elapsed));
total_frames = 0;
last_log_time = now;
}
}
}
catch (const std::exception& e)
{
LOG_ERROR("Exception: %s", e.what());
}
// 12. Cleanup
cleanup();
LOG_INFO("=== SIPL Init Test %s ===", success ? "PASSED" : "FAILED");
return success ? 0 : 1;
}