Rubylight Download 2.2.10
On this page
  1. Negotiation
  2. Framing
  3. 0x5531 display luminance
  4. 0x5532 reconfigure
  5. Error correction reports

Control messages #

Documentation · Configuration · Microphone

Besides Moonlight's own control messages, Rubylight takes a few of its own from the client. Their wire formats are defined once, in the rubylight-protocol crate of Rubylight Android (protocol/core), which the host and the Android client both build. This page is for client authors.

Negotiation #

From 2.2.0 the host's RTSP DESCRIBE answer lists the Rubylight control messages it takes:

a=x-rl-control:0x5530,0x5531,0x5532

Send a message only when its id is in that list. Older hosts and other Moonlight hosts do not send the line; to them, send none of these messages. A host ignores control message ids it does not know.

Framing #

Each message travels on the control stream like Moonlight's client-to-host control messages: the 16-bit message type, then the payload, encrypted when the session encrypts its control stream. All fields are little-endian. Every payload starts with a version byte, currently 1. A newer client may append fields; the host reads the fields it knows and ignores the rest. A payload that is too short, has another version or carries implausible values is ignored (logged at debug).

Id Name Crate type Host behaviour
0x5530 Phase lock report phase_lock::Report Times captures to the client's display latch while reports arrive about every 500 ms; after 3 s without one the host returns to its own pacing.
0x5531 Display luminance control::DisplayCaps Describes the client's screen in the HDR metadata (below).
0x5532 Reconfigure control::Reconfigure Changes the stream's resolution or frame rate without reconnecting (below).

0x5531 display luminance #

Send it once the control stream is connected and again whenever the display changes, for example when a foldable switches screens or the window moves to another display. Sending the same values again is harmless; the host acts only on a change.

Bytes Field Value
0 version 1
1 flags bit 0: the display can show HDR (HDR10 or HLG) right now
2–3 reserved 0
4–7 peak peak luminance in hundredths of a nit (1000 nits = 100000); 0 if unknown
8–11 average maximum frame-average luminance in hundredths of a nit; 0 if unknown
12–15 black minimum luminance in ten-thousandths of a nit (0.0005 nits = 5); 0 if unknown

The host ignores a message whose peak is outside 1–10,000 nits, whose average is above the peak, or whose black level is 1 nit or more.

What the host does with it:

0x5532 reconfigure #

A client whose display changes during a stream, such as a foldable phone opening its inner screen or a window being resized, can ask for another size and rate. The stream continues on the same connection; there is no new RTSP exchange.

The message #

Send it on the encrypted control stream like any other control message (the keyframe request 0x0302, for example), with type 0x5532 and this 12-byte payload, little-endian:

Bytes Field Value
0 version 1
1 reserved 0
2–3 width pixels, even, 256–8192
4–5 height pixels, even, 256–8192
6–7 reserved 0
8–11 frame rate thousandths of a frame per second, 10 000–500 000 (120 fps is 120000, 59.94 fps is 59940)

A payload that is shorter, has another version or has a value outside these limits is ignored. Reconfigure::encode in rubylight-protocol (rp_reconfigure_encode in C) writes it.

What the host does #

The host does not reply. It keeps the old size when:

The host logs each switch as reconfigure with the old and new mode and how long the switch took, and each refusal as reconfigure refused with the reason.

Client advice #

Error correction reports #

Moonlight's own FEC status message (0x5502, SS_FRAME_FEC_STATUS in moonlight-common-c) needs no announcement. A client that sends it for frames it had to repair or could not rebuild gets adaptive error correction from 2.2.0: the share of recovery packets rises with reported loss, returns to the configured fec_percentage when a frame could not be rebuilt, and falls slowly towards 5 % while no loss is reported. A client that never sends it keeps the configured percentage.

View this page on GitHub