11#include <promeki/config.h>
12#if PROMEKI_ENABLE_V4L2
32typedef struct _snd_pcm snd_pcm_t;
34PROMEKI_NAMESPACE_BEGIN
39struct V4l2RequeueGate;
151class V4l2MediaIO :
public DedicatedThreadMediaIO {
152 PROMEKI_OBJECT(V4l2MediaIO, DedicatedThreadMediaIO)
155 static inline const MediaIOStats::ID StatsCaptured{
"V4l2Captured"};
157 static inline const MediaIOStats::ID StatsAlsaOverruns{
"V4l2AlsaOverruns"};
160 V4l2MediaIO(ObjectBase *parent =
nullptr);
163 ~V4l2MediaIO()
override;
176 int instanceID()
const {
return _instanceId; }
179 static PixelFormat::ID v4l2ToPixelFormat(uint32_t v4l2fmt);
182 static uint32_t pixelFormatToV4l2(PixelFormat::ID pd);
202 static bool captureFormatAllowsZeroCopy(
const PixelFormat &pd) {
return !pd.isCompressed(); }
205 Error executeCmd(MediaIOCommandOpen &cmd)
override;
206 Error executeCmd(MediaIOCommandClose &cmd)
override;
207 Error executeCmd(MediaIOCommandRead &cmd)
override;
208 Error executeCmd(MediaIOCommandStats &cmd)
override;
214 void cancelBlockingWork()
override;
231 Error openVideo(
const MediaIO::Config &cfg);
232 Error startStreaming();
233 void stopStreaming();
237 Error openAudio(
const MediaIO::Config &cfg);
242 void videoCaptureLoop();
243 void audioCaptureLoop();
253 void *start =
nullptr;
258 List<MmapBuffer> _buffers;
259 bool _streaming =
false;
260 ImageDesc _imageDesc;
270 bool _zeroCopy =
false;
271 size_t _bufLength = 0;
272 SharedPtr<V4l2RequeueGate> _requeueGate;
275 snd_pcm_t *_pcm =
nullptr;
276 AudioDesc _audioDesc;
277 bool _audioEnabled =
false;
280 Atomic<bool> _stopFlag{
false};
281 Atomic<int> _deviceError{0};
290 Atomic<bool> _readCancelled{
false};
291 BasicThread _videoThread;
292 BasicThread _audioThread;
296 static constexpr int VideoQueueDepth = 2;
297 Queue<VideoPayload::Ptr> _videoQueue;
306 AudioBuffer _audioRing;
309 Atomic<int64_t> _framesCaptured{0};
310 Atomic<int64_t> _alsaOverruns{0};
313 PeriodicCallback _debugReport;
314 int64_t _ringAccum = 0;
315 int64_t _ringAccumFrames = 0;
316 double _ringAvgBaseline = 0.0;
317 TimeStamp _ringBaselineTime;
318 bool _ringBaselineSet =
false;
321 TimeStamp _lastCaptureTime;
322 TimeStamp _firstCaptureTime;
323 int64_t _firstCaptureFrame = -1;
324 double _frameDeltaSum = 0.0;
325 double _frameDeltaSqSum = 0.0;
326 int64_t _frameDeltaCount = 0;
327 double _prevPeriodFps = 0.0;
330 bool _ringOverflowWarned =
false;
349 Atomic<uint32_t> _lastVbufSequence{0};
350 Atomic<int64_t> _kernelDroppedPeriod{0};
351 Atomic<int64_t> _loopIterationsPeriod{0};
352 Atomic<int64_t> _loopTimeSumUsPeriod{0};
353 Atomic<int64_t> _loopTimeMaxUsPeriod{0};
354 Atomic<int64_t> _dqbufLagSumUsPeriod{0};
355 Atomic<int64_t> _dqbufLagMaxUsPeriod{0};
356 Atomic<int64_t> _dqbufLagCountPeriod{0};
363 Atomic<int64_t> _timestampSubstitutedPeriod{0};
365 bool _seqInitialized =
false;
366 int64_t _prevIterUs = 0;
369 FrameRate _frameRate;
370 FrameCount _frameCount{0};
377class V4l2Factory :
public MediaIOFactory {
379 V4l2Factory() =
default;
381 String name()
const override {
return String(
"V4L2"); }
382 String displayName()
const override {
return String(
"V4L2 Capture"); }
383 String description()
const override {
384 return String(
"V4L2 video capture with optional ALSA audio (Linux)");
387 bool canBeSource()
const override {
return true; }
389 bool canHandlePath(
const String &path)
const override;
390 StringList enumerate()
const override;
391 Config::SpecMap configSpecs()
const override;
392 List<MediaDesc> queryDevice(
const Config &config)
const override;
393 void printDeviceInfo(
const Config &config)
const override;
395 MediaIO *create(
const Config &config, ObjectBase *parent =
nullptr)
const override;