This page describes how to use the Co-Watching API to support a co-watching scenario.
Initial setup
To prepare the library for use, the live sharing application should initialize a
CoWatchingClient
object which represents a co-watching session.
To use the Meet Live Sharing SDK, call the
AddonClientFactory.getClient
method. This returns an
AddonClient
that serves as the entry point for the co-watching session.
To use the client, call the
newSessionBuilder
method from the AddonClient
to return a builder for a new
AddonSession
.
The newSessionBuilder
implements the
AddonSessionHandler
interface to handle the callbacks provided by the
add-on for the session.
To begin a session, add the
withCoWatching
method onto the builder.
The following code sample shows a basic initialization of the co-watching client object:
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();
}
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.
You can control the co-watching state using these methods:
CoWatchingClient.notifyBuffering
: Notifies Meet that the media isn't ready to be played out due to buffering, due to a prior media switch, media seek, or normal network congestion.CoWatchingClient.notifyEnded
: Notifies Meet that the media player has reached the end of the current media.CoWatchingClient.notifyPauseState
Notify Meet that the user has paused or unpaused the playback of media, so Meet can mirror that action for other users.CoWatchingClient.notifyPlayoutRate
: Notifies Meet that the user has updated the playout rate of the media to a new value (for example, 1.25x).CoWatchingClient.notifyQueueUpdate
: Notifies Meet that the queue has changed, so Meet can mirror that for other users.CoWatchingClient.notifyReady
: Notifies Meet that the buffering is complete and the media is now ready to be played, starting at the supplied timestamp.CoWatchingClient.notifySeekToTimestamp
: Notifies Meet that the user has seeked the playback point of the media, so Meet can mirror that action for other users.CoWatchingClient.notifySwitchedToMedia
: Notifies Meet that the user has switched media, so Meet can pass that along to other users. Also has an option for simultaneous queue update.
The following code sample shows how to notify users:
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);
};
Manage remote state
To apply incoming updates from remote participants, you must offer
Meet a way to directly manage the local media playout state using
the
CoWatchingHandler.onCoWatchingStateChanged()
callback.
Meet also needs to retrieve the current position of the media
playout by calling the
CoWatchingHandler.onStateQuery()
callback. This is called regularly, so it should be written to be performant
(for example, <100 ms).
The following code sample shows an implementation of the
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));
}
}