skip to content

In Appium, how does the live MJPEG screen stream differ on Android and iOS?

level: middleimportance: should knowfreq 38%

answer

  1. same label, different plumbing
  2. live frames, not a file
  3. three settings shared, two are not
  4. forwarded on one side, broadcast on the other
  5. appium:mjpegServerPort is declared by both drivers

basics

~20 s

Both the UiAutomator2 and XCUITest drivers declare appium:mjpegServerPort, but it names two different mechanisms: on Android it is a host-forwarded port onto the device-side server, and on Apple platforms it is WebDriverAgent's own broadcast port.

solid answer

~40 s

The MJPEG stream is Appium's live view of the device — a sequence of JPEG frames a client can watch while the session runs. `appium:mjpegServerPort` is declared by **both** the UiAutomator2 driver and the XCUITest driver, and that shared name hides a real divergence: on Android it is a port forwarded from the host onto the device-side server, while on Apple platforms it is WebDriverAgent's own broadcast port. `appium:mjpegScreenshotUrl` is shared the same way. Three settings overlap — `mjpegServerFramerate`, `mjpegScalingFactor`, `mjpegServerScreenshotQuality` — while `mjpegBilinearFiltering` is UiAutomator2's and `mjpegFixOrientation` is XCUITest's. Consumers differ too: the Android drivers declare `mobile: startScreenStreaming`, and XCUITest's `mobile: startScreenRecording` captures the stream with ffmpeg.

go deeper

for a junior

Know that Appium can serve a live MJPEG frame stream in addition to stills and recordings, and that appium:mjpegServerPort is the capability that configures where it is served.

for a middle

Explain the divergence behind the shared name: a host-forwarded port onto the device-side server on Android, WebDriverAgent's own broadcast port on Apple platforms, and which settings each side actually declares.

for a senior

Show that you know the stream-derived Apple recorder inherits the stream's framerate, scaling and quality, so tuning the live view silently changes the artefact quality of one of the two recording paths.

for a principal

Weigh a continuously served stream against discrete artefacts across a fleet: the stream's cost runs for the whole session and its knobs differ per platform, so a single fleet-wide quality policy will not land evenly.

## What the MJPEG stream is for A screenshot is a moment and a recording is a file you read afterwards. The **MJPEG stream** sits between them: the driver serves a continuous sequence of JPEG frames over HTTP, and anything that can render a motion-JPEG feed — an inspector window, a dashboard, a capture process — can watch a cattle-auction bidding session as it happens. It is the "live" leg of screen capture, and it is the leg where the two platforms diverge most quietly, because the configuration keys look identical. ## One capability name, two mechanisms `appium:mjpegServerPort` is declared by the UiAutomator2 driver and by the XCUITest driver, and both drivers' parallel-run guidance treats it as a per-session value. That much looks like portability. It is not: - On **Android**, it is a **host-forwarded port**. The frames originate on the device, and the port is the forwarded path from your host machine onto that device-side server. - On **Apple platforms**, it is **WebDriverAgent's own broadcast port** — the agent already running on the device serves the stream directly. So the same capability name configures two different plumbing arrangements. `appium:mjpegScreenshotUrl` is shared in exactly the same way and carries exactly the same caveat. This is the archetype of what platform divergence means on the Appium tree: the token verifies on both sides, and the mechanism behind it does not match. | Concern | Android (UiAutomator2) | Apple (XCUITest) | |---|---|---| | `appium:mjpegServerPort` | host-forwarded port onto the device-side server | WebDriverAgent's own broadcast port | | `appium:mjpegScreenshotUrl` | declared, same forwarding shape | declared, served by the agent | | Shared settings | `mjpegServerFramerate`, `mjpegScalingFactor`, `mjpegServerScreenshotQuality` | the same three names | | Platform-only setting | `mjpegBilinearFiltering` | `mjpegFixOrientation` | | Related capture command | `mobile: startScreenStreaming` | `mobile: startScreenRecording` (ffmpeg over the stream) | ## The settings that tune it, and who owns each The stream's quality and cost are driven by driver settings rather than capabilities, and the overlap is partial: 1. `mjpegServerFramerate` — how many frames per second the stream emits. Declared on both. 2. `mjpegScalingFactor` — how far each frame is scaled down before transmission. Declared on both. 3. `mjpegServerScreenshotQuality` — the JPEG compression applied per frame. Declared on both. 4. `mjpegBilinearFiltering` — smoothing applied when Android frames are scaled. UiAutomator2's, and it does not appear in XCUITest's settings reference. 5. `mjpegFixOrientation` — corrects frame orientation on Apple platforms. XCUITest's, and it does not appear in UiAutomator2's list. XCUITest's settings reference also carries `screenshotQuality`, `screenshotOrientation` and `webScreenshotMode`, which govern the still image rather than the stream. Keeping the still-image settings and the stream settings apart is worth doing deliberately, because three of the MJPEG names read like still-image settings and are not. ## Where the stream is consumed The stream is not only for a human watching a window: - The Android drivers declare `mobile: startScreenStreaming`, a live broadcast command on that side. - XCUITest's `mobile: startScreenRecording` / `mobile: stopScreenRecording` pair is an **ffmpeg capture taken over the MJPEG stream** — a recording built from the live feed rather than from XCTest's own recorder. - An inspector or dashboard can simply point a viewer at the served endpoint while the run continues. That third bullet is why the Apple side has two recorders at all: one is XCTest-native (`mobile: startXCTestScreenRecording`) and one is derived from the stream. If you tune the stream down to save bandwidth and then record through the stream-based path, you have also tuned your video down — a coupling that surprises people. ## Choosing between a still, a stream and a recording Each costs something different, and the choice is a mechanism question before it is a policy question: - A **still** is one `GET /session/:sessionId/screenshot` call, cheap, and identical on both platforms. - A **stream** is continuous and its cost is ongoing for the whole session, tuned by framerate, scaling and quality. - A **recording** is a discrete artefact whose command names differ per platform and whose payload arrives from the stop call. A suite that wants to watch a long cattle-auction bidding run live wants the stream; a suite that wants an artefact attached to a failing case wants a still, a recording, or both. The mistake worth avoiding is assuming that because `appium:mjpegServerPort` reads the same in both capability sets, the thing behind it is the same on both devices. It is not.

  • Which MJPEG settings are shared across both drivers, and which belong to only one?
    `mjpegServerFramerate`, `mjpegScalingFactor` and `mjpegServerScreenshotQuality` appear on both the UiAutomator2 and XCUITest sides. `mjpegBilinearFiltering` is UiAutomator2's and is absent from XCUITest's settings reference; `mjpegFixOrientation` is XCUITest's and is absent from UiAutomator2's. Assuming a shared prefix means a shared setting is the trap.
  • Why can turning the stream's quality down also degrade an Apple recording?
    Because XCUITest's `mobile: startScreenRecording` is an ffmpeg capture taken over the MJPEG stream, so it inherits the stream's framerate, scaling and per-frame quality. `mobile: startXCTestScreenRecording` does not — it is XCTest's own recorder. If your video quality tracks your stream settings, you are on the stream-derived path.

Think of appium:mjpegServerPort as a socket labelled identically in two buildings: the label matches, but on Android the wire runs from your host across to the device, while on Apple platforms it terminates inside WebDriverAgent itself.

saying these in an interview costs you the question

  • Assumes a shared capability name means a shared mechanism
  • Says WebDriverAgent serves the stream on Android too
  • Treats mjpegBilinearFiltering as available on Apple platforms
  • Confuses screenshotQuality with mjpegServerScreenshotQuality
  • Thinks the stream produces a file the way a recording does