MediaPlayerLib — Media Player Library
Audio and video playback for Plan9Basic applets. The library provides two components: a non-visual Media Player for background audio, and a visual Media Control for video display. Both share similar playback APIs (load, play, pause, stop, seek, volume) and event callbacks. 58 functions.
| Component | Prefix | Type | Use Case |
|---|---|---|---|
| Media Player | media_* | Non-visual | Background audio, music players, sound effects |
| Media Control | media_ctrl_* | Visual (placed on form) | Video playback, multimedia presentations |
| Category | Player | Control | Total |
|---|---|---|---|
| Error Handling | 4 | — | 4 |
| Creation & Destruction | 2 | 2 | 4 |
| Loading & Playback | 4 | 4 | 8 |
| Properties | 7 | 9 + hasplayer | 17 |
| Position & Size | — | 10 | 10 |
| Events | 2 | 8 | 10 |
| Utility | — | 1 (clear) | 1 |
| Total | 19 | 39 | 58 |
Cross-Platform Support
Plan9Basic delegates media decoding to the host operating system's native media services. This provides the best performance but means supported formats vary by platform.
| Platform | Best Video Formats | Best Audio Formats | Notes |
|---|---|---|---|
| Windows | WMV, AVI | WMA, MP3, WAV | MP4 may work depending on codecs |
| Linux | Depends on system codecs | MP3, OGG, WAV | Install multimedia codecs for broader support |
| Android | MP4 (H.264), 3GP, WebM | AAC, MP3 | Standard mobile codecs |
http://, https://). Video files should use local paths — URL streaming for video has not been reliably tested.Cross-Platform Recommendations
| Strategy | Description |
|---|---|
| Use MP3 for audio | Works reliably on all platforms, supports URL streaming |
| Provide multiple video formats | Load the appropriate format based on detected platform |
| Test on target platforms | Codec support varies; always verify playback on each target |
Media States
Both components use the same state values, returned by media_state() and media_ctrl_state().
| Value | State | Description |
|---|---|---|
| 0 | Unavailable | No media loaded or an error occurred |
| 1 | Stopped | Media is loaded but not playing |
| 2 | Playing | Media is currently playing |
| 3 | Paused | Playback is paused (platform-dependent) |
Error Handling
| Function | Signature | Description |
|---|---|---|
media_error() | media_error@ | Last error code (0 = no error) |
media_errormsg$() | media_errormsg$@ | Last error message as string |
media_strerror$(code) | media_strerror$@n | Description for a given error code |
media_clearerror() | media_clearerror@ | Clear the error state |
| Code | Error | Description |
|---|---|---|
| 0 | ERR_NONE | No error |
| 1 | ERR_INVALID_PLAYER | Invalid media player pointer |
| 2 | ERR_INVALID_CONTROL | Invalid media control pointer |
| 3 | ERR_INVALID_PARENT | Invalid parent for control |
| 4 | ERR_INVALID_VALUE | Invalid parameter value |
| 5 | ERR_CREATE_FAILED | Failed to create component |
| 6 | ERR_LOAD_FAILED | Failed to load media file |
| 7 | ERR_FILE_NOT_FOUND | Media file not found |
| 8 | ERR_NOT_LOADED | No media is loaded |
| 9 | ERR_INVALID_CALLBACK | Invalid callback function |
OnStateChanged.Media Player (Audio)
A non-visual component for background audio playback. Does not require a form — works entirely in the background.
Creation & Destruction
| Function | Signature | Description |
|---|---|---|
media_player#() | media_player#@ | Create a new media player (returns pointer) |
media_free(player#) | media_free@# | Destroy media player and release resources |
Loading & Playback
| Function | Signature | Description |
|---|---|---|
media_load#(player#, file$) | media_load#@#$ | Load media file (local path or URL) |
media_play(player#) | media_play@# | Start or resume playback |
media_pause(player#) | media_pause@# | Pause playback |
media_stop(player#) | media_stop@# | Stop playback and reset position to beginning |
media_clear(player#) | media_clear@# | Clear the loaded media |
Properties
| Function | Signature | Description |
|---|---|---|
media_state(player#) | media_state@# | Current state (0=unavailable, 1=stopped, 2=playing, 3=paused) |
media_volume(player#) | media_volume@# | Get volume (0.0–1.0) |
media_volume#(player#, vol) | media_volume#@#n | Set volume |
media_duration(player#) | media_duration@# | Total duration in seconds |
media_position(player#) | media_position@# | Current playback position in seconds |
media_position#(player#, secs) | media_position#@#n | Seek to position in seconds |
media_filename$(player#) | media_filename$@# | Get the loaded filename/URL |
media_isplaying(player#) | media_isplaying@# | Returns 1 if playing, 0 otherwise |
Events
| Event Setter | Callback Signature | When It Fires |
|---|---|---|
media_onend#(player#, func$) | function name(sender#) | When playback reaches the end |
media_onstatechanged#(player#, func$) | function name(sender#, state) | When state changes (0/1/2/3) |
' Complete audio player example let player# = media_player#() media_load#(player#, "https://www.w3schools.com/html/horse.mp3") media_volume#(player#, 0.7) media_onend#(player#, "OnEnd") media_onstatechanged#(player#, "OnState") media_play(player#) function OnEnd(sender#) println "Playback finished!" endfunction function OnState(sender#, state) if state = 2 then println "Playing" if state = 1 then println "Stopped" endfunction
Media Control (Video)
A visual component that displays video content within a form. Supports both audio and video playback with a visible rendering surface.
Creation & Destruction
| Function | Signature | Description |
|---|---|---|
media_control#(parent#, x, y, w, h) | media_ctrl_control#@#nnnn | Create media control with position and size |
media_ctrl_free(ctrl#) | media_ctrl_free@# | Destroy media control |
Loading & Playback
| Function | Signature | Description |
|---|---|---|
media_ctrl_load#(ctrl#, file$) | media_ctrl_load#@#$ | Load media file (local path recommended) |
media_ctrl_play(ctrl#) | media_ctrl_play@# | Start or resume playback |
media_ctrl_pause(ctrl#) | media_ctrl_pause@# | Pause playback |
media_ctrl_stop(ctrl#) | media_ctrl_stop@# | Stop playback and reset to beginning |
media_ctrl_clear(ctrl#) | media_ctrl_clear@# | Clear loaded media |
Playback Properties
| Function | Signature | Description |
|---|---|---|
media_ctrl_state(ctrl#) | media_ctrl_state@# | Current state (0–3) |
media_ctrl_volume(ctrl#) | media_ctrl_volume@# | Get volume (0.0–1.0) |
media_ctrl_volume#(ctrl#, vol) | media_ctrl_volume#@#n | Set volume |
media_ctrl_duration(ctrl#) | media_ctrl_duration@# | Total duration in seconds |
media_ctrl_position(ctrl#) | media_ctrl_position@# | Current position in seconds |
media_ctrl_position#(ctrl#, secs) | media_ctrl_position#@#n | Seek to position in seconds |
media_ctrl_filename$(ctrl#) | media_ctrl_filename$@# | Get loaded filename |
media_ctrl_isplaying(ctrl#) | media_ctrl_isplaying@# | Returns 1 if playing, 0 otherwise |
media_ctrl_hasplayer(ctrl#) | media_ctrl_hasplayer@# | Returns 1 if an internal player is available |
Position & Size
| Function | Signature | Description |
|---|---|---|
media_ctrl_x(ctrl#) | media_ctrl_x@# | Get X position |
media_ctrl_y(ctrl#) | media_ctrl_y@# | Get Y position |
media_ctrl_width(ctrl#) | media_ctrl_width@# | Get width |
media_ctrl_height(ctrl#) | media_ctrl_height@# | Get height |
media_ctrl_pos#(ctrl#, x, y) | media_ctrl_pos#@#nn | Set position |
media_ctrl_size#(ctrl#, w, h) | media_ctrl_size#@#nn | Set size |
media_ctrl_bounds#(ctrl#, x, y, w, h) | media_ctrl_bounds#@#nnnn | Set position and size in one call |
media_ctrl_visible(ctrl#) | media_ctrl_visible@# | Get visibility (0/1) |
media_ctrl_visible#(ctrl#, n) | media_ctrl_visible#@#n | Set visibility |
media_ctrl_enabled(ctrl#) | media_ctrl_enabled@# | Get enabled state (0/1) |
media_ctrl_enabled#(ctrl#, n) | media_ctrl_enabled#@#n | Set enabled state |
media_ctrl_align(ctrl#) | media_ctrl_align@# | Get alignment |
media_ctrl_align#(ctrl#, n) | media_ctrl_align#@#n | Set alignment (0=None, 1=Top, 2=Left, 3=Right, 4=Bottom, 9=Client, 11=Center) |
Events
| Event Setter | Callback Signature | When It Fires |
|---|---|---|
media_ctrl_onend#(ctrl#, func$) | function name(sender#) | When playback reaches the end |
media_ctrl_onstatechanged#(ctrl#, func$) | function name(sender#, state) | When state changes (0/1/2/3) |
media_ctrl_onclick#(ctrl#, func$) | function name(sender#) | When the control is clicked |
media_ctrl_ondblclick#(ctrl#, func$) | function name(sender#) | When double-clicked |
media_ctrl_onmousedown#(ctrl#, func$) | function name(sender#, button, x, y) | Mouse button pressed |
media_ctrl_onmouseup#(ctrl#, func$) | function name(sender#, button, x, y) | Mouse button released |
media_ctrl_onmousemove#(ctrl#, func$) | function name(sender#, x, y) | Mouse moved over control |
media_ctrl_onresize#(ctrl#, func$) | function name(sender#) | When the control is resized |
Media Player vs Media Control
| Feature | Media Player (media_*) | Media Control (media_ctrl_*) |
|---|---|---|
| Type | Non-visual (no form needed) | Visual (placed on a form) |
| Video Display | ❌ No | ✅ Yes |
| Audio Playback | ✅ Yes | ✅ Yes |
| URL Loading | ✅ Tested with audio URLs | ❌ Local files recommended |
| Position/Size | N/A | Full position/size/alignment API |
| Mouse Events | N/A | Click, double-click, mouse down/up/move, resize |
| Best For | Background music, sound effects, audio-only | Video playback, multimedia presentations |
Complete Examples
Music Player with Playlist
dim playlist$(5) let currentTrack = 0 playlist$(0) = "track1.mp3" playlist$(1) = "track2.mp3" playlist$(2) = "track3.mp3" playlist$(3) = "track4.mp3" playlist$(4) = "track5.mp3" function PlayNextTrack(sender#) currentTrack = currentTrack + 1 if currentTrack >= 5 then currentTrack = 0 media_load#(player#, playlist$(currentTrack)) media_play(player#) label_text#(lblTrack#, "Track " + str$(currentTrack + 1) + ": " + playlist$(currentTrack)) endfunction function OnPlay(sender#) media_load#(player#, playlist$(currentTrack)) media_play(player#) label_text#(lblTrack#, "Track " + str$(currentTrack + 1) + ": " + playlist$(currentTrack)) endfunction function OnStop(sender#) media_stop(player#) endfunction function OnNext(sender#) PlayNextTrack(player#) endfunction function OnPrev(sender#) currentTrack = currentTrack - 1 if currentTrack < 0 then currentTrack = 4 media_load#(player#, playlist$(currentTrack)) media_play(player#) label_text#(lblTrack#, "Track " + str$(currentTrack + 1) + ": " + playlist$(currentTrack)) endfunction let frm# = form#("Music Player", 400, 150) form_position#(frm#, 4) let lblTrack# = label#(frm#, "Ready") label_bounds#(lblTrack#, 10, 10, 380, 30) button_onclick#(button#(frm#, "Prev", 10, 50, 80, 30), "OnPrev") button_onclick#(button#(frm#, "Play", 100, 50, 80, 30), "OnPlay") button_onclick#(button#(frm#, "Next", 190, 50, 80, 30), "OnNext") button_onclick#(button#(frm#, "Stop", 280, 50, 80, 30), "OnStop") let player# = media_player#() media_volume#(player#, 0.7) media_onend#(player#, "PlayNextTrack") form_show(frm#) while form_visible(frm#) = 1 processmessages() end while
Video Player with Controls
function OnPlay(sender#) media_ctrl_play(video#) endfunction function OnPause(sender#) media_ctrl_pause(video#) endfunction function OnStop(sender#) media_ctrl_stop(video#) trackbar_value#(trkPos#, 0) endfunction function OnVolumeChange(sender#) local vol vol = trackbar_value(trkVol#) / 100 media_ctrl_volume#(video#, vol) endfunction function OnPositionChange(sender#) local dur, pos, t dur = media_ctrl_duration(video#) pos = trackbar_value(trkPos#) t = (pos / 1000) * dur media_ctrl_position#(video#, t) endfunction function OnUpdateUI(sender#) local pos, dur, pct, pm, ps, dm, ds, s$ pos = media_ctrl_position(video#) dur = media_ctrl_duration(video#) if dur > 0 then pct = (pos / dur) * 1000 trackbar_value#(trkPos#, int(pct)) pm = int(pos / 60) : ps = int(pos) mod 60 dm = int(dur / 60) : ds = int(dur) mod 60 s$ = str$(pm) + ":" + right$("0" + str$(ps), 2) s$ = s$ + " / " + str$(dm) + ":" + right$("0" + str$(ds), 2) label_text#(lblStatus#, s$) endif endfunction function OnVideoEnd(sender#) label_text#(lblStatus#, "Playback completed") trackbar_value#(trkPos#, 0) endfunction let frm# = form#("Video Player", 800, 650) form_position#(frm#, 4) let video# = media_control#(frm#, 10, 10, 780, 480) button_onclick#(button#(frm#, "Play", 10, 500, 80, 30), "OnPlay") button_onclick#(button#(frm#, "Pause", 100, 500, 80, 30), "OnPause") button_onclick#(button#(frm#, "Stop", 190, 500, 80, 30), "OnStop") label#(frm#, "Volume:", 300, 505) let trkVol# = trackbar#(frm#, 360, 500, 150, 30) trackbar_max#(trkVol#, 100) trackbar_value#(trkVol#, 80) trackbar_onchange#(trkVol#, "OnVolumeChange") let lblStatus# = label#(frm#, "Ready") label_bounds#(lblStatus#, 10, 540, 780, 25) let trkPos# = trackbar#(frm#, 10, 570, 780, 30) trackbar_max#(trkPos#, 1000) trackbar_onchange#(trkPos#, "OnPositionChange") media_ctrl_onend#(video#, "OnVideoEnd") media_ctrl_load#(video#, "movie.wmv") media_ctrl_volume#(video#, 0.8) let tmr# = timer#() timer_interval#(tmr#, 250) timer_ontimer#(tmr#, "OnUpdateUI") timer_start#(tmr#) form_show(frm#) while form_visible(frm#) = 1 processmessages() end while
Audio Progress with Timer
function UpdateProgress(sender#) local pos, dur, pct, bar$, i pos = media_position(player#) dur = media_duration(player#) pct = 0 if dur > 0 then pct = int((pos / dur) * 100) bar$ = "[" for i = 1 to 20 if i <= pct / 5 then bar$ = bar$ + "=" else bar$ = bar$ + " " endif next bar$ = bar$ + "] " + str$(pct) + "%" label_text#(lblProgress#, bar$) endfunction function OnSongEnd(sender#) timer_stop#(tmr#) label_text#(lblProgress#, "Playback complete!") endfunction function OnStopClick(sender#) timer_stop#(tmr#) media_stop(player#) form_close(frm#) endfunction let frm# = form#("Now Playing", 400, 100) form_position#(frm#, 4) let lblProgress# = label#(frm#, "Loading...") label_bounds#(lblProgress#, 10, 10, 380, 30) label_fontfamily#(lblProgress#, "Consolas") button_onclick#(button#(frm#, "Stop", 160, 50, 80, 30), "OnStopClick") let player# = media_player#() media_load#(player#, "https://www.w3schools.com/html/horse.mp3") media_onend#(player#, "OnSongEnd") media_play(player#) let tmr# = timer#() timer_interval#(tmr#, 500) timer_ontimer#(tmr#, "UpdateProgress") timer_start#(tmr#) form_show(frm#) while form_visible(frm#) = 1 processmessages() end while
Fullscreen Video
function OnDoubleClick(sender#) if form_windowstate(frm#) = 2 then form_windowstate#(frm#, 0) form_borderstyle#(frm#, 1) else form_windowstate#(frm#, 2) form_borderstyle#(frm#, 0) endif endfunction function OnVideoEnd(sender#) form_close(frm#) endfunction let frm# = form#("", 1024, 768) form_windowstate#(frm#, 2) form_borderstyle#(frm#, 0) let video# = media_control#(frm#, 0, 0, 100, 100) media_ctrl_align#(video#, 9) ' Client - fills entire form media_ctrl_load#(video#, "movie.wmv") media_ctrl_ondblclick#(video#, "OnDoubleClick") media_ctrl_onend#(video#, "OnVideoEnd") media_ctrl_play(video#) form_show(frm#) while form_visible(frm#) = 1 processmessages() end while
Troubleshooting
| Problem | Likely Cause | Solution |
|---|---|---|
| “Unsupported media file” | OS cannot decode the format | Use platform-appropriate format (WMV for Windows, MOV/MP4 for macOS/iOS, MP4 for Android) |
| Video loads but doesn't display | Control not visible or zero-sized | Ensure non-zero width/height, control is parented to a visible form |
| State remains 0 after loading | State changes asynchronously | Use OnStateChanged callback; state changes to 1 or 2 after calling play |
| No audio on video playback | Volume too low or no audio track | Check media_ctrl_volume#(ctrl#, 1.0); verify file has an audio track |
| URL streaming not working | Server blocking non-browser requests | Verify URL is accessible; use HTTPS; some servers block programmatic access |
| Playback stutters on mobile | File too large or high bitrate | Use lower bitrate, smaller resolution, or pre-download files |
Best Practices
| Practice | Why |
|---|---|
Use OnStateChanged to verify playback | More reliable than checking error codes after load |
Use MP3 for cross-platform audio | Works reliably on all platforms, supports URL streaming |
| Use a Timer for UI position updates | Don't poll in a loop; use timer_interval#(tmr#, 250) for 4×/second updates |
Set OnEnd for playlist behavior | Automatically advance to next track when playback finishes |
Use media_ctrl_align#(ctrl#, 9) for fullscreen video | Client alignment fills the entire parent container |
Clean up with media_stop + media_free | Always stop playback before freeing to release resources |
Initialize pointers with Pointer#(0) | Good practice for all pointer variables |
| Start volume at 0.7 | Safer than full volume; let the user adjust |
media_stop(player#) before media_free(player#). Freeing a player during active playback may cause issues on some platforms.Quick Reference
Media Player (Audio) — 19 functions
| Function | Signature | Description |
|---|---|---|
media_error / media_errormsg$ / media_strerror$ / media_clearerror | various | Error handling (4) |
media_player#() | media_player#@ | Create audio player |
media_free(player#) | media_free@# | Destroy player |
media_load# / play / pause / stop / clear | various | Playback (5) |
media_state / volume / duration / position / filename$ / isplaying | various | Properties get (6) |
media_volume# / media_position# | various | Properties set (2) |
media_onend# / media_onstatechanged# | various | Events (2) |
Media Control (Video) — 39 functions
| Function | Signature | Description |
|---|---|---|
media_control#(parent#, x, y, w, h) | media_ctrl_control#@#nnnn | Create video control |
media_ctrl_free(ctrl#) | media_ctrl_free@# | Destroy control |
media_ctrl_load# / play / pause / stop / clear | various | Playback (5) |
media_ctrl_state / volume / duration / position / filename$ / isplaying / hasplayer | various | Properties get (7) |
media_ctrl_volume# / media_ctrl_position# | various | Properties set (2) |
media_ctrl_x / y / width / height / pos# / size# / bounds# / visible / enabled / align | various | Position & visual (13) |
media_ctrl_onend# / onstatechanged# / onclick# / ondblclick# / onmousedown# / onmouseup# / onmousemove# / onresize# | various | Events (8) |
58 functions total. Part of the Plan9Basic GUI library system.