Skip to main content

API & Config

P2P Configuration

A P2pConfig instance can be obtained via its builder. The parameters below show the default values:

val config = P2pConfig.Builder()
.logEnabled(false) // Enable or disable log
.logLevel(LogLevel.WARN) // Print log level
.trackerZone(TrackerZone.Europe) // The country enum for the tracker server address(Europe, HongKong, USA).
.downloadTimeout(15000, TimeUnit.MILLISECONDS) // TS file download timeout by HTTP
.localPortHls(0) // The port for local http server of HLS(Use random port by default)
.localPortDash(0) // The port for local http server of DASH(Use random port by default)
.diskCacheLimit(2000*1024*1024) // The max size of binary data that can be stored in the disk cache for VOD(Set to 0 will disable disk cache)
.memoryCacheCountLimit(15) // The max count of ts files that can be stored in the memory cache
.p2pEnabled(true) // Enable or disable p2p engine
.withTag(null) // Add a custom label to every different user session, which in turn will provide you the ability to have more meaningful analysis of the data gathered
.webRTCConfig(null) // Providing options to configure WebRTC connections
.maxPeerConnections(25) // Max peer connections at the same time
.startFromSegmentOffset(3) // The segment offset that start to connect to tracker server
.useHttpRange(true) // Use HTTP ranges requests where it is possible. Allows to continue (and not start over) aborted P2P downloads over HTTP
.useStrictHlsSegmentId(false) // Use segment url based segment id instead of sequence number based one
.httpHeadersForHls(null) // Set HTTP Headers while requesting ts and m3u8.
.httpHeadersForDash(null) // Set HTTP Headers while requesting Dash files.
.sharePlaylist(false) // Allow the P2P transmission of m3u8 file.
.prefetchOnly(false) // Only use prefetch strategy in p2p downloading(Only for HLS).
.logPersistent(false) // Save logs to the file({Environment.getExternalStorageDirectory()}/logger/).
.insertTimeOffsetTag(null) // Insert "#ext-x-start: time-offset = [timeOffset]" in m3u8 file to force the player to start loading from the first ts of playlist, where [timeOffset] is the offset in seconds to start playing the video, only works on live mode
.p2pProtocolVersion(P2pProtocolVersion.V8) // The version of P2P protocol, only have the same protocol version as another platform can both interconnect with each other
.dashMediaFiles(
arrayListOf("mp4", "fmp4", "webm", "m4s", "m4v")) // The supported media file type of DASH.
.build()

P2pEngine

Instantiate the P2pEngine singleton:

P2pEngine.init(context, token, config)

Parameter description:


paramtyperequireddescription
contextContextYesThe Application Context instance is recommended.
tokenStringYesToken assigned by CDNBye.
configP2pConfigNoCustom configuration.

Switch Stream URL

When switching to a new stream URL, pass the URL through the P2pEngine instance before handing it to the player:

val parsedUrl = P2pEngine.instance.parseStreamUrl(url)

P2pEngine API

P2pEngine.version

Current SDK version.

P2pEngine.instance

Returns the singleton instance of P2pEngine.

engine.parseStreamUrl(url: String)

Converts the original playback URL (m3u8/mpd) into a local proxy server address.

engine.parseStreamUrl(url: String, videoId: String)

Passes a video ID, used together with the original playback URL to generate the channel ID.

engine.parseStreamUrl(url: String, videoId: String, mimeType: MimeType)

If your URI doesn't end with .m3u8 or .mpd, pass MimeType.APPLICATION_M3U8 or MimeType.APPLICATION_MPD as the third parameter of parseStreamUrl to explicitly specify the content type.

engine.isConnected

Checks whether the SDK is connected to the CDNBye backend.

engine.stopP2p()

Once playback finishes, stop the P2P streaming session you created earlier. Calling this method completes any ongoing tasks and releases the associated resources.

engine.restartP2p()

Resumes P2P streaming after it has been stopped.

engine.peerId

Returns the peer ID of this engine.

engine.setHttpHeadersForHls(headers: Map<String, String>?)

Dynamically sets the HTTP headers used when requesting ts and m3u8 files.

engine.setHttpHeadersForDash(headers: Map<String, String>?)

Dynamically sets the HTTP headers used when requesting DASH files.

engine.notifyPlaybackStalled()

Notifies the SDK that playback has stalled.

engine.disableP2p()

Disables P2P dynamically at runtime; the change takes effect starting with the next media file played.

engine.enableP2p()

Enables P2P dynamically at runtime; the change takes effect starting with the next media file played.

engine.shutdown()

Stops P2P streaming and shuts down the proxy server.

P2P Statistics

Register a P2pStatisticsListener observer to monitor download statistics:

engine.addP2pStatisticsListener(object : P2pStatisticsListener {
override fun onHttpDownloaded(value: Int) {
}

override fun onP2pDownloaded(value: Int, speed: Int) {
}

override fun onP2pUploaded(value: Int, speed: Int) {
}

override fun onPeers(peers: List<String>) {
}

override fun onServerConnected(connected: Boolean) {
}
})
note

Download and upload volumes are measured in KB. Download speed is measured in KB/s.

Advanced Usage

Callback Player Stats

For live streaming, to improve performance, we recommend informing the P2P engine of the duration between the current playback position and the end of the buffered interval. To do so, use the setPlayerInteractor callback.

P2pEngine.instance?.setPlayerInteractor(object : PlayerInteractor() {
override fun onBufferedDuration(): Long {
return if (player != null) {
// Exoplayer in milliseconds
player!!.bufferedPosition - player!!.currentPosition
} else {
-1
}
}
})

For VOD, video duration can be significant. Matching peers with similar playback positions helps improve P2P performance, so we recommend informing the P2P engine of the player's current playback time:

P2pEngine.instance?.setPlayerInteractor(object : PlayerInteractor() {
override fun onCurrentPosition(): Long {
// Exoplayer in milliseconds
return player?.currentPosition ?: -1
}
})

Dynamic Url Support

The channelId is an identifier used by our backend to match peers watching the same content. It's an optional parameter — by default, videoId is generated from the content URL by stripping the protocol and any query parameters. However, if a unique URL is generated for each viewer (for example, when a security token is embedded in the URL path), players will appear to be watching different content, which prevents the P2P plugin from working efficiently. In this case, an explicit videoId is required.

val videoId = extractVideoIdFromUrl(urlString)    // extractVideoIdFromUrl is a function defined by yourself, you just need to extract video id from url
val parsedUrl = P2pEngine.instance?.parseStreamUrl(urlString, videoId)
note

To interconnect with another platform, ensure both sides use the same token and channelId.

StrictSegmentId Mode

You can use a URL-based segment ID instead of a sequence-number-based one:

P2pEngine.instance?.setHlsSegmentIdGenerator(StrictHlsSegmentIdGenerator())

Setup HTTP headers

Some HTTP requests require additional header information, such as User-Agent, for anti-leech protection or analytics purposes. This can be configured via setHttpHeaders :

val headers = mapOf("User-Agent" to "XXX")
P2pEngine.instance?.setHttpHeadersForHls(headers)
P2pEngine.instance?.setHttpHeadersForDash(headers)

Specify the Preferred Point in the Video to Start Playback.

The SDK can insert the EXT-X-START tag to start a live stream from a specific point in the playlist. Note that this may introduce additional stream delay:

val config = P2pConfig.Builder()
.insertTimeOffsetTag(0.0)
.build()

Report Player Rebuffering

You can report player rebuffering events to the SDK, then view the average rebuffer ratio in the SwarmCloud dashboard:

// Take exoplayer as example
player?.addListener(object : Player.Listener {
var isDetecting = false
override fun onPlayerStateChanged(playWhenReady: Boolean, playbackState: Int) {
if (playbackState == 2) { // STATE_BUFFERING
if (isDetecting) return
isDetecting = true
Timer().schedule(object : TimerTask() {
override fun run() {
runOnUiThread {
isDetecting = false
if (!player!!.isPlaying) {
P2pEngine.instance!!.notifyPlaybackStalled()
}
}
}
}, 8000)
}
}
})

Intercept m3u8/mpd

The SDK parses the contents of m3u8/mpd files after they are downloaded. If you use encrypted m3u8/mpd files, use the interceptor to intercept them and return the standard, decrypted file:

P2pEngine.instance?.setHlsInterceptor(object : HlsInterceptor() {
override fun interceptPlaylist(text: ByteArray, url: String): ByteArray {
return handlePlaylist(text, url);
}
})
P2pEngine.instance?.setDashInterceptor(object : DashInterceptor() {
override fun interceptPlaylist(text: ByteArray, url: String): ByteArray {
return handlePlaylist(text, url)
}
})

Support Media Files without Suffixes

Some media segment files may not end with ".ts" or any other recognizable suffix. You can use the following hook function to determine whether such a URL points to a media file:

P2pEngine.instance?.setHlsInterceptor(object : HlsInterceptor() {
override fun isMediaSegment(url: String): Boolean {
return true
}
})

Pass Your Customized OkHttpClient

val httpClient = OkHttpClient.Builder()
.addInterceptor(YourInterceptor())
.build()
val config = P2pConfig.Builder().okHttpClient(httpClient).build()

Bypass User-specific Segments

Sometimes certain ts segments should not be shared — for example, user-specific segments generated by SSAI (Server-Side Ad Insertion). In this case, use the segmentBypass function to exclude them from sharing:

P2pEngine.instance?.setHlsInterceptor(object : HlsInterceptor() {
override fun shouldBypassSegment(url: String): Boolean {
return isSSAISegment(url)
}
})

Listen to SDK Exception

The SDK may throw exceptions due to network issues, server errors, algorithm bugs, or other causes. You can listen for these exceptions using registerExceptionListener :

P2pEngine.instance?.registerExceptionListener(object : EngineExceptionListener {
override fun onTrackerException(e: EngineException) {
// Tracker Exception
}
override fun onSignalException(e: EngineException) {
// Signal Server Exception
}
override fun onSchedulerException(e: EngineException) {
// Scheduler Exception
}
override fun onOtherException(e: EngineException) {
// Other Exception
}
})