The SDK provides thumbnail preview support through ThumbnailsAPI.
![]()
Engine overview
ThumbnailsAPI supports two thumbnail engines:
.castlabs: UsesThumbnailsPluginand supports WebVTT/grid loading with local cache..apple: Uses nativeAVPlayer-based preview rendering (renderOn).
PRESTOplaySDK.shared.thumbnails(for:player) defaults to .castlabs.
Setup
Enable plugins during SDK setup.
For castlabs thumbnails:
import PRESTOplay
import CastlabsThumbnails
_ = PRESTOplaySDK.shared.setup("LICENSE", [ThumbnailsPlugin()])
For Apple native player thumbnails, ensure HLSPlugin is enabled.
Basic usage (.castlabs)
Create a player, get thumbnails API, load data, then query images by timestamp:
let player = PRESTOplaySDK.shared.player()
guard let thumbnails = PRESTOplaySDK.shared.thumbnails(for: player, engine: .castlabs),
let vttURL = URL(string: "https://example.com/thumbs.vtt")
else { return }
thumbnails.loadThumbnailsFrom(webVTTtrack: vttURL) { error in
guard error == nil else { return }
if let thumb = thumbnails.getThumbnail(atTimestamp: 12.0) {
// thumb.imageData, thumb.timeStampStart, thumb.timeStampEnd
}
}
getThumbnail(atTimestamp: CMTime) is deprecated. Use Double seconds.
WebVTT thumbnails
WebVTT thumbnail tracks can reference single images or grid regions.
Single image entries:
WEBVTT
00:00:00.000 --> 00:00:09.999
thumb1.png
00:00:10.000 --> 00:00:19.999
thumb2.png
The timestamp needs to be in the exact (HH:)MM:SS.MMM format.
Grid-region entries:
WEBVTT
00:00:00.000 --> 00:00:09.999
thumbsGrid.png#0,0,430,242
00:00:10.000 --> 00:00:19.999
thumbsGrid.png#430,0,430,242
![]()
Absolute URLs are also supported in WebVTT entries. For bundled local WebVTT file please make sure all the grid images are also bundled at the same path as WebVTT file (or relative to):

or are a valid Url to the image:
WEBVTT
00:00:00.000 --> 00:00:10.000
https://demo.castlabs.com/media/Route66/dash/thumbs/1_7.jpg#xywh=0,0,262,180
00:00:10.000 --> 00:00:20.000
https://demo.castlabs.com/media/Route66/dash/thumbs/1_7.jpg#xywh=262,0,262,180
00:00:20.000 --> 00:00:30.000
https://demo.castlabs.com/media/Route66/dash/thumbs/1_7.jpg#xywh=524,0,262,180
Master grid thumbnails
You can define grid-based thumbnails without WebVTT by using GridThumbnail.
Explicit template:
let grid = GridThumbnail(
baseUrl: URL(string: "https://demo.cf.castlabs.com/media/Route66/dash/thumbs")!,
pathTemplate: "$index$_7.jpg",
gridHeight: 10,
gridWidth: 10,
durationMs: 10000,
maxIndex: 7
)
Single URL pattern (max index derived from .../$index$_N.ext):
let grid = GridThumbnail(
baseUrl: URL(string: "https://demo.cf.castlabs.com/media/Route66/dash/thumbs/$index$_7.jpg")!,
gridHeight: 10,
gridWidth: 10,
durationMs: 10000
)
Load grid thumbnails:
thumbnails.loadThumbnailsFrom(gridThumbnail: grid) { error in
// Handle completion
}
Cache management
Loaded WebVTT/grid resources are cached on device storage for reuse (including offline reuse).
thumbnails.deleteLocalCache()
Apple TV player - ApplePlayerViewController
To display thumbnails on the Apple TV player, I-Frames are required in the HLS manifest file.
# I-Frame Playlists
#EXT-X-I-FRAME-STREAM-INF:AVERAGE-BANDWIDTH=241218,BANDWIDTH=1074106,CODECS="avc1.4D401F",RESOLUTION=1280x720,URI="media-1/iframes.m3u8"
Native Apple thumbnails (.apple) - Experimental!
Based on AVPlayer track selection - AVPlayer decides whether to use I-Frames tracks or current playback track to form thumbnails. To display thumbnails a dedicated UIView is needed.
For native preview rendering:
let thumbnails = PRESTOplaySDK.shared.thumbnails(for: player, engine: .apple)
DispatchQueue.main.async {
thumbnails?.getThumbnail(atTimestamp: 10.0, renderOn: thumbnailView) { error in
// Optional completion
}
}
IFrame track requirements are described in the following documents: