Summary
arv_gv_device_auto_packet_size() negotiates packet sizes that exceed the network path MTU, resulting in IP fragmentation and 3–7x performance degradation for GigE Vision streaming. Reproduced on two different camera vendors (OMRON Sentech and Basler) — this is a fundamental aravis issue, not vendor-specific.
Environment
- aravis: 0.8.x (PyGObject bindings)
- Cameras tested:
- OMRON SENTECH STC-MCS500POE (2448×2048, BayerRG8, GigE Vision, FW 20.50)
- Basler acA1600-60gm (1600×1200, Mono8, GigE Vision, FW 106203-16 / V3.15-11)
- NIC: Intel I219-V (PCI 00:1f.6), driver
e1000e, MTU set to 9000 (jumbo frames)
- OS: Debian GNU/Linux 13 (trixie), kernel 6.12.74
- Network topology: Camera → PoE injector → managed switch → server. The switch has one uplink to the main network for remote access but carries negligible traffic — effectively a near-direct link. No router between camera and server.
Reproduction
import gi
gi.require_version('Aravis', '0.8')
from gi.repository import Aravis
# --- Sentech ---
cam = Aravis.Camera.new('192.168.1.199')
auto_size = cam.gv_auto_packet_size()
print(f"Sentech auto: {auto_size}") # Output: 12316 (MTU 9000!)
del cam
# --- Basler ---
cam = Aravis.Camera.new('192.168.1.145')
auto_size = cam.gv_auto_packet_size()
print(f"Basler auto: {auto_size}") # Output: 12316 (same!)
del cam
Both cameras return exactly 12316 — well above the NIC MTU of 9000.
Evidence
Sentech STC-MCS500POE (2448×2048 BayerRG8, 5.01 MB/frame)
| Packet Size |
Avg Grab Time |
Notes |
| 1504 |
111.8 ms |
Default (MTU 1500) |
| 8228 |
93.2 ms |
Within MTU 9000, no fragmentation |
| 12316 |
263.7 ms |
Auto-negotiated, above MTU → 2.8x slower |
Basler acA1600-60gm (1600×1200 Mono8, 1.92 MB/frame)
| Packet Size |
Avg Grab Time |
Notes |
| 1500 |
23.6 ms |
Default (MTU 1500) |
| 8228 |
22.7 ms |
Within MTU 9000, optimal — 109 MB/s throughput |
| 12316 |
158.2 ms |
Auto-negotiated, above MTU → 7x slower |
| 16404 |
152.8 ms |
Camera max (16404), above MTU → same degradation |
The Basler results are particularly clear: 8228 and 1500 perform nearly identically (the image is small enough that packet overhead is minimal), but exceeding MTU causes catastrophic 7x slowdown.
Root Cause Analysis
The auto_packet_size() implementation in arvgvdevice.c does set ArvGevSCPSDoNotFragment = TRUE before the binary search. However, on this network topology (switch, no router), the DF bit doesn't help:
- DF only triggers ICMP "Fragmentation Needed" from routers. A Layer 2 switch forwards Ethernet frames — it doesn't inspect IP headers or enforce DF. There is no router in the camera-to-server path.
- The OS network stack reassembles fragments transparently — the test packet arrives successfully even though it was fragmented at the IP layer. The binary search sees "success" and keeps increasing.
- Both cameras report
GevSCPSDoNotFragment support (Basler defaults it to True, Sentech doesn't expose it), yet both produce the same 12316 result. The DF flag is set on the wire but has no enforcer.
The test_packet_check() function only verifies "did a packet of the right size arrive?" — it cannot detect whether the packet was fragmented and reassembled en route.
Why exactly 12316?
Both cameras — different vendors, different firmware, different packet size ranges (Sentech: 72–16260, Basler: 220–16404) — converge on exactly 12316. This is the binary search ceiling where IP reassembly still reliably succeeds within aravis's test timeout. Above ~12–13 KB, reassembly latency or fragment loss causes the test to fail, stopping the search. The value is determined by OS reassembly behavior, not by any meaningful network parameter.
Impact
- 3–7x streaming performance degradation depending on image size
- Affects any setup where the network path lacks a router to enforce DF (common in industrial camera setups which use switches or direct connections)
- Cameras with
GevSCPSPacketSize max above NIC MTU are always affected
- Users relying on default auto-negotiation get silently degraded performance
- The issue is invisible — no errors, no warnings, just slow transfers
Workaround
Set GvPacketSizeAdjustment.NEVER before creating the stream, and manually set packet size to a value within the NIC MTU:
cam.gv_set_packet_size_adjustment(Aravis.GvPacketSizeAdjustment.NEVER)
cam.gv_set_packet_size(8228) # Must be <= NIC MTU - 28 (IP+UDP+GVSP headers)
stream = cam.create_stream(None, None)
Suggested Fix
The auto-negotiation should additionally verify that the negotiated packet size does not exceed the interface MTU of the host NIC. This can be queried from the OS via ioctl(SIOCGIFMTU) on Linux (the socket/interface is already available in aravis's GigE device code). The negotiated size should be capped at min(camera_max, nic_mtu - 28).
Alternatively, test_packet_check() could measure round-trip timing — fragmented packets take measurably longer than unfragmented ones, and a timing threshold could detect this.
Additional aravis API issue
cam.set_trigger("Off") fails on both Sentech and Basler cameras:
- Sentech: tries to set
TriggerSource to "Off", which isn't a valid enum
- Basler: same error —
arv-gc-error-quark: [TriggerSource] 'Off' not an entry
The workaround is to write TriggerMode=Off directly via GenICam:
dev = cam.get_device()
dev.set_string_feature_value("TriggerMode", "Off")
Summary
arv_gv_device_auto_packet_size()negotiates packet sizes that exceed the network path MTU, resulting in IP fragmentation and 3–7x performance degradation for GigE Vision streaming. Reproduced on two different camera vendors (OMRON Sentech and Basler) — this is a fundamental aravis issue, not vendor-specific.Environment
e1000e, MTU set to 9000 (jumbo frames)Reproduction
Both cameras return exactly 12316 — well above the NIC MTU of 9000.
Evidence
Sentech STC-MCS500POE (2448×2048 BayerRG8, 5.01 MB/frame)
Basler acA1600-60gm (1600×1200 Mono8, 1.92 MB/frame)
The Basler results are particularly clear: 8228 and 1500 perform nearly identically (the image is small enough that packet overhead is minimal), but exceeding MTU causes catastrophic 7x slowdown.
Root Cause Analysis
The
auto_packet_size()implementation inarvgvdevice.cdoes setArvGevSCPSDoNotFragment = TRUEbefore the binary search. However, on this network topology (switch, no router), the DF bit doesn't help:GevSCPSDoNotFragmentsupport (Basler defaults it toTrue, Sentech doesn't expose it), yet both produce the same 12316 result. The DF flag is set on the wire but has no enforcer.The
test_packet_check()function only verifies "did a packet of the right size arrive?" — it cannot detect whether the packet was fragmented and reassembled en route.Why exactly 12316?
Both cameras — different vendors, different firmware, different packet size ranges (Sentech: 72–16260, Basler: 220–16404) — converge on exactly 12316. This is the binary search ceiling where IP reassembly still reliably succeeds within aravis's test timeout. Above ~12–13 KB, reassembly latency or fragment loss causes the test to fail, stopping the search. The value is determined by OS reassembly behavior, not by any meaningful network parameter.
Impact
GevSCPSPacketSizemax above NIC MTU are always affectedWorkaround
Set
GvPacketSizeAdjustment.NEVERbefore creating the stream, and manually set packet size to a value within the NIC MTU:Suggested Fix
The auto-negotiation should additionally verify that the negotiated packet size does not exceed the interface MTU of the host NIC. This can be queried from the OS via
ioctl(SIOCGIFMTU)on Linux (the socket/interface is already available in aravis's GigE device code). The negotiated size should be capped atmin(camera_max, nic_mtu - 28).Alternatively,
test_packet_check()could measure round-trip timing — fragmented packets take measurably longer than unfragmented ones, and a timing threshold could detect this.Additional aravis API issue
cam.set_trigger("Off")fails on both Sentech and Basler cameras:TriggerSourceto"Off", which isn't a valid enumarv-gc-error-quark: [TriggerSource] 'Off' not an entryThe workaround is to write
TriggerMode=Offdirectly via GenICam: