파이어폭스, 크롬, 엣지 등과 같은 브라우저에서 Media Recorder Web API를 사용하여 사용자의 마이크에 접근하여 어플레케이션을 개발하는 경우 시행착오를 줄이고 삽질을 방지하기 위해 고려해야할 사항에 대해 소개합니다. 사용 방법 및 코드는 구글링 및 생성형AI에게 요청하면 대부분 잘 짜주니 본 글에서는 생략하겠습니다.
다음은 내부 프로젝트에서 Media Recorder API를 사용하는 시나리오를 보여주고 있습니다.
웹 브라우저의 노이즈 캔슬링, 에코(잔향) 캔슬링 등의 어떠한 필터도 적용되지 않은 순수한 마이크 소리를 입력 받고, 이를 Node.js 에서 중간 처리, Python 코드에서 후처리하고 ML 수행 후 그 결과를 Web Socket 으로 실시간 Return 하는 어플리케이션입니다.
여기서 중점으로 보고자 하는 것은 웹 브라우저 상에서 사용자 디바이스의 마이크를 접근하고, 그 데이터를 활용하는데에 있어서 몇가지 고려해야할 부분에 대해 다룹니다.
당연하지만: HTTPS, SSL/TLS 적용 필요
브라우저 보안 정책은 중간자 공격을 방지하기 위해 HTTPS 및 SSL/TLS 암호화를 필수로 요구하고 있습니다.
개발 단계에서 localhost 환경에서는 이러한 보안 요구사항이 예외적으로 적용되지 않으므로 보안되지 않은 환경에서도 사용이 가능합니다. 외부 네트워크 환경에서는 Let’s Encrypt와 같은 서비스를 통해 적절한 SSL 인증서를 발급받아 Nginx 프록시 서버에 적용하여 사용할 수 있습니다.
만약 보안 연결을 적용하기 어려운 상황이라면, Chrome 브라우저에서 chrome://flags/#unsafely-treat-insecure-origin-as-secure
주소로 접속하여 예외 처리할 도메인 주소를 등록하여 해결할 수 있습니다.
브라우저 지원 및 호환성 확인
다음은 지원하는 최소 브라우저 버전입니다.
Source: https://www.w3.org/TR/mediacapture-streams/#stream-api
현재 사용되는 대부분의 최신 브라우저는 원활하게 작동합니다. 하지만, Android WebView에서는 사용이 제한될 수 있으므로, WebView 사용이 감지될 경우, 사용자가 설치한 외부 브라우저로 이동시키는 등의 조치를 취할 수 있습니다.
Source: https://caniuse.com/stream
현재 사용 중인 브라우저에서 Web API의 특정 기능을 지원하는지 확인하기 위해서는 다음 두 가지 API를 호출하여 확인할 수 있습니다:
- https://developer.mozilla.org/en-US/docs/Web/API/MediaStreamTrack/getSettings
- https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getSupportedConstraints
위 함수를 주소의 함수를 사용하여 크롬에서 오디오 트랙 설정과 지원하는 기능들을 보여주고 있습니다.
Audio Track Settings:
autoGainControl: true
channelCount: 1
deviceId: default
echoCancellation: true
groupId: d7ea08ac58bbabe8345489d723d8e32a255cb32fce7491d880bfbd7602d2e6bd
latency: 0.01
noiseSuppression: true
sampleRate: 48000
sampleSize: 16
Supported Constraints:
aspectRatio: true
autoGainControl: true
brightness: true
channelCount: true
colorTemperature: true
contrast: true
deviceId: true
displaySurface: true
echoCancellation: true
exposureCompensation: true
exposureMode: true
exposureTime: true
facingMode: true
focusDistance: true
focusMode: true
frameRate: true
groupId: true
height: true
iso: true
latency: true
noiseSuppression: true
pan: true
pointsOfInterest: true
resizeMode: true
sampleRate: true
sampleSize: true
saturation: true
sharpness: true
suppressLocalAudioPlayback: true
tilt: true
torch: true
whiteBalanceMode: true
width: true
zoom: true
Opus 오디오 코덱 사용
Web API에서 Media Recorder는 실시간 스트리밍에 최적화되어 있고, 로열티가 없는 Opus 코덱을 사용합니다. 대충 다음과 같은 개쩌는 기술을 가지고 있습니다. 프로젝트에서 사용하는 Python 코드에서 .WAV 코덱을 받아서 처리하므로, 녹음된 소리를 처리하고자 하면, Opus 코덱을 처리하는 부분도 고려하여야 할 것입니다.
- Bitrates from 6 kb/s to 510 kb/s
- Sampling rates from 8 kHz (narrowband) to 48 kHz (fullband)
- Frame sizes from 2.5 ms to 60 ms
- Support for both constant bitrate (CBR) and variable bitrate (VBR)
- Audio bandwidth from narrowband to fullband
- Support for speech and music
- Support for mono and stereo
- Support for up to 255 channels (multistream frames)
- Dynamically adjustable bitrate, audio bandwidth, and frame size
- Good loss robustness and packet loss concealment (PLC)
- Floating point and fixed-point implementation
Source: https://opus-codec.org/
여기서 Opus 파일을 .WAV, .MP3 파일 등으로 변환하고자 하면, FFmpeg 을 사용할 수 있습니다. 다음은 앞서 프로젝트 시나리오에서 FFmpeg를 사용하여 .opus 파일을 .wav 파일로 변환하는 것을 보여주고 있습니다.
Node.js 에서 FFmpeg 프로세스를 호출하여 변환하는데, 다음의 코드로 커맨드를 만들어서 변환할 수 있을 것입니다.
// Input Command
let inputFilePath = __dirname + '/uploads/opus/' + file.filename; // data from client
let outputFilePath = __dirname + '/uploads/wav/' + file.filename.replace(/\.[^/.]+$/, ".WAV");
let ffmpegConvertCmd = 'ffmpeg -y -i ' + inputFilePath + ' -vn ' + outputFilePath;
출력되는 최종 커멘트는 다음과 같습니다.
// Output
ffmpeg -y -i /home/.../uploads/opus/test.opus -vn /home/.../uploads/wav/test.WAV
노이즈 캔슬링, 에코 캔슬링 등 필터 영향
다음은 웹 표준을 개발하고 장려하는 3WC에서 제공하는 문서에서 MediaRecorder 사용시 MediaStreamTrack Object의 소리 필터링 옵션을 보여주고 있습니다.
Source: https://www.w3.org/TR/mediacapture-streams/#constrainable-properties
여러가지 옵션이 있지만, 그 중에서 소리 필터 먹이는 옵션은 echoCancellation, autoGainControl, noiseSuppression 으로, 이 옵션들은 아무것도 지정하지 않으면, 기본적으로 활성화 상태가 되며, 일부 브라우저에 따라 몇몇 옵션은 기본 활성화 옵션이 다를 수 있습니다.
Audio Track Settings:
autoGainControl: true
channelCount: 1
deviceId: default
echoCancellation: true
groupId: d7ea08ac58bbabe8345489d723d8e32a255cb32fce7491d880bfbd7602d2e6bd
latency: 0.01
noiseSuppression: true
sampleRate: 48000
sampleSize: 16
위 사진은 PC 크롬 브라우저 기준으로 필터 옵션들이 기본적으로 활성화 되어 있음을 보여주고 있습니다. 따라서 RAW 마이크 소리를 받고자 하면 반드시 필터 옵션을 비활성화 해야합니다.
가장, 가장, 아주 기본적이고 기초적인 부분이지만, 설마가 사람을 잡습니다. 소리 분석이 중요한 어플리케이션에서는 꼭 해당 기능이 필요한지 면밀히 검토하도록 합시다.
논외: 실험과 검증, VB-Audio Voicemeeter
다음 다이어그램은 실제 음원 샘플과 녹음된 음원이 일치한지 검증을 위한 구성 시나리오를 보여주고 있습니다
VB-Audio의 Voicemeeter (Voicemeter 아님)을 사용하여 가상 마이크를 구현하여 검증을 진행했고, 음원 샘플과 녹음된 음원이 다름을 인지 후 디버깅을 수행한 결과 앞선 확인이 필요한 사항에서 필터가 말썽였던걸로 결론났던 이슈입니다.
따라서 local 환경에서 음원 관련 실험과 검증이 필요하면 Voicemeeter 소프트웨어를 사용하는 방법도 가능할 것입니다.
결론: 꼭 기억해야 할 것 2가지
- 기술 문서를 꼭 정독하도록 합시다
- 1번을 잊지 않습니다.
Reference:
- https://blog.addpipe.com/audio-constraints-getusermedia/
- https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints
- https://developer.chrome.com/blog/more-native-echo-cancellation/
- https://stackoverflow.com/questions/37326846/disabling-auto-gain-conctrol-with-webrtc-app