@stream-io/video-client 1.6.0 → 1.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/dist/index.browser.es.js +96 -54
- package/dist/index.browser.es.js.map +1 -1
- package/dist/index.cjs.js +96 -54
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +96 -54
- package/dist/index.es.js.map +1 -1
- package/dist/src/events/call.d.ts +5 -1
- package/dist/src/events/internal.d.ts +0 -4
- package/dist/src/rtc/codecs.d.ts +14 -0
- package/dist/src/store/CallState.d.ts +5 -0
- package/package.json +1 -1
- package/src/events/__tests__/call.test.ts +48 -1
- package/src/events/call.ts +29 -2
- package/src/events/internal.ts +3 -17
- package/src/rtc/__tests__/codecs.test.ts +145 -0
- package/src/rtc/__tests__/mocks/webrtc.mocks.ts +7 -0
- package/src/rtc/codecs.ts +67 -35
- package/src/store/CallState.ts +9 -4
package/dist/index.cjs.js
CHANGED
|
@@ -6756,7 +6756,7 @@ const logLevels = Object.freeze({
|
|
|
6756
6756
|
warn: 3,
|
|
6757
6757
|
error: 4,
|
|
6758
6758
|
});
|
|
6759
|
-
let logger$
|
|
6759
|
+
let logger$1;
|
|
6760
6760
|
let level = 'info';
|
|
6761
6761
|
const logToConsole = (logLevel, message, ...args) => {
|
|
6762
6762
|
let logMethod;
|
|
@@ -6790,7 +6790,7 @@ const logToConsole = (logLevel, message, ...args) => {
|
|
|
6790
6790
|
logMethod(message, ...args);
|
|
6791
6791
|
};
|
|
6792
6792
|
const setLogger = (l, lvl) => {
|
|
6793
|
-
logger$
|
|
6793
|
+
logger$1 = l;
|
|
6794
6794
|
if (lvl) {
|
|
6795
6795
|
setLogLevel(lvl);
|
|
6796
6796
|
}
|
|
@@ -6800,7 +6800,7 @@ const setLogLevel = (l) => {
|
|
|
6800
6800
|
};
|
|
6801
6801
|
const getLogLevel = () => level;
|
|
6802
6802
|
const getLogger = (withTags) => {
|
|
6803
|
-
const loggerMethod = logger$
|
|
6803
|
+
const loggerMethod = logger$1 || logToConsole;
|
|
6804
6804
|
const tags = (withTags || []).filter(Boolean).join(':');
|
|
6805
6805
|
const result = (logLevel, message, ...args) => {
|
|
6806
6806
|
if (logLevels[logLevel] >= logLevels[level]) {
|
|
@@ -6841,50 +6841,74 @@ const retryable = async (rpc, signal) => {
|
|
|
6841
6841
|
return result;
|
|
6842
6842
|
};
|
|
6843
6843
|
|
|
6844
|
+
/**
|
|
6845
|
+
* Returns back a list of sorted codecs, with the preferred codec first.
|
|
6846
|
+
*
|
|
6847
|
+
* @param kind the kind of codec to get.
|
|
6848
|
+
* @param preferredCodec the codec to prioritize (vp8, h264, vp9, av1...).
|
|
6849
|
+
* @param codecToRemove the codec to exclude from the list.
|
|
6850
|
+
*/
|
|
6844
6851
|
const getPreferredCodecs = (kind, preferredCodec, codecToRemove) => {
|
|
6845
|
-
|
|
6846
|
-
if (!('getCapabilities' in RTCRtpReceiver)) {
|
|
6847
|
-
logger('warn', 'RTCRtpReceiver.getCapabilities is not supported');
|
|
6852
|
+
if (!('getCapabilities' in RTCRtpReceiver))
|
|
6848
6853
|
return;
|
|
6849
|
-
|
|
6850
|
-
|
|
6851
|
-
if (!cap)
|
|
6854
|
+
const capabilities = RTCRtpReceiver.getCapabilities(kind);
|
|
6855
|
+
if (!capabilities)
|
|
6852
6856
|
return;
|
|
6853
|
-
const
|
|
6854
|
-
const
|
|
6855
|
-
const
|
|
6856
|
-
|
|
6857
|
-
|
|
6858
|
-
|
|
6859
|
-
const
|
|
6857
|
+
const preferred = [];
|
|
6858
|
+
const partiallyPreferred = [];
|
|
6859
|
+
const unpreferred = [];
|
|
6860
|
+
const preferredCodecMimeType = `${kind}/${preferredCodec.toLowerCase()}`;
|
|
6861
|
+
const codecToRemoveMimeType = codecToRemove && `${kind}/${codecToRemove.toLowerCase()}`;
|
|
6862
|
+
for (const codec of capabilities.codecs) {
|
|
6863
|
+
const codecMimeType = codec.mimeType.toLowerCase();
|
|
6864
|
+
const shouldRemoveCodec = codecMimeType === codecToRemoveMimeType;
|
|
6860
6865
|
if (shouldRemoveCodec)
|
|
6861
|
-
|
|
6862
|
-
const
|
|
6863
|
-
if (!
|
|
6864
|
-
|
|
6865
|
-
|
|
6866
|
+
continue; // skip this codec
|
|
6867
|
+
const isPreferredCodec = codecMimeType === preferredCodecMimeType;
|
|
6868
|
+
if (!isPreferredCodec) {
|
|
6869
|
+
unpreferred.push(codec);
|
|
6870
|
+
continue;
|
|
6866
6871
|
}
|
|
6867
|
-
//
|
|
6868
|
-
// profile-level-id is 42e01f
|
|
6869
|
-
|
|
6870
|
-
|
|
6871
|
-
|
|
6872
|
-
|
|
6873
|
-
|
|
6874
|
-
partialMatched.push(c);
|
|
6875
|
-
}
|
|
6876
|
-
return;
|
|
6872
|
+
// h264 is a special case, we want to prioritize the baseline codec with
|
|
6873
|
+
// profile-level-id is 42e01f and packetization-mode=0 for maximum
|
|
6874
|
+
// cross-browser compatibility.
|
|
6875
|
+
// this branch covers the other cases, such as vp8.
|
|
6876
|
+
if (codecMimeType !== 'video/h264') {
|
|
6877
|
+
preferred.push(codec);
|
|
6878
|
+
continue;
|
|
6877
6879
|
}
|
|
6878
|
-
|
|
6879
|
-
|
|
6880
|
-
|
|
6880
|
+
const sdpFmtpLine = codec.sdpFmtpLine;
|
|
6881
|
+
if (!sdpFmtpLine || !sdpFmtpLine.includes('profile-level-id=42e01f')) {
|
|
6882
|
+
// this is not the baseline h264 codec, prioritize it lower
|
|
6883
|
+
partiallyPreferred.push(codec);
|
|
6884
|
+
continue;
|
|
6885
|
+
}
|
|
6886
|
+
// packetization-mode mode is optional; when not present it defaults to 0:
|
|
6887
|
+
// https://datatracker.ietf.org/doc/html/rfc6184#section-6.2
|
|
6888
|
+
if (sdpFmtpLine.includes('packetization-mode=0') ||
|
|
6889
|
+
!sdpFmtpLine.includes('packetization-mode')) {
|
|
6890
|
+
preferred.unshift(codec);
|
|
6891
|
+
}
|
|
6892
|
+
else {
|
|
6893
|
+
preferred.push(codec);
|
|
6894
|
+
}
|
|
6895
|
+
}
|
|
6896
|
+
// return a sorted list of codecs, with the preferred codecs first
|
|
6897
|
+
return [...preferred, ...partiallyPreferred, ...unpreferred];
|
|
6881
6898
|
};
|
|
6899
|
+
/**
|
|
6900
|
+
* Returns a generic SDP for the given direction.
|
|
6901
|
+
* We use this SDP to send it as part of our JoinRequest so that the SFU
|
|
6902
|
+
* can use it to determine client's codec capabilities.
|
|
6903
|
+
*
|
|
6904
|
+
* @param direction the direction of the transceiver.
|
|
6905
|
+
*/
|
|
6882
6906
|
const getGenericSdp = async (direction) => {
|
|
6883
6907
|
const tempPc = new RTCPeerConnection();
|
|
6884
6908
|
tempPc.addTransceiver('video', { direction });
|
|
6885
6909
|
tempPc.addTransceiver('audio', { direction });
|
|
6886
6910
|
const offer = await tempPc.createOffer();
|
|
6887
|
-
|
|
6911
|
+
const sdp = offer.sdp ?? '';
|
|
6888
6912
|
tempPc.getTransceivers().forEach((t) => {
|
|
6889
6913
|
t.stop?.();
|
|
6890
6914
|
});
|
|
@@ -7903,6 +7927,13 @@ class CallState {
|
|
|
7903
7927
|
this.setOwnCapabilities = (capabilities) => {
|
|
7904
7928
|
return this.setCurrentValue(this.ownCapabilitiesSubject, capabilities);
|
|
7905
7929
|
};
|
|
7930
|
+
/**
|
|
7931
|
+
* Sets the time when this call has been ended.
|
|
7932
|
+
* @param endedAt the time when this call has been ended.
|
|
7933
|
+
*/
|
|
7934
|
+
this.setEndedAt = (endedAt) => {
|
|
7935
|
+
return this.setCurrentValue(this.endedAtSubject, endedAt);
|
|
7936
|
+
};
|
|
7906
7937
|
/**
|
|
7907
7938
|
* Will try to find the participant with the given sessionId in the current call.
|
|
7908
7939
|
*
|
|
@@ -8089,7 +8120,7 @@ class CallState {
|
|
|
8089
8120
|
this.setCurrentValue(this.createdAtSubject, new Date(call.created_at));
|
|
8090
8121
|
this.setCurrentValue(this.updatedAtSubject, new Date(call.updated_at));
|
|
8091
8122
|
this.setCurrentValue(this.startsAtSubject, call.starts_at ? new Date(call.starts_at) : undefined);
|
|
8092
|
-
this.
|
|
8123
|
+
this.setEndedAt(call.ended_at ? new Date(call.ended_at) : undefined);
|
|
8093
8124
|
this.setCurrentValue(this.createdBySubject, call.created_by);
|
|
8094
8125
|
this.setCurrentValue(this.customSubject, call.custom);
|
|
8095
8126
|
this.setCurrentValue(this.egressSubject, call.egress);
|
|
@@ -8738,7 +8769,7 @@ const enableHighQualityAudio = (sdp, trackMid, maxBitrate = 510000) => {
|
|
|
8738
8769
|
return SDP__namespace.write(parsedSdp);
|
|
8739
8770
|
};
|
|
8740
8771
|
|
|
8741
|
-
const version = "1.6.
|
|
8772
|
+
const version = "1.6.2" ;
|
|
8742
8773
|
const [major, minor, patch] = version.split('.');
|
|
8743
8774
|
let sdkInfo = {
|
|
8744
8775
|
type: SdkType.PLAIN_JAVASCRIPT,
|
|
@@ -10028,15 +10059,37 @@ const watchCallRejected = (call) => {
|
|
|
10028
10059
|
* Event handler that watches the delivery of `call.ended` Websocket event.
|
|
10029
10060
|
*/
|
|
10030
10061
|
const watchCallEnded = (call) => {
|
|
10031
|
-
return
|
|
10062
|
+
return function onCallEnded() {
|
|
10032
10063
|
const { callingState } = call.state;
|
|
10033
10064
|
if (callingState === exports.CallingState.RINGING ||
|
|
10034
10065
|
callingState === exports.CallingState.JOINED ||
|
|
10035
10066
|
callingState === exports.CallingState.JOINING) {
|
|
10036
|
-
|
|
10067
|
+
call.leave({ reason: 'call.ended event received' }).catch((err) => {
|
|
10068
|
+
call.logger('error', 'Failed to leave call after call.ended ', err);
|
|
10069
|
+
});
|
|
10037
10070
|
}
|
|
10038
10071
|
};
|
|
10039
10072
|
};
|
|
10073
|
+
/**
|
|
10074
|
+
* Watches for `callEnded` events.
|
|
10075
|
+
*/
|
|
10076
|
+
const watchSfuCallEnded = (call) => {
|
|
10077
|
+
return call.on('callEnded', async (e) => {
|
|
10078
|
+
if (call.state.callingState === exports.CallingState.LEFT)
|
|
10079
|
+
return;
|
|
10080
|
+
try {
|
|
10081
|
+
// `call.ended` event arrived after the call is already left
|
|
10082
|
+
// and all event handlers are detached. We need to manually
|
|
10083
|
+
// update the call state to reflect the call has ended.
|
|
10084
|
+
call.state.setEndedAt(new Date());
|
|
10085
|
+
const reason = CallEndedReason[e.reason];
|
|
10086
|
+
await call.leave({ reason: `callEnded received: ${reason}` });
|
|
10087
|
+
}
|
|
10088
|
+
catch (err) {
|
|
10089
|
+
call.logger('error', 'Failed to leave call after being ended by the SFU', err);
|
|
10090
|
+
}
|
|
10091
|
+
});
|
|
10092
|
+
};
|
|
10040
10093
|
|
|
10041
10094
|
/**
|
|
10042
10095
|
* Event handler that watches for `callGrantsUpdated` events.
|
|
@@ -10064,7 +10117,6 @@ const watchCallGrantsUpdated = (state) => {
|
|
|
10064
10117
|
};
|
|
10065
10118
|
};
|
|
10066
10119
|
|
|
10067
|
-
const logger$1 = getLogger(['events']);
|
|
10068
10120
|
/**
|
|
10069
10121
|
* An event responder which handles the `changePublishQuality` event.
|
|
10070
10122
|
*/
|
|
@@ -10110,7 +10162,7 @@ const watchLiveEnded = (dispatcher, call) => {
|
|
|
10110
10162
|
return;
|
|
10111
10163
|
if (!call.permissionsContext.hasPermission(OwnCapability.JOIN_BACKSTAGE)) {
|
|
10112
10164
|
call.leave({ reason: 'live ended' }).catch((err) => {
|
|
10113
|
-
logger
|
|
10165
|
+
call.logger('error', 'Failed to leave call after live ended', err);
|
|
10114
10166
|
});
|
|
10115
10167
|
}
|
|
10116
10168
|
});
|
|
@@ -10122,8 +10174,9 @@ const watchSfuErrorReports = (dispatcher) => {
|
|
|
10122
10174
|
return dispatcher.on('error', (e) => {
|
|
10123
10175
|
if (!e.error)
|
|
10124
10176
|
return;
|
|
10177
|
+
const logger = getLogger(['SfuClient']);
|
|
10125
10178
|
const { error, reconnectStrategy } = e;
|
|
10126
|
-
logger
|
|
10179
|
+
logger('error', 'SFU reported error', {
|
|
10127
10180
|
code: ErrorCode[error.code],
|
|
10128
10181
|
reconnectStrategy: WebsocketReconnectStrategy[reconnectStrategy],
|
|
10129
10182
|
message: error.message,
|
|
@@ -10141,17 +10194,6 @@ const watchPinsUpdated = (state) => {
|
|
|
10141
10194
|
state.setServerSidePins(pins);
|
|
10142
10195
|
};
|
|
10143
10196
|
};
|
|
10144
|
-
/**
|
|
10145
|
-
* Watches for `callEnded` events.
|
|
10146
|
-
*/
|
|
10147
|
-
const watchSfuCallEnded = (call) => {
|
|
10148
|
-
return call.on('callEnded', (e) => {
|
|
10149
|
-
const reason = CallEndedReason[e.reason];
|
|
10150
|
-
call.leave({ reason }).catch((err) => {
|
|
10151
|
-
logger$1('error', 'Failed to leave call after call ended by the SFU', err);
|
|
10152
|
-
});
|
|
10153
|
-
});
|
|
10154
|
-
};
|
|
10155
10197
|
|
|
10156
10198
|
/**
|
|
10157
10199
|
* An event handler that handles soft mutes.
|
|
@@ -16069,7 +16111,7 @@ class StreamClient {
|
|
|
16069
16111
|
});
|
|
16070
16112
|
};
|
|
16071
16113
|
this.getUserAgent = () => {
|
|
16072
|
-
const version = "1.6.
|
|
16114
|
+
const version = "1.6.2" ;
|
|
16073
16115
|
return (this.userAgent ||
|
|
16074
16116
|
`stream-video-javascript-client-${this.node ? 'node' : 'browser'}-${version}`);
|
|
16075
16117
|
};
|