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.
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.
| Name | Description |
|---|---|
| FIT | A 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. |
| FILL | A 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. |
| ZOOM | A 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.
| Name | Type | Required? | Description |
|---|---|---|---|
| noOfcolumns | Int | Yes | The number of columns in each sprite image. |
| noOfRows | Int | Yes | The number of rows in each sprite image. |
| keyFrameDuration | Int | Yes | The time (in seconds) that each thumbnail in each sprite image corresponds to. |
| thumbnailWidth | Int | Yes | The width (in pixels) of each thumbnail in each sprite image. |
| thumbnailHeight | Int | Yes | The height (in pixels) of each thumbnail in each sprite image. |
| isPrefetchRequired | Boolean? | No | Indicates whether the sprite images should be downloaded even before the user starts seeking, which can help in smoother performance. Default value is True. |
| shouldDeleteFromDiskOnExit | Boolean? | No | Indicates 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
)
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.
| Name | Type | Required? | Description | Default Value |
|---|---|---|---|---|
| spriteURL | String | Yes | The 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 |
| maxConcurrentThumbnailDownloads | Int? | No | The maximum number of concurrent sprite image downloads allowed. | Number of processors on the device |
| thumbnailPreviewConfig | ThumbnailPreviewConfig? | No | The 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.
| Parameter | Type | Description |
|---|---|---|
| positionMs | Long | The position of the playhead for which the thumbnail is to be retrieved, in milliseconds. |
| thumbnailHandler | (Bitmap?) -> Unit | The 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
getThumbnailfunction runs on the IO thread. Therefore, you should always handle UI updates on the Main thread. - Error Handling: The
thumbnailHandlermay 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)
}
- 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.
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.