| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #pragma once | ||
| 2 | /* | ||
| 3 | * This file is part of the Falcon Player (FPP) and is Copyright (C) | ||
| 4 | * 2013-2026 by the Falcon Player Developers. | ||
| 5 | * | ||
| 6 | * The Falcon Player (FPP) is free software, and is covered under | ||
| 7 | * multiple Open Source licenses. Please see the included 'LICENSES' | ||
| 8 | * file for descriptions of what files are covered by each license. | ||
| 9 | * | ||
| 10 | * This source file is covered under the LGPL v2.1 as described in the | ||
| 11 | * included LICENSE.LGPL file. | ||
| 12 | */ | ||
| 13 | |||
| 14 | #include <string> | ||
| 15 | #include <vector> | ||
| 16 | |||
| 17 | /** | ||
| 18 | * V4L2Device — direct V4L2 queries against a capture device, for the | ||
| 19 | * things GStreamer's v4l2src either can't express or gets wrong for us. | ||
| 20 | * | ||
| 21 | * Two jobs: | ||
| 22 | * | ||
| 23 | * 1. Anti-flicker / exposure controls. A camera lit by mains lighting | ||
| 24 | * bands or pulses unless its auto-exposure quantises exposure to whole | ||
| 25 | * half-cycles of the supply. That's V4L2_CID_POWER_LINE_FREQUENCY, | ||
| 26 | * and it's a device control, not something the pipeline can do | ||
| 27 | * downstream: no amount of videorate/videoscale fixes light that was | ||
| 28 | * already integrated wrong. | ||
| 29 | * | ||
| 30 | * Controls are addressed by numeric CID rather than by name through | ||
| 31 | * v4l2src's extra-controls property, because the names moved in | ||
| 32 | * Linux 6.x (exposure_auto -> auto_exposure, exposure_absolute -> | ||
| 33 | * exposure_time_absolute) and extra-controls only warns on a name it | ||
| 34 | * can't resolve. CIDs are stable ABI. | ||
| 35 | * | ||
| 36 | * 2. Capture mode enumeration, so the configured resolution/framerate can | ||
| 37 | * be pinned on the device instead of being quietly resampled after the | ||
| 38 | * fact. See SelectMode(). | ||
| 39 | * | ||
| 40 | * Every entry point is a no-op returning failure on non-Linux builds. | ||
| 41 | */ | ||
| 42 | namespace V4L2Device { | ||
| 43 | |||
| 44 | /// One discrete capture mode the device advertises. | ||
| 45 | struct Mode { | ||
| 46 | std::string fourcc; ///< e.g. "MJPG", "YUYV" | ||
| 47 | int width = 0; | ||
| 48 | int height = 0; | ||
| 49 | int fpsNum = 0; ///< framerate = fpsNum / fpsDen | ||
| 50 | int fpsDen = 1; | ||
| 51 | |||
| 52 | ✗ | double fps() const { return fpsDen ? (double)fpsNum / fpsDen : 0.0; } | |
| 53 | }; | ||
| 54 | |||
| 55 | /// Requested device controls. -1 / "camera" means "leave the camera's own | ||
| 56 | /// setting alone" so an untouched config behaves exactly as it did before. | ||
| 57 | struct ControlSettings { | ||
| 58 | /// V4L2_CID_POWER_LINE_FREQUENCY: -1 leave, 0 disabled, 1 = 50Hz, 2 = 60Hz. | ||
| 59 | int powerLineFrequency = -1; | ||
| 60 | |||
| 61 | /// "camera" (leave), "auto", or "manual". Maps onto whichever | ||
| 62 | /// V4L2_CID_EXPOSURE_AUTO menu entries the device actually offers. | ||
| 63 | std::string exposureMode = "camera"; | ||
| 64 | |||
| 65 | /// V4L2_CID_EXPOSURE_ABSOLUTE, in 100us units. Only applied when | ||
| 66 | /// exposureMode == "manual". -1 leaves the camera's value. | ||
| 67 | int exposureTime100us = -1; | ||
| 68 | |||
| 69 | /// V4L2_CID_EXPOSURE_AUTO_PRIORITY ("exposure_dynamic_framerate"): | ||
| 70 | /// -1 leave, 1 = let AE drop the framerate, 0 = hold the framerate. | ||
| 71 | /// Worth holding when chasing flicker — a variable frame interval | ||
| 72 | /// reintroduces beating against the light even once exposure is | ||
| 73 | /// quantised correctly. | ||
| 74 | int dynamicFramerate = -1; | ||
| 75 | |||
| 76 | 129 | bool AnyRequested() const { | |
| 77 |
3/6✓ Branch 0 taken 129 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 129 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 129 times.
|
129 | return powerLineFrequency >= 0 || exposureMode != "camera" || dynamicFramerate >= 0; |
| 78 | } | ||
| 79 | }; | ||
| 80 | |||
| 81 | /// Apply `s` to `device`. Controls the device doesn't implement are | ||
| 82 | /// skipped, not treated as failure. `summary` gets a human-readable | ||
| 83 | /// "name=value" list of what actually landed, for the log. | ||
| 84 | /// Returns false only if the device could not be opened at all. | ||
| 85 | bool ApplyControls(const std::string& device, const ControlSettings& s, std::string& summary); | ||
| 86 | |||
| 87 | /// All discrete capture modes the device advertises. Empty if the device | ||
| 88 | /// can't be opened or enumerates nothing usable. | ||
| 89 | std::vector<Mode> EnumerateModes(const std::string& device); | ||
| 90 | |||
| 91 | /// Pick the mode to ask the device for, given what the config wants. | ||
| 92 | /// | ||
| 93 | /// Prefers an exact width/height/framerate match; failing that the | ||
| 94 | /// smallest mode that still covers the requested size (so videoscale | ||
| 95 | /// downscales rather than upscales), and among equals the one whose | ||
| 96 | /// framerate best covers the request. Returns false when `modes` is | ||
| 97 | /// empty, in which case the caller should not constrain the device at all. | ||
| 98 | bool SelectMode(const std::vector<Mode>& modes, int width, int height, int fps, Mode& out); | ||
| 99 | |||
| 100 | /// GStreamer caps for a mode, e.g. "image/jpeg,width=1280,height=720,framerate=30/1". | ||
| 101 | /// Empty string if the fourcc has no caps mapping we trust, in which case | ||
| 102 | /// the caller should leave the device unconstrained. | ||
| 103 | std::string ModeToCaps(const Mode& m); | ||
| 104 | |||
| 105 | } // namespace V4L2Device | ||
| 106 |