ExoPlayerAdapter

Media3/ExoPlayer implementation of the portable PlayerAdapter (P4b-2).

Wraps an ExoPlayer the integrator owns (created and attached to their own PlayerView); the SDK only ever drives the content player through this adapter, keeping the brain player-agnostic. A single Player.Listener fans Media3 callbacks out to the adapter's PlayerAdapterEvent handlers.

Thread safety

ExoPlayer is thread-affine: every access must happen on its application thread (ExoPlayer.applicationLooper, normally the main thread) or it throws IllegalStateException: Player is accessed on the wrong thread.

The SDK cannot honour that from the caller's side. BreakScheduler.tick() reads currentTime on whatever thread drives the ticker, and both OptiViewAds's default scope and a plainly-constructed CoroutineSchedulerTicker use Dispatchers.Default — so ticking crashed on the very first tick unless the integrator happened to pass a Dispatchers.Main scope (which is the only reason the demo apps worked).

This adapter therefore makes itself safe from ANY thread:

  • Reads (currentTime, duration, paused, muted, volume, programDateTime) are served from a @Volatile snapshot refreshed ON the application thread — after every listener callback and on a periodic poll (position advances between callbacks, so events alone are not enough).

  • Commands (pause, play, seek, load, and the muted/volume setters) run inline when already on the application thread, and are posted to it otherwise.

Constructors

Link copied to clipboard
constructor(player: ExoPlayer)

Properties

Link copied to clipboard
open override val currentTime: Double

PlayerAdapter.currentTime from the application-thread snapshot, in seconds.

Link copied to clipboard
open override val duration: Double

PlayerAdapter.duration from the snapshot, in seconds; the live window length for live streams.

Link copied to clipboard
open override val isLive: Boolean?

PlayerAdapter.isLive from the snapshot: whether the current Media3 window is live.

Link copied to clipboard
open override var muted: Boolean

PlayerAdapter.muted: reads the snapshot, writes on the application thread.

Link copied to clipboard
open override val paused: Boolean

PlayerAdapter.paused from the snapshot.

Link copied to clipboard
open override var playbackRate: Double

Playback speed, read from the ENGINE rather than echoed back from the last write.

Link copied to clipboard

True on live: media3's getCurrentPosition() is relative to the current Timeline.Window, whose start advances as the live window slides. See the contract for what that breaks.

Link copied to clipboard
open override val programDateTime: Long?

Wallclock of the current playhead (epoch ms) for HLS EXT-X-PROGRAM-DATE-TIME matching, derived from the live window's start time plus the position in the window. Null when the timeline carries no wallclock anchor (VOD/no PDT).

Link copied to clipboard
open override var volume: Double

PlayerAdapter.volume (0.0 to 1.0): reads the snapshot, writes on the application thread.

Functions

Link copied to clipboard
open override fun contentVideoSize(): ContentVideoSize?

ContentVideoSizeProvider: lets the renderer confine break layouts to the video.

Link copied to clipboard
open override fun destroy()

PlayerAdapter.destroy: detaches from the player and stops the snapshot poll; the player itself is left to the app.

Link copied to clipboard
open override fun load(url: String)

PlayerAdapter.load: sets url as the player's media item and prepares it, on the application thread.

Link copied to clipboard
open override fun off(event: PlayerAdapterEvent, handler: PlayerAdapterEventHandler)

PlayerAdapter.off: removes a handler added with on.

Link copied to clipboard
open override fun on(event: PlayerAdapterEvent, handler: PlayerAdapterEventHandler)
Link copied to clipboard
open override fun pause()

PlayerAdapter.pause on the application thread.

Link copied to clipboard
open override fun play()

PlayerAdapter.play on the application thread.

Link copied to clipboard
open override fun seek(time: Double)

Seek the content player, snapping to the nearest sync sample.

Link copied to clipboard
open override fun seekExact(time: Double)

SDK-owned post-break resume seek: keyframe-aligned via CLOSEST_SYNC, the same parameters as seek.

Link copied to clipboard
open override fun setVideoQuality(quality: VideoQuality?): Boolean

Pin to the cheapest rendition with setMaxVideoBitrate, not by selecting a track.