Compare commits

..

28 Commits

Author SHA1 Message Date
233f49a998 Update server 2022-11-22 20:43:30 +02:00
127f17cd97 Update server 2022-11-22 20:42:55 +02:00
d1ad8b4d3a Update server 2022-11-22 20:38:16 +02:00
f20e1ad260 Update build 2022-11-22 20:04:43 +02:00
27151a26d1 Update build 2022-11-22 20:03:36 +02:00
544e9e59ab Update build 2022-11-22 20:00:44 +02:00
4e4cd6f893 Update build 2022-11-22 19:55:55 +02:00
e9ff060544 Update build 2022-11-22 19:52:25 +02:00
7d677f4a34 Update build 2022-11-22 19:52:11 +02:00
8f96b8c98b Update build 2022-11-22 19:51:09 +02:00
1084a808c7 Update build 2022-11-22 19:40:02 +02:00
3838f774bf Update build 2022-11-22 19:33:38 +02:00
06bb275f0d Update build 2022-11-22 19:18:48 +02:00
a05f7cc987 Update build 2022-11-22 19:15:34 +02:00
c5c8bc5bb3 Update build 2022-11-22 19:14:50 +02:00
d6bc4e51e5 Update build 2022-11-22 19:11:00 +02:00
4ae02f70d6 Update build 2022-11-22 18:54:22 +02:00
d593d6dc83 Update build 2022-11-22 18:35:36 +02:00
1a1fa9450e Update build 2022-11-22 18:34:12 +02:00
0d24604f2a Update build 2022-11-22 18:33:46 +02:00
1d7c994036 Update build 2022-11-22 18:33:06 +02:00
bc2bf24a65 Update build 2022-11-22 18:32:47 +02:00
cdbfc7891d Update build 2022-11-22 18:30:25 +02:00
c730341674 Update build 2022-11-22 18:28:50 +02:00
b621b76e37 Connect to mediasoup with timeout(fix when it appears offline) 2022-11-22 18:27:56 +02:00
39ad9cad27 Update bundle 2022-11-22 18:10:05 +02:00
8860423e21 LH-265: Update client config 2022-11-22 10:28:45 +02:00
9179a67f64 LH-265: Enable audio on video server 2022-11-21 22:59:41 +02:00
6 changed files with 271 additions and 357 deletions

View File

@ -22,20 +22,18 @@
2. Run the `npm start:prod` command to start the server in production mode. 2. Run the `npm start:prod` command to start the server in production mode.
(To connect to the terminal, use `pm2 log video-server`) (To connect to the terminal, use `pm2 log video-server`)
---
### Web client
- The server will start by default on port 3000, and the ssl certificates will have to be configured - The server will start by default on port 3000, and the ssl certificates will have to be configured
- The web client can be accessed using the /sfu path - The web client can be accessed using the /sfu path
ex: https://HOST/sfu/?assetId=1&&accountId=1&producer=true&dest_asset_id=75&assetName=Adi ex: http://localhost:3000/sfu/?assetId=1&&accountId=1&producer=true&assetName=Adi&assetType=linx
assetId = asset id of the unit on which you are doing the test assetId = asset id of the unit on which you are doing the test
accountId = account id of the unit on which you are doing the test accountId = account id of the unit on which you are doing the test
producer = it will always be true because you are the producer producer = it will always be true because you are the producer
(it's possible to put false, but then you have to have another client with producer true) (it's possible to put false, but then you have to have another client with producer true)
assetName = asset name of the unit on which you are doing the test assetName = asset name of the unit on which you are doing the test
dest_asset_id= the addressee with whom the call is made assetType = asset type of the unit on which you are doing the test
- To make a call using this client, you need a microphone and permission to use it
- For any changes related to the client, the command `npm run watch' will have to be used to generate the bundle.js used by the web client
### Demo project ### Demo project
The demo project used initially and then modified for our needs `https://github.com/jamalag/mediasoup2` The demo project used initially and then modified for our needs `https://github.com/jamalag/mediasoup2`

272
app.js
View File

@ -90,81 +90,39 @@ worker = createWorker();
// https://mediasoup.org/documentation/v3/mediasoup/rtp-parameters-and-capabilities/#RtpCodecCapability // https://mediasoup.org/documentation/v3/mediasoup/rtp-parameters-and-capabilities/#RtpCodecCapability
// list of media codecs supported by mediasoup ... // list of media codecs supported by mediasoup ...
// https://github.com/versatica/mediasoup/blob/v3/src/supportedRtpCapabilities.ts // https://github.com/versatica/mediasoup/blob/v3/src/supportedRtpCapabilities.ts
const mediaCodecs = [ const mediaCodecs = [
{ {
kind : 'audio', // kind: 'audio',
mimeType : 'audio/opus', // mimeType: 'audio/opus',
clockRate : 48000, // clockRate: 48000,
channels : 2 // channels: 2,
clockRate: 48000,
mimeType: "audio/opus",
parameters: {
minptime: 10,
useinbandfec: 1
},
payloadType: 111,
rtcpFeedback: [{
parameter: "",
type: "transport-cc"
}]
}, },
{ {
kind : 'video', kind: 'video',
mimeType : 'video/VP8', mimeType: 'video/VP8',
clockRate : 90000, clockRate: 90000,
parameters : parameters: {
{ 'x-google-start-bitrate': 1000,
'x-google-start-bitrate' : 1000
}, },
channels : 2
}, },
{
kind : 'video',
mimeType : 'video/VP9',
clockRate : 90000,
parameters :
{
'profile-id' : 2,
'x-google-start-bitrate' : 1000
}
},
{
kind : 'video',
mimeType : 'video/h264',
clockRate : 90000,
parameters :
{
'packetization-mode' : 1,
'profile-level-id' : '4d0032',
'level-asymmetry-allowed' : 1,
'x-google-start-bitrate' : 1000
}
},
{
kind : 'video',
mimeType : 'video/h264',
clockRate : 90000,
parameters :
{
'packetization-mode' : 1,
'profile-level-id' : '42e01f',
'level-asymmetry-allowed' : 1,
'x-google-start-bitrate' : 1000
}
}
// {
// kind: 'audio',
// mimeType: 'audio/opus',
// clockRate: 48000,
// channels: 2,
// },
// {
// kind: 'video',
// mimeType: 'video/VP8',
// clockRate: 90000,
// parameters: {
// 'x-google-start-bitrate': 1000,
// },
// },
]; ];
const closeCall = (callId) => { const closeCall = (callId) => {
try { try {
if (callId && videoCalls[callId]) { if (callId && videoCalls[callId]) {
videoCalls[callId].producerVideo?.close(); videoCalls[callId].producer?.close();
videoCalls[callId].producerAudio?.close(); videoCalls[callId].consumer?.close();
videoCalls[callId].consumerVideo?.close();
videoCalls[callId].consumerAudio?.close();
videoCalls[callId]?.consumerTransport?.close(); videoCalls[callId]?.consumerTransport?.close();
videoCalls[callId]?.producerTransport?.close(); videoCalls[callId]?.producerTransport?.close();
videoCalls[callId]?.router?.close(); videoCalls[callId]?.router?.close();
@ -283,54 +241,29 @@ peers.on('connection', async socket => {
- The event sent by the client (PRODUCER) after successfully connecting to producerTransport - The event sent by the client (PRODUCER) after successfully connecting to producerTransport
- For the router with the id callId, we make produce on producerTransport - For the router with the id callId, we make produce on producerTransport
- Create the handler on producer at the 'transportclose' event - Create the handler on producer at the 'transportclose' event
*/ */
socket.on('transport-produce', async ({ kind, rtpParameters, appData }, callback) => { socket.on('transport-produce', async ({ kind, rtpParameters, appData }, callback) => {
try { try {
const callId = socketDetails[socket.id]; const callId = socketDetails[socket.id];
if (typeof rtpParameters === 'string') rtpParameters = JSON.parse(rtpParameters); if (typeof rtpParameters === 'string') rtpParameters = JSON.parse(rtpParameters);
console.log(`[transport-produce] kind: ${kind} | socket.id: ${socket.id} | callId: ${callId}`);
console.log('kind', kind);
console.log('rtpParameters', rtpParameters);
if (kind === 'video') { console.log('[transport-produce] | socket.id', socket.id, '| callId', callId);
videoCalls[callId].producerVideo = await videoCalls[callId].producerTransport.produce({ videoCalls[callId].producer = await videoCalls[callId].producerTransport.produce({
kind, kind,
rtpParameters, rtpParameters,
}); });
console.log(`[transport-produce] Producer ID: ${videoCalls[callId].producer.id} | kind: ${videoCalls[callId].producer.kind}`);
videoCalls[callId].producer.on('transportclose', () => {
const callId = socketDetails[socket.id];
console.log('transport for this producer closed', callId)
closeCall(callId);
});
console.log(`[transport-produce] Producer ID: ${videoCalls[callId].producerVideo.id} | kind: ${videoCalls[callId].producerVideo.kind}`); // Send back to the client the Producer's id
callback && callback({
videoCalls[callId].producerVideo.on('transportclose', () => { id: videoCalls[callId].producer.id
const callId = socketDetails[socket.id]; });
console.log('transport for this producer closed', callId)
closeCall(callId);
});
// Send back to the client the Producer's id
callback && callback({
id: videoCalls[callId].producerVideo.id
});
} else if (kind === 'audio') {
videoCalls[callId].producerAudio = await videoCalls[callId].producerTransport.produce({
kind,
rtpParameters,
});
console.log(`[transport-produce] Producer ID: ${videoCalls[callId].producerAudio.id} | kind: ${videoCalls[callId].producerAudio.kind}`);
videoCalls[callId].producerAudio.on('transportclose', () => {
const callId = socketDetails[socket.id];
console.log('transport for this producer closed', callId)
closeCall(callId);
});
// Send back to the client the Producer's id
callback && callback({
id: videoCalls[callId].producerAudio.id
});
}
} catch (error) { } catch (error) {
console.log(`ERROR | transport-produce | callId ${socketDetails[socket.id]} | ${error.message}`); console.log(`ERROR | transport-produce | callId ${socketDetails[socket.id]} | ${error.message}`);
} }
@ -359,36 +292,48 @@ peers.on('connection', async socket => {
*/ */
socket.on('consume', async ({ rtpCapabilities }, callback) => { socket.on('consume', async ({ rtpCapabilities }, callback) => {
try { try {
console.log(`[consume] rtpCapabilities: ${JSON.stringify(rtpCapabilities)}`);
const callId = socketDetails[socket.id]; const callId = socketDetails[socket.id];
console.log('[consume] callId', callId); console.log('[consume] callId', callId);
const canConsumeVideo = !!videoCalls[callId].producerVideo && !!videoCalls[callId].router.canConsume({ // Check if the router can consume the specified producer
producerId: videoCalls[callId].producerVideo.id, if (videoCalls[callId].router.canConsume({
producerId: videoCalls[callId].producer.id,
rtpCapabilities rtpCapabilities
}) })) {
console.log('[consume] Can consume', callId);
// Transport can now consume and return a consumer
videoCalls[callId].consumer = await videoCalls[callId].consumerTransport.consume({
producerId: videoCalls[callId].producer.id,
rtpCapabilities,
paused: true,
});
const canConsumeAudio = !!videoCalls[callId].producerAudio && !!videoCalls[callId].router.canConsume({ // https://mediasoup.org/documentation/v3/mediasoup/api/#consumer-on-transportclose
producerId: videoCalls[callId].producerAudio.id, videoCalls[callId].consumer.on('transportclose', () => {
rtpCapabilities const callId = socketDetails[socket.id];
}) console.log('transport close from consumer', callId);
closeCall(callId);
});
console.log('[consume] canConsumeVideo', canConsumeVideo); // https://mediasoup.org/documentation/v3/mediasoup/api/#consumer-on-producerclose
console.log('[consume] canConsumeAudio', canConsumeAudio); videoCalls[callId].consumer.on('producerclose', () => {
const callId = socketDetails[socket.id];
console.log('producer of consumer closed', callId);
closeCall(callId);
});
if (canConsumeVideo && !canConsumeAudio) { // From the consumer extract the following params to send back to the Client
console.log('1'); const params = {
const videoParams = await consumeVideo(callId, rtpCapabilities) id: videoCalls[callId].consumer.id,
console.log('videoParams', videoParams); producerId: videoCalls[callId].producer.id,
callback({ videoParams, audioParams: null }); kind: videoCalls[callId].consumer.kind,
} else if (canConsumeVideo && canConsumeAudio) { rtpParameters: videoCalls[callId].consumer.rtpParameters,
console.log('2'); };
const videoParams = await consumeVideo(callId, rtpCapabilities)
const audioParams = await consumeAudio(callId, rtpCapabilities) // Send the parameters to the client
callback({ videoParams, audioParams }); callback({ params });
} else { } else {
console.log(`[consume] Can't consume | callId ${callId}`); console.log(`[canConsume] Can't consume | callId ${callId}`);
callback(null); callback(null);
} }
} catch (error) { } catch (error) {
@ -405,71 +350,13 @@ peers.on('connection', async socket => {
try { try {
const callId = socketDetails[socket.id]; const callId = socketDetails[socket.id];
console.log(`[consumer-resume] callId ${callId}`) console.log(`[consumer-resume] callId ${callId}`)
await videoCalls[callId].consumerVideo.resume(); await videoCalls[callId].consumer.resume();
await videoCalls[callId].consumerAudio.resume();
} catch (error) { } catch (error) {
console.log(`ERROR | consumer-resume | callId ${socketDetails[socket.id]} | ${error.message}`); console.log(`ERROR | consumer-resume | callId ${socketDetails[socket.id]} | ${error.message}`);
} }
}); });
}); });
const consumeVideo = async (callId, rtpCapabilities) => {
videoCalls[callId].consumerVideo = await videoCalls[callId].consumerTransport.consume({
producerId: videoCalls[callId].producerVideo.id,
rtpCapabilities,
paused: true,
});
// https://mediasoup.org/documentation/v3/mediasoup/api/#consumer-on-transportclose
videoCalls[callId].consumerVideo.on('transportclose', () => {
const callId = socketDetails[socket.id];
console.log('transport close from consumer', callId);
closeCall(callId);
});
// https://mediasoup.org/documentation/v3/mediasoup/api/#consumer-on-producerclose
videoCalls[callId].consumerVideo.on('producerclose', () => {
const callId = socketDetails[socket.id];
console.log('producer of consumer closed', callId);
closeCall(callId);
});
return {
id: videoCalls[callId].consumerVideo.id,
producerId: videoCalls[callId].producerVideo.id,
kind: 'video',
rtpParameters: videoCalls[callId].consumerVideo.rtpParameters,
}
}
const consumeAudio = async (callId, rtpCapabilities) => {
videoCalls[callId].consumerAudio = await videoCalls[callId].consumerTransport.consume({
producerId: videoCalls[callId].producerAudio.id,
rtpCapabilities,
paused: true,
});
// https://mediasoup.org/documentation/v3/mediasoup/api/#consumer-on-transportclose
videoCalls[callId].consumerAudio.on('transportclose', () => {
const callId = socketDetails[socket.id];
console.log('transport close from consumer', callId);
closeCall(callId);
});
// https://mediasoup.org/documentation/v3/mediasoup/api/#consumer-on-producerclose
videoCalls[callId].consumerAudio.on('producerclose', () => {
const callId = socketDetails[socket.id];
console.log('producer of consumer closed', callId);
closeCall(callId);
});
return {
id: videoCalls[callId].consumerAudio.id,
producerId: videoCalls[callId].producerAudio.id,
kind: 'audio',
rtpParameters: videoCalls[callId].consumerAudio.rtpParameters,
}
}
/* /*
- Called from at event 'createWebRtcTransport' and assigned to the consumer or producer transport - Called from at event 'createWebRtcTransport' and assigned to the consumer or producer transport
- It will return parameters, these are required for the client to create the RecvTransport - It will return parameters, these are required for the client to create the RecvTransport
@ -517,7 +404,6 @@ const createWebRtcTransportLayer = async (callId, callback) => {
dtlsParameters: transport.dtlsParameters, dtlsParameters: transport.dtlsParameters,
}; };
console.log('[createWebRtcTransportLayer] callback params', params);
// Send back to the client the params // Send back to the client the params
callback({ params }); callback({ params });

View File

@ -43,4 +43,6 @@ fi
## POST BUILD ## POST BUILD
cd - cd -

View File

@ -20373,36 +20373,6 @@ console.log('[URL] ASSET_ID', ASSET_ID, '| ACCOUNT_ID', ACCOUNT_ID, '| callId',
console.log('🟩 config', config) console.log('🟩 config', config)
let socket, hub let socket, hub
let device
let rtpCapabilities
let producerTransport
let consumerTransport
let producerVideo
let producerAudio
let consumer
let originAssetId
// https://mediasoup.org/documentation/v3/mediasoup-client/api/#ProducerOptions
// https://mediasoup.org/documentation/v3/mediasoup-client/api/#transport-produce
let videoParams = {
encodings: [
{ scaleResolutionDownBy: 4, maxBitrate: 500000 },
{ scaleResolutionDownBy: 2, maxBitrate: 1000000 },
{ scaleResolutionDownBy: 1, maxBitrate: 5000000 },
{ scalabilityMode: 'S3T3_KEY' }
],
codecOptions: {
videoGoogleStartBitrate: 1000
}
}
let audioParams = {
codecOptions :
{
opusStereo : true,
opusDtx : true
}
}
setTimeout(() => { setTimeout(() => {
hub = io(config.hubAddress) hub = io(config.hubAddress)
@ -20476,10 +20446,66 @@ setTimeout(() => {
connectToMediasoup() connectToMediasoup()
} }
}, 1600); }, 2000);
let device
let rtpCapabilities
let producerTransport
let consumerTransport
let producerVideo
let producerAudio
let consumer
let originAssetId
// let originAssetName = 'Adi'
// let originAssetTypeName = 'linx'
// https://mediasoup.org/documentation/v3/mediasoup-client/api/#ProducerOptions
// https://mediasoup.org/documentation/v3/mediasoup-client/api/#transport-produce
let videoParams = {
// mediasoup params
encodings: [
{
maxBitrate: 100000,
scalabilityMode: 'S1T3',
},
{
maxBitrate: 300000,
scalabilityMode: 'S1T3',
},
{
maxBitrate: 900000,
scalabilityMode: 'S1T3',
},
],
// https://mediasoup.org/documentation/v3/mediasoup-client/api/#ProducerCodecOptions
codecOptions: {
videoGoogleStartBitrate: 1000
}
}
let audioParams = {
// mediasoup params
encodings: [
{
maxBitrate: 100000,
scalabilityMode: 'S1T3',
},
{
maxBitrate: 300000,
scalabilityMode: 'S1T3',
},
{
maxBitrate: 900000,
scalabilityMode: 'S1T3',
},
],
codecOptions: {
opusStereo: true
}
}
const streamSuccess = (stream) => { const streamSuccess = (stream) => {
console.log('[streamSuccess] device', device);
localVideo.srcObject = stream localVideo.srcObject = stream
console.log('stream', stream); console.log('stream', stream);
const videoTrack = stream.getVideoTracks()[0] const videoTrack = stream.getVideoTracks()[0]
@ -20504,23 +20530,20 @@ const getLocalStream = () => {
navigator.mediaDevices.getUserMedia({ navigator.mediaDevices.getUserMedia({
audio: true, audio: true,
video: { video: {
qvga : { width: { ideal: 320 }, height: { ideal: 240 } }, width: {
vga : { width: { ideal: 640 }, height: { ideal: 480 } }, min: 640,
hd : { width: { ideal: 1280 }, height: { ideal: 720 } } max: 1920,
},
height: {
min: 400,
max: 1080,
}
} }
}) })
.then(streamSuccess) .then(streamSuccess)
.catch(error => { .catch(error => {
console.log(error.message) console.log(error.message)
}) })
navigator.permissions.query(
{ name: 'microphone' }
).then((permissionStatus) =>{
console.log('🟨 [PERMISSION] permissionStatus', permissionStatus); // granted, denied, prompt
// It will block the code from execution and display "Permission denied" if we don't have microphone permissions
})
} }
const goConnect = () => { const goConnect = () => {
@ -20537,6 +20560,7 @@ const goCreateTransport = () => {
// server side to send/recive media // server side to send/recive media
const createDevice = async () => { const createDevice = async () => {
try { try {
console.log('[createDevice]');
device = new mediasoupClient.Device() device = new mediasoupClient.Device()
// https://mediasoup.org/documentation/v3/mediasoup-client/api/#device-load // https://mediasoup.org/documentation/v3/mediasoup-client/api/#device-load
@ -20547,8 +20571,7 @@ const createDevice = async () => {
}) })
console.log('Device RTP Capabilities', device.rtpCapabilities) console.log('Device RTP Capabilities', device.rtpCapabilities)
console.log('[createDevice] device', device);
// once the device loads, create transport // once the device loads, create transport
goCreateTransport() goCreateTransport()
@ -20580,17 +20603,16 @@ const createSendTransport = () => {
console.log('[createSendTransport'); console.log('[createSendTransport');
// see server's socket.on('createWebRtcTransport', sender?, ...) // see server's socket.on('createWebRtcTransport', sender?, ...)
// this is a call from Producer, so sender = true // this is a call from Producer, so sender = true
socket.emit('createWebRtcTransport', { sender: true, callId }, (value) => { socket.emit('createWebRtcTransport', { sender: true, callId }, ({ params }) => {
console.log(`[createWebRtcTransport] value: ${JSON.stringify(value)}`);
const params = value.params;
// The server sends back params needed // The server sends back params needed
// to create Send Transport on the client side // to create Send Transport on the client side
if (params.error) { if (params.error) {
console.log(params.error) console.log(params.error)
return return
} }
console.log('[createWebRtcTransport] params', params)
// creates a new WebRTC Transport to send media // creates a new WebRTC Transport to send media
// based on the server's producer transport params // based on the server's producer transport params
// https://mediasoup.org/documentation/v3/mediasoup-client/api/#TransportOptions // https://mediasoup.org/documentation/v3/mediasoup-client/api/#TransportOptions
@ -20619,7 +20641,7 @@ const createSendTransport = () => {
console.log('[produce] parameters', parameters) console.log('[produce] parameters', parameters)
try { try {
// Tell the server to create a Producer // tell the server to create a Producer
// with the following parameters and produce // with the following parameters and produce
// and expect back a server side producer id // and expect back a server side producer id
// see server's socket.on('transport-produce', ...) // see server's socket.on('transport-produce', ...)
@ -20645,40 +20667,33 @@ const connectSendTransport = async () => {
console.log('[connectSendTransport] producerTransport'); console.log('[connectSendTransport] producerTransport');
// We now call produce() to instruct the producer transport // we now call produce() to instruct the producer transport
// to send media to the Router // to send media to the Router
// https://mediasoup.org/documentation/v3/mediasoup-client/api/#transport-produce // https://mediasoup.org/documentation/v3/mediasoup-client/api/#transport-produce
// this action will trigger the 'connect' and 'produce' events above // this action will trigger the 'connect' and 'produce' events above
// Produce video
producerVideo = await producerTransport.produce(videoParams) producerVideo = await producerTransport.produce(videoParams)
console.log('videoParams', videoParams);
console.log('producerVideo', producerVideo); console.log('producerVideo', producerVideo);
producerVideo.on('trackended', () => { producerVideo.on('trackended', () => {
console.log('track ended') console.log('track ended')
// close video track // close video track
}) })
producerVideo.on('transportclose', () => { producerVideo.on('transportclose', () => {
console.log('transport ended') console.log('transport ended')
// close video track // close video track
})
// Produce audio
producerAudio = await producerTransport.produce(audioParams)
console.log('audioParams', audioParams);
console.log('producerAudio', producerAudio);
producerAudio.on('trackended', () => {
console.log('track ended')
// close audio track
}) })
producerAudio.on('transportclose', () => { // producerAudio = await producerTransport.produce(audioParams)
console.log('transport ended') // console.log('producerAudio', producerAudio);
// close audio track // producerAudio.on('trackended', () => {
}) // console.log('track ended')
// // close video track
// })
// producerAudio.on('transportclose', () => {
// console.log('transport ended')
// // close video track
// })
const answer = { const answer = {
origin_asset_id: ASSET_ID, origin_asset_id: ASSET_ID,
@ -20688,7 +20703,7 @@ const connectSendTransport = async () => {
origin_asset_type_name: ASSET_TYPE, origin_asset_type_name: ASSET_TYPE,
origin_asset_name: ASSET_NAME, origin_asset_name: ASSET_NAME,
video_call_id: callId, video_call_id: callId,
answer: 'accepted', // answer: accepted/rejected answer: 'accepted', // answer: 'rejected'
}; };
console.log('SEND answer', answer); console.log('SEND answer', answer);
@ -20704,7 +20719,7 @@ const connectSendTransport = async () => {
const createRecvTransport = async () => { const createRecvTransport = async () => {
console.log('createRecvTransport'); console.log('createRecvTransport');
// See server's socket.on('consume', sender?, ...) // see server's socket.on('consume', sender?, ...)
// this is a call from Consumer, so sender = false // this is a call from Consumer, so sender = false
await socket.emit('createWebRtcTransport', { sender: false, callId }, ({ params }) => { await socket.emit('createWebRtcTransport', { sender: false, callId }, ({ params }) => {
// The server sends back params needed // The server sends back params needed
@ -20716,13 +20731,13 @@ const createRecvTransport = async () => {
console.log('[createRecvTransport] params', params) console.log('[createRecvTransport] params', params)
// Creates a new WebRTC Transport to receive media // creates a new WebRTC Transport to receive media
// based on server's consumer transport params // based on server's consumer transport params
// https://mediasoup.org/documentation/v3/mediasoup-client/api/#device-createRecvTransport // https://mediasoup.org/documentation/v3/mediasoup-client/api/#device-createRecvTransport
consumerTransport = device.createRecvTransport(params) consumerTransport = device.createRecvTransport(params)
// https://mediasoup.org/documentation/v3/communication-between-client-and-server/#producing-media // https://mediasoup.org/documentation/v3/communication-between-client-and-server/#producing-media
// This event is raised when a first call to transport.produce() is made // this event is raised when a first call to transport.produce() is made
// see connectRecvTransport() below // see connectRecvTransport() below
consumerTransport.on('connect', async ({ dtlsParameters }, callback, errback) => { consumerTransport.on('connect', async ({ dtlsParameters }, callback, errback) => {
try { try {
@ -20756,7 +20771,7 @@ const resetCallSettings = () => {
const connectRecvTransport = async () => { const connectRecvTransport = async () => {
console.log('connectRecvTransport'); console.log('connectRecvTransport');
// For consumer, we need to tell the server first // for consumer, we need to tell the server first
// to create a consumer based on the rtpCapabilities and consume // to create a consumer based on the rtpCapabilities and consume
// if the router can consume, it will send back a set of params as below // if the router can consume, it will send back a set of params as below
await socket.emit('consume', { await socket.emit('consume', {
@ -20768,7 +20783,7 @@ const connectRecvTransport = async () => {
return return
} }
// Then consume with the local consumer transport // then consume with the local consumer transport
// which creates a consumer // which creates a consumer
consumer = await consumerTransport.consume({ consumer = await consumerTransport.consume({
id: params.id, id: params.id,
@ -20811,7 +20826,6 @@ const closeCall = () => {
resetCallSettings() resetCallSettings()
} }
btnLocalVideo.addEventListener('click', getLocalStream) btnLocalVideo.addEventListener('click', getLocalStream)
btnRecvSendTransport.addEventListener('click', goConnect) btnRecvSendTransport.addEventListener('click', goConnect)
btnCloseCall.addEventListener('click', closeCall) btnCloseCall.addEventListener('click', closeCall)

View File

@ -43,7 +43,7 @@
<tr> <tr>
<td> <td>
<div id="sharedBtns"> <div id="sharedBtns">
<video id="localVideo" autoplay class="video" muted></video> <video id="localVideo" autoplay class="video" ></video>
</div> </div>
</td> </td>
<td> <td>

View File

@ -15,36 +15,6 @@ console.log('[URL] ASSET_ID', ASSET_ID, '| ACCOUNT_ID', ACCOUNT_ID, '| callId',
console.log('🟩 config', config) console.log('🟩 config', config)
let socket, hub let socket, hub
let device
let rtpCapabilities
let producerTransport
let consumerTransport
let producerVideo
let producerAudio
let consumer
let originAssetId
// https://mediasoup.org/documentation/v3/mediasoup-client/api/#ProducerOptions
// https://mediasoup.org/documentation/v3/mediasoup-client/api/#transport-produce
let videoParams = {
encodings: [
{ scaleResolutionDownBy: 4, maxBitrate: 500000 },
{ scaleResolutionDownBy: 2, maxBitrate: 1000000 },
{ scaleResolutionDownBy: 1, maxBitrate: 5000000 },
{ scalabilityMode: 'S3T3_KEY' }
],
codecOptions: {
videoGoogleStartBitrate: 1000
}
}
let audioParams = {
codecOptions :
{
opusStereo : true,
opusDtx : true
}
}
setTimeout(() => { setTimeout(() => {
hub = io(config.hubAddress) hub = io(config.hubAddress)
@ -118,10 +88,66 @@ setTimeout(() => {
connectToMediasoup() connectToMediasoup()
} }
}, 1600); }, 2000);
let device
let rtpCapabilities
let producerTransport
let consumerTransport
let producerVideo
let producerAudio
let consumer
let originAssetId
// let originAssetName = 'Adi'
// let originAssetTypeName = 'linx'
// https://mediasoup.org/documentation/v3/mediasoup-client/api/#ProducerOptions
// https://mediasoup.org/documentation/v3/mediasoup-client/api/#transport-produce
let videoParams = {
// mediasoup params
encodings: [
{
maxBitrate: 100000,
scalabilityMode: 'S1T3',
},
{
maxBitrate: 300000,
scalabilityMode: 'S1T3',
},
{
maxBitrate: 900000,
scalabilityMode: 'S1T3',
},
],
// https://mediasoup.org/documentation/v3/mediasoup-client/api/#ProducerCodecOptions
codecOptions: {
videoGoogleStartBitrate: 1000
}
}
let audioParams = {
// mediasoup params
encodings: [
{
maxBitrate: 100000,
scalabilityMode: 'S1T3',
},
{
maxBitrate: 300000,
scalabilityMode: 'S1T3',
},
{
maxBitrate: 900000,
scalabilityMode: 'S1T3',
},
],
codecOptions: {
opusStereo: true
}
}
const streamSuccess = (stream) => { const streamSuccess = (stream) => {
console.log('[streamSuccess] device', device);
localVideo.srcObject = stream localVideo.srcObject = stream
console.log('stream', stream); console.log('stream', stream);
const videoTrack = stream.getVideoTracks()[0] const videoTrack = stream.getVideoTracks()[0]
@ -146,23 +172,20 @@ const getLocalStream = () => {
navigator.mediaDevices.getUserMedia({ navigator.mediaDevices.getUserMedia({
audio: true, audio: true,
video: { video: {
qvga : { width: { ideal: 320 }, height: { ideal: 240 } }, width: {
vga : { width: { ideal: 640 }, height: { ideal: 480 } }, min: 640,
hd : { width: { ideal: 1280 }, height: { ideal: 720 } } max: 1920,
},
height: {
min: 400,
max: 1080,
}
} }
}) })
.then(streamSuccess) .then(streamSuccess)
.catch(error => { .catch(error => {
console.log(error.message) console.log(error.message)
}) })
navigator.permissions.query(
{ name: 'microphone' }
).then((permissionStatus) =>{
console.log('🟨 [PERMISSION] permissionStatus', permissionStatus); // granted, denied, prompt
// It will block the code from execution and display "Permission denied" if we don't have microphone permissions
})
} }
const goConnect = () => { const goConnect = () => {
@ -179,6 +202,7 @@ const goCreateTransport = () => {
// server side to send/recive media // server side to send/recive media
const createDevice = async () => { const createDevice = async () => {
try { try {
console.log('[createDevice]');
device = new mediasoupClient.Device() device = new mediasoupClient.Device()
// https://mediasoup.org/documentation/v3/mediasoup-client/api/#device-load // https://mediasoup.org/documentation/v3/mediasoup-client/api/#device-load
@ -189,8 +213,7 @@ const createDevice = async () => {
}) })
console.log('Device RTP Capabilities', device.rtpCapabilities) console.log('Device RTP Capabilities', device.rtpCapabilities)
console.log('[createDevice] device', device);
// once the device loads, create transport // once the device loads, create transport
goCreateTransport() goCreateTransport()
@ -222,17 +245,16 @@ const createSendTransport = () => {
console.log('[createSendTransport'); console.log('[createSendTransport');
// see server's socket.on('createWebRtcTransport', sender?, ...) // see server's socket.on('createWebRtcTransport', sender?, ...)
// this is a call from Producer, so sender = true // this is a call from Producer, so sender = true
socket.emit('createWebRtcTransport', { sender: true, callId }, (value) => { socket.emit('createWebRtcTransport', { sender: true, callId }, ({ params }) => {
console.log(`[createWebRtcTransport] value: ${JSON.stringify(value)}`);
const params = value.params;
// The server sends back params needed // The server sends back params needed
// to create Send Transport on the client side // to create Send Transport on the client side
if (params.error) { if (params.error) {
console.log(params.error) console.log(params.error)
return return
} }
console.log('[createWebRtcTransport] params', params)
// creates a new WebRTC Transport to send media // creates a new WebRTC Transport to send media
// based on the server's producer transport params // based on the server's producer transport params
// https://mediasoup.org/documentation/v3/mediasoup-client/api/#TransportOptions // https://mediasoup.org/documentation/v3/mediasoup-client/api/#TransportOptions
@ -261,7 +283,7 @@ const createSendTransport = () => {
console.log('[produce] parameters', parameters) console.log('[produce] parameters', parameters)
try { try {
// Tell the server to create a Producer // tell the server to create a Producer
// with the following parameters and produce // with the following parameters and produce
// and expect back a server side producer id // and expect back a server side producer id
// see server's socket.on('transport-produce', ...) // see server's socket.on('transport-produce', ...)
@ -287,40 +309,33 @@ const connectSendTransport = async () => {
console.log('[connectSendTransport] producerTransport'); console.log('[connectSendTransport] producerTransport');
// We now call produce() to instruct the producer transport // we now call produce() to instruct the producer transport
// to send media to the Router // to send media to the Router
// https://mediasoup.org/documentation/v3/mediasoup-client/api/#transport-produce // https://mediasoup.org/documentation/v3/mediasoup-client/api/#transport-produce
// this action will trigger the 'connect' and 'produce' events above // this action will trigger the 'connect' and 'produce' events above
// Produce video
producerVideo = await producerTransport.produce(videoParams) producerVideo = await producerTransport.produce(videoParams)
console.log('videoParams', videoParams);
console.log('producerVideo', producerVideo); console.log('producerVideo', producerVideo);
producerVideo.on('trackended', () => { producerVideo.on('trackended', () => {
console.log('track ended') console.log('track ended')
// close video track // close video track
}) })
producerVideo.on('transportclose', () => { producerVideo.on('transportclose', () => {
console.log('transport ended') console.log('transport ended')
// close video track // close video track
})
// Produce audio
producerAudio = await producerTransport.produce(audioParams)
console.log('audioParams', audioParams);
console.log('producerAudio', producerAudio);
producerAudio.on('trackended', () => {
console.log('track ended')
// close audio track
}) })
producerAudio.on('transportclose', () => { // producerAudio = await producerTransport.produce(audioParams)
console.log('transport ended') // console.log('producerAudio', producerAudio);
// close audio track // producerAudio.on('trackended', () => {
}) // console.log('track ended')
// // close video track
// })
// producerAudio.on('transportclose', () => {
// console.log('transport ended')
// // close video track
// })
const answer = { const answer = {
origin_asset_id: ASSET_ID, origin_asset_id: ASSET_ID,
@ -330,7 +345,7 @@ const connectSendTransport = async () => {
origin_asset_type_name: ASSET_TYPE, origin_asset_type_name: ASSET_TYPE,
origin_asset_name: ASSET_NAME, origin_asset_name: ASSET_NAME,
video_call_id: callId, video_call_id: callId,
answer: 'accepted', // answer: accepted/rejected answer: 'accepted', // answer: 'rejected'
}; };
console.log('SEND answer', answer); console.log('SEND answer', answer);
@ -346,7 +361,7 @@ const connectSendTransport = async () => {
const createRecvTransport = async () => { const createRecvTransport = async () => {
console.log('createRecvTransport'); console.log('createRecvTransport');
// See server's socket.on('consume', sender?, ...) // see server's socket.on('consume', sender?, ...)
// this is a call from Consumer, so sender = false // this is a call from Consumer, so sender = false
await socket.emit('createWebRtcTransport', { sender: false, callId }, ({ params }) => { await socket.emit('createWebRtcTransport', { sender: false, callId }, ({ params }) => {
// The server sends back params needed // The server sends back params needed
@ -358,13 +373,13 @@ const createRecvTransport = async () => {
console.log('[createRecvTransport] params', params) console.log('[createRecvTransport] params', params)
// Creates a new WebRTC Transport to receive media // creates a new WebRTC Transport to receive media
// based on server's consumer transport params // based on server's consumer transport params
// https://mediasoup.org/documentation/v3/mediasoup-client/api/#device-createRecvTransport // https://mediasoup.org/documentation/v3/mediasoup-client/api/#device-createRecvTransport
consumerTransport = device.createRecvTransport(params) consumerTransport = device.createRecvTransport(params)
// https://mediasoup.org/documentation/v3/communication-between-client-and-server/#producing-media // https://mediasoup.org/documentation/v3/communication-between-client-and-server/#producing-media
// This event is raised when a first call to transport.produce() is made // this event is raised when a first call to transport.produce() is made
// see connectRecvTransport() below // see connectRecvTransport() below
consumerTransport.on('connect', async ({ dtlsParameters }, callback, errback) => { consumerTransport.on('connect', async ({ dtlsParameters }, callback, errback) => {
try { try {
@ -398,7 +413,7 @@ const resetCallSettings = () => {
const connectRecvTransport = async () => { const connectRecvTransport = async () => {
console.log('connectRecvTransport'); console.log('connectRecvTransport');
// For consumer, we need to tell the server first // for consumer, we need to tell the server first
// to create a consumer based on the rtpCapabilities and consume // to create a consumer based on the rtpCapabilities and consume
// if the router can consume, it will send back a set of params as below // if the router can consume, it will send back a set of params as below
await socket.emit('consume', { await socket.emit('consume', {
@ -410,7 +425,7 @@ const connectRecvTransport = async () => {
return return
} }
// Then consume with the local consumer transport // then consume with the local consumer transport
// which creates a consumer // which creates a consumer
consumer = await consumerTransport.consume({ consumer = await consumerTransport.consume({
id: params.id, id: params.id,
@ -453,7 +468,6 @@ const closeCall = () => {
resetCallSettings() resetCallSettings()
} }
btnLocalVideo.addEventListener('click', getLocalStream) btnLocalVideo.addEventListener('click', getLocalStream)
btnRecvSendTransport.addEventListener('click', goConnect) btnRecvSendTransport.addEventListener('click', goConnect)
btnCloseCall.addEventListener('click', closeCall) btnCloseCall.addEventListener('click', closeCall)