How to Make a Movie-Watching App in a Few Hours

Talk to an Expert
Since my girlfriend and I reside in different cities, we don't get to spend much time in each other's physical company. When we were on video chat one day, she said: "Hey, it's been a while since the two of us went to the cinema together. When can we watch do that again... "
She had a point. It's been quite some time...
Though we couldn't see each other right away, we could watch a movie together online. I am aware that there are several apps available to achieve that. More and more of these apps and services have come out to help people stay in touch, especially since the pandemic started. Then it occured to me that, wouldn't it be cool if I made an exclusive app just for the two of us?
I have everything I need to build such an app, as the company I work for, ZEGOCLOUD, is the cloud-based RTC API platform that allows developers easily integrate real-time audio/video and in-app chat features into any app.
So I went on staring my little project.
...
A few hours later, I got this:

As a developer, I enjoy constructing things and discussing how they are made. To help you better understand the implementation, I'm going to detail some of the most significant processes in the following parts.
The implementation
Set up the development environment
- Android Studio (version 2.1 or later)
- Android SDK 25, Build-Tools 25.0.2, Platform-Tools 25.x.x or later
- A device running Android 4.1 or later with a camera and microphone
- Internet connection
Prerequisites
Here are a few prerequisites that must be met before you can begin developing using ZEGOCLOUD.
- Create a ZEGOCLOUD account. Clearly, I already have an account, therefore this step is unnecessary.
- In the ZEGOCLOUD Admin Console, get your AppID and AppSign, which are needed for SDK authentication. See ZEGOCLOUD Admin Console - Project Management for further information.
Now let's begin coding!
Features overview
This will be a simple app that can do the following:
2 participants will join in the room and watch a movie together. Viewer A and Viewer B.
I decided to play and stream the movie to the room from a second device to guarantee a synchronized watching experience. It'll be known as the movie streamer. We will call it the movie streamer.
Streaming delay may cause out-of-sync issues if the movie is played from one of the participants' devices. If the movie is streamed from a separate device, both participants will be subscribing to and playing the cloud-based movie stream, reducing the likelihood of an out-of-sync issue.
While watching the movie, participants can video chat and text chat.
To keep things simple, I limited the number of people in the room to 3 for this project.
Movie streamer - Play a movie and have it streamed into the room
Step 1: Create a 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);
Step 2: Set the engine to capture custom video.
ZegoCustomVideoCaptureConfig videoCaptureConfig = new ZegoCustomVideoCaptureConfig();
// Use RAW_DATA as video buffer data type
videoCaptureConfig.bufferType = ZegoVideoBufferType.RAW_DATA; engine.enableCustomVideoCapture(true, videoCaptureConfig, ZegoPublishChannel.MAIN);
Step 3: To use custom video capture, create a callback handler and implement the callback functions.
// 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.
...
}
});
Step 4: Join the room.
/// Create a user
ZegoUser user = new ZegoUser("userA");
/// Join a room
engine.loginRoom("room", user);
Step 5: Create a video event handler for ZegoMediaPlayer
. By configuring this handler, you may get video frame data from the movie being played via 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 publishing to the stream. Start playing the movie from the supplied file location (local file directory or URL).
/// 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: Stop publishing the stream when there are no other people in the room or when the person who is publishing the stream leaves the room. This will trigger the callback for stopping custom video capture.
ZegoSDKManager.getInstance().getDeviceService().setCustomVideoCaptureHandler(new IZegoVideoCaptureCallback() {
@Override
public void onStart(ZegoPublishChannel channel) {
canSenRawData = true;
}
@Override
public void onStop(ZegoPublishChannel channel) {
canSenRawData = false;
}
});
Movie viewer - subscribe to the movie stream to watch movies together, as well as video and text chat
To make the "watch together" feature work, we'll have to do the following:
- The viewers subscribe to the video stream of the currently playing movie so that they may watch it together.
- Both viewers can control the movie's playback. For instance, either of them can pause and restart playing.
- Publish/subscribe to a stream and use the camera and microphone to video chat while watching a movie.
- Real-time texting in the room.
High-level logic for movie-playing:
The movie streamer will first set up a room, and the viewers must join it (by using the same Room ID when using the loginRoom
function).
If viewers try to join a room that doesn't exist or has no movie streamer, they'll see Room doesn't exist
.
The room login will fail if the given room has surpassed its maximum capacity, and the participants will be notified.
Call setRoomExtraInfo()
to send a notice to the movie streamer when a viewer presses the Start Movie button. The movie streamer will perform the logic to start playing the movie and streaming the movie to the room once it receives such notice via the onRoomExtraInfoUpdate()
callback.
Both viewers will then receive the movie stream and watch it on their own devices.
/// start playing stream
engine.startPlayingStream("stream1", new ZegoCanvas(play_view));
The function setRoomExtraInfo(String roomID,String key,String value,null)
may be used by both viewers to control the movie playing (Play or Pause).
The following table provides key-value arguments you may supply to setRoomExtraInfo
to deliver different notifications:
keyvalueroomInfo0: movie being loaded...
1: movie playing
2: movie on pause
3: room has been closed
High-level logic for video chat:
- After entering the room, viewer A (or B) can begin publishing video from the camera with a unique Stream ID and begin the local preview.
- When another viewer joins the room and begins publishing video to the room, the ZEGO Express SDK will issue an event notice. Viewer A (or B) can then begin playing the video of the other viewer by inputting the Stream ID obtained from the callback message.
- Both viewers may control their camera (front/rear, on/off) and microphone (mute/unmute).
/// 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);
High-level logic for text chat:
- Both viewers in the room can send and receive text messages in real time.
- To send a Broadcast Message (no longer than 1024 bytes) to other participants in the room, use the
sendBroadcastMessage
function.
/// 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
onIMRecvBroadcastMessage
callback function specified inIZegoEventHandler
to listen for and handle Broadcast Messages sent by other participants. This callback returns message data such as message content, message ID, time message was delivered, and message sender.
/// reveice message
public void onIMRecvBroadcastMessage(String roomID, ArrayList<ZegoBroadcastMessageInfo> messageList){
}
How the final app looks like
The movie streamer view

Viewer 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
I hope that after reading this article, you'll see that making an app for watching movies together is not that hard at all. Ready to create your own app?