@stream-io/video-client 1.38.2 → 1.39.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 +25 -0
- package/dist/index.browser.es.js +93 -20
- package/dist/index.browser.es.js.map +1 -1
- package/dist/index.cjs.js +93 -20
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +93 -20
- package/dist/index.es.js.map +1 -1
- package/dist/src/StreamVideoClient.d.ts +5 -0
- package/dist/src/rtc/helpers/sdp.d.ts +1 -0
- package/dist/src/types.d.ts +8 -0
- package/package.json +8 -3
- package/src/StreamVideoClient.ts +40 -6
- package/src/__tests__/StreamVideoClient.test.ts +106 -0
- package/src/devices/devices.ts +3 -1
- package/src/rpc/__tests__/createClient.test.ts +41 -0
- package/src/rpc/createClient.ts +10 -13
- package/src/rtc/Publisher.ts +23 -4
- package/src/rtc/helpers/__tests__/sdp.startBitrate.test.ts +112 -0
- package/src/rtc/helpers/sdp.ts +40 -0
- package/src/types.ts +8 -0
package/dist/index.cjs.js
CHANGED
|
@@ -3750,22 +3750,23 @@ const withRequestLogger = (logger, level) => {
|
|
|
3750
3750
|
};
|
|
3751
3751
|
};
|
|
3752
3752
|
const withRequestTracer = (trace) => {
|
|
3753
|
+
const exclusions = new Set(['SendStats']);
|
|
3754
|
+
const responseInclusions = new Set(['SetPublisher']);
|
|
3753
3755
|
const traceError = (name, input, err) => trace(`${name}OnFailure`, [err, input]);
|
|
3754
|
-
const exclusions = {
|
|
3755
|
-
SendStats: true,
|
|
3756
|
-
};
|
|
3757
3756
|
return {
|
|
3758
3757
|
interceptUnary(next, method, input, options) {
|
|
3759
|
-
|
|
3758
|
+
const name = method.name;
|
|
3759
|
+
if (exclusions.has(name))
|
|
3760
3760
|
return next(method, input, options);
|
|
3761
|
-
|
|
3762
|
-
trace(method.name, input);
|
|
3761
|
+
trace(name, input);
|
|
3763
3762
|
const unaryCall = next(method, input, options);
|
|
3764
3763
|
unaryCall.then((invocation) => {
|
|
3765
|
-
const
|
|
3766
|
-
if (
|
|
3767
|
-
traceError(
|
|
3768
|
-
|
|
3764
|
+
const response = invocation.response;
|
|
3765
|
+
if (response.error)
|
|
3766
|
+
traceError(name, input, response.error);
|
|
3767
|
+
if (responseInclusions.has(name))
|
|
3768
|
+
trace(`${name}Response`, response);
|
|
3769
|
+
}, (error) => traceError(name, input, error));
|
|
3769
3770
|
return unaryCall;
|
|
3770
3771
|
},
|
|
3771
3772
|
};
|
|
@@ -3967,6 +3968,37 @@ const extractMid = (transceiver, transceiverInitIndex, sdp) => {
|
|
|
3967
3968
|
return '';
|
|
3968
3969
|
return String(transceiverInitIndex);
|
|
3969
3970
|
};
|
|
3971
|
+
/*
|
|
3972
|
+
* Sets the start bitrate for the VP9 and H264 codecs in the SDP.
|
|
3973
|
+
*
|
|
3974
|
+
* @param offerSdp the offer SDP to modify.
|
|
3975
|
+
* @param startBitrate the start bitrate in kbps to set. Default is 1000 kbps.
|
|
3976
|
+
*/
|
|
3977
|
+
const setStartBitrate = (offerSdp, maxBitrateKbps, startBitrateFactor, targetMid) => {
|
|
3978
|
+
// start bitrate should be between 300kbps and max-bitrate-kbps
|
|
3979
|
+
const startBitrate = Math.max(Math.min(maxBitrateKbps, startBitrateFactor * maxBitrateKbps), 300);
|
|
3980
|
+
const parsedSdp = sdpTransform.parse(offerSdp);
|
|
3981
|
+
const targetCodecs = new Set(['av1', 'vp9', 'h264']);
|
|
3982
|
+
for (const media of parsedSdp.media) {
|
|
3983
|
+
if (media.type !== 'video')
|
|
3984
|
+
continue;
|
|
3985
|
+
if (String(media.mid) !== targetMid)
|
|
3986
|
+
continue;
|
|
3987
|
+
for (const rtp of media.rtp) {
|
|
3988
|
+
if (!targetCodecs.has(rtp.codec.toLowerCase()))
|
|
3989
|
+
continue;
|
|
3990
|
+
for (const fmtp of media.fmtp) {
|
|
3991
|
+
if (fmtp.payload === rtp.payload) {
|
|
3992
|
+
if (!fmtp.config.includes('x-google-start-bitrate')) {
|
|
3993
|
+
fmtp.config += `;x-google-start-bitrate=${startBitrate}`;
|
|
3994
|
+
}
|
|
3995
|
+
break;
|
|
3996
|
+
}
|
|
3997
|
+
}
|
|
3998
|
+
}
|
|
3999
|
+
}
|
|
4000
|
+
return sdpTransform.write(parsedSdp);
|
|
4001
|
+
};
|
|
3970
4002
|
/**
|
|
3971
4003
|
* Enables stereo in the answer SDP based on the offered stereo in the offer SDP.
|
|
3972
4004
|
*
|
|
@@ -6013,7 +6045,7 @@ const getSdkVersion = (sdk) => {
|
|
|
6013
6045
|
return sdk ? `${sdk.major}.${sdk.minor}.${sdk.patch}` : '0.0.0-development';
|
|
6014
6046
|
};
|
|
6015
6047
|
|
|
6016
|
-
const version = "1.
|
|
6048
|
+
const version = "1.39.2";
|
|
6017
6049
|
const [major, minor, patch] = version.split('.');
|
|
6018
6050
|
let sdkInfo = {
|
|
6019
6051
|
type: SdkType.PLAIN_JAVASCRIPT,
|
|
@@ -7841,10 +7873,25 @@ class Publisher extends BasePeerConnection {
|
|
|
7841
7873
|
this.isIceRestarting = options?.iceRestart ?? false;
|
|
7842
7874
|
await this.pc.setLocalDescription(offer);
|
|
7843
7875
|
const { sdp: baseSdp = '' } = offer;
|
|
7844
|
-
const { dangerouslyForceCodec, fmtpLine } = this.clientPublishOptions || {};
|
|
7845
|
-
|
|
7876
|
+
const { dangerouslyForceCodec, dangerouslySetStartBitrateFactor, fmtpLine, } = this.clientPublishOptions || {};
|
|
7877
|
+
let sdp = dangerouslyForceCodec
|
|
7846
7878
|
? removeCodecsExcept(baseSdp, dangerouslyForceCodec, fmtpLine)
|
|
7847
7879
|
: baseSdp;
|
|
7880
|
+
if (dangerouslySetStartBitrateFactor) {
|
|
7881
|
+
this.transceiverCache.items().forEach((t) => {
|
|
7882
|
+
if (t.publishOption.trackType !== TrackType.VIDEO)
|
|
7883
|
+
return;
|
|
7884
|
+
const maxBitrateBps = t.publishOption.bitrate;
|
|
7885
|
+
const trackId = t.transceiver.sender.track?.id;
|
|
7886
|
+
if (!trackId)
|
|
7887
|
+
return;
|
|
7888
|
+
const mid = tracks.find((x) => x.trackId === trackId)?.mid;
|
|
7889
|
+
if (!mid)
|
|
7890
|
+
return;
|
|
7891
|
+
sdp = setStartBitrate(sdp, maxBitrateBps / 1000, // convert to kbps
|
|
7892
|
+
dangerouslySetStartBitrateFactor, mid);
|
|
7893
|
+
});
|
|
7894
|
+
}
|
|
7848
7895
|
const { response } = await this.sfuClient.setPublisher({ sdp, tracks });
|
|
7849
7896
|
if (response.error)
|
|
7850
7897
|
throw new NegotiationError(response.error);
|
|
@@ -9950,6 +9997,9 @@ const getDevices = (permission, kind, tracer) => {
|
|
|
9950
9997
|
const checkIfAudioOutputChangeSupported = () => {
|
|
9951
9998
|
if (typeof document === 'undefined')
|
|
9952
9999
|
return false;
|
|
10000
|
+
// Safari uses WebAudio API for playing audio, so we check the AudioContext prototype
|
|
10001
|
+
if (isSafari())
|
|
10002
|
+
return 'setSinkId' in AudioContext.prototype;
|
|
9953
10003
|
const element = document.createElement('audio');
|
|
9954
10004
|
return 'setSinkId' in element;
|
|
9955
10005
|
};
|
|
@@ -15010,7 +15060,7 @@ class StreamClient {
|
|
|
15010
15060
|
this.getUserAgent = () => {
|
|
15011
15061
|
if (!this.cachedUserAgent) {
|
|
15012
15062
|
const { clientAppIdentifier = {} } = this.options;
|
|
15013
|
-
const { sdkName = 'js', sdkVersion = "1.
|
|
15063
|
+
const { sdkName = 'js', sdkVersion = "1.39.2", ...extras } = clientAppIdentifier;
|
|
15014
15064
|
this.cachedUserAgent = [
|
|
15015
15065
|
`stream-video-${sdkName}-v${sdkVersion}`,
|
|
15016
15066
|
...Object.entries(extras).map(([key, value]) => `${key}=${value}`),
|
|
@@ -15214,12 +15264,7 @@ class StreamVideoClient {
|
|
|
15214
15264
|
.map((call) => call.cid);
|
|
15215
15265
|
if (callsToReWatch.length <= 0)
|
|
15216
15266
|
return;
|
|
15217
|
-
this.
|
|
15218
|
-
this.queryCalls({
|
|
15219
|
-
watch: true,
|
|
15220
|
-
filter_conditions: { cid: { $in: callsToReWatch } },
|
|
15221
|
-
sort: [{ field: 'cid', direction: 1 }],
|
|
15222
|
-
}).catch((err) => {
|
|
15267
|
+
this.rewatchCalls(callsToReWatch).catch((err) => {
|
|
15223
15268
|
this.logger.error('Failed to re-watch calls', err);
|
|
15224
15269
|
});
|
|
15225
15270
|
}));
|
|
@@ -15283,6 +15328,34 @@ class StreamVideoClient {
|
|
|
15283
15328
|
this.logger.error(`Failed to init call from event ${e.type}`, err);
|
|
15284
15329
|
}
|
|
15285
15330
|
};
|
|
15331
|
+
/**
|
|
15332
|
+
* Rewatches the given calls with retry logic.
|
|
15333
|
+
* @param callsToReWatch array of call IDs to rewatch
|
|
15334
|
+
*/
|
|
15335
|
+
this.rewatchCalls = async (callsToReWatch) => {
|
|
15336
|
+
this.logger.info(`Rewatching calls ${callsToReWatch.join(', ')}`);
|
|
15337
|
+
const maxRetries = 3;
|
|
15338
|
+
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
15339
|
+
try {
|
|
15340
|
+
this.logger.info(`Rewatching calls ${callsToReWatch.join(', ')} attempt ${attempt + 1}`);
|
|
15341
|
+
await this.queryCalls({
|
|
15342
|
+
watch: true,
|
|
15343
|
+
filter_conditions: { cid: { $in: callsToReWatch } },
|
|
15344
|
+
});
|
|
15345
|
+
return;
|
|
15346
|
+
}
|
|
15347
|
+
catch (err) {
|
|
15348
|
+
if (err instanceof ErrorFromResponse && err.unrecoverable) {
|
|
15349
|
+
throw err;
|
|
15350
|
+
}
|
|
15351
|
+
this.logger.warn(`Failed to re-watch calls (attempt ${attempt + 1}/${maxRetries}), retrying.`, err);
|
|
15352
|
+
if (attempt === maxRetries - 1) {
|
|
15353
|
+
throw err;
|
|
15354
|
+
}
|
|
15355
|
+
}
|
|
15356
|
+
await sleep(retryInterval(attempt));
|
|
15357
|
+
}
|
|
15358
|
+
};
|
|
15286
15359
|
/**
|
|
15287
15360
|
* Connects the given user to the client.
|
|
15288
15361
|
* Only one user can connect at a time, if you want to change users, call `disconnectUser` before connecting a new user.
|