FastPix player for Android

Integrate FastPix Android Player SDK for Media3-based video streaming with security and analytics.

The FastPix Android Player SDK is a full-featured, Media3-based playback library for Android apps. Built on top of ExoPlayer, it supports live and on-demand video streaming, playback security, analytics, track switching, and resolution control, giving you everything you need to deliver reliable, branded, and insight-rich video playback inside your Android applications.

You can use the GitHub repository for FastPix Android Player.


Prerequisites

Before you start, make sure you meet the following development and FastPix setup requirements.

Android environment

  • Android Studio: Arctic Fox or later
  • Android SDK: Version 24+
  • FastPix Player dependency for Player
  • FastPix ExoPlayer SDK (Media3-compatible) as a dependency
  • GitHub Personal Access Token (PAT) for private Maven access

FastPix requirements

Complete the following setup steps in the FastPix Dashboard before integrating the SDK:

  1. Login to FastPix Dashboard: Go to dashboard.fastpix.io and sign in.
  2. Create Media: Use the UI or API to create a media asset (via push or pull URL).
  3. Get Playback ID: Go to View Media, select your uploaded file, and copy the playbackId.
  4. Use the Playback ID: This will be required to generate the playback URL in your app.

Install and configure the SDK

Open your android studio project

Open the project where you want to integrate the SDK.

Add the FastPix player SDK dependency

Navigate to your app-level build.gradle file (or build.gradle.kts if using Kotlin DSL) and add the following under the dependencies section:

dependencies {
    // Check for latest version
    implementation("io.fastpix.player:android:1.0.5")
}

Add the GitHub Maven repository

Navigate to your settings.gradle (or settings.gradle.kts) file. You will need a GitHub Personal Access Token (PAT) to access the private Maven package.

Recommended: Load credentials from local.properties so you do not hardcode secrets.

Add the following lines inside the repositories section:

repositories {
    maven {
        url = uri("https://maven.pkg.github.com/FastPix/fastpix-android-player")
        credentials {
            gpr.user=<your_github_username>
            gpr.key=<your_github_pat>
        }
    }
}

Sync your project with gradle files

Click Sync Now in the notification bar to download and integrate the FastPix Player SDK. Once the dependency is added, you can use the FastPix Player SDK module in any part of your Android project.


Import the SDK

Required imports

import io.fastpix.media3.PlayerView
import io.fastpix.media3.PlaybackListener
import io.fastpix.media3.core.FastPixPlayer
import io.fastpix.media3.core.PlaybackResolution
import io.fastpix.media3.core.RenditionOrder
import io.fastpix.media3.core.StreamType
import androidx.media3.common.MediaItem
import androidx.media3.common.PlaybackException

Global declarations

You can either:

  • Use PlayerView only (it auto-creates a FastPixPlayer with default settings), or
  • Create a configured FastPixPlayer yourself (loop, autoplay, seek preview, analytics, etc.) and attach it to PlayerView.

A global declaration ensures these objects can be accessed throughout the lifecycle of your Activity/Fragment.

class CustomizedPlayerActivity : AppCompatActivity() {
    private lateinit var binding: ActivityCustomizedPlayerBinding
    private val playerView get() = binding.playerView
    private var player: FastPixPlayer? = null
    private lateinit var playbackListener: PlaybackListener
}

Activity setup with playback

The following example demonstrates a complete Activity setup including player initialization, listener registration, and resource cleanup:

import io.fastpix.media3.PlayerView
import io.fastpix.media3.PlaybackListener
import io.fastpix.media3.core.FastPixPlayer
import io.fastpix.media3.core.PlaybackResolution
import androidx.media3.common.MediaItem
import androidx.media3.common.PlaybackException

class CustomizedPlayerActivity : AppCompatActivity() {
    private lateinit var binding: ActivityCustomizedPlayerBinding
    private val playerView get() = binding.playerView
    private var player: FastPixPlayer? = null
    private lateinit var playbackListener: PlaybackListener

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityCustomizedPlayerBinding.inflate(layoutInflater)
        setContentView(binding.root)
        // Create a configured player (optional; PlayerView can also auto-create one).
        player = FastPixPlayer.Builder(this)
            .setAutoplay(true)
            .setLoop(false)
            .build()
        playerView.player = player
    }

    override fun onStart() {
        super.onStart()
        startPlayback()
    }

    private fun startPlayback() {
        playbackListener = object : PlaybackListener {
            override fun onPlay() {}
            override fun onPause() {}
            override fun onPlaybackStateChanged(isPlaying: Boolean) {}
            override fun onError(error: PlaybackException) {}
        }
        player?.addPlaybackListener(playbackListener)
        // Option A: Direct URL playback
        // player?.setMediaItem(MediaItem.fromUri("https://example.com/video.m3u8"))
        // Option B: FastPix playback via playbackId (recommended)
        player?.setFastPixMediaItem {
            playbackId = "YOUR_PLAYBACK_ID"
            maxResolution = PlaybackResolution.FHD_1080
            // playbackToken = "YOUR_PLAYBACK_TOKEN" // optional (secure playback)
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        player?.removePlaybackListener(playbackListener)
        if (isFinishing) {
            // Forces release even if config-change retention is enabled on the view.
            playerView.release()
        }
    }
}

Key points about this setup:

  • CustomizedPlayerActivity uses view binding (ActivityCustomizedPlayerBinding) to access PlayerView from the layout.
  • A FastPixPlayer instance is created once and attached to PlayerView.
  • A PlaybackListener observes playback state and errors.
  • setFastPixMediaItem { ... } generates the playback URL using playbackId plus optional parameters.
  • Call playerView.release() in onDestroy() when the Activity is finishing to release resources.

Playback setup and use cases

The SDK supports several playback configurations depending on your security, quality, and routing requirements. The following sections cover each major scenario.

Public media playback (with stream type)

private fun startPlayback() {
    player?.setFastPixMediaItem {
        playbackId = "YOUR_PLAYBACK_ID" // mandatory
        // streamType = StreamType.onDemand // reserved for future use
    }
}

Stream type notes:

  • setFastPixMediaItem { playbackId = ... } resolves a FastPix HLS stream URL: https://stream.fastpix.io/{playbackId}.m3u8
  • If you already have a playback URL (VOD or live), use direct URL playback via setMediaItem(MediaItem.fromUri(url)).
  • StreamType is currently included in the builder for API compatibility but is not yet applied to the resolved URL in this SDK version.

Secure playback (token-based)

private fun startPlayback() {
    player?.setFastPixMediaItem {
        playbackId = "YOUR_PLAYBACK_ID" // mandatory
        playbackToken = "SET_PLAYBACK_TOKEN" // optional
    }
}

Token benefits:

  • Unique playback ID: Identifies each video asset individually for secure and trackable playback.
  • Signed token authentication: Grants access only if a valid, cryptographically signed token is present in the playback URL.
  • Time-limited access: Tokens can include expiration timestamps to allow playback only within a defined time window.
  • User-specific control: Tokens can be generated per user or session, restricting access based on identity or device.
  • Prevents unauthorized sharing: Signed tokens stop others from reusing the URL, protecting content from piracy and hotlinking.
  • Customizable restrictions: Tokens can include rules like IP restrictions, usage limits, or geo-blocking for enhanced security.

Playback resolution control

private fun startPlayback() {
    player?.setFastPixMediaItem {
        playbackId = "YOUR_PLAYBACK_ID" // mandatory
        minResolution = PlaybackResolution.LD_480
        maxResolution = PlaybackResolution.FHD_1080
        // Or force a fixed value:
        // resolution = PlaybackResolution.HD_720
    }
}

Benefits:

  • Min/max resolution: Set quality boundaries to manage bandwidth and device performance.
  • Fixed resolution: Force playback at a specific resolution (for example, always 720p).
  • Adaptive range: Let the player switch between defined min–max levels (for example, 480p to 1080p) based on network.
  • Optimized playback: Achieve smoother streaming by balancing quality and performance.
  • Full control: Tailor video quality to user settings or app-specific requirements.

Rendition order (quality preference)

Define which resolutions should be preferred during adaptive playback (for example, prefer HD over SD when available). You can align playback quality with user-selected settings (like "Data Saver," "Auto," or "High Quality"). The player intelligently switches resolutions based on real-time network and device conditions, while respecting the set priorities. This also helps avoid sudden quality drops or unnecessary resolution changes, maintaining smoother playback.

private fun startPlayback() {
    player?.setFastPixMediaItem {
        playbackId = "YOUR_PLAYBACK_ID" // mandatory
        renditionOrder = RenditionOrder.Descending
    }
}

Modes:

  • Ascending: 144p → 360p → 720p → 1080p (quick startup)
  • Descending: 1080p → 720p → 360p → 144p (best-quality first)

Custom domain support

private fun startPlayback() {
    player?.setFastPixMediaItem {
        playbackId = "YOUR_PLAYBACK_ID" // mandatory
        customDomain = "stream.yoursite.com" // optional (defaults to stream.fastpix.io)
    }
}

Why use a custom domain:

  • Stream videos directly from your own domain (for example, stream.yoursite.com) instead of default CDN URLs.
  • Supports public and private content.
  • You can use signed playback tokens for private videos to restrict access to authorized users only.
  • Manage who can view your content with minimal setup using domain and token-based security.
  • Hosting via your custom domain can improve performance and ensure better branding consistency.
  • Seamless transition for white-labeled apps.

Full configuration example

The complete example below includes listener setup, URL generation, player configuration, seek preview, and analytics:

import io.fastpix.media3.PlayerView
import io.fastpix.media3.PlaybackListener
import io.fastpix.media3.analytics.AnalyticsConfig
import io.fastpix.media3.core.FastPixPlayer
import io.fastpix.media3.core.PlaybackResolution
import io.fastpix.media3.core.RenditionOrder
import io.fastpix.media3.seekpreview.models.PreviewFallbackMode
import io.fastpix.media3.seekpreview.models.SeekPreviewConfig
import io.fastpix.data.domain.model.VideoDataDetails
import androidx.media3.common.PlaybackException

class CustomizedPlayerActivity : AppCompatActivity() {
    private lateinit var binding: ActivityCustomizedPlayerBinding
    private val playerView get() = binding.playerView
    private var player: FastPixPlayer? = null
    private lateinit var playbackListener: PlaybackListener

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityCustomizedPlayerBinding.inflate(layoutInflater)
        setContentView(binding.root)

        val seekPreviewConfig = SeekPreviewConfig.Builder()
            .setEnabled(true)
            .setFallbackMode(PreviewFallbackMode.TIMESTAMP)
            .setEnablePreload(true)
            .setPreloadRadius(1)
            .setCacheEnabled(true)
            .build()

        val analyticsConfig = AnalyticsConfig.Builder(playerView, "YOUR_WORKSPACE_ID")
            .setVideoDataDetails(VideoDataDetails(videoId = "videoId", videoTitle = "videoTitle"))
            .setEnabled(true)
            .build()

        player = FastPixPlayer.Builder(this)
            .setAutoplay(true)
            .setLoop(false)
            .setSeekPreviewConfig(seekPreviewConfig)
            .setAnalyticsConfig(analyticsConfig)
            .build()
        playerView.player = player
    }

    override fun onStart() {
        super.onStart()
        startPlayback()
    }

    private fun startPlayback() {
        playbackListener = object : PlaybackListener {
            override fun onPlay() {}
            override fun onPause() {}
            override fun onPlaybackStateChanged(isPlaying: Boolean) {}
            override fun onError(error: PlaybackException) {}
        }
        player?.addPlaybackListener(playbackListener)
        val ok = player?.setFastPixMediaItem {
            playbackId = "YOUR_PLAYBACK_ID"
            minResolution = PlaybackResolution.LD_480
            maxResolution = PlaybackResolution.FHD_1080
            renditionOrder = RenditionOrder.Descending
            playbackToken = "SET_PLAYBACK_TOKEN"
            customDomain = "SET_CUSTOM_DOMAIN"
        } ?: false
        if (!ok) return
    }

    override fun onStop() { super.onStop() }
    override fun onPause() { super.onPause() }

    override fun onDestroy() {
        super.onDestroy()
        player?.removePlaybackListener(playbackListener)
        if (isFinishing) {
            playerView.release()
        }
    }
}

SDK API reference

This section documents the SDK's primary API surface and how to use it.

Player objects

  • io.fastpix.media3.PlayerView: UI component for XML layouts. It renders video and owns a FastPixPlayer instance.
  • io.fastpix.media3.core.FastPixPlayer: The core player (wrapping Media3 ExoPlayer). This is where most controls and callbacks live.

Typical pattern:

val player = FastPixPlayer.Builder(context)
    .setAutoplay(true)
    .setLoop(false)
    .build()
playerView.player = player

Media setup (FastPix streams vs. direct URL)

FastPix playback (Recommended)

Use this when you have a playbackId from the FastPix dashboard.

val ok = player.setFastPixMediaItem {
    playbackId = "YOUR_PLAYBACK_ID" // required
    // Optional playback security:
    playbackToken = "YOUR_PLAYBACK_TOKEN"
    // Optional custom domain:
    customDomain = "stream.yoursite.com" // default: stream.fastpix.io
    // Optional quality controls:
    minResolution = PlaybackResolution.LD_480
    maxResolution = PlaybackResolution.FHD_1080
    // resolution = PlaybackResolution.HD_720 // fixed quality
    renditionOrder = RenditionOrder.Descending
    // StreamType is currently reserved for future use:
    // streamType = StreamType.onDemand
}
if (!ok) {
    // Validation failed (for example, empty playbackId). Details arrive via PlaybackListener.onError().
}

Direct URL playback

Use this when you already have a URL (HLS .m3u8, DASH, progressive MP4, etc.).

player.setMediaItem(MediaItem.fromUri("https://example.com/video.m3u8"))

Playback controls (Play / Pause / Seek / State)

These methods are on FastPixPlayer and are safe to call from the main thread (recommended). They delegate to Media3 internally.

player.play()                     // start/resume
player.pause()                    // pause
player.togglePlayPause()          // toggle
player.seekTo(positionMs = 5_000) // seek to 5s
val isAutoPlay = player.autoplay  // whether playWhenReady is set when ready
player.setPlayWhenReady(true)     // start automatically when ready
val willPlayWhenReady = player.getPlayWhenReady()
val pos = player.getCurrentPosition()
val duration = player.getDuration()
val state = player.getPlaybackState() // Media3: STATE_IDLE/BUFFERING/READY/ENDED

Behavior details:

  • play(): Starts playback if prepared; if buffering/ready, it resumes. Call setMediaItem(...) or setFastPixMediaItem { ... } first.
  • pause(): Pauses if playing.
  • togglePlayPause(): Convenience for tap gestures or a single button.
  • seekTo(ms): Seeks within the current item. Seek events are surfaced via PlaybackListener.onSeekStart and onSeekEnd.
  • getDuration(): Returns 0L when unknown.
  • getPlaybackState(): Media3 playback state. Use with buffering callbacks to show spinners.
  • setPlayWhenReady(true) / autoplay: Use either, depending on whether you want a one-off "start when ready" vs a persistent player-level policy.

Volume and mute / unmute

player.setVolume(0.75f)  // 0.0..1.0
val vol = player.getVolume()
player.mute()
player.unmute()

Behavior details:

  • setVolume(x): Clamps to 0.0 ≤ x ≤ 1.0.
  • mute() / unmute(): mute() stores the previous non-zero volume and unmute() restores it (or defaults to 1.0 if nothing was saved).
  • Device volume buttons: The SDK monitors system volume while you have at least one playback listener attached and fires PlaybackListener.onVolumeChanged(volumeLevel) and PlaybackListener.onMuteStateChanged(isMuted).

Playback speed (rate control)

The SDK supports speeds from 0.25x to 2.0x.

player.setPlaybackSpeed(1.5f)       // picks closest supported speed if needed
val speed = player.getPlaybackSpeed()
val all = player.getAvailablePlaybackSpeeds() // FloatArray
player.fast()                        // next speed (wraps)
player.slow()                        // previous speed (wraps)
player.normalize()                   // back to 1.0x

Listen for speed changes with PlaybackListener.onPlaybackRateChanged(rate).


Playback callbacks (PlaybackListener)

Attach a listener to receive events. All callbacks are invoked on the main thread.

val listener = object : PlaybackListener {
    override fun onPlay() {
        // Playback started/resumed
    }
    override fun onPause() {
        // Playback paused (not called for ended/idle transitions)
    }
    override fun onPlaybackStateChanged(isPlaying: Boolean) {
        // Unified play/pause state changes
    }
    override fun onPlayerReady(durationMs: Long) {
        // Called once per media item when player becomes READY the first time
    }
    override fun onTimeUpdate(currentPositionMs: Long, durationMs: Long, bufferedPositionMs: Long) {
        // Periodic time updates during active playback (default ~500ms)
    }
    override fun onSeekStart(currentPositionMs: Long) {
        // Fired when a seek begins (user scrub or programmatic seekTo)
    }
    override fun onSeekEnd(fromPositionMs: Long, toPositionMs: Long, durationMs: Long) {
        // Fired when seek completes (READY again or discontinuity completes)
    }
    override fun onBufferingStart() {
        // Transition to buffering
    }
    override fun onBufferingEnd() {
        // Transition from buffering to ready
    }
    override fun onVolumeChanged(volumeLevel: Float) {
        // Device volume changed (0.0..1.0)
    }
    override fun onMuteStateChanged(isMuted: Boolean) {
        // Convenience mute state
    }
    override fun onPlaybackRateChanged(rate: Float) {
        // Playback speed updated
    }
    override fun onCompleted() {
        // Reached end of media (still called even if loop=true, before repeating)
    }
    override fun onError(error: PlaybackException) {
        // Playback or validation error
    }
}
player.addPlaybackListener(listener)

Callback semantics (practical notes):

  • onPlayerReady(durationMs): Fires once per media item when the player reaches READY the first time (not on every rebuffer/seek).
  • onTimeUpdate(...): Fires periodically during active playback (default ~500ms) and stops automatically when paused/ended or when no listeners remain.
  • onSeekStart / onSeekEnd: Triggered for both user scrubs and programmatic seekTo calls.
  • onBufferingStart / onBufferingEnd: Emitted on buffering transitions; ideal for showing/hiding a spinner.
  • onCompleted(): Called when reaching the end; if loop = true, still fired before the video repeats.

Remove the listener when you're done:

player.removePlaybackListener(listener)

Seek preview (spritesheet thumbnails)

Seek preview shows thumbnail previews while the user scrubs. When enabled (via FastPixPlayer.Builder.setSeekPreviewConfig(...)), you can wire seek preview into your scrubbing UI:

  • Call sdk.showPreview() when the user starts dragging.
  • Call sdk.loadPreview(positionMs) while dragging (safe to call frequently).
  • Call sdk.hidePreview() when the user stops dragging.

How spritesheets are resolved (FastPix streams)

  • Stream URL: https://stream.fastpix.io/{playbackId}.m3u8
  • Spritesheet metadata: https://images.fastpix.io/{playbackId}/spritesheet.json

If the spritesheet does not exist, or the current media is not a FastPix stream, the SDK follows PreviewFallbackMode:

  • TIMESTAMP: Shows a time label (for example "02:30"); SpritesheetMetadata.bitmap may be null.
  • NONE: Shows nothing.

Enable seek preview during player creation

val seekPreviewConfig = SeekPreviewConfig.Builder()
    .setEnabled(true)
    .setFallbackMode(PreviewFallbackMode.TIMESTAMP) // or NONE
    .setEnablePreload(true)
    .setPreloadRadius(1)
    .setCacheEnabled(true)
    .build()

val player = FastPixPlayer.Builder(context)
    .setSeekPreviewConfig(seekPreviewConfig)
    .build()

Listen for preview lifecycle and frames

player.setSeekPreviewListener(object : SeekPreviewListenerAdapter() {
    override fun onSpritesheetInitialized() {
        // Spritesheet metadata is available and preview can be used
    }
    override fun onSpritesheetFailed(error: Throwable) {
        // Spritesheet unavailable; fallback may still work depending on config
    }
    override fun onPreviewShow() {
        // Show your preview container
    }
    override fun onPreviewHide() {
        // Hide your preview container
    }
    override fun onSpritesheetLoaded(metadata: SpritesheetMetadata) {
        // metadata.bitmap: bitmap for current position (can be null in timestamp fallback)
        // metadata.timestampMs: current seek timestamp (ms)
        // Additional fields: rows/columns/frameWidth/frameHeight/frameCount
        // durationMs/intervalMs (distance between frames)
    }
})

Hook to SeekBar scrubbing

seekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
    override fun onStartTrackingTouch(seekBar: SeekBar) {
        player.showPreview()
    }
    override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
        if (fromUser) {
            player.loadPreview(progress.toLong()) // recommended: progress in ms
        }
    }
    override fun onStopTrackingTouch(seekBar: SeekBar) {
        player.hidePreview()
        player.seekTo(seekBar.progress.toLong())
    }
})

Optional: Request a preview bitmap directly

If you want to render previews yourself (for example, custom UI), you can fetch bitmaps:

// Call from a coroutine
val bmp = player.getPreviewBitmap(timeMs = 30_000)
val label = player.formatTimestamp(timeMs = 30_000)

Audio tracks (discovery and switching)

FastPixPlayer can discover and switch audio tracks (commonly on HLS streams with multiple audio renditions).

What an AudioTrack contains (useful for UI):

  • id: Pass this to setAudioTrack(id).
  • languageCode / languageName / label: Show in your menu.
  • isSelected, isPlayable, isDefault: For UI state.
  • Optional diagnostics: role, channels, codec, bitrate, groupId.

Listen for audio track updates

import io.fastpix.media3.tracks.AudioTrackListener
import io.fastpix.media3.tracks.AudioTrackUpdateReason

player.addAudioTrackListener(object : AudioTrackListener {
    override fun onAudioTracksLoaded(tracks: List<AudioTrack>, reason: AudioTrackUpdateReason) {
        // Populate your UI (language menu, etc.)
        // Each AudioTrack has an id; use it to switch.
    }
    override fun onAudioTracksChange(selectedTrack: AudioTrack) {
        // Update selected UI state
    }
    override fun onAudioTrackSwitching(isSwitching: Boolean) {
        // Optional: show/hide a "switching" indicator
    }
    override fun onAudioTracksLoadedFailed(error: AudioTrackError) {
        // Handle invalid id, player not ready, selection failure, etc.
    }
})

Switch audio track by ID

NOTE:
If a seek is in progress, the SDK defers switching until the seek completes.

val tracks = player.getAudioTracks()
val target = tracks.firstOrNull() ?: return
player.setAudioTrack(target.id)

Example: Build a simple language menu

val tracks = player.getAudioTracks()
val items = tracks.filter { it.isPlayable }.map { t ->
    val title = t.languageName ?: t.label ?: t.languageCode ?: "Unknown"
    title to t.id
}
// Present items in a dialog; on click: player.setAudioTrack(id)

Subtitles (Discovery, Switching, and Cues)

What a SubtitleTrack contains:

  • id : Pass to setSubtitleTrack(id).
  • languageCode / languageName / label
  • isSelected, isPlayable, isDefault, isForced
  • Optional: role, codec, groupId

Listen for subtitle track updates and cue changes

import io.fastpix.media3.tracks.SubtitleTrackListener

player.addSubtitleTrackListener(object : SubtitleTrackListener {
    override fun onSubtitlesLoaded(tracks: List<SubtitleTrack>) {
        // Populate subtitle selection UI
    }
    override fun onSubtitleChange(track: SubtitleTrack?) {
        // track == null means subtitles disabled
    }
    override fun onSubtitlesLoadedFailed(error: SubtitleTrackError) {
        // Track not found, not playable, player not ready, etc.
    }
    override fun onSubtitleCueChange(info: SubtitleRenderInfo) {
        // Optional: observe cues (text + timing) if building a custom subtitle renderer
    }
})

Switch subtitle track by ID, or disable subtitles

val subs = player.getSubtitleTracks()
val target = subs.firstOrNull()
if (target != null) {
    player.setSubtitleTrack(target.id)
} else {
    player.disableSubtitles()
}

Preferred / Default language (auto selection)

These preferences apply automatically when tracks become available, and never override a manual selection:

player.setDefaultAudioTrack(languageName = "English")
player.setDefaultSubtitleTrack(languageName = "Spanish")

NOTE:
Use BCP-47 / ISO language names like "English", "Spanish", "Hindi", "French". If the preferred language does not exist in the stream, the player keeps its normal selection.


PlayerView details (XML integration tips)

If you are using the XML io.fastpix.media3.PlayerView, the following properties are important:

  • Configuration-change survival: By default PlayerView.retainPlayerOnConfigChange = true. For best results, assign an android:id in XML; otherwise the view cannot retain/recover the stored player instance across rotation.
  • Tap gesture: playerView.isTapGestureEnabled = true enables single-tap toggle play/pause using togglePlayPause().
  • Release: Call playerView.release() when the Activity is finishing (onDestroy with isFinishing == true) to force a real release even if retention is enabled.