A movie-watching app is an excellent solution for remote friends or lovers who still want to enjoy watching content together.
In this article, I will introduce how I built the movie-watching app that allowed me to spend quality free time with my girlfriend, who lives in a different city. We will use ZEGOCLOUD solutions.
Implementation of your movie-watching app
Set up the development environment
- Android Studio (version 2.1 or later)
- Android SDK 25, Android SDK Build-Tools 25.0.2, Android SDK Platform-Tools 25. x.x or later
- An Android device (Android 4.1 or later) with a microphone and camera
- Android is connected to the internet.
Prerequisites
Here are a few things that need to be done before you can start building with ZEGOCLOUD.
- Create an account with ZEGOCLOUD. I already have an account, so I can skip this step.
- Create a project in the ZEGOCLOUD Admin Console and obtain your AppID and AppSign, which are required for SDK authentication. For details, see ZEGOCLOUD Admin Console-Project management.
Now, we can start coding!
Movie-watching app Feature Overview
This is gonna be a simple app with the following features:
- There will be two participants joining a room to watch a movie together. Let’s call them viewer A and viewer B.
- To ensure that the two participants can have a truly synchronized watching together experience, I decided to play and stream the movie to the room from a separate device. Let’s call it the movie streamer. The consideration in this decision is that if the film is played and streamed to the room from the device of one of the participants, then the streaming latency may result in a perceivable out-of-sync issue between the two participants. But if the movie is played and streamed from a separate device, then both participants will subscribe to and play the movie stream from the cloud. The probability of having a perceivable out-of-sync issue will be much lower.
- Participants in the room can have video chat and text chat while watching the movie together.
- To make it simple, in this project, I set the maximum number of participants in a room to 3.
The movie streamer side – Play a movie and stream it in the room
1: Create an ZegoExpressEngine
instance.
/// Define the ZegoExpressEngine object
ZegoExpressEngine engine;
/// Specify the AppID and AppSign for SDK authentication
/// AppID Format:123456789L
long appID = ;
/// AppSign Format:"0123456789012345678901234567890123456789012345678901234567890123"
String appSign = "";
/// Create a ZegoExpressEngine instance
engine = ZegoExpressEngine.createEngine(appID, appSign, true, ZegoScenario.GENERAL, getApplication(), null);
2: Enable custom video capture for the engine.
ZegoCustomVideoCaptureConfig videoCaptureConfig = new ZegoCustomVideoCaptureConfig();
// Use RAW_DATA as video buffer data type
videoCaptureConfig.bufferType = ZegoVideoBufferType.RAW_DATA; engine.enableCustomVideoCapture(true, videoCaptureConfig, ZegoPublishChannel.MAIN);
3: Set a callback handler for custom video capture and implement the callback methods.
// Set the engine itself as the callback handler object
engine.setCustomVideoCaptureHandler(new IZegoCustomVideoCaptureHandler() {
@Override
public void onStart(ZegoPublishChannel channel) {
// On receiving the onStart callback, start to capture video and send the captured video frame data to the ZegoExpressEngine.
...
}
@Override
public void onStop(ZegoPublishChannel channel) {
// On receiving the onStop callback, stop the video capture process.
...
}
});
4: Join a room.
/// Create a user
ZegoUser user = new ZegoUser("userA");
/// Join a room
engine.loginRoom("room", user);
Step 5: Set up a video event handler for the ZegoMediaPlayer
. By setting up this handler, you can receive the video frame data of the movie played through the callback. onVideoFrame
.
mZegoMediaPlayer.setVideoHandler(
new IZegoMediaPlayerVideoHandler() {
@Override
public void onVideoFrame(ZegoMediaPlayer zegoMediaPlayer, ByteBuffer[] byteBuffers, int[] ints, ZegoVideoFrameParam zegoVideoFrameParam) {
if (RoomManager.getInstance().isCanSenRawData()) {
int totalDataLength = byteBuffers[0].capacity();
if (tempByteBuffer == null || tempByteBuffer.capacity() != totalDataLength) {
tempByteBuffer = ByteBuffer.allocateDirect(byteBuffers[0].capacity()).put(byteBuffers[0]);
} else {
tempByteBuffer.clear();
tempByteBuffer.put(byteBuffers[0]);
}
ZegoSDKManager.getInstance().getStreamService().sendCustomVideoCaptureRawData(tempByteBuffer, tempByteBuffer.capacity(), zegoVideoFrameParam);
}
}
}, ZegoVideoFrameFormat.RGBA32);
Step 6: Start stream publishing. Load the movie from the specified file path (local file path or an URL that points to an internet media resource), and start playing the movie.
/// Start publishing the stream
engine.startPublishingStream("streamMovie");
mZegoMediaPlayer.loadResource(path, new IZegoMediaPlayerLoadResourceCallback() {
@Override
public void onLoadResourceCallback(int code) {
if (code == 0) {
mZegoMediaPlayer.start();
if (callback != null) {
callback.onLoadResourceCallback(code);
}
}
}
});
Step 7: When no other participants are in the room or the participant publishing the stream leaves the room, stop posting the stream, and the callback for stopping custom video capture will be triggered.
ZegoSDKManager.getInstance().getDeviceService().setCustomVideoCaptureHandler(new IZegoVideoCaptureCallback() {
@Override
public void onStart(ZegoPublishChannel channel) {
canSenRawData = true;
}
@Override
public void onStop(ZegoPublishChannel channel) {
canSenRawData = false;
}
});
The movie viewer’s side
Subscribe to the movie stream to watch together, video chat, and text chat
To realize the watch together feature, we will need to implement the following:
- The viewers subscribe to the video stream of the movie being played so they can watch it together.
- Both viewers can have control over the movie playback. For example, either one of them can pause and resume the playback.
- Stream publishing/subscribing and camera/microphone operations for video chat while watching the movie.
- Sending and receiving real-time text messaging in the room.
The high-level program logic for movie playing:
- The movie streamer will create a room first, and the viewers need to join the same room (by specifying the same Room ID when calling the
loginRoom
Method). If viewers attempt to join a room that doesn’t exist or without a movie streamer in there, they will be promoted with a messageRoom doesn't exist
. If the specified room has already reached its total capacity, the login will fail, and the user will be notified accordingly. - When a viewer clicks the Start Movie button, call them setRoomExtraInfo() to notify the movie streamer. Upon receiving such information through the onRoomExtraInfoUpdate() callback, the movie streamer will execute the logic to start playing the movie and stream the film to the room.
- The movie stream will then be received by both viewers and played on their devices.
/// start playing stream
engine.startPlayingStream("stream1", new ZegoCanvas(play_view));
- Both viewers can call the
setRoomExtraInfo(String roomID,String key,String value,null)
method to control the movie playback (Play or Pause).
The following table shows the key-value parameters you can pass to the setRoomExtraInfo
plan to send out different notifications:
key | value |
---|---|
room info | 0: movie being loaded… 1: movie playing 2: movie on pause 3: the room has been closed |
The high-level program logic for video chat:
- After joining the room, viewer A (or B) can start publishing video from the camera with a unique Stream ID and start the local preview.
- When the other viewer joins the room and starts publishing his/her video to the room, the ZEGO Express SDK will send out an event notification accordingly. Viewer A (or B) then can start playing the other viewer’s video using the Stream ID received from the callback notification.
- Both viewers can control their camera (e.g., switch between front/rear camera, turn on/off the camera) and microphone (e.g., mute/unmute) as they wish.
/// Switch between the front/rear camera
expressEngine.useFrontCamera(front);
/// Turn on/off the camera
expressEngine.enableCamera(enable);
/// Mute or unmute the microphone
expressEngine.muteMicrophone(!enable);
The high-level program logic for text chat:
- Both viewers can send and receive real-time text messages in the room.
- Call
sendBroadcastMessage
method to send a Broadcast Message (no longer than 1024 bytes) to other participants in the room.
/// Send broadcast messages
engine.sendBroadcastMessage(roomID, msg, new IZegoIMSendBroadcastMessageCallback() {
@Override
public void onIMSendBroadcastMessageResult(int errorCode, long messageID) {
/// Returned result of sending broadcast messages
}
});
- Implement the callback method
onIMRecvBroadcastMessage
defined inIZegoEventHandler
to listen for and handle the Broadcast Messages sent by other participants. You can get the message details from this callback, including the message content, message ID, time the message was sent, and message sender.java /// reveice message public void onIMRecvBroadcastMessage(String roomID, ArrayList<ZegoBroadcastMessageInfo> messageList){ }
How the final movie-watching app looks like
The movie streamer view
Viewers view
You may download the demo app and try it yourself:
Demo instructions:
- Start the movie streamer, choose a movie, then configure the Room ID.
- Start the viewer app and enter the same Room ID.
Conclusion
After reading this article, I hope you’ll see that making an app for watching movies together is not that hard at all. Ready to create your own app?
Let’s Build APP Together
Start building with real-time video, voice & chat SDK for apps today!