Declare a Player
A Player is one unit of equipment: a screen, the outputs its sound
goes to, the GPU that draws, and the controllers that drive it. This
guide declares one for a machine that is connected to a television or
a monitor, reads its status until the screen is present, and proves
it with one Play. At the end, the unit shows its idle screen and a
remote drives it.
You need:
- The operator and its bus, from the install .
- The
display-operatorand theaudio-operatorinstalled, with the consumer classes your cluster names for a screen, a render node, and an output. Each operator’s install guide gives the YAML for its class, and the class names below are examples. - A
Remotefor each controller the unit owns, from Map a new controller . APlayerwith no controllers is fine; add them later. kubectlaccess to the namespace.
1. Confirm the machine publishes the hardware
The scheduler places every pod of a Player on the one machine that
owns all of its devices, so start with what that machine publishes.
The display operator publishes one device per connector, and the
audio operator one per output:
kubectl get resourceslice <node>-display.liken.sh -o yaml
kubectl get displays
kubectl get sinks
The monitor you want must appear as a Display, and the outputs you
want as Sinks on that node. A connector with no monitor publishes
with a disconnected taint, and a claim on it parks.
A monitor that is switched to another input sends no hotplug event,
and the display operator polls nothing. If the Display for a
connected monitor is missing, switch the monitor to this machine’s
input, and it appears within seconds.
2. Write the Player
Declare the unit in the namespace its Remotes are in. Name each
device by class, and narrow the class with a selector where the
machine has more than one of a kind:
kubectl apply -f - <<EOF
apiVersion: media.liken.sh/v1alpha1
kind: Player
metadata:
name: den
namespace: media
spec:
zone: den
displayName: Den Television
display:
class: display-output
displayName: Television
selector: |
has(device.attributes["display.liken.sh"].model) &&
device.attributes["display.liken.sh"].model == "LG TV"
render:
class: gpu-render
sinks:
- class: audio-output
displayName: Television Speakers
selector: |
has(device.attributes["audio.liken.sh"].sink) &&
device.attributes["audio.liken.sh"].connectionType == "hdmi"
remotes:
- name: den-remote
displayName: Den Remote
EOF
The selectors are the same CEL a hand-written claim would use, over
the attributes each hardware operator publishes. Guard an attribute
that comes from the monitor with has(), because it is absent on an
empty connector, and a selector that reads a missing attribute fails
the whole allocation. The display operator’s
claim guide
and the
audio operator’s claim guide
list the attributes and the selectors that survive a re-cabling.
render is required for video. mpv decodes and draws through the
GPU, and a Player with a display and no render node plays nothing.
Omit display and render together for a unit that plays sound
alone. Players
describes every field.
3. Read the status
The operator creates a persistent claim for the screen and a pod that
draws the idle screen, named <player>-idle:
kubectl get player den
NAME ZONE ACTIVITY PLAY IDLE SCREEN AGE
den den Idle media.liken.sh/idle-screen Present 40s
SCREEN is the reason of the Screen condition. Present means the
screen’s Display reports a panel on its connector, and the idle
screen is on it. The condition is absent until the claim resolves
once, so an empty column right after the apply is normal. Read the
whole status for the screen and the outputs the claim resolved to:
kubectl get player den -o jsonpath='{.status.screen}{"\n"}{.status.sinks}{"\n"}'
status.sinks fills after the first Play, because the operator
reads the outputs from an allocated playback claim.
4. When the idle pod stays Pending
A Pending idle pod means the claim on the screen did not allocate.
The Screen condition says why:
-
PanelAway: theDisplayreports no panel on the connector. The monitor shows another input, or its cable is out. Switch the input or seat the cable, and the pod schedules on its own. This park is by design, and the pod staysPendinguntil the panel returns. -
NoDisplayorNotReported: noDisplaycarries the monitor the claim remembers. Go back to step 1 and confirm the machine publishes the screen. -
An empty column: the claim has not resolved. Read the claim’s events for the selector that matched nothing, or matched devices on two machines:
kubectl describe resourceclaim den-idle-devices
A selector that reads an attribute without has() fails on every
device that lacks it. If every candidate lacks that attribute, the
resulting claim matches no device.
A pod whose events say Insufficient memory or Insufficient cpu
has a different cause: the machine that has the screen has too little
memory or cpu left for the pod’s requests. Every pod of a Player
must run on that machine, so the scheduler cannot place it on another
one. At the defaults, the idle pod requests 96Mi of memory and 5m of
cpu. A playback pod requests 592Mi and 100m in all: 432Mi and 80m for
the player, 144Mi and 10m for the display, and 16Mi and 10m for the
command sidecar. The idle pod keeps running under a film, so a machine
must hold both pods at once: about 688Mi and 105m. Free memory on the
machine, lower the requests as the
install guide
shows, or
connect the screen to a machine with more.
kubectl describe pod den-idle
5. Prove it plays
Create a Play that names the unit, with one file the machine can
reach:
kubectl apply -f - <<EOF
apiVersion: media.liken.sh/v1alpha1
kind: Play
metadata:
name: den-check
namespace: media
spec:
players: [den]
items:
- uri: nfs://nas/media/movies/Example (2019)/Example.mkv
EOF
The Player’s ACTIVITY goes to Starting, then Playing, and the
film is on the screen with its sound on the outputs you named. Press
a button on the remote to confirm it drives the unit. Delete the
Play to stop, and the idle screen returns:
kubectl delete play den-check
A Play that stays Starting has a pod that did not schedule, and
its events name the device that did not allocate. A uri scheme the
operator does not know fails the Play before any pod exists.
Plays
describes the item forms.
Choose what the screen does when idle
The operator’s own idle screen is the default. Three fields on
spec.idle change it, and each one overrides the default
MediaPreferences on its own:
controllerhands the screen to another operator, such as the library operator’s browser. Hand the idle screen to another controller covers both sides.fadeAfterSecondsfades the idle screen to black after that much quiet.offAfterSecondsdarkens the panel itself after that much quiet, at leastfadeAfterSeconds, and only where aDisplayexists for the screen.- Set
offAfterSeconds: 0for a monitor that other machines share. The panel cannot say whose input it would darken.
Set the defaults once for every unit in the cluster’s
MediaPreferences
, and set a
field on a Player only where that unit differs.