LINE 프리미엄콜 개발기: 통화 중 영상 전환에도 끊기지 않는 녹화와 온디바이스 STT
LY Corporation 기술 블로그에 LINE 프리미엄콜(통화 녹음/녹화) 개발기가 올라왔다. LINE Plus에서 VoIP Android 서비스를 개발하는 안덕용 님이 쓴 글로, 통화 도중 카메라를 켜도 녹화가 끊기지 않게 만든 구조와 통화 중인 기기에서 자체 온디바이스 STT 모델을 함께 돌리기 위해 내린 결정을 다룬다. 실시간 미디어 위에 로컬 모델 추론을 얹는 사람이라면 스트림 분리, VAD 적용 범위, 종료 순서 같은 판단을 그대로 가져다 쓸 수 있다.
(이 글의 초안은 설치된 Hermes 에이전트가 작성했고, Claude가 검수 후 발행했다.)
TL;DR: 프리미엄콜은 음성 통화 때부터 오디오·비디오 트랙을 함께 열어 두고 영상 통화로 바뀌면 비디오 입력만 프로필 화면에서 실제 영상으로 바꿔 같은 파일에 기록을 잇는다. 온디바이스 STT에는 VoIP 엔진이 미리 만든 다운샘플링 스트림을 넣고 음성 수신과 인식은 비동기로 떼어 놓는다. VAD로 말소리 구간만 인식하지만 녹음에는 침묵을 남긴다. 통화가 끝나면 수신을 먼저 멈추고 남은 처리를 마친 뒤 파일을 닫는다.
프리미엄콜은 어떤 기능인가
프리미엄콜은 LINE Labs 기능으로 나왔고 1:1 음성·영상 통화 중 원하는 구간을 기록한다. 녹음/녹화 기능은 일본·대만·태국에서 제공되며 국가마다 이용 가능한 서비스가 다를 수 있다. 기록은 Keep Memo에 저장해 두고 나중에 다시 볼 수 있고, 녹음 중이라는 사실은 통화 상대에게도 안내된다.
음성 통화에서는 설정에 따라 STT를 함께 쓸 수 있다. 현재 지원 언어는 일본어이고 음성 통화 구간에서만 동작한다. 인식은 서버를 거치지 않고 기기 안의 자체 모델이 맡는다.
음성에서 영상으로 바뀌어도 파일은 하나
핵심 요구사항은 음성 통화 중 카메라를 켜도 진행 중인 기록이 멈추지 않는 것이다. 카메라를 켰다고 녹화 버튼을 다시 눌러야 한다면 사용자는 대화보다 녹화 상태를 먼저 신경 쓰게 된다. 음성 구간과 영상 구간을 따로 저장했다가 나중에 이어 붙이는 방법도 있지만 그러면 결과 파일이 갈라지고 임시 파일과 후처리까지 관리해야 한다.
음성 통화 때부터 비디오 트랙을 연다
팀은 기록을 시작할 때부터 오디오 트랙과 비디오 트랙을 함께 준비했다. 오디오에는 양쪽 목소리를 담고 비디오에는 상대방 프로필·이름·통화 시작 시각으로 만든 화면을 프레임으로 넣는다. 영상 통화로 넘어가면 비디오 트랙에 들어가던 프로필 화면만 실제 통화 영상으로 갈아 끼운다. 하나의 녹화를 관리하는 단위를 녹화 세션이라 부르는데, 세션을 새로 시작하지 않고 그 안의 화면만 바꾸는 방식이다.
원문 근거
"영상 통화로 바뀌면 비디오 트랙에 전달하던 프로필 화면을 실제 통화 영상으로 교체합니다. 오디오는 계속 기록하고, 파일과 트랙도 유지합니다."
음성 통화 녹음 파일을 열었을 때 누구와 한 통화인지 바로 알아볼 수 있다는 점도 이 구조를 택한 이유다.
상태 변경 이벤트와 프레임 도착을 따로 본다
VoIP 엔진은 통화 세션의 미디어 상태가 바뀌었다는 이벤트를 보내지만 내 카메라 영상(Local input)과 상대방 영상(Remote input)은 각자의 경로로 들어온다. 어느 쪽이 먼저 준비될지는 정해져 있지 않다.
그래서 영상 통화 전환 신호를 받았다고 곧장 프로필을 걷어내지 않는다. 입력별로 실제 프레임이 도착했는지, 화면에 그릴 수 있는 상태인지 확인하고 준비된 쪽부터 실제 영상으로 바꾼다. 아직 영상이 없는 영역에는 프로필을 유지한다. 양쪽 영상이 다 준비될 때까지 녹화 전체를 멈출 필요가 없고 통화 중 카메라를 끄는 경우에도 같은 구분이 쓰인다.
원문 근거
"‘영상 통화를 시작했다’는 것과 ‘기록할 영상이 도착했다’는 것을 나눠 보는 것입니다."
녹화 화면은 화면 캡처가 아니라 따로 그린다
녹화용 화면은 휴대폰 화면을 캡처한 것이 아니다. VoIP 엔진이 넘겨주는 내 영상과 상대방 영상으로 따로 구성하기 때문에 알림이나 통화와 무관한 UI 요소가 기록에 섞이지 않는다.
두 영상의 크기·비율이 다르고 통화 중 해상도가 바뀌기도 한다. 이 변화를 녹화 파일의 화면 크기에 그대로 반영하면 입력 하나가 바뀔 때마다 뒤쪽 기록 경로까지 흔들린다. 그래서 영상이 들어오는 쪽과 파일에 쓰는 쪽을 나눴다. 소스나 해상도가 바뀌면 필요할 때 렌더러에 입력을 다시 연결하되, 파일에 기록하는 최종 프레임 크기는 고정하고 그 안에서 두 영상을 영역에 맞춰 그린다. 이 동안에도 녹화 세션은 유지된다.
온디바이스 STT: 통화 중인 기기에 추론을 얹는 법
글쓴이가 기술적으로 가장 어려웠다고 꼽은 부분이 STT다. 통화 음성을 서버로 보내지 않으니 추론 연산도 기기가 감당해야 한다. 상대방 목소리를 재생하고 내 음성을 보내고 녹음까지 하는 기기에서 모델을 함께 돌려야 했다. 인식 정확도만으로는 부족했고 통화 중 늘어나는 부하를 줄이는 데 공을 많이 들였다고 한다.
모델 입력은 다운샘플링한 음성으로
사람이 듣는 음성과 같은 형식을 모델에 그대로 넣을 필요는 없었다. 팀이 쓰는 온디바이스 모델은 샘플레이트를 낮춘 음성으로도 대화 내용을 충분히 받아 적었다. 통화 재생 음질은 그대로 두고 모델에는 입력 조건에 맞춘 음성을 넣어 전달하는 데이터 양을 줄였다.
스트림은 VoIP 엔진에서 둘로 나눈다
다운샘플링은 앱이 아니라 VoIP 엔진 내부의 오디오 처리 단계에서 한다. 내 목소리와 상대방 목소리가 각각 필요한 처리를 거친 뒤 두 스트림으로 나온다.
- 고음질 스트림: 통화 재생용
- 최적화 스트림: 앱으로 전달해 녹음과 STT에 함께 사용
원문 근거
"고음질 스트림은 통화 재생에 사용하고, 최적화 스트림은 앱으로 전달해 녹음과 STT에 함께 사용합니다."
앱은 엔진이 준 최적화 스트림을 받아 쓰기만 하므로 STT용으로 다시 다운샘플링할 일이 없다. 녹음 모듈은 파일 만들기에, STT 모듈은 모델 실행과 인식 결과 처리에 집중하도록 역할도 갈랐다.
수신과 인식은 비동기, 인식 구간은 VAD로
음성을 받는 작업과 인식하는 작업은 비동기로 연결했다. 새 음성을 받는 쪽이 인식 완료를 기다리지 않게 하려는 것이다. 다만 두 작업이 같은 기기 자원을 쓰기 때문에 서로 영향을 줄 수 있고 인식이 밀리면 처리할 작업이 쌓일 수 있다는 점은 원문도 짚고 있다.
인식 구간도 골라낸다. STT에는 나와 상대방의 음성을 구분해 넘기고 VAD(voice activity detection)로 말소리가 없는 구간의 처리를 줄인다.
같은 기준을 녹음에 적용하면 문제가 생긴다. 상대가 물건을 찾느라 잠깐 말이 없던 구간을 잘라 붙이면, 실제로는 한참 기다렸다 이어진 대화가 곧바로 이어진 것처럼 들린다. 그래서 STT는 말소리 구간만 처리해도 녹음에는 침묵을 그대로 남긴다. 사람이 다시 들을 기록과 모델이 인식할 입력을 목적에 맞게 따로 다룬 셈이다.
원문 근거
"그래서 STT에서는 말소리가 있는 구간을 골라 처리하더라도, 녹음에는 대화 사이의 침묵도 함께 남깁니다."
통화 종료와 기록 완성은 다르다
통화가 끝났다고 기록이 다 된 것은 아니다. 새 데이터 수신이 끝나도 마지막으로 받은 음성·영상이 아직 처리 중일 수 있다. 정리 순서는 이렇다.
- 새 음성과 영상 수신을 멈춘다.
- 남은 미디어 처리를 마친 뒤 파일을 닫고 자원을 해제한다.
- STT는 녹화 파일과 별도로 텍스트를 만들므로, 남은 인식 작업과 결과를 따로 정리한다. 자원을 해제할 때 대기 시간에 한도를 두고 인식이 끝나지 못한 경우도 결과 상태에 반영한다.
원문 근거
"파일부터 닫으면 아직 처리 중인 데이터를 기록하지 못할 수 있기 때문입니다."
사용자 화면에는 파일과 STT 결과의 상태를 모아 보여 준다. 통화 상태, 영상 입력 준비 여부, 기록 완성 여부를 각각 따로 관리한 것이다.
내 환경에 옮겨 볼 지점
원문은 Android VoIP 앱 이야기지만 실시간 입력 위에 로컬 모델 추론을 얹는 파이프라인이라면 대부분 그대로 통한다.
- 모델 입력 포맷은 상류에서 만든다. 재생·저장용 원본과 모델용 경량 스트림을 입력 단계에서 한 번에 나누면 소비자마다 변환을 반복하지 않는다.
- 수신 루프가 추론을 기다리지 않게 한다. 비동기로 떼어 놓더라도 같은 기기 자원을 쓰는 한 추론 지연이 쌓일 수 있으니 큐 적체를 지켜봐야 한다.
- 필터링은 용도별로 건다. VAD 같은 전처리는 추론 입력에만 걸고 사람이 다시 볼 원본에는 걸지 않는다.
- “이벤트 수신”과 “데이터 도착”을 구분한다. 상태 전환 신호를 받았다고 데이터가 준비됐다고 가정하지 않고 준비된 입력부터 반영한다.
- 종료는 수신 중단 → 잔여 처리 → 닫기 → 해제 순으로 한다. 비동기 추론 작업에는 대기 한도를 두고 미완료도 하나의 결과 상태로 남긴다.
참고
- 녹화 버튼 뒤에서 일어나는 일: LINE 프리미엄콜 개발기 - LY Corporation Tech Blog
- LY Corporation Tech Blog
- 타이틀 사진: Unsplash