#include "fgc/VimbaCameraSource.h" #include "fgc/Logger.h" #include #include #include #include #include using namespace VmbCPP; namespace fgc { namespace { long long nowMs() { return std::chrono::duration_cast( std::chrono::system_clock::now().time_since_epoch()) .count(); } // Frame observer: dumps the first few frames after each trigger (sensor // settling), then delivers completed frames to the source callback. class FrameObserver : public IFrameObserver { public: FrameObserver(CameraPtr camera, int cam_id, ICameraSource::FrameCallback cb) : IFrameObserver(camera), cam_id_(cam_id), cb_(std::move(cb)) {} void resetSettle() { settle_.store(3); } void FrameReceived(const FramePtr pFrame) override { if (settle_.fetch_sub(1) > 0) { // still settling -> discard m_pCamera->QueueFrame(pFrame); return; } VmbFrameStatusType status; if (pFrame->GetReceiveStatus(status) == VmbErrorSuccess && status == VmbFrameStatusComplete) { VmbUint32_t w = 0, h = 0, sz = 0; const VmbUchar_t* buf = nullptr; if (pFrame->GetWidth(w) == VmbErrorSuccess && pFrame->GetHeight(h) == VmbErrorSuccess && pFrame->GetBufferSize(sz) == VmbErrorSuccess && pFrame->GetBuffer(buf) == VmbErrorSuccess && buf && w && h) { Frame f; f.width = w; f.height = h; f.channels = static_cast(sz / (static_cast(w) * h)); f.cam_id = cam_id_; f.timestamp_ms = nowMs(); f.data.assign(buf, buf + sz); LOG_TRACE_CAT(LogCat::Camera) << "RX frame cam" << cam_id_ << ' ' << w << 'x' << h << ' ' << sz << 'B'; if (cb_) cb_(f); } } m_pCamera->QueueFrame(pFrame); } private: int cam_id_; ICameraSource::FrameCallback cb_; std::atomic settle_{3}; }; } // namespace struct VimbaCameraSource::Impl { explicit Impl(std::vector ids) : camera_ids(std::move(ids)), sys(VmbSystem::GetInstance()) {} std::vector camera_ids; VmbSystem& sys; std::vector cameras; std::vector observers; ICameraSource::FrameCallback callback; bool started = false; }; VimbaCameraSource::VimbaCameraSource(std::vector camera_ids) : impl_(std::make_unique(std::move(camera_ids))) {} VimbaCameraSource::~VimbaCameraSource() { try { stop(); close(); } catch (const std::exception& e) { LOG_WARN << "VimbaCameraSource shutdown: " << e.what(); } } void VimbaCameraSource::open() { if (impl_->sys.Startup() != VmbErrorSuccess) throw std::runtime_error("Could not start Vimba X API"); for (const auto& id : impl_->camera_ids) { CameraPtr cam; if (impl_->sys.GetCameraByID(id.c_str(), cam) != VmbErrorSuccess) throw std::runtime_error("Camera not found: " + id); if (cam->Open(VmbAccessModeFull) != VmbErrorSuccess) throw std::runtime_error("Could not open camera: " + id); impl_->cameras.push_back(cam); LOG_INFO << "Opened camera " << id; } } void VimbaCameraSource::close() { for (auto& cam : impl_->cameras) cam->Close(); impl_->cameras.clear(); impl_->sys.Shutdown(); } void VimbaCameraSource::start() { int cam_id = 0; for (auto& cam : impl_->cameras) { IFrameObserverPtr observer(new FrameObserver(cam, cam_id, impl_->callback)); if (cam->StartContinuousImageAcquisition(5, observer) != VmbErrorSuccess) throw std::runtime_error("Could not start acquisition"); impl_->observers.push_back(observer); ++cam_id; } impl_->started = true; } void VimbaCameraSource::stop() { for (auto& cam : impl_->cameras) cam->StopContinuousImageAcquisition(); impl_->observers.clear(); impl_->started = false; } bool VimbaCameraSource::trigger() { if (impl_->cameras.empty()) return false; LOG_TRACE_CAT(LogCat::Camera) << "TX trigger cam0"; for (auto& obs : impl_->observers) SP_DYN_CAST(obs)->resetSettle(); VmbErrorType result = VmbErrorSuccess; for (int i = 0; i < 4; ++i) { // 3 settle frames + 1 real FeaturePtr feature; if (SP_ACCESS(impl_->cameras[0])->GetFeatureByName("TriggerSoftware", feature) == VmbErrorSuccess) result = feature->RunCommand(); std::this_thread::sleep_for(std::chrono::milliseconds(400)); } return result == VmbErrorSuccess; } bool VimbaCameraSource::setFrameRate(double fps) { VmbErrorType result = VmbErrorSuccess; for (auto& cam : impl_->cameras) { FeaturePtr feature; if (SP_ACCESS(cam)->GetFeatureByName("AcquisitionFrameRate", feature) == VmbErrorSuccess) result = feature->SetValue(fps); } if (result == VmbErrorSuccess) LOG_INFO << "camera fps set to " << fps; return result == VmbErrorSuccess; } void VimbaCameraSource::setFrameCallback(FrameCallback cb) { impl_->callback = std::move(cb); } int VimbaCameraSource::cameraCount() const { return static_cast(impl_->cameras.size()); } } // namespace fgc