Skip to content

Commit 48a5e3b

Browse files
lukasIOtheomonnom
andauthored
Add support for capture options on set-enabled APIs (#251)
* add support for respective capture options on set-enabled APIs * add changeset * fix audiocapture options * start publishing multiple tracks simultaneously * typo * make change a minor version change Co-authored-by: Théo Monnom <[email protected]>
1 parent cdc7e5c commit 48a5e3b

File tree

3 files changed

+71
-17
lines changed

3 files changed

+71
-17
lines changed

.changeset/honest-pears-retire.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'livekit-client': minor
3+
---
4+
5+
Add optional CaptureOptions for set-enabled methods

example/sample.ts

+10-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
VideoCodec,
2020
VideoQuality,
2121
RemoteVideoTrack,
22+
RemoteTrackPublication,
2223
LogLevel,
2324
} from '../src/index';
2425

@@ -238,7 +239,7 @@ const appActions = {
238239
const enabled = currentRoom.localParticipant.isScreenShareEnabled;
239240
appendLog(`${enabled ? 'stopping' : 'starting'} screen share`);
240241
setButtonDisabled('share-screen-button', true);
241-
await currentRoom.localParticipant.setScreenShareEnabled(!enabled);
242+
await currentRoom.localParticipant.setScreenShareEnabled(!enabled, { audio: true });
242243
setButtonDisabled('share-screen-button', false);
243244
updateButtonsForPublishState();
244245
},
@@ -585,6 +586,7 @@ function renderScreenShare() {
585586
let screenSharePub: TrackPublication | undefined = currentRoom.localParticipant.getTrack(
586587
Track.Source.ScreenShare,
587588
);
589+
let screenShareAudioPub: RemoteTrackPublication | undefined;
588590
if (!screenSharePub) {
589591
currentRoom.participants.forEach((p) => {
590592
if (screenSharePub) {
@@ -595,6 +597,10 @@ function renderScreenShare() {
595597
if (pub?.isSubscribed) {
596598
screenSharePub = pub;
597599
}
600+
const audioPub = p.getTrack(Track.Source.ScreenShareAudio);
601+
if (audioPub?.isSubscribed) {
602+
screenShareAudioPub = audioPub;
603+
}
598604
});
599605
} else {
600606
participant = currentRoom.localParticipant;
@@ -604,6 +610,9 @@ function renderScreenShare() {
604610
div.style.display = 'block';
605611
const videoElm = <HTMLVideoElement>$('screenshare-video');
606612
screenSharePub.videoTrack?.attach(videoElm);
613+
if (screenShareAudioPub) {
614+
screenShareAudioPub.audioTrack?.attach(videoElm);
615+
}
607616
videoElm.onresize = () => {
608617
updateVideoSize(videoElm, <HTMLSpanElement>$('screenshare-resolution'));
609618
};

src/room/participant/LocalParticipant.ts

+56-16
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,12 @@ import LocalVideoTrack, {
2020
videoLayersFromEncodings,
2121
} from '../track/LocalVideoTrack';
2222
import {
23+
AudioCaptureOptions,
2324
CreateLocalTracksOptions,
2425
ScreenShareCaptureOptions,
2526
ScreenSharePresets,
2627
TrackPublishOptions,
28+
VideoCaptureOptions,
2729
VideoCodec,
2830
} from '../track/options';
2931
import { Track } from '../track/Track';
@@ -117,8 +119,11 @@ export default class LocalParticipant extends Participant {
117119
* If a track has already published, it'll mute or unmute the track.
118120
* Resolves with a `LocalTrackPublication` instance if successful and `undefined` otherwise
119121
*/
120-
setCameraEnabled(enabled: boolean): Promise<LocalTrackPublication | undefined> {
121-
return this.setTrackEnabled(Track.Source.Camera, enabled);
122+
setCameraEnabled(
123+
enabled: boolean,
124+
options?: VideoCaptureOptions,
125+
): Promise<LocalTrackPublication | undefined> {
126+
return this.setTrackEnabled(Track.Source.Camera, enabled, options);
122127
}
123128

124129
/**
@@ -127,16 +132,22 @@ export default class LocalParticipant extends Participant {
127132
* If a track has already published, it'll mute or unmute the track.
128133
* Resolves with a `LocalTrackPublication` instance if successful and `undefined` otherwise
129134
*/
130-
setMicrophoneEnabled(enabled: boolean): Promise<LocalTrackPublication | undefined> {
131-
return this.setTrackEnabled(Track.Source.Microphone, enabled);
135+
setMicrophoneEnabled(
136+
enabled: boolean,
137+
options?: AudioCaptureOptions,
138+
): Promise<LocalTrackPublication | undefined> {
139+
return this.setTrackEnabled(Track.Source.Microphone, enabled, options);
132140
}
133141

134142
/**
135143
* Start or stop sharing a participant's screen
136144
* Resolves with a `LocalTrackPublication` instance if successful and `undefined` otherwise
137145
*/
138-
setScreenShareEnabled(enabled: boolean): Promise<LocalTrackPublication | undefined> {
139-
return this.setTrackEnabled(Track.Source.ScreenShare, enabled);
146+
setScreenShareEnabled(
147+
enabled: boolean,
148+
options?: ScreenShareCaptureOptions,
149+
): Promise<LocalTrackPublication | undefined> {
150+
return this.setTrackEnabled(Track.Source.ScreenShare, enabled, options);
140151
}
141152

142153
/** @internal */
@@ -155,16 +166,32 @@ export default class LocalParticipant extends Participant {
155166
* Resolves with LocalTrackPublication if successful and void otherwise
156167
*/
157168
private async setTrackEnabled(
158-
source: Track.Source,
169+
source: Extract<Track.Source, Track.Source.Camera>,
159170
enabled: boolean,
160-
): Promise<LocalTrackPublication | undefined> {
171+
options?: VideoCaptureOptions,
172+
): Promise<LocalTrackPublication | undefined>;
173+
private async setTrackEnabled(
174+
source: Extract<Track.Source, Track.Source.Microphone>,
175+
enabled: boolean,
176+
options?: AudioCaptureOptions,
177+
): Promise<LocalTrackPublication | undefined>;
178+
private async setTrackEnabled(
179+
source: Extract<Track.Source, Track.Source.ScreenShare>,
180+
enabled: boolean,
181+
options?: ScreenShareCaptureOptions,
182+
): Promise<LocalTrackPublication | undefined>;
183+
private async setTrackEnabled(
184+
source: Track.Source,
185+
enabled: true,
186+
options?: VideoCaptureOptions | AudioCaptureOptions | ScreenShareCaptureOptions,
187+
) {
161188
log.debug('setTrackEnabled', { source, enabled });
162189
let track = this.getTrack(source);
163190
if (enabled) {
164191
if (track) {
165192
await track.unmute();
166193
} else {
167-
let localTrack: LocalTrack | undefined;
194+
let localTracks: Array<LocalTrack> | undefined;
168195
if (this.pendingPublishing.has(source)) {
169196
log.info('skipping duplicate published source', { source });
170197
// no-op it's already been requested
@@ -174,23 +201,32 @@ export default class LocalParticipant extends Participant {
174201
try {
175202
switch (source) {
176203
case Track.Source.Camera:
177-
[localTrack] = await this.createTracks({
178-
video: true,
204+
localTracks = await this.createTracks({
205+
video: (options as VideoCaptureOptions | undefined) ?? true,
179206
});
207+
180208
break;
181209
case Track.Source.Microphone:
182-
[localTrack] = await this.createTracks({
183-
audio: true,
210+
localTracks = await this.createTracks({
211+
audio: (options as AudioCaptureOptions | undefined) ?? true,
184212
});
185213
break;
186214
case Track.Source.ScreenShare:
187-
[localTrack] = await this.createScreenTracks({ audio: false });
215+
localTracks = await this.createScreenTracks({
216+
...(options as ScreenShareCaptureOptions | undefined),
217+
});
188218
break;
189219
default:
190220
throw new TrackInvalidError(source);
191221
}
192-
193-
track = await this.publishTrack(localTrack);
222+
const publishPromises: Array<Promise<LocalTrackPublication>> = [];
223+
for (const localTrack of localTracks) {
224+
publishPromises.push(this.publishTrack(localTrack));
225+
}
226+
const publishedTracks = await Promise.all(publishPromises);
227+
// for screen share publications including audio, this will only return the screen share publication, not the screen share audio one
228+
// revisit if we want to return an array of tracks instead for v2
229+
[track] = publishedTracks;
194230
} catch (e) {
195231
if (e instanceof Error && !(e instanceof TrackInvalidError)) {
196232
this.emit(ParticipantEvent.MediaDevicesError, e);
@@ -204,6 +240,10 @@ export default class LocalParticipant extends Participant {
204240
// screenshare cannot be muted, unpublish instead
205241
if (source === Track.Source.ScreenShare) {
206242
track = this.unpublishTrack(track.track);
243+
const screenAudioTrack = this.getTrack(Track.Source.ScreenShareAudio);
244+
if (screenAudioTrack && screenAudioTrack.track) {
245+
this.unpublishTrack(screenAudioTrack.track);
246+
}
207247
} else {
208248
await track.mute();
209249
}

0 commit comments

Comments
 (0)