@stream-io/video-client 1.6.3 → 1.6.4

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 CHANGED
@@ -2,6 +2,13 @@
2
2
 
3
3
  This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver).
4
4
 
5
+ ## [1.6.4](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.6.3...@stream-io/video-client-1.6.4) (2024-09-13)
6
+
7
+
8
+ ### Bug Fixes
9
+
10
+ * allow video target bitrate override ([#1487](https://github.com/GetStream/stream-video-js/issues/1487)) ([bfe34a3](https://github.com/GetStream/stream-video-js/commit/bfe34a3609182da5bbb03331978d86569cada098))
11
+
5
12
  ## [1.6.3](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.6.2...@stream-io/video-client-1.6.3) (2024-09-11)
6
13
 
7
14
 
@@ -4,9 +4,9 @@ import { ServiceType, stackIntercept, RpcError } from '@protobuf-ts/runtime-rpc'
4
4
  import axios, { AxiosHeaders } from 'axios';
5
5
  export { AxiosError } from 'axios';
6
6
  import { TwirpFetchTransport, TwirpErrorCode } from '@protobuf-ts/twirp-transport';
7
+ import { UAParser } from 'ua-parser-js';
7
8
  import { ReplaySubject, combineLatest, BehaviorSubject, map as map$1, shareReplay, distinctUntilChanged, takeWhile, distinctUntilKeyChanged, fromEventPattern, startWith, concatMap, merge, from, fromEvent, debounceTime, pairwise, of, debounce, timer } from 'rxjs';
8
9
  import * as SDP from 'sdp-transform';
9
- import { UAParser } from 'ua-parser-js';
10
10
  import WebSocket$1 from 'isomorphic-ws';
11
11
  import { fromByteArray } from 'base64-js';
12
12
 
@@ -6820,6 +6820,72 @@ const retryable = async (rpc, signal) => {
6820
6820
  return result;
6821
6821
  };
6822
6822
 
6823
+ const version = "1.6.4" ;
6824
+ const [major, minor, patch] = version.split('.');
6825
+ let sdkInfo = {
6826
+ type: SdkType.PLAIN_JAVASCRIPT,
6827
+ major,
6828
+ minor,
6829
+ patch,
6830
+ };
6831
+ let osInfo;
6832
+ let deviceInfo;
6833
+ let webRtcInfo;
6834
+ const setSdkInfo = (info) => {
6835
+ sdkInfo = info;
6836
+ };
6837
+ const getSdkInfo = () => {
6838
+ return sdkInfo;
6839
+ };
6840
+ const setOSInfo = (info) => {
6841
+ osInfo = info;
6842
+ };
6843
+ const getOSInfo = () => {
6844
+ return osInfo;
6845
+ };
6846
+ const setDeviceInfo = (info) => {
6847
+ deviceInfo = info;
6848
+ };
6849
+ const getDeviceInfo = () => {
6850
+ return deviceInfo;
6851
+ };
6852
+ const getWebRTCInfo = () => {
6853
+ return webRtcInfo;
6854
+ };
6855
+ const setWebRTCInfo = (info) => {
6856
+ webRtcInfo = info;
6857
+ };
6858
+ const getClientDetails = () => {
6859
+ if (isReactNative()) {
6860
+ // Since RN doesn't support web, sharing browser info is not required
6861
+ return {
6862
+ sdk: getSdkInfo(),
6863
+ os: getOSInfo(),
6864
+ device: getDeviceInfo(),
6865
+ };
6866
+ }
6867
+ const userAgent = new UAParser(navigator.userAgent);
6868
+ const { browser, os, device, cpu } = userAgent.getResult();
6869
+ return {
6870
+ sdk: getSdkInfo(),
6871
+ browser: {
6872
+ name: browser.name || navigator.userAgent,
6873
+ version: browser.version || '',
6874
+ },
6875
+ os: {
6876
+ name: os.name || '',
6877
+ version: os.version || '',
6878
+ architecture: cpu.architecture || '',
6879
+ },
6880
+ device: {
6881
+ name: [device.vendor, device.model, device.type]
6882
+ .filter(Boolean)
6883
+ .join(' '),
6884
+ version: '',
6885
+ },
6886
+ };
6887
+ };
6888
+
6823
6889
  /**
6824
6890
  * Returns back a list of sorted codecs, with the preferred codec first.
6825
6891
  *
@@ -6894,6 +6960,20 @@ const getGenericSdp = async (direction) => {
6894
6960
  tempPc.close();
6895
6961
  return sdp;
6896
6962
  };
6963
+ /**
6964
+ * Returns the optimal codec for RN.
6965
+ */
6966
+ const getRNOptimalCodec = () => {
6967
+ const osName = getOSInfo()?.name.toLowerCase();
6968
+ // in ipads it was noticed that if vp8 codec is used
6969
+ // then the bytes sent is 0 in the outbound-rtp
6970
+ // so we are forcing h264 codec for ipads
6971
+ if (osName === 'ipados')
6972
+ return 'h264';
6973
+ if (osName === 'android')
6974
+ return 'vp8';
6975
+ return undefined;
6976
+ };
6897
6977
 
6898
6978
  const sfuEventKinds = {
6899
6979
  subscriberOffer: undefined,
@@ -7017,12 +7097,13 @@ const defaultBitratePerRid = {
7017
7097
  *
7018
7098
  * @param videoTrack the video track to find optimal layers for.
7019
7099
  * @param targetResolution the expected target resolution.
7100
+ * @param preferredBitrate the preferred bitrate for the video track.
7020
7101
  */
7021
- const findOptimalVideoLayers = (videoTrack, targetResolution = defaultTargetResolution) => {
7102
+ const findOptimalVideoLayers = (videoTrack, targetResolution = defaultTargetResolution, preferredBitrate) => {
7022
7103
  const optimalVideoLayers = [];
7023
7104
  const settings = videoTrack.getSettings();
7024
7105
  const { width: w = 0, height: h = 0 } = settings;
7025
- const maxBitrate = getComputedMaxBitrate(targetResolution, w, h);
7106
+ const maxBitrate = getComputedMaxBitrate(targetResolution, w, h, preferredBitrate);
7026
7107
  let downscaleFactor = 1;
7027
7108
  ['f', 'h', 'q'].forEach((rid) => {
7028
7109
  // Reversing the order [f, h, q] to [q, h, f] as Chrome uses encoding index
@@ -7053,18 +7134,20 @@ const findOptimalVideoLayers = (videoTrack, targetResolution = defaultTargetReso
7053
7134
  * @param targetResolution the target resolution.
7054
7135
  * @param currentWidth the current width of the track.
7055
7136
  * @param currentHeight the current height of the track.
7137
+ * @param preferredBitrate the preferred bitrate for the track.
7056
7138
  */
7057
- const getComputedMaxBitrate = (targetResolution, currentWidth, currentHeight) => {
7139
+ const getComputedMaxBitrate = (targetResolution, currentWidth, currentHeight, preferredBitrate) => {
7058
7140
  // if the current resolution is lower than the target resolution,
7059
7141
  // we want to proportionally reduce the target bitrate
7060
- const { width: targetWidth, height: targetHeight } = targetResolution;
7142
+ const { width: targetWidth, height: targetHeight, bitrate: targetBitrate, } = targetResolution;
7143
+ const bitrate = preferredBitrate || targetBitrate;
7061
7144
  if (currentWidth < targetWidth || currentHeight < targetHeight) {
7062
7145
  const currentPixels = currentWidth * currentHeight;
7063
7146
  const targetPixels = targetWidth * targetHeight;
7064
7147
  const reductionFactor = currentPixels / targetPixels;
7065
- return Math.round(targetResolution.bitrate * reductionFactor);
7148
+ return Math.round(bitrate * reductionFactor);
7066
7149
  }
7067
- return targetResolution.bitrate;
7150
+ return bitrate;
7068
7151
  };
7069
7152
  /**
7070
7153
  * Browsers have different simulcast constraints for different video resolutions.
@@ -8748,72 +8831,6 @@ const enableHighQualityAudio = (sdp, trackMid, maxBitrate = 510000) => {
8748
8831
  return SDP.write(parsedSdp);
8749
8832
  };
8750
8833
 
8751
- const version = "1.6.3" ;
8752
- const [major, minor, patch] = version.split('.');
8753
- let sdkInfo = {
8754
- type: SdkType.PLAIN_JAVASCRIPT,
8755
- major,
8756
- minor,
8757
- patch,
8758
- };
8759
- let osInfo;
8760
- let deviceInfo;
8761
- let webRtcInfo;
8762
- const setSdkInfo = (info) => {
8763
- sdkInfo = info;
8764
- };
8765
- const getSdkInfo = () => {
8766
- return sdkInfo;
8767
- };
8768
- const setOSInfo = (info) => {
8769
- osInfo = info;
8770
- };
8771
- const getOSInfo = () => {
8772
- return osInfo;
8773
- };
8774
- const setDeviceInfo = (info) => {
8775
- deviceInfo = info;
8776
- };
8777
- const getDeviceInfo = () => {
8778
- return deviceInfo;
8779
- };
8780
- const getWebRTCInfo = () => {
8781
- return webRtcInfo;
8782
- };
8783
- const setWebRTCInfo = (info) => {
8784
- webRtcInfo = info;
8785
- };
8786
- const getClientDetails = () => {
8787
- if (isReactNative()) {
8788
- // Since RN doesn't support web, sharing browser info is not required
8789
- return {
8790
- sdk: getSdkInfo(),
8791
- os: getOSInfo(),
8792
- device: getDeviceInfo(),
8793
- };
8794
- }
8795
- const userAgent = new UAParser(navigator.userAgent);
8796
- const { browser, os, device, cpu } = userAgent.getResult();
8797
- return {
8798
- sdk: getSdkInfo(),
8799
- browser: {
8800
- name: browser.name || navigator.userAgent,
8801
- version: browser.version || '',
8802
- },
8803
- os: {
8804
- name: os.name || '',
8805
- version: os.version || '',
8806
- architecture: cpu.architecture || '',
8807
- },
8808
- device: {
8809
- name: [device.vendor, device.model, device.type]
8810
- .filter(Boolean)
8811
- .join(' '),
8812
- version: '',
8813
- },
8814
- };
8815
- };
8816
-
8817
8834
  /**
8818
8835
  * The `Publisher` is responsible for publishing/unpublishing media streams to/from the SFU
8819
8836
  *
@@ -8942,24 +8959,12 @@ class Publisher {
8942
8959
  const targetResolution = settings?.video
8943
8960
  .target_resolution;
8944
8961
  const screenShareBitrate = settings?.screensharing.target_resolution?.bitrate;
8962
+ const { preferredBitrate, preferredCodec, screenShareSettings } = opts;
8945
8963
  const videoEncodings = trackType === TrackType.VIDEO
8946
- ? findOptimalVideoLayers(track, targetResolution)
8964
+ ? findOptimalVideoLayers(track, targetResolution, preferredBitrate)
8947
8965
  : trackType === TrackType.SCREEN_SHARE
8948
- ? findOptimalScreenSharingLayers(track, opts.screenShareSettings, screenShareBitrate)
8966
+ ? findOptimalScreenSharingLayers(track, screenShareSettings, screenShareBitrate)
8949
8967
  : undefined;
8950
- let preferredCodec = opts.preferredCodec;
8951
- if (!preferredCodec && trackType === TrackType.VIDEO && isReactNative()) {
8952
- const osName = getOSInfo()?.name.toLowerCase();
8953
- if (osName === 'ipados') {
8954
- // in ipads it was noticed that if vp8 codec is used
8955
- // then the bytes sent is 0 in the outbound-rtp
8956
- // so we are forcing h264 codec for ipads
8957
- preferredCodec = 'H264';
8958
- }
8959
- else if (osName === 'android') {
8960
- preferredCodec = 'VP8';
8961
- }
8962
- }
8963
8968
  // listen for 'ended' event on the track as it might be ended abruptly
8964
8969
  // by an external factor as permission revokes, device disconnected, etc.
8965
8970
  // keep in mind that `track.stop()` doesn't trigger this event.
@@ -8978,8 +8983,11 @@ class Publisher {
8978
8983
  this.transceiverInitOrder.push(trackType);
8979
8984
  this.transceiverRegistry[trackType] = transceiver;
8980
8985
  this.publishOptionsPerTrackType.set(trackType, opts);
8986
+ const codec = isReactNative() && trackType === TrackType.VIDEO && !preferredCodec
8987
+ ? getRNOptimalCodec()
8988
+ : preferredCodec;
8981
8989
  const codecPreferences = 'setCodecPreferences' in transceiver
8982
- ? this.getCodecPreferences(trackType, preferredCodec)
8990
+ ? this.getCodecPreferences(trackType, codec)
8983
8991
  : undefined;
8984
8992
  if (codecPreferences) {
8985
8993
  this.logger('info', `Setting ${TrackType[trackType]} codec preferences`, codecPreferences);
@@ -9293,7 +9301,7 @@ class Publisher {
9293
9301
  const publishOpts = this.publishOptionsPerTrackType.get(trackType);
9294
9302
  optimalLayers =
9295
9303
  trackType === TrackType.VIDEO
9296
- ? findOptimalVideoLayers(track, targetResolution)
9304
+ ? findOptimalVideoLayers(track, targetResolution, publishOpts?.preferredBitrate)
9297
9305
  : trackType === TrackType.SCREEN_SHARE
9298
9306
  ? findOptimalScreenSharingLayers(track, publishOpts?.screenShareSettings)
9299
9307
  : [];
@@ -12167,6 +12175,17 @@ class CameraManager extends InputMediaDeviceManager {
12167
12175
  height: 720,
12168
12176
  };
12169
12177
  }
12178
+ /**
12179
+ * The publish options for the camera.
12180
+ *
12181
+ * @internal internal use only, not part of the public API.
12182
+ */
12183
+ get publishOptions() {
12184
+ return {
12185
+ preferredCodec: this.preferredCodec,
12186
+ preferredBitrate: this.preferredBitrate,
12187
+ };
12188
+ }
12170
12189
  /**
12171
12190
  * Select the camera direction.
12172
12191
  *
@@ -12223,6 +12242,15 @@ class CameraManager extends InputMediaDeviceManager {
12223
12242
  setPreferredCodec(codec) {
12224
12243
  this.preferredCodec = codec;
12225
12244
  }
12245
+ /**
12246
+ * Sets the preferred bitrate for encoding the video.
12247
+ *
12248
+ * @internal internal use only, not part of the public API.
12249
+ * @param bitrate the bitrate to use for encoding the video.
12250
+ */
12251
+ setPreferredBitrate(bitrate) {
12252
+ this.preferredBitrate = bitrate;
12253
+ }
12226
12254
  getDevices() {
12227
12255
  return getVideoDevices();
12228
12256
  }
@@ -12238,9 +12266,7 @@ class CameraManager extends InputMediaDeviceManager {
12238
12266
  return getVideoStream(constraints);
12239
12267
  }
12240
12268
  publishStream(stream) {
12241
- return this.call.publishVideoStream(stream, {
12242
- preferredCodec: this.preferredCodec,
12243
- });
12269
+ return this.call.publishVideoStream(stream, this.publishOptions);
12244
12270
  }
12245
12271
  stopPublishStream(stopTracks) {
12246
12272
  return this.call.stopPublish(TrackType.VIDEO, stopTracks);
@@ -13633,9 +13659,7 @@ class Call {
13633
13659
  case TrackType.VIDEO:
13634
13660
  const videoStream = this.camera.state.mediaStream;
13635
13661
  if (videoStream) {
13636
- await this.publishVideoStream(videoStream, {
13637
- preferredCodec: this.camera.preferredCodec,
13638
- });
13662
+ await this.publishVideoStream(videoStream, this.camera.publishOptions);
13639
13663
  }
13640
13664
  break;
13641
13665
  case TrackType.SCREEN_SHARE:
@@ -14311,9 +14335,7 @@ class Call {
14311
14335
  if (this.camera.enabled &&
14312
14336
  this.camera.state.mediaStream &&
14313
14337
  !this.publisher?.isPublishing(TrackType.VIDEO)) {
14314
- await this.publishVideoStream(this.camera.state.mediaStream, {
14315
- preferredCodec: this.camera.preferredCodec,
14316
- });
14338
+ await this.publishVideoStream(this.camera.state.mediaStream, this.camera.publishOptions);
14317
14339
  }
14318
14340
  // Start camera if backend config specifies, and there is no local setting
14319
14341
  if (this.camera.state.status === undefined &&
@@ -16092,7 +16114,7 @@ class StreamClient {
16092
16114
  });
16093
16115
  };
16094
16116
  this.getUserAgent = () => {
16095
- const version = "1.6.3" ;
16117
+ const version = "1.6.4" ;
16096
16118
  return (this.userAgent ||
16097
16119
  `stream-video-javascript-client-${this.node ? 'node' : 'browser'}-${version}`);
16098
16120
  };