@stream-io/video-client 1.40.2 → 1.41.0

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/dist/index.cjs.js CHANGED
@@ -46,6 +46,38 @@ const AudioSettingsResponseDefaultDeviceEnum = {
46
46
  SPEAKER: 'speaker',
47
47
  EARPIECE: 'earpiece',
48
48
  };
49
+ /**
50
+ * @export
51
+ */
52
+ const CallRecordingFailedEventRecordingTypeEnum = {
53
+ COMPOSITE: 'composite',
54
+ INDIVIDUAL: 'individual',
55
+ RAW: 'raw',
56
+ };
57
+ /**
58
+ * @export
59
+ */
60
+ const CallRecordingReadyEventRecordingTypeEnum = {
61
+ COMPOSITE: 'composite',
62
+ INDIVIDUAL: 'individual',
63
+ RAW: 'raw',
64
+ };
65
+ /**
66
+ * @export
67
+ */
68
+ const CallRecordingStartedEventRecordingTypeEnum = {
69
+ COMPOSITE: 'composite',
70
+ INDIVIDUAL: 'individual',
71
+ RAW: 'raw',
72
+ };
73
+ /**
74
+ * @export
75
+ */
76
+ const CallRecordingStoppedEventRecordingTypeEnum = {
77
+ COMPOSITE: 'composite',
78
+ INDIVIDUAL: 'individual',
79
+ RAW: 'raw',
80
+ };
49
81
  /**
50
82
  * @export
51
83
  */
@@ -81,6 +113,22 @@ const FrameRecordingSettingsResponseModeEnum = {
81
113
  DISABLED: 'disabled',
82
114
  AUTO_ON: 'auto-on',
83
115
  };
116
+ /**
117
+ * @export
118
+ */
119
+ const IndividualRecordingSettingsRequestModeEnum = {
120
+ AVAILABLE: 'available',
121
+ DISABLED: 'disabled',
122
+ AUTO_ON: 'auto-on',
123
+ };
124
+ /**
125
+ * @export
126
+ */
127
+ const IndividualRecordingSettingsResponseModeEnum = {
128
+ AVAILABLE: 'available',
129
+ DISABLED: 'disabled',
130
+ AUTO_ON: 'auto-on',
131
+ };
84
132
  /**
85
133
  * @export
86
134
  */
@@ -146,11 +194,15 @@ const OwnCapability = {
146
194
  START_BROADCAST_CALL: 'start-broadcast-call',
147
195
  START_CLOSED_CAPTIONS_CALL: 'start-closed-captions-call',
148
196
  START_FRAME_RECORD_CALL: 'start-frame-record-call',
197
+ START_INDIVIDUAL_RECORD_CALL: 'start-individual-record-call',
198
+ START_RAW_RECORD_CALL: 'start-raw-record-call',
149
199
  START_RECORD_CALL: 'start-record-call',
150
200
  START_TRANSCRIPTION_CALL: 'start-transcription-call',
151
201
  STOP_BROADCAST_CALL: 'stop-broadcast-call',
152
202
  STOP_CLOSED_CAPTIONS_CALL: 'stop-closed-captions-call',
153
203
  STOP_FRAME_RECORD_CALL: 'stop-frame-record-call',
204
+ STOP_INDIVIDUAL_RECORD_CALL: 'stop-individual-record-call',
205
+ STOP_RAW_RECORD_CALL: 'stop-raw-record-call',
154
206
  STOP_RECORD_CALL: 'stop-record-call',
155
207
  STOP_TRANSCRIPTION_CALL: 'stop-transcription-call',
156
208
  UPDATE_CALL: 'update-call',
@@ -188,6 +240,22 @@ const RTMPSettingsRequestQualityEnum = {
188
240
  PORTRAIT_1080X1920: 'portrait-1080x1920',
189
241
  PORTRAIT_1440X2560: 'portrait-1440x2560',
190
242
  };
243
+ /**
244
+ * @export
245
+ */
246
+ const RawRecordingSettingsRequestModeEnum = {
247
+ AVAILABLE: 'available',
248
+ DISABLED: 'disabled',
249
+ AUTO_ON: 'auto-on',
250
+ };
251
+ /**
252
+ * @export
253
+ */
254
+ const RawRecordingSettingsResponseModeEnum = {
255
+ AVAILABLE: 'available',
256
+ DISABLED: 'disabled',
257
+ AUTO_ON: 'auto-on',
258
+ };
191
259
  /**
192
260
  * @export
193
261
  */
@@ -4003,15 +4071,18 @@ const extractMid = (transceiver, transceiverInitIndex, sdp) => {
4003
4071
  return '';
4004
4072
  return String(transceiverInitIndex);
4005
4073
  };
4006
- /*
4007
- * Sets the start bitrate for the VP9 and H264 codecs in the SDP.
4074
+ /**
4075
+ * Sets the start bitrate for the VP9, H264, and AV1 codecs in the SDP.
4008
4076
  *
4009
4077
  * @param offerSdp the offer SDP to modify.
4010
- * @param startBitrate the start bitrate in kbps to set. Default is 1000 kbps.
4078
+ * @param maxBitrateKbps the maximum bitrate in kbps.
4079
+ * @param startBitrateFactor the factor (0-1) to multiply with maxBitrateKbps to get the start bitrate.
4080
+ * @param targetMid the media ID to target.
4011
4081
  */
4012
4082
  const setStartBitrate = (offerSdp, maxBitrateKbps, startBitrateFactor, targetMid) => {
4013
4083
  // start bitrate should be between 300kbps and max-bitrate-kbps
4014
- const startBitrate = Math.max(Math.min(maxBitrateKbps, startBitrateFactor * maxBitrateKbps), 300);
4084
+ // Clamp to max first, then ensure minimum of 300 (but never exceed max)
4085
+ const startBitrate = Math.min(maxBitrateKbps, Math.max(300, startBitrateFactor * maxBitrateKbps));
4015
4086
  const parsedSdp = sdpTransform.parse(offerSdp);
4016
4087
  const targetCodecs = new Set(['av1', 'vp9', 'h264']);
4017
4088
  for (const media of parsedSdp.media) {
@@ -4022,14 +4093,27 @@ const setStartBitrate = (offerSdp, maxBitrateKbps, startBitrateFactor, targetMid
4022
4093
  for (const rtp of media.rtp) {
4023
4094
  if (!targetCodecs.has(rtp.codec.toLowerCase()))
4024
4095
  continue;
4025
- for (const fmtp of media.fmtp) {
4026
- if (fmtp.payload === rtp.payload) {
4027
- if (!fmtp.config.includes('x-google-start-bitrate')) {
4028
- fmtp.config += `;x-google-start-bitrate=${startBitrate}`;
4029
- }
4030
- break;
4096
+ // Find existing fmtp entry for this payload
4097
+ // Guard against media.fmtp being undefined when SDP has no a=fmtp lines
4098
+ const fmtpList = media.fmtp ?? (media.fmtp = []);
4099
+ const existingFmtp = fmtpList.find((fmtp) => fmtp.payload === rtp.payload);
4100
+ if (existingFmtp) {
4101
+ // Append to existing fmtp if not already present
4102
+ // Guard against undefined or empty config from malformed SDP
4103
+ const config = existingFmtp.config ?? '';
4104
+ if (!config.includes('x-google-start-bitrate')) {
4105
+ existingFmtp.config = config
4106
+ ? `${config};x-google-start-bitrate=${startBitrate}`
4107
+ : `x-google-start-bitrate=${startBitrate}`;
4031
4108
  }
4032
4109
  }
4110
+ else {
4111
+ // Create new fmtp entry if none exists
4112
+ fmtpList.push({
4113
+ payload: rtp.payload,
4114
+ config: `x-google-start-bitrate=${startBitrate}`,
4115
+ });
4116
+ }
4033
4117
  }
4034
4118
  }
4035
4119
  return sdpTransform.write(parsedSdp);
@@ -4959,6 +5043,10 @@ const paginatedLayoutSortPreset = combineComparators(pinned, ifInvisibleOrUnknow
4959
5043
  */
4960
5044
  const livestreamOrAudioRoomSortPreset = combineComparators(ifInvisibleBy(combineComparators(dominantSpeaker, speaking, reactionType('raised-hand'), withVideoIngressSource, publishingVideo, publishingAudio)), role('admin', 'host', 'speaker'));
4961
5045
 
5046
+ const ensureExhausted = (x, message) => {
5047
+ videoLoggerSystem.getLogger('helpers').warn(message, x);
5048
+ };
5049
+
4962
5050
  /**
4963
5051
  * Returns the default egress object - when no egress data is available.
4964
5052
  */
@@ -4988,6 +5076,8 @@ class CallState {
4988
5076
  this.egressSubject = new rxjs.BehaviorSubject(undefined);
4989
5077
  this.ingressSubject = new rxjs.BehaviorSubject(undefined);
4990
5078
  this.recordingSubject = new rxjs.BehaviorSubject(false);
5079
+ this.individualRecordingSubject = new rxjs.BehaviorSubject(false);
5080
+ this.rawRecordingSubject = new rxjs.BehaviorSubject(false);
4991
5081
  this.sessionSubject = new rxjs.BehaviorSubject(undefined);
4992
5082
  this.settingsSubject = new rxjs.BehaviorSubject(undefined);
4993
5083
  this.transcribingSubject = new rxjs.BehaviorSubject(false);
@@ -5414,7 +5504,10 @@ class CallState {
5414
5504
  this.setCurrentValue(this.customSubject, call.custom);
5415
5505
  this.setCurrentValue(this.egressSubject, call.egress);
5416
5506
  this.setCurrentValue(this.ingressSubject, call.ingress);
5417
- this.setCurrentValue(this.recordingSubject, call.recording);
5507
+ const { individual_recording, composite_recording, raw_recording } = call.egress;
5508
+ this.setCurrentValue(this.recordingSubject, call.recording || composite_recording?.status === 'running');
5509
+ this.setCurrentValue(this.individualRecordingSubject, individual_recording?.status === 'running');
5510
+ this.setCurrentValue(this.rawRecordingSubject, raw_recording?.status === 'running');
5418
5511
  const s = this.setCurrentValue(this.sessionSubject, call.session);
5419
5512
  this.updateParticipantCountFromSession(s);
5420
5513
  this.setCurrentValue(this.settingsSubject, call.settings);
@@ -5490,6 +5583,21 @@ class CallState {
5490
5583
  },
5491
5584
  }));
5492
5585
  };
5586
+ this.updateFromRecordingEvent = (type, running) => {
5587
+ // handle the legacy format, where `type` is absent in the emitted events
5588
+ if (type === undefined || type === 'composite') {
5589
+ this.setCurrentValue(this.recordingSubject, running);
5590
+ }
5591
+ else if (type === 'individual') {
5592
+ this.setCurrentValue(this.individualRecordingSubject, running);
5593
+ }
5594
+ else if (type === 'raw') {
5595
+ this.setCurrentValue(this.rawRecordingSubject, running);
5596
+ }
5597
+ else {
5598
+ ensureExhausted(type, 'Unknown recording type');
5599
+ }
5600
+ };
5493
5601
  this.updateParticipantCountFromSession = (session) => {
5494
5602
  // when in JOINED state, we should use the participant count coming through
5495
5603
  // the SFU healthcheck event, as it's more accurate.
@@ -5724,6 +5832,8 @@ class CallState {
5724
5832
  }), rxjs.distinctUntilChanged(isShallowEqual), rxjs.shareReplay({ bufferSize: 1, refCount: true }));
5725
5833
  this.participantCount$ = duc(this.participantCountSubject);
5726
5834
  this.recording$ = duc(this.recordingSubject);
5835
+ this.individualRecording$ = duc(this.individualRecordingSubject);
5836
+ this.rawRecording$ = duc(this.rawRecordingSubject);
5727
5837
  this.transcribing$ = duc(this.transcribingSubject);
5728
5838
  this.captioning$ = duc(this.captioningSubject);
5729
5839
  this.eventHandlers = {
@@ -5790,9 +5900,15 @@ class CallState {
5790
5900
  },
5791
5901
  'call.permissions_updated': this.updateOwnCapabilities,
5792
5902
  'call.reaction_new': this.updateParticipantReaction,
5793
- 'call.recording_started': () => this.setCurrentValue(this.recordingSubject, true),
5794
- 'call.recording_stopped': () => this.setCurrentValue(this.recordingSubject, false),
5795
- 'call.recording_failed': () => this.setCurrentValue(this.recordingSubject, false),
5903
+ 'call.recording_started': (e) => {
5904
+ this.updateFromRecordingEvent(e.recording_type, true);
5905
+ },
5906
+ 'call.recording_stopped': (e) => {
5907
+ this.updateFromRecordingEvent(e.recording_type, false);
5908
+ },
5909
+ 'call.recording_failed': (e) => {
5910
+ this.updateFromRecordingEvent(e.recording_type, false);
5911
+ },
5796
5912
  'call.rejected': (e) => this.updateFromCallResponse(e.call),
5797
5913
  'call.ring': (e) => this.updateFromCallResponse(e.call),
5798
5914
  'call.missed': (e) => this.updateFromCallResponse(e.call),
@@ -5975,11 +6091,23 @@ class CallState {
5975
6091
  return this.getCurrentValue(this.ingress$);
5976
6092
  }
5977
6093
  /**
5978
- * Will provide the recording state of this call.
6094
+ * Will provide the composite recording state of this call.
5979
6095
  */
5980
6096
  get recording() {
5981
6097
  return this.getCurrentValue(this.recording$);
5982
6098
  }
6099
+ /**
6100
+ * Will provide the individual recording state of this call.
6101
+ */
6102
+ get individualRecording() {
6103
+ return this.getCurrentValue(this.individualRecording$);
6104
+ }
6105
+ /**
6106
+ * Will provide the raw recording state of this call.
6107
+ */
6108
+ get rawRecording() {
6109
+ return this.getCurrentValue(this.rawRecording$);
6110
+ }
5983
6111
  /**
5984
6112
  * Will provide the session data of this call.
5985
6113
  */
@@ -6080,7 +6208,7 @@ const getSdkVersion = (sdk) => {
6080
6208
  return sdk ? `${sdk.major}.${sdk.minor}.${sdk.patch}` : '0.0.0-development';
6081
6209
  };
6082
6210
 
6083
- const version = "1.40.2";
6211
+ const version = "1.41.0";
6084
6212
  const [major, minor, patch] = version.split('.');
6085
6213
  let sdkInfo = {
6086
6214
  type: SdkType.PLAIN_JAVASCRIPT,
@@ -7428,10 +7556,6 @@ class TransceiverCache {
7428
7556
  }
7429
7557
  }
7430
7558
 
7431
- const ensureExhausted = (x, message) => {
7432
- videoLoggerSystem.getLogger('helpers').warn(message, x);
7433
- };
7434
-
7435
7559
  const trackTypeToParticipantStreamKey = (trackType) => {
7436
7560
  switch (trackType) {
7437
7561
  case TrackType.SCREEN_SHARE:
@@ -7912,7 +8036,7 @@ class Publisher extends BasePeerConnection {
7912
8036
  let sdp = dangerouslyForceCodec
7913
8037
  ? removeCodecsExcept(baseSdp, dangerouslyForceCodec, fmtpLine)
7914
8038
  : baseSdp;
7915
- if (dangerouslySetStartBitrateFactor) {
8039
+ if (dangerouslySetStartBitrateFactor !== undefined) {
7916
8040
  this.transceiverCache.items().forEach((t) => {
7917
8041
  if (t.publishOption.trackType !== TrackType.VIDEO)
7918
8042
  return;
@@ -11950,8 +12074,6 @@ class SpeakerManager {
11950
12074
  /**
11951
12075
  * Set the volume of a participant.
11952
12076
  *
11953
- * Note: This method is not supported in React Native.
11954
- *
11955
12077
  * @param sessionId the participant's session id.
11956
12078
  * @param volume a number between 0 and 1. Set it to `undefined` to use the default volume.
11957
12079
  */
@@ -12294,6 +12416,7 @@ class Call {
12294
12416
  this.ringingSubject.next(false);
12295
12417
  this.cancelAutoDrop();
12296
12418
  this.clientStore.unregisterCall(this);
12419
+ globalThis.streamRNVideoSDK?.callManager.stop();
12297
12420
  this.camera.dispose();
12298
12421
  this.microphone.dispose();
12299
12422
  this.screenShare.dispose();
@@ -12626,6 +12749,7 @@ class Call {
12626
12749
  // re-apply them on later reconnections or server-side data fetches
12627
12750
  if (!this.deviceSettingsAppliedOnce && this.state.settings) {
12628
12751
  await this.applyDeviceConfig(this.state.settings, true);
12752
+ globalThis.streamRNVideoSDK?.callManager.start();
12629
12753
  this.deviceSettingsAppliedOnce = true;
12630
12754
  }
12631
12755
  // We shouldn't persist the `ring` and `notify` state after joining the call
@@ -12817,7 +12941,7 @@ class Call {
12817
12941
  }
12818
12942
  if (this.streamClient._hasConnectionID()) {
12819
12943
  this.watching = true;
12820
- this.clientStore.registerCall(this);
12944
+ this.clientStore.registerOrUpdateCall(this);
12821
12945
  }
12822
12946
  return joinResponse;
12823
12947
  };
@@ -13417,14 +13541,22 @@ class Call {
13417
13541
  /**
13418
13542
  * Starts recording the call
13419
13543
  */
13420
- this.startRecording = async (request) => {
13421
- return this.streamClient.post(`${this.streamClientBasePath}/start_recording`, request ? request : {});
13544
+ this.startRecording = async (dataOrType, type) => {
13545
+ type = typeof dataOrType === 'string' ? dataOrType : type;
13546
+ dataOrType = typeof dataOrType === 'string' ? undefined : dataOrType;
13547
+ const endpoint = !type
13548
+ ? `/start_recording`
13549
+ : `/recordings/${encodeURIComponent(type)}/start`;
13550
+ return this.streamClient.post(`${this.streamClientBasePath}${endpoint}`, dataOrType);
13422
13551
  };
13423
13552
  /**
13424
13553
  * Stops recording the call
13425
13554
  */
13426
- this.stopRecording = async () => {
13427
- return this.streamClient.post(`${this.streamClientBasePath}/stop_recording`, {});
13555
+ this.stopRecording = async (type) => {
13556
+ const endpoint = !type
13557
+ ? `/stop_recording`
13558
+ : `/recordings/${encodeURIComponent(type)}/stop`;
13559
+ return this.streamClient.post(`${this.streamClientBasePath}${endpoint}`);
13428
13560
  };
13429
13561
  /**
13430
13562
  * Starts the transcription of the call.
@@ -13821,6 +13953,9 @@ class Call {
13821
13953
  * @internal
13822
13954
  */
13823
13955
  this.applyDeviceConfig = async (settings, publish) => {
13956
+ globalThis.streamRNVideoSDK?.callManager.setup({
13957
+ default_device: settings.audio.default_device,
13958
+ });
13824
13959
  await this.camera.apply(settings.video, publish).catch((err) => {
13825
13960
  this.logger.warn('Camera init failed', err);
13826
13961
  });
@@ -15132,7 +15267,7 @@ class StreamClient {
15132
15267
  this.getUserAgent = () => {
15133
15268
  if (!this.cachedUserAgent) {
15134
15269
  const { clientAppIdentifier = {} } = this.options;
15135
- const { sdkName = 'js', sdkVersion = "1.40.2", ...extras } = clientAppIdentifier;
15270
+ const { sdkName = 'js', sdkVersion = "1.41.0", ...extras } = clientAppIdentifier;
15136
15271
  this.cachedUserAgent = [
15137
15272
  `stream-video-${sdkName}-v${sdkVersion}`,
15138
15273
  ...Object.entries(extras).map(([key, value]) => `${key}=${value}`),
@@ -15780,6 +15915,10 @@ exports.AudioSettingsRequestDefaultDeviceEnum = AudioSettingsRequestDefaultDevic
15780
15915
  exports.AudioSettingsResponseDefaultDeviceEnum = AudioSettingsResponseDefaultDeviceEnum;
15781
15916
  exports.Browsers = browsers;
15782
15917
  exports.Call = Call;
15918
+ exports.CallRecordingFailedEventRecordingTypeEnum = CallRecordingFailedEventRecordingTypeEnum;
15919
+ exports.CallRecordingReadyEventRecordingTypeEnum = CallRecordingReadyEventRecordingTypeEnum;
15920
+ exports.CallRecordingStartedEventRecordingTypeEnum = CallRecordingStartedEventRecordingTypeEnum;
15921
+ exports.CallRecordingStoppedEventRecordingTypeEnum = CallRecordingStoppedEventRecordingTypeEnum;
15783
15922
  exports.CallState = CallState;
15784
15923
  exports.CallType = CallType;
15785
15924
  exports.CallTypes = CallTypes;
@@ -15793,6 +15932,8 @@ exports.ErrorFromResponse = ErrorFromResponse;
15793
15932
  exports.FrameRecordingSettingsRequestModeEnum = FrameRecordingSettingsRequestModeEnum;
15794
15933
  exports.FrameRecordingSettingsRequestQualityEnum = FrameRecordingSettingsRequestQualityEnum;
15795
15934
  exports.FrameRecordingSettingsResponseModeEnum = FrameRecordingSettingsResponseModeEnum;
15935
+ exports.IndividualRecordingSettingsRequestModeEnum = IndividualRecordingSettingsRequestModeEnum;
15936
+ exports.IndividualRecordingSettingsResponseModeEnum = IndividualRecordingSettingsResponseModeEnum;
15796
15937
  exports.IngressAudioEncodingOptionsRequestChannelsEnum = IngressAudioEncodingOptionsRequestChannelsEnum;
15797
15938
  exports.IngressSourceRequestFpsEnum = IngressSourceRequestFpsEnum;
15798
15939
  exports.IngressVideoLayerRequestCodecEnum = IngressVideoLayerRequestCodecEnum;
@@ -15804,6 +15945,8 @@ exports.OwnCapability = OwnCapability;
15804
15945
  exports.RNSpeechDetector = RNSpeechDetector;
15805
15946
  exports.RTMPBroadcastRequestQualityEnum = RTMPBroadcastRequestQualityEnum;
15806
15947
  exports.RTMPSettingsRequestQualityEnum = RTMPSettingsRequestQualityEnum;
15948
+ exports.RawRecordingSettingsRequestModeEnum = RawRecordingSettingsRequestModeEnum;
15949
+ exports.RawRecordingSettingsResponseModeEnum = RawRecordingSettingsResponseModeEnum;
15807
15950
  exports.RecordSettingsRequestModeEnum = RecordSettingsRequestModeEnum;
15808
15951
  exports.RecordSettingsRequestQualityEnum = RecordSettingsRequestQualityEnum;
15809
15952
  exports.RxUtils = rxUtils;