You strobe a sensor on demand, the images look right, and the frame numbers coming back from V4L2 run ahead of the frames you actually received — by roughly one count for every start-of-frame timeout the driver logs. It shows up on embedded MIPI CSI-2 capture where the sensor runs in slave mode and the stream is deliberately discontinuous, and it is not a fault in your code. What follows is where the count is generated, why every timeout advances it, how large the drift grows, and three ways to rebuild frame numbers you can trust: start-of-frame timestamps, a trigger-side counter, or a camera that counts frames for itself.
What the sequence field counts
Every buffer you dequeue with VIDIOC_DQBUF carries a sequence value in its v4l2_buffer structure. The V4L2 buffer API defines this as a frame count that 'starts at zero and includes dropped or repeated frames'. Read the second half literally: the field counts buffer transactions on the host, and a transaction can fail. It was never specified as a count of sensor frame starts.
On the embedded capture drivers at issue, the increment sits in the video-input channel's buffer-release function (release_buffer). A completed capture and a timed-out wait both pass through that path, so both add one to the count. Specification and implementation agree with each other; the mistake is expecting sensor-side semantics from a host-side counter.
Where the count is generated in the capture pipeline
Walk one triggered frame through the path:
- Your controller strobes the sensor, and the sensor drives a MIPI CSI-2 frame into the host's video-input (VI) block.
- The driver has a buffer armed and is waiting on a hardware syncpoint for start of frame, under a timeout.
- On success the frame lands in the buffer; the wait call fills a timestamp output with the instant the syncpoint fired, and that instant becomes the buffer's
timestamp. - On timeout the wait returns an error, the buffer is released unfilled — and the release still increments the count.
- Userspace dequeues only good buffers, each tagged with the inflated count.
The trap is step 5: the failed buffer never reaches you. Your application sees healthy images whose indices have silently advanced, with no error flag anywhere. The timestamp stays honest because it comes from the syncpoint rather than the bookkeeping, which is why timestamps are the repair tool. Which clock those values use, and how to read them, is covered in V4L2 buffer timestamps: when a frame was really captured.
Two neighbouring cases sit close to this one. Timeouts that appear while the sensor is framed correctly are a host-side matter — why one-shot capture misses frames separates them. When the stream errors instead of stopping cleanly, MIPI CSI stream timeouts and short-frame errors covers the signalling side.
The drift is your timeout count
Three relations govern the behaviour, with all times in seconds:
Here t_silence is how long the sensor stays quiet, t_timeout the start-of-frame wait, T_trigger your strobe period, Δt_sof the timestamp gap between consecutive delivered buffers, and T the sensor frame period.
The middle line is the one teams miss. If the wait is shorter than the trigger period, it expires inside every inter-frame gap while capture is perfectly healthy, and the count races several steps ahead per delivered frame. Video-input blocks of this class are designed around continuous frame delivery; slave-mode capture with silent periods is exactly the corner the counter does not model.
Worked example: 100 frames at 1 Hz
Strobe the sensor once per part at a steady 1 Hz, so T_trigger is 1000 ms, with the start-of-frame wait set to 300 ms. In steady state the loop re-arms immediately after each release, and the next start of frame is 1000 ms away.
- Wait 1 runs 0–300 ms: no start of frame, timeout, release, count +1.
- Wait 2 runs 300–600 ms: timeout, count +1.
- Wait 3 runs 600–900 ms: timeout, count +1.
- Wait 4 starts at 900 ms; the start of frame at 1000 ms lands 100 ms into the window, so the frame is captured, count +1.
Three waits expire inside every 1000 ms period. Over 100 delivered frames that is 100 × 3 = 300 timeouts plus 100 captures, so 400 release events in total. The count starts at zero, so the last buffer carries sequence 399 where a frame counter would read 99. The drift is 399 − 99 = 300, one for every timeout line in the kernel log.
Raise the wait to 1500 ms, comfortably above the 1000 ms period. Every normal start of frame now lands inside the window, the count advances once per frame, and the last buffer reads 99. Timeouts then appear only when signalling genuinely stops: a 12 s feed stoppage produces 12 s ÷ 1.5 s = 8 timeouts, an 8-count drift you reconcile against the log in one line.
The third relation covers free-running sensors with host-side drops. At 60 fps, T is about 16.7 ms; a 50 ms timestamp gap between consecutive buffers means round(50 ÷ 16.7) − 1 = 2 frames were lost between them.
Rebuild frame numbers you can trust
Three repairs, strongest last:
| Number source | Filled in by | Advances on timeout | Use it for |
|---|---|---|---|
sequence field |
host driver, buffer release | Yes | delivery order only |
timestamp |
start-of-frame syncpoint | No | gap detection, pairing |
| kernel log timeout lines | the start-of-frame wait | is the drift itself | confirming the count |
| trigger-side counter | your PLC or pulse source | No | part-to-image pairing |
| camera frame counter, chunk data | camera firmware, per exposure | No | audit-grade identity |
Start-of-frame timestamps. Check that buffers carry monotonic timestamps, then log the gap Δt_sof between consecutive frames. For a free-running sensor, round(Δt_sof / T) − 1 gives the drop count directly. For a triggered one, the gap tells you how long the sensor stayed silent, which you check against the trigger log. If gaps grow with processing load instead, the drops are host-side and V4L2 DQBUF stalls after a few frames works out the queue depth you need. Polling only the newest frame adds its own confusion, because the same image arrives twice until you drain the buffer queue.
A trigger-side counter. Whatever strobes the sensor — PLC, motion controller, function generator — knows exactly how many frames it requested. Tag each pulse with an index where the pulse is generated, then pair index to image by timestamp. Host-side behaviour cannot corrupt a number that never passes through the host.
A camera that counts at the source. A GenICam-compatible machine vision camera can stamp each image payload with chunk data: a frame counter and a timestamp, filled in once per real exposure. Timeouts and dropped buffers on the PC cannot shift a counter the camera owns. When frame identity must be auditable end to end, this is the architecture to buy.
Cameras that count frames at the source
For a new build, move the counting into the camera and choose the interface by cable run and host. Two options for a strobed station:
- Sensor format
- 1.1"
- Lens mount
- C-mount
- Sensor format
- 1"
- Lens mount
- C-mount
The MV-CH120-11UC is a 12 MP USB3 area scan camera with a 1.1" sensor, a C-mount and a global shutter — global shutter is what you want when the part moves under the strobe. For longer cable runs, the MV-CS200-10GC is a 20 MP GigE camera with a 1" rolling-shutter sensor and a C-mount. Chunk data is a GenICam standard feature, so on a compliant camera the frame counter is a firmware setting rather than a redesign. Either way, the frame number you log comes from the camera, not from a host release path.
On the GigE side, give the cameras a board of their own from frame grabbers:
- Interface
- GigE
- Ports
- 2 ports
The MV-GE2002P is a dual-port PoE NIC, with camera power riding the data cable. If the host is embedded Linux and buffers still time out, socket buffer tuning for GigE cameras covers that side.
For a camera that moves with the part, take the cable from camera cables:
The LS-CBL-0219 is a 3 m USB3.0 camera cable in a high-flex drag-chain rating. Browse further by interface in area scan cameras or through the camera finder, and let the system configurator walk the station from the camera step.
Checks before you rely on any frame counter
- Run one production session and count the start-of-frame timeout lines in the kernel log. That number must equal your sequence drift; a mismatch points at host-side drops rather than silence.
- Set the start-of-frame wait above your longest trigger period: at a 1 Hz strobe, 1500 ms.
- Confirm that buffers carry monotonic timestamps, and log the gap per frame. A gap that is not a multiple of your trigger period means the sensor, not the host, went quiet.
- Tag every strobe pulse with an index at the point where it is generated, and pair index to image by timestamp.
- For audit-grade traceability, enable the camera's frame-counter chunk and store it beside every image.
When the sequence field is safe
In continuous streaming with no gaps, the count advances once per frame start and works as a plain index; it still starts at zero. Single-shot capture also behaves, because you arm exactly one buffer per expected frame and read the failure yourself.
Patching the driver is the tempting route: move the increment out of the buffer-release function onto the frame-start success path, and sequence follows real frames. That route fails in the field: every board-support-package update overwrites the change, and the field then contradicts its own definition, which includes dropped frames on purpose. Rebuild the numbers in userspace or in the camera, where they survive updates.
If raw capture fails on frame dimensions rather than timeouts, V4L2 raw capture with an undersized frame height is the matching guide.
Bring your trigger rate, your wait value and the drift you measured to the contact form, and we will size the counting scheme with you. More capture-pipeline write-ups sit under the systems topic.
Questions
Does the V4L2 sequence number count sensor frame starts?
No. The buffer API defines it as a buffer count that starts at zero and includes dropped or repeated frames, and the host driver writes it when a buffer is released. A start-of-frame timeout passes through the same release path, so it advances the count even though no frame existed. Only in a continuous, gap-free stream does the value coincide with frame starts.
How do I confirm the drift comes from timeouts?
Run one capture session, then count the start-of-frame timeout lines the kernel printed. That count should equal the difference between the last buffer's sequence value and your received-frame count — 300 timeout lines against a drift of 300 in the worked example. A mismatch means your losses are host-side drops, not silent periods, and the repair moves to queue depth and dequeue rate.
What start-of-frame wait value should I set for a triggered sensor?
Set it above your longest trigger period, with margin: a 1 Hz strobe needs at least 1500 ms, and 1.5 times the period is a sound starting point. Below the period, the wait expires in every inter-frame gap and the count advances several times per delivered frame — at a 1000 ms period with a 300 ms wait, three timeouts per frame. The only cost of waiting longer is slower detection of a genuinely dead sensor.
Can I patch the driver so the count only follows real frames?
You can: the increment lives in the video-input channel's buffer-release function, and moving it onto the frame-start success path makes the count follow real frames. We advise against it. Each board-support-package update overwrites the patch, and the field then contradicts the buffer API's definition, which includes dropped frames on purpose. Rebuild the numbers from timestamps or your trigger counter instead.
Do GigE and USB3 machine vision cameras have this problem?
No. GenICam-compatible machine vision cameras can stamp each image with chunk data — a frame counter and timestamp filled in per real exposure — so host-side timeouts and dropped buffers never shift it. The host driver becomes a transport rather than the authority on frame count. If frame identity must survive anything the PC does, choose the camera that counts at the source.



