· · 7 min read

Testing HDMI output with a USB capture device

A stable HDMI picture can still ignore the requested image. A USB capture rig gave AI coding sessions a way to check the recording path, separate signal loss from progress, and test whether their display changes reached the output.

I wanted a repeatable way to test display-driver changes on real hardware without me babysitting a computer monitor. The results needed to be deterministic and capture the picture the HDMI actually produced.

A USB capture device lets me record HDMI as video (.mp4, .mkv, etc). This is perfect because I was stuck; HDMI put something on the screen, but it was visual gibberish. I needed to be able to capture that gibberish, and test if it was changing as I made software changes.

Choose a capture device scripts can use

My SG2000 board sends pixel data over MIPI DSI, a serial display link, to a bridge chip that converts it to HDMI (it had HDMI, but behind layers of red tape). My starting point was weak but concrete, a vendor provided driver that had basic DSI support.

I had the following requirements for the new capture device:

  • It needed to accept 720p at 60 frames per second
  • It needed to support USB Video Class (UVC), which has good support with Linux/ffmpeg
  • It needed to handle turbulent input, since the signal would frequently cut out or have invalid content

An HDMI analyzer or oscilloscope could reveal more about the signal, but the capture device created a baseline. Here I needed a device that scripts could open on every run, so the acceptance test started simply: find it on USB, find its Linux video device, and capture a frame.

From the board's display output to a recorded frameInside the board, the SG2000 sends pixel data over DSI to the HDMI bridge. HDMI connects the bridge to a USB capture device. USB connects the capture device to a laptop, where scripts record video and compare the captured frames.Board under testSG2000pixel sourceDSIHDMIbridgeHDMIUSB capturedeviceUSBLaptopRecord + compare
From the board's display output to a recorded frameInside the board, the SG2000 sends pixel data over DSI to the HDMI bridge. HDMI connects the bridge to a USB capture device. USB connects the capture device to a laptop, where scripts record video and compare the captured frames.Board under testSG2000pixel sourceDSIHDMI bridgeHDMIUSB capturedeviceUSBLaptopRecord + compare
The board supplies HDMI; the laptop records it over USB. To check the capture path independently, the laptop’s own HDMI output replaces the board as the source.

Check the capture path on a known source

Before testing the board, I tested the capture device by looping my laptop’s HDMI output back into my capture device’s HDMI input. A recognizable desktop established that the capture path worked independently of the board’s broken state.

Surprisingly - even though I could see my desktop, the test exposed a flaw. With this device, the laptop only detected a connected display after I opened a capture stream. This meant that there was a delay between watching the stream, and usable video data; noise and dark images appeared for roughly 2 seconds. My first script would grab a frame too early and flag a false result, so the test changed to recording a short clip and keeping its last frame.

A known source still had startup noise and a dark interval. The laptop's desktop appears by about 3.3 seconds.

Further testing of the device led to:

  • Adding “no device” and “no frame” exit codes
  • A 20 second delay after closing a stream before opening a new one (rapid on/off caused unpredictable behavior)
  • Detecting if an image was byte identical to the previous image

The known-source desktop check remained useful whenever the board stopped producing a signal. During debugging I captured 15-second recordings sampled once a second on both the board, and my laptop. The laptop passed those checks, showing that the capture path still worked.

A working capture path can still record the wrong source: a session recorded my desktop in what should have been a board recording. I had left the HDMI input connected to the laptop, so those recordings were excluded from the board test results.

Compare and categorize captures

Running the vendor’s DSI drivers, the board could reliably produce a colored-pinstripe artifact. That was a promising start, but it didn’t establish whether the picture followed the software’s requests.

The SoC, the board’s main chip, has a test-pattern generator that draws built-in images without reading frames from memory. A session requested solid red, green and blue in turn and compared hashes of the captures. All three captures were byte-identical. Each showed the same fine vertical stripes, one pixel column wide. Even swapping the red, green and blue channels should have produced different pictures.

  • Different hashes alone wouldn’t have proved success. The capture stream uses MJPEG, a stream of compressed JPEG frames, and small amounts of noise can change a recording of the same input. A changed capture still needs to be checked against the requested picture.

  • Identical captures can also mean a dead signal. With HDMI unplugged, the capture device sees a dark frame for red, green and blue requests. Here it recorded the same colored stripes instead: there was a picture to compare, but it didn’t follow the requests.

The first comparison script counted a flat no-signal frame as a change from the stored stripe, both failures but of different types. A register sweep, trying different hardware settings in turn, could therefore report progress simply by losing the signal. Classifying flat dark frames as no signal prevented that false positive.

Most tests followed a change, build, capture loop. For one sync polarity experiment, a session recorded continuously through settings changes and resets, using the same picture classifications (consistent checks, flexible experiments).

The picture briefly breaks up and goes dark before returning to exactly the same stripes. The classifier recorded signal loss during the transition and the unchanged failure afterward.

Test the bridge’s output independently

The unchanged stripe still left several possible faults between the SoC and the recording. The bridge offered a way to test part of that chain: it can generate its own test picture independently, bypassing the incoming DSI pixels.

One write over I2C, the bridge’s two-wire control bus, enabled a black frame. Setting its fill bit changed the output to white at 1280 by 720. The recording followed the changed request.

That established that the bridge’s HDMI transmitter, output clock, cable and capture device could deliver a deliberately chosen picture. The bridge generated it internally, so the test didn’t exercise the SoC’s DSI output or the bridge’s DSI receiver. Those remained under investigation.

A plain white 16:9 capture frame with no stripes or noise.
The bridge's own generator produced this white 1280 by 720 frame. It tests the HDMI and capture path while bypassing the incoming DSI pixels.

Keep the measurement consistent

Comparing driver changes also meant keeping the capture conditions consistent. Otherwise, a change in the recording setup could look like a change in the board’s output.

I pinned the stream at MJPEG, 1280 by 720, requesting 60 frames per second. The scripts then enforced three checks:

  • Choose the window in advance. Frames had to arrive at a steady rate throughout a fixed 15-second window, starting two seconds into the recording. The script couldn’t search for a cleaner stretch afterward.

  • Preserve the evidence. Keep the full recording, including startup faults. A later automatic check verified that extracting the window preserved its compressed video packets and frame timing, allowing timestamp rounding.

  • Check the picture separately. Compare image pixels against a named reference once a second after settling. This could identify the wrong picture, though brief faults between samples could still be missed.

That completed the capture rig: coding sessions could test driver changes against real HDMI output and retain the evidence for review. The SoC’s picture still needed fixing. I now had a repeatable way to measure that work without watching a monitor on every run.

More in Linux drivers and bring-up →

Share