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.9")
}

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.

DRM (Widevine) Playback

FastPix supports Widevine DRM for protected streams. To enable DRM playback, you must provide both a playbackToken and a drmConfig in the same setFastPixMediaItem call. If playbackToken is set without drmConfig, the player emits a DRM configuration error through PlaybackListener.onError.

Basic DRM setup

import io.fastpix.media3.core.DrmConfig
import io.fastpix.media3.core.FastPixPlayer
import io.fastpix.media3.core.StreamType

val player = FastPixPlayer.Builder(this)
    .setAutoplay(true)
    .build()
binding.playerView.player = player

player.setFastPixMediaItem {
    playbackId = "your-playback-id"
    streamType = StreamType.onDemand   // Use StreamType.live for live streams
    playbackToken = "your-secure-playback-token"
    drmConfig = DrmConfig()            // Defaults to Widevine UUID, multiSession=true
}

Custom DRM options

To override the default Widevine configuration, pass a DrmConfig with explicit parameters:

import androidx.media3.common.C
import io.fastpix.media3.core.DrmConfig

player.setFastPixMediaItem {
    playbackId = "your-playback-id"
    playbackToken = "your-secure-playback-token"
    drmConfig = DrmConfig(
        uuid = C.WIDEVINE_UUID,
        multiSession = true
    )
}

Key points:

  • streamType determines which FastPix DRM license endpoint is used — onDemand or live.
  • DRM playback is only supported on physical Android devices. Test on the minimum API level your app targets.
  • DrmConfig() with no arguments defaults to Widevine UUID with multiSession = true.

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)

Video Quality Switching

These APIs let users select a fixed quality level (for example, 1080p) or return to adaptive bitrate (ABR) mode. They are available on FastPixPlayer and do not reset playback position or recreate the player.

Get available qualities and the current quality

val player = binding.playerView.player as? FastPixPlayer ?: return

// All available quality tracks for the current media
val qualities: List<VideoTrack> = player.getVideoQualities()

// Currently active quality (in ABR mode, reflects the rendition currently rendering)
val current: VideoTrack? = player.getCurrentVideoQuality()

Each VideoTrack exposes: id, width, height, bitrate, label, isSelected, and isAuto.

Switch to a fixed quality

// Pass a track id from getVideoQualities()
player.setVideoQuality(trackId)

Switch back to ABR (auto)

player.enableAutoQuality()

Always include an Auto option in your quality menu that calls enableAutoQuality(). This lets the player resume adaptive switching after the user has selected a fixed rendition.

Listen for quality changes

player.addPlaybackListener(object : PlaybackListener {
    override fun onVideoQualityChanged(
        quality: VideoTrack?,
        source: PlaybackListener.VideoQualityChangeSource
    ) {
        // source = MANUAL  → triggered by setVideoQuality() or enableAutoQuality()
        // source = ABR     → triggered automatically by the SDK
        updateQualityUi(quality, source)
    }
    override fun onPlay() {}
    override fun onPause() {}
    override fun onPlaybackStateChanged(isPlaying: Boolean) {}
    override fun onError(error: PlaybackException) {}
})

Key points:

  • Quality changes are applied after any in-progress seek completes.
  • Use source in onVideoQualityChanged to distinguish between user-initiated and SDK-initiated rendition switches.

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)

Analytics (FastPix Data Core SDK)

The FastPix Android Data Core SDK provides analytics for FastPix playback on Android. It is not a standalone video player. Instead, it integrates with your player and automatically captures playback analytics, including:

  • Playback lifecycle events (play, pause, ready, complete, errors)
  • Buffering behavior and seek patterns
  • Engagement signals and session-level playback usage

The SDK sends collected analytics to your FastPix workspace, where you can monitor them in near real time through the FastPix dashboard. The integration is lightweight and does not interrupt or degrade playback.

What it is for

Use analytics when you want to:

  • Measure viewer engagement and completion trends
  • Detect buffering and quality-of-experience issues
  • Correlate playback behavior with video metadata (for example, title and ID,etc)
  • Monitor player health and failures in production

How to use it

Configure analytics using AnalyticsConfig and pass it to FastPixPlayer.Builder.

import io.fastpix.data.domain.model.VideoDataDetails
import io.fastpix.media3.analytics.AnalyticsConfig
import io.fastpix.media3.core.FastPixPlayer

val videoDataDetails = VideoDataDetails("video-123", "Launch Demo")

val analyticsConfig = AnalyticsConfig.Builder(
    playerView = binding.playerView,         // Required
    workSpaceId = "your-workspace-id"        // Required
)
    .setVideoDataDetails(videoDataDetails) // Optional metadata
    .setEnabled(true) // default is true
    .build()

val fastPixPlayer = FastPixPlayer.Builder(this)
    .setAutoplay(true)
    .setLoop(false)
    .setAnalyticsConfig(analyticsConfig)
    .build()

binding.playerView.player = fastPixPlayer

Notes

  • playerView and workSpaceId are mandatory.
  • videoDataDetails, playerDataDetails, and customDataDetails are optional and can be added based on your use case.
  • If analytics setup fails at runtime, playback continues. Analytics is fail-safe by design.
  • Current analytics APIs are optimized for Java-first Android integration. Kotlin ergonomics and customization options will improve in future releases.

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.

Changelog

All notable changes to the FastPix Player for Android is documented below.

Current verison

[1.0.9]

Added

  • DRM Support (Widevine): SDK-level DRM abstraction for protected content playback

    • Added DrmConfig data class for configuring DRM without exposing Media3 internals
    • Added DrmManager internal manager that builds Media3 DrmConfiguration from SDK config
    • Added drmConfig property in FastPixMediaItemBuilder to enable DRM on a per-media-item basis
    • DRM license URL is auto-constructed from playbackId, playbackToken, and streamType — no manual URL assembly required
    • Widevine UUID and multi-session enabled by default
  • Video Quality Switching: Manual and automatic video quality (rendition) selection

    • Added VideoTrack data model exposing id, width, height, bitrate, label, isSelected, and isAuto
    • Added getVideoQualities() to retrieve available video renditions sorted by resolution
    • Added getCurrentVideoQuality() to get the currently active rendition
    • Added setVideoQuality(trackId: String) to lock playback to a specific quality
    • Added enableAutoQuality() to re-enable adaptive bitrate (ABR) selection
    • Quality switches are deferred during seek operations and applied on seek completion
    • Video track discovery integrated into TrackManager alongside existing audio/subtitle track management

Previous version

[1.0.8]

  • Bumped media3-data version from 1.2.4 to 1.2.5

[1.0.7]

Updates:

  • Bumped media3 version from 1.2.3 to 1.2.4
  • Updated library version to 1.0.7 in README.md, build.gradle.kts, and FastPixPlayerLibraryInfo.kt
  • Added AnalyticsConfig and VideoDataDetails integration in MainActivity
  • Refactored and cleaned up code in MainActivity.kt

[1.0.6] - 2026

Fix

  • Crash Fix on Seek Preview Config

[1.0.5] - 2026

Added

  • Audio Track Switching: Support for discovering and switching between multiple audio tracks

    • Added getAudioTracks() to retrieve available audio tracks
    • Added getCurrentAudioTrack() to get the currently selected track
    • Added setAudioTrack(trackId: String) to switch audio tracks dynamically without interrupting playback
    • Added setDefaultAudioTrack(languageCode: String) to automatically select preferred language
    • Added AudioTrackListener with callbacks:
      • onAudioTracksLoaded()
      • onAudioTracksChange()
      • onAudioTracksLoadedFailed()
      • onAudioTrackSwitching()
  • Subtitle Track Switching: Support for subtitles with dynamic switching and rendering

    • Added getSubtitleTracks() to retrieve available subtitle tracks
    • Added getCurrentSubtitleTrack() to get current subtitle selection
    • Added setSubtitleTrack(trackId: String) to enable specific subtitle track
    • Added disableSubtitles() to turn off subtitles
    • Added setDefaultSubtitleTrack(languageCode: String) for automatic subtitle selection
    • Added SubtitleTrackListener with callbacks:
      • onSubtitlesLoaded()
      • onSubtitleChange()
      • onSubtitlesLoadedFailed()
      • onSubtitleCueChange()
  • Subtitle Rendering Support

    • Introduced SubtitleCueInfo and SubtitleRenderInfo for real-time subtitle rendering
    • Enables custom subtitle UI implementation using cue updates

[1.0.4] - 2026

Added

  • Seek Preview (Spritesheet thumbnails): Thumbnail previews while scrubbing
    • Added SeekPreviewConfig to configure seek preview behavior (enabled, preload, cache, fallback mode)
    • Added PreviewFallbackMode for graceful fallback when thumbnails are unavailable (timestamp/none)
    • Added SeekPreviewListener callbacks: onSpritesheetInitialized, onSpritesheetFailed, onPreviewShow, onPreviewHide, onSpritesheetLoaded
    • Added FastPixPlayer.Builder.setSeekPreviewConfig(config: SeekPreviewConfig?) to enable seek preview at player creation time
    • Added FastPixPlayer.setSeekPreviewListener(listener: SeekPreviewListener?) to receive preview frames
    • Added FastPixPlayer.showPreview(), FastPixPlayer.loadPreview(timeMs), FastPixPlayer.hidePreview() for seek-bar integration
    • Default FastPix spritesheet URL is auto-resolved from the current stream URL (e.g. stream.fastpix.ioimages.fastpix.io + /{playbackId}/spritesheet.json)

[1.0.3] - 2026

Added

  • Volume Control: Comprehensive volume management API

    • Added setVolume(volume: Float) method to set volume level (0.0f to 1.0f)
    • Added getVolume() method to get current volume level
    • Added mute() method to mute playback (saves current volume for restoration)
    • Added unmute() method to restore previous volume level
    • Added onVolumeChanged(volumeLevel: Float) callback in PlaybackListener - triggered when device volume changes via hardware buttons or system controls
    • Added onMuteStateChanged(isMuted: Boolean) callback in PlaybackListener - triggered when mute state changes
    • Automatic device volume monitoring with periodic checks (200ms interval)
    • Volume state is preserved across configuration changes
  • AutoPlay Support: Automatic playback start when media is ready

    • Added setAutoplay(autoplay: Boolean) method in FastPixPlayer.Builder to configure autoplay during player creation
    • Added autoplay property on FastPixPlayer to enable/disable autoplay at runtime
    • When enabled, playback automatically starts when media is ready to play
    • No manual call to play() required when autoplay is enabled
    • Autoplay state is preserved across configuration changes
  • Loop Playback: Seamless looping functionality

    • Added setLoop(loop: Boolean) method in FastPixPlayer.Builder to configure loop during player creation
    • Added loop property on FastPixPlayer to enable/disable looping at runtime
    • When enabled, playback automatically restarts from the beginning when it reaches the end
    • Current media item repeats indefinitely until loop is disabled
    • Loop state is preserved across configuration changes
  • Playback Rate Control: Flexible playback speed adjustment

    • Added setPlaybackSpeed(speed: Float) method to set playback speed to a specific value
    • Added getPlaybackSpeed() method to get current playback speed
    • Added getAvailablePlaybackSpeeds() method to get all available speed options
    • Available playback speeds: 0.25x, 0.5x, 0.75x, 1.0x, 1.25x, 1.5x, 1.75x, 2.0x
    • Added onPlaybackRateChanged(rate: Float) callback in PlaybackListener - triggered when playback speed changes
    • Playback speed automatically adjusts to closest available speed if exact value is not available
    • Playback speed state is preserved across configuration changes

[1.0.2] - 2026

Improved

  • Code Optimization
  • Refactoring

[1.0.1] - 2026

Added

  • Playback Control Methods: Simple and intuitive playback control API

    • Added play() method to start or resume playback
    • Added pause() method to pause playback
    • Added togglePlayPause() method to toggle between play and pause states
    • Added isPlaying() method to check current playback state
  • PlaybackListener Interface: Comprehensive event-driven architecture for playback monitoring

    • Added onPlay() callback - triggered when playback starts or resumes
    • Added onPause() callback - triggered when playback is paused
    • Added onPlaybackStateChanged(isPlaying: Boolean) callback - triggered on play/pause state changes
    • Added onError(error: PlaybackException) callback - triggered when playback errors occur
    • Added onTimeUpdate(currentPositionMs, durationMs, bufferedPositionMs) callback - continuous time updates during playback (similar to HTML5 onTimeUpdate)
      • Invoked approximately every 500ms during active playback
      • Provides current position, total duration, and buffered position
      • Enables real-time UI updates for seek bars and time displays
    • Added onSeekStart(currentPositionMs) callback - triggered when seek operations begin
    • Added onSeekEnd(fromPositionMs, toPositionMs, durationMs) callback - triggered when seek operations complete
    • Added onBufferingStart() callback - triggered when player enters buffering state
    • Added onBufferingEnd() callback - triggered when player exits buffering state
    • All callbacks are invoked on the main thread
    • Optional callbacks have default empty implementations for flexibility
  • Listener Management: Methods for managing playback listeners

    • Added addPlaybackListener(listener: PlaybackListener) method
    • Added removePlaybackListener(listener: PlaybackListener) method
    • Added clearPlaybackListeners() method

Improved

  • Enhanced player API with more intuitive playback control methods
  • Better developer experience for building custom playback UIs with comprehensive callbacks
  • Real-time time updates enable smooth seek bar and time display synchronization
  • Seek operation tracking provides better UX during seeking

Technical Details

  • Playback speed is implemented using Media3's PlaybackParameters API
  • Speed state is preserved across configuration changes
  • All speed methods are available on both PlayerView and the underlying player instance
  • Time updates are automatically managed - start when playback begins, stop when paused/ended
  • Seek callbacks automatically pause time updates during seek operations for better performance

[1.0.0]

Initial Release:

The first public release of the FastPix Android Player SDK, packed with modern playback capabilities for both live and on-demand streaming scenarios.

  • Support for Public & Private Media: Secure token-based playback for private videos and effortless access for public streams.

  • Live & On-Demand Streaming: Adaptive support for both real-time and pre-recorded content with optimized buffering strategies.

  • Audio Track Switching: Dynamically switch between available audio tracks, ensuring accessibility and multi-language support.

  • Subtitle Track Switching: Enhance viewer experience with support for multiple subtitle tracks and on-the-fly switching.

  • QoE & Playback Metrics: Built-in tracking of key Video Quality of Experience (QoE) indicators such as: rebuffer events, bitrate/resolution changes, and startup time — enabling deep insights into playback performance.

  • Custom Playback Resolution: Programmatically set or limit playback resolution to suit user preferences or bandwidth constraints.

  • Stream Type Configuration: Fine-tuned handling of stream types like HLS, DASH, and others — with control over live latency modes and playback strategies.

  • Custom Domain Support: Compatible with FastPix's custom domain system for secure and branded media delivery.

  • Rendition Order Control: Configure and prioritize video renditions based on bitrate, resolution, etc., to ensure predictable quality behavior. Supports both ascending (low ➜ high) and descending (high ➜ low) strategies. Ideal for optimizing playback under bandwidth constraints or enforcing quality-first strategies.