#include #include #include #include #include #include #include #include namespace { constexpr const char *optPlanarFlag = "--opt-planar"; constexpr const char *fullPlanarOptionalFlag = "--full-planar-is-optional"; constexpr const char *reconfigureTwiceFlag = "--reconfigure-twice"; constexpr const char *colourSpacePrefix = "--colour-space="; constexpr const char *colorSpacePrefix = "--color-space="; struct ConfigureProbeArgs { std::string deviceSelector; unsigned width = 0; unsigned height = 0; lcamera_dev::LcameraDevColourSpace colourSpace = lcamera_dev::LcameraDevColourSpace::Yuv; bool fullPlanarIsOptional = false; bool reconfigureTwice = false; }; void printUsage(std::ostream& stream) { stream << "Usage: lcameraDev_configure_probe " "[options]\n" "Options:\n" " --colour-space=yuv Semantic colour-space (only yuv today)\n" " --opt-planar Set fullPlanarIsOptional=true on request\n" " --full-planar-is-optional Same as --opt-planar\n" " --reconfigure-twice Configure twice with identical request " "(no-op check)\n" "Examples:\n" " lcameraDev_configure_probe model-substr:Integrated 640 480\n" " lcameraDev_configure_probe model-substr:HDMI 1280 720 --opt-planar\n" " lcameraDev_configure_probe index:0 640 480 --reconfigure-twice\n"; } std::string colourSpaceToString(lcamera_dev::LcameraDevColourSpace colourSpace) { switch (colourSpace) { case lcamera_dev::LcameraDevColourSpace::Yuv: return "yuv"; default: return "unknown"; } } bool parseColourSpaceValue(const std::string& value) { if (value == "yuv" || value == "YUV") { return true; } throw std::runtime_error( "unsupported colour-space \"" + value + "\" (only yuv is supported)"); } unsigned parseDimensionArg(const char *arg, const char *label) { try { const unsigned long parsed = std::stoul(arg); if (parsed == 0) { throw std::runtime_error( std::string(label) + " must be non-zero"); } return static_cast(parsed); } catch (const std::exception&) { throw std::runtime_error( std::string("invalid ") + label + " \"" + arg + "\""); } } ConfigureProbeArgs parseConfigureProbeArgs(int argc, char *argv[]) { if (argc < 4) { throw std::runtime_error("missing required arguments"); } ConfigureProbeArgs args; args.deviceSelector = argv[1]; args.width = parseDimensionArg(argv[2], "width"); args.height = parseDimensionArg(argv[3], "height"); for (int i = 4; i < argc; ++i) { const std::string token = argv[i]; if (token == optPlanarFlag || token == fullPlanarOptionalFlag) { args.fullPlanarIsOptional = true; continue; } if (token == reconfigureTwiceFlag) { args.reconfigureTwice = true; continue; } const std::string colourSpacePrefixStr = colourSpacePrefix; const std::string colorSpacePrefixStr = colorSpacePrefix; if (token.rfind(colourSpacePrefixStr, 0) == 0) { parseColourSpaceValue(token.substr(colourSpacePrefixStr.size())); continue; } if (token.rfind(colorSpacePrefixStr, 0) == 0) { parseColourSpaceValue(token.substr(colorSpacePrefixStr.size())); continue; } throw std::runtime_error("unknown option \"" + token + "\""); } return args; } void printRequest(const lcamera_dev::LcameraDevCameraModeRequest& request) { std::cout << "request:" << " width=" << request.width << " height=" << request.height << " colour-space=" << colourSpaceToString(request.colourSpace) << " fullPlanarIsOptional=" << (request.fullPlanarIsOptional ? "true" : "false") << '\n'; } void printConfiguredMode( const lcamera_dev::LcameraDevConfiguredCameraMode& configuredMode) { std::cout << "configured:" << " width=" << configuredMode.width << " height=" << configuredMode.height << " colour-space=" << colourSpaceToString(configuredMode.colourSpace) << " pixelFormat=" << configuredMode.pixelFormatName << " isFullyPlanar=" << (configuredMode.isFullyPlanar ? "true" : "false") << " planeCount=" << configuredMode.planeCount << '\n'; } void runNurseryRethrowing( const std::shared_ptr& componentThread, const std::function& invokerFactory) { std::exception_ptr slotException; sscl::co::NonViralTaskNursery nursery; nursery.openAdmission(); nursery.launch( invokerFactory, [&slotException](std::exception_ptr& exceptionPtr) { slotException = exceptionPtr; }); nursery.closeAdmission(); nursery.syncAwaitAllSettlements(componentThread->getIoContext()); if (slotException) { std::rethrow_exception(slotException); } } sscl::co::NonViralNonPostingInvoker getOrCreateCInd( std::exception_ptr& exceptionStorage, std::function callerLambda, const std::string& deviceSelector, lcamera_dev::LcameraDevGetOrCreateResult& createResult) { (void)callerLambda; createResult = co_await lcameraDev_getOrCreateDeviceCReq(deviceSelector); if (exceptionStorage) { std::rethrow_exception(exceptionStorage); } co_return; } sscl::co::NonViralNonPostingInvoker configureProbeCInd( std::exception_ptr& exceptionStorage, std::function callerLambda, const std::shared_ptr& deviceSession, const lcamera_dev::LcameraDevCameraModeRequest& request, lcamera_dev::LcameraDevConfiguredCameraMode& configuredMode) { (void)callerLambda; configuredMode = co_await lcameraDev_configureSessionModeCReq(deviceSession, request); if (exceptionStorage) { std::rethrow_exception(exceptionStorage); } co_return; } sscl::co::NonViralNonPostingInvoker releaseCInd( std::exception_ptr& exceptionStorage, std::function callerLambda, const std::shared_ptr& deviceSession) { (void)exceptionStorage; (void)callerLambda; co_await lcameraDev_releaseDeviceCReq(deviceSession); co_return; } void releaseSessionAndExit( const std::shared_ptr& componentThread, std::shared_ptr& deviceSession) { if (!deviceSession) { lcameraDev_exit(); return; } runNurseryRethrowing( componentThread, [&deviceSession](sscl::co::NonViralTaskNursery::Slot::Lease& lease) { return releaseCInd( lease.getExceptionStorage(), lease.getCallerLambda(), deviceSession); }); std::cout << "lcameraDev_configure_probe: released session\n"; /** Drop the session before stopping CameraManager so libcamera does not * tear down media devices while a Camera handle is still alive. */ deviceSession.reset(); lcameraDev_exit(); } int runConfigureProbe( const std::shared_ptr& componentThread, const ConfigureProbeArgs& args) { lcameraDev_main(componentThread); lcamera_dev::LcameraDevGetOrCreateResult createResult; runNurseryRethrowing( componentThread, [&args, &createResult](sscl::co::NonViralTaskNursery::Slot::Lease& lease) { return getOrCreateCInd( lease.getExceptionStorage(), lease.getCallerLambda(), args.deviceSelector, createResult); }); std::cout << "lcameraDev_configure_probe: opened session for camera id=" << createResult.resolvedIdentity.id << '\n'; lcamera_dev::LcameraDevCameraModeRequest request; request.width = args.width; request.height = args.height; request.colourSpace = args.colourSpace; request.fullPlanarIsOptional = args.fullPlanarIsOptional; printRequest(request); lcamera_dev::LcameraDevConfiguredCameraMode firstMode; try { runNurseryRethrowing( componentThread, [&createResult, &request, &firstMode]( sscl::co::NonViralTaskNursery::Slot::Lease& lease) { return configureProbeCInd( lease.getExceptionStorage(), lease.getCallerLambda(), createResult.deviceSession, request, firstMode); }); printConfiguredMode(firstMode); const int configureCallCountAfterFirst = createResult.deviceSession->getLibcameraConfigureCallCount(); std::cout << "libcameraConfigureCallCount=" << configureCallCountAfterFirst << '\n'; if (args.reconfigureTwice) { lcamera_dev::LcameraDevConfiguredCameraMode secondMode; runNurseryRethrowing( componentThread, [&createResult, &request, &secondMode]( sscl::co::NonViralTaskNursery::Slot::Lease& lease) { return configureProbeCInd( lease.getExceptionStorage(), lease.getCallerLambda(), createResult.deviceSession, request, secondMode); }); printConfiguredMode(secondMode); std::cout << "libcameraConfigureCallCount=" << createResult.deviceSession->getLibcameraConfigureCallCount() << " (after identical reconfigure)\n"; } } catch (const std::exception& configureException) { std::cerr << "lcameraDev_configure_probe: configure failed: " << configureException.what() << '\n'; releaseSessionAndExit( componentThread, createResult.deviceSession); return 1; } releaseSessionAndExit( componentThread, createResult.deviceSession); return 0; } } // namespace int main(int argc, char *argv[]) { try { const ConfigureProbeArgs args = parseConfigureProbeArgs(argc, argv); int exitCode = 0; lcamera_dev_probe::runOnComponentThread( [&args, &exitCode]( const std::shared_ptr& componentThread) { exitCode = runConfigureProbe(componentThread, args); }); return exitCode; } catch (const std::runtime_error& exception) { std::cerr << "lcameraDev_configure_probe: " << exception.what() << '\n'; printUsage(std::cerr); return 1; } catch (const std::exception& exception) { std::cerr << "lcameraDev_configure_probe: " << exception.what() << '\n'; return 1; } }