Skip to content

Commit 9ff78e1

Browse files
yuchem2claude
andcommitted
feat(meeting): 마이크에 노이즈 억제 오디오 제약 적용
getUserMedia 를 { audio: true }(브라우저 기본 의존) 에서 명시적 제약으로 변경: echoCancellation/noiseSuppression/autoGainControl=true + voiceIsolation(Chrome/Edge 110+ ML 음성 격리, 미지원 시 무시). 배경 노이즈 억제 강화. spec 단언도 갱신. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 8b3cd10 commit 9ff78e1

2 files changed

Lines changed: 23 additions & 4 deletions

File tree

‎apps/frontend/src/feature/meeting/hooks/useMediasoupViewModel.spec.tsx‎

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -955,12 +955,19 @@ describe('useMediasoupViewModel.muteToggle', () => {
955955
expect(result.current.localStream).toBeNull();
956956
});
957957

958-
it('toggleAudio 최초 호출 시 getUserMedia({audio:true}) 로 취득해 produce(source=audio) 하고 isAudioMuted=false', async () => {
958+
it('toggleAudio 최초 호출 시 getUserMedia(노이즈 억제 오디오 제약) 로 취득해 produce(source=audio) 하고 isAudioMuted=false', async () => {
959959
const { result, send } = await setupReady();
960960
await act(async () => {
961961
await result.current.toggleAudio();
962962
});
963-
expect(getUserMediaMock).toHaveBeenCalledWith({ audio: true });
963+
expect(getUserMediaMock).toHaveBeenCalledWith({
964+
audio: {
965+
echoCancellation: true,
966+
noiseSuppression: true,
967+
autoGainControl: true,
968+
voiceIsolation: true,
969+
},
970+
});
964971
expect(send.produce).toHaveBeenCalledTimes(1);
965972
expect(send.produce.mock.calls[0][0].appData).toMatchObject({ source: 'audio' });
966973
expect(result.current.isAudioMuted).toBe(false);

‎apps/frontend/src/feature/meeting/hooks/useMediasoupViewModel.ts‎

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ export function useMediasoupViewModel(
340340
code,
341341
transportId: recvTransport.id,
342342
producerId: payload.producerId,
343-
rtpCapabilities: device.rtpCapabilities as unknown,
343+
rtpCapabilities: device.recvRtpCapabilities as unknown,
344344
};
345345
const consumeRes = await rpcWithTimeout<ConsumeResponse>(
346346
socket,
@@ -490,7 +490,19 @@ export function useMediasoupViewModel(
490490
const sendTransport = sendTransportRef.current;
491491
if (sendTransport === null) return;
492492
try {
493-
const constraints = kind === 'audio' ? { audio: true } : { video: true };
493+
// 오디오는 노이즈 억제/에코 제거/자동 게인을 명시적으로 요청한다(기본값 의존 X).
494+
// voiceIsolation 은 표준 외(Chrome/Edge 110+): 지원 시 ML 음성 격리 강화, 미지원 시 무시.
495+
const constraints: MediaStreamConstraints =
496+
kind === 'audio'
497+
? {
498+
audio: {
499+
echoCancellation: true,
500+
noiseSuppression: true,
501+
autoGainControl: true,
502+
voiceIsolation: true,
503+
} as MediaTrackConstraints,
504+
}
505+
: { video: true };
494506
const stream = await navigator.mediaDevices.getUserMedia(constraints);
495507
const track =
496508
kind === 'audio' ? stream.getAudioTracks()[0] : stream.getVideoTracks()[0];

0 commit comments

Comments
 (0)