實作 Co-Watching API

本頁面說明如何使用 Co-Watching API 支援共同觀看情境。

初始設定

如要準備程式庫使用,即時分享應用程式應初始化代表共同觀看工作階段的 CoWatchingClient 物件。

如要使用 Meet 即時分享 SDK,請呼叫 AddonClientFactory.getClient 方法。這樣做會回傳 AddonClient,做為共同觀看工作階段的進入點。

如要使用用戶端,請從 AddonClient 呼叫 newSessionBuilder 方法,傳回新 AddonSession 的建構工具。newSessionBuilder 會實作 AddonSessionHandler 介面,以處理工作階段外掛程式提供的回呼。

如要啟動工作階段,請將 withCoWatching 方法新增至建構工具。

以下程式碼範例顯示共同觀看用戶端物件的基本初始化:

Java

class AwesomeVideoAddonSessionHandler implements AddonSessionHandler {}

// For sample implementation, see the "Manage remote state" section below.
class AwesomeVideoCoWatchingHandler implements CoWatchingHandler {}

public ListenableFuture<AddonSession> initialSetup() {
  AddonClient meetClient = AddonClientFactory.getClient();
  return meetClient
      .newSessionBuilder(
          new AwesomeVideoAddonSessionHandler())
      .withCoWatching(new AwesomeVideoCoWatchingHandler())
      .begin();
}

在使用者動作時傳送通知

當本機使用者在裝置上執行動作 (例如暫停或尋找播放媒體) 時,程式庫必須獲得通知,才能將這些動作鏡像至共同觀看體驗中的其他參與者。如需如何通知程式庫多個狀態的範例,請參閱「開始使用」。

您可以透過以下方法控制共同觀看狀態:

以下程式碼範例說明如何通知使用者:

Java

public void onVideoPaused(Duration currentTimestamp) {
  // Use Meet to broadcast the pause state to ensure other participants also pause.
  this.session.getCoWatching().notifyPauseState(/* paused= */ true, currentTimestamp);
};

管理遠端狀態

如要套用遠端參與者傳入的更新,您必須讓 Meet 使用 CoWatchingHandler.onCoWatchingStateChanged() 回呼直接管理本機媒體播放狀態。

Meet 也需要呼叫 CoWatchingHandler.onStateQuery() 回呼,以擷取媒體播放的目前位置。系統會定期呼叫這個 API,因此其寫入作業必須具有高效能 (例如小於 100 毫秒)。

以下程式碼範例顯示 CoWatchingHandler 的實作方式:

Java

class AwesomeVideoCoWatchingHandler implements CoWatchingHandler {
  /** Applies incoming playback state to the local video. */
  public void onCoWatchingStateChanged(CoWatchingState newState) {
    // Handle transition to new video.
    if (!newState.mediaId().equals(this.videoPlayer.videoUrl)) {
      this.videoPlayer.loadVideo(newState.mediaId());
    }

    // Only adjust the local video playout if it's sufficiently diverged from the timestamp in the
    // applied update.
    if (newState
            .mediaPlayoutPosition()
            .minus(this.videoPlayer.videoTimestamp)
            .compareTo(Duration.ofMillis(500))
        > 0) {
      this.videoPlayer.seek(newState.mediaPlayoutPosition());
    }

    // Update pause state, if necessary.
    if (newState.playbackState().equals(PLAY) && this.videoPlayer.isPaused) {
      this.videoPlayer.unpause();
    } else if (newState.playbackState().equals(PAUSE) && !this.videoPlayer.isPaused) {
      this.videoPlayer.pause();
    }
  }

  /** Returns local video playback state. */
  public Optional<QueriedCoWatchingState> onStateQuery() {
    return Optional.of(QueriedCoWatchingState.of(
      /* mediaPlayoutPosition= */ this.videoPlayer.videoTimestamp));
  }
}