@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/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver).
|
|
4
4
|
|
|
5
|
+
## [1.6.2](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.6.1...@stream-io/video-client-1.6.2) (2024-09-09)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* prioritize h264 baseline profile ([#1482](https://github.com/GetStream/stream-video-js/issues/1482)) ([3ea3c5e](https://github.com/GetStream/stream-video-js/commit/3ea3c5ecf57b50d3f909d59a96811f636b07d8aa))
|
|
11
|
+
|
|
12
|
+
## [1.6.1](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.6.0...@stream-io/video-client-1.6.1) (2024-09-05)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Bug Fixes
|
|
16
|
+
|
|
17
|
+
* update state.endedAt after the SFU terminates the call ([#1477](https://github.com/GetStream/stream-video-js/issues/1477)) ([135b11f](https://github.com/GetStream/stream-video-js/commit/135b11f2e29f486f2f43b9ac2a84848d0fd0b5b4))
|
|
18
|
+
|
|
5
19
|
## [1.6.0](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.5.2...@stream-io/video-client-1.6.0) (2024-09-03)
|
|
6
20
|
|
|
7
21
|
|
package/dist/index.browser.es.js
CHANGED
|
@@ -6735,7 +6735,7 @@ const logLevels = Object.freeze({
|
|
|
6735
6735
|
warn: 3,
|
|
6736
6736
|
error: 4,
|
|
6737
6737
|
});
|
|
6738
|
-
let logger$
|
|
6738
|
+
let logger$1;
|
|
6739
6739
|
let level = 'info';
|
|
6740
6740
|
const logToConsole = (logLevel, message, ...args) => {
|
|
6741
6741
|
let logMethod;
|
|
@@ -6769,7 +6769,7 @@ const logToConsole = (logLevel, message, ...args) => {
|
|
|
6769
6769
|
logMethod(message, ...args);
|
|
6770
6770
|
};
|
|
6771
6771
|
const setLogger = (l, lvl) => {
|
|
6772
|
-
logger$
|
|
6772
|
+
logger$1 = l;
|
|
6773
6773
|
if (lvl) {
|
|
6774
6774
|
setLogLevel(lvl);
|
|
6775
6775
|
}
|
|
@@ -6779,7 +6779,7 @@ const setLogLevel = (l) => {
|
|
|
6779
6779
|
};
|
|
6780
6780
|
const getLogLevel = () => level;
|
|
6781
6781
|
const getLogger = (withTags) => {
|
|
6782
|
-
const loggerMethod = logger$
|
|
6782
|
+
const loggerMethod = logger$1 || logToConsole;
|
|
6783
6783
|
const tags = (withTags || []).filter(Boolean).join(':');
|
|
6784
6784
|
const result = (logLevel, message, ...args) => {
|
|
6785
6785
|
if (logLevels[logLevel] >= logLevels[level]) {
|
|
@@ -6820,50 +6820,74 @@ const retryable = async (rpc, signal) => {
|
|
|
6820
6820
|
return result;
|
|
6821
6821
|
};
|
|
6822
6822
|
|
|
6823
|
+
/**
|
|
6824
|
+
* Returns back a list of sorted codecs, with the preferred codec first.
|
|
6825
|
+
*
|
|
6826
|
+
* @param kind the kind of codec to get.
|
|
6827
|
+
* @param preferredCodec the codec to prioritize (vp8, h264, vp9, av1...).
|
|
6828
|
+
* @param codecToRemove the codec to exclude from the list.
|
|
6829
|
+
*/
|
|
6823
6830
|
const getPreferredCodecs = (kind, preferredCodec, codecToRemove) => {
|
|
6824
|
-
|
|
6825
|
-
if (!('getCapabilities' in RTCRtpReceiver)) {
|
|
6826
|
-
logger('warn', 'RTCRtpReceiver.getCapabilities is not supported');
|
|
6831
|
+
if (!('getCapabilities' in RTCRtpReceiver))
|
|
6827
6832
|
return;
|
|
6828
|
-
|
|
6829
|
-
|
|
6830
|
-
if (!cap)
|
|
6833
|
+
const capabilities = RTCRtpReceiver.getCapabilities(kind);
|
|
6834
|
+
if (!capabilities)
|
|
6831
6835
|
return;
|
|
6832
|
-
const
|
|
6833
|
-
const
|
|
6834
|
-
const
|
|
6835
|
-
|
|
6836
|
-
|
|
6837
|
-
|
|
6838
|
-
const
|
|
6836
|
+
const preferred = [];
|
|
6837
|
+
const partiallyPreferred = [];
|
|
6838
|
+
const unpreferred = [];
|
|
6839
|
+
const preferredCodecMimeType = `${kind}/${preferredCodec.toLowerCase()}`;
|
|
6840
|
+
const codecToRemoveMimeType = codecToRemove && `${kind}/${codecToRemove.toLowerCase()}`;
|
|
6841
|
+
for (const codec of capabilities.codecs) {
|
|
6842
|
+
const codecMimeType = codec.mimeType.toLowerCase();
|
|
6843
|
+
const shouldRemoveCodec = codecMimeType === codecToRemoveMimeType;
|
|
6839
6844
|
if (shouldRemoveCodec)
|
|
6840
|
-
|
|
6841
|
-
const
|
|
6842
|
-
if (!
|
|
6843
|
-
|
|
6844
|
-
|
|
6845
|
+
continue; // skip this codec
|
|
6846
|
+
const isPreferredCodec = codecMimeType === preferredCodecMimeType;
|
|
6847
|
+
if (!isPreferredCodec) {
|
|
6848
|
+
unpreferred.push(codec);
|
|
6849
|
+
continue;
|
|
6845
6850
|
}
|
|
6846
|
-
//
|
|
6847
|
-
// profile-level-id is 42e01f
|
|
6848
|
-
|
|
6849
|
-
|
|
6850
|
-
|
|
6851
|
-
|
|
6852
|
-
|
|
6853
|
-
partialMatched.push(c);
|
|
6854
|
-
}
|
|
6855
|
-
return;
|
|
6851
|
+
// h264 is a special case, we want to prioritize the baseline codec with
|
|
6852
|
+
// profile-level-id is 42e01f and packetization-mode=0 for maximum
|
|
6853
|
+
// cross-browser compatibility.
|
|
6854
|
+
// this branch covers the other cases, such as vp8.
|
|
6855
|
+
if (codecMimeType !== 'video/h264') {
|
|
6856
|
+
preferred.push(codec);
|
|
6857
|
+
continue;
|
|
6856
6858
|
}
|
|
6857
|
-
|
|
6858
|
-
|
|
6859
|
-
|
|
6859
|
+
const sdpFmtpLine = codec.sdpFmtpLine;
|
|
6860
|
+
if (!sdpFmtpLine || !sdpFmtpLine.includes('profile-level-id=42e01f')) {
|
|
6861
|
+
// this is not the baseline h264 codec, prioritize it lower
|
|
6862
|
+
partiallyPreferred.push(codec);
|
|
6863
|
+
continue;
|
|
6864
|
+
}
|
|
6865
|
+
// packetization-mode mode is optional; when not present it defaults to 0:
|
|
6866
|
+
// https://datatracker.ietf.org/doc/html/rfc6184#section-6.2
|
|
6867
|
+
if (sdpFmtpLine.includes('packetization-mode=0') ||
|
|
6868
|
+
!sdpFmtpLine.includes('packetization-mode')) {
|
|
6869
|
+
preferred.unshift(codec);
|
|
6870
|
+
}
|
|
6871
|
+
else {
|
|
6872
|
+
preferred.push(codec);
|
|
6873
|
+
}
|
|
6874
|
+
}
|
|
6875
|
+
// return a sorted list of codecs, with the preferred codecs first
|
|
6876
|
+
return [...preferred, ...partiallyPreferred, ...unpreferred];
|
|
6860
6877
|
};
|
|
6878
|
+
/**
|
|
6879
|
+
* Returns a generic SDP for the given direction.
|
|
6880
|
+
* We use this SDP to send it as part of our JoinRequest so that the SFU
|
|
6881
|
+
* can use it to determine client's codec capabilities.
|
|
6882
|
+
*
|
|
6883
|
+
* @param direction the direction of the transceiver.
|
|
6884
|
+
*/
|
|
6861
6885
|
const getGenericSdp = async (direction) => {
|
|
6862
6886
|
const tempPc = new RTCPeerConnection();
|
|
6863
6887
|
tempPc.addTransceiver('video', { direction });
|
|
6864
6888
|
tempPc.addTransceiver('audio', { direction });
|
|
6865
6889
|
const offer = await tempPc.createOffer();
|
|
6866
|
-
|
|
6890
|
+
const sdp = offer.sdp ?? '';
|
|
6867
6891
|
tempPc.getTransceivers().forEach((t) => {
|
|
6868
6892
|
t.stop?.();
|
|
6869
6893
|
});
|
|
@@ -7882,6 +7906,13 @@ class CallState {
|
|
|
7882
7906
|
this.setOwnCapabilities = (capabilities) => {
|
|
7883
7907
|
return this.setCurrentValue(this.ownCapabilitiesSubject, capabilities);
|
|
7884
7908
|
};
|
|
7909
|
+
/**
|
|
7910
|
+
* Sets the time when this call has been ended.
|
|
7911
|
+
* @param endedAt the time when this call has been ended.
|
|
7912
|
+
*/
|
|
7913
|
+
this.setEndedAt = (endedAt) => {
|
|
7914
|
+
return this.setCurrentValue(this.endedAtSubject, endedAt);
|
|
7915
|
+
};
|
|
7885
7916
|
/**
|
|
7886
7917
|
* Will try to find the participant with the given sessionId in the current call.
|
|
7887
7918
|
*
|
|
@@ -8068,7 +8099,7 @@ class CallState {
|
|
|
8068
8099
|
this.setCurrentValue(this.createdAtSubject, new Date(call.created_at));
|
|
8069
8100
|
this.setCurrentValue(this.updatedAtSubject, new Date(call.updated_at));
|
|
8070
8101
|
this.setCurrentValue(this.startsAtSubject, call.starts_at ? new Date(call.starts_at) : undefined);
|
|
8071
|
-
this.
|
|
8102
|
+
this.setEndedAt(call.ended_at ? new Date(call.ended_at) : undefined);
|
|
8072
8103
|
this.setCurrentValue(this.createdBySubject, call.created_by);
|
|
8073
8104
|
this.setCurrentValue(this.customSubject, call.custom);
|
|
8074
8105
|
this.setCurrentValue(this.egressSubject, call.egress);
|
|
@@ -8717,7 +8748,7 @@ const enableHighQualityAudio = (sdp, trackMid, maxBitrate = 510000) => {
|
|
|
8717
8748
|
return SDP.write(parsedSdp);
|
|
8718
8749
|
};
|
|
8719
8750
|
|
|
8720
|
-
const version = "1.6.
|
|
8751
|
+
const version = "1.6.2" ;
|
|
8721
8752
|
const [major, minor, patch] = version.split('.');
|
|
8722
8753
|
let sdkInfo = {
|
|
8723
8754
|
type: SdkType.PLAIN_JAVASCRIPT,
|
|
@@ -10007,15 +10038,37 @@ const watchCallRejected = (call) => {
|
|
|
10007
10038
|
* Event handler that watches the delivery of `call.ended` Websocket event.
|
|
10008
10039
|
*/
|
|
10009
10040
|
const watchCallEnded = (call) => {
|
|
10010
|
-
return
|
|
10041
|
+
return function onCallEnded() {
|
|
10011
10042
|
const { callingState } = call.state;
|
|
10012
10043
|
if (callingState === CallingState.RINGING ||
|
|
10013
10044
|
callingState === CallingState.JOINED ||
|
|
10014
10045
|
callingState === CallingState.JOINING) {
|
|
10015
|
-
|
|
10046
|
+
call.leave({ reason: 'call.ended event received' }).catch((err) => {
|
|
10047
|
+
call.logger('error', 'Failed to leave call after call.ended ', err);
|
|
10048
|
+
});
|
|
10016
10049
|
}
|
|
10017
10050
|
};
|
|
10018
10051
|
};
|
|
10052
|
+
/**
|
|
10053
|
+
* Watches for `callEnded` events.
|
|
10054
|
+
*/
|
|
10055
|
+
const watchSfuCallEnded = (call) => {
|
|
10056
|
+
return call.on('callEnded', async (e) => {
|
|
10057
|
+
if (call.state.callingState === CallingState.LEFT)
|
|
10058
|
+
return;
|
|
10059
|
+
try {
|
|
10060
|
+
// `call.ended` event arrived after the call is already left
|
|
10061
|
+
// and all event handlers are detached. We need to manually
|
|
10062
|
+
// update the call state to reflect the call has ended.
|
|
10063
|
+
call.state.setEndedAt(new Date());
|
|
10064
|
+
const reason = CallEndedReason[e.reason];
|
|
10065
|
+
await call.leave({ reason: `callEnded received: ${reason}` });
|
|
10066
|
+
}
|
|
10067
|
+
catch (err) {
|
|
10068
|
+
call.logger('error', 'Failed to leave call after being ended by the SFU', err);
|
|
10069
|
+
}
|
|
10070
|
+
});
|
|
10071
|
+
};
|
|
10019
10072
|
|
|
10020
10073
|
/**
|
|
10021
10074
|
* Event handler that watches for `callGrantsUpdated` events.
|
|
@@ -10043,7 +10096,6 @@ const watchCallGrantsUpdated = (state) => {
|
|
|
10043
10096
|
};
|
|
10044
10097
|
};
|
|
10045
10098
|
|
|
10046
|
-
const logger$1 = getLogger(['events']);
|
|
10047
10099
|
/**
|
|
10048
10100
|
* An event responder which handles the `changePublishQuality` event.
|
|
10049
10101
|
*/
|
|
@@ -10089,7 +10141,7 @@ const watchLiveEnded = (dispatcher, call) => {
|
|
|
10089
10141
|
return;
|
|
10090
10142
|
if (!call.permissionsContext.hasPermission(OwnCapability.JOIN_BACKSTAGE)) {
|
|
10091
10143
|
call.leave({ reason: 'live ended' }).catch((err) => {
|
|
10092
|
-
logger
|
|
10144
|
+
call.logger('error', 'Failed to leave call after live ended', err);
|
|
10093
10145
|
});
|
|
10094
10146
|
}
|
|
10095
10147
|
});
|
|
@@ -10101,8 +10153,9 @@ const watchSfuErrorReports = (dispatcher) => {
|
|
|
10101
10153
|
return dispatcher.on('error', (e) => {
|
|
10102
10154
|
if (!e.error)
|
|
10103
10155
|
return;
|
|
10156
|
+
const logger = getLogger(['SfuClient']);
|
|
10104
10157
|
const { error, reconnectStrategy } = e;
|
|
10105
|
-
logger
|
|
10158
|
+
logger('error', 'SFU reported error', {
|
|
10106
10159
|
code: ErrorCode[error.code],
|
|
10107
10160
|
reconnectStrategy: WebsocketReconnectStrategy[reconnectStrategy],
|
|
10108
10161
|
message: error.message,
|
|
@@ -10120,17 +10173,6 @@ const watchPinsUpdated = (state) => {
|
|
|
10120
10173
|
state.setServerSidePins(pins);
|
|
10121
10174
|
};
|
|
10122
10175
|
};
|
|
10123
|
-
/**
|
|
10124
|
-
* Watches for `callEnded` events.
|
|
10125
|
-
*/
|
|
10126
|
-
const watchSfuCallEnded = (call) => {
|
|
10127
|
-
return call.on('callEnded', (e) => {
|
|
10128
|
-
const reason = CallEndedReason[e.reason];
|
|
10129
|
-
call.leave({ reason }).catch((err) => {
|
|
10130
|
-
logger$1('error', 'Failed to leave call after call ended by the SFU', err);
|
|
10131
|
-
});
|
|
10132
|
-
});
|
|
10133
|
-
};
|
|
10134
10176
|
|
|
10135
10177
|
/**
|
|
10136
10178
|
* An event handler that handles soft mutes.
|
|
@@ -16050,7 +16092,7 @@ class StreamClient {
|
|
|
16050
16092
|
});
|
|
16051
16093
|
};
|
|
16052
16094
|
this.getUserAgent = () => {
|
|
16053
|
-
const version = "1.6.
|
|
16095
|
+
const version = "1.6.2" ;
|
|
16054
16096
|
return (this.userAgent ||
|
|
16055
16097
|
`stream-video-javascript-client-${this.node ? 'node' : 'browser'}-${version}`);
|
|
16056
16098
|
};
|