The Google Meet Live Sharing SDK is in preview. Developers can apply for access through our Early Access Program.

Implement the Co-Watching API

This page describes how to use the Google Meet Live Sharing Co-Watching API.

Initial setup

Before you begin, the live sharing app should initialize a CoWatchingSession object to prepare the library for use.

The following example shows a basic initialization use case:

Java

class AwesomeVideoMeetingDisconnectHandler implements MeetingDisconnectHandler {
};

class AwesomeVideoCoWatchingSessionDelegate implements CoWatchingSessionDelegate {
  // For example implementation, see the "Manage remote state" section below.
};

public ListenableFuture<CoWatchingSession> initialSetup() {
  LiveSharingClient meetClient = LiveSharingClientFactory.getClient();

  ListenableFuture<LiveSharingMeetingInfo> meetingInfoFuture =
      meetClient.connectMeeting(
          appContext,
          /* liveSharingApplicationName= */ "AwesomeVideoApp",
          new AwesomeVideoMeetingDisconnectHandler());

  return Futures.transformAsync(
      meetingInfoFuture,
      (LiveSharingMeetingInfo meetingInfo) -> {
        this.meetingInfo = meetingInfo;
        return meetClient.beginCoWatching(new AwesomeVideoCoWatchingSessionDelegate());
      },
      executor);
}

Notify on user actions

When the local user performs actions—for example, pausing or seeking the media playout on their device—the library must be informed so those actions can be mirrored to other participants in the co-watching experience. For an example of how to notify the library for multiple states, see Get started.

The following example shows a basic use case:

Java

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

Manage remote state

In order to apply incoming updates from remote participants, you must offer Meet a way to directly manage the local media playout state using the CoWatchingSessionDelegate.onCoWatchingStateChanged() callback.

Meet also needs to retrieve the current state of the media playout by calling the CoWatchingSessionDelegate.onCoWatchingStateQuery() callback. This might be called regularly, so it should be written to be performant (for example, <100 ms).

The following example shows an implementation of the CoWatchingSessionDelegate:

Java

class AwesomeVideoCoWatchingSessionDelegate implements CoWatchingSessionDelegate {
  // Apply 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());
    }

    // If the timestamp in the applied update has sufficiently diverged, adjust
    // the local video playout.
    if (newState.mediaPlayoutPosition()
                .minus(this.videoPlayer.videoTimestamp)
                .abs() > Duration.ofMillis(500)) {
      this.videoPlayer.seek(newState.mediaPlayoutPosition());
    }

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

  // Return local video playback state.
  public Optional<CoWatchingState> onCoWatchingStateQuery() {
    CoWatchingState myCurrentPlaybackState =
        CoWatchingState.builder()
            .setMediaId(this.videoPlayer.videoUrl)
            .setMediaPlayoutPosition(this.videoPlayer.videoTimestamp)
            .setPlaybackState(this.videoPlayer.isPaused ? PlaybackState.PAUSE : PlaybackState.PLAY)
            .setMediaPlayoutRate(1.0)
            .build();
    return Optional.of(myCurrentPlaybackState);
  }
}