Skip to content

P2P設定

ビルダーパターンで P2pConfig をインスタンス化します。以下のパラメータはデフォルト値です:

kotlin
val config = P2pConfig.Builder()
.logEnabled(false)                                  // ログを出力するかどうか
.logLevel(LogLevel.WARN)                            // ログ出力のレベル
.trackerZone(TrackerZone.Europe)                    // trackerサーバーアドレスの所在国の列挙値。China、Europe、HongKong、USA に分かれます
.downloadTimeout(15000, TimeUnit.MILLISECONDS)       // HTTPでtsファイルをダウンロードする際のタイムアウト時間
.localPortHls(0)                                    // HLSローカルプロキシサーバーのポート番号(デフォルトはランダムポート)
.localPortDash(0)                                   // DASHローカルプロキシサーバーのポート番号(デフォルトはランダムポート)
.diskCacheLimit(2000*1024*1024)                     // VODモードでP2Pがディスクキャッシュする最大データ量(0に設定するとディスクキャッシュを無効化できます)
.memoryCacheCountLimit(15)                          // P2Pがメモリキャッシュする最大データ量。tsファイルの個数で表します
.p2pEnabled(true)                                   // p2p engineを有効化・無効化
.withTag(null)                                      // ユーザー定義のカスタムラベル。管理パネルで分布図を確認できます
.webRTCConfig(null)                                 // webRTCConfigを通じてWebRTCのデフォルト設定を変更
.maxPeerConnections(25)                             // 最大接続ノード数
.startFromSegmentOffset(3)                          // trackerサービスへのリクエストを開始するsegmentオフセット
.useHttpRange(true)                                 // 可能な場合にHttp Rangeリクエストを使用して、p2pダウンロードのタイムアウトによる残りのデータを補完
.useStrictHlsSegmentId(false)                       // デフォルトのシーケンス番号ベースの代わりに、urlベースのSegmentIdを使用
.httpHeadersForHls(null)                            // tsおよびm3u8をリクエストする際のHTTPリクエストヘッダーを設定
.httpHeadersForDash(null)                           // Dashファイルをリクエストする際のHTTPリクエストヘッダーを設定
.sharePlaylist(false)                               // m3u8ファイルのP2P伝送を許可するかどうか
.prefetchOnly(false)                                // HLSモードでプリフェッチ方式のみでP2Pダウンロードを行う
.logPersistent(false)                               // ログを外部ストレージ(Environment.getExternalStorageDirectory()配下のloggerフォルダ)に永続化するかどうか
.insertTimeOffsetTag(null)                          // ライブ配信モードでのみ有効。m3u8ファイルに "#EXT-X-START:TIME-OFFSET=[timeOffset]" を挿入し、プレイヤーを強制的に特定の位置から読み込ませます。[timeOffset] はプレイリスト内のオフセットで、負の値の場合はプレイリストの末尾からのオフセットになります(単位:秒)
.p2pProtocolVersion(P2pProtocolVersion.V8)          // P2Pプロトコルのバージョン番号。他のプラットフォームと相互接続するには、P2Pプロトコルのバージョン番号が同じである必要があります
.dashMediaFiles(
    arrayListOf("mp4", "fmp4", "webm", "m4s", "m4v"))       // サポートするDASHメディアファイルの拡張子
.build()

P2pEngine

P2pEngineをインスタンス化し、グローバルなシングルトンを取得します:

kotlin
P2pEngine.init(context, token, config)
java
P2pEngine.init(context, token, config);

パラメータの説明:

パラメータ必須説明
contextContextはいApplicationのContextオブジェクトを使用することを推奨します。
tokenStringはいCDNByeが発行したtoken。
configP2pConfigいいえカスタム設定。

ソースの切り替え

プレイヤーが新しい再生アドレスに切り替える際は、新しい再生アドレスを P2pEngine に渡すだけで、新しいローカル再生アドレスを取得できます:

kotlin
val parsedUrl = P2pEngine.instance.parseStreamUrl(url)
java
String parsedUrl = P2pEngine.getInstance().parseStreamUrl(url);

P2pEngine API

P2pEngine.version

本プラグインの現在のバージョン番号。

P2pEngine.instance

P2pEngine のシングルトンを取得します。

engine.parseStreamUrl(url: String)

元の再生アドレス(m3u8/mpd)をローカルプロキシサーバーのアドレスに変換します。

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

元の再生アドレス(m3u8/mpd)に加え、videoIdを渡してchannelIdの構築に使用します。

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

元の再生アドレス(m3u8/mpd)とvideoIdに加え、mimeType(現在MimeType.APPLICATION_M3U8とMimeType.APPLICATION_MPDに対応)を渡します。".m3u8"や".mpd"の拡張子がないurlに使用します。

engine.isConnected

CDNByeのバックエンドと接続が確立されているかどうか。

engine.stopP2p()

P2Pアクセラレーションを即座に停止し、リソースを解放します。通常はアプリ終了時にのみ呼び出せば十分です。SDKは「遅延解放」戦略を採用しており、p2pを再起動する際にのみリソースが解放されます。性能の低いデバイスでは再生開始までの時間が顕著に長くなる可能性があるため、動画再生前に事前に engine.stopP2p() を呼び出すことを推奨します。

engine.restartP2p()

P2Pアクセラレーションサービスを再起動します。通常は呼び出す必要はありません。

engine.peerId

ピア接続のidを取得します。

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

tsおよびm3u8をリクエストする際のHTTPリクエストヘッダーを動的に設定します。

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

Dashファイルをリクエストする際のHTTPリクエストヘッダーを動的に設定します。

engine.notifyPlaybackStalled()

カクつきが発生した際にSDKに通知します。

engine.disableP2p()

実行時に動的にP2Pを無効化します。次のメディアファイルを再生する際に反映されます。

engine.enableP2p()

実行時に動的にP2Pを有効化します。次のメディアファイルを再生する際に反映されます。

engine.shutdown()

P2Pを停止し、プロキシサーバーを終了します。

P2P統計

P2pStatisticsListener を通じてP2Pダウンロード情報を監視します:

kotlin
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) {
    }
})
java
engine.addP2pStatisticsListener(new P2pStatisticsListener() {
    @Override
    public void onHttpDownloaded(int value) {

    }

    @Override
    public void onP2pDownloaded(int value, int speed) {

    }

    @Override
    public void onP2pUploaded(int value, int speed) {

    }

    @Override
    public void onPeers(@NonNull List<String> peers) {

    }

    @Override
    public void onServerConnected(boolean connected) {

    }
});

WARNING

ダウンロード・アップロードデータ量の単位はKB、ダウンロード速度の単位はKB/sです。

高度な使い方

プレイヤー情報のコールバック

ライブ配信モードでは、P2P効果を高めて再生の流暢さを向上させるために、setPlayerInteractor を通じて、現在の再生時刻からバッファ先端までの時間間隔をp2p engineにコールバックすることを推奨します。

kotlin
P2pEngine.instance?.setPlayerInteractor(object : PlayerInteractor() {
    override fun onBufferedDuration(): Long {
        return if (player != null) {
            // Exoplayer 単位:ミリ秒
            player!!.bufferedPosition - player!!.currentPosition
        } else {
            -1
        }
    }
})
java
P2pEngine.getInstance().setPlayerInteractor(new PlayerInteractor() {
    public long onBufferedDuration() {
        // Exoplayer in milliseconds
        if (play != null) {
            return player.getBufferedPosition() - player.getCurrentPosition();
        }
        return -1;
    }
});

VODモードでは、動画の長さが比較的大きい場合があり、ノードのマッチング時に再生時刻が近いノードを優先的にマッチングできればP2P効果の向上に役立ちます。この機能を実現するには、SDK側でプレイヤーの現在の再生時刻を取得する必要があります:

kotlin
P2pEngine.instance?.setPlayerInteractor(object : PlayerInteractor() {
    override fun onCurrentPosition(): Long {
        // Exoplayer 単位:ミリ秒
        return player?.currentPosition ?: -1
    }
})
java
P2pEngine.getInstance().setPlayerInteractor(new PlayerInteractor() {
    public long onCurrentPosition() {
        // Exoplayer 単位:ミリ秒
        if (play != null) {
            return player.getCurrentPosition();
        }
        return -1;
    }
});

動的なurlパスの問題を解決する

一部のCDN事業者が提供するurlは動的に生成されており、ノードごとにアドレスが異なります。例えばexample.com/clientId1/streamId.m3u8とexample.com/clientId2/streamId.m3u8のようになります。本プラグインはデフォルトでurl(クエリパラメータを除いたもの)をchannelIdとして使用します。このような場合、実際に同じライブ配信/動画を視聴している(同じファイルをダウンロードしている)ノードを同じチャンネルに配置するために、共通のchannelIdを構築する必要があります。channelIdの構築方法は以下の通りです:

kotlin
val videoId = extractVideoIdFromUrl(urlString)     // extractVideoIdFromUrl は自身で定義する必要があり、url内の動画IDを抽出して結果として返すことができます
val parsedUrl = P2pEngine.instance?.parseStreamUrl(urlString, videoId)
java
String videoId = extractVideoIdFromUrl(urlString);     // extractVideoIdFromUrl は自身で定義する必要があり、url内の動画IDを抽出して結果として返すことができます
String parsedUrl = P2pEngine.getInstance().parseStreamUrl(urlString, videoId);

WARNING

他のプラットフォームと相互接続する場合は、両者が同じ token と channelId を持つことを必ず確認してください。

厳密なセグメントIDモード

デフォルトのシーケンス番号ベースの代わりに、urlベースのSegmentIdを使用します:

kotlin
P2pEngine.instance?.setHlsSegmentIdGenerator(StrictHlsSegmentIdGenerator())
java
P2pEngine.getInstance().setHlsSegmentIdGenerator(new StrictHlsSegmentIdGenerator());

HTTPリクエストヘッダーの設定

不正リンク防止や統計上の必要性から、一部のHTTPリクエストに User-Agent などのヘッダー情報を追加する必要がある場合、setHttpHeaders で設定できます:

kotlin
val headers = mapOf("User-Agent" to "XXX")
P2pEngine.instance?.setHttpHeadersForHls(headers)
P2pEngine.instance?.setHttpHeadersForDash(headers)
java
Map headers = new HashMap();
headers.put("User-Agent", "XXX");
engine.setHttpHeadersForHls(headers);
engine.setHttpHeadersForDash(headers);

再生時間オフセットを設定する

m3u8に特殊なタグを設定することで、プレイヤーを強制的にリストの先頭位置から読み込ませることができ、P2Pの効果を向上させられますが、同時に遅延が増加するため、トレードオフを考慮する必要があります。

kotlin
val config = P2pConfig.Builder()
        .insertTimeOffsetTag(0.0)
        .build()
java
P2pConfig config = new P2pConfig.Builder()
        .insertTimeOffsetTag(0.0)
        .build();

プレイヤーのカクつき統計

SwarmCloudの管理パネルではクライアントの平均カクつき率を監視できます。プレイヤーでカクつきが発生した際にSDKへ報告するだけで済みます。Exoplayerを例にすると:

kotlin
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()
                        }
                    }
                }
            }, 7000)
        }
    }
})
java
player.addListener(new Player.Listener() {
    Boolean isDetecting = false;
    @Override
    public void onPlaybackStateChanged(int playbackState) {
        if (playbackState == 2) {       // STATE_BUFFERING
            if (isDetecting) return;
            isDetecting = true;
            new Timer().schedule(new TimerTask() {
                @Override
                public void run() {
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            isDetecting = false;
                            if (!player.isPlaying()) {
                                P2pEngine.getInstance().notifyPlaybackStalled();
                            }
                        }
                    });
                }
            }, 8000);
        }
    }
});

m3u8またはmpdファイルのインターセプト

SDKはm3u8/mpdの内容を解析する必要があるため、暗号化されたm3u8/mpdを使用している場合は、インターセプターで内容をインターセプトし、標準的なm3u8/mpdファイルを返す必要があります:

kotlin
P2pEngine.instance?.setHlsInterceptor(object : HlsInterceptor() {
    override fun interceptPlaylist(text: ByteArray, url: String): ByteArray {
        return handlePlaylist(text, url);
    }
})
java
P2pEngine.getInstance().setHlsInterceptor(new HlsInterceptor() {
    @Override
    public byte[] interceptPlaylist(byte[] text, String url) {
        return handlePlaylist(text, url);
    }
});

拡張子のないメディアファイルへの対応

一部の特殊なファイルは ".ts" で終わらず、他のいかなる拡張子も持たない場合があります。この場合、hook関数でそれがメディアファイルかどうかを判定できます:

kotlin
P2pEngine.instance?.setHlsInterceptor(object : HlsInterceptor() {
    override fun isMediaSegment(url: String): Boolean {
        return true
    }
})
java
P2pEngine.getInstance().setHlsInterceptor(new HlsInterceptor() {
    @Override
    public boolean isMediaSegment(@NonNull String url) {
        return true;
    }
});

カスタムOkHttpClientの指定

kotlin
val httpClient = OkHttpClient.Builder()
    .addInterceptor(YourInterceptor())
    .build()
val config = P2pConfig.Builder().okHttpClient(httpClient).build()
java
OkHttpClient httpClient = new OkHttpClient.Builder()
                .addInterceptor(new YourInterceptor())
                .build();
P2pConfig config = new P2pConfig.Builder()
        .okHttpClient(httpClient)
        .build();

特定のセグメントファイルを除外する

特定のセグメントファイル、例えばSSAI(Server Side Ad Insertion)によって生成されたユーザー固有のセグメントをP2Pに参加させたくない場合があります。このような場合、segmentBypass 関数を利用してフィルタリングできます:

kotlin
P2pEngine.instance?.setHlsInterceptor(object : HlsInterceptor() {
    override fun shouldBypassSegment(url: String): Boolean {
        return isSSAISegment(url)
    }
})
java
P2pEngine.getInstance().setHlsInterceptor(new HlsInterceptor() {
    @Override
    public boolean shouldBypassSegment(@NonNull String url) {
        return isSSAISegment(url);
    }
});

SDKの例外情報を監視する

ネットワーク、サーバー、アルゴリズムのバグなどの理由により、SDKに例外が発生する場合があります。registerExceptionListener メソッドで例外を監視できます:

kotlin
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
    }
})
java
P2pEngine.getInstance().registerExceptionListener(new EngineExceptionListener() {
    @Override
    public void onTrackerException(EngineException e) {
        // Tracker Exception
    }

    @Override
    public void onSignalException(EngineException e) {
        // Signal Server Exception
    }

    @Override
    public void onSchedulerException(EngineException e) {
        // Scheduler Exception
    }

    @Override
    public void onOtherException(EngineException e) {
        // Other Exception
    }
});

粤ICP备18075581号