CosMix Media

cosmix-media is a Wayland-native Bevy/CTK player for local audio and video. Version 0.1.1 uses GStreamer inside the process for demuxing, decoding and a shared audio/video playback clock. Audio uses pulsesink, compatible with PipeWire's PulseAudio server. It does not launch mpv, FFmpeg or an X11 window.

Run cosmix-media /absolute/path/movie.mp4, or start without a file and choose one with File → Open… (Ctrl+O). The native CTK file chooser opens initially in --directory DIR (default $HOME/Downloads) and remembers the last selected directory for this session. --service NAME changes the default Bus name media for multiple instances.

The conventional menu bar replaces DCS panel furniture: File contains Open and Quit; Playback contains play/pause, stop and ten-second relative seeks; Audio contains volume and mute; View contains fullscreen. The video fills the remaining area above a small status row. There are no sidebars, pin/float controls or separate button toolbar.

Space toggles playback; Left/Right seek; M toggles mute; F toggles fullscreen. View → Toggle fullscreen performs the same action; Escape exits fullscreen. Fullscreen hides the player's menu and status bar, leaving the video fitted inside the output with its aspect ratio preserved. Playback shortcuts are suspended while the file chooser is active. Files can also be opened through the Bus. Playlists, subtitle controls and network URLs are not implemented.

Runtime requirements

The Wayland session must be available through WAYLAND_DISPLAY. CTK builds with its Wayland platform feature; no X11 platform feature is requested. Audio follows PULSE_SERVER when set, otherwise the normal PulseAudio client discovery path. Opening a container audio socket requires the session host to provide that socket and access permissions.

GStreamer 1.x runtime plugins must provide playbin, appsink, pulsesink, MP4 demuxing and the codecs in the files. On Arch, the baseline packages are gstreamer, gst-plugins-base, gst-plugins-good and gst-libav. The Rust application is MIT licensed; the separately installed GStreamer libraries and plugins retain their upstream licences. This is not a pure-Rust codec stack.

MP3 and H.264/AAC MP4 are the initial acceptance formats. An MP4 extension does not guarantee codec support. Other installed GStreamer codecs may also work. Errors are visible in the status bar and Bus status; unsupported files are not reported as successful playback.

Bus control

Commands use JSON object bodies. A mutation reply means the playback worker applied the operation or initiated the asynchronous pipeline state change; read media.status for subsequent codec errors and progress. A timeout does not cancel an operation already queued. All controls use the same worker queue as the UI. This initial service exposes local file paths to its Bus callers; deploy it on the trusted desktop Bus.

CommandBody
media.open{"path":"/absolute/path/movie.mp4"}
media.play, media.pause, media.toggle, media.stop{}
media.seek{"seconds":30} (absolute, keyframe seek)
media.volume{"value":0.8} (0–1)
media.mute, media.fullscreen{"value":true}
media.fullscreen.toggle{}
media.status{}
media.props.get{"path":"position"} or {} for the complete snapshot
media.quit{}

Status contains phase, path, position/duration in seconds, volume, muted, fullscreen, video dimensions, generation, frame counters, backend, version, PID and error. playing describes the requested pipeline state; advancing position and video frames are the evidence of actual playback. ended is EOS. decoded_frames counts frames delivered to the application, including preroll; replaced_frames counts those superseded before Bevy consumed them. These are not hardware decoder throughput or compositor presentation statistics. fullscreen is also a requested state: the compositor must honour the Wayland fullscreen request. CosMix Comp 0.51.1 applies fullscreen at the acknowledged surface commit, hides compositor decorations and focused native Quoin panels, and restores the previous window geometry on exit. The compositor's windows.<id>.fullscreen property reports the committed state; the player's flag reports the request. Earlier compositor versions only acknowledged it.

For example, from Mix on a desktop Bus:

send media media.fullscreen.toggle
send media media.toggle
send media media.fullscreen value=false

The menu, keyboard and Bus all enqueue the same worker actions. Two queued fullscreen toggles therefore cancel each other even before the UI refreshes.

Rendering and limits

Decoding, pipeline state changes and seeks run on a dedicated worker. The command queue holds at most 32 requests. Appsink retains at most two frames; the renderer handoff retains only the latest frame. A stalled window renderer therefore drops obsolete video instead of blocking audio or growing a queue. Decoded output is bounded to 4096 × 4096; this does not bound every allocation inside third-party demuxers/decoders.

This baseline copies RGBA pixels into Bevy images. It is not zero-copy and does not guarantee hardware decoding. Future VA-API/DMA-BUF or Vulkan Video backends must preserve audio/video clocking, seek/EOS handling and bounded frame ownership while replacing this handoff. Matching wgpu versions alone does not establish compatible device creation or decoder support.