π Introduction
Integrating image capture in a JavaScript video chat app enhances user interaction and functionality. With this feature, users can take snapshots during video calls, adding a new dimension to their communication experience. By incorporating image capture, the app enables users to capture memorable moments or important information shared during the call. JavaScript's flexibility allows seamless integration of image capture, ensuring a smooth user experience across different devices and browsers.
Benefits of Integrate Image Capture in JavaScript Video Chat App:
- Enhanced Communication : Image capture adds a visual element to video calls, improving communication and understanding.
- Memorable Moments : Users can capture snapshots of important moments during the call, preserving memories.
- Increased Engagement : Interactive features like image capture keep users engaged and active during video calls.
Use Case of Integrate Image Capture in JavaScript Video Chat App:
- Education: Students can capture whiteboard content or diagrams shared during online classes for later review.
- Business Meetings: Participants can capture key points discussed in meetings or presentations, ensuring clarity and accountability.
- Remote Collaboration: Teams working remotely can capture design mockups, charts, or code snippets for collaborative brainstorming sessions.
This tutorial will guide you through a step-by-step process of how to integrate image capture functionality in a JavaScript chat App with VideoSDK.
π Getting Started with VideoSDK
To take advantage of Image Feature Integration functionality, we must use the capabilities that the VideoSDK offers. Before diving into the implementation steps, let's ensure you complete the necessary prerequisites.
Create a VideoSDK Account
Go to your VideoSDK dashboard. This account gives you access to the required Video SDK token, which acts as an authentication key that allows your application to interact with VideoSDK functionality.
Generate your Auth Token
Visit your VideoSDK dashboard and navigate to the "API Key" section to generate your auth token. This token is crucial in authorizing your application to use VideoSDK features. For a more visual understanding of the account creation and token generation process, consider referring to the provided tutorial.
Prerequisites
Before proceeding, ensure that your development environment meets the following requirements:
- VideoSDK Developer Account (if you do not have one, follow VideoSDK Dashboard)
- Have Node and NPM installed on your device.
π οΈ Install VideoSDK
Import VideoSDK using the <script>
tag or install it using the following npm command. Make sure you are in your app directory before you run this command.
// JavaScript
<html>
<head>
<!--.....-->
</head>
<body>
<!--.....-->
<script src="https://sdk.videosdk.live/js-sdk/0.0.83/videosdk.js"></script>
</body>
</html>
npm
npm install @videosdk.live/js-sdk
Yarn
yarn add @videosdk.live/js-sdk
Structure of the project
Your project structure should look like this.
root
βββ index.html
βββ config.js
βββ index.js
You will be working on the following files:
- index.html: Responsible for creating a basic UI.
- config.js: Responsible for storing the token.
- index.js: Responsible for rendering the meeting view and the join meeting functionality.
π₯ Essential Steps to Implement Video Call Functionality
Once you've successfully installed VideoSDK in your project, you'll have access to a range of functionalities for building your video call application. Image Capture is one such feature that leverages VideoSDK's capabilities. It leverages VideoSDK's capabilities to identify the user with the strongest audio signal (the one speaking).
Step 1: Design the user interface (UI)β
Create an HTML file containing the screens, join-screen
and grid-screen
.
<!DOCTYPE html>
<html>
<head> </head>
<body>
<div id="join-screen">
<!-- Create new Meeting Button -->
<button id="createMeetingBtn">New Meeting</button>
OR
<!-- Join existing Meeting -->
<input type="text" id="meetingIdTxt" placeholder="Enter Meeting id" />
<button id="joinBtn">Join Meeting</button>
</div>
<!-- for Managing meeting status -->
<div id="textDiv"></div>
<div id="grid-screen" style="display: none">
<!-- To Display MeetingId -->
<h3 id="meetingIdHeading"></h3>
<!-- Controllers -->
<button id="leaveBtn">Leave</button>
<button id="toggleMicBtn">Toggle Mic</button>
<button id="toggleWebCamBtn">Toggle WebCam</button>
<!-- render Video -->
<div class="row" id="videoContainer"></div>
</div>
<!-- Add VideoSDK script -->
<script src="https://sdk.videosdk.live/js-sdk/0.0.83/videosdk.js"></script>
<script src="config.js"></script>
<script src="index.js"></script>
</body>
</html>
Step 2: Implement Join Screenβ
Configure the token in the config.js
file, which you can obtain from the VideoSDK Dashboard.
// Auth token will be used to generate a meeting and connect to it
TOKEN = "Your_Token_Here";
Next, retrieve all the elements from the DOM and declare the following variables in the index.js
file. Then, add an event listener to the join and create meeting buttons.
// Getting Elements from DOM
const joinButton = document.getElementById("joinBtn");
const leaveButton = document.getElementById("leaveBtn");
const toggleMicButton = document.getElementById("toggleMicBtn");
const toggleWebCamButton = document.getElementById("toggleWebCamBtn");
const createButton = document.getElementById("createMeetingBtn");
const videoContainer = document.getElementById("videoContainer");
const textDiv = document.getElementById("textDiv");
// Declare Variables
let meeting = null;
let meetingId = "";
let isMicOn = false;
let isWebCamOn = false;
function initializeMeeting() {}
function createLocalParticipant() {}
function createVideoElement() {}
function createAudioElement() {}
function setTrack() {}
// Join Meeting Button Event Listener
joinButton.addEventListener("click", async () => {
document.getElementById("join-screen").style.display = "none";
textDiv.textContent = "Joining the meeting...";
roomId = document.getElementById("meetingIdTxt").value;
meetingId = roomId;
initializeMeeting();
});
// Create Meeting Button Event Listener
createButton.addEventListener("click", async () => {
document.getElementById("join-screen").style.display = "none";
textDiv.textContent = "Please wait, we are joining the meeting";
// API call to create meeting
const url = `https://api.videosdk.live/v2/rooms`;
const options = {
method: "POST",
headers: { Authorization: TOKEN, "Content-Type": "application/json" },
};
const { roomId } = await fetch(url, options)
.then((response) => response.json())
.catch((error) => alert("error", error));
meetingId = roomId;
initializeMeeting();
});
Step 3: Initialize Meetingβ
Following that, initialize the meeting using the initMeeting()
function and proceed to join the meeting.
// Initialize meeting
function initializeMeeting() {
window.VideoSDK.config(TOKEN);
meeting = window.VideoSDK.initMeeting({
meetingId: meetingId, // required
name: "Thomas Edison", // required
micEnabled: true, // optional, default: true
webcamEnabled: true, // optional, default: true
});
meeting.join();
// Creating local participant
createLocalParticipant();
// Setting local participant stream
meeting.localParticipant.on("stream-enabled", (stream) => {
setTrack(stream, null, meeting.localParticipant, true);
});
// meeting joined event
meeting.on("meeting-joined", () => {
textDiv.style.display = "none";
document.getElementById("grid-screen").style.display = "block";
document.getElementById(
"meetingIdHeading"
).textContent = `Meeting Id: ${meetingId}`;
});
// meeting left event
meeting.on("meeting-left", () => {
videoContainer.innerHTML = "";
});
// Remote participants Event
// participant joined
meeting.on("participant-joined", (participant) => {
// ...
});
// participant left
meeting.on("participant-left", (participant) => {
// ...
});
}
Step 4: Create the Media Elementsβ
In this step, Create a function to generate audio and video elements for displaying both local and remote participants. Set the corresponding media track based on whether it's a video or audio stream.
// creating video element
function createVideoElement(pId, name) {
let videoFrame = document.createElement("div");
videoFrame.setAttribute("id", `f-${pId}`);
videoFrame.style.width = "300px";
//create video
let videoElement = document.createElement("video");
videoElement.classList.add("video-frame");
videoElement.setAttribute("id", `v-${pId}`);
videoElement.setAttribute("playsinline", true);
videoElement.setAttribute("width", "300");
videoFrame.appendChild(videoElement);
let displayName = document.createElement("div");
displayName.innerHTML = `Name : ${name}`;
videoFrame.appendChild(displayName);
return videoFrame;
}
// creating audio element
function createAudioElement(pId) {
let audioElement = document.createElement("audio");
audioElement.setAttribute("autoPlay", "false");
audioElement.setAttribute("playsInline", "true");
audioElement.setAttribute("controls", "false");
audioElement.setAttribute("id", `a-${pId}`);
audioElement.style.display = "none";
return audioElement;
}
// creating local participant
function createLocalParticipant() {
let localParticipant = createVideoElement(
meeting.localParticipant.id,
meeting.localParticipant.displayName
);
videoContainer.appendChild(localParticipant);
}
// setting media track
function setTrack(stream, audioElement, participant, isLocal) {
if (stream.kind == "video") {
isWebCamOn = true;
const mediaStream = new MediaStream();
mediaStream.addTrack(stream.track);
let videoElm = document.getElementById(`v-${participant.id}`);
videoElm.srcObject = mediaStream;
videoElm
.play()
.catch((error) =>
console.error("videoElem.current.play() failed", error)
);
}
if (stream.kind == "audio") {
if (isLocal) {
isMicOn = true;
} else {
const mediaStream = new MediaStream();
mediaStream.addTrack(stream.track);
audioElement.srcObject = mediaStream;
audioElement
.play()
.catch((error) => console.error("audioElem.play() failed", error));
}
}
}
Step 5: Handle participant eventsβ
Thereafter, implement the events related to the participants and the stream.
The following are the events to be executed in this step:
-
participant-joined
: When a remote participant joins, this event will trigger. In the event callback, create video and audio elements previously defined for rendering their video and audio streams. -
participant-left
: When a remote participant leaves, this event will trigger. In the event callback, remove the corresponding video and audio elements. -
stream-enabled
: This event manages the media track of a specific participant by associating it with the appropriate video or audio element.
// Initialize meeting
function initializeMeeting() {
// ...
// participant joined
meeting.on("participant-joined", (participant) => {
let videoElement = createVideoElement(
participant.id,
participant.displayName
);
let audioElement = createAudioElement(participant.id);
// stream-enabled
participant.on("stream-enabled", (stream) => {
setTrack(stream, audioElement, participant, false);
});
videoContainer.appendChild(videoElement);
videoContainer.appendChild(audioElement);
});
// participants left
meeting.on("participant-left", (participant) => {
let vElement = document.getElementById(`f-${participant.id}`);
vElement.remove(vElement);
let aElement = document.getElementById(`a-${participant.id}`);
aElement.remove(aElement);
});
}
Step 6: Implement Controlsβ
Next, implement the meeting controls such as toggleMic, toggleWebcam, and leave the meeting.
// leave Meeting Button Event Listener
leaveButton.addEventListener("click", async () => {
meeting?.leave();
document.getElementById("grid-screen").style.display = "none";
document.getElementById("join-screen").style.display = "block";
});
// Toggle Mic Button Event Listener
toggleMicButton.addEventListener("click", async () => {
if (isMicOn) {
// Disable Mic in Meeting
meeting?.muteMic();
} else {
// Enable Mic in Meeting
meeting?.unmuteMic();
}
isMicOn = !isMicOn;
});
// Toggle Web Cam Button Event Listener
toggleWebCamButton.addEventListener("click", async () => {
if (isWebCamOn) {
// Disable Webcam in Meeting
meeting?.disableWebcam();
let vElement = document.getElementById(`f-${meeting.localParticipant.id}`);
vElement.style.display = "none";
} else {
// Enable Webcam in Meeting
meeting?.enableWebcam();
let vElement = document.getElementById(`f-${meeting.localParticipant.id}`);
vElement.style.display = "inline";
}
isWebCamOn = !isWebCamOn;
});
You can check out the complete code here.
Integrate Image Capture Feature
This guide provides instructions on capturing images of participants from a video stream. This capability proves particularly valuable in Video KYC scenarios, enabling the capture of images where users can hold up their identity for verification.
By using the captureImage()
method of the Participant
class, you can capture an image of a local participant from their video stream.
- You have the option to specify the desired height and width in the
captureImage()
function; however, these parameters are optional. If not provided, the VideoSDK will automatically use the dimensions of the local participant's webcamStream. - The
captureImage()
function returns the image in the form of abase64
string.
let meeting;
// Initialize Meeting
meeting = VideoSDK.initMeeting({
// ...
});
let isWebcamOn; // status of your webcam, true/false
async function imageCapture() {
if (isWebcamOn) {
const base64Data = await meeting.localParticipant.captureImage(); // captureImage will return base64 string
console.log("base64", base64);
} else {
console.error("Camera must be on to capture an image");
}
}
TIP:
Rather than utilizing theparticipants.get(participantId).captureImage()
method to capture an image of a remote participant, it is advisable to refer to the provided documentation for a more effective approach.The
participants.get(participantId).captureImage()
method captures an image from the current video stream being consumed from the remote participant. The alternative documentation is likely to provide a better and more appropriate method to achieve the desired result.
How to capture an image of a remote participant?β
- Before proceeding, it's crucial to understand VideoSDK's temporary file storage system and the underlying pubsub mechanism.
- Here's a breakdown of the steps, using the names Participant A and Participant B for clarity:
Step 1: Initiate Image Capture Requestβ
- In this step, you have to first send a request to Participant B, whose image you want to capture, using Pubsub.
- To do that, you have to create a Pubsub topic called
IMAGE_CAPTURE
theindex.js
File.β - Here, you will be using the
sendOnly
property of thepublish()
method. Therefore, the request will be sent to that participant only.
let meeting;
// Initialize Meeting
meeting = VideoSDK.initMeeting({
// ...
});
function sendRequest({ participantId }) {
// Pass the participantId of the participant twhose image you want to capture
// Here, it will be Participant B's id, as you want to capture the the image of Participant B
let message = "Sending request to capture image";
meeting.pubSub
.publish("IMAGE_CAPTURE", message, {
persist: false,
sendOnly: [participantId],
})
.then((res) => console.log(`response of publish : ${res}`))
.catch((err) => console.log(`error of publish : ${err}`));
}
Place one button to capture an image of a remote participant in index.js
file inside createVideoElement function.
// creating video element
function createVideoElement(pId, name) {
// ...
// Create wrapper div
let wrapperDiv = document.createElement("div");
// Create button element
let buttonElement = document.createElement("button");
buttonElement.setAttribute("id", `btnCaptureImage-${pId}`);
buttonElement.textContent = "CaptureImage";
buttonElement.classList.add("capture-button");
// Append video and button elements to the wrapper div
wrapperDiv.appendChild(videoElement);
wrapperDiv.appendChild(buttonElement);
// Append the wrapper div before the video element
division.appendChild(wrapperDiv);
// Set up event listener for hover effect
wrapperDiv.addEventListener("mouseover", function () {
buttonElement.style.display = "block";
});
wrapperDiv.addEventListener("mouseout", function () {
buttonElement.style.display = "none";
});
wrapperDiv.style.width = `${videoElement.getAttribute("width")}px`;
wrapperDiv.style.height = `${videoElement.getAttribute("height")}px`;
//...
}
Now on the capture Image button click the call sendRequest()
in index.js
// creating video element
function createVideoElement(pId, name) {
// ...
buttonElement.addEventListener("click", async function () {
if (pId == meeting.localParticipant.id) {
const base64Data = await meeting.localParticipant.captureImage();
base64 = "data:data:image/jpeg;base64," + base64Data;
captureImageDiv.style.display = "block";
captureImage.src = base64;
captureImage.onload = function () {
alert(this.width + "x" + this.height);
};
} else {
let participantId = await participants.get(pId);
sendRequest({ participantId: participantId.id });
}
});
}
Step 2: Capture and Upload Fileβ
- To capture an image from the remote participant [Participant B], you have to subscribe to the pub-sub topic on the
meeting-joined
event of theMeeting
class. When a participant receives an image capture request, this component uses thecaptureImage
method of theParticipant
class to capture the image.
let meeting;
// Initialize Meeting
meeting = VideoSDK.initMeeting({
// ...
});
async function captureAndStoreImage() {
// capture image
const base64Data = await meeting.localParticipant.captureImage();
console.log("base64Data", base64Data);
}
const _handleOnImageCaptureMessageReceived = (message) => {
try {
if (message.senderId !== meeting.localParticipant.id) {
// capture and store image when message received
captureAndStoreImage();
}
} catch (err) {
console.log("error on image capture", err);
}
};
meeting.on("meeting-joined", () => {
// ...
meeting.pubSub.subscribe("IMAGE_CAPTURE", (data) => {
_handleOnImageCaptureMessageReceived(data);
});
});
- The captured image is then stored in VideoSDK's temporary file storage system using the
uploadBase64File()
function of theMeeting
class. This operation returns a uniquefileUrl
of the stored image.
let meeting;
// Initialize Meeting
meeting = VideoSDK.initMeeting({
// ...
});
async function captureAndStoreImage() {
// capture image
const base64Data = await meeting.localParticipant.captureImage();
// upload image to videosdk storage system
const fileUrl = await meeting.uploadBase64File({
base64Data,
token: "VIDEOSDK_TOKEN",
fileName: "myImage.jpeg", // specify a name for image file with extension
});
console.log("fileUrl", fileUrl);
}
- Next, the
fileUrl
is sent back to the participant who initiated the request using theIMAGE_TRANSFER
topic.
async function captureAndStoreImage() {
// ...
const fileUrl = await meeting.uploadBase64File({
base64Data,
token: "VIDEOSDK_TOKEN",
fileName: "myImage.jpeg", // specify a name for image file with extension
});
// publish image Transfer
meeting.pubSub
.publish("IMAGE_TRANSFER", fileUrl, {
persist: false,
sendOnly: [senderId],
})
.then((res) => console.log(`response of publish : ${res}`))
.catch((err) => console.log(`error of publish : ${err}`));
}
Step 2: Fetch and Display Imageβ
- Upon publishing on the
IMAGE_TRANSFER
topic, subscribe to the same topic within themeeting-joined
event of theMeeting
class. This will provide access to thefileUrl
associated with the captured image. Once obtained, use thefetchBase64File()
method of theMeeting
class to retrieve the file inbase64
format from VideoSDK's temporary storage.
async function captureImageAndDisplay(message) {
const token = "VIDEOSDK_TOKEN";
let base64 = await meeting.fetchBase64File({
url: message.message,
token,
});
console.log("base64", base64); // here is your image in a form of base64
}
meeting.on("meeting-joined", () => {
// ...
meeting.pubSub.subscribe("IMAGE_TRANSFER", (data) => {
if (data.senderId !== meeting.localParticipant.id) {
captureImageAndDisplay(data);
}
});
});
- With the
base64
data in hand, you can now display the image.
let captureImage = document.getElementById("captureImage");
async function captureImageAndDisplay(message) {
const token = "VIDEOSDK_TOKEN";
let base64 = await meeting.fetchBase64File({
url: message.message,
token,
});
console.log("base64", base64); // here is your image in a form of base64
base64 = "data:data:image/jpeg;base64," + base64;
captureImage.src = base64;
}
<!-- -->
<img id="captureImage" />
NOTE:
The file stored in the VideoSDK's temporary file storage system will be automatically deleted once the current room/meeting comes to an end.
π Conclusion
Integrating image capture into a JavaScript video call app significantly enhances its functionality and user experience. This feature not only adds a visual dimension to communication but also provides users with a valuable tool for capturing and preserving important moments, information, or visuals shared during the call.
VideoSDK's capabilities ensure seamless integration of image capture functionality with VideoSDK, providing a smooth user experience across different platforms and devices. By incorporating this feature, the JavaScript video call app becomes more versatile, empowering users with enhanced communication tools and fostering a more interactive and engaging environment.
Ready to get started? Sign up today and take advantage of 10,000 free minutes to experiment with image capture and explore the other powerful features VideoSDK offers.
Top comments (0)