System designTroubleshooting

USB camera at /dev/video0 but no video in your app UI

Your USB camera streams in GStreamer but the app shows no video? Check the real /dev/video0 node, the format the app requests and the fix that works.

LumaSpec Vision10 min read Share on LinkedIn

On this page 9 sections
  1. 01Why does GStreamer show video while your app stays grey?
  2. 02Which /dev/videoN node does the application actually need?
  3. 03Is the app still bound to the previous camera?
  4. 04What format is the app asking the camera for?
  5. 05Is another process holding the node?
  6. 06How do you prove where it failed without the UI?
  7. 07When is the hardware guilty, and what do you order?
  8. 08What is the checklist before you spend anything?
  9. 09Questions

The symptom is precise: gst-launch-1.0 v4l2src ! videoconvert ! autovideosink plays your USB camera in a window, and the application panel on the controller still sits grey. The same camera works on your laptop. That combination is a software failure with a short checklist. You will identify which /dev/videoN node actually captures frames, learn why one USB camera can register two nodes, and check whether the application is still bound to the camera you replaced. Then you will do the bandwidth arithmetic that blanks uncompressed previews on a USB 2.0 port. Work the causes in the order below — node, source binding, format, then hardware — because that is their order of probability in the field.

Why does GStreamer show video while your app stays grey?

Because gst-launch-1.0 v4l2src ! videoconvert ! autovideosink showing live frames proves the entire path — sensor, cable, driver, node, decode, display — the failure sits one layer up: the application's device selection, its format request, or a stale binding to the camera you just replaced. Everything below the working pipeline is proven; everything above it is suspect. Grey panels come from four places, in field order of probability: the stored device setting points at the wrong node; the application is still bound to a previous camera; the requested format, resolution or frame rate is not on the camera's list; the uncompressed format cannot fit the port's bandwidth. Real hardware failure comes last, not first.

The classic report reads like this: a controller that streamed from a smartphone camera gets a USB camera on /dev/video0, the panel shows a spinner or grey, and the old phone stream still appears on demand. The panel never switched sources — a binding problem, not a camera problem. Two neighbouring symptoms have their own write-ups: video in setup but a grey image at runtime, and nothing playing anywhere, which starts at the driver layer with GStreamer missing plugins.

Where it fails What you see The test that proves it The fix
Wrong /dev/videoN node Grey panel, no error v4l2-ctl --list-devices Point the app at the capture node
Stale camera binding Spinner; old camera still streams Device settings in the app Unpair the old source, enable USB, reboot
Unsupported format request Grey after connect; lower resolution plays Compare the app request with the camera's list Set one listed format, resolution and rate
USB 2.0 bandwidth MJPEG plays, uncompressed stalls The bitrate arithmetic below MJPEG, lower resolution, or USB 3
Node held busy Second app cannot open the node Close every viewer, restart One reader per node

Which /dev/videoN node does the application actually need?

The capture node — and under the uvcvideo driver one camera commonly registers two nodes, one delivering frames and one metadata node delivering none, so an application pinned to the wrong number opens cleanly and still shows grey. The two-node behaviour is documented kernel behaviour, not a defect in your camera: one node carries video capture, the other carries metadata, and a video application that opens the metadata node gets no frames and no error. Find the capture node in two steps:

  1. Run v4l2-ctl --list-devices; every registered camera prints with its node numbers.
  2. Query each candidate with v4l2-ctl --device=/dev/video0 --all and read the Driver Info block: the node reporting video capture is the node the application must open.

An application that stores a bare node number rots the day enumeration order changes — a second camera, a receiver on the board, a reboot — so re-check this line after any hardware change. The same right-node discipline applies to controls: v4l2-ctl list-ctrls returns nothing when you query the wrong node. If the application runs in a container, the node has to reach it first: pass the V4L2 media nodes, not just /dev/video0.

Is the app still bound to the previous camera?

If the application was ever paired to another source — a smartphone stream, an IP camera, an earlier USB model — it keeps waiting for that device and never opens your node until you disable the old binding, enable the USB camera and reboot the controller. The giveaways: a spinner that never resolves, a pairing screen that still names the old device, and the old camera's stream appearing the moment its own app starts. The binding is stored by the controller or its cloud service, not by the camera, which is why replugging the USB camera changes nothing. Fix it in this order: disable the previous camera source in the device settings, enable the USB camera option, reboot the controller, open the panel. The reboot belongs to the fix, because the active source is read at startup.

When the previous source was an IP camera rather than a phone, the grey-panel symptom moves to the network layer and gets its own checklist — troubleshooting an IP camera viewer that shows no video covers that path.

What format is the app asking the camera for?

A V4L2 camera delivers only the format, resolution and frame-rate combinations on its own list; request a combination the camera does not offer and many frameworks open the node, allocate a grey buffer and stream nothing. Webcams typically offer MJPEG and an uncompressed format such as YUYV at overlapping resolutions, and an application's automatic pick is often the uncompressed one. Query the node as above, read the formats it prints, then set the application to exactly one row from that list — width, height, pixel format and frame rate.

The uncompressed pick then fails on USB 2.0 for arithmetic reasons, not driver ones:

bus load = width × height × bytes per pixel × fps × 8 YUYV 1280 × 720 × 2 × 30 × 8 = 442,368,000 bit/s ≈ 442 Mbit/s

Work it through: 1280 × 720 is 921,600 pixels. YUYV carries 2 bytes per pixel, so a frame is 1,843,200 bytes. At 30 fps that is 55,296,000 bytes per second — 55.3 MB/s, or 442 Mbit/s. USB 2.0's 480 Mbit/s is 60 MB/s on paper, and protocol overhead leaves a camera roughly 30 to 35 MB/s in practice, so 55.3 MB/s does not fit and the request stalls into a grey panel.

The decision: on a USB 2.0 board, set the application to MJPEG at 1280 × 720, or keep YUYV and drop to 640 × 480. On USB 3, with 5 Gbit/s of headroom, the uncompressed mode is fine. Check what the port actually negotiated with lsusb -t — a USB 3 device silently sitting at USB 2 is common enough for its own write-up: why lsusb -t shows 480M.

Is another process holding the node?

One client at a time: V4L2 gives the capture node to a single reader, so a test pipeline, an old viewer or a crashed instance still holding /dev/video0 leaves the application a busy node — and some UIs paint that grey instead of raising an error. Close every GStreamer window and preview before starting the application, and kill crashed instances. If the node stays busy after the process is gone, the kernel still holds the open file descriptor: V4L2 device busy after restart walks that release. Two applications racing for one node produce the vanishing-device variant in camera device nodes disappear when a second application starts.

How do you prove where it failed without the UI?

Play each layer directly and compare: two commands — v4l2-ctl --list-devices and the GStreamer pipeline — isolate the failing layer in under five minutes, before you open a configuration screen or order a part.

  1. Run v4l2-ctl --list-devices and write down every node each camera owns.
  2. Query each candidate with v4l2-ctl --device=/dev/videoN --all until the Driver Info block shows video capture.
  3. Play that node: gst-launch-1.0 v4l2src ! videoconvert ! autovideosink.
  4. Start the application and compare its configured device, format, resolution and frame rate against what you just proved, changing one setting at a time.
  5. Reboot the controller after any source change and retest, because bindings load at startup.

If step 3 shows frames and step 4 stays grey, the node is innocent and the application's source selection or format request is guilty — your answer, with no spare parts involved. For the device itself, getting a UVC camera streaming over USB with no config file is the minimal path, and capturing raw frames from several V4L2 cameras with v4l2-ctl shows the same node-by-node discipline at capture time.

When is the hardware guilty, and what do you order?

Only when the pipeline that plays on your laptop fails on the board is hardware guilty; the suspects are the port, the cable and the camera's power draw, in that order. Check lsusb -t for the negotiated speed, try a second port, then try a short cable. Long passive runs and unpowered extensions are the usual offenders on single-board controllers, which also budget less port current than a laptop; a camera that browns out enumerates and then delivers nothing. A powered hub settles the current question in one test.

The first swap is the cable: the LS-CBL-0219 is a 3 m USB3.0 camera cable rated for drag-chain use.

Cables3 m USB3.0 camera cable, high-flex (drag chain) (LS-CBL-0219)LS-CBL-0219$62.00View partBuild a system with it

If the board has no spare USB 3 controller of its own, add one: the MV-GU2204 is a quad USB3.0 adaptor.

Frame grabbersHikrobot MV-GU2204 MV-GU2200 Series Quad USB3.0 AdaptorMV-GU2204
Interface
USB3
Ports
4 ports
$469.00View partBuild a system with it

What is the checklist before you spend anything?

Run the checks that cost minutes first: the lines below settle the fault on the board itself, and you order a part only for the line that fails last.

  • v4l2-ctl --list-devices run; two nodes per camera is normal, not a fault.
  • Application device setting points at the capture node, not the metadata node.
  • Previous camera source disabled or unpaired, USB camera enabled, controller rebooted after the change.
  • Application format, resolution and frame rate copied from one row of the camera's list.
  • USB 2.0 arithmetic done: uncompressed 1280 × 720 at 30 fps needs 442 Mbit/s and does not fit; MJPEG does.
  • Every other viewer closed; one reader per node.
  • Cable short on USB 3, camera on its own port, no unpowered extension.

The decision, case by case: pipeline plays and the panel stays grey — fix the application's node or source binding; pipeline fails on the board only — port, then cable, then camera; format chase dragging past half a day — stop debugging webcams and put a documented camera on the node. An industrial camera publishes its formats in the data sheet, so the negotiation failures above stop being your problem. The LS-CMR-0166 is a 12 MP USB3 area scan camera with a 4000 × 3000 resolution.

Area scan cameras12 MP USB3 area scan camera, IMX226, 1/1.7" rolling shutter, 30 fps, 4000 × 3000 resolution (LS-CMR-0166)LS-CMR-0166
Sensor format
1/1.7"
Interface
USB3
Resolution
12 MP
Shutter
Rolling
$560.00View partBuild a system with it

If this panel grows into an inspection station, start from the camera finder or build the system step by step. For a failure that will not reproduce on the bench, bring the two command outputs from the steps above to the contact page.

Questions

Why does one USB camera create two /dev/videoN entries?

The uvcvideo driver registers a video-capture node and a metadata node for the same hardware, so one camera can own /dev/video0 and /dev/video1. Only the capture node delivers frames. Query each node and read its Driver Info block before pointing the application at a number. The numbering also shifts when a second camera or the board's own receiver enumerates first, so an application that stores a bare number can break after a reboot.

Can a USB 2.0 port really blank the preview?

Yes, when the application demands uncompressed frames. YUYV at 640 × 480 and 30 fps is 640 × 480 × 2 bytes × 30 × 8 = 147,456,000 bit/s, about 18 MB/s, and that fits. The same camera at 1280 × 720 in YUYV needs about 442 Mbit/s, which does not. On a USB 2.0-only board set the application to MJPEG or cap the resolution, or move the camera to a USB 3 port.

The panel went grey right after replacing the old camera — why?

The application stores its camera source, and a paired device keeps priority until you remove it: the UI spins waiting for the old camera while the USB node sits idle. Open the controller's device settings, disable or unpair the previous camera, enable the USB camera option and reboot. On cloud-connected systems the pairing can survive that reboot because the server caches it — clear the device entry there too, then reboot once more.

How do I test the camera without writing code?

Two commands: v4l2-ctl --list-devices shows every node the kernel registered, and gst-launch-1.0 v4l2src ! videoconvert ! autovideosink plays the capture node in a window. Run the window test as the only open client: a viewer that grabs the node while the application is starting produces a false negative and sends you hunting the wrong layer.

Should I replace the webcam with an industrial USB3 camera?

Do it when the panel feeds inspection rather than casual viewing, or when the webcam's uncompressed modes keep colliding with the port budget. A machine-vision USB3 camera documents its format, resolution and frame-rate list, holds its cable with a locking connector, and USB 3 headroom makes its uncompressed modes practical. Keep the webcam for remote viewing.