@stream-io/video-client 1.32.0 → 1.33.1

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.
Files changed (70) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/index.browser.es.js +344 -91
  3. package/dist/index.browser.es.js.map +1 -1
  4. package/dist/index.cjs.js +345 -92
  5. package/dist/index.cjs.js.map +1 -1
  6. package/dist/index.es.js +344 -91
  7. package/dist/index.es.js.map +1 -1
  8. package/dist/src/Call.d.ts +3 -2
  9. package/dist/src/devices/AudioDeviceManager.d.ts +25 -0
  10. package/dist/src/devices/AudioDeviceManagerState.d.ts +24 -0
  11. package/dist/src/devices/CameraManager.d.ts +2 -2
  12. package/dist/src/devices/CameraManagerState.d.ts +3 -4
  13. package/dist/src/devices/{InputMediaDeviceManager.d.ts → DeviceManager.d.ts} +6 -6
  14. package/dist/src/devices/{InputMediaDeviceManagerState.d.ts → DeviceManagerState.d.ts} +4 -4
  15. package/dist/src/devices/MicrophoneManager.d.ts +5 -3
  16. package/dist/src/devices/MicrophoneManagerState.d.ts +6 -10
  17. package/dist/src/devices/ScreenShareManager.d.ts +4 -2
  18. package/dist/src/devices/ScreenShareState.d.ts +6 -2
  19. package/dist/src/devices/SpeakerState.d.ts +4 -4
  20. package/dist/src/devices/index.d.ts +2 -2
  21. package/dist/src/gen/coordinator/index.d.ts +169 -2
  22. package/dist/src/gen/video/sfu/models/models.d.ts +43 -0
  23. package/dist/src/rtc/BasePeerConnection.d.ts +2 -12
  24. package/dist/src/rtc/Publisher.d.ts +9 -6
  25. package/dist/src/rtc/Subscriber.d.ts +2 -1
  26. package/dist/src/rtc/TransceiverCache.d.ts +10 -11
  27. package/dist/src/rtc/index.d.ts +1 -1
  28. package/dist/src/rtc/{videoLayers.d.ts → layers.d.ts} +7 -1
  29. package/dist/src/rtc/types.d.ts +31 -0
  30. package/dist/src/sorting/participants.d.ts +5 -2
  31. package/package.json +3 -2
  32. package/src/Call.ts +13 -6
  33. package/src/__tests__/Call.publishing.test.ts +14 -3
  34. package/src/__tests__/StreamVideoClient.api.test.ts +1 -1
  35. package/src/devices/AudioDeviceManager.ts +61 -0
  36. package/src/devices/AudioDeviceManagerState.ts +44 -0
  37. package/src/devices/CameraManager.ts +4 -4
  38. package/src/devices/CameraManagerState.ts +9 -8
  39. package/src/devices/{InputMediaDeviceManager.ts → DeviceManager.ts} +11 -8
  40. package/src/devices/{InputMediaDeviceManagerState.ts → DeviceManagerState.ts} +7 -4
  41. package/src/devices/MicrophoneManager.ts +26 -6
  42. package/src/devices/MicrophoneManagerState.ts +18 -19
  43. package/src/devices/ScreenShareManager.ts +23 -4
  44. package/src/devices/ScreenShareState.ts +11 -3
  45. package/src/devices/SpeakerState.ts +6 -14
  46. package/src/devices/__tests__/CameraManager.test.ts +1 -0
  47. package/src/devices/__tests__/{InputMediaDeviceManager.test.ts → DeviceManager.test.ts} +4 -4
  48. package/src/devices/__tests__/{InputMediaDeviceManagerFilters.test.ts → DeviceManagerFilters.test.ts} +4 -4
  49. package/src/devices/__tests__/{InputMediaDeviceManagerState.test.ts → DeviceManagerState.test.ts} +2 -2
  50. package/src/devices/__tests__/MicrophoneManager.test.ts +41 -1
  51. package/src/devices/__tests__/NoiseCancellationStub.ts +3 -1
  52. package/src/devices/__tests__/ScreenShareManager.test.ts +5 -1
  53. package/src/devices/index.ts +2 -2
  54. package/src/events/__tests__/internal.test.ts +25 -11
  55. package/src/gen/coordinator/index.ts +169 -2
  56. package/src/gen/video/sfu/models/models.ts +65 -0
  57. package/src/rtc/BasePeerConnection.ts +1 -16
  58. package/src/rtc/Publisher.ts +74 -31
  59. package/src/rtc/Subscriber.ts +2 -4
  60. package/src/rtc/TransceiverCache.ts +23 -27
  61. package/src/rtc/__tests__/Publisher.test.ts +61 -29
  62. package/src/rtc/__tests__/{videoLayers.test.ts → layers.test.ts} +76 -1
  63. package/src/rtc/index.ts +2 -1
  64. package/src/rtc/{videoLayers.ts → layers.ts} +28 -7
  65. package/src/rtc/types.ts +44 -0
  66. package/src/sorting/__tests__/sorting.test.ts +106 -0
  67. package/src/sorting/participants.ts +30 -14
  68. package/src/sorting/presets.ts +16 -4
  69. package/src/store/CallState.ts +36 -10
  70. package/src/store/__tests__/CallState.test.ts +20 -2
@@ -86,6 +86,12 @@ export interface AudioSettingsRequest {
86
86
  * @memberof AudioSettingsRequest
87
87
  */
88
88
  default_device: AudioSettingsRequestDefaultDeviceEnum;
89
+ /**
90
+ *
91
+ * @type {boolean}
92
+ * @memberof AudioSettingsRequest
93
+ */
94
+ hifi_audio_enabled?: boolean;
89
95
  /**
90
96
  *
91
97
  * @type {boolean}
@@ -146,6 +152,12 @@ export interface AudioSettingsResponse {
146
152
  * @memberof AudioSettingsResponse
147
153
  */
148
154
  default_device: AudioSettingsResponseDefaultDeviceEnum;
155
+ /**
156
+ *
157
+ * @type {boolean}
158
+ * @memberof AudioSettingsResponse
159
+ */
160
+ hifi_audio_enabled?: boolean;
149
161
  /**
150
162
  *
151
163
  * @type {boolean}
@@ -408,6 +420,24 @@ export interface CallClosedCaption {
408
420
  * @memberof CallClosedCaption
409
421
  */
410
422
  end_time: string;
423
+ /**
424
+ *
425
+ * @type {string}
426
+ * @memberof CallClosedCaption
427
+ */
428
+ id: string;
429
+ /**
430
+ *
431
+ * @type {string}
432
+ * @memberof CallClosedCaption
433
+ */
434
+ language: string;
435
+ /**
436
+ *
437
+ * @type {string}
438
+ * @memberof CallClosedCaption
439
+ */
440
+ service?: string;
411
441
  /**
412
442
  *
413
443
  * @type {string}
@@ -426,6 +456,12 @@ export interface CallClosedCaption {
426
456
  * @memberof CallClosedCaption
427
457
  */
428
458
  text: string;
459
+ /**
460
+ *
461
+ * @type {boolean}
462
+ * @memberof CallClosedCaption
463
+ */
464
+ translated: boolean;
429
465
  /**
430
466
  *
431
467
  * @type {UserResponse}
@@ -917,6 +953,18 @@ export interface CallIngressResponse {
917
953
  * @memberof CallIngressResponse
918
954
  */
919
955
  rtmp: RTMPIngress;
956
+ /**
957
+ *
958
+ * @type {SRTIngress}
959
+ * @memberof CallIngressResponse
960
+ */
961
+ srt: SRTIngress;
962
+ /**
963
+ *
964
+ * @type {WHIPIngress}
965
+ * @memberof CallIngressResponse
966
+ */
967
+ whip: WHIPIngress;
920
968
  }
921
969
  /**
922
970
  * This event is sent when a call is started. Clients receiving this event should start the call.
@@ -1791,6 +1839,12 @@ export interface CallResponse {
1791
1839
  * @memberof CallResponse
1792
1840
  */
1793
1841
  transcribing: boolean;
1842
+ /**
1843
+ *
1844
+ * @type {boolean}
1845
+ * @memberof CallResponse
1846
+ */
1847
+ translating: boolean;
1794
1848
  /**
1795
1849
  * The type of call
1796
1850
  * @type {string}
@@ -2099,6 +2153,12 @@ export interface CallSessionParticipantLeftEvent {
2099
2153
  * @memberof CallSessionParticipantLeftEvent
2100
2154
  */
2101
2155
  participant: CallParticipantResponse;
2156
+ /**
2157
+ * The reason why the participant left the session
2158
+ * @type {string}
2159
+ * @memberof CallSessionParticipantLeftEvent
2160
+ */
2161
+ reason?: string;
2102
2162
  /**
2103
2163
  * Call session ID
2104
2164
  * @type {string}
@@ -2732,7 +2792,7 @@ export interface CallUpdatedEvent {
2732
2792
  */
2733
2793
  created_at: string;
2734
2794
  /**
2735
- * The type of event: "call.ended" in this case
2795
+ * The type of event: "call.updated" in this case
2736
2796
  * @type {string}
2737
2797
  * @memberof CallUpdatedEvent
2738
2798
  */
@@ -2835,6 +2895,12 @@ export interface CallUserMutedEvent {
2835
2895
  * @memberof CallUserMutedEvent
2836
2896
  */
2837
2897
  muted_user_ids: Array<string>;
2898
+ /**
2899
+ *
2900
+ * @type {string}
2901
+ * @memberof CallUserMutedEvent
2902
+ */
2903
+ reason: string;
2838
2904
  /**
2839
2905
  * The type of event: "call.user_muted" in this case
2840
2906
  * @type {string}
@@ -3787,6 +3853,12 @@ export interface GetCallReportResponse {
3787
3853
  * @memberof GetCallReportResponse
3788
3854
  */
3789
3855
  report: ReportResponse;
3856
+ /**
3857
+ *
3858
+ * @type {CallSessionResponse}
3859
+ * @memberof GetCallReportResponse
3860
+ */
3861
+ session?: CallSessionResponse;
3790
3862
  /**
3791
3863
  *
3792
3864
  * @type {string}
@@ -4928,6 +5000,7 @@ export const OwnCapability = {
4928
5000
  REMOVE_CALL_MEMBER: 'remove-call-member',
4929
5001
  SCREENSHARE: 'screenshare',
4930
5002
  SEND_AUDIO: 'send-audio',
5003
+ SEND_CLOSED_CAPTIONS_CALL: 'send-closed-captions-call',
4931
5004
  SEND_VIDEO: 'send-video',
4932
5005
  START_BROADCAST_CALL: 'start-broadcast-call',
4933
5006
  START_CLOSED_CAPTIONS_CALL: 'start-closed-captions-call',
@@ -6184,6 +6257,19 @@ export interface SFUResponse {
6184
6257
  */
6185
6258
  ws_endpoint: string;
6186
6259
  }
6260
+ /**
6261
+ *
6262
+ * @export
6263
+ * @interface SRTIngress
6264
+ */
6265
+ export interface SRTIngress {
6266
+ /**
6267
+ *
6268
+ * @type {string}
6269
+ * @memberof SRTIngress
6270
+ */
6271
+ address: string;
6272
+ }
6187
6273
  /**
6188
6274
  *
6189
6275
  * @export
@@ -6349,6 +6435,25 @@ export interface SortParamRequest {
6349
6435
  */
6350
6436
  field?: string;
6351
6437
  }
6438
+ /**
6439
+ *
6440
+ * @export
6441
+ * @interface SpeechSegmentConfig
6442
+ */
6443
+ export interface SpeechSegmentConfig {
6444
+ /**
6445
+ *
6446
+ * @type {number}
6447
+ * @memberof SpeechSegmentConfig
6448
+ */
6449
+ max_speech_caption_ms?: number;
6450
+ /**
6451
+ *
6452
+ * @type {number}
6453
+ * @memberof SpeechSegmentConfig
6454
+ */
6455
+ silence_duration_ms?: number;
6456
+ }
6352
6457
  /**
6353
6458
  *
6354
6459
  * @export
@@ -6373,6 +6478,12 @@ export interface StartClosedCaptionsRequest {
6373
6478
  * @memberof StartClosedCaptionsRequest
6374
6479
  */
6375
6480
  language?: StartClosedCaptionsRequestLanguageEnum;
6481
+ /**
6482
+ *
6483
+ * @type {SpeechSegmentConfig}
6484
+ * @memberof StartClosedCaptionsRequest
6485
+ */
6486
+ speech_segment_config?: SpeechSegmentConfig;
6376
6487
  }
6377
6488
 
6378
6489
  /**
@@ -6937,7 +7048,19 @@ export interface TranscriptionSettingsRequest {
6937
7048
  * @type {string}
6938
7049
  * @memberof TranscriptionSettingsRequest
6939
7050
  */
6940
- mode: TranscriptionSettingsRequestModeEnum;
7051
+ mode?: TranscriptionSettingsRequestModeEnum;
7052
+ /**
7053
+ *
7054
+ * @type {SpeechSegmentConfig}
7055
+ * @memberof TranscriptionSettingsRequest
7056
+ */
7057
+ speech_segment_config?: SpeechSegmentConfig;
7058
+ /**
7059
+ *
7060
+ * @type {TranslationSettings}
7061
+ * @memberof TranscriptionSettingsRequest
7062
+ */
7063
+ translation?: TranslationSettings;
6941
7064
  }
6942
7065
 
6943
7066
  /**
@@ -7031,6 +7154,18 @@ export interface TranscriptionSettingsResponse {
7031
7154
  * @memberof TranscriptionSettingsResponse
7032
7155
  */
7033
7156
  mode: TranscriptionSettingsResponseModeEnum;
7157
+ /**
7158
+ *
7159
+ * @type {SpeechSegmentConfig}
7160
+ * @memberof TranscriptionSettingsResponse
7161
+ */
7162
+ speech_segment_config?: SpeechSegmentConfig;
7163
+ /**
7164
+ *
7165
+ * @type {TranslationSettings}
7166
+ * @memberof TranscriptionSettingsResponse
7167
+ */
7168
+ translation?: TranslationSettings;
7034
7169
  }
7035
7170
 
7036
7171
  /**
@@ -7100,6 +7235,25 @@ export const TranscriptionSettingsResponseModeEnum = {
7100
7235
  export type TranscriptionSettingsResponseModeEnum =
7101
7236
  (typeof TranscriptionSettingsResponseModeEnum)[keyof typeof TranscriptionSettingsResponseModeEnum];
7102
7237
 
7238
+ /**
7239
+ *
7240
+ * @export
7241
+ * @interface TranslationSettings
7242
+ */
7243
+ export interface TranslationSettings {
7244
+ /**
7245
+ *
7246
+ * @type {boolean}
7247
+ * @memberof TranslationSettings
7248
+ */
7249
+ enabled?: boolean;
7250
+ /**
7251
+ *
7252
+ * @type {Array<string>}
7253
+ * @memberof TranslationSettings
7254
+ */
7255
+ languages?: Array<string>;
7256
+ }
7103
7257
  /**
7104
7258
  * UnblockUserRequest is the payload for unblocking a user.
7105
7259
  * @export
@@ -7925,6 +8079,19 @@ export const VideoSettingsResponseCameraFacingEnum = {
7925
8079
  export type VideoSettingsResponseCameraFacingEnum =
7926
8080
  (typeof VideoSettingsResponseCameraFacingEnum)[keyof typeof VideoSettingsResponseCameraFacingEnum];
7927
8081
 
8082
+ /**
8083
+ *
8084
+ * @export
8085
+ * @interface WHIPIngress
8086
+ */
8087
+ export interface WHIPIngress {
8088
+ /**
8089
+ * URL for a new whip input, every time a new link is created
8090
+ * @type {string}
8091
+ * @memberof WHIPIngress
8092
+ */
8093
+ address: string;
8094
+ }
7928
8095
  /**
7929
8096
  * Websocket auth message
7930
8097
  * @export
@@ -301,6 +301,12 @@ export interface PublishOption {
301
301
  * @generated from protobuf field: bool use_single_layer = 9;
302
302
  */
303
303
  useSingleLayer: boolean;
304
+ /**
305
+ * Audio bitrate profiles for different audio quality profiles.
306
+ *
307
+ * @generated from protobuf field: repeated stream.video.sfu.models.AudioBitrate audio_bitrate_profiles = 10;
308
+ */
309
+ audioBitrateProfiles: AudioBitrate[];
304
310
  }
305
311
  /**
306
312
  * @generated from protobuf message stream.video.sfu.models.Codec
@@ -344,6 +350,19 @@ export interface ICETrickle {
344
350
  */
345
351
  sessionId: string;
346
352
  }
353
+ /**
354
+ * @generated from protobuf message stream.video.sfu.models.AudioBitrate
355
+ */
356
+ export interface AudioBitrate {
357
+ /**
358
+ * @generated from protobuf field: stream.video.sfu.models.AudioBitrateProfile profile = 1;
359
+ */
360
+ profile: AudioBitrateProfile;
361
+ /**
362
+ * @generated from protobuf field: int32 bitrate = 2;
363
+ */
364
+ bitrate: number;
365
+ }
347
366
  /**
348
367
  * @generated from protobuf message stream.video.sfu.models.TrackInfo
349
368
  */
@@ -794,6 +813,23 @@ export enum ParticipantSource {
794
813
  */
795
814
  SRT = 5,
796
815
  }
816
+ /**
817
+ * @generated from protobuf enum stream.video.sfu.models.AudioBitrateProfile
818
+ */
819
+ export enum AudioBitrateProfile {
820
+ /**
821
+ * @generated from protobuf enum value: AUDIO_BITRATE_PROFILE_VOICE_STANDARD_UNSPECIFIED = 0;
822
+ */
823
+ VOICE_STANDARD_UNSPECIFIED = 0,
824
+ /**
825
+ * @generated from protobuf enum value: AUDIO_BITRATE_PROFILE_VOICE_HIGH_QUALITY = 1;
826
+ */
827
+ VOICE_HIGH_QUALITY = 1,
828
+ /**
829
+ * @generated from protobuf enum value: AUDIO_BITRATE_PROFILE_MUSIC_HIGH_QUALITY = 2;
830
+ */
831
+ MUSIC_HIGH_QUALITY = 2,
832
+ }
797
833
  /**
798
834
  * @generated from protobuf enum stream.video.sfu.models.ErrorCode
799
835
  */
@@ -1398,6 +1434,13 @@ class PublishOption$Type extends MessageType<PublishOption> {
1398
1434
  kind: 'scalar',
1399
1435
  T: 8 /*ScalarType.BOOL*/,
1400
1436
  },
1437
+ {
1438
+ no: 10,
1439
+ name: 'audio_bitrate_profiles',
1440
+ kind: 'message',
1441
+ repeat: 2 /*RepeatType.UNPACKED*/,
1442
+ T: () => AudioBitrate,
1443
+ },
1401
1444
  ]);
1402
1445
  }
1403
1446
  }
@@ -1461,6 +1504,28 @@ class ICETrickle$Type extends MessageType<ICETrickle> {
1461
1504
  */
1462
1505
  export const ICETrickle = new ICETrickle$Type();
1463
1506
  // @generated message type with reflection information, may provide speed optimized methods
1507
+ class AudioBitrate$Type extends MessageType<AudioBitrate> {
1508
+ constructor() {
1509
+ super('stream.video.sfu.models.AudioBitrate', [
1510
+ {
1511
+ no: 1,
1512
+ name: 'profile',
1513
+ kind: 'enum',
1514
+ T: () => [
1515
+ 'stream.video.sfu.models.AudioBitrateProfile',
1516
+ AudioBitrateProfile,
1517
+ 'AUDIO_BITRATE_PROFILE_',
1518
+ ],
1519
+ },
1520
+ { no: 2, name: 'bitrate', kind: 'scalar', T: 5 /*ScalarType.INT32*/ },
1521
+ ]);
1522
+ }
1523
+ }
1524
+ /**
1525
+ * @generated MessageType for protobuf message stream.video.sfu.models.AudioBitrate
1526
+ */
1527
+ export const AudioBitrate = new AudioBitrate$Type();
1528
+ // @generated message type with reflection information, may provide speed optimized methods
1464
1529
  class TrackInfo$Type extends MessageType<TrackInfo> {
1465
1530
  constructor() {
1466
1531
  super('stream.video.sfu.models.TrackInfo', [
@@ -16,22 +16,7 @@ import { StreamSfuClient } from '../StreamSfuClient';
16
16
  import { AllSfuEvents, Dispatcher } from './Dispatcher';
17
17
  import { withoutConcurrency } from '../helpers/concurrency';
18
18
  import { StatsTracer, Tracer, traceRTCPeerConnection } from '../stats';
19
-
20
- export type OnReconnectionNeeded = (
21
- kind: WebsocketReconnectStrategy,
22
- reason: string,
23
- ) => void;
24
-
25
- export type BasePeerConnectionOpts = {
26
- sfuClient: StreamSfuClient;
27
- state: CallState;
28
- connectionConfig?: RTCConfiguration;
29
- dispatcher: Dispatcher;
30
- onReconnectionNeeded?: OnReconnectionNeeded;
31
- tag: string;
32
- enableTracing: boolean;
33
- iceRestartDelay?: number;
34
- };
19
+ import { BasePeerConnectionOpts, OnReconnectionNeeded } from './types';
35
20
 
36
21
  /**
37
22
  * A base class for the `Publisher` and `Subscriber` classes.
@@ -1,7 +1,9 @@
1
+ import { BasePeerConnection } from './BasePeerConnection';
1
2
  import {
2
- BasePeerConnection,
3
- BasePeerConnectionOpts,
4
- } from './BasePeerConnection';
3
+ PublishBundle,
4
+ PublisherConstructorOpts,
5
+ TrackPublishOptions,
6
+ } from './types';
5
7
  import { NegotiationError } from './NegotiationError';
6
8
  import { TransceiverCache } from './TransceiverCache';
7
9
  import {
@@ -12,20 +14,17 @@ import {
12
14
  } from '../gen/video/sfu/models/models';
13
15
  import { VideoSender } from '../gen/video/sfu/event/events';
14
16
  import {
17
+ computeAudioLayers,
15
18
  computeVideoLayers,
16
19
  toSvcEncodings,
17
20
  toVideoLayers,
18
- } from './videoLayers';
21
+ } from './layers';
19
22
  import { isSvcCodec } from './codecs';
20
23
  import { isAudioTrackType } from './helpers/tracks';
21
24
  import { extractMid } from './helpers/sdp';
22
25
  import { withoutConcurrency } from '../helpers/concurrency';
23
26
  import { isReactNative } from '../helpers/platforms';
24
27
 
25
- export type PublisherConstructorOpts = BasePeerConnectionOpts & {
26
- publishOptions: PublishOption[];
27
- };
28
-
29
28
  /**
30
29
  * The `Publisher` is responsible for publishing/unpublishing media streams to/from the SFU
31
30
  *
@@ -77,8 +76,13 @@ export class Publisher extends BasePeerConnection {
77
76
  *
78
77
  * @param track the track to publish.
79
78
  * @param trackType the track type to publish.
79
+ * @param options the publish options to use.
80
80
  */
81
- publish = async (track: MediaStreamTrack, trackType: TrackType) => {
81
+ publish = async (
82
+ track: MediaStreamTrack,
83
+ trackType: TrackType,
84
+ options: TrackPublishOptions = {},
85
+ ) => {
82
86
  if (!this.publishOptions.some((o) => o.trackType === trackType)) {
83
87
  throw new Error(`No publish options found for ${TrackType[trackType]}`);
84
88
  }
@@ -90,12 +94,17 @@ export class Publisher extends BasePeerConnection {
90
94
  // appear in the SDP in multiple transceivers
91
95
  const trackToPublish = this.cloneTrack(track);
92
96
 
93
- const transceiver = this.transceiverCache.get(publishOption);
97
+ const { transceiver } = this.transceiverCache.get(publishOption) || {};
94
98
  if (!transceiver) {
95
- await this.addTransceiver(trackToPublish, publishOption);
99
+ await this.addTransceiver(trackToPublish, publishOption, options);
96
100
  } else {
97
101
  const previousTrack = transceiver.sender.track;
98
- await this.updateTransceiver(transceiver, trackToPublish, trackType);
102
+ await this.updateTransceiver(
103
+ transceiver,
104
+ trackToPublish,
105
+ trackType,
106
+ options,
107
+ );
99
108
  if (!isReactNative()) {
100
109
  this.stopTrack(previousTrack);
101
110
  }
@@ -109,11 +118,14 @@ export class Publisher extends BasePeerConnection {
109
118
  private addTransceiver = async (
110
119
  track: MediaStreamTrack,
111
120
  publishOption: PublishOption,
121
+ options: TrackPublishOptions,
112
122
  ) => {
113
- const videoEncodings = computeVideoLayers(track, publishOption);
123
+ const encodings = isAudioTrackType(publishOption.trackType)
124
+ ? computeAudioLayers(publishOption, options)
125
+ : computeVideoLayers(track, publishOption);
114
126
  const sendEncodings = isSvcCodec(publishOption.codec?.name)
115
- ? toSvcEncodings(videoEncodings)
116
- : videoEncodings;
127
+ ? toSvcEncodings(encodings)
128
+ : encodings;
117
129
  const transceiver = this.pc.addTransceiver(track, {
118
130
  direction: 'sendonly',
119
131
  sendEncodings,
@@ -125,7 +137,7 @@ export class Publisher extends BasePeerConnection {
125
137
 
126
138
  const trackType = publishOption.trackType;
127
139
  this.logger('debug', `Added ${TrackType[trackType]} transceiver`);
128
- this.transceiverCache.add(publishOption, transceiver);
140
+ this.transceiverCache.add({ publishOption, transceiver, options });
129
141
  this.trackIdToTrackType.set(track.id, trackType);
130
142
 
131
143
  await this.negotiate();
@@ -138,11 +150,44 @@ export class Publisher extends BasePeerConnection {
138
150
  transceiver: RTCRtpTransceiver,
139
151
  track: MediaStreamTrack | null,
140
152
  trackType: TrackType,
153
+ options: TrackPublishOptions = {},
141
154
  ) => {
142
155
  const sender = transceiver.sender;
143
156
  if (sender.track) this.trackIdToTrackType.delete(sender.track.id);
144
157
  await sender.replaceTrack(track);
145
158
  if (track) this.trackIdToTrackType.set(track.id, trackType);
159
+ if (isAudioTrackType(trackType)) {
160
+ await this.updateAudioPublishOptions(trackType, options);
161
+ }
162
+ };
163
+
164
+ /**
165
+ * Updates the publish options for the given track type.
166
+ */
167
+ private updateAudioPublishOptions = async (
168
+ trackType: TrackType,
169
+ options: TrackPublishOptions,
170
+ ) => {
171
+ for (const publishOption of this.publishOptions) {
172
+ if (publishOption.trackType !== trackType) continue;
173
+ const bundle = this.transceiverCache.get(publishOption);
174
+ if (!bundle) continue;
175
+
176
+ const { transceiver, options: current } = bundle;
177
+ if (current.audioBitrateProfile !== options.audioBitrateProfile) {
178
+ const encodings = computeAudioLayers(publishOption, options);
179
+ if (encodings && encodings.length > 0) {
180
+ const params = transceiver.sender.getParameters();
181
+ const [currentEncoding] = params.encodings;
182
+ const [targetEncoding] = encodings;
183
+ if (currentEncoding.maxBitrate !== targetEncoding.maxBitrate) {
184
+ currentEncoding.maxBitrate = targetEncoding.maxBitrate;
185
+ }
186
+ await transceiver.sender.setParameters(params);
187
+ }
188
+ }
189
+ this.transceiverCache.update(publishOption, { options });
190
+ }
146
191
  };
147
192
 
148
193
  /**
@@ -160,12 +205,12 @@ export class Publisher extends BasePeerConnection {
160
205
  !!i.transceiver.sender.track &&
161
206
  i.publishOption.trackType === trackType,
162
207
  );
163
- if (!item || !item.transceiver) continue;
208
+ if (!item) continue;
164
209
 
165
210
  // take the track from the existing transceiver for the same track type,
166
211
  // clone it and publish it with the new publish options
167
212
  const track = this.cloneTrack(item.transceiver.sender.track!);
168
- await this.addTransceiver(track, publishOption);
213
+ await this.addTransceiver(track, publishOption, item.options);
169
214
  }
170
215
 
171
216
  // stop publishing with options not required anymore -> [vp9]
@@ -372,11 +417,8 @@ export class Publisher extends BasePeerConnection {
372
417
  getAnnouncedTracks = (sdp: string | undefined): TrackInfo[] => {
373
418
  const trackInfos: TrackInfo[] = [];
374
419
  for (const bundle of this.transceiverCache.items()) {
375
- const { transceiver, publishOption } = bundle;
376
- const track = transceiver.sender.track;
377
- if (!track) continue;
378
-
379
- trackInfos.push(this.toTrackInfo(transceiver, publishOption, sdp));
420
+ if (!bundle.transceiver.sender.track) continue;
421
+ trackInfos.push(this.toTrackInfo(bundle, sdp));
380
422
  }
381
423
  return trackInfos;
382
424
  };
@@ -390,10 +432,9 @@ export class Publisher extends BasePeerConnection {
390
432
  const sdp = this.pc.localDescription?.sdp;
391
433
  const trackInfos: TrackInfo[] = [];
392
434
  for (const publishOption of this.publishOptions) {
393
- const transceiver = this.transceiverCache.get(publishOption);
394
- if (!transceiver || !transceiver.sender.track) continue;
395
-
396
- trackInfos.push(this.toTrackInfo(transceiver, publishOption, sdp));
435
+ const bundle = this.transceiverCache.get(publishOption);
436
+ if (!bundle || !bundle.transceiver.sender.track) continue;
437
+ trackInfos.push(this.toTrackInfo(bundle, sdp));
397
438
  }
398
439
  return trackInfos;
399
440
  };
@@ -402,10 +443,10 @@ export class Publisher extends BasePeerConnection {
402
443
  * Converts the given transceiver to a `TrackInfo` object.
403
444
  */
404
445
  private toTrackInfo = (
405
- transceiver: RTCRtpTransceiver,
406
- publishOption: PublishOption,
446
+ bundle: PublishBundle,
407
447
  sdp: string | undefined,
408
448
  ): TrackInfo => {
449
+ const { transceiver, publishOption } = bundle;
409
450
  const track = transceiver.sender.track!;
410
451
  const isTrackLive = track.readyState === 'live';
411
452
  const layers = isTrackLive
@@ -414,16 +455,18 @@ export class Publisher extends BasePeerConnection {
414
455
  this.transceiverCache.setLayers(publishOption, layers);
415
456
 
416
457
  const isAudioTrack = isAudioTrackType(publishOption.trackType);
417
- const isStereo = isAudioTrack && track.getSettings().channelCount === 2;
418
458
  const transceiverIndex = this.transceiverCache.indexOf(transceiver);
419
459
  const audioSettings = this.state.settings?.audio;
460
+ const stereo =
461
+ publishOption.trackType === TrackType.SCREEN_SHARE_AUDIO ||
462
+ (isAudioTrack && !!audioSettings?.hifi_audio_enabled);
420
463
 
421
464
  return {
422
465
  trackId: track.id,
423
466
  layers: toVideoLayers(layers),
424
467
  trackType: publishOption.trackType,
425
468
  mid: extractMid(transceiver, transceiverIndex, sdp),
426
- stereo: isStereo,
469
+ stereo,
427
470
  dtx: isAudioTrack && !!audioSettings?.opus_dtx_enabled,
428
471
  red: isAudioTrack && !!audioSettings?.redundant_coding_enabled,
429
472
  muted: !isTrackLive,
@@ -1,7 +1,5 @@
1
- import {
2
- BasePeerConnection,
3
- BasePeerConnectionOpts,
4
- } from './BasePeerConnection';
1
+ import { BasePeerConnection } from './BasePeerConnection';
2
+ import { BasePeerConnectionOpts } from './types';
5
3
  import { NegotiationError } from './NegotiationError';
6
4
  import { PeerType } from '../gen/video/sfu/models/models';
7
5
  import { SubscriberOffer } from '../gen/video/sfu/event/events';