Media Player
Add live video screens to your world with the Basis Media Player — OS hardware-decoded streaming into a Unity texture, with audio, surround sound and networked playback.
Overview
The Basis Media Player (com.basis.mediaplayer) plays live video inside a world. It decodes streams with the operating-system hardware codecs (Media Foundation on Windows, AMediaCodec on Android/Quest) and presents the picture zero-copy into a Unity texture — no transcode server, no per-frame CPU copy.
A working video screen is a small stack of components on one GameObject:
BasisMediaPlayer— the core. Owns the stream, decoding and playback (play/pause/stop/seek), and exposes the decoded picture asOutputTexture.- A display surface —
BasisVideoMaterialOutputpaints the picture onto a mesh's material (a quad, a cinema screen), orBasisVideoDisplaypaints it onto a uGUIRawImage. - Audio (optional) —
BasisMediaPlayerAudioplays the stream's sound through anAudioSource, orBasisMediaPlayerMultiChannelAudiospreads a surround stream across several positioned speakers. - Networking (optional) —
BasisMediaPlayerNetworkingkeeps the URL and playback state in sync for everyone in the room.
The player is designed for live broadcast — RTSP, RTMP, MPEG-TS, fMP4 and HLS over the network. Point it at a stream URL and it starts playing; it does not need a local video file.
Quick start
The fastest path is the ready-made prefab.
- Drag
Prefabs/MediaPlayerStreamingfrom the Media Player package into your scene. - Select it and find the Basis Media Player Streaming component.
- Set Stream Url to your stream (for example
rtspt://stream.example.com/live/mystream). - Position and scale the screen mesh where you want the video.
That's it — the prefab carries the player, a display surface and an audio source already wired together, and it loads the URL on play.
To build a screen from scratch instead, add the components yourself:
var player = gameObject.AddComponent<BasisMediaPlayer>();
gameObject.AddComponent<BasisVideoMaterialOutput>().TargetRenderer = screenRenderer;
player.LoadUrl("rtspt://stream.example.com/live/mystream"); // auto-playsChoosing a stream URL
The player picks the right protocol from the URL scheme. Use the lowest-latency option each platform supports.
| Scheme | Best for | Example |
|---|---|---|
rtspt:// | PC / VR, lowest latency (RTP over TCP) | rtspt://stream.example.com/live/mystream |
https://….ts | Quest / Android (MPEG-TS over HTTPS) | https://stream.example.com/live/mystream.ts |
rtmp:// | RTMP pull | rtmp://stream.example.com/live/mystream |
https://….mp4 | Fragmented MP4 over HTTPS | https://stream.example.com/live/mystream.mp4 |
https://….m3u8 | HLS / Low-Latency HLS (Windows) | https://stream.example.com/live/index.m3u8 |
Many streaming services publish the same stream under several schemes. The BasisMediaPlayerStreaming helper can pick the right one for you: enable Auto Select Per Platform and fill in Pc Url (RTSPT) and Quest Url (MPEG-TS). On a desktop or VR build it uses the PC URL; on an Android/Quest build it uses the Quest URL.
Displaying the video
The player produces a texture; a display component binds that texture to something visible.
On a mesh (cinema screen, quad)
Add BasisVideoMaterialOutput to the GameObject and assign Target Renderer to the screen mesh's renderer.
- Texture Property Name — which shader slot receives the picture. Use
_BaseMapfor URP (the default) or_MainTexfor the built-in render pipeline. - Flip Vertically — defaults on, which is correct for the GPU textures the player produces. Turn it off only if your content comes out upside-down.
- Projection Mode —
Monofor normal flat video, orSideBySideLR/OverUnderTBfor stereoscopic sources, orEquirect360/VR180for spherical video on a matching sphere mesh and shader. - Aspect Mode —
FitInsideletterboxes to preserve the source shape,FitOutsidecrops to fill,Originalsamples untransformed.
On a UI canvas (RawImage)
Add BasisVideoDisplay and assign Target Raw Image. Optionally assign an Aspect Fitter (AspectRatioFitter) so the image tracks the stream's aspect ratio automatically. See UI Canvas for setting up the canvas itself.
Audio
Stereo
Add BasisMediaPlayerAudio to the player's GameObject alongside an AudioSource. The player feeds it the decoded sound. Spatialise it like any other AudioSource — set its 3D settings so the audio comes from the screen's position in the world.
The player's Volume (0–1) and Mute fields on BasisMediaPlayer drive the audio path.
Surround / multichannel (5.1, 7.1)
Instead of mixing surround down to one stereo AudioSource, you can send each decoded channel to its own positioned AudioSource — so the centre, surrounds and LFE come from where their speakers sit in the world, and Basis's spatialisation places them for each listener. The package owns playback; you own where the speakers go.
The channels are really just content lanes, so this isn't only for cinema surround: you could put a music mix on channels 1–2 and positioned ambience or effects on channels 3–4 of the same stream.
How channels map to the stream
A BasisMediaAudioChannel selects a channel by number. For a standard surround stream the channels carry the usual WAVE layout:
| Selection | 5.1 / 7.1 role |
|---|---|
| Channel 1 | Front Left |
| Channel 2 | Front Right |
| Channel 3 | Front Centre |
| Channel 4 | LFE (subwoofer) |
| Channel 5 | Surround / Back Left |
| Channel 6 | Surround / Back Right |
| Channel 7 | Side Left (7.1) |
| Channel 8 | Side Right (7.1) |
| Stereo | A stereo downmix of the whole stream |
Set it up
The quickest route is the Prefabs/MediaPlayerMultiChannelStreaming prefab, which already has the routing, the multichannel sink and a set of channel speakers wired up — position its speaker objects, set the stream URL, and play.
To configure it on your own player:
- On the
BasisMediaPlayer, set Audio Routing to Unity Multi Channel Sources. - Add a
BasisMediaPlayerMultiChannelAudiocomponent to the player's GameObject. This replaces the stereoBasisMediaPlayerAudio— the routing decides which sink plays, so you don't need both. - Create one child GameObject with an
AudioSourceper speaker, and position each one where that speaker should sit in the world. Set eachAudioSourceto 3D (spatial blend 1) so it localises. - Add a
BasisMediaAudioChannelto each speakerAudioSourceand set its Channel to the channel that speaker should play (see the table above). - Add every speaker
AudioSourceto the Outputs list onBasisMediaPlayerMultiChannelAudio.
Useful behaviours
- Stereo downmix output — set a speaker's channel to Stereo and it plays a 2-channel fold-down of the whole stream, regardless of how many channels the source has. Handy for a single fallback speaker or a stereo zone.
- Fan-out — the same channel can drive more than one
AudioSource. Point two speakers at Channel 3 to play the centre/dialogue in two areas of a large space. - Graceful fallback — an output whose channel isn't present in the current stream simply stays silent, so a 5.1 speaker layout still behaves sensibly when fed a stereo source (the front pair plays, the rest are quiet).
- Volume Gain / Mute —
BasisMediaPlayerMultiChannelAudiohas its own Volume Gain and Mute that apply across all channels; use eachAudioSource's own volume for per-speaker balance.
5.1 surround over AAC plays on Windows. For 7.1 (eight channels), or to carry surround on platforms whose decoder caps at 5.1, use LPCM audio over an m2ts MPEG-TS stream — uncompressed PCM that needs no platform audio decoder, so any channel count up to 7.1 plays everywhere. It is producible with stock ffmpeg (-c:a pcm_bluray -f mpegts -mpegts_m2ts_mode 1).
Networked playback
For a shared screen, add BasisMediaPlayerNetworking. It synchronises the URL and playback state (play / pause / stop / seek) across everyone in the room, and corrects late joiners and drift automatically.
- Allow Anyone To Take Control — when on (default), any player can change the video or control playback. When off, only the current owner can.
- Admin Only — restrict control to players with the
basis.mediaplayer.control(or admin*) permission. Overrides the setting above. - Drift Seek Threshold Seconds — remote viewers seek to catch up when they drift more than this far from the owner's position. Set to 0 to disable.
Network sources should go through the world's trusted-URL flow so players consent before a screen connects to an arbitrary address. Keep control gated (Admin Only or owner-only) on public screens you don't want any visitor re-pointing.
Scripting
Drive the player from your own components through its public API.
// Load and control
player.LoadUrl("rtspt://stream.example.com/live/mystream"); // auto-plays by default
player.Play();
player.Pause();
player.Resume();
player.Stop();
player.TogglePause();
// Seek (only for seekable sources; live streams are not seekable)
player.Seek(System.TimeSpan.FromSeconds(30));
// Volume / mute
player.Volume = 0.8f; // 0..1
player.Mute = false;
player.Loop = true;React to playback with events:
player.OnReady += () => { /* stream prepared */ };
player.OnFirstFrameReady += () => { /* first frame is on screen */ };
player.OnEnded += () => { /* stream ended */ };
player.OnError += (ex) => { /* connection or decode error */ };
player.OnOutputTextureChanged += (tex) => { /* picture texture (re)bound */ };When a BasisMediaPlayerNetworking component is present, prefer routing playback control through it so the change is synced — calling Play()/LoadUrl() directly on the player only affects the local client.
Platform notes
- Windows (PC / VR) — H.264 / H.265 video and AAC audio via Media Foundation. HLS and 5.1 AAC surround are supported here.
- Android / Quest — H.264 / H.265 + AAC via AMediaCodec. Use the MPEG-TS (
.ts) URL for the most reliable path. - Latency depends on the protocol and the buffer: RTSPT on PC/VR is the lowest. The player holds a short jitter buffer for smooth playback, and aligns audio to video so lip-sync stays correct across reconnects.
Next steps
Last updated on