2. Streaming Playback
Explains how to create a KollusPlayerView using a JWT URL and stream content directly to a device in real time.
Unlike playing locally downloaded content, streaming uses a one-time URL freshly issued from the customer's server at each playback.
All example code in this document is based on the official sample app kollus_player_ios.
Content delivery methods
The Kollus service delivers content using one of three delivery methods depending on the channel settings.
At the application client layer, you receive the same JWT URL and build a KollusPlayerView(contentURL:) instance, so no separate code branching is required.
However, since Adaptive Bitrate(ABR) behavior, DRM verification and license issuance flow, and AirPlay compatibility policy differ by delivery method, understanding your infrastructure channel configuration will help with debugging and optimization.
| Delivery method | Default | Description | Channel settings |
|---|---|---|---|
| MP4 Progressive Download | ◯ | Downloads a single MP4 file in real time while playing simultaneously. This is a single-quality method that does not support Adaptive Bitrate(ABR). | No separate setting (default channel) |
| HLS streaming | - | Composed of a manifest (.m3u8) and segment files. Optimized for the iOS AVKit system, it automatically switches quality in real time (ABR) as network bandwidth changes. | Enable HLS output in console channel settings |
| Multi-DRM (FairPlay) | - | Combines the HLS media structure with a FairPlay security license issuance flow. The PallyConFPSSDK component is integrated internally. | Register DRM policy in console channel settings |
In application source code, you can play all three methods simply by calling the KollusPlayerView(contentURL: jwtUrl) constructor.
The actual streaming delivery method is determined by the Kollus server based on the channel settings and delivered to the client.
Detailed delivery method comparison
| Comparison area | MP4 Progressive Download | HLS streaming | Multi DRM |
|---|---|---|---|
| ABR option control | Bandwidth settings have no effect | Applied (follows iOS AVKit default policy) | Applied (follows iOS AVKit default policy) |
| Seek behavior | HTTP Byte-Range request-based control | Position seeking in segment units | Position seeking in segment units |
| Additional framework integration | No external dependencies | No external dependencies | Must include PallyConFPSSDK.framework library build |
| License issuance flow | No license integration | No license integration | Performs FairPlay certificate and SPC data extraction, then CKC exchange |
| AirPlay / external output | External display output allowed | External display output allowed | Output blocking controlled according to content DRM policy |
Most customers start with the MP4 Progressive method in the early stages of service, then switch to HLS streaming when traffic increases or an Adaptive Bitrate(ABR) environment is needed. Afterwards, it is common to additionally separate and operate a Multi DRM (FairPlay) channel only for content that requires copyright protection and DRM security.
Basic streaming playback
Match the infrastructure environment and delegate objects for real-time playback to the KollusPlayerView of the KollusPlayer SDK, then add it to the project UIView hierarchy to start real-time streaming playback.
class PlayerViewController: UIViewController {
var playerView: KollusPlayerView!
// 1. Specify data source (JWT URL)
func play(streamingURL: String) {
DispatchQueue.main.async { [weak self] in
self?.playerView = KollusPlayerView(contentURL: streamingURL)
DispatchQueue.global().async {
self?.initPlayerView()
}
}
}
func initPlayerView() {
// 2. Configure internal environment settings and connect dedicated delegate receiver objects
playerView.debug = false
playerView.storage = StorageManager.shared.storage // Connect KollusStorage
playerView.delegate = self // KollusPlayerDelegate (playback lifecycle)
playerView.DRMDelegate = self // KollusPlayerDRMDelegate (Multi DRM)
playerView.LMSDelegate = self // KollusPlayerLMSDelegate (watch statistics)
playerView.bookmarkDelegate = self // KollusPlayerBookmarkDelegate (bookmark detection)
playerView.scalingMode = .scaleAspectFit
playerView.proxyPort = LICENSE_KEY_PROXY_PORT // Port received when issuing the license key
// 3. Add to UIView hierarchy
view.addSubview(playerView)
playerView.frame = view.bounds
// 4. Prepare for playback
// Using prepareToPlayWithError: is recommended (see [Playback preparation verification] section below)
}
}
KollusPlayerView initialization options
| Initialization constructor | Purpose |
|---|---|
KollusPlayerView(contentURL: String) | Streaming playback (JWT URL) |
KollusPlayerView(mediaContentKey: String) | Offline playback (downloaded content) |
The streaming/offline mode is determined at the time of instance creation. You cannot switch between the two modes with the same instance, so it is common to create a new KollusPlayerView and attach it when changing content.
Specifying proxyPort
The playerView.proxyPort property is the port used by the SDK to handle internal communication.
Specify the proxy port number received along with the key issuance as-is. Setting an arbitrary value will prevent normal streaming playback.
Playback preparation verification
It is recommended to track the playback preparation state of KollusPlayerView through the delegate callback method prepareToPlayWithError: instead of a simple prepareToPlay call.
A simple call makes it difficult to capture precise internal errors triggered during the player initialization phase, but using the prepareToPlayWithError: method receives a specific NSError object at the time of preparation success or failure, enabling specific responses.
func kollusPlayerView(_ playerView: KollusPlayerView,
prepareToPlayWithError error: Error?) {
if let error = error {
// Initialization failed — guide the user based on error code
let nsError = error as NSError
UIApplication.presentErrorViewController(
title: "Playback preparation failed (\(nsError.code))",
errorDescription: nil,
errorReason: error.localizedDescription)
return
}
// Playback preparation complete — integrate control methods to start auto-play or wait for user input.
}
Key player properties
Playback control
The following properties control playback by directly setting (set) their values.
| Property | Type | Description |
|---|---|---|
contentURL | NSString | Streaming playback URL (specified at initialization) |
currentPlaybackTime | NSTimeInterval | Current playback position (set to seek) |
currentPlaybackRate | float | Playback speed (1.0, 1.25, 1.5, 2.0, etc.) |
scalingMode | KollusPlayerContentMode | Screen output mode |
repeatMode | KollusPlayerRepeatMode | Playback loop section mode |
playerContentFrame | CGRect | Player screen area |
AIRateEnable | BOOL | Whether AI Speed is supported |
Settings up to 10x speed are possible, but if the currentPlaybackRate value exceeds 2.0, audio-video sync issues or temporary quality degradation may occur.
Seek and playback control examples
// Seek
playerView.currentPlaybackTime = 60.0 // Move playback position to the 60-second mark
// Playback speed
playerView.currentPlaybackRate = 1.5 // Play at 1.5x speed
// Loop section
playerView.repeatMode = .all // Loop all (.none: off, .one: single content loop)
Status information inquiry
The following properties are read-only and used to check the current player state.
| Property | Description |
|---|---|
isPreparedToPlay | Whether playback preparation is complete |
isPlaying | Whether real-time playback is in progress |
isBuffering | Whether buffering is in progress |
isSeeking | Whether seeking is in progress |
isScrolling | Whether screen scrolling is in progress |
isAudioOnly | Whether the content is audio-only |
naturalSize | Original video size (CGSize) |
screenConnectEnabled | Whether external display output is allowed |
Player lifecycle control
Receive playback lifecycle and hardware-integrated runtime event signals through KollusPlayerDelegate and integrate them with the app's business UI logic.
// 1. Playback preparation complete
func kollusPlayerView(_ playerView: KollusPlayerView,
prepareToPlayWithError error: Error?) {
if let error = error {
// Handle preparation failure
return
}
// Auto-play or wait for user input
}
// 2. Playback started
func kollusPlayerView(_ playerView: KollusPlayerView,
play userInteraction: Bool,
error: Error?) {
// userInteraction == true : Playback started by play() command from the application
// userInteraction == false : Auto-play inside the SDK (e.g., auto-resume after earphone disconnect)
}
// 3. Pause
func kollusPlayerView(_ playerView: KollusPlayerView,
pause userInteraction: Bool,
error: Error?) { }
// 4. Buffering state change
func kollusPlayerView(_ playerView: KollusPlayerView,
buffering: Bool,
prepared: Bool,
error: Error?) {
// buffering == true : Buffering started
// buffering == false : Buffering resolved
}
// 5. Stop
func kollusPlayerView(_ playerView: KollusPlayerView,
stop userInteraction: Bool,
error: Error?) { }
// 6. Seek: called twice in total, before and after seeking
func kollusPlayerView(_ playerView: KollusPlayerView,
position: TimeInterval,
error: Error?) {
// Distinguish before/after using playerView.isSeeking
}
Due to iOS system environment characteristics, the pause delegate method may be called before the buffering: true notification event signal is received.
Therefore, it is safer to explicitly restore the playback state by referencing the application main UI control at the point when the buffering: false status signal is received.
DRM content streaming
If a valid DRM policy is included inside the JWT URL structure, the SDK automatically controls the license parsing and handshaking phases.
- FairPlay (Multi DRM): The
PallyConFPSSDKframework component is integrated with the SDK and handles the entire FairPlay certificate exchange and SPC·CKC license issuance flow. - Kollus DRM: Parses the license without any additional dependency configuration steps.
DRM-related events are delivered through KollusPlayerDRMDelegate. For detailed delegate method specifications, please refer to the interface declarations in the SDK's internal header files.
Player type branching
In the official sample app, the optimal player type is automatically designated through hint metadata received inside the content URL.
In most general environments, it is not necessary to directly coordinate hardware availability check branching code at the development layer every time, and it is recommended to use the SDK's automatic detection.
let strPlayerType = checkPlayerType(streamingURL)
switch strPlayerType {
case "hw": playerType = 0 // Kollus built-in hardware decoder
case "sw": playerType = 1 // Kollus built-in software decoder
case "native": playerType = 2 // iOS AVPlayer
default: break
}
// playerType == 0 or 1 → Assign KollusPlayer
// playerType == 2 → Assign Native AVPlayer
if playerType == 0 || playerType == 1 {
try self.playerView.prepareToPlay(withMode: .PlayerTypeKollus)
} else if playerType == 2 {
try self.playerView.prepareToPlay(withMode: .PlayerTypeNative)
}
LMS (watch statistics) data integration
For content that requires sending watch statistics to a Learning Management System (LMS), the SDK periodically calls LMS callbacks.
Transmission status and statistics processing result feedback are handled through the connected KollusPlayerLMSDelegate callback.
LMS data that fails to be transmitted during offline viewing accumulates in the local KollusStorage storage.
When network connectivity is restored, explicitly calling the storage.sendStoredLms() method will batch-send the accumulated local watch statistics data to the LMS server.
For more information, refer to 8. Download Events/Callbacks.
Background playback
To maintain long-duration background playback, the media background execution permission structure specified by the iOS operating system must be declared in advance.
- Add the
audiooption toUIBackgroundModesinInfo.plist - Set code to elevate the hardware audio session to playback mode at application startup
AVAudioSession.sharedInstance().setCategory(.playback, ...) - Finally register and activate the configured audio session at the system level
AVAudioSession.sharedInstance().setActive(true)
The KollusPlayerView instance itself does not acquire background audio execution permission from the iOS operating system.
Therefore, you must implement the above three hardware audio session approval and activation steps yourself to maintain uninterrupted background audio output.