What is the benefit of using protoo for signaling instead socket.io ?
iam new to the area of this signaling and i notice some bug happen a lot more often when using websocket like doublicat the messages.
You can use whatever you want. The mediasoup demo is not a production grade application.
Preformatted text``Preformatted text``Preformatted text
Hi @ibc i did use protoo along side mediasoup to develop a videoconferencing app all in node
i actually use the official demo server side code, but on the client side, instead of using react as they did, i’m still using node. i actually succeeded writing the code with no error, now the problem is i am rightfully producing and consuming media, but the consume media is not displaying on the video element, though the video element is created when consuming but no video is been displayed. i dont know if you can help me out
this is the clientcode
import { Peer, WebSocketTransport } from ‘protoo-client’;
import mediasoupClient, { Device } from ‘mediasoup-client’;
document.addEventListener(‘DOMContentLoaded’, () => {
var device
let rtpCapabilities
let producerTransport
let recvTransport
let consumerTransports =
let producer
let consumer
let deviceName
// Force VP8 codec for sending.
// @type {Boolean}
let forceVP8 = true;
// Force H264 codec for sending.
// @type {Boolean}
let forceH264 = true;
// Force VP9 codec for sending.
// @type {Boolean}
let forceVP9 = true;
let consumers = new Map(); // Add this line to store consumers
const joinForm = document.getElementById(‘joinForm’);
const peerList = document.getElementById(‘peerList’);
let peer;
joinForm.addEventListener(‘submit’, async (event) => {
event.preventDefault();
document.getElementById(‘joinFormContainer’).style.display = ‘none’;
document.getElementById(‘videoContainer’).style.display = ‘block’;
const localVideo = document.getElementById(‘localvideo’)
const displayName = document.getElementById('displayName').value;
const roomId = '3763';
const serverAddress = 'localhost:4443';
const peerId = `${displayName}1242`;
const protooUrl = `wss://${serverAddress}/?roomId=${roomId}&peerId=${peerId}`;
console.log(displayName)
try {
const protooTransport = new WebSocketTransport(protooUrl);
peer = new Peer(protooTransport);
peer.on('open', () => {
console.log('Peer connected');
joinRoom(displayName, deviceName);
});
peer.on('close', () => {
console.log('Peer disconnected');
});
peer.on('error', (error) => {
console.error('Peer error:', error);
});
// Handle new consumer notifications
peer.on('request', async (request, accept, reject) => {
switch (request.method) {
case 'newConsumer':
{
const {
peerId,
producerId,
id,
kind,
rtpParameters,
type,
appData,
producerPaused
} = request.data;
try {
const consumer = await recvTransport.consume({
id,
producerId,
kind,
rtpParameters,
type,
appData: { ...appData, peerId }
});
// Store the Consumer in the local state
consumerTransports.push(consumer);
consumers.set(consumer.id, consumer);
console.log('Consumed media from producer', producerId);
// Set up event handlers for the Consumer
consumer.on('transportclose', () => {
console.log('Consumer transport closed');
consumers.delete(consumer.id);
consumerTransports = consumerTransports.filter(c => c.id !== consumer.id);
});
const { spatialLayers, temporalLayers } =
mediasoupClient.parseScalabilityMode(
consumer.rtpParameters.encodings[0].scalabilityMode
);
const remoteVideo = document.createElement('video');
const stream = new MediaStream();
if(consumer.track){
console.log('there is a track attached to this consumer', consumer.track)
}else{
console.log('consumer does not have a track')
}
stream.addTrack(consumer.track);
// remoteVideo.srcObject = new MediaStream([consumer.track]);
remoteVideo.srcObject = stream
remoteVideo.autoplay = true;
remoteVideo.controls = true
remoteVideo.id = consumer.id
remoteVideo.playsInline = true;
remoteVideo.muted = true; // Ensure it is not muted
document.getElementById('remoteVideos').appendChild(remoteVideo);
console.log('New consumer added:', consumer.id, [consumer.track]);
accept();
// Resume the consumer
await consumer.resume();
console.log('New consumer added after resume:', consumer.id, consumer.track.type);
// Log when the video actually starts playing
remoteVideo.addEventListener('playing', () => {
console.log('Remote video started playing:', consumer.id);
remoteVideo.muted = false; // Unmute after playback starts
});
}
catch (error) {
console.log('"newConsumer" request failed:%o', error);
}
break;
}
case 'resumeConsumer':
{
const { consumerId } = request.data;
console.log('consumerResumed', request.data)
const consumer = consumers.get(consumerId)
if (!consumer)
break;
consumer.resume();
break
}
}
});
peer.on('notification', (notification) => {
console.log('Received notification:', notification);
switch (notification.method) {
case 'newPeer':
{
const peer = notification.data;
console.log('newPeer', peer)
break
}
case 'producerScore':
{
const { producerId, score } = notification.data;
console.log('producerScore', notification.data)
break;
}
case 'consumerResumed':
{
const { consumerId } = notification.data;
console.log('consumerResumed', notification.data)
const consumer = consumers.get(consumerId)
if (!consumer)
break;
consumer.resume();
break
}
case 'mediasoup-version':
{
const { version } = notification.data;
console.log(version, notification.data)
break;
}
case 'consumerResumed':
{
const { consumerId } = notification.data
const consumer = consumers.get(consumerId);
if (!consumer)
break;
consumer.resume();
}
// Handle other notifications
}
});
} catch (error) {
console.error('Failed to create peer:', error);
}
});
async function joinRoom(displayName, deviceInput) {
try {
deviceName = mediasoupClient.detectDevice();
if (deviceName) {
console.log("detected handler: %s", deviceName);
} else {
console.warn("no suitable handler found for current browser/device");
}
// Properly initialize the device from mediasoup-client
device = new mediasoupClient.Device({
handlerName: deviceName
});
// Ensure the RTP capabilities are loaded properly before proceeding
const routerRtpCapabilities = await peer.request('getRouterRtpCapabilities');
await device.load({ routerRtpCapabilities });
console.log('Router RTP Capabilities loaded:', device.rtpCapabilities);
// Create mediasoup Device
await createDevice(deviceInput);
const { peers } = await peer.request('join', {
displayName: displayName,
device: deviceName,
rtpCapabilities: device.rtpCapabilities, // Add this line
sctpCapabilities: device.sctpCapabilities
});
// For each existing peer, create consumers for their producers
for (const existingPeer of peers) {
console.log('producers,', existingPeer.producer)
// await consumeProducer(producer.id);
}
console.log('Joined room. Peers:', peers);
updatePeerList(peers);
//await createRecvTransport();
// Start producing media
await produce();
} catch (error) {
console.error('Failed to join room:', error);
}
}
function updatePeerList(peers) {
peerList.innerHTML = ‘’;
peers.forEach(peer => {
const li = document.createElement(‘li’);
li.textContent = ${peer.displayName} (${peer.device})
;
li.className = ‘list-group-item’;
console.log(peer.rtpCapabilities)
peerList.appendChild(li);
});
}
//A device is an endpoint connecting to a Router on the server side to send/recive media
const createDevice = async (deviceInput) => {
try {
console.log(‘Device RTP Capabilities’, device.rtpCapabilities)
deviceInput = deviceName
// Ensure the device can produce audio and video
if (!device.canProduce('video') || !device.canProduce('audio')) {
throw new Error('Device cannot produce media (audio/video). Check capabilities.');
}
console.log('Device loaded successfully');
// once the device loads, create transport
await createSendTransport()
await createRecvTransport()
} catch (error) {
console.log(error)
if (error.name === 'UnsupportedError')
console.warn('browser not supported')
}
}
async function createSendTransport() {
try {
// 1. Request transport parameters from the server
const transportParams = await peer.request(‘createWebRtcTransport’, {
forceTcp: false,
producing: true,
consuming: false,
sctpCapabilities: device.sctpCapabilities,
});
let track
const { id, iceParameters, iceCandidates, dtlsParameters, sctpParameters } = transportParams
// Create a new mediasoup-client Transport
// 2. Create a send transport
producerTransport = device.createSendTransport({
id,
iceParameters,
iceCandidates,
dtlsParameters: {
...dtlsParameters, role: 'auto'
},
sctpParameters,
iceServers: []
});
// 3. Set up transport event handlers
producerTransport.on('connect', async ({ dtlsParameters, iceParameters, appData }, callback, errback) => {
try {
const response = await peer.request('connectWebRtcTransport', {
transportId: producerTransport.id,
dtlsParameters,
iceParameters
});
console.log('Producer transport connected successfully');
callback(response);
} catch (error) {
console.error('Producer transport connection error:', error);
await errback(error);
}
});
producerTransport.on('produce', async ({ kind, rtpParameters, appData }, callback, errback) => {
try {
const { id } = await peer.request('produce', {
transportId: producerTransport.id,
kind,
rtpParameters,
appData
});
callback({ id });
} catch (error) {
await errback(error);
}
});
} catch (error) {
console.log('Producer transport error');
console.log(error)
}
}
async function produce() {
// Produce our webcam video.
try {
const stream = await navigator.mediaDevices.getUserMedia({
video: true, audio: true
})
let encodings;
let codec;
const codecOptions =
{
videoGoogleStartBitrate : 1000
};
encodings =[
{
scaleResolutionDownBy:1,
maxBitrate: 5000000,
scalabilityMode:'L1T3'
}
]
encodings.unshift(
{
scaleResolutionDownBy:2,
maxBitrate:1000000,
scalabilityMode:'L1T3'
}
)
codec = device.rtpCapabilities.codecs
.find((c)=>c.mimeType.toLowerCase()==='video/vp8')
if (!codec)
{
console.log('desired VP8 codec+configuration is not supported');
codec = device.rtpCapabilities.codecs
.find((c) => c.mimeType.toLowerCase() === 'video/h264');
}
const videoTrack = stream.getVideoTracks()[0];
const audioTrack = stream.getAudioTracks()[0];
const producer = await producerTransport.produce({ track: videoTrack, encodings,codecOptions,codec });
// await producerTransport.produce({
// track: audioTrack, encodings: [{ maxBitrate: 1000000 }],
// codecOptions: { videoGoogleStartBitrate: 1000 }
// });
console.log('production going on,', producer)
producer.on('trackended', () => {
console.log('Track ended');
});
localVideo.srcObject = stream
localVideo.play();
console.log('Started producing media');
} catch (error) {
console.error('Failed to produce media:', error);
}
}
async function createRecvTransport() {
try {
// Request transport parameters from the server for receiving media
const transportParams = await peer.request(‘createWebRtcTransport’, {
forceTcp: false,
producing: false,
consuming: true,
sctpCapabilities: device.sctpCapabilities,
});
// Create a receiving transport
console.log(‘receiving transport:’, transportParams)
const {
id,
iceParameters,
iceCandidates,
dtlsParameters,
sctpParameters
} = transportParams;
recvTransport = device.createRecvTransport({
id,
iceParameters,
iceCandidates,
dtlsParameters: { …dtlsParameters, role: ‘auto’ },
sctpParameters,
iceServers: ,
appData: { consuming: true } // Add this line
});
// Handle the transport connection
recvTransport.on('connect', async ({ dtlsParameters, iceParameters }, callback, errback) => {
try {
const response = await peer.request('connectWebRtcTransport', {
transportId: recvTransport.id,
dtlsParameters,
iceParameters
});
callback(response);
console.log('Receiver transport connected successfully');
} catch (error) {
await errback(error);
console.error('Receiver transport connection error:', error);
}
});
// Store the transport for future use
consumerTransports.push(recvTransport);
// return recvTransport;
} catch (error) {
console.error('Failed to create receive transport:', error);
}
}
async function consumeMedia(peerId, producerId, consumerId, kind, rtpParameters, type, appData) {
try {
// Create a new mediasoup-client Consumer
if (!recvTransport) {
await createRecvTransport();
}
consumer = await recvTransport.consume({
id: consumerId,
producerId,
kind,
rtpParameters,
type,
appData: { …appData, peerId }
});
// Store the Consumer in the local state
consumerTransports.push(consumer);
console.log('Consumed media from producer', producerId);
} catch (error) {
console.error('Error consuming media:', error);
console.error('Consumer details:', { peerId, producerId, consumerId, kind });
}
}
});
````Preformatted text`
images of my console id attach to this post
![Screenshot (34)|690x387](upload://c8zjNHo2ZtG3PaL9aMKhZAdOfLo.png)
![Screenshot (35)|690x387](upload://sYi9rqBAqxejzRJZln946TtS7Vr.png)