#include #include #include #include #include #define CL_TARGET_OPENCL_VERSION 120 #include #include namespace smo { namespace stim_buff { namespace lcamera_buff { YuvStimProducer::YuvStimProducer( const std::shared_ptr& deviceAttachmentSpec, boost::asio::io_context& ioContext, const std::shared_ptr& _deviceSession, const lcamera_dev::CameraIdentityRecord& resolvedIdentity, const LcameraBuffParsedParams& _parsedParams, const lcamera_dev::LcameraDevConfiguredCameraMode& _configuredMode) : StimulusProducer(deviceAttachmentSpec, ioContext), deviceSession(_deviceSession), resolvedCameraId(resolvedIdentity.id), parsedParams(_parsedParams), configuredMode(_configuredMode), layoutPath(classifyYuvCaptureLayoutPath(_configuredMode)), chromaSubsampling(classifyYuvChromaSubsampling(_configuredMode)), channelBackingPlan(getChannelBackingPlanForLayoutPath(layoutPath)) {} bool YuvStimProducer::supportsQualeIfaceApi(const std::string& qualeIfaceApi) { return qualeIfaceApi == "colour-yuv-y" || qualeIfaceApi == "colour-yuv-u" || qualeIfaceApi == "colour-yuv-v"; } bool YuvStimProducer::exportsQualeIfaceApi( const std::string& qualeIfaceApi) const { return supportsQualeIfaceApi(qualeIfaceApi); } std::shared_ptr YuvStimProducer::getOrCreateAttachedStimulusBuffer( const std::shared_ptr& attachSpec) { if (!lcameraBuffSmoHooksPtr) { throw std::runtime_error( "lcameraBuff: SMO hooks not initialized"); } if (!supportsQualeIfaceApi(attachSpec->qualeIfaceApi)) { throw std::runtime_error( "lcameraBuff: unsupported qualeIfaceApi '" + attachSpec->qualeIfaceApi + "'"); } const LcameraBuffParsedParams attachParsedParams = parseLcameraBuffParams(*attachSpec); if (!lcameraBuffParamsEqual(attachParsedParams, parsedParams)) { throw std::runtime_error( "lcameraBuff: conflicting stimbuff-api params across Y/U/V " "attachments for camera '" + resolvedCameraId + "'"); } auto existingBuffer = getAttachedStimulusBuffer(attachSpec); if (existingBuffer) { return existingBuffer; } /* One YuvStimProducer == one libcamera session. A second DAP line may use * a different deviceSelector yet resolve to this same session; it must not * attach the same qualeIface API twice. */ if (hasBufferWithQualeIfaceApi(attachSpec->qualeIfaceApi)) { throw std::runtime_error( "lcameraBuff: qualeIface '" + attachSpec->qualeIfaceApi + "' is already attached for camera session '" + resolvedCameraId + "'; each producer allows one buffer per " "qualeIface API"); } const YuvChannelKind channelKind = YuvChannelStimulusBuffer::yuvChannelKindFromQualeIfaceApi( attachSpec->qualeIfaceApi); const size_t channelByteSize = computeDeinterleavedChannelByteSize( channelKind, configuredMode.width, configuredMode.height, chromaSubsampling); auto channelBuffer = std::make_shared( *this, attachSpec, CONFIG_STIMBUFF_FRAME_PERIOD_MS, channelKind, channelByteSize, *lcameraBuffSmoHooksPtr, CL_MEM_READ_WRITE); if (!addAttachedStimulusBufferIfNotExists(channelBuffer)) { if (hasBufferWithQualeIfaceApi(attachSpec->qualeIfaceApi)) { throw std::runtime_error( "lcameraBuff: qualeIface '" + attachSpec->qualeIfaceApi + "' is already attached for camera session '" + resolvedCameraId + "'"); } throw std::runtime_error( "lcameraBuff: duplicate stimbuff attachment for " + attachSpec->stringify()); } return channelBuffer; } void YuvStimProducer::destroyAttachedStimulusBuffer( const std::shared_ptr& buffer) { StimulusProducer::destroyAttachedStimulusBuffer(buffer); } void YuvStimProducer::start() { // Stage 2: setup-only attach; capture daemon deferred to Stage 8. } void YuvStimProducer::stop() { // Stage 2: setup-only attach; capture daemon deferred to Stage 8. } sscl::co::ViralNonPostingInvoker YuvStimProducer::stimFrameProductionTimesliceCInd( sscl::SyncCancelerForAsyncWork& canceler) { (void)canceler; throw std::logic_error( "lcameraBuff: capture daemon not implemented in Stage 2"); co_return; } } // namespace lcamera_buff } // namespace stim_buff } // namespace smo