实现 Co-Doing API

本页介绍了如何使用 Co-Doing API 支持协同操作 场景。

初始设置

要准备库以供使用,实时共享应用应初始化 CoDoingClient 对象。

要使用 Meet 实时共享 SDK,请调用 AddonClientFactory.getClient 方法。这将返回一个 AddonClient 作为共同操作会话的入口点

要使用客户端,请调用 newSessionBuilder AddonClient 方法返回新 AddonSessionnewSessionBuilder 会实现 AddonSessionHandler 用于处理由 API 提供的 插件

要开始会话,请添加 withCoDoing 添加到构建器上。

以下代码示例展示了协作操作客户端的基本初始化 对象:

Java

class AwesomeVideoAddonSessionHandler implements AddonSessionHandler {}

//For sample implementation, see the "Handle incoming updates" section.
class AwesomeVideoCoDoingHandler implements CoDoingHandler {}

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

暂停视频

参与实时共享体验时,如果用户暂停了播放 进行直播,那么您必须确保所有参与者都能 也会暂停视频

为此,请制作一个 CoDoingState 显示视频已暂停的消息,并指示 Google Meet 向该视频广播 其他所有使用 setGlobalState 方法。共享的全局状态会成为所有参与者的默认状态, 现有状态或新的状态,直到设置新状态。

以下代码示例展示了如何通知用户已暂停状态:

Java

public void onVideoPaused(String videoUrl, Instant currentTimestamp) {
  // Create an internal state object to share with other participants. Note: It's
  // good practice to encode all metadata—even seemingly irrelevant data—into
  // ActivityState updates to guard against race conditions and other subtle
  // failures.
  AwesomeVideoState videoState = AwesomeVideoState
    .builder()
    .videoUrl(videoUrl)
    .videoTimestamp(currentTimestamp)
    .isPaused(true)
    .build();

  // Create the CoDoingState object to wrap the internal state
  CoDoingState coDoingState = new CoDoingState();
  coDoingState.state = SerializationUtils.serialize(videoState);

  // Use Meet to broadcast internal state update to all other participants
  this.coDoingClient.setGlobalState(coDoingState);
};

此代码示例触发要广播到的序列化 videoState 对象 参与实时共享的所有其他 Meet 实例 体验。详细了解如何从其他 请参阅处理传入更新 部分。

下图描述了暂停操作触发后的事件序列, 已触发:

启动实时共享 API 示意图。

取消暂停视频

暂停视频类似,如果用户在自己的设备上 则 Meet 必须将此操作广播到其他直播 共享参与者。

在发送方(即取消暂停视频的用户)方面, 暂停示例是 isPaused 状态已更新。

以下代码示例展示了如何从 发送方:

Java

public void onVideoUnpaused(String videoUrl, Instant currentTimestamp) {
  AwesomeVideoState videoState = AwesomeVideoState
    .builder()
    .videoUrl(videoUrl)
    .videoTimestamp(currentTimestamp)
    .isPaused(false)
    .build();

  CoDoingState coDoingState = new CoDoingState();
  coDoingState.state = SerializationUtils.serialize(videoState);

  this.coDoingClient.setGlobalState(coDoingState);
}

跳转视频

就像暂停视频取消暂停视频一样, 如果用户将本地应用上的时间轴拖动到新的时间戳, Meet 必须向所有参与者广播此操作。

以下代码示例展示了如何通知用户时间戳已更新 :

Java

public void onVideoSeeked(String videoUrl, Instant currentTimestamp, bool isPaused) {
  AwesomeVideoState videoState = AwesomeVideoState
    .builder()
    .videoUrl(videoUrl)
    .videoTimestamp(currentTimestamp)
    .isPaused(isPaused)
    .build();

  CoDoingState coDoingState = new CoDoingState();
  coDoingState.state = SerializationUtils.serialize(videoState);

  this.coDoingClient.setGlobalState(coDoingState);
}

播放其他视频

如果用户还通过选择 Meet 必须播放新视频才能进行所有实时共享 参与者。更改后的视频存储在 videoState.videoUrl 中。

以下代码示例展示了如何通知用户视频网址已更新:

Java

public void onVideoChanged(String videoUrl, Duration currentTimestamp, bool isPaused) {
  AwesomeVideoState videoState = AwesomeVideoState
    .builder()
    .videoUrl(videoUrl)
    .videoTimestamp(currentTimestamp)
    .isPaused(isPaused)
    .build();

  CoDoingState coDoingState = new CoDoingState();
  coDoingState.state = SerializationUtils.serialize(videoState);

  this.coDoingClient.setGlobalState(coDoingState);
}

结束共享活动

当用户选择结束 Activity 时, endSession 方法与 Meet 应用断开连接。这并不强制 Meet 结束会议,且不会导致用户退出会议 会议。

以下代码示例展示了如何通知用户会话已停止:

Java

public void endCoDoing() {
  this.session.endSession();
}

处理传入的更新

当其他参与者的 Meet 应用收到广播时, onGlobalStateChanged() 触发回调函数。通常情况下,在决定要采用哪种方法 对传入更新执行的操作(例如,仅匹配传入的 视频时间戳(如果它们与本地时间戳明显不同)。

以下代码示例展示了如何处理不同的传入更新:

Java

class AwesomeVideoCoDoingHandler implements CoDoingHandler {
  public void onGlobalStateChanged(CoDoingState update) {
    AwesomeVideoState videoState = SerializationUtils.deserialize(update.state());

    // Handle transition to new video.
    if (!videoState.videoUrl.equals(this.videoPlayer.videoUrl)) {
      this.videoPlayer.loadVideo(videoState.videoUrl);
    }

    // If the timestamp in the arriving update has sufficiently diverged, adjust
    // the local video playout.
    if (videoState.videoTimestamp.minus(this.videoPlayer.videoTimestamp).abs() >
                                        Duration.ofSeconds(2)) {
      this.videoPlayer.seek(videoState.videoTimestamp);
    }

    // Update pause state, if necessary.
    if (!videoState.isPaused && this.videoPlayer.isPaused) {
      this.videoPlayer.unpause();
    } else if (videoState.isPaused && !this.videoPlayer.isPaused) {
      this.videoPlayer.pause();
    }
  }
}