Skip to content

P2P設定

實例化 P2pConfig

swift
let config = P2pConfig()
objective-c
P2pConfig* config = [P2pConfig defaultConfiguration];

覆蓋相應的欄位:

欄位型別預設值描述
trackerZoneTrackerZone.Europetracker伺服器位址所在國家的列舉,分為China、Europe、HongKong、USA
debugBoolfalse是否列印日誌
logLevelLogLevel.WARN列印日誌的等級(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")]用於Stun伺服器設定
announceString?niltracker伺服器位址
diskCacheLimitUInt2000 * 1024 * 1024隨選模式下P2P在磁碟快取的最大資料量(設為0可以停用磁碟快取)
memoryCacheCountLimitUInt20P2P在記憶體快取的最大檔案數量
p2pEnabledBooltrue開啟或關閉p2p engine
localPortHlsUInt0播放HLS串流媒體本地代理伺服器的連接埠號,預設隨機連接埠
customLabelString?${platform}-$使用者自訂的標籤,可以在控制台查看分布圖
maxPeerConnectionsInt25最大連線節點數量
startFromSegmentOffsetUInt3開始請求tracker服務的segment偏移量
useHttpRangeBooltrue在可能的情況下使用Http Range請求來補足p2p下載逾時的剩餘部分資料
useStrictHlsSegmentIdBoolfalse使用基於url的SegmentId,替代預設基於序號的
httpHeadersHls[String : String]?nil設定請求ts和m3u8時的HTTP請求標頭
sharePlaylistBoolfalse是否允許m3u8檔案的P2P傳輸
prefetchOnlyBoolfalse只採用預先載入的方式進行P2P下載
logPersistentBoolfalse是否將日誌持久化到外部儲存空間,預設路徑是 Library/Caches/p2pengine.log

P2pEngine

初始化 P2pEngine

swift
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    P2pEngine.setup(token: YOUR_TOKEN)
    return true
}
objective-c
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    [P2pEngine setupWithToken:YOUR_TOKEN config:NULL];
    return YES;
}

其中 YOUR_TOKEN 是用於標識使用者的字串,請替換成從控制台取得的token。將原始播放位址(m3u8)傳給 P2pEngine,從而取得本地播放位址:

swift
let parsedUrl = P2pEngine.shared.parseStreamUrl(ORIGINAL_URL)
objective-c
NSString *parsedUrl = [[P2pEngine sharedInstance] parseStreamUrl:ORIGINAL_URL];

切換來源

當播放器切換到新的播放位址時,只需要將新的播放位址(m3u8)傳給 P2pEngine,從而取得新的本地播放位址:

swift
let newParsedURL = P2pEngine.shared.parseStreamUrl(NEW_ORIGINAL_URL)
objective-c
NSString *newParsedURL = [[P2pEngine sharedInstance] parseStreamUrl:NEW_ORIGINAL_URL];

停止 P2P

停止目前播放影片的P2P傳輸,但不停止本地代理:

swift
override func viewDidDisappear(_ animated: Bool) {
    P2pEngine.shared.stopP2p()
}
objective-c
- (void)viewDidDisappear:(BOOL)animated {
    [P2pEngine.sharedInstance stopP2p];
}

執行時動態關閉P2P

在播放下一個媒體檔案時才生效

swift
P2pEngine.shared.disableP2p()
objective-c
[P2pEngine.sharedInstance disableP2p];

執行時動態開啟P2P

在播放下一個媒體檔案時才生效

swift
P2pEngine.shared.enableP2p()
objective-c
[P2pEngine.sharedInstance enableP2p];

停止P2P和本地代理

停止目前播放影片的P2P傳輸和本地代理:

swift
P2pEngine.shared.shutdown()
objective-c
[P2pEngine.sharedInstance shutdown];

重新啟動本地代理

swift
P2pEngine.shared.startLocalServer()
objective-c
[P2pEngine.sharedInstance startLocalServer];

P2P統計

透過 P2pStatisticsMonitor 來監聽P2P下載資訊:

swift
let monitor = P2pStatisticsMonitor()
P2pEngine.shared.p2pStatisticsMonitor = monitor
objective-c
P2pStatisticsMonitor* monitor = [[P2pStatisticsMonitor alloc] initWithQueue:dispatch_get_main_queue()];
P2pEngine.shared.p2pStatisticsMonitor = monitor;

在回呼函式中取得 p2pDownloadedp2pUploadedhttpDownloadedpeersserverConnected 等:

swift
monitor.onPeers = { peers in
}
monitor.onP2pUploaded = { value in
}
monitor.onP2pDownloaded = { value, speed in
}
monitor.onHttpDownloaded = { value in
}
monitor.onServerConnected = { connected in
}
objective-c
monitor.onPeers = ^(NSArray<NSString *> * _Nonnull peers) {
};
monitor.onP2pUploaded = ^(NSInteger value) {
};
monitor.onP2pDownloaded = ^(NSInteger value, NSInteger speed) {
};
monitor.onHttpDownloaded = ^(NSInteger value) {
};
monitor.onServerConnected = ^(BOOL connected) {
};

WARNING

下載和上傳資料量的單位是KB。

進階用法

回呼播放器資訊

在直播模式下,為了增強P2P效果,建議透過實作代理 PlayerInteractor 的方式,將從目前播放時間到緩衝前沿的時間間隔回呼給p2p engine。

swift
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;
    }
}
objective-c
@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;
}

在隨選模式下,一部影片的時長可能比較大,如果能在節點配對的時候優先配對播放時間接近的節點則有助於P2P效果的提升,要實現這個功能需要在SDK層面取得播放器的目前播放時間:

swift
class ViewController: UIViewController {
...
P2pEngine.shared.playerInteractor = self
...
}
extension ViewController : PlayerInteractor {
    func onCurrentPosition() -> TimeInterval {
        return CMTimeGetSeconds(self.avplayer.currentTime())
    }
}
objective-c
@interface ViewController () <PlayerInteractor>
...
P2pEngine.shared.playerInteractor = self;
...
- (NSTimeInterval)onCurrentPosition {
    return CMTimeGetSeconds([self.player currentTime]);
}

解決動態m3u8路徑問題

某些串流媒體提供商的m3u8是動態產生的,不同節點的m3u8位址不一樣,例如example.com/clientId1/streamId.m3u8和example.com/clientId2/streamId.m3u8,而本外掛程式預設使用m3u8位址(去掉查詢參數)作為channelId。這時候就要建構一個共同的channelId,使實際觀看同一直播/影片的節點處在相同頻道中。

swift
let videoId = extractVideoIdFromUrl(url: orginalUrl)                      // extractVideoIdFromUrl 需要自己定義,可以擷取url中的影片ID作為結果回傳
let parsedUrl = P2pEngine.shared.parseStreamUrl(orginalUrl, videoId: videoId)
objective-c
NSString *videoId = [Utils extractVideoIdFromUrl:originalUrl];            // extractVideoIdFromUrl 需要自己定義,可以擷取url中的影片ID作為結果回傳
NSURL *parsedUrl = [[P2pEngine sharedInstance] parseStreamUrl:originalUrl videoId:videoId];

設定HTTP請求標頭

出於防盜鏈或統計的需求,有些HTTP請求需要加上 User-Agent 等標頭資訊,可以透過 httpHeaders 進行設定:

swift
p2pConfig.httpHeadersHls = ["User-Agent": "XXX"]
objective-c
p2pConfig.httpHeadersHls = @{@"User-Agent":@"XXX"};

也可以執行時動態設定:

swift
P2pEngine.shared.setHttpHeadersForHls(headers: ["User-Agent": "XXX"])
objective-c
[P2pEngine.shared setHttpHeadersForHlsWithHeaders: @{@"User-Agent": @"XXX"}];

支援 AirPlay 模式

SwarmCloud暫不支援 AirPlay,但是可以透過只在不進行 AirPlay 的時候使用本地代理來避開限制:

objective-c
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]];
}

擴充支援的HLS媒體檔案

預設只支援「.ts」等為副檔名的常見檔案類型,如果需要支援類似「.image」的檔案類型,需要做以下設定:

swift
let config = P2pConfig()
config.hlsMediaFiles = ["ts", "image"]
P2pEngine.shared.setup(token: YOUR_TOKEN, config: config)
objective-c
P2pConfig *config = [P2pConfig defaultConfiguration];
config.hlsMediaFiles = @[@"ts", @"image"];
[[P2pEngine sharedInstance] setupWithToken:YOUR_TOKEN config:config];

一般HLS媒體檔案以「.xx」結尾,但也有例外情況,比如其他任意字元結尾,為了讓本地代理能識別出這些副檔名,需要做以下設定:

swift
class ViewController: UIViewController {
...
P2pEngine.shared.hlsInterceptor = self
...
}
extension ViewController : HlsInterceptor {
    func isMediaSegment(url: String) -> Bool {
        return mediaFileRegex.match(url)
    }
}
objective-c
@interface ViewController () <HlsInterceptor>
...
P2pEngine.shared.hlsInterceptor = self;
...
#pragma mark - **************** HlsInterceptor ****************
- (BOOL)isMediaSegmentWithUrl:(NSString *)url {
    return [mediaFileRegex match:url];
}

排除某些特殊的切片檔案

某些情況下我們不想讓某些切片檔案參與P2P,比如SSAI(Server Side Ad Insertion)產生的特定於使用者的切片,這個時候可以利用 segmentBypass 這個函式來進行過濾:

swift
class ViewController: UIViewController {
...
P2pEngine.shared.hlsInterceptor = self
...
}
extension ViewController : HlsInterceptor {
    func shouldBypassSegment(url: String) -> Bool {
        return isSSAISegment(url)
    }
}
objective-c
@interface ViewController () <HlsInterceptor>
...
P2pEngine.shared.hlsInterceptor = self;
...
#pragma mark - **************** HlsInterceptor ****************
- (BOOL)shouldBypassSegment:(NSString *)url {
    return [Utils isSSAISegment:url];
}

攔截 m3u8 檔案

由於 SDK 需要解析 m3u8 的內容,如果您用了加密的 m3u8,則需要使用攔截器攔截並回傳標準的 m3u8 檔案:

swift
class ViewController: UIViewController {
...
P2pEngine.shared.hlsInterceptor = self
...
}
extension ViewController : HlsInterceptor {
    func interceptPlaylist(data: Data, url: String) -> Data {
        return handlePlaylist(data, url)
    }
}
objective-c
@interface ViewController () <HlsInterceptor>
...
P2pEngine.shared.hlsInterceptor = self;
...
#pragma mark - **************** HlsInterceptor ****************
- (NSData *)interceptPlaylistWithData:(NSData *)data url:(NSString *)url {
    return [Utils handlePlaylistWithData:data url:url];
}

播放器卡頓統計

SwarmCloud 控制台可以監測用戶端的平均卡頓率,只需要在播放器卡頓發生時回報給SDK即可:

swift
NotificationCenter.default.addObserver(self, selector: #selector(handleItemPlayBackStall), name: NSNotification.Name.AVPlayerItemPlaybackStalled, object: player.currentItem)

@objc func handleItemPlayBackStall() {
    P2pEngine.shared.notifyPlaybackStalled()
}
objective-c
[NSNotificationCenter.defaultCenter addObserver:self selector:@selector(handleItemPlayBackStall) name:AVPlayerItemPlaybackStalledNotification object:self.playerVC.player.currentItem];

- (void)handleItemPlayBackStall {
    [P2pEngine.sharedInstance notifyPlaybackStalled];
}

粤ICP备18075581号