Skip to content

Commit c0bdd35

Browse files
committed
refactor(audio): pause producers instead of stopping them
Earlier we were stopping/killing them due to f16bdab. But audio should've been paused instead of stopped.
1 parent 7a05320 commit c0bdd35

6 files changed

Lines changed: 124 additions & 26 deletions

File tree

frontend/src/composables/useMeetingLogic.js

Lines changed: 18 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -576,40 +576,34 @@ export function useMeetingLogic(meetingState, meetingId, options = {}) {
576576
// Apply noise cancellation if enabled
577577
const track = await getProcessedAudioTrack(stream);
578578
if (mh?.audioProducer) {
579-
if (meetingState.isScreenSharing.value) {
580-
const currentTrack = mh.audioProducer.track;
581-
if (currentTrack && currentTrack.readyState === "ended") {
582-
await mh.audioProducer.replaceTrack({ track });
583-
} else {
584-
mh.audioProducer.resume?.();
585-
if (track) track.enabled = true;
586-
}
587-
} else {
588-
const producer =
589-
await sfuManager.value.transportManager.createProducer(track, {
590-
type: "microphone",
591-
});
592-
mh?.setProducers({ audioProducer: producer });
579+
// if we have an existing paused producer resume it
580+
const currentTrack = mh.audioProducer.track;
581+
if (currentTrack && currentTrack.readyState === "ended") {
582+
// if track died unexpectedly, replace it
583+
await mh.audioProducer.replaceTrack({ track });
584+
} else if (track) {
585+
track.enabled = true;
586+
}
587+
mh.audioProducer.resume?.();
588+
589+
const sfuClient = getSFUClient();
590+
if (sfuClient.isConnected()) {
591+
sfuClient.resumeProducer(mh.audioProducer.id).catch(() => {});
593592
}
594593
} else if (track && sfuManager.value?.transportManager) {
594+
// if no existing producer create one
595595
const producer =
596596
await sfuManager.value.transportManager.createProducer(track, {
597597
type: "microphone",
598598
});
599599
mh?.setProducers({ audioProducer: producer });
600600
}
601601
} else {
602-
// Turning mic OFF
602+
// disable the track and pause the producer
603603
if (stream) {
604604
const at = stream.getAudioTracks()[0];
605605
if (at) {
606-
if (meetingState.isScreenSharing.value) {
607-
// Keep track alive for resuming, else user can't unmute after screen share
608-
at.enabled = false;
609-
} else {
610-
at.stop();
611-
stream.removeTrack(at);
612-
}
606+
at.enabled = false;
613607
}
614608
}
615609

@@ -619,14 +613,12 @@ export function useMeetingLogic(meetingState, meetingId, options = {}) {
619613
}
620614

621615
if (mh?.audioProducer) {
622-
mh.audioProducer.close?.();
616+
mh.audioProducer.pause?.();
623617

624618
const sfuClient = getSFUClient();
625619
if (sfuClient.isConnected()) {
626-
sfuClient.closeProducer(mh.audioProducer.id).catch(() => {});
620+
sfuClient.pauseProducer(mh.audioProducer.id);
627621
}
628-
629-
mh.audioProducer = null;
630622
}
631623
}
632624

frontend/src/utils/sfu-client.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,14 @@ class SFUClient {
490490
return this.sendRequest("close_producer", { producerId });
491491
}
492492

493+
async pauseProducer(producerId) {
494+
return this.sendRequest("pause_producer", { producerId });
495+
}
496+
497+
async resumeProducer(producerId) {
498+
return this.sendRequest("resume_producer", { producerId });
499+
}
500+
493501
async closeConsumer(consumerId) {
494502
return this.sendRequest("close_consumer", { consumerId });
495503
}

sfu-server/src/mediasoup/MediasoupManager.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,14 @@ export class MediasoupManager {
409409
this.consumerManager.closeConsumer(consumerId);
410410
}
411411

412+
async pauseProducer(producerId: string): Promise<boolean> {
413+
return this.producerManager.pauseProducer(producerId);
414+
}
415+
416+
async resumeProducer(producerId: string): Promise<boolean> {
417+
return this.producerManager.resumeProducer(producerId);
418+
}
419+
412420
async updateConsumerPreferences(options: {
413421
consumerId: string;
414422
visible: boolean;

sfu-server/src/mediasoup/ProducerManager.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,56 @@ export class ProducerManager extends EventEmitter {
9393
return { isScreen, removedConsumers: [] };
9494
}
9595

96+
async pauseProducer(producerId: string): Promise<boolean> {
97+
const producerData = this.producers.get(producerId);
98+
if (!producerData) {
99+
return false;
100+
}
101+
102+
const { producer } = producerData;
103+
if (producer.paused) {
104+
return false;
105+
}
106+
107+
try {
108+
await producer.pause();
109+
loggers.producerManager.info('Producer paused: %s', producerId);
110+
return true;
111+
} catch (error) {
112+
loggers.producerManager.warn(
113+
'Failed to pause producer %s: %s',
114+
producerId,
115+
(error as Error).message,
116+
);
117+
return false;
118+
}
119+
}
120+
121+
async resumeProducer(producerId: string): Promise<boolean> {
122+
const producerData = this.producers.get(producerId);
123+
if (!producerData) {
124+
return false;
125+
}
126+
127+
const { producer } = producerData;
128+
if (!producer.paused) {
129+
return false;
130+
}
131+
132+
try {
133+
await producer.resume();
134+
loggers.producerManager.info('Producer resumed: %s', producerId);
135+
return true;
136+
} catch (error) {
137+
loggers.producerManager.warn(
138+
'Failed to resume producer %s: %s',
139+
producerId,
140+
(error as Error).message,
141+
);
142+
return false;
143+
}
144+
}
145+
96146
getProducer(producerId: string): mediasoup.types.Producer | undefined {
97147
return this.producers.get(producerId)?.producer;
98148
}

sfu-server/src/server/SocketHandlerManager.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -766,6 +766,38 @@ export class SocketHandlerManager {
766766
callback({ success: false, error: (error as Error).message });
767767
}
768768
});
769+
770+
socket.on('pause_producer', async (data, callback) => {
771+
try {
772+
this.authManager.ensureFullAccess(socket);
773+
const { producerId } = data;
774+
const paused = await this.mediasoup.pauseProducer(producerId);
775+
776+
callback({ success: true, paused });
777+
} catch (error) {
778+
loggers.socketHandler.error(
779+
'Error pausing producer: %s',
780+
(error as Error).message,
781+
);
782+
callback({ success: false, error: (error as Error).message });
783+
}
784+
});
785+
786+
socket.on('resume_producer', async (data, callback) => {
787+
try {
788+
this.authManager.ensureFullAccess(socket);
789+
const { producerId } = data;
790+
const resumed = await this.mediasoup.resumeProducer(producerId);
791+
792+
callback({ success: true, resumed });
793+
} catch (error) {
794+
loggers.socketHandler.error(
795+
'Error resuming producer: %s',
796+
(error as Error).message,
797+
);
798+
callback({ success: false, error: (error as Error).message });
799+
}
800+
});
769801
}
770802

771803
private setupMediaControlHandlers(socket: Socket): void {

sfu-server/src/types/index.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,14 @@ export interface ClientToServerEvents {
181181
data: { producerId: string },
182182
callback: (response: CloseProducerResponse) => void,
183183
) => void;
184+
pause_producer: (
185+
data: { producerId: string },
186+
callback: (response: SFUResponse & { paused?: boolean }) => void,
187+
) => void;
188+
resume_producer: (
189+
data: { producerId: string },
190+
callback: (response: SFUResponse & { resumed?: boolean }) => void,
191+
) => void;
184192
close_consumer: (
185193
data: { consumerId: string },
186194
callback: (response: SFUResponse) => void,

0 commit comments

Comments
 (0)