What I'm seeing
In v2, Python users could build one Pipeline and call device.startPipeline(pipeline) to run the same graph on multiple devices from one process. That's gone in v3 Python bindings, even though the C++ method is still there and still shipping. The result is that every device in a multi-camera manager now needs its own fresh Pipeline object, which costs real host RAM per camera.
Repro
import depthai as dai
print("Device.startPipeline exists: ", hasattr(dai.Device, "startPipeline"))
print("DeviceBase.startPipeline exists:", hasattr(dai.DeviceBase, "startPipeline"))
print("depthai version: ", dai.__version__)
Output (depthai 3.5.0):
Device.startPipeline exists: False
DeviceBase.startPipeline exists: False
depthai version: 3.5.0
The C++ method is still there
include/depthai/device/DeviceBase.hpp:286:
bool startPipeline(const Pipeline& pipeline);
src/device/DeviceBase.cpp:1717:
bool DeviceBase::startPipeline(const Pipeline& pipeline) {
return startPipelineImpl(pipeline);
}
Used internally by Pipeline::start() at src/pipeline/Pipeline.cpp:894:
defaultDevice->startPipeline(Pipeline(shared_from_this()));
And bindings/python/src/DeviceBindings.cpp has no .def("startPipeline", ...) anywhere. grep -r "startPipeline" bindings/ returns zero hits.
Bonus: utilities/stress_test.py:169 still calls device.startPipeline(pipeline). That script cannot run against the shipped v3 Python bindings. Probably worth fixing or removing depending on how this issue resolves.
Why it matters
Most multi-camera managers run the same graph on every camera. In v2 the clean pattern was:
self.pipeline = create_pipeline() # build the graph once
...
for device_info in device_infos:
device = dai.Device(device_info)
device.startPipeline(self.pipeline)
I'm porting a production eggseer manager forward that uses exactly this pattern across 24 cameras. In v3 the only option is dai.Pipeline(device) per device, which means 24 separate Pipeline objects, each paying their own per-Pipeline allocation cost. I measured this at roughly 17 MiB steady-state and 60 MiB transient per device during construction. At 24 devices that's 400 to 700 MiB of duplicated Pipeline state for 24 identical graphs.
Worse, Device is no longer the hot-swappable unit. When a camera disconnects in v2 you dropped its Device and reused the same Pipeline on reconnect. In v3 you tear down the whole Pipeline and rebuild it, which means paying the 60 MiB transient again and running through all the node wiring code on every reconnect.
Suggested fix
Add one line to bindings/python/src/DeviceBindings.cpp:
deviceBase.def("startPipeline",
&DeviceBase::startPipeline,
py::arg("pipeline"),
DOC(dai, DeviceBase, startPipeline));
The C++ method is already public, already tested (it's called internally by Pipeline::start()), and already shipping. Re-exposing it to Python is a pure bindings change with no C++ risk.
Questions
- Was
startPipeline removed from Python bindings intentionally in the v2-to-v3 migration? If so, what's the v3-blessed way to share one graph definition across multiple devices in one process?
- If it was an oversight, can the binding be added back? Happy to open a PR with the one-line change if that'd help.
- If there's a deeper architectural reason it can't come back, it'd be great to have that documented so multi-camera users know to plan around it.
Environment
- depthai 3.5.0
- Python 3.14
- Linux (WSL2, Debian)
- OAK-D x 3 (RVC2, Myriad X)
What I'm seeing
In v2, Python users could build one
Pipelineand calldevice.startPipeline(pipeline)to run the same graph on multiple devices from one process. That's gone in v3 Python bindings, even though the C++ method is still there and still shipping. The result is that every device in a multi-camera manager now needs its own freshPipelineobject, which costs real host RAM per camera.Repro
Output (depthai 3.5.0):
The C++ method is still there
include/depthai/device/DeviceBase.hpp:286:src/device/DeviceBase.cpp:1717:Used internally by
Pipeline::start()atsrc/pipeline/Pipeline.cpp:894:defaultDevice->startPipeline(Pipeline(shared_from_this()));And
bindings/python/src/DeviceBindings.cpphas no.def("startPipeline", ...)anywhere.grep -r "startPipeline" bindings/returns zero hits.Bonus:
utilities/stress_test.py:169still callsdevice.startPipeline(pipeline). That script cannot run against the shipped v3 Python bindings. Probably worth fixing or removing depending on how this issue resolves.Why it matters
Most multi-camera managers run the same graph on every camera. In v2 the clean pattern was:
I'm porting a production eggseer manager forward that uses exactly this pattern across 24 cameras. In v3 the only option is
dai.Pipeline(device)per device, which means 24 separatePipelineobjects, each paying their own per-Pipeline allocation cost. I measured this at roughly 17 MiB steady-state and 60 MiB transient per device during construction. At 24 devices that's 400 to 700 MiB of duplicated Pipeline state for 24 identical graphs.Worse, Device is no longer the hot-swappable unit. When a camera disconnects in v2 you dropped its Device and reused the same Pipeline on reconnect. In v3 you tear down the whole Pipeline and rebuild it, which means paying the 60 MiB transient again and running through all the node wiring code on every reconnect.
Suggested fix
Add one line to
bindings/python/src/DeviceBindings.cpp:The C++ method is already public, already tested (it's called internally by
Pipeline::start()), and already shipping. Re-exposing it to Python is a pure bindings change with no C++ risk.Questions
startPipelineremoved from Python bindings intentionally in the v2-to-v3 migration? If so, what's the v3-blessed way to share one graph definition across multiple devices in one process?Environment