This topic describes how to quickly implement a basic real-time video call.
Before you begin, learn about the following basic concepts about audio and video:
For more concepts, see Glossary.
Before you begin, make sure you complete the following steps:
The following is complete HTML sample code for a basic video call and can be used for reference during development.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Zego Express Video Call</title>
<!-- Enter the correct SDK version number. -->
<script src="ZegoExpressWebRTC-x.x.x.js"></script>
<style type="text/css">
h1,
h4 {
text-align: center;
}
.video-wrapper {
width: 610px;
margin: 0 auto;
}
.video-wrapper h4 {
width: 300px;
display: inline-block;
position: relative;
}
#remote-video, #local-video {
width: 300px;
height: 270px;
display: inline-block;
position: relative;
}
.video-wrapper video {
height: auto;
}
</style>
</head>
<body>
<h1>
Zego RTC Video Call
</h1>
<div class="video-wrapper">
<h4>Local video</h4>
<h4>Remote video</h4>
<div id="local-video"></div>
<div id="remote-video"></div>
</div>
<script>
// Unique AppID of a project, which is of the Number type. You can obtain it in ZEGOCLOUD Admin Console.
let appID = 0
// Access server address, which is of the String type. You can obtain it in ZEGOCLOUD Admin Console. (For more information about how to obtain it, see Prerequisites.)
let server = ""
// Instance initialization
const zg = new ZegoExpressEngine(appID, server);
zg.setDebugVerbose(false)
// Room status update callback
// In the sample code, streams are published immediately after you successfully log in to a room. When implementing your service, you can choose to publish streams at any time when the room is connected status.
// Room status update callback
zg.on('roomStateChanged', async (roomID, reason, errorCode, extendedData) => {
if (reason == 'LOGINED') {
console.log ("Connected to the room successfully. You can perform operations such as stream publishing and playing only after the room is successfully connected.")
}
})
zg.on('roomUserUpdate', (roomID, updateType, userList) => {
// Notification of users joining or leaving a room
});
zg.on('roomStreamUpdate', async (roomID, updateType, streamList, extendedData) => {
// Notification of audio or video stream updates of other users in a room
if (updateType == 'ADD') {
// When streams are added, play them.
// In the sample code, the audio and video of the first stream in the stream addition list are played.
const streamID = streamList[0].streamID;
// The stream list specified by `streamList` contains the ID of the corresponding stream.
const remoteStream = await zg.startPlayingStream(streamID);
// Create a media stream player.
const remoteView = zg.createRemoteStreamView(remoteStream);
remoteView.play("remote-video", {enableAutoplayDialog:true});
} else if (updateType == 'DELETE') {
// When streams are deleted, stop playing them based on `streamID` of the streams in the stream deletion list specified by `streamList`.
const streamID = streamList[0].streamID;
zg.stopPlayingStream(streamID)
}
});
// Log in to a room. If the login succeeds, `true` is returned.
// The `roomUserUpdate` callback can be received only when `userUpdate` is set to `true`.
let userID = "user1"; // You can set `userID` based on your service requirements but ensure that it is globally unique.
let userName = "user1";// You can set `userName` based on your service requirements, which is not necessarily to be unique.
let roomID = "123"; // You can set `roomID` based on your service requirements but ensure that it is globally unique.
// The value of `token` is generated by your server. To quickly perform debugging, you can apply for a temporary audio and video token of the String type in ZEGOCLOUD Admin Console (https://console.zego.im/).
let token = ``;
zg.loginRoom(roomID, token, { userID, userName: userID }, { userUpdate: true }).then(async result => {
if (result == true) {
console.log("login success");
// Connected to the room successfully. You can perform operations such as stream publishing and playing only after the room is successfully connected.
// Create a stream and start the preview.
// After calling the createZegoStream method, you need to wait for the ZEGO server to return the local stream object before any further operation.
const localStream = await zg.createZegoStream();
// Play preview of the stream
localStream.playVideo(document.querySelector("#local-video"), {enableAutoplayDialog:true});
// Publish a stream to ZEGOCLOUD audio and video cloud. You can set `streamID` based on your service requirements but ensure that it is globally unique.
let streamID = new Date().getTime().toString();
zg.startPublishingStream(streamID, localStream)
}
});
// // Alternative code for room login
// (async function main(){
// await zg.loginRoom(roomID, token, { userID, userName: userID }, { userUpdate: true })
// })()
</script>
</body>
</html>
The following diagram shows a basic real-time video call process of User A playing a stream published by User B.
roomStreamUpdate
callback, indicating that there is a stream change in the room.roomStreamUpdate
callback event. When receiving the callback, indicating that there is a new stream, User A obtains the ID of the stream published by User B and plays the stream.The following provides a UI layout and sample code, helping you implement basic real-time audio and video features.
Open or create the index.html file and copy the following code to the file.
<html>
<head>
<meta charset="UTF-8">
<title>Zego Express Video Call</title>
<style type="text/css">
* {
font-family: sans-serif;
}
h1,
h4 {
text-align: center;
}
#local-video, #remote-video {
width: 400px;
height: 300px;
border: 1px solid #dfdfdf;
}
#local-video {
position: relative;
margin: 0 auto;
display: block;
}
#remote-video {
display: flex;
margin: auto;
position: relative !important;
}
</style>
</head>
<body>
<h1>
Zego RTC Video Call
</h1>
<h4>Local video</h4>
<div id="local-video"></div>
<h4>Remote video</h4>
<div id="remote-video"></div>
<script>
// Copy and paste the JavaScript sample code in the following part of this topic here.
// const zg = new ZegoExpressEngine(appID, server);
</script>
</body>
</html>
Create and initialize a ZegoExpressEngine
instance. Set the appID
and server
parameters to the AppID and access server address of your project.
Instances of ZegoExpressEngine cannot be processed reactively by the framework, otherwise unpredictable problems may occur.
Listen for callback events immediately to prevent yourself from missing any notifications. The SDK provides notification callbacks for room connection status updates, audio and video stream updates, and user login or logout.
// Unique AppID of a project, which is of the Number type. You can obtain it in ZEGOCLOUD Admin Console.
let appID = ;
// Access server address, which is of the String type. You can obtain it in ZEGOCLOUD Admin Console. (For more information about how to obtain it, see Prerequisites.)
let server = "";
// Instance initialization
const zg = new ZegoExpressEngine(appID, server);
// Room status update callback
zg.on('roomStateChanged', (roomID, reason, errorCode, extendData) => {
if (reason == 'LOGINING') {
// Logging in.
} else if (reason == 'LOGINED') {
// Login successful.
// Only after a user successfully logs in to a room or switches the room, can `startPublishingStream` and `startPlayingStream` be called to publish and play streams properly.
// Publish streams to ZEGOCLOUD audio and video cloud.
} else if (reason == 'LOGIN_FAILED') {
// Login failed.
} else if (reason == 'RECONNECTING') {
// Reconnecting.
} else if (reason == 'RECONNECTED') {
// Reconnection successful.
} else if (reason == 'RECONNECT_FAILED') {
// Reconnection failed.
} else if (reason == 'KICKOUT') {
// Forced to log out of a room.
} else if (reason == 'LOGOUT') {
// Logout successful.
} else if (reason == 'LOGOUT_FAILED') {
// Logout failed.
}
});
// Notification of users joining or leaving a room
// The `roomUserUpdate` callback can be received only when `ZegoRoomConfig` in which the `userUpdate` parameter is set to `true` is passed in the `loginRoom` method.
zg.on('roomUserUpdate', (roomID, updateType, userList) => {
if (updateType == 'ADD') {
for (var i = 0; i < userList.length; i++) {
console.log(userList[i]['userID'], 'joins the room:', roomID)
}
} else if (updateType == 'DELETE') {
for (var i = 0; i < userList.length; i++) {
console.log(userList[i]['userID'], 'leaves the room:', roomID)
}
}
});
zg.on('roomStreamUpdate', async (roomID, updateType, streamList, extendedData) => {
// Notification of audio or video stream updates of other users in a room
});
Some browsers do not support WebRTC. Therefore, you need to check whether your browser can properly run WebRTC before implementing the stream publishing and playing features.
You can call the checkSystemRequirements
method to check your browser's compatibility with WebRTC. For details about the check results, see the parameter description of the ZegoCapabilityDetection
API.
const result = await zg.checkSystemRequirements();
// The `result` parameter that is returned indicates the compatibility check result. WebRTC is supported if the value of `webRTC` is `true`. For other attributes of this API, see the API reference.
console.log(result);
// {
// webRTC: true,
// customCapture: true,
// camera: true,
// microphone: true,
// videoCodec: { H264: true, H265: false, VP8: true, VP9: true },
// screenSharing: true,
// errInfo: {}
// }
Alternatively, you can open the Online detection tool provided by ZEGOCLOUD in your browser to check its compatibility with WebRTC. For details about compatible browser versions, see Browser compatibility.
Generate a Token
A Token used for identity authentication is required for room login. You can apply for a temporary Token, which is valid for 24 hours, in ZEGOCLOUD Admin Console.
The temporary Token is available only for debugging. Before bringing your app into commercial use, generate a Token from your service server. For details, see Use Tokens for authentication.
Log in to a room
Call the loginRoom
method and set roomID
, token
, user
, and config
to log in to a room. If the room does not exist, calling this method will create and log in to the room.
roomID
, userID
, and userName
are user-defined.roomID
and userID
must be unique. You are advised to associate userID
with the account system of your service to make the value meaningful. roomUserUpdate
callback can be received only when ZegoRoomConfig
in which the userUpdate
parameter is set to true
is passed in the loginRoom
method.// Log in to a room. If the login succeeds, `true` is returned.
// The `roomUserUpdate` callback can be received only when `userUpdate` is set to `true`.
let userID = Util.getBrow() + '_' + new Date().getTime();
let userName = "user0001";
let roomID = "0001";
let token = ;
// To prevent yourself from missing any notification, listen for callback events such as user login or logout, room connection status updates, and stream publishing status updates before logging in to a room.
zg.on('roomStateChanged', async (roomID, reason, errorCode, extendedData) => {
})
zg.loginRoom(roomID, token, { userID, userName: userID }, { userUpdate: true }).then(result => {
if (result == true) {
console.log("login success")
}
});
You can use the roomStateChanged
callback to listen for the room connection status in real time. You can perform operations such as stream publishing and playing only after the room is successfully connected. If you fail to log in to a room, perform troubleshooting by referring to Error codes.
Create a local audio and video stream, and call the createZegoStream method to obtain the media stream object. By default, images and sounds will be captured from the camera and microphone.
Through the playVideo and playAudio interfaces of localStream, create a local media stream playback component to play audio and video that is waiting to be published or has already been successfully published. Alternatively, you can assign the localStream.stream
object to the srcObject
attribute of the video
element to play the object.
Call the startPublishingStream
method and set the streamID
and localStream
parameter to publish your local audio and video stream to remote users, where localStream
indicates the media stream object that is obtained during stream creation.
streamID
is generated locally and must be globally unique under the same AppID. If different streams are published with the same streamID
, the ones that are published after the first one will fail.// In the sample code, streams are published immediately after you successfully log in to a room. When implementing your service, you can choose to publish streams at any time when the room is connected status.
zg.loginRoom(roomID, token, { userID, userName: userID }, { userUpdate: true }).then(async result => {
if (result == true) {
console.log("login success")
// Create a stream and start the preview.
// After calling the `createZegoStream` method, you cannot perform subsequent operations until the ZEGOCLOUD server returns a streaming media object.
const localStream = await zg.createZegoStream();
// Preview the stream and mount the playback component to the page. "local-video" is the id of the <div> element that serves as the component container.
localStream.playVideo(document.querySelector("#local-video"));
// Start to publish an audio and video stream to the ZEGOCLOUD audio and video cloud.
let streamID = new Date().getTime().toString();
zg.startPublishingStream(streamID, localStream)
}
});
Optional: Set audio and video capture parameters.
You can set audio and video capture parameters by using the following attributes of the createZegoStream method. For details, see Custom video capture.
During a video call, audio and video streams of other users need to be played.
When another user joins a room, the SDK triggers the roomStreamUpdate
callback to notify you of stream addition in the room. The callback will carry the streamID
parameter of the user who joins the room. In this scenario, you can call the startPlayingStream
method to play the audio and video stream that has been published to the ZEGOCLOUD server by the remote user based on the streamID
parameter. To play streams from the CDN, see Live streaming through via CDN.
// Stream status update callback
zg.on('roomStreamUpdate', async (roomID, updateType, streamList, extendedData) => {
// When `updateType` is set to `ADD`, an audio and video stream is added, and you can call the `startPlayingStream` method to play the stream.
if (updateType == 'ADD') {
// When streams are added, play them.
// For the conciseness of the sample code, only the first stream in the list of newly added audio and video streams is played here. In a real service, it is recommended that you traverse the stream list to play each stream.
const streamID = streamList[0].streamID;
// The stream list specified by `streamList` contains the ID of the corresponding stream.
const remoteStream = await zg.startPlayingStream(streamID);
// Create a media stream player object to play remote media streams.
const remoteView = zg.createRemoteStreamView(remoteStream);
// Mount the player to a page. In the sample code, `remote-video` indicates the DOM element ID of the player.
remoteView.play("remote-video");
} else if (updateType == 'DELETE') {
// When streams are deleted, stop playing them.
}
});
ZegoStreamView
. By default, the SDK will display a pop-up window on the UI to ask whether to return the playing.options.enableAutoplayDialog
to false
in the ZegoStreamView.play()
method. In addition, you can design a resume button to be displayed on the UI when the autoplayFailed
callback is triggered. Users can click this button to resume playing.Now, you have implemented a basic real-time video call. You can open index.html in your browser to test out the real-time audio and video features.
// Room connection status update callback
// After the `loginRoom` method is called, you can use the `roomStateChanged` callback to listen for the room connection status in real time.
zg.on('roomStateChanged', (roomID, reason, errorCode, extendData) => {
if (reason == 'LOGINING') {
// Logging in.
} else if (reason == 'LOGINED') {
// Login successful.
// Only after a user successfully logs in to a room or switches the room, can `startPublishingStream` and `startPlayingStream` be called to publish and play streams properly.
// Publish streams to ZEGOCLOUD audio and video cloud
} else if (reason == 'LOGIN_FAILED') {
// Login failed.
} else if (reason == 'RECONNECTING') {
// Reconnecting.
} else if (reason == 'RECONNECTED') {
// Reconnection successful.
} else if (reason == 'RECONNECT_FAILED') {
// Reconnection failed.
} else if (reason == 'KICKOUT') {
// Forced to log out of a room.
} else if (reason == 'LOGOUT') {
// Logout successful.
} else if (reason == 'LOGOUT_FAILED') {
// Logout failed.
}
});
// Notification of audio or video stream updates of other users in a room
// No notification will not be provided for streams published by yourself.
zg.on('roomStreamUpdate', async (roomID, updateType, streamList, extendedData) => {
if (updateType == 'ADD') {
// A stream is added.
for (var i = 0; i < streamList.length; i++) {
console.log('Room',roomID,'has a stream added:', streamList[i]['streamID'])
}
const message = "Video stream ID of the user: " + streamID.toString();
} else if (updateType == 'DELETE') {
// A stream is deleted.
for (var i = 0; i < streamList.length; i++) {
console.log('Room',roomID,'has a stream deleted:', streamList[i]['streamID'])
}
}
});
// Notification of users joining or leaving a room
// The `roomUserUpdate` callback can be received only when `ZegoRoomConfig` in which the `userUpdate` parameter is set to `true` is passed in the `loginRoom` method.
zg.on('roomUserUpdate', (roomID, updateType, userList) => {
if (updateType == 'ADD') {
for (var i = 0; i < userList.length; i++) {
console.log(userList[i]['userID'], 'joins the room:', roomID)
}
} else if (updateType == 'DELETE') {
for (var i = 0; i < userList.length; i++) {
console.log(userList[i]['userID'], 'leaves the room:', roomID)
}
}
});
// Status notification of audio and video stream publishing
// This callback is received when the status of audio and video stream publishing of a user changes. If an exception occurs during stream publishing due to a network interruption, the SDK retries to publish the streams and triggers this status change notification.
zg.on('publisherStateUpdate', result => {
// Stream publishing status update callback
var state = result['state']
var streamID = result['streamID']
var errorCode = result['errorCode']
var extendedData = result['extendedData']
if (state == 'PUBLISHING') {
console.log('Successfully published an audio and video stream:', streamID);
} else if (state == 'NO_PUBLISH') {
console.log('No audio and video stream published');
} else if (state == 'PUBLISH_REQUESTING') {
console.log('Requesting to publish an audio and video stream:', streamID);
}
console.log('Error code:', errorCode,' Extra info:', extendedData)
})
// Quality callback for published streams
// After successfully publishing streams, you will regularly receive callbacks showing the quality data (such as resolution, frame rate, and bitrate) of audio and video streams.
zg.on('publishQualityUpdate', (streamID, stats) => {
// Quality callback for published streams
console.log('Stream quality callback')
})
// Status notifications of audio and video stream playing.
// This callback is received when the status of audio and video stream playing of a user changes. If an exception occurs during stream playing due to a network interruption, the SDK automatically retries to play the streams.
zg.on('playerStateUpdate', result => {
// Stream playing status update callback
var state = result['state']
var streamID = result['streamID']
var errorCode = result['errorCode']
var extendedData = result['extendedData']
if (state == 'PLAYING') {
console.log('Successfully played an audio and video stream:', streamID);
} else if (state == 'NO_PLAY') {
console.log('No audio and video stream played');
} else if (state == 'PLAY_REQUESTING') {
console.log('Requesting to play an audio and video stream:', streamID);
}
console.log('Error code:', errorCode,' Extra info:', extendedData)
})
// Quality callback for audio or video stream playing
// After successfully playing streams, you will regularly receive the notification of quality data (such as resolution, frame rate, and bitrate) during audio or video stream playing.
zg.on('playQualityUpdate', (streamID,stats) => {
// Quality callback for played streams
})
// Notification of receiving a broadcast message
zg.on('IMRecvBroadcastMessage', (roomID, chatData) => {
console.log('Broadcast message defined by using IMRecvBroadcastMessage', roomID, chatData[0].message);
alert(chatData[0].message)
});
// Notification of receiving a pop-up message
zg.on('IMRecvBarrageMessage', (roomID, chatData) => {
console.log('Pop-up message defined by using IMRecvBroadcastMessage', roomID, chatData[0].message);
alert(chatData[0].message)
});
// Notification of receiving a custom signaling message
zg.on('IMRecvCustomCommand', (roomID, fromUser, command) => {
console.log('Custom message defined by using IMRecvCustomCommand', roomID, fromUser, command);
alert(command)
});
Stop publishing a stream and destroy a stream
Call the stopPublishingStream
method to stop publishing local audio and video streams to remote users. Call the destroyStream
method to destroy created stream data. After stream data is destroyed, you need to destroy the video to stop capturing video data.
// Stop publishing a stream based on the local `streamID`.
zg.stopPublishingStream(streamID)
// The `localStream` parameter indicates the `MediaStream` object obtained when the `createZegoStream` method is called.
zg.destroyStream(localStream)
Stop playing streams
Call the stopPlayingStream
method to stop playing audio and video streams published by remote users.
// Stream status update callback
zg.on('roomStreamUpdate', async (roomID, updateType, streamList, extendedData) => {
if (updateType == 'ADD') {
// When streams are added, play them.
} else if (updateType == 'DELETE') {
// When streams are deleted, stop playing them based on `streamID` of the streams in the stream deletion list specified by `streamList`.
const streamID = streamList[0].streamID;
zg.stopPlayingStream(streamID)
}
});
Log out of a room
Call the logoutRoom
method to log out of a room.
zg.logoutRoom(roomID)
Destroy the ZegoExpressEngine instance
To destroy the SDK and release the resources it occupies, call the destroy method.
zg.destroyEngine();
zg = null;
We recommend you run your project on a real device. If your app runs successfully, you should hear the sound and see the video captured locally from your device.
To test out the real-time audio and video features, visit the ZEGO Express Web Demo, and enter the same AppID
, Server
and RoomID
to join the same room. If it runs successfully, you should be able to view the video from both the local side and the remote side, and hear the sound from both sides as well.
In audio-only scenarios, no video will be captured and displayed.
Instances of ZegoExpressEngine cannot be processed reactively by the framework;otherwise, errors will occur.