Skip to main content

API & Config

P2P Configuration

A P2pConfig can be obtained via its builder, the parameters below is 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(30_000, 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
.useHttpRange(true) // Use HTTP ranges requests where it is possible. Allows to continue (and not start over) aborted P2P downloads over HTTP
.httpHeadersForHls(null) // Set HTTP Headers while requesting ts and m3u8.
.httpHeadersForDash(null) // Set HTTP Headers while requesting Dash files.
.isSetTopBox(false) // Set it as true if SDK is running on set-top box
.httpLoadTime(3_000) // Time for HTTP download if p2p download timeout.
.sharePlaylist(false) // Allow the P2P transmission of m3u8 file.
.logPersistent(false) // Save logs to the file({Environment.getExternalStorageDirectory()}/logger/).
.geoIpPreflight(true) // Make a preflight request to online Geo IP database provider to get ASN.
.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
.mediaFileSeparator(".") // Separator that used to identify media file suffix
.hlsMediaFiles(
arrayListOf("mp4", "fmp4", "ts","m4s","m4v")) // The supported media file type of HLS.
.dashMediaFiles(
arrayListOf("mp4", "fmp4", "webm", "m4s", "m4v")) // The supported media file type of DASH.
.build()

P2pEngine

Instantiate P2pEngine,which is a singleton:

P2pEngine.init(context, token, config)

Explanation:


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

Switch Stream URL

When Switching to a new stream URL, before passing new stream url to the player, pass that URL through P2pEngine instance:

val parsedUrl = P2pEngine.instance.parseStreamUrl(url)

P2pEngine API

P2pEngine.version

Current SDK version.

P2pEngine.instance

Get the singleton of P2pEngine.

engine.parseStreamUrl(url: String)

Convert original playback address (m3u8/mpd) to the address of the local proxy server.

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

Pass video ID for making channel ID in addition to original playback address.

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

If your URI doesn’t end with .m3u8/.mpd, you can pass MimeType.APPLICATION_M3U8 or MimeType.APPLICATION_MPD to the third parameter of parseStreamUrl to explicitly indicate the type of the content.

engine.isConnected

Check if connected with CDNBye backend.

engine.stopP2p()

Once the video is done playing, you have to stop the P2P streaming you created earlier. Calling this method will finish the ongoing tasks and release the resources.

engine.restartP2p()

Resume P2P if it has been stopped.

engine.peerId

Get the peer ID of this engine.

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

Set HTTP Headers while requesting ts and m3u8 dynamically.

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

Set HTTP Headers while requesting Dash files dynamically.

engine.notifyPlaybackStalled()

Notify SDK the player stalled.

engine.disableP2p()

P2P will be disabled dynamically at runtime, it will not take effect until the next media file is played.

engine.enableP2p()

P2P will be enabled dynamically at runtime, it will not take effect until the next media file is played.

engine.shutdown()

Stop P2P and shut down the proxy server.

P2P Statistics

Add an observer P2pStatisticsListener to observe downloading 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

The unit of download and upload is KB. The unit of download speed is KB/s.

Advanced Usage

Callback Player Stats

On Live streaming, to improve performance, we recommend telling p2p engine the duration from the playback time to the end of the buffered interval. In order to do so, you need to use callback setPlayerInteractor .

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

On the VOD mode, the duration of a video may be large. If we can match peers with similar playback time, it will help to improve the P2P performance, so we recommend to tell p2p engine the current playback time of player:

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 that are watching the same content. It is an optional parameter, and by default, we generate channelId from the content URL by removing any query parameters and protocol from it. Some urls play the same live/vod but have different paths on them. For example, example.com/clientId1/streamId.m3u8 and example.com/clientId2/streamId.m3u8. In this case, you can format a common channelId for them.

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

Interconnect with other platform should ensure that both have the same token and channelId.

StrictSegmentId Mode

You can use segment url based segment id instead of sequence number based one:

P2pEngine.instance?.setHlsSegmentIdGenerator(StrictHlsSegmentIdGenerator())

Setup HTTP headers

Some HTTP requests need to add header information such as User-Agent for Anti-Leech or statistical requirements. It can be set 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 playing a live stream from the start point in time of playlist, however, it may bring more delay to the stream:

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

Report Player Rebuffering

You may want to report the player rebuffering event to the sdk, then get average rebuffer ratio from 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 will parse the contents of m3u8/mpd when downloaded, if you use encrypted m3u8/mpd, you need to use the interceptor to intercept and return the standard m3u8/mpd 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 special files do not end with ".ts" or any other suffixes. You can use the hook function to determine whether they are media files:

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 we don't want some ts files to be shared, such as user-specific ts generated by SSAI (Server Side Ad Insertion). At this time, we can use the segmentBypass function to bypass it:

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

Listen to SDK Exception

Due to network, server, algorithm bugs and other reasons, the SDK may have exceptions. You can listen to the exceptions by using the 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
}
})