This page describes how to use the Co-Doing API to support a co-doing scenario.
Initial setup
Similar to the Co-Watching initial setup, the following example shows a basic initialization use case:
Java
class AwesomeVideoAddonSessionHandler implements AddonSessionHandler {}
// For sample implementation, see the "Manage current state" section below.
class AwesomeVideoCoDoingHandler implements CoDoingHandler {}
public ListenableFuture<AddonSession> initialSetup() {
AddonClient meetClient = AddonClientFactory.getClient();
return meetClient
.newSessionBuilder(
/* liveSharingApplicationName= */ "awesome-video-app",
appContext,
new AwesomeVideoAddonSessionHandler())
.withCoDoing(new AwesomeVideoCoDoingHandler())
.begin();
}
Pause video
When participating in a live sharing experience, if a user pauses the playback on their local video app then presumably you'll likely want to make sure all participants in the live sharing experience also pause their video.
To do this, craft a
CoDoingState
message showing the video is paused, and tell Meet to broadcast it all other
live sharing participants:
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);
};
The above code triggers the serialized VideoState
object to be broadcast to
all other instances of Meet currently participating in the live sharing
experience. For details on how to receive broadcast updates from other
participants, see the Handle incoming updates
section below.
The following sequence diagram describes the sequence of events after the pause action is triggered:
Unpause video
Similar to pause, if a user unpauses the video on their local app Meet needs to broadcast this operation to other live sharing participants.
On the sender side (the user who unpauses the video) the only difference from
the pause example is the isPaused
status is updated. The sender side code
should look like this:
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);
}
Seek video
When a user drags the timeline on the local app to a new timestamp, we want to broadcast this operation to all participants similar to the pause and unpause sections. The sender side code looks like this:
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);
}
Play a different video
If the user also changes the video being watched by selecting another video on
the local app, we need to play the new video for all live sharing participants.
The change in video is held in 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);
}
End co-doing
When the user selects "stop live sharing,” we need to end their live sharing experience.
After the library is called to end the meeting, the live sharing app can quit or go back to its homepage (depending on the product’s preference).
Java
public void endCoDoing() {
this.session.endSession();
}
Handle incoming updates
When another participant’s Meet app receives a broadcast, the
onGlobalStateChanged()
callback is triggered. Usually, it's important to make good decisions on what
action to take in response to incoming updates such as only matching incoming
video timestamps if they're sufficiently different from the local timestamp.
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();
}
}
}