LCamDev: implement configureSessionModeCReq
We can, theoretically, now change the v4l camera's mode.
This commit is contained in:
@@ -0,0 +1,376 @@
|
||||
#include <boostAsioLinkageFix.h>
|
||||
|
||||
#include <cameraSession.h>
|
||||
#include <lcameraDev.h>
|
||||
#include <probeRunner.h>
|
||||
#include <iostream>
|
||||
#include <spinscale/co/nonViralTaskNursery.h>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
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 <deviceSelector> <width> <height> "
|
||||
"[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<unsigned>(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<sscl::ComponentThread>& componentThread,
|
||||
const std::function<sscl::co::NonViralNonPostingInvoker(
|
||||
sscl::co::NonViralTaskNursery::Slot::Lease&)>& 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<void()> 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<void()> callerLambda,
|
||||
const std::shared_ptr<lcamera_dev::CameraSession>& 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<void()> callerLambda,
|
||||
const std::shared_ptr<lcamera_dev::CameraSession>& deviceSession)
|
||||
{
|
||||
(void)exceptionStorage;
|
||||
(void)callerLambda;
|
||||
|
||||
co_await lcameraDev_releaseDeviceCReq(deviceSession);
|
||||
co_return;
|
||||
}
|
||||
|
||||
void releaseSessionAndExit(
|
||||
const std::shared_ptr<sscl::ComponentThread>& componentThread,
|
||||
std::shared_ptr<lcamera_dev::CameraSession>& 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<sscl::ComponentThread>& 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<sscl::ComponentThread>& 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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user