API & Config
P2P Configuration
Create P2pConfig instance.
- swift
- objective-c
let config = P2pConfig()
P2pConfig* config = [P2pConfig defaultConfiguration];
The default fields (shown below) can be overridden.
Field | Type | Default | Description |
---|---|---|---|
trackerZone | TrackerZone | .Europe | The country enum for the tracker server address(Europe, HongKong, USA) |
debug | Bool | false | Enable or disable log |
logLevel | LogLevel | .WARN | Print log level(VERBOSE, DEBUG, INFO, WARN, ERROR) |
iceServers | [IceServer] | [IceServer(url: "stun:stun.l.google.com:19302"), IceServer(url: "stun:global.stun.twilio.com:3478?transport=udp")] | For WebRTC Stun Configuration |
announce | String? | nil | The address of tracker server |
diskCacheLimit | UInt | 2000 1024 1024 | The max size of binary data that can be stored in the cache for VOD(Set to 0 will disable disk cache) |
memoryCacheCountLimit | UInt | 20 | The max count of ts files that can be stored in the memory cache |
p2pEnabled | Bool | true | Enable or disable p2p engine |
localPortHls | UInt | 0 | The port for local http server of HLS(Use random port by default) |
customLabel | String? | ${platform}-${system_version} | 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 |
maxPeerConnections | Int | 25 | Max peer connections at the same time |
useHttpRange | Bool | true | Use HTTP ranges requests where it is possible. Allows to continue (and not start over) aborted P2P downloads over HTTP |
useStrictHlsSegmentId | Bool | false | Use segment url based segment id instead of sequence number based one |
httpHeadersHls | [String : String]? | nil | Set HTTP Headers while requesting ts and m3u8 |
sharePlaylist | Bool | false | Allow the P2P transmission of m3u8 file |
prefetchOnly | Bool | false | Only use prefetch strategy in p2p downloading |
logPersistent | Bool | false | Save logs to the file, default path is Library/Caches/p2pengine.log |
geoIpPreflight | Bool | true | Make a preflight request to online Geo IP database provider to get ASN |
P2pEngine
Initialize P2pEngine in AppDelegate.m:
- swift
- objective-c
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
P2pEngine.setup(token: YOUR_TOKEN)
return true
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[P2pEngine setupWithToken:YOUR_TOKEN config:NULL];
return YES;
}
Where token is your Customer ID, please replace it by your own token obtained from console.
- swift
- objective-c
let parsedUrl = P2pEngine.shared.parseStreamUrl(ORIGINAL_URL)
NSString *parsedUrl = [[P2pEngine sharedInstance] parseStreamUrl:ORIGINAL_URL];
Switch Stream URL
When Switching to a new stream URL, before passing new stream url(m3u8) to the player, pass that URL through P2pEngine instance:
- swift
- objective-c
let newParsedURL = P2pEngine.shared.parseStreamUrl(NEW_ORIGINAL_URL)
NSString *newParsedURL = [[P2pEngine sharedInstance] parseStreamUrl:NEW_ORIGINAL_URL];
Stop P2P
Stop p2p streaming of current source, but keep local proxy working:
- swift
- objective-c
override func viewDidDisappear(_ animated: Bool) {
P2pEngine.shared.stopP2p()
}
- (void)viewDidDisappear:(BOOL)animated {
[P2pEngine.sharedInstance stopP2p];
}
Disable P2P at runtime
it will not take effect until the next media file is played.
- swift
- objective-c
P2pEngine.shared.disableP2p()
[P2pEngine.sharedInstance disableP2p];
Enable P2P at runtime
it will not take effect until the next media file is played.
- swift
- objective-c
P2pEngine.shared.enableP2p()
[P2pEngine.sharedInstance enableP2p];
Stop P2P and Local Proxy
Stop p2p streaming of current source and local proxy:
- swift
- objective-c
P2pEngine.shared.shutdown()
[P2pEngine.sharedInstance shutdown];
Restart Local Proxy
- swift
- objective-c
P2pEngine.shared.startLocalServer()
[P2pEngine.sharedInstance startLocalServer];
P2P Statistics
Use P2pStatisticsMonitor to observe downloading statistics:
- swift
- objective-c
let monitor = P2pStatisticsMonitor(queue: .main)
P2pEngine.shared.p2pStatisticsMonitor = monitor
P2pStatisticsMonitor* monitor = [[P2pStatisticsMonitor alloc] initWithQueue:dispatch_get_main_queue()];
P2pEngine.shared.p2pStatisticsMonitor = monitor;
Then get the downloading statistics, including p2pDownloaded、p2pUploaded、httpDownloaded、peers、serverConnected
- swift
- objective-c
monitor.onPeers = { peers in
}
monitor.onP2pUploaded = { value in
}
monitor.onP2pDownloaded = { value, speed in
}
monitor.onHttpDownloaded = { value in
}
monitor.onServerConnected = { connected in
}
monitor.onPeers = ^(NSArray<NSString *> * _Nonnull peers) {
};
monitor.onP2pUploaded = ^(NSInteger value) {
};
monitor.onP2pDownloaded = ^(NSInteger value, NSInteger speed) {
};
monitor.onHttpDownloaded = ^(NSInteger value) {
};
monitor.onServerConnected = ^(BOOL connected) {
};
The unit of download and upload is KB.
Advanced Usage
Callback Player Stats
On Live streaming, to improve performance, we recommend to tell p2p engine the duration from the playback time to the end of the buffered interval. In order to do so, you need to implement PlayerInteractor .
- swift
- objective-c
class ViewController: UIViewController {
...
P2pEngine.shared.playerInteractor = self
...
}
extension ViewController : PlayerInteractor {
func onBufferedDuration() -> TimeInterval {
let currentTime = CMTimeGetSeconds(self.avplayer.currentTime())
var bufferedDuration: Double = 0.0
let timeRanges = self.avplayer.currentItem!.loadedTimeRanges
for value in timeRanges {
let timeRange = value.timeRangeValue
let start = CMTimeGetSeconds(timeRange.start)
let end = start + CMTimeGetSeconds(timeRange.duration);
if (currentTime >= start && currentTime <= end) {
bufferedDuration = end - currentTime;
break;
}
}
return bufferedDuration;
}
}
@interface ViewController () <PlayerInteractor>
...
P2pEngine.shared.playerInteractor = self;
...
#pragma mark - **************** PlayerInteractor ****************
-(NSTimeInterval)onBufferedDuration {
NSTimeInterval currentTime = CMTimeGetSeconds([self.player currentTime]);
NSTimeInterval bufferedDuration = 0;
for (NSValue *value in [[self.player currentItem] loadedTimeRanges]) {
CMTimeRange timeRange = [value CMTimeRangeValue];
NSTimeInterval start = CMTimeGetSeconds(timeRange.start);
NSTimeInterval end = start + CMTimeGetSeconds(timeRange.duration);
if (currentTime >= start && currentTime <= end) {
bufferedDuration = end - currentTime;
break;
}
}
return bufferedDuration;
}
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:
- swift
- objective-c
class ViewController: UIViewController {
...
P2pEngine.shared.playerInteractor = self
...
}
extension ViewController : PlayerInteractor {
func onCurrentPosition() -> TimeInterval {
return CMTimeGetSeconds(self.avplayer.currentTime())
}
}
@interface ViewController () <PlayerInteractor>
...
P2pEngine.shared.playerInteractor = self;
...
- (NSTimeInterval)onCurrentPosition {
return CMTimeGetSeconds([self.player currentTime]);
}
Dynamic M3u8 Path 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 m3u8 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.
- swift
- objective-c
let videoId = extractVideoIdFromUrl(url: orginalUrl) // extractVideoIdFromUrl 需要自己定义,可以抽取url中的视频ID作为结果返回
let parsedUrl = P2pEngine.shared.parseStreamUrl(orginalUrl, videoId: videoId)
NSString *videoId = [Utils extractVideoIdFromUrl:originalUrl]; // extractVideoIdFromUrl 需要自己定义,可以抽取url中的视频ID作为结果返回
NSURL *parsedUrl = [[P2pEngine sharedInstance] parseStreamUrl:originalUrl videoId:videoId];
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 httpHeaders :
- swift
- objective-c
p2pConfig.httpHeadersHls = ["User-Agent": "XXX"]
p2pConfig.httpHeadersHls = @{@"User-Agent": @"XXX"};
Or set HTTP headers dynamically:
- swift
- objective-c
P2pEngine.shared.setHttpHeadersForHls(headers: ["User-Agent": "XXX"])
[P2pEngine.shared setHttpHeadersForHlsWithHeaders: @{@"User-Agent": @"XXX"}];
Work in AirPlay mode
SwarmCloud does not directly support AirPlay mode at the moment but it is easy to make it work in an app. The way to do this is to only use the Proxy URL when the app is not airplaying:
if (/* player is in AirPlay mode */) {
// set URL to original version
[player replaceCurrentItemWithPlayerItem:[AVPlayerItem playerItemWithURL:original]];
} else {
// airplay stopped - set URL to result of parseStreamURL
[player replaceCurrentItemWithPlayerItem:[AVPlayerItem playerItemWithURL:rewritten]];
}
Support additional media file type of HLS
By default, only common file types with suffix such as ".ts" are supported. If you need to support file types similar to ".image", you need to make the following configuration:
- swift
- objective-c
let config = P2pConfig()
config.hlsMediaFiles = ["ts", "image"]
P2pEngine.shared.setup(token: YOUR_TOKEN, config: config)
P2pConfig *config = [P2pConfig defaultConfiguration];
config.hlsMediaFiles = @[@"ts", @"image"];
[[P2pEngine sharedInstance] setupWithToken:YOUR_TOKEN config:config];
Generally, HLS media files end with ".xx", but there are exceptions. In order for the local proxy to recognize these extensions, the following configuration is required:
- swift
- objective-c
class ViewController: UIViewController {
...
P2pEngine.shared.hlsInterceptor = self
...
}
extension ViewController : HlsInterceptor {
func isMediaSegment(url: String) -> Bool {
return mediaFileRegex.match(url)
}
}
@interface ViewController () <HlsInterceptor>
...
P2pEngine.shared.hlsInterceptor = self;
...
#pragma mark - **************** HlsInterceptor ****************
- (BOOL)isMediaSegmentWithUrl:(NSString *)url {
return [mediaFileRegex match:url];
}
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:
- swift
- objective-c
class ViewController: UIViewController {
...
P2pEngine.shared.hlsInterceptor = self
...
}
extension ViewController : HlsInterceptor {
func shouldBypassSegment(url: String) -> Bool {
return isSSAISegment(url)
}
}
@interface ViewController () <HlsInterceptor>
...
P2pEngine.shared.hlsInterceptor = self;
...
#pragma mark - **************** HlsInterceptor ****************
- (BOOL)shouldBypassSegment:(NSString *)url {
return [Utils isSSAISegment:url];
}
Intercept playlist
The SDK will parse the contents of m3u8 when downloaded, if you use encrypted m3u8, you need to use the interceptor to intercept and return the standard m3u8 file:
- swift
- objective-c
class ViewController: UIViewController {
...
P2pEngine.shared.hlsInterceptor = self
...
}
extension ViewController : HlsInterceptor {
func interceptPlaylist(data: Data, url: String) -> Data {
return handlePlaylist(data, url)
}
}
@interface ViewController () <HlsInterceptor>
...
P2pEngine.shared.hlsInterceptor = self;
...
#pragma mark - **************** HlsInterceptor ****************
- (NSData *)interceptPlaylistWithData:(NSData *)data url:(NSString *)url {
return [Utils handlePlaylistWithData:data url:url];
}
Report Player Rebuffering
You may want to report the player rebuffering event to the sdk, then get average rebuffer ratio from SwarmCloud dashboard:
- swift
- objective-c
NotificationCenter.default.addObserver(self, selector: #selector(handleItemPlayBackStall), name: NSNotification.Name.AVPlayerItemPlaybackStalled, object: player.currentItem)
@objc func handleItemPlayBackStall() {
P2pEngine.shared.notifyPlaybackStalled()
}
[NSNotificationCenter.defaultCenter addObserver:self selector:@selector(handleItemPlayBackStall) name:AVPlayerItemPlaybackStalledNotification object:self.playerVC.player.currentItem];
- (void)handleItemPlayBackStall {
[P2pEngine.sharedInstance notifyPlaybackStalled];
}