New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

mediasoup-client

Package Overview
Dependencies
Maintainers
1
Versions
245
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

mediasoup-client - npm Package Compare versions

Comparing version 0.6.8 to 0.6.9

95

lib/Consumer.js

@@ -5,2 +5,4 @@ import Logger from './Logger';

const PROFILES = new Set([ 'low', 'medium', 'high' ]);
const logger = new Logger('Consumer');

@@ -15,2 +17,3 @@

* @emits {originator: String, [appData]: Any} resume
* @emits {profile: String} effectiveprofilechange
* @emits unhandled

@@ -68,2 +71,10 @@ * @emits {originator: String} close

this._remotelyPaused = false;
// Preferred profile.
// @type {String}
this._preferredProfile = null;
// Effective profile.
// @type {String}
this._effectiveProfile = null;
}

@@ -192,2 +203,22 @@

/**
* The preferred profile.
*
* @type {String}
*/
get preferredProfile()
{
return this._preferredProfile;
}
/**
* The effective profile.
*
* @type {String}
*/
get effectiveProfile()
{
return this._effectiveProfile;
}
/**
* Closes the Consumer.

@@ -424,2 +455,66 @@ * This is called when the local Room is closed.

/**
* Set preferred receiving profile.
*
* @param {String} profile
*/
setPreferredProfile(profile)
{
logger.debug('setPreferredProfile() [profile:%s]', profile);
if (this._closed)
{
logger.error('setPreferredProfile() | Consumer closed');
return;
}
else if (profile === this._preferredProfile)
{
return;
}
else if (!PROFILES.has(profile))
{
logger.error('setPreferredProfile() | invalid profile "%s"', profile);
return;
}
this._preferredProfile = profile;
if (this._transport)
this._transport.setConsumerPreferredProfile(this, this._preferredProfile);
}
/**
* Preferred receiving profile was set on my remote Consumer.
*
* @param {String} profile
*/
remoteSetPreferredProfile(profile)
{
logger.debug('remoteSetPreferredProfile() [profile:%s]', profile);
if (this._closed || profile === this._preferredProfile)
return;
this._preferredProfile = profile;
}
/**
* Effective receiving profile changed on my remote Consumer.
*
* @param {String} profile
*/
remoteEffectiveProfileChanged(profile)
{
logger.debug('remoteEffectiveProfileChanged() [profile:%s]', profile);
if (this._closed || profile === this._effectiveProfile)
return;
this._effectiveProfile = profile;
this.safeEmit('effectiveprofilechange', this._effectiveProfile);
}
/**
* Mark this Consumer as suitable for reception or not.

@@ -426,0 +521,0 @@ *

@@ -112,2 +112,16 @@ import sdpTransform from 'sdp-transform';

{
// If simulcast is set, mangle the offer.
if (producer.simulcast)
{
logger.debug('addProducer() | enabling simulcast');
const sdpObject = sdpTransform.parse(offer.sdp);
sdpPlanBUtils.addSimulcastForTrack(sdpObject, track);
const offerSdp = sdpTransform.write(sdpObject);
offer = { type: 'offer', sdp: offerSdp };
}
logger.debug(

@@ -242,2 +256,16 @@ 'addProducer() | calling pc.setLocalDescription() [offer:%o]',

{
// If simulcast is set, mangle the offer.
if (producer.simulcast)
{
logger.debug('addProducer() | enabling simulcast');
const sdpObject = sdpTransform.parse(offer.sdp);
sdpPlanBUtils.addSimulcastForTrack(sdpObject, track);
const offerSdp = sdpTransform.write(sdpObject);
offer = { type: 'offer', sdp: offerSdp };
}
logger.debug(

@@ -244,0 +272,0 @@ 'replaceProducerTrack() | calling pc.setLocalDescription() [offer:%o]',

@@ -84,2 +84,6 @@ import sdpTransform from 'sdp-transform';

this._stream = new MediaStream();
// RID value counter for simulcast (so they never match).
// @type {Number}
this._nextRid = 1;
}

@@ -108,3 +112,53 @@

rtpSender = this._pc.addTrack(track, this._stream);
})
.then(() =>
{
// If simulcast is not enabled, do nothing.
if (!producer.simulcast)
return;
logger.debug('addProducer() | enabling simulcast');
const encodings = [];
if (producer.simulcast.high)
{
encodings.push(
{
rid : `high${this._nextRid}`,
active : true,
priority : 'high',
maxBitrate : producer.simulcast.high
});
}
if (producer.simulcast.medium)
{
encodings.push(
{
rid : `medium${this._nextRid}`,
active : true,
priority : 'medium',
maxBitrate : producer.simulcast.medium
});
}
if (producer.simulcast.low)
{
encodings.push(
{
rid : `low${this._nextRid}`,
active : true,
priority : 'low',
maxBitrate : producer.simulcast.low
});
}
// Update RID counter for future ones.
this._nextRid++;
return rtpSender.setParameters({ encodings });
})
.then(() =>
{
return this._pc.createOffer();

@@ -567,2 +621,21 @@ })

// NOTE: We need to add a real video track to get the RID extension mapping.
const canvas = document.createElement('canvas');
// NOTE: Otherwise Firefox fails in next line.
canvas.getContext('2d');
const fakeStream = canvas.captureStream();
const fakeVideoTrack = fakeStream.getVideoTracks()[0];
const rtpSender = pc.addTrack(fakeVideoTrack, fakeStream);
rtpSender.setParameters(
{
encodings :
[
{ rid: 'RID1', maxBitrate: 40000 },
{ rid: 'RID2', maxBitrate: 10000 }
]
});
return pc.createOffer(

@@ -575,2 +648,5 @@ {

{
try { canvas.remove(); }
catch (error) {}
try { pc.close(); }

@@ -586,2 +662,5 @@ catch (error) {}

{
try { canvas.remove(); }
catch (error2) {}
try { pc.close(); }

@@ -588,0 +667,0 @@ catch (error2) {}

228

lib/handlers/sdp/planBUtils.js
/**
* Fill the given RTP parameters for the given track.
*
* NOTE: Currently it assumes a single encoding (no simulcast).
*
* @param {RTCRtpParameters} rtpParameters - RTP parameters to be filled.

@@ -13,3 +11,2 @@ * @param {Object} sdpObj - Local SDP Object generated by sdp-transform.

const kind = track.kind;
const encoding = {};
const rtcp =

@@ -28,2 +25,118 @@ {

// First media SSRC (or the only one).
let firstSsrc;
// Get all the SSRCs.
const ssrcs = new Set();
for (const line of mSection.ssrcs || [])
{
if (line.attribute !== 'msid')
continue;
const trackId = line.value.split(' ')[1];
if (trackId === track.id)
{
const ssrc = line.id;
ssrcs.add(ssrc);
if (!firstSsrc)
firstSsrc = ssrc;
}
}
if (ssrcs.size === 0)
throw new Error(`a=ssrc line not found for local track [track.id:${track.id}]`);
// Get media and RTX SSRCs.
const ssrcToRtxSsrc = new Map();
// First assume RTX is used.
for (const line of mSection.ssrcGroups || [])
{
if (line.semantics !== 'FID')
continue;
let [ ssrc, rtxSsrc ] = line.ssrcs.split(/\s+/);
ssrc = Number(ssrc);
rtxSsrc = Number(rtxSsrc);
if (ssrcs.has(ssrc))
{
// Remove both the SSRC and RTX SSRC from the Set so later we know that they
// are already handled.
ssrcs.delete(ssrc);
ssrcs.delete(rtxSsrc);
// Add to the map.
ssrcToRtxSsrc.set(ssrc, rtxSsrc);
}
}
// If the Set of SSRCs is not empty it means that RTX is not being used, so take
// media SSRCs from there.
for (const ssrc of ssrcs)
{
// Add to the map.
ssrcToRtxSsrc.set(ssrc, null);
}
// Get RTCP info.
const ssrcCnameLine = mSection.ssrcs
.find((line) =>
{
return (line.attribute === 'cname' && line.id === firstSsrc);
});
if (ssrcCnameLine)
rtcp.cname = ssrcCnameLine.value;
// Fill RTP parameters.
rtpParameters.rtcp = rtcp;
rtpParameters.encodings = [];
const simulcast = ssrcToRtxSsrc.size > 1;
const simulcastProfiles = [ 'low', 'medium', 'high' ];
for (const [ ssrc, rtxSsrc ] of ssrcToRtxSsrc)
{
const encoding = { ssrc };
if (rtxSsrc)
encoding.rtx = { ssrc: rtxSsrc };
if (simulcast)
encoding.profile = simulcastProfiles.shift();
rtpParameters.encodings.push(encoding);
}
}
/**
* Adds simulcast into the given SDP for the given track.
*
* @param {Object} sdpObj - Local SDP Object generated by sdp-transform.
* @param {MediaStreamTrack} track
*/
export function addSimulcastForTrack(sdpObj, track)
{
const kind = track.kind;
const mSection = (sdpObj.media || [])
.find((m) => m.type === kind);
if (!mSection)
throw new Error(`m=${kind} section not found`);
let ssrc;
let rtxSsrc;
let msid;
// Get the SSRC.

@@ -40,3 +153,8 @@

if (trackId === track.id)
{
ssrc = line.id;
msid = line.value.split(' ')[0];
return true;
}
});

@@ -47,6 +165,2 @@

const ssrc = ssrcMsidLine.id;
encoding.ssrc = ssrc;
// Get the SSRC for RTX.

@@ -60,10 +174,8 @@

const ssrcs = line.ssrcs.split(/[ ]+/);
const ssrcs = line.ssrcs.split(/\s+/);
if (Number(ssrcs[0]) === ssrc)
{
const rtxSsrc = Number(ssrcs[1]);
rtxSsrc = Number(ssrcs[1]);
encoding.rtx = { ssrc: rtxSsrc };
return true;

@@ -73,4 +185,2 @@ }

// Get RTCP info.
const ssrcCnameLine = mSection.ssrcs

@@ -82,8 +192,90 @@ .find((line) =>

if (ssrcCnameLine)
rtcp.cname = ssrcCnameLine.value;
if (!ssrcCnameLine)
throw new Error(`CNAME line not found for local track [track.id:${track.id}]`);
// Fill RTP parameters.
rtpParameters.encodings = [ encoding ];
rtpParameters.rtcp = rtcp;
const cname = ssrcCnameLine.value;
const ssrc2 = ssrc + 1;
const ssrc3 = ssrc + 2;
mSection.ssrcGroups = mSection.ssrcGroups || [];
mSection.ssrcGroups.push(
{
semantics : 'SIM',
ssrcs : `${ssrc} ${ssrc2} ${ssrc3}`
});
mSection.ssrcs.push(
{
id : ssrc2,
attribute : 'cname',
value : cname
});
mSection.ssrcs.push(
{
id : ssrc2,
attribute : 'msid',
value : `${msid} ${track.id}`
});
mSection.ssrcs.push(
{
id : ssrc3,
attribute : 'cname',
value : cname
});
mSection.ssrcs.push(
{
id : ssrc3,
attribute : 'msid',
value : `${msid} ${track.id}`
});
if (rtxSsrc)
{
const rtxSsrc2 = rtxSsrc + 1;
const rtxSsrc3 = rtxSsrc + 2;
mSection.ssrcGroups.push(
{
semantics : 'FID',
ssrcs : `${ssrc2} ${rtxSsrc2}`
});
mSection.ssrcs.push(
{
id : rtxSsrc2,
attribute : 'cname',
value : cname
});
mSection.ssrcs.push(
{
id : rtxSsrc2,
attribute : 'msid',
value : `${msid} ${track.id}`
});
mSection.ssrcGroups.push(
{
semantics : 'FID',
ssrcs : `${ssrc3} ${rtxSsrc3}`
});
mSection.ssrcs.push(
{
id : rtxSsrc3,
attribute : 'cname',
value : cname
});
mSection.ssrcs.push(
{
id : rtxSsrc3,
attribute : 'msid',
value : `${msid} ${track.id}`
});
}
}

@@ -187,2 +187,6 @@ import sdpTransform from 'sdp-transform';

// If video, be ready for simulcast.
if (kind === 'video')
remoteMediaObj.xGoogleFlag = 'conference';
remoteMediaObj.rtp = [];

@@ -248,2 +252,9 @@ remoteMediaObj.rtcpFb = [];

{
// Don't add a header extension if not present in the offer.
const matchedLocalExt = (localMediaObj.ext || [])
.find((localExt) => localExt.uri === ext.uri);
if (!matchedLocalExt)
continue;
remoteMediaObj.ext.push(

@@ -250,0 +261,0 @@ {

@@ -257,2 +257,9 @@ import sdpTransform from 'sdp-transform';

{
// Don't add a header extension if not present in the offer.
const matchedLocalExt = (localMediaObj.ext || [])
.find((localExt) => localExt.uri === ext.uri);
if (!matchedLocalExt)
continue;
remoteMediaObj.ext.push(

@@ -266,2 +273,26 @@ {

// Simulcast.
if (localMediaObj.simulcast_03)
{
// eslint-disable-next-line camelcase
remoteMediaObj.simulcast_03 =
{
value : localMediaObj.simulcast_03.value.replace(/send/g, 'recv')
};
remoteMediaObj.rids = [];
for (const rid of localMediaObj.rids || [])
{
if (rid.direction !== 'send')
continue;
remoteMediaObj.rids.push(
{
id : rid.id,
direction : 'recv'
});
}
}
remoteMediaObj.rtcpMux = 'rtcp-mux';

@@ -268,0 +299,0 @@ remoteMediaObj.rtcpRsize = 'rtcp-rsize';

66

lib/handlers/sdp/unifiedPlanUtils.js
/**
* Fill the given RTP parameters for the given track.
*
* NOTE: Currently it assumes a single encoding (no simulcast).
*
* @param {RTCRtpParameters} rtpParameters - RTP parameters to be filled.

@@ -13,3 +11,2 @@ * @param {Object} sdpObj - Local SDP Object generated by sdp-transform.

const kind = track.kind;
const encoding = {};
const rtcp =

@@ -47,33 +44,52 @@ {

if (!ssrcCnameLine)
throw new Error(`a=ssrc line not found for local track [track.id:${track.id}]`);
let ssrc;
const ssrc = ssrcCnameLine.id;
if (ssrcCnameLine)
{
ssrc = ssrcCnameLine.id;
rtcp.cname = ssrcCnameLine.value;
}
encoding.ssrc = ssrcCnameLine.id;
rtcp.cname = ssrcCnameLine.value;
// Get a=rid lines.
// Get the SSRC for RTX.
// Array of Objects with rid and profile keys.
const simulcastStreams = [];
(mSection.ssrcGroups || [])
.some((line) =>
{
if (line.semantics !== 'FID')
return;
for (const rid of mSection.rids || [])
{
if (rid.direction !== 'send')
continue;
const ssrcs = line.ssrcs.split(/[ ]+/);
if (/^low/.test(rid.id))
simulcastStreams.push({ rid: rid.id, profile: 'low' });
else if (/^medium/.test(rid.id))
simulcastStreams.push({ rid: rid.id, profile: 'medium' });
if (/^high/.test(rid.id))
simulcastStreams.push({ rid: rid.id, profile: 'high' });
}
if (Number(ssrcs[0]) === ssrc)
{
const rtxSsrc = Number(ssrcs[1]);
// Fill RTP parameters.
encoding.rtx = { ssrc: rtxSsrc };
rtpParameters.rtcp = rtcp;
rtpParameters.encodings = [];
return true;
}
});
if (simulcastStreams.length === 0)
{
const encoding = { ssrc };
// Fill RTP parameters.
rtpParameters.encodings = [ encoding ];
rtpParameters.rtcp = rtcp;
rtpParameters.encodings.push(encoding);
}
else
{
for (const simulcastStream of simulcastStreams)
{
const encoding =
{
encodingId : simulcastStream.rid,
profile : simulcastStream.profile
};
rtpParameters.encodings.push(encoding);
}
}
}

@@ -6,2 +6,9 @@ import Logger from './Logger';

const SIMULCAST_DEFAULT =
{
low : 100000,
medium : 300000,
high : 1500000
};
const logger = new Logger('Producer');

@@ -20,5 +27,4 @@

* @emits {originator: String, [appData]: Any} @close
*
*/
constructor(track, appData)
constructor(track, options, appData)
{

@@ -47,2 +53,9 @@ super(logger);

// Simulcast.
// @type {Object|false}
this._simulcast = false;
if (options.simulcast)
this._simulcast = Object.assign({}, SIMULCAST_DEFAULT, options.simulcast);
// Associated Transport.

@@ -63,2 +76,5 @@ // @type {Transport}

this._remotelyPaused = false;
// Handle the effective track.
this._handleTrack();
}

@@ -117,2 +133,12 @@

/**
* Simulcast settings.
*
* @return {Object|false}
*/
get simulcast()
{
return this._simulcast;
}
/**
* App custom data.

@@ -426,3 +452,3 @@ *

// Stop the previous track.
try { this._track.stop(); }
try { this._track.onended = null; this._track.stop(); }
catch (error) {}

@@ -441,2 +467,5 @@

// Handle the effective track.
this._handleTrack();
// Return the new track.

@@ -458,2 +487,20 @@ return this._track;

}
/**
* @private
*/
_handleTrack()
{
// If the cloned track is closed (for example if the desktop sharing is closed
// via chrome UI) close the Producer.
this._track.onended = () =>
{
if (this._closed)
return;
logger.warn('track "ended" event, closing Producer');
this.close();
};
}
}

@@ -475,2 +475,4 @@ import Logger from './Logger';

* @param {MediaStreamTrack} track
* @param {Object} [options]
* @param {Object} [options.simulcast]
* @param {Any} [appData] - App custom data.

@@ -484,5 +486,5 @@ *

*/
createProducer(track, appData)
createProducer(track, options, appData)
{
logger.debug('createProducer() [track:%o]', track);
logger.debug('createProducer() [track:%o, options:%o]', track, options);

@@ -498,4 +500,6 @@ if (!this.joined)

options = options || {};
// Create a new Producer.
const producer = new Producer(track, appData);
const producer = new Producer(track, options, appData);

@@ -712,2 +716,38 @@ // Store it.

case 'consumerPreferredProfileSet':
{
const { id, peerName, profile } = notification;
const peer = this._peers.get(peerName);
if (!peer)
throw new Error(`no Peer found [name:"${peerName}"]`);
const consumer = peer.getConsumerById(id);
if (!consumer)
throw new Error(`Consumer not found [id:${id}]`);
consumer.remoteSetPreferredProfile(profile);
break;
}
case 'consumerEffectiveProfileChanged':
{
const { id, peerName, profile } = notification;
const peer = this._peers.get(peerName);
if (!peer)
throw new Error(`no Peer found [name:"${peerName}"]`);
const consumer = peer.getConsumerById(id);
if (!consumer)
throw new Error(`Consumer not found [id:${id}]`);
consumer.remoteEffectiveProfileChanged(profile);
break;
}
default:

@@ -714,0 +754,0 @@ throw new Error(`unknown notification method "${method}"`);

@@ -416,2 +416,18 @@ import Logger from './Logger';

/**
* @private
*/
setConsumerPreferredProfile(consumer, profile)
{
logger.debug('setConsumerPreferredProfile() [consumer:%o]', consumer);
const data =
{
id : consumer.id,
profile : profile
};
this.safeEmit('@notify', 'setConsumerPreferredProfile', data);
}
_execCommand(command, promiseHolder)

@@ -557,5 +573,6 @@ {

{
id : consumer.id,
transportId : this.id,
paused : consumer.locallyPaused
id : consumer.id,
transportId : this.id,
paused : consumer.locallyPaused,
preferredProfile : consumer.preferredProfile
};

@@ -567,3 +584,3 @@

{
const { paused } = response;
const { paused, preferredProfile, effectiveProfile } = response;

@@ -573,2 +590,8 @@ if (paused)

if (preferredProfile)
consumer.remoteSetPreferredProfile(preferredProfile);
if (effectiveProfile)
consumer.remoteEffectiveProfileChanged(effectiveProfile);
return consumerTrack;

@@ -575,0 +598,0 @@ });

@@ -273,3 +273,4 @@ # mediasoup protocol

transportId: 9999,
paused: false
paused: false,
preferredProfile: 'low'
}

@@ -282,3 +283,5 @@ ```

{
paused : false
paused: false,
preferredProfile: null,
effectiveProfile: 'default'
}

@@ -322,2 +325,18 @@ ```

### setConsumerPreferredProfile
Set the desired receiving profile.
Notification:
```js
{
method: 'setConsumerPreferredProfile',
notification: true,
id: 3333,
profile: 'high'
}
```
## From server to client

@@ -463,3 +482,5 @@

rtpParameters: {},
paused: false
paused: false,
preferredProfile: 'high',
effectiveProfile: 'medium',
appData: Any

@@ -504,5 +525,5 @@ }

### consumerClosed
### consumerPreferredProfileSet
A server-side `Consumer` has been closed (its originating `Peer` may have left the room, he may have closed it, or his server-side `Peer` or `Producer` may have been closed in the server).
A server-side `Consumer` has set its preferred receiving profile.

@@ -513,7 +534,25 @@ Notification:

{
method: 'consumerClosed',
method: 'consumerPreferredProfileSet',
notification: true,
id: 3333,
peerName: 'alice'
peerName: 'alice',
profile: 'medium'
}
```
### consumerEffectiveProfileChanged
The effective receiving profile in a server-side `Consumer` changed.
Notification:
```js
{
method: 'consumerEffectiveProfileChanged',
notification: true,
id: 3333,
peerName: 'alice',
profile: 'high'
}
```
{
"name": "mediasoup-client",
"version": "0.6.8",
"version": "0.6.9",
"description": "mediasoup client SDK for mediasoup >= 2.0.0",

@@ -5,0 +5,0 @@ "author": "Iñaki Baz Castillo <ibc@aliax.net> (https://inakibaz.me)",

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc