Skip to main content

Basic player configuration

Set VOD playback start position

You can set the initial time (in milliseconds) from which the player should start playback using PlaybackProperties. Pass this object to the PlayerBuilder.

const val INITIAL_PLAYBACK_TIME_MS = 0L // use preferred value here

val playbackPropertiesBuilder = PlaybackProperties.Builder()
playbackPropertiesBuilder.initialStartTimeMs(INITIAL_PLAYBACK_TIME_MS)

val playbackProperties = playbackPropertiesBuilder.build()

val player = PlayerBuilder()
.mediaURL(url)
.mediaType(MediaType.DASH)
.drmScheme(DRMScheme.NONE)
.playbackProperties(playbackProperties)
.build(applicationContext)

For tweaking start position in live streams, refer to Advanced player configuration.

Trick play

You can set the factor by which playback should be sped up.

/**
* The factor by which playback should be sped up.
* Must be greater than zero.
* 1.0f indicates normal speed.
*/
player.playbackRateFactor = 2.0f // Renders in 2X mode.

Configure forward and back seeking time interval

seeking forward

Seeks forward in the current content by Seek interval. ExoPlayer's default value of 15000 milliseconds is used if you don't provide an override value.

    const val DEFAULT_SEEK_FORWARD_INCREMENT_MS = 15000L // default value

val playbackPropertiesBuilder = PlaybackProperties.Builder()
playbackPropertiesBuilder.preferredSeekForwardIncrement(DEFAULT_SEEK_FORWARD_INCREMENT_MS)

val playbackProperties = playbackPropertiesBuilder.build()

val player = PlayerBuilder()
.mediaURL(url)
.mediaType(MediaType.DASH)
.drmScheme(DRMScheme.NONE)
.playbackProperties(playbackProperties)
.build(applicationContext)

seeking back

Seeks back in the current content by Seek interval. ExoPlayer's default value of 5000 milliseconds is used if you don't provide an override value.

    const val DEFAULT_SEEK_BACK_INCREMENT_MS = 5000L // default value

val playbackPropertiesBuilder = PlaybackProperties.Builder()
playbackPropertiesBuilder.preferredSeekForwardIncrement(DEFAULT_SEEK_BACK_INCREMENT_MS)

val playbackProperties = playbackPropertiesBuilder.build()

val player = PlayerBuilder()
.mediaURL(url)
.mediaType(MediaType.DASH)
.drmScheme(DRMScheme.NONE)
.playbackProperties(playbackProperties)
.build(applicationContext)

Subtitle positioning

You can change the vertical position of subtitles using the subtitleBottomMarginPosition attribute.

// value below specifies bottom margin size to be set.
player.subtitleBottomMarginPosition = 100

The value you set to this attribute is applied as bottom margin (extra space) on the bottom side of the subtitle view to position the subtitle as required. This is typically used to raise the subtitle position above the playback controls when they become visible.

note

This space is outside the subtitle view's bounds. subtitleBottomMarginPosition value should be positive.

Play automatically when ready

You can use the autoPlayOnLoad property of PlaybackProperties to automatically start playing as soon as the player is ready. This is equivalent to calling Player.play() directly without calling Player.load(). The default value is true.

val playbackPropertiesBuilder = PlaybackProperties.Builder()
playbackpropertiesBuilder.autoPlayOnLoad(true) //use preferred value here
// build PlaybackProperties and pass to PlayerBuilder

Resize mode

You can set the aspect ratio of the video output. The default mode is FIT.

NameDescription
FITA resize mode that scales the content to fit within the surface rendering area, while maintaining the aspect ratio of the video source. The entire area is used only if the surface has the same aspect ratio as the video content, otherwise the content scales to the maximum size without cropping and is rendered at the center of the surface.
FILLA resize mode that completely fills the surface rendering area, while maintaining the aspect ratio of the video source. The entire surface rendering area is always used and the video content outside of the surface gets cropped.
ZOOMA resize mode that stretches the video to completely fill the surface rendering area. The entire surface rendering area is always used.
//Set the resize mode of choice
player.resizeMode = ResizeMode.ZOOM

Thumbnail preview

Thumbnail preview is a functionality that lets users see a preview image of the video while seeking.

The Player uses a series of images woven into a sprite from a given URL, which is used to retrieve the corresponding image for a given playhead position.

Setup thumbnail preview

ThumbnailPreviewConfig

ThumbnailPreviewConfig describes the dimensions and duration of each image in a given sprite. You can obtain these values from the keyframeMetadata property of ContentAuthorizationToken. Additionally, it also holds other properties that dictate the image caching strategy.

NameTypeRequired?Description
noOfcolumnsIntYesThe number of columns in each sprite image.
noOfRowsIntYesThe number of rows in each sprite image.
keyFrameDurationIntYesThe time (in seconds) that each thumbnail in each sprite image corresponds to.
thumbnailWidthIntYesThe width (in pixels) of each thumbnail in each sprite image.
thumbnailHeightIntYesThe height (in pixels) of each thumbnail in each sprite image.
isPrefetchRequiredBoolean?NoIndicates whether the sprite images should be downloaded even before the user starts seeking, which can help in smoother performance. Default value is True.
shouldDeleteFromDiskOnExitBoolean?NoIndicates whether the sprite images in the disk cache should be cleared on exiting the player or not. Default value is True.
val keyframeMetadata = contentAuthorizationToken.keyframeMetadata
val isPrefetchRequired = true // Preferred configuration
val shouldDeleteFromDiskOnExit = true // Preferred configuration
val thumbnailPreviewConfig = ThumbnailPreviewConfig(
keyframeMetadata.numberOfColumns,
keyframeMetadata.numberOfRows,
keyframeMetadata.thumbnailHeight,
keyframeMetadata.thumbnailWidth,
(keyframeMetadata.thumbnailFrequency * 1000).toLong(),
isPrefetchRequired, // Optional
shouldDeleteFromDiskOnExit // Optional
)
note

Convert the thumbnailFrequency obtained from KeyframeMetadata from seconds (Int) to milliseconds (Long) before creating the ThumbnailPreviewConfig instance.

Pass information to PlayerBuilder

Thumbnail preview is an optional feature that you can opt into by passing the required information to thumbnailPreviewProperties of the corresponding PlayerBuilder (See Player creation) instance.

NameTypeRequired?DescriptionDefault Value
spriteURLStringYesThe fully formed sprite image URL that combines the endpoint URL and generic name, which includes the flag ~index~. Player calculates the sprite index based on given position and replaces ~index~ with the index to construct the sprite URL.N/A
maxConcurrentThumbnailDownloadsInt?NoThe maximum number of concurrent sprite image downloads allowed.Number of processors on the device
thumbnailPreviewConfigThumbnailPreviewConfig?NoThe ThumbnailPreviewConfig instance that holds the required metadata for sprite images.noOfColumns = 10, noOfRows = 10, thumbnailWidth = 384, thumbnailHeight = 216, keyframeDuration = 6000L, isPrefetchRequired = true, shouldDeleteFromDiskOnExit = true
playerBuilder.thumbnailPreviewProperties(
spriteURL,
maxConcurrentThumbnailDownloads,
thumbnailPreviewConfig
)

Implement thumbnail preview

You can obtain the thumbnail preview image for a particular position by calling the getThumbnail extension API on the Player instance. The API returns the preview image for the given position in the form of a Bitmap.

ParameterTypeDescription
positionMsLongThe position of the playhead for which the thumbnail is to be retrieved, in milliseconds.
thumbnailHandler(Bitmap?) -> UnitThe callback function that returns a Bitmap of the thumbnail or null in case of any error or insufficient memory.

The following example demonstrates how to request and display a thumbnail in response to user interactions with the scrub bar.

var scrubJob: Job? = null
val thumbnailView: View = findViewById(R.id.thumbnail_view) //The UI component to display the thumbnail

scrubJob?.cancel()
scrubJob = thumbnailCoroutineScope.launch(Dispatchers.IO) {
player.getThumbnail(position) { thumbnail ->
withContext(Dispatchers.Main) {
thumbnailView.setImageBitmap(thumbnail) // Display thumbnail as required on the UI thread
}
}
}

This function is typically called in tandem with a scrub bar listener attached to the Player which listens to all the seeking activity from the user.

Best practices

Follow these best practices when implementing thumbnail preview:

  • Coroutine Cancellation: Ensure proper cancellation of previous requests so that new ones are prioritized.
  • Thread Management: The getThumbnail function runs on the IO thread. Therefore, you should always handle UI updates on the Main thread.
  • Error Handling: The thumbnailHandler may return null due to errors or memory constraints. Ensure you have fallback handling for such cases.

Optimizing thumbnail loading on TV devices

For efficient thumbnail loading on TV devices, implement a debounce mechanism to avoid frequent unnecessary calls to getThumbnail(). We recommend a debounce interval of 100 ms.

Debounce implementation for key events
private const val KEY_DOWN_EVENT_DEBOUNCE_TIME = 100L
private var lastKeyDownEventTimestamp = 0L

private fun isEventValid(): Boolean {
val currentTime = System.currentTimeMillis()
return if (currentTime - lastKeyDownEventTimestamp > KEY_DOWN_EVENT_DEBOUNCE_TIME) {
lastKeyDownEventTimestamp = currentTime
true
} else {
false
}
}

override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
if (isEnabled) {
when (keyCode) {
KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_RIGHT -> {
if (isEventValid()) {
// Proceed with getThumbnail() call
return true
}
return true
}
}
}
return super.onKeyDown(keyCode, event)
}
Summary
  • Use getThumbnail(positionMs, thumbnailHandler) to retrieve thumbnails.
  • Always update UI elements on the main thread.
  • Implement coroutine cancellation to prioritize new thumbnail requests.
  • Use debounce logic (recommended: 100 ms) to optimize thumbnail retrieval for TV devices.
note

QuickPlay Player SDK version 7.1.x also supports DASH-IF streams with thumbnail representations. This feature is enabled by default, which means that if the internal player logic detects that the player is using a Dash manifest with AdaptaionSet (for example, as shown below):

<AdaptationSet id="3" mimeType="image/jpeg" contentType="image">
<SegmentTemplate media="$RepresentationID$/tile_$Number$.jpg" duration="100" startNumber="1"/>
<Representation bandwidth="12288" id="thumbnails_320x180" width="3200" height="180">
<EssentialProperty schemeIdUri="http://dashif.org/thumbnail_tile" value="10x1"/>
</Representation>
</AdaptationSet>

Then the player serves the available thumbnails using the getThumbnail(positionMs, thumbnailHandler) API, as is the case when you use Thumbnail Preview functionality. Note that if Thumbnail Preview functionality is enabled, then it's used as a primary method for providing thumbnails.