@stream-io/video-client 1.32.0 → 1.33.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/CHANGELOG.md +10 -0
- package/dist/index.browser.es.js +307 -74
- package/dist/index.browser.es.js.map +1 -1
- package/dist/index.cjs.js +308 -75
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +307 -74
- package/dist/index.es.js.map +1 -1
- package/dist/src/Call.d.ts +3 -2
- package/dist/src/devices/AudioDeviceManager.d.ts +25 -0
- package/dist/src/devices/AudioDeviceManagerState.d.ts +24 -0
- package/dist/src/devices/CameraManager.d.ts +2 -2
- package/dist/src/devices/CameraManagerState.d.ts +3 -4
- package/dist/src/devices/{InputMediaDeviceManager.d.ts → DeviceManager.d.ts} +6 -6
- package/dist/src/devices/{InputMediaDeviceManagerState.d.ts → DeviceManagerState.d.ts} +4 -4
- package/dist/src/devices/MicrophoneManager.d.ts +5 -3
- package/dist/src/devices/MicrophoneManagerState.d.ts +6 -10
- package/dist/src/devices/ScreenShareManager.d.ts +4 -2
- package/dist/src/devices/ScreenShareState.d.ts +6 -2
- package/dist/src/devices/SpeakerState.d.ts +4 -4
- package/dist/src/devices/index.d.ts +2 -2
- package/dist/src/gen/coordinator/index.d.ts +169 -2
- package/dist/src/gen/video/sfu/models/models.d.ts +43 -0
- package/dist/src/rtc/BasePeerConnection.d.ts +2 -12
- package/dist/src/rtc/Publisher.d.ts +9 -6
- package/dist/src/rtc/Subscriber.d.ts +2 -1
- package/dist/src/rtc/TransceiverCache.d.ts +10 -11
- package/dist/src/rtc/index.d.ts +1 -1
- package/dist/src/rtc/{videoLayers.d.ts → layers.d.ts} +7 -1
- package/dist/src/rtc/types.d.ts +31 -0
- package/package.json +3 -2
- package/src/Call.ts +13 -6
- package/src/__tests__/Call.publishing.test.ts +14 -3
- package/src/__tests__/StreamVideoClient.api.test.ts +1 -1
- package/src/devices/AudioDeviceManager.ts +61 -0
- package/src/devices/AudioDeviceManagerState.ts +44 -0
- package/src/devices/CameraManager.ts +4 -4
- package/src/devices/CameraManagerState.ts +9 -8
- package/src/devices/{InputMediaDeviceManager.ts → DeviceManager.ts} +11 -8
- package/src/devices/{InputMediaDeviceManagerState.ts → DeviceManagerState.ts} +7 -4
- package/src/devices/MicrophoneManager.ts +26 -6
- package/src/devices/MicrophoneManagerState.ts +18 -19
- package/src/devices/ScreenShareManager.ts +23 -4
- package/src/devices/ScreenShareState.ts +11 -3
- package/src/devices/SpeakerState.ts +6 -14
- package/src/devices/__tests__/CameraManager.test.ts +1 -0
- package/src/devices/__tests__/{InputMediaDeviceManager.test.ts → DeviceManager.test.ts} +4 -4
- package/src/devices/__tests__/{InputMediaDeviceManagerFilters.test.ts → DeviceManagerFilters.test.ts} +4 -4
- package/src/devices/__tests__/{InputMediaDeviceManagerState.test.ts → DeviceManagerState.test.ts} +2 -2
- package/src/devices/__tests__/MicrophoneManager.test.ts +41 -1
- package/src/devices/__tests__/NoiseCancellationStub.ts +3 -1
- package/src/devices/__tests__/ScreenShareManager.test.ts +5 -1
- package/src/devices/index.ts +2 -2
- package/src/events/__tests__/internal.test.ts +25 -11
- package/src/gen/coordinator/index.ts +169 -2
- package/src/gen/video/sfu/models/models.ts +65 -0
- package/src/rtc/BasePeerConnection.ts +1 -16
- package/src/rtc/Publisher.ts +74 -31
- package/src/rtc/Subscriber.ts +2 -4
- package/src/rtc/TransceiverCache.ts +23 -27
- package/src/rtc/__tests__/Publisher.test.ts +61 -29
- package/src/rtc/__tests__/{videoLayers.test.ts → layers.test.ts} +76 -1
- package/src/rtc/index.ts +2 -1
- package/src/rtc/{videoLayers.ts → layers.ts} +28 -7
- package/src/rtc/types.ts +44 -0
- package/src/sorting/presets.ts +2 -2
- package/src/store/CallState.ts +36 -10
- package/src/store/__tests__/CallState.test.ts +20 -2
package/dist/index.browser.es.js
CHANGED
|
@@ -113,6 +113,7 @@ const OwnCapability = {
|
|
|
113
113
|
REMOVE_CALL_MEMBER: 'remove-call-member',
|
|
114
114
|
SCREENSHARE: 'screenshare',
|
|
115
115
|
SEND_AUDIO: 'send-audio',
|
|
116
|
+
SEND_CLOSED_CAPTIONS_CALL: 'send-closed-captions-call',
|
|
116
117
|
SEND_VIDEO: 'send-video',
|
|
117
118
|
START_BROADCAST_CALL: 'start-broadcast-call',
|
|
118
119
|
START_CLOSED_CAPTIONS_CALL: 'start-closed-captions-call',
|
|
@@ -958,6 +959,24 @@ var ParticipantSource;
|
|
|
958
959
|
*/
|
|
959
960
|
ParticipantSource[ParticipantSource["SRT"] = 5] = "SRT";
|
|
960
961
|
})(ParticipantSource || (ParticipantSource = {}));
|
|
962
|
+
/**
|
|
963
|
+
* @generated from protobuf enum stream.video.sfu.models.AudioBitrateProfile
|
|
964
|
+
*/
|
|
965
|
+
var AudioBitrateProfile;
|
|
966
|
+
(function (AudioBitrateProfile) {
|
|
967
|
+
/**
|
|
968
|
+
* @generated from protobuf enum value: AUDIO_BITRATE_PROFILE_VOICE_STANDARD_UNSPECIFIED = 0;
|
|
969
|
+
*/
|
|
970
|
+
AudioBitrateProfile[AudioBitrateProfile["VOICE_STANDARD_UNSPECIFIED"] = 0] = "VOICE_STANDARD_UNSPECIFIED";
|
|
971
|
+
/**
|
|
972
|
+
* @generated from protobuf enum value: AUDIO_BITRATE_PROFILE_VOICE_HIGH_QUALITY = 1;
|
|
973
|
+
*/
|
|
974
|
+
AudioBitrateProfile[AudioBitrateProfile["VOICE_HIGH_QUALITY"] = 1] = "VOICE_HIGH_QUALITY";
|
|
975
|
+
/**
|
|
976
|
+
* @generated from protobuf enum value: AUDIO_BITRATE_PROFILE_MUSIC_HIGH_QUALITY = 2;
|
|
977
|
+
*/
|
|
978
|
+
AudioBitrateProfile[AudioBitrateProfile["MUSIC_HIGH_QUALITY"] = 2] = "MUSIC_HIGH_QUALITY";
|
|
979
|
+
})(AudioBitrateProfile || (AudioBitrateProfile = {}));
|
|
961
980
|
/**
|
|
962
981
|
* @generated from protobuf enum stream.video.sfu.models.ErrorCode
|
|
963
982
|
*/
|
|
@@ -1571,6 +1590,13 @@ class PublishOption$Type extends MessageType {
|
|
|
1571
1590
|
kind: 'scalar',
|
|
1572
1591
|
T: 8 /*ScalarType.BOOL*/,
|
|
1573
1592
|
},
|
|
1593
|
+
{
|
|
1594
|
+
no: 10,
|
|
1595
|
+
name: 'audio_bitrate_profiles',
|
|
1596
|
+
kind: 'message',
|
|
1597
|
+
repeat: 2 /*RepeatType.UNPACKED*/,
|
|
1598
|
+
T: () => AudioBitrate,
|
|
1599
|
+
},
|
|
1574
1600
|
]);
|
|
1575
1601
|
}
|
|
1576
1602
|
}
|
|
@@ -1634,6 +1660,28 @@ let ICETrickle$Type$1 = class ICETrickle$Type extends MessageType {
|
|
|
1634
1660
|
*/
|
|
1635
1661
|
const ICETrickle$1 = new ICETrickle$Type$1();
|
|
1636
1662
|
// @generated message type with reflection information, may provide speed optimized methods
|
|
1663
|
+
class AudioBitrate$Type extends MessageType {
|
|
1664
|
+
constructor() {
|
|
1665
|
+
super('stream.video.sfu.models.AudioBitrate', [
|
|
1666
|
+
{
|
|
1667
|
+
no: 1,
|
|
1668
|
+
name: 'profile',
|
|
1669
|
+
kind: 'enum',
|
|
1670
|
+
T: () => [
|
|
1671
|
+
'stream.video.sfu.models.AudioBitrateProfile',
|
|
1672
|
+
AudioBitrateProfile,
|
|
1673
|
+
'AUDIO_BITRATE_PROFILE_',
|
|
1674
|
+
],
|
|
1675
|
+
},
|
|
1676
|
+
{ no: 2, name: 'bitrate', kind: 'scalar', T: 5 /*ScalarType.INT32*/ },
|
|
1677
|
+
]);
|
|
1678
|
+
}
|
|
1679
|
+
}
|
|
1680
|
+
/**
|
|
1681
|
+
* @generated MessageType for protobuf message stream.video.sfu.models.AudioBitrate
|
|
1682
|
+
*/
|
|
1683
|
+
const AudioBitrate = new AudioBitrate$Type();
|
|
1684
|
+
// @generated message type with reflection information, may provide speed optimized methods
|
|
1637
1685
|
class TrackInfo$Type extends MessageType {
|
|
1638
1686
|
constructor() {
|
|
1639
1687
|
super('stream.video.sfu.models.TrackInfo', [
|
|
@@ -1984,6 +2032,8 @@ var models = /*#__PURE__*/Object.freeze({
|
|
|
1984
2032
|
get AndroidThermalState () { return AndroidThermalState; },
|
|
1985
2033
|
AppleState: AppleState,
|
|
1986
2034
|
get AppleThermalState () { return AppleThermalState; },
|
|
2035
|
+
AudioBitrate: AudioBitrate,
|
|
2036
|
+
get AudioBitrateProfile () { return AudioBitrateProfile; },
|
|
1987
2037
|
Browser: Browser,
|
|
1988
2038
|
Call: Call$1,
|
|
1989
2039
|
get CallEndedReason () { return CallEndedReason; },
|
|
@@ -4658,11 +4708,11 @@ const ifInvisibleOrUnknownBy = conditional((a, b) => a.viewportVisibilityState?.
|
|
|
4658
4708
|
/**
|
|
4659
4709
|
* The default sorting preset.
|
|
4660
4710
|
*/
|
|
4661
|
-
const defaultSortPreset = combineComparators(
|
|
4711
|
+
const defaultSortPreset = combineComparators(screenSharing, pinned, ifInvisibleBy(combineComparators(dominantSpeaker, speaking, reactionType('raised-hand'), publishingVideo, publishingAudio)));
|
|
4662
4712
|
/**
|
|
4663
4713
|
* The sorting preset for speaker layout.
|
|
4664
4714
|
*/
|
|
4665
|
-
const speakerLayoutSortPreset = combineComparators(
|
|
4715
|
+
const speakerLayoutSortPreset = combineComparators(screenSharing, pinned, dominantSpeaker, ifInvisibleBy(combineComparators(speaking, reactionType('raised-hand'), publishingVideo, publishingAudio)));
|
|
4666
4716
|
/**
|
|
4667
4717
|
* The sorting preset for layouts that don't render all participants but
|
|
4668
4718
|
* instead, render them in pages.
|
|
@@ -5009,14 +5059,32 @@ class CallState {
|
|
|
5009
5059
|
* @param pins the latest pins from the server.
|
|
5010
5060
|
*/
|
|
5011
5061
|
this.setServerSidePins = (pins) => {
|
|
5012
|
-
const
|
|
5013
|
-
|
|
5062
|
+
const now = Date.now();
|
|
5063
|
+
const unknownSymbol = Symbol('unknown');
|
|
5064
|
+
// generate a lookup table of pinnedAt timestamps by userId and sessionId
|
|
5065
|
+
// if there are multiple pins for the same userId, then we set the pinnedAt
|
|
5066
|
+
// to `unknown` (for that userId lookup) so that we don't apply any pin for that participant
|
|
5067
|
+
// this is to avoid conflicts during reconstruction of the pin state after reconnections
|
|
5068
|
+
// as sessionIds can change
|
|
5069
|
+
const pinnedAtByIdentifier = pins.reduce((lookup, pin, index) => {
|
|
5070
|
+
var _a;
|
|
5071
|
+
const pinnedAt = now + (pins.length - index);
|
|
5072
|
+
if (lookup[pin.userId]) {
|
|
5073
|
+
lookup[pin.userId] = unknownSymbol;
|
|
5074
|
+
}
|
|
5075
|
+
else {
|
|
5076
|
+
lookup[pin.userId] = pinnedAt;
|
|
5077
|
+
}
|
|
5078
|
+
lookup[_a = pin.sessionId] ?? (lookup[_a] = pinnedAt);
|
|
5014
5079
|
return lookup;
|
|
5015
5080
|
}, {});
|
|
5016
5081
|
return this.setParticipants((participants) => participants.map((participant) => {
|
|
5017
|
-
|
|
5082
|
+
// first check by sessionId as that is 100% correct, then by attempt reconstruction by userId
|
|
5083
|
+
const serverSidePinnedAt = pinnedAtByIdentifier[participant.sessionId] ??
|
|
5084
|
+
pinnedAtByIdentifier[participant.userId];
|
|
5018
5085
|
// the participant is newly pinned
|
|
5019
|
-
if (serverSidePinnedAt
|
|
5086
|
+
if (typeof serverSidePinnedAt === 'number' &&
|
|
5087
|
+
typeof participant.pin?.pinnedAt !== 'number') {
|
|
5020
5088
|
return {
|
|
5021
5089
|
...participant,
|
|
5022
5090
|
pin: {
|
|
@@ -5027,7 +5095,8 @@ class CallState {
|
|
|
5027
5095
|
}
|
|
5028
5096
|
// the participant is no longer pinned server side
|
|
5029
5097
|
// we need to reset the pin
|
|
5030
|
-
if (
|
|
5098
|
+
if (typeof serverSidePinnedAt !== 'number' &&
|
|
5099
|
+
participant.pin?.isLocalPin === false) {
|
|
5031
5100
|
return {
|
|
5032
5101
|
...participant,
|
|
5033
5102
|
pin: undefined,
|
|
@@ -5764,7 +5833,7 @@ const getSdkVersion = (sdk) => {
|
|
|
5764
5833
|
return sdk ? `${sdk.major}.${sdk.minor}.${sdk.patch}` : '0.0.0-development';
|
|
5765
5834
|
};
|
|
5766
5835
|
|
|
5767
|
-
const version = "1.
|
|
5836
|
+
const version = "1.33.0";
|
|
5768
5837
|
const [major, minor, patch] = version.split('.');
|
|
5769
5838
|
let sdkInfo = {
|
|
5770
5839
|
type: SdkType.PLAIN_JAVASCRIPT,
|
|
@@ -6962,15 +7031,24 @@ class TransceiverCache {
|
|
|
6962
7031
|
/**
|
|
6963
7032
|
* Adds a transceiver to the cache.
|
|
6964
7033
|
*/
|
|
6965
|
-
this.add = (
|
|
6966
|
-
this.cache.push(
|
|
6967
|
-
this.transceiverOrder.push(transceiver);
|
|
7034
|
+
this.add = (bundle) => {
|
|
7035
|
+
this.cache.push(bundle);
|
|
7036
|
+
this.transceiverOrder.push(bundle.transceiver);
|
|
6968
7037
|
};
|
|
6969
7038
|
/**
|
|
6970
7039
|
* Gets the transceiver for the given publish option.
|
|
6971
7040
|
*/
|
|
6972
7041
|
this.get = (publishOption) => {
|
|
6973
|
-
return this.
|
|
7042
|
+
return this.cache.find((bundle) => bundle.publishOption.id === publishOption.id &&
|
|
7043
|
+
bundle.publishOption.trackType === publishOption.trackType);
|
|
7044
|
+
};
|
|
7045
|
+
/**
|
|
7046
|
+
* Updates the cached bundle with the given patch.
|
|
7047
|
+
*/
|
|
7048
|
+
this.update = (publishOption, patch) => {
|
|
7049
|
+
const bundle = this.get(publishOption);
|
|
7050
|
+
if (bundle)
|
|
7051
|
+
Object.assign(bundle, patch);
|
|
6974
7052
|
};
|
|
6975
7053
|
/**
|
|
6976
7054
|
* Checks if the cache has the given publish option.
|
|
@@ -7016,10 +7094,6 @@ class TransceiverCache {
|
|
|
7016
7094
|
this.layers.push({ publishOption, layers });
|
|
7017
7095
|
}
|
|
7018
7096
|
};
|
|
7019
|
-
this.findTransceiver = (publishOption) => {
|
|
7020
|
-
return this.cache.find((item) => item.publishOption.id === publishOption.id &&
|
|
7021
|
-
item.publishOption.trackType === publishOption.trackType);
|
|
7022
|
-
};
|
|
7023
7097
|
this.findLayer = (publishOption) => {
|
|
7024
7098
|
return this.layers.find((item) => item.publishOption.id === publishOption.id &&
|
|
7025
7099
|
item.publishOption.trackType === publishOption.trackType);
|
|
@@ -7077,10 +7151,20 @@ const toTrackType = (trackType) => {
|
|
|
7077
7151
|
};
|
|
7078
7152
|
const isAudioTrackType = (trackType) => trackType === TrackType.AUDIO || trackType === TrackType.SCREEN_SHARE_AUDIO;
|
|
7079
7153
|
|
|
7080
|
-
|
|
7081
|
-
|
|
7082
|
-
|
|
7083
|
-
|
|
7154
|
+
/**
|
|
7155
|
+
* Prepares the audio layer for the given track.
|
|
7156
|
+
* Based on the provided audio bitrate profile, we apply the appropriate bitrate.
|
|
7157
|
+
*/
|
|
7158
|
+
const computeAudioLayers = (publishOption, options) => {
|
|
7159
|
+
const { audioBitrateProfile } = options;
|
|
7160
|
+
const profileConfig = publishOption.audioBitrateProfiles?.find((config) => config.profile === audioBitrateProfile);
|
|
7161
|
+
const maxBitrate = profileConfig?.bitrate ||
|
|
7162
|
+
{
|
|
7163
|
+
[AudioBitrateProfile.VOICE_STANDARD_UNSPECIFIED]: 64000,
|
|
7164
|
+
[AudioBitrateProfile.VOICE_HIGH_QUALITY]: 128000,
|
|
7165
|
+
[AudioBitrateProfile.MUSIC_HIGH_QUALITY]: 128000,
|
|
7166
|
+
}[audioBitrateProfile || AudioBitrateProfile.VOICE_STANDARD_UNSPECIFIED];
|
|
7167
|
+
return [{ maxBitrate }];
|
|
7084
7168
|
};
|
|
7085
7169
|
/**
|
|
7086
7170
|
* In SVC, we need to send only one video encoding (layer).
|
|
@@ -7093,7 +7177,7 @@ const toSvcEncodings = (layers) => {
|
|
|
7093
7177
|
if (!layers)
|
|
7094
7178
|
return;
|
|
7095
7179
|
// we take the highest quality layer, and we assign it to `q` encoder.
|
|
7096
|
-
const withRid = (rid) => (
|
|
7180
|
+
const withRid = (rid) => (layer) => layer.rid === rid;
|
|
7097
7181
|
const highestLayer = layers.find(withRid('f')) ||
|
|
7098
7182
|
layers.find(withRid('h')) ||
|
|
7099
7183
|
layers.find(withRid('q'));
|
|
@@ -7147,7 +7231,8 @@ const computeVideoLayers = (videoTrack, publishOption) => {
|
|
|
7147
7231
|
rid,
|
|
7148
7232
|
width: Math.round(width / downscaleFactor),
|
|
7149
7233
|
height: Math.round(height / downscaleFactor),
|
|
7150
|
-
maxBitrate: Math.round(maxBitrate / bitrateFactor) ||
|
|
7234
|
+
maxBitrate: Math.round(maxBitrate / bitrateFactor) ||
|
|
7235
|
+
{ q: 300000, h: 750000, f: 1250000 }[rid],
|
|
7151
7236
|
maxFramerate: fps,
|
|
7152
7237
|
};
|
|
7153
7238
|
if (svcCodec) {
|
|
@@ -7313,8 +7398,9 @@ class Publisher extends BasePeerConnection {
|
|
|
7313
7398
|
*
|
|
7314
7399
|
* @param track the track to publish.
|
|
7315
7400
|
* @param trackType the track type to publish.
|
|
7401
|
+
* @param options the publish options to use.
|
|
7316
7402
|
*/
|
|
7317
|
-
this.publish = async (track, trackType) => {
|
|
7403
|
+
this.publish = async (track, trackType, options = {}) => {
|
|
7318
7404
|
if (!this.publishOptions.some((o) => o.trackType === trackType)) {
|
|
7319
7405
|
throw new Error(`No publish options found for ${TrackType[trackType]}`);
|
|
7320
7406
|
}
|
|
@@ -7324,13 +7410,13 @@ class Publisher extends BasePeerConnection {
|
|
|
7324
7410
|
// create a clone of the track as otherwise the same trackId will
|
|
7325
7411
|
// appear in the SDP in multiple transceivers
|
|
7326
7412
|
const trackToPublish = this.cloneTrack(track);
|
|
7327
|
-
const transceiver = this.transceiverCache.get(publishOption);
|
|
7413
|
+
const { transceiver } = this.transceiverCache.get(publishOption) || {};
|
|
7328
7414
|
if (!transceiver) {
|
|
7329
|
-
await this.addTransceiver(trackToPublish, publishOption);
|
|
7415
|
+
await this.addTransceiver(trackToPublish, publishOption, options);
|
|
7330
7416
|
}
|
|
7331
7417
|
else {
|
|
7332
7418
|
const previousTrack = transceiver.sender.track;
|
|
7333
|
-
await this.updateTransceiver(transceiver, trackToPublish, trackType);
|
|
7419
|
+
await this.updateTransceiver(transceiver, trackToPublish, trackType, options);
|
|
7334
7420
|
if (!isReactNative()) {
|
|
7335
7421
|
this.stopTrack(previousTrack);
|
|
7336
7422
|
}
|
|
@@ -7340,11 +7426,13 @@ class Publisher extends BasePeerConnection {
|
|
|
7340
7426
|
/**
|
|
7341
7427
|
* Adds a new transceiver carrying the given track to the peer connection.
|
|
7342
7428
|
*/
|
|
7343
|
-
this.addTransceiver = async (track, publishOption) => {
|
|
7344
|
-
const
|
|
7429
|
+
this.addTransceiver = async (track, publishOption, options) => {
|
|
7430
|
+
const encodings = isAudioTrackType(publishOption.trackType)
|
|
7431
|
+
? computeAudioLayers(publishOption, options)
|
|
7432
|
+
: computeVideoLayers(track, publishOption);
|
|
7345
7433
|
const sendEncodings = isSvcCodec(publishOption.codec?.name)
|
|
7346
|
-
? toSvcEncodings(
|
|
7347
|
-
:
|
|
7434
|
+
? toSvcEncodings(encodings)
|
|
7435
|
+
: encodings;
|
|
7348
7436
|
const transceiver = this.pc.addTransceiver(track, {
|
|
7349
7437
|
direction: 'sendonly',
|
|
7350
7438
|
sendEncodings,
|
|
@@ -7354,20 +7442,49 @@ class Publisher extends BasePeerConnection {
|
|
|
7354
7442
|
await transceiver.sender.setParameters(params);
|
|
7355
7443
|
const trackType = publishOption.trackType;
|
|
7356
7444
|
this.logger('debug', `Added ${TrackType[trackType]} transceiver`);
|
|
7357
|
-
this.transceiverCache.add(publishOption, transceiver);
|
|
7445
|
+
this.transceiverCache.add({ publishOption, transceiver, options });
|
|
7358
7446
|
this.trackIdToTrackType.set(track.id, trackType);
|
|
7359
7447
|
await this.negotiate();
|
|
7360
7448
|
};
|
|
7361
7449
|
/**
|
|
7362
7450
|
* Updates the transceiver with the given track and track type.
|
|
7363
7451
|
*/
|
|
7364
|
-
this.updateTransceiver = async (transceiver, track, trackType) => {
|
|
7452
|
+
this.updateTransceiver = async (transceiver, track, trackType, options = {}) => {
|
|
7365
7453
|
const sender = transceiver.sender;
|
|
7366
7454
|
if (sender.track)
|
|
7367
7455
|
this.trackIdToTrackType.delete(sender.track.id);
|
|
7368
7456
|
await sender.replaceTrack(track);
|
|
7369
7457
|
if (track)
|
|
7370
7458
|
this.trackIdToTrackType.set(track.id, trackType);
|
|
7459
|
+
if (isAudioTrackType(trackType)) {
|
|
7460
|
+
await this.updateAudioPublishOptions(trackType, options);
|
|
7461
|
+
}
|
|
7462
|
+
};
|
|
7463
|
+
/**
|
|
7464
|
+
* Updates the publish options for the given track type.
|
|
7465
|
+
*/
|
|
7466
|
+
this.updateAudioPublishOptions = async (trackType, options) => {
|
|
7467
|
+
for (const publishOption of this.publishOptions) {
|
|
7468
|
+
if (publishOption.trackType !== trackType)
|
|
7469
|
+
continue;
|
|
7470
|
+
const bundle = this.transceiverCache.get(publishOption);
|
|
7471
|
+
if (!bundle)
|
|
7472
|
+
continue;
|
|
7473
|
+
const { transceiver, options: current } = bundle;
|
|
7474
|
+
if (current.audioBitrateProfile !== options.audioBitrateProfile) {
|
|
7475
|
+
const encodings = computeAudioLayers(publishOption, options);
|
|
7476
|
+
if (encodings && encodings.length > 0) {
|
|
7477
|
+
const params = transceiver.sender.getParameters();
|
|
7478
|
+
const [currentEncoding] = params.encodings;
|
|
7479
|
+
const [targetEncoding] = encodings;
|
|
7480
|
+
if (currentEncoding.maxBitrate !== targetEncoding.maxBitrate) {
|
|
7481
|
+
currentEncoding.maxBitrate = targetEncoding.maxBitrate;
|
|
7482
|
+
}
|
|
7483
|
+
await transceiver.sender.setParameters(params);
|
|
7484
|
+
}
|
|
7485
|
+
}
|
|
7486
|
+
this.transceiverCache.update(publishOption, { options });
|
|
7487
|
+
}
|
|
7371
7488
|
};
|
|
7372
7489
|
/**
|
|
7373
7490
|
* Synchronizes the current Publisher state with the provided publish options.
|
|
@@ -7382,12 +7499,12 @@ class Publisher extends BasePeerConnection {
|
|
|
7382
7499
|
continue;
|
|
7383
7500
|
const item = this.transceiverCache.find((i) => !!i.transceiver.sender.track &&
|
|
7384
7501
|
i.publishOption.trackType === trackType);
|
|
7385
|
-
if (!item
|
|
7502
|
+
if (!item)
|
|
7386
7503
|
continue;
|
|
7387
7504
|
// take the track from the existing transceiver for the same track type,
|
|
7388
7505
|
// clone it and publish it with the new publish options
|
|
7389
7506
|
const track = this.cloneTrack(item.transceiver.sender.track);
|
|
7390
|
-
await this.addTransceiver(track, publishOption);
|
|
7507
|
+
await this.addTransceiver(track, publishOption, item.options);
|
|
7391
7508
|
}
|
|
7392
7509
|
// stop publishing with options not required anymore -> [vp9]
|
|
7393
7510
|
for (const item of this.transceiverCache.items()) {
|
|
@@ -7567,11 +7684,9 @@ class Publisher extends BasePeerConnection {
|
|
|
7567
7684
|
this.getAnnouncedTracks = (sdp) => {
|
|
7568
7685
|
const trackInfos = [];
|
|
7569
7686
|
for (const bundle of this.transceiverCache.items()) {
|
|
7570
|
-
|
|
7571
|
-
const track = transceiver.sender.track;
|
|
7572
|
-
if (!track)
|
|
7687
|
+
if (!bundle.transceiver.sender.track)
|
|
7573
7688
|
continue;
|
|
7574
|
-
trackInfos.push(this.toTrackInfo(
|
|
7689
|
+
trackInfos.push(this.toTrackInfo(bundle, sdp));
|
|
7575
7690
|
}
|
|
7576
7691
|
return trackInfos;
|
|
7577
7692
|
};
|
|
@@ -7584,17 +7699,18 @@ class Publisher extends BasePeerConnection {
|
|
|
7584
7699
|
const sdp = this.pc.localDescription?.sdp;
|
|
7585
7700
|
const trackInfos = [];
|
|
7586
7701
|
for (const publishOption of this.publishOptions) {
|
|
7587
|
-
const
|
|
7588
|
-
if (!
|
|
7702
|
+
const bundle = this.transceiverCache.get(publishOption);
|
|
7703
|
+
if (!bundle || !bundle.transceiver.sender.track)
|
|
7589
7704
|
continue;
|
|
7590
|
-
trackInfos.push(this.toTrackInfo(
|
|
7705
|
+
trackInfos.push(this.toTrackInfo(bundle, sdp));
|
|
7591
7706
|
}
|
|
7592
7707
|
return trackInfos;
|
|
7593
7708
|
};
|
|
7594
7709
|
/**
|
|
7595
7710
|
* Converts the given transceiver to a `TrackInfo` object.
|
|
7596
7711
|
*/
|
|
7597
|
-
this.toTrackInfo = (
|
|
7712
|
+
this.toTrackInfo = (bundle, sdp) => {
|
|
7713
|
+
const { transceiver, publishOption } = bundle;
|
|
7598
7714
|
const track = transceiver.sender.track;
|
|
7599
7715
|
const isTrackLive = track.readyState === 'live';
|
|
7600
7716
|
const layers = isTrackLive
|
|
@@ -7602,15 +7718,16 @@ class Publisher extends BasePeerConnection {
|
|
|
7602
7718
|
: this.transceiverCache.getLayers(publishOption);
|
|
7603
7719
|
this.transceiverCache.setLayers(publishOption, layers);
|
|
7604
7720
|
const isAudioTrack = isAudioTrackType(publishOption.trackType);
|
|
7605
|
-
const isStereo = isAudioTrack && track.getSettings().channelCount === 2;
|
|
7606
7721
|
const transceiverIndex = this.transceiverCache.indexOf(transceiver);
|
|
7607
7722
|
const audioSettings = this.state.settings?.audio;
|
|
7723
|
+
const stereo = publishOption.trackType === TrackType.SCREEN_SHARE_AUDIO ||
|
|
7724
|
+
(isAudioTrack && !!audioSettings?.hifi_audio_enabled);
|
|
7608
7725
|
return {
|
|
7609
7726
|
trackId: track.id,
|
|
7610
7727
|
layers: toVideoLayers(layers),
|
|
7611
7728
|
trackType: publishOption.trackType,
|
|
7612
7729
|
mid: extractMid(transceiver, transceiverIndex, sdp),
|
|
7613
|
-
stereo
|
|
7730
|
+
stereo,
|
|
7614
7731
|
dtx: isAudioTrack && !!audioSettings?.opus_dtx_enabled,
|
|
7615
7732
|
red: isAudioTrack && !!audioSettings?.redundant_coding_enabled,
|
|
7616
7733
|
muted: !isTrackLive,
|
|
@@ -9894,7 +10011,7 @@ function resolveDeviceId(deviceId, kind) {
|
|
|
9894
10011
|
*/
|
|
9895
10012
|
const isMobile = () => /Mobi/i.test(navigator.userAgent);
|
|
9896
10013
|
|
|
9897
|
-
class
|
|
10014
|
+
class DeviceManager {
|
|
9898
10015
|
constructor(call, state, trackType) {
|
|
9899
10016
|
/**
|
|
9900
10017
|
* if true, stops the media stream when call is left
|
|
@@ -10101,8 +10218,8 @@ class InputMediaDeviceManager {
|
|
|
10101
10218
|
}
|
|
10102
10219
|
});
|
|
10103
10220
|
}
|
|
10104
|
-
publishStream(stream) {
|
|
10105
|
-
return this.call.publish(stream, this.trackType);
|
|
10221
|
+
publishStream(stream, options) {
|
|
10222
|
+
return this.call.publish(stream, this.trackType, options);
|
|
10106
10223
|
}
|
|
10107
10224
|
stopPublishStream() {
|
|
10108
10225
|
return this.call.stopPublish(this.trackType);
|
|
@@ -10338,16 +10455,15 @@ class InputMediaDeviceManager {
|
|
|
10338
10455
|
}
|
|
10339
10456
|
}
|
|
10340
10457
|
|
|
10341
|
-
class
|
|
10458
|
+
class DeviceManagerState {
|
|
10342
10459
|
/**
|
|
10343
|
-
* Constructs new InputMediaDeviceManagerState instance.
|
|
10460
|
+
* Constructs a new InputMediaDeviceManagerState instance.
|
|
10344
10461
|
*
|
|
10345
10462
|
* @param disableMode the disable mode to use.
|
|
10346
10463
|
* @param permission the BrowserPermission to use for querying.
|
|
10347
10464
|
* `undefined` means no permission is required.
|
|
10348
10465
|
*/
|
|
10349
|
-
constructor(disableMode
|
|
10350
|
-
this.disableMode = disableMode;
|
|
10466
|
+
constructor(disableMode, permission) {
|
|
10351
10467
|
this.statusSubject = new BehaviorSubject(undefined);
|
|
10352
10468
|
this.optimisticStatusSubject = new BehaviorSubject(undefined);
|
|
10353
10469
|
this.mediaStreamSubject = new BehaviorSubject(undefined);
|
|
@@ -10378,6 +10494,7 @@ class InputMediaDeviceManagerState {
|
|
|
10378
10494
|
* The default constraints for the device.
|
|
10379
10495
|
*/
|
|
10380
10496
|
this.defaultConstraints$ = this.defaultConstraintsSubject.asObservable();
|
|
10497
|
+
this.disableMode = disableMode;
|
|
10381
10498
|
this.hasBrowserPermission$ = permission
|
|
10382
10499
|
? permission.asObservable().pipe(shareReplay(1))
|
|
10383
10500
|
: of(true);
|
|
@@ -10464,10 +10581,15 @@ class InputMediaDeviceManagerState {
|
|
|
10464
10581
|
}
|
|
10465
10582
|
}
|
|
10466
10583
|
|
|
10467
|
-
class CameraManagerState extends
|
|
10584
|
+
class CameraManagerState extends DeviceManagerState {
|
|
10468
10585
|
constructor() {
|
|
10469
10586
|
super('stop-tracks', getVideoBrowserPermission());
|
|
10470
10587
|
this.directionSubject = new BehaviorSubject(undefined);
|
|
10588
|
+
/**
|
|
10589
|
+
* Observable that emits the preferred camera direction
|
|
10590
|
+
* front - means the camera facing the user
|
|
10591
|
+
* back - means the camera facing the environment
|
|
10592
|
+
*/
|
|
10471
10593
|
this.direction$ = this.directionSubject
|
|
10472
10594
|
.asObservable()
|
|
10473
10595
|
.pipe(distinctUntilChanged());
|
|
@@ -10507,7 +10629,7 @@ class CameraManagerState extends InputMediaDeviceManagerState {
|
|
|
10507
10629
|
}
|
|
10508
10630
|
}
|
|
10509
10631
|
|
|
10510
|
-
class CameraManager extends
|
|
10632
|
+
class CameraManager extends DeviceManager {
|
|
10511
10633
|
/**
|
|
10512
10634
|
* Constructs a new CameraManager.
|
|
10513
10635
|
*
|
|
@@ -10653,18 +10775,87 @@ class CameraManager extends InputMediaDeviceManager {
|
|
|
10653
10775
|
}
|
|
10654
10776
|
}
|
|
10655
10777
|
|
|
10656
|
-
|
|
10778
|
+
/**
|
|
10779
|
+
* Base class for High Fidelity enabled Device Managers.
|
|
10780
|
+
*/
|
|
10781
|
+
class AudioDeviceManager extends DeviceManager {
|
|
10782
|
+
/**
|
|
10783
|
+
* Sets the audio bitrate profile and stereo mode.
|
|
10784
|
+
*/
|
|
10785
|
+
async setAudioBitrateProfile(profile) {
|
|
10786
|
+
if (!this.call.state.settings?.audio.hifi_audio_enabled) {
|
|
10787
|
+
throw new Error('High Fidelity audio is not enabled for this call');
|
|
10788
|
+
}
|
|
10789
|
+
this.doSetAudioBitrateProfile(profile);
|
|
10790
|
+
this.state.setAudioBitrateProfile(profile);
|
|
10791
|
+
if (this.enabled) {
|
|
10792
|
+
await this.applySettingsToStream();
|
|
10793
|
+
}
|
|
10794
|
+
}
|
|
10795
|
+
/**
|
|
10796
|
+
* Overrides the default `publishStream` method to inject the audio bitrate profile.
|
|
10797
|
+
*/
|
|
10798
|
+
publishStream(stream, options) {
|
|
10799
|
+
return super.publishStream(stream, {
|
|
10800
|
+
audioBitrateProfile: this.state.audioBitrateProfile,
|
|
10801
|
+
...options,
|
|
10802
|
+
});
|
|
10803
|
+
}
|
|
10804
|
+
}
|
|
10805
|
+
/**
|
|
10806
|
+
* Prepares a new MediaTrackConstraints set based on the provided arguments.
|
|
10807
|
+
*/
|
|
10808
|
+
const createAudioConstraints = (profile) => {
|
|
10809
|
+
const stereo = profile === AudioBitrateProfile.MUSIC_HIGH_QUALITY;
|
|
10810
|
+
return {
|
|
10811
|
+
echoCancellation: !stereo,
|
|
10812
|
+
noiseSuppression: !stereo,
|
|
10813
|
+
autoGainControl: !stereo,
|
|
10814
|
+
channelCount: { ideal: stereo ? 2 : 1 },
|
|
10815
|
+
};
|
|
10816
|
+
};
|
|
10817
|
+
|
|
10818
|
+
/**
|
|
10819
|
+
* Base state class for High Fidelity enabled device managers.
|
|
10820
|
+
*/
|
|
10821
|
+
class AudioDeviceManagerState extends DeviceManagerState {
|
|
10822
|
+
/**
|
|
10823
|
+
* Constructs a new AudioDeviceManagerState instance.
|
|
10824
|
+
*/
|
|
10825
|
+
constructor(disableMode, permission, profile) {
|
|
10826
|
+
super(disableMode, permission);
|
|
10827
|
+
this.audioBitrateProfileSubject = new BehaviorSubject(profile);
|
|
10828
|
+
this.audioBitrateProfile$ = this.audioBitrateProfileSubject
|
|
10829
|
+
.asObservable()
|
|
10830
|
+
.pipe(distinctUntilChanged());
|
|
10831
|
+
}
|
|
10832
|
+
/**
|
|
10833
|
+
* Returns the current audio bitrate profile.
|
|
10834
|
+
*/
|
|
10835
|
+
get audioBitrateProfile() {
|
|
10836
|
+
return getCurrentValue(this.audioBitrateProfile$);
|
|
10837
|
+
}
|
|
10838
|
+
/**
|
|
10839
|
+
* Sets the audio bitrate profile and stereo mode.
|
|
10840
|
+
*/
|
|
10841
|
+
setAudioBitrateProfile(profile) {
|
|
10842
|
+
setCurrentValue(this.audioBitrateProfileSubject, profile);
|
|
10843
|
+
}
|
|
10844
|
+
}
|
|
10845
|
+
|
|
10846
|
+
class MicrophoneManagerState extends AudioDeviceManagerState {
|
|
10657
10847
|
constructor(disableMode) {
|
|
10658
|
-
super(disableMode, getAudioBrowserPermission());
|
|
10848
|
+
super(disableMode, getAudioBrowserPermission(), AudioBitrateProfile.VOICE_STANDARD_UNSPECIFIED);
|
|
10659
10849
|
this.speakingWhileMutedSubject = new BehaviorSubject(false);
|
|
10850
|
+
/**
|
|
10851
|
+
* An Observable that emits `true` if the user's microphone is muted, but they're speaking.
|
|
10852
|
+
*/
|
|
10660
10853
|
this.speakingWhileMuted$ = this.speakingWhileMutedSubject
|
|
10661
10854
|
.asObservable()
|
|
10662
10855
|
.pipe(distinctUntilChanged());
|
|
10663
10856
|
}
|
|
10664
10857
|
/**
|
|
10665
|
-
* `true` if the user's microphone is muted but they'
|
|
10666
|
-
*
|
|
10667
|
-
* This feature is not available in the React Native SDK.
|
|
10858
|
+
* `true` if the user's microphone is muted but they're speaking.
|
|
10668
10859
|
*/
|
|
10669
10860
|
get speakingWhileMuted() {
|
|
10670
10861
|
return getCurrentValue(this.speakingWhileMuted$);
|
|
@@ -10897,7 +11088,7 @@ class RNSpeechDetector {
|
|
|
10897
11088
|
}
|
|
10898
11089
|
}
|
|
10899
11090
|
|
|
10900
|
-
class MicrophoneManager extends
|
|
11091
|
+
class MicrophoneManager extends AudioDeviceManager {
|
|
10901
11092
|
constructor(call, disableMode = 'stop-tracks') {
|
|
10902
11093
|
super(call, new MicrophoneManagerState(disableMode), TrackType.AUDIO);
|
|
10903
11094
|
this.speakingWhileMutedNotificationEnabled = true;
|
|
@@ -11093,6 +11284,21 @@ class MicrophoneManager extends InputMediaDeviceManager {
|
|
|
11093
11284
|
getStream(constraints) {
|
|
11094
11285
|
return getAudioStream(constraints, this.call.tracer);
|
|
11095
11286
|
}
|
|
11287
|
+
doSetAudioBitrateProfile(profile) {
|
|
11288
|
+
this.setDefaultConstraints({
|
|
11289
|
+
...this.state.defaultConstraints,
|
|
11290
|
+
...createAudioConstraints(profile),
|
|
11291
|
+
});
|
|
11292
|
+
if (this.noiseCancellation) {
|
|
11293
|
+
const disableAudioProcessing = profile === AudioBitrateProfile.MUSIC_HIGH_QUALITY;
|
|
11294
|
+
if (disableAudioProcessing) {
|
|
11295
|
+
this.noiseCancellation.disable(); // disable for high quality music mode
|
|
11296
|
+
}
|
|
11297
|
+
else {
|
|
11298
|
+
this.noiseCancellation.enable(); // restore it for other modes if available
|
|
11299
|
+
}
|
|
11300
|
+
}
|
|
11301
|
+
}
|
|
11096
11302
|
async startSpeakingWhileMutedDetection(deviceId) {
|
|
11097
11303
|
await withoutConcurrency(this.soundDetectorConcurrencyTag, async () => {
|
|
11098
11304
|
await this.stopSpeakingWhileMutedDetection();
|
|
@@ -11129,9 +11335,12 @@ class MicrophoneManager extends InputMediaDeviceManager {
|
|
|
11129
11335
|
}
|
|
11130
11336
|
}
|
|
11131
11337
|
|
|
11132
|
-
class ScreenShareState extends
|
|
11338
|
+
class ScreenShareState extends AudioDeviceManagerState {
|
|
11339
|
+
/**
|
|
11340
|
+
* Constructs a new ScreenShareState instance.
|
|
11341
|
+
*/
|
|
11133
11342
|
constructor() {
|
|
11134
|
-
super(
|
|
11343
|
+
super('stop-tracks', undefined, AudioBitrateProfile.MUSIC_HIGH_QUALITY);
|
|
11135
11344
|
this.audioEnabledSubject = new BehaviorSubject(true);
|
|
11136
11345
|
this.settingsSubject = new BehaviorSubject(undefined);
|
|
11137
11346
|
/**
|
|
@@ -11180,7 +11389,7 @@ class ScreenShareState extends InputMediaDeviceManagerState {
|
|
|
11180
11389
|
}
|
|
11181
11390
|
}
|
|
11182
11391
|
|
|
11183
|
-
class ScreenShareManager extends
|
|
11392
|
+
class ScreenShareManager extends AudioDeviceManager {
|
|
11184
11393
|
constructor(call) {
|
|
11185
11394
|
super(call, new ScreenShareState(), TrackType.SCREEN_SHARE);
|
|
11186
11395
|
}
|
|
@@ -11190,6 +11399,7 @@ class ScreenShareManager extends InputMediaDeviceManager {
|
|
|
11190
11399
|
const maybeTargetResolution = settings?.screensharing.target_resolution;
|
|
11191
11400
|
if (maybeTargetResolution) {
|
|
11192
11401
|
this.setDefaultConstraints({
|
|
11402
|
+
...this.state.defaultConstraints,
|
|
11193
11403
|
video: {
|
|
11194
11404
|
width: maybeTargetResolution.width,
|
|
11195
11405
|
height: maybeTargetResolution.height,
|
|
@@ -11246,6 +11456,19 @@ class ScreenShareManager extends InputMediaDeviceManager {
|
|
|
11246
11456
|
}
|
|
11247
11457
|
return stream;
|
|
11248
11458
|
}
|
|
11459
|
+
doSetAudioBitrateProfile(profile) {
|
|
11460
|
+
const { defaultConstraints } = this.state;
|
|
11461
|
+
const baseAudioConstraints = typeof defaultConstraints?.audio !== 'boolean'
|
|
11462
|
+
? defaultConstraints?.audio
|
|
11463
|
+
: null;
|
|
11464
|
+
this.setDefaultConstraints({
|
|
11465
|
+
...defaultConstraints,
|
|
11466
|
+
audio: {
|
|
11467
|
+
...baseAudioConstraints,
|
|
11468
|
+
...createAudioConstraints(profile),
|
|
11469
|
+
},
|
|
11470
|
+
});
|
|
11471
|
+
}
|
|
11249
11472
|
async stopPublishStream() {
|
|
11250
11473
|
return this.call.stopPublish(TrackType.SCREEN_SHARE, TrackType.SCREEN_SHARE_AUDIO);
|
|
11251
11474
|
}
|
|
@@ -11259,19 +11482,27 @@ class ScreenShareManager extends InputMediaDeviceManager {
|
|
|
11259
11482
|
|
|
11260
11483
|
class SpeakerState {
|
|
11261
11484
|
constructor(tracer) {
|
|
11485
|
+
this.tracer = tracer;
|
|
11262
11486
|
this.selectedDeviceSubject = new BehaviorSubject('');
|
|
11263
11487
|
this.volumeSubject = new BehaviorSubject(1);
|
|
11264
11488
|
/**
|
|
11265
11489
|
* [Tells if the browser supports audio output change on 'audio' elements](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/setSinkId).
|
|
11266
11490
|
*/
|
|
11267
11491
|
this.isDeviceSelectionSupported = checkIfAudioOutputChangeSupported();
|
|
11268
|
-
|
|
11492
|
+
/**
|
|
11493
|
+
* An Observable that emits the currently selected device
|
|
11494
|
+
*
|
|
11495
|
+
* Note: this feature is not supported in React Native
|
|
11496
|
+
*/
|
|
11269
11497
|
this.selectedDevice$ = this.selectedDeviceSubject
|
|
11270
11498
|
.asObservable()
|
|
11271
11499
|
.pipe(distinctUntilChanged());
|
|
11272
|
-
|
|
11273
|
-
|
|
11274
|
-
|
|
11500
|
+
/**
|
|
11501
|
+
* An Observable that emits the currently selected volume
|
|
11502
|
+
*
|
|
11503
|
+
* Note: this feature is not supported in React Native
|
|
11504
|
+
*/
|
|
11505
|
+
this.volume$ = this.volumeSubject.asObservable().pipe(distinctUntilChanged());
|
|
11275
11506
|
}
|
|
11276
11507
|
/**
|
|
11277
11508
|
* The currently selected device
|
|
@@ -12596,10 +12827,11 @@ class Call {
|
|
|
12596
12827
|
*
|
|
12597
12828
|
* @param mediaStream the media stream to publish.
|
|
12598
12829
|
* @param trackType the type of the track to announce.
|
|
12830
|
+
* @param options the publish options.
|
|
12599
12831
|
*/
|
|
12600
|
-
this.publish = async (mediaStream, trackType) => {
|
|
12832
|
+
this.publish = async (mediaStream, trackType, options) => {
|
|
12601
12833
|
if (!this.sfuClient)
|
|
12602
|
-
throw new Error(`Call not joined yet
|
|
12834
|
+
throw new Error(`Call is not joined yet`);
|
|
12603
12835
|
// joining is in progress, and we should wait until the client is ready
|
|
12604
12836
|
await this.sfuClient.joinTask;
|
|
12605
12837
|
if (!this.permissionsContext.canPublish(trackType)) {
|
|
@@ -12617,14 +12849,15 @@ class Call {
|
|
|
12617
12849
|
throw new Error(`Can't publish ended tracks.`);
|
|
12618
12850
|
}
|
|
12619
12851
|
pushToIfMissing(this.trackPublishOrder, trackType);
|
|
12620
|
-
await this.publisher.publish(track, trackType);
|
|
12852
|
+
await this.publisher.publish(track, trackType, options);
|
|
12621
12853
|
const trackTypes = [trackType];
|
|
12622
12854
|
if (trackType === TrackType.SCREEN_SHARE) {
|
|
12623
12855
|
const [audioTrack] = mediaStream.getAudioTracks();
|
|
12624
12856
|
if (audioTrack) {
|
|
12625
|
-
|
|
12626
|
-
|
|
12627
|
-
|
|
12857
|
+
const screenShareAudio = TrackType.SCREEN_SHARE_AUDIO;
|
|
12858
|
+
pushToIfMissing(this.trackPublishOrder, screenShareAudio);
|
|
12859
|
+
await this.publisher.publish(audioTrack, screenShareAudio, options);
|
|
12860
|
+
trackTypes.push(screenShareAudio);
|
|
12628
12861
|
}
|
|
12629
12862
|
}
|
|
12630
12863
|
if (track.kind === 'video') {
|
|
@@ -14545,7 +14778,7 @@ class StreamClient {
|
|
|
14545
14778
|
this.getUserAgent = () => {
|
|
14546
14779
|
if (!this.cachedUserAgent) {
|
|
14547
14780
|
const { clientAppIdentifier = {} } = this.options;
|
|
14548
|
-
const { sdkName = 'js', sdkVersion = "1.
|
|
14781
|
+
const { sdkName = 'js', sdkVersion = "1.33.0", ...extras } = clientAppIdentifier;
|
|
14549
14782
|
this.cachedUserAgent = [
|
|
14550
14783
|
`stream-video-${sdkName}-v${sdkVersion}`,
|
|
14551
14784
|
...Object.entries(extras).map(([key, value]) => `${key}=${value}`),
|
|
@@ -15142,5 +15375,5 @@ class StreamVideoClient {
|
|
|
15142
15375
|
}
|
|
15143
15376
|
StreamVideoClient._instances = new Map();
|
|
15144
15377
|
|
|
15145
|
-
export { AudioSettingsRequestDefaultDeviceEnum, AudioSettingsResponseDefaultDeviceEnum, browsers as Browsers, Call, CallState, CallType, CallTypes, CallingState, CameraManager, CameraManagerState, CreateDeviceRequestPushProviderEnum, DebounceType, DynascaleManager, ErrorFromResponse, FrameRecordingSettingsRequestModeEnum, FrameRecordingSettingsRequestQualityEnum, FrameRecordingSettingsResponseModeEnum, IngressAudioEncodingOptionsRequestChannelsEnum, IngressVideoLayerRequestCodecEnum,
|
|
15378
|
+
export { AudioSettingsRequestDefaultDeviceEnum, AudioSettingsResponseDefaultDeviceEnum, browsers as Browsers, Call, CallState, CallType, CallTypes, CallingState, CameraManager, CameraManagerState, CreateDeviceRequestPushProviderEnum, DebounceType, DeviceManager, DeviceManagerState, DynascaleManager, ErrorFromResponse, FrameRecordingSettingsRequestModeEnum, FrameRecordingSettingsRequestQualityEnum, FrameRecordingSettingsResponseModeEnum, IngressAudioEncodingOptionsRequestChannelsEnum, IngressVideoLayerRequestCodecEnum, LayoutSettingsRequestNameEnum, MicrophoneManager, MicrophoneManagerState, NoiseCancellationSettingsModeEnum, OwnCapability, RNSpeechDetector, RTMPBroadcastRequestQualityEnum, RTMPSettingsRequestQualityEnum, RecordSettingsRequestModeEnum, RecordSettingsRequestQualityEnum, rxUtils as RxUtils, ScreenShareManager, ScreenShareState, events as SfuEvents, models as SfuModels, SpeakerManager, SpeakerState, StartClosedCaptionsRequestLanguageEnum, StartTranscriptionRequestLanguageEnum, StreamSfuClient, StreamVideoClient, StreamVideoReadOnlyStateStore, StreamVideoWriteableStateStore, TranscriptionSettingsRequestClosedCaptionModeEnum, TranscriptionSettingsRequestLanguageEnum, TranscriptionSettingsRequestModeEnum, TranscriptionSettingsResponseClosedCaptionModeEnum, TranscriptionSettingsResponseLanguageEnum, TranscriptionSettingsResponseModeEnum, VideoSettingsRequestCameraFacingEnum, VideoSettingsResponseCameraFacingEnum, ViewportTracker, VisibilityState, checkIfAudioOutputChangeSupported, combineComparators, conditional, createSoundDetector, defaultSortPreset, descending, deviceIds$, disposeOfMediaStream, dominantSpeaker, getAudioBrowserPermission, getAudioDevices, getAudioOutputDevices, getAudioStream, getClientDetails, getDeviceState, getLogLevel, getLogger, getScreenShareStream, getSdkInfo, getVideoBrowserPermission, getVideoDevices, getVideoStream, getWebRTCInfo, hasAudio, hasPausedTrack, hasScreenShare, hasScreenShareAudio, hasVideo, isPinned, livestreamOrAudioRoomSortPreset, logLevels, logToConsole, name, noopComparator, paginatedLayoutSortPreset, pinned, publishingAudio, publishingVideo, reactionType, resolveDeviceId, role, screenSharing, setDeviceInfo, setLogLevel, setLogger, setOSInfo, setPowerState, setSdkInfo, setThermalState, setWebRTCInfo, speakerLayoutSortPreset, speaking, withParticipantSource };
|
|
15146
15379
|
//# sourceMappingURL=index.browser.es.js.map
|