@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/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver).
|
|
4
4
|
|
|
5
|
+
## [1.39.2](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.39.1...@stream-io/video-client-1.39.2) (2025-12-23)
|
|
6
|
+
|
|
7
|
+
- upgrade stream dependencies ([#2065](https://github.com/GetStream/stream-video-js/issues/2065)) ([04ca858](https://github.com/GetStream/stream-video-js/commit/04ca858517072f861c1ddae0876f0b425ca658e2))
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
- **safari:** verify that AudioContext supports `setSinkId` ([#2069](https://github.com/GetStream/stream-video-js/issues/2069)) ([e7fbe10](https://github.com/GetStream/stream-video-js/commit/e7fbe10d06acce52a2e3f4f7d008882fa23e9c89))
|
|
12
|
+
- slow rampup on vp9/h264 codec ([#2056](https://github.com/GetStream/stream-video-js/issues/2056)) ([b5ad360](https://github.com/GetStream/stream-video-js/commit/b5ad360eab83a139198d05b4f42b777315135ab6))
|
|
13
|
+
|
|
14
|
+
## [1.39.1](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.39.0...@stream-io/video-client-1.39.1) (2025-12-18)
|
|
15
|
+
|
|
16
|
+
### Bug Fixes
|
|
17
|
+
|
|
18
|
+
- **provenance:** add repository info to every package ([4159633](https://github.com/GetStream/stream-video-js/commit/4159633b908afe6542b4be53151da6218175426c))
|
|
19
|
+
|
|
20
|
+
## [1.39.0](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.38.2...@stream-io/video-client-1.39.0) (2025-12-18)
|
|
21
|
+
|
|
22
|
+
### Features
|
|
23
|
+
|
|
24
|
+
- **react:** Retryable call watching ([#2046](https://github.com/GetStream/stream-video-js/issues/2046)) ([7205011](https://github.com/GetStream/stream-video-js/commit/7205011a451995585848b89388c91ae9a1b0bc64))
|
|
25
|
+
|
|
26
|
+
### Bug Fixes
|
|
27
|
+
|
|
28
|
+
- add response tracing for the SetPublisher RPC ([#2055](https://github.com/GetStream/stream-video-js/issues/2055)) ([a25d9a8](https://github.com/GetStream/stream-video-js/commit/a25d9a89870db47be046f31c85888995e43d44cd))
|
|
29
|
+
|
|
5
30
|
## [1.38.2](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.38.1...@stream-io/video-client-1.38.2) (2025-12-11)
|
|
6
31
|
|
|
7
32
|
### Bug Fixes
|
package/dist/index.browser.es.js
CHANGED
|
@@ -3730,22 +3730,23 @@ const withRequestLogger = (logger, level) => {
|
|
|
3730
3730
|
};
|
|
3731
3731
|
};
|
|
3732
3732
|
const withRequestTracer = (trace) => {
|
|
3733
|
+
const exclusions = new Set(['SendStats']);
|
|
3734
|
+
const responseInclusions = new Set(['SetPublisher']);
|
|
3733
3735
|
const traceError = (name, input, err) => trace(`${name}OnFailure`, [err, input]);
|
|
3734
|
-
const exclusions = {
|
|
3735
|
-
SendStats: true,
|
|
3736
|
-
};
|
|
3737
3736
|
return {
|
|
3738
3737
|
interceptUnary(next, method, input, options) {
|
|
3739
|
-
|
|
3738
|
+
const name = method.name;
|
|
3739
|
+
if (exclusions.has(name))
|
|
3740
3740
|
return next(method, input, options);
|
|
3741
|
-
|
|
3742
|
-
trace(method.name, input);
|
|
3741
|
+
trace(name, input);
|
|
3743
3742
|
const unaryCall = next(method, input, options);
|
|
3744
3743
|
unaryCall.then((invocation) => {
|
|
3745
|
-
const
|
|
3746
|
-
if (
|
|
3747
|
-
traceError(
|
|
3748
|
-
|
|
3744
|
+
const response = invocation.response;
|
|
3745
|
+
if (response.error)
|
|
3746
|
+
traceError(name, input, response.error);
|
|
3747
|
+
if (responseInclusions.has(name))
|
|
3748
|
+
trace(`${name}Response`, response);
|
|
3749
|
+
}, (error) => traceError(name, input, error));
|
|
3749
3750
|
return unaryCall;
|
|
3750
3751
|
},
|
|
3751
3752
|
};
|
|
@@ -3947,6 +3948,37 @@ const extractMid = (transceiver, transceiverInitIndex, sdp) => {
|
|
|
3947
3948
|
return '';
|
|
3948
3949
|
return String(transceiverInitIndex);
|
|
3949
3950
|
};
|
|
3951
|
+
/*
|
|
3952
|
+
* Sets the start bitrate for the VP9 and H264 codecs in the SDP.
|
|
3953
|
+
*
|
|
3954
|
+
* @param offerSdp the offer SDP to modify.
|
|
3955
|
+
* @param startBitrate the start bitrate in kbps to set. Default is 1000 kbps.
|
|
3956
|
+
*/
|
|
3957
|
+
const setStartBitrate = (offerSdp, maxBitrateKbps, startBitrateFactor, targetMid) => {
|
|
3958
|
+
// start bitrate should be between 300kbps and max-bitrate-kbps
|
|
3959
|
+
const startBitrate = Math.max(Math.min(maxBitrateKbps, startBitrateFactor * maxBitrateKbps), 300);
|
|
3960
|
+
const parsedSdp = parse(offerSdp);
|
|
3961
|
+
const targetCodecs = new Set(['av1', 'vp9', 'h264']);
|
|
3962
|
+
for (const media of parsedSdp.media) {
|
|
3963
|
+
if (media.type !== 'video')
|
|
3964
|
+
continue;
|
|
3965
|
+
if (String(media.mid) !== targetMid)
|
|
3966
|
+
continue;
|
|
3967
|
+
for (const rtp of media.rtp) {
|
|
3968
|
+
if (!targetCodecs.has(rtp.codec.toLowerCase()))
|
|
3969
|
+
continue;
|
|
3970
|
+
for (const fmtp of media.fmtp) {
|
|
3971
|
+
if (fmtp.payload === rtp.payload) {
|
|
3972
|
+
if (!fmtp.config.includes('x-google-start-bitrate')) {
|
|
3973
|
+
fmtp.config += `;x-google-start-bitrate=${startBitrate}`;
|
|
3974
|
+
}
|
|
3975
|
+
break;
|
|
3976
|
+
}
|
|
3977
|
+
}
|
|
3978
|
+
}
|
|
3979
|
+
}
|
|
3980
|
+
return write(parsedSdp);
|
|
3981
|
+
};
|
|
3950
3982
|
/**
|
|
3951
3983
|
* Enables stereo in the answer SDP based on the offered stereo in the offer SDP.
|
|
3952
3984
|
*
|
|
@@ -5993,7 +6025,7 @@ const getSdkVersion = (sdk) => {
|
|
|
5993
6025
|
return sdk ? `${sdk.major}.${sdk.minor}.${sdk.patch}` : '0.0.0-development';
|
|
5994
6026
|
};
|
|
5995
6027
|
|
|
5996
|
-
const version = "1.
|
|
6028
|
+
const version = "1.39.2";
|
|
5997
6029
|
const [major, minor, patch] = version.split('.');
|
|
5998
6030
|
let sdkInfo = {
|
|
5999
6031
|
type: SdkType.PLAIN_JAVASCRIPT,
|
|
@@ -7821,10 +7853,25 @@ class Publisher extends BasePeerConnection {
|
|
|
7821
7853
|
this.isIceRestarting = options?.iceRestart ?? false;
|
|
7822
7854
|
await this.pc.setLocalDescription(offer);
|
|
7823
7855
|
const { sdp: baseSdp = '' } = offer;
|
|
7824
|
-
const { dangerouslyForceCodec, fmtpLine } = this.clientPublishOptions || {};
|
|
7825
|
-
|
|
7856
|
+
const { dangerouslyForceCodec, dangerouslySetStartBitrateFactor, fmtpLine, } = this.clientPublishOptions || {};
|
|
7857
|
+
let sdp = dangerouslyForceCodec
|
|
7826
7858
|
? removeCodecsExcept(baseSdp, dangerouslyForceCodec, fmtpLine)
|
|
7827
7859
|
: baseSdp;
|
|
7860
|
+
if (dangerouslySetStartBitrateFactor) {
|
|
7861
|
+
this.transceiverCache.items().forEach((t) => {
|
|
7862
|
+
if (t.publishOption.trackType !== TrackType.VIDEO)
|
|
7863
|
+
return;
|
|
7864
|
+
const maxBitrateBps = t.publishOption.bitrate;
|
|
7865
|
+
const trackId = t.transceiver.sender.track?.id;
|
|
7866
|
+
if (!trackId)
|
|
7867
|
+
return;
|
|
7868
|
+
const mid = tracks.find((x) => x.trackId === trackId)?.mid;
|
|
7869
|
+
if (!mid)
|
|
7870
|
+
return;
|
|
7871
|
+
sdp = setStartBitrate(sdp, maxBitrateBps / 1000, // convert to kbps
|
|
7872
|
+
dangerouslySetStartBitrateFactor, mid);
|
|
7873
|
+
});
|
|
7874
|
+
}
|
|
7828
7875
|
const { response } = await this.sfuClient.setPublisher({ sdp, tracks });
|
|
7829
7876
|
if (response.error)
|
|
7830
7877
|
throw new NegotiationError(response.error);
|
|
@@ -9930,6 +9977,9 @@ const getDevices = (permission, kind, tracer) => {
|
|
|
9930
9977
|
const checkIfAudioOutputChangeSupported = () => {
|
|
9931
9978
|
if (typeof document === 'undefined')
|
|
9932
9979
|
return false;
|
|
9980
|
+
// Safari uses WebAudio API for playing audio, so we check the AudioContext prototype
|
|
9981
|
+
if (isSafari())
|
|
9982
|
+
return 'setSinkId' in AudioContext.prototype;
|
|
9933
9983
|
const element = document.createElement('audio');
|
|
9934
9984
|
return 'setSinkId' in element;
|
|
9935
9985
|
};
|
|
@@ -14992,7 +15042,7 @@ class StreamClient {
|
|
|
14992
15042
|
this.getUserAgent = () => {
|
|
14993
15043
|
if (!this.cachedUserAgent) {
|
|
14994
15044
|
const { clientAppIdentifier = {} } = this.options;
|
|
14995
|
-
const { sdkName = 'js', sdkVersion = "1.
|
|
15045
|
+
const { sdkName = 'js', sdkVersion = "1.39.2", ...extras } = clientAppIdentifier;
|
|
14996
15046
|
this.cachedUserAgent = [
|
|
14997
15047
|
`stream-video-${sdkName}-v${sdkVersion}`,
|
|
14998
15048
|
...Object.entries(extras).map(([key, value]) => `${key}=${value}`),
|
|
@@ -15196,12 +15246,7 @@ class StreamVideoClient {
|
|
|
15196
15246
|
.map((call) => call.cid);
|
|
15197
15247
|
if (callsToReWatch.length <= 0)
|
|
15198
15248
|
return;
|
|
15199
|
-
this.
|
|
15200
|
-
this.queryCalls({
|
|
15201
|
-
watch: true,
|
|
15202
|
-
filter_conditions: { cid: { $in: callsToReWatch } },
|
|
15203
|
-
sort: [{ field: 'cid', direction: 1 }],
|
|
15204
|
-
}).catch((err) => {
|
|
15249
|
+
this.rewatchCalls(callsToReWatch).catch((err) => {
|
|
15205
15250
|
this.logger.error('Failed to re-watch calls', err);
|
|
15206
15251
|
});
|
|
15207
15252
|
}));
|
|
@@ -15265,6 +15310,34 @@ class StreamVideoClient {
|
|
|
15265
15310
|
this.logger.error(`Failed to init call from event ${e.type}`, err);
|
|
15266
15311
|
}
|
|
15267
15312
|
};
|
|
15313
|
+
/**
|
|
15314
|
+
* Rewatches the given calls with retry logic.
|
|
15315
|
+
* @param callsToReWatch array of call IDs to rewatch
|
|
15316
|
+
*/
|
|
15317
|
+
this.rewatchCalls = async (callsToReWatch) => {
|
|
15318
|
+
this.logger.info(`Rewatching calls ${callsToReWatch.join(', ')}`);
|
|
15319
|
+
const maxRetries = 3;
|
|
15320
|
+
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
15321
|
+
try {
|
|
15322
|
+
this.logger.info(`Rewatching calls ${callsToReWatch.join(', ')} attempt ${attempt + 1}`);
|
|
15323
|
+
await this.queryCalls({
|
|
15324
|
+
watch: true,
|
|
15325
|
+
filter_conditions: { cid: { $in: callsToReWatch } },
|
|
15326
|
+
});
|
|
15327
|
+
return;
|
|
15328
|
+
}
|
|
15329
|
+
catch (err) {
|
|
15330
|
+
if (err instanceof ErrorFromResponse && err.unrecoverable) {
|
|
15331
|
+
throw err;
|
|
15332
|
+
}
|
|
15333
|
+
this.logger.warn(`Failed to re-watch calls (attempt ${attempt + 1}/${maxRetries}), retrying.`, err);
|
|
15334
|
+
if (attempt === maxRetries - 1) {
|
|
15335
|
+
throw err;
|
|
15336
|
+
}
|
|
15337
|
+
}
|
|
15338
|
+
await sleep(retryInterval(attempt));
|
|
15339
|
+
}
|
|
15340
|
+
};
|
|
15268
15341
|
/**
|
|
15269
15342
|
* Connects the given user to the client.
|
|
15270
15343
|
* Only one user can connect at a time, if you want to change users, call `disconnectUser` before connecting a new user.
|