JavaScript Video Tutorial
In 10 minutes we'll build
A simple web app in vanilla javascript to make a video with audio from a caller to a callee.
Get an API Key
You need a Telnx account to create an API key so you can interact with our Rooms API
- If you don't have one please: Sign up for a free account
- Navigate to API Keys section and create an API Key by clicking
Create API Key
button. - Copy your API key
Create a Room
Let's use our Rooms API to create a room.
Request
NoteDon't forget to update
YOUR_API_KEY
here.
curl -X POST "https://api.telnyx.com/v2/rooms" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
--data-binary '{
"unique_name": "My room",
"max_participants": "10",
"webhook_event_url": "https://example.com",
"enable_recording": "false"
}'
Response
{
"data": {
"active_session_id": null,
"created_at": "2022-07-29T01:25:51.938184Z",
"enable_recording": false,
"id": "599e4d1e-d0aa-40ee-867a-a527f2b2dccd",
"max_participants": 10,
"record_type": "room",
"unique_name": "Jons Room 2",
"updated_at": "2022-07-29T01:25:51.940294Z",
"video_codecs": ["h264", "vp8"],
"webhook_event_failover_url": "",
"webhook_event_url": "https://example.com",
"webhook_timeout_secs": null
}
}
Great, grab the response has an id
field, this is the id for your newly created Room.
Generate a client token
You'll need a client access token to join the room.
To create one use our REST API rooms/create
endpoint.
NOTEYou'll need to replace ROOM_ID in in the url of the command with your room id, above. As well as YOUR_API_KEY as you have in previous steps.
Request
curl -X POST \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--header "Authorization: Bearer YOUR_API_KEY" \
--data '{"refresh_token_ttl_secs":3600,"token_ttl_secs":600}' \
https://api.telnyx.com/v2/rooms/ROOM_ID/actions/generate_join_client_token
Response
{
"data": {
"recort_type": "client_token",
"refresh_token": "eyJhb***************************",
"refresh_token_expires_at": "2022-07-29T02:29:07Z",
"token": "eyJhb**********************************",
"token_expires_at": "2022-07-29T01:39:07Z"
}
}
The token
field in the response is the client access token you'll use to join the room.
🏁 Checkpoint - run the app
At this point you should have the following:
- An API Key
- A room ID
- A room client access token
Let's run the app in a sandbox
There's a README with instructions inside the sandbox. We can use this sandbox as a template to build out the rest of our application.
Let's write some code
Basic concepts of the video SDK
Here are some basic concepts of the SDK that will help you better understand how it works.
-
A
Room
represents a real time audio/video/screen share session with other people or participants. It is fundamental to building a video application. -
A
Participant
represents a person inside aRoom
. EachRoom
has oneLocal Participant
and one or moreRemote Participants
. -
A
Stream
represents the audio/video media streams that are shared byParticipants
in aRoom
- A
Stream
is indentified by it'sparticipantId
andstreamKey
- A
-
A
Participant
can have one or moreStream
's associated with it. -
A
Subscription
is used to subscribe to aStream
belonging to aRemote Participant
Room Events
There are a few events trigger on a Room
instance that we'll need to finish building this app that makes a video call.
connected
- triggers when a room instance has connected to the serverparticipant_joined
- triggers when a remote participant joins the roomstream_published
- triggers when a stream has started being published to the roomsubscription_started
- triggers when subscription to remote stream has started
Handling an event looks like this:
room.on("connected", async () => {
...
});
🏁 Checkpoint
It might be helpful to take a look at the full list of
Events
available on aRoom
instance.
Connect to the room and get local media
NOTE:
We can start implementing our code after the room.initialize
block inside the sandbox above.
Let's connect to the room and use the standard WebRTC API to get the audio and video tracks from the device.
// connected to the room as the local participant
telnyxVideoClient.on("connected", async () => {
// use the webrtc api to get media from devices
let intercomStream = await navigator.mediaDevices.getUserMedia({
audio: true,
video: true
});
console.log("got local stream using getUserMedia...");
// Get audio and video tracks from the MediaStream's
// since the sdk works with MediaStreamTrack
let intercomAudioTrack = intercomStream.getAudioTracks()[0];
let intercomVideoTrack = intercomStream.getVideoTracks()[0];
});
Publish the stream and render it
We want to a add/publish a stream the room which consists of the audio and video track we received from the user's device, above.
We'll give the stream a key of "caller", which is how the stream is identified.
// add/publish a stream with a key "caller" to the room
await telnyxVideoClient.addStream("intercom", {
audio: callerAudioTrack,
video: callerVideoTrack
});
console.log("published local stream to the room...");
Render the stream to the DOM
Now, that we have the caller's stream we want to render it to the DOM.
The entire room.connected
block looks like this:
// connected to the room as the local participant
room.on("connected", async () => {
// use the webrtc api to get media from devices
let callerStream = await navigator.mediaDevices.getUserMedia({
audio: true,
video: true
});
console.log("got local stream using getUserMedia...");
// Get audio and video tracks from the MediaStream's
// since the sdk works with MediaStreamTrack
let callerAudioTrack = callerStream.getAudioTracks()[0];
let callerVideoTrack = callerStream.getVideoTracks()[0];
// add/publish the stream with the key "caller" to the room
await room.addStream("caller", {
audio: callerAudioTrack,
video: callerVideoTrack
});
console.log("published local stream to the room...");
// render the caller stream to the page/DOM
let videoElement = document.getElementById("caller");
videoElement.srcObject = callerStream;
});
Great, we're now publishing the local partipants'/caller's stream, to the room which the callee can subscribe to.
Subscribe to the callee's remote stream
As a caller, how do we know when the callee has joined the room or has started publishing a stream?
That's where the participant_joined
and the stream_published
events that we went over earlier, come into play.
We need to listen for teh stream_published
event like this:
// a stream has been published to the room
room.on(
"stream_published",
async (participantId, streamKey, state) => {
...
}
);
Understanding how subscriptions work
__The stream_published
event is triggered for both local and remote streams. __
In our case, we want to know when the callee's remote stream starts publishing so we can subscribe to it. We already know that the caller has published a "caller" stream so we can ignore that event.
Subscribing to a stream
When a stream is published in a room it doesn't mean it's audio and video tracks are accessible, yet. In order to access a remote stream's track we must explicitly subscribe to that stream and wait for the subscription_started
event.
// a stream has been published to the room
room.on("stream_published", async (participantId, streamKey, state) => {
// ignore streams that are published by the local participant
// we only care about remote stream from other remote participant
let participant = state.participants.get(participantId);
if (participant.origin === "local") {
return;
}
// the remote stream is identified using the participantId and streamKey
// you need to subscribe to a remote stream in order to access it's `MediaStreamTrack`s
await room.addSubscription(participantId, streamKey, {
audio: true,
video: true
});
});
Render the callee's remote stream
We're almost there! Now, we can listen to the subscription_started
event and use args from that handler to get the callee's remote stream and render it to the DOM.
// a subscription to a remote stream has started
room.on("subscription_started", (participantId, streamKey, state) => {
console.log(
`subscription to the: ${participantId} ${streamKey} stream started...`
);
// use a helper method to easily access a remote participants' stream
let remoteStream = room.getParticipantStream(participantId, streamKey);
// create a MediaStream object from the remote stream's track so we can render it
// to a video element
let remoteMediaStream = new MediaStream([
remoteStream.audioTrack,
remoteStream.videoTrack
]);
const calleeVideo = document.getElementById("callee");
calleeVideo.srcObject = remoteMediaStream;
});
Run the app
To run the app:
- Open the sandbox, the browser here represents the caller's app
- Open the app in another tab, this represents the callee's application which you'll need to interact with.
From the caller app
- Click the call button
- You should be prompted by the browser for camera/micrphone access please allow.
- You should also see video from your device's camera and hear audio from the microphone
- You should see some console log, if things are working property
- If you see errors your client token has expired and you need to regenerate it.
From the callee's app
- Click the call button
- Notice in the caller's app a stream subscription was logged
- The callee's video/audio stream is rendered.