VoIP
상위 문서: Multimedia Networking
개요
인터넷을 통한 실시간 음성 대화는 종종 인터넷 전화(Internet telephony)라고 불리는데, 사용자 관점에서는 전통적인 회선 교환 전화 서비스와 유사하기 때문이다. 또한 실시간 음성 대화는 일반적으로 VoIP(Voice-over-IP)라고 불린다. 화상 대화(conversational video)는 VoIP와 많은 면에서 유사하지만, 음성뿐 아니라 참가자의 영상도 포함된다는 점에서 차이가 있다.
Limitations of the Best-Effort IP Service
IP(Internet Protocol)는 Best-Effort 서비스를 제공한다. 즉, IP는 각 데이터그램을 출발지에서 목적지로 가능한 한 빨리 전송하려고 노력하지만, 지연 시간이나 패킷 손실 등에 대한 보장은 하지 않는다. 이는 큰 문제가 될 수 있는데, VoIP 애플리케이션은 지연(delay), 지터(jitter), 손실(loss)에 매우 민감하기 때문이다. 이때 VoIP 애플리케이션이 reliable data transfer를 위해서 TCP를 사용하지 않는 이유는 TCP의 재전송 메커니즘이 end system 사이의 지연을 증가시키기 때문이다. 또한 TCP의 혼잡 제어는 패킷 손실이 발생했을 때 전송 속도를 낮추므로 수신 속도보다 송신 속도가 느려질 수 있고, 이에 따라 버퍼 고갈(buffer starvation)이 발생하여 수신자 측에서 음성 이해 가능성(intelligibility)이 심각하게 저하될 수 있다. 이에 따라 대부분의 VoIP 애플리케이션은 기본적으로 UDP를 사용한다.
해당 문서에서는 best-effort 네트워크 환경에서도 application layer에서 VoIP 성능을 향상시킬 수 있는 방법들을 다룬다. 이에 대한 논의를 구체적으로 하기 위해서, 다음의 VoIP 예시를 사용한다:
- 송신자는 초당 8,000 바이트를 생성한다.
- 송신자는 20ms마다 바이트들을 모아 하나의 청크로 만든다.
- 이 chunk는 특수 헤더와 함께 UDP 세그먼트에 캡슐화되어 전송된다.
- 계산하면,
20ms × 8,000 bytes/sec = 160 bytes. 즉, 20ms마다 160바이트 UDP 세그먼트가 전송된다.
- 계산하면,
Packet Loss
이 상황에서 수신자는 언제 청크를 재생할지, 누락된 청크에 대해서 어떻게 대응할지에 대해서 결정해야 한다. VoIP 애플리케이션에서 생성된 UDP 세그먼트는 IP 데이터그램으로 캡슐화되어 전송된다. 이 데이터그램은 전송 중에 라우터의 버퍼(큐)를 거치는데, 이 중 하나라도 버퍼가 가득 차 있다면, 해당 IP 데이터그램은 폐기(dropped)되고 수신자에게 도달하지 못한다. 하지만 패킷 손실은 생각보다 치명적이지 않을 수도 있는데, 실제로 VoIP 애플리케이션에서 1~20% 정도의 손실률은 허용 가능하며, 이는 음성의 인코딩 방식과, 패킷 손실을 은폐하는 기술에 따라 달라진다. 예를 들어 FEC(Forward Error Correction) 기법은 중복되는 정보를 담은 비트를 추가적으로 전송하여, 일부 손실된 데이터를 복구할 수 있다. 하지만 손실률이 10~20% 이상이거나 링크가 매우 혼잡하면 QoS를 더 이상 유지할 수 없다.
End-to-End Delay
VoIP 애플리케이션에게는 종단 간 지연(end-to-end delay)도 매우 중요한 요소이며, 종단 간 지연은 다음의 총 합이다:
- transmission delay
- processing delay
- queueing delay
- propagation delay
- end-system processing delays
이때 VoIP 애플리케이션 기준으로, 150ms 미만의 지연은 사용자에게 감지되지 않으며, 150~400ms의 지연 시간은 받아들일 수 있는 수준의 지연시간이다. 또한 400ms 이상의 지연 시간은 현실적으로 대화가 더 이상 불가능한 수준이다. 이에 따라 수신 측은 보통 400ms 초과된 패킷은 무시하며, 이는 결과적으로 패킷 손실과 유사하다.
Packet Jitter
Packet jitter란 패킷 간 도착 간격의 불규칙성을 의미한다. 예를 들어 송신자는 20ms 간격으로 패킷 전송을 하지만, 수신 시점의 간격은 20ms보다 크거나 작을 수 있다는 것이다. 예를 들어 첫 패킷이 라우터의 거의 빈 큐에 도착하고, 두 번째 패킷은 도착 후 다른 패킷들과 겹친다면, 두 패킷은 20ms 간격으로 전송됐지만, 실제 수신 간격은 20ms보다 더 길어진다. 이 경우 오디오가 끊기거나 느려진 느낌을 줄 수 있다.
반대로 첫 번째 패킷은 라우터의 큐 뒤쪽에 도착해서 오래 기다리고, 두 번째 패킷은 거의 곧바로 뒤따라 도착해서 바로 처리된다면 실제 수신 간격은 20ms보다 짧아진다. 이 경우 음성이 너무 빨리 나와 들리기도 전에 다음 소리가 덮을 수 있다. 따라서 jitter를 완화하는 것이 QoS를 위해서 필요하다.
Removing Jitter at the Receiver for Audio
VoIP 애플리케이션에서는 패킷이 주기적으로 생성되기 때문에, 수신자는 랜덤한 네트워크 jitter가 존재하더라도, 음성 청크의 주기적인 재생(playout)을 제공해야 한다. 이는 일반적으로 아래의 두가지 메커니즘을 결합하여 수행한다:
- 각 chunk에 타임스탬프를 추가한다.
- 송신자는 각 chunk에 그것이 생성된 시점의 시간 정보(timestamp)를 붙인다.
- 수신 측에서 재생을 지연시킨다.
- Streaming Stored Video 문서에서 논의한 것처럼, 수신된 오디오 chunk의 재생 지연(playout delay)은, 대부분의 패킷이 예정된 재생 시점 전에 도착할 수 있도록 충분히 길어야 한다.
- 이 재생 지연은 세션 내내 고정된 값일 수도 있고, 세션 동안 adaptive하게 변화할 수도 있다.
이러한 메커니즘을 조합하여, jitter의 영향을 완화하거나 제거할 수 있다.
Fixed Playout Delay

이 전략에서는 수신자가 각 청크를 생성 시점에서 정확히 q 밀리초 후에 재생하려고 시도한다. 예를 들어, 송신자가 어떤 청크를 시간 t에 timestamp했다면, 수신자는 그 청크를 시점 t + q에 재생한다. 하지만 청크가 예정된 재생 시점 이후에 도착하였다면, 해당 청크를 담은 패킷은 버려지며 손실로 간주된다.
이때 q 값을 설정해야 한다. VoIP는 약 400ms까지의 지연은 허용 가능하지만, 하지만, 더 작은 q 값을 사용할수록 대화 품질이 향상된다. 하지만 단, q를 너무 작게 하면 jitter 때문에 많은 패킷이 제시간에 도착하지 못한다. 따라서 종단 간 지연의 변화 폭이 큰 경우에는 q는 크게 설정하는 것이 좋고, 지연 시간과 jitter가 모두 작은 경우에는 q를 작게 설정하는 것이 좋다. Figure 1은 재생 지연과 패킷 손실 간의 trade-off를 보여준다. 송신자는 20ms 간격으로 패킷을 전송하며, 첫 패킷은 r에 도착하고 이후의 패킷은 jitter에 의해 고르지 않게 도착한다. 첫 번째 스케쥴에서는 초기 재생 지연이 p - r인데, 이는 4번째 패킷이 제시간에 도착하지 않아 손실이 일어난다. 하지만 두 번째 스케쥴에서는 초기 재생 지연이 p' - r에 해당하고, 모든 패킷이 제시간에 도착하여 손실이 일어나지 않는 것을 볼 수 있다.
Adaptive Playout Delay
Fixed Playout Delay에서는 지연 시간과 패킷 손실 간의 trade-off 관계가 나타난다. 하지만 이상적으로는, 패킷의 손실률이 임계치 이하로 유지되면서도 지연 시간은 최소화하고자 한다. 따라서 이를 위해서는 네트워크 딜레이와 딜레이의 분산(variability)를 측정하고 발화 구간(talk spurt)의 시작마다 재생 지연을 조정하는 것이 좋다.
따라서 중요한 것은 재생 지연을 계산하는 알고리즘이다. 이를 위해서 다음과 같은 정의를 사용한다:
- ti: i번째 패킷의 타임스탬프(생성 시각)
- ri: i번째 패킷이 수신자에게 도착한 시간
- pi: i번째 패킷의 재생 시각
- ri − ti: i번째 패킷의 종단 간 지연(지터 때문에 값이 패킷마다 다름)
위의 정의를 바탕으로 평균 지연의 추정치인 di를 계산하면 아래와 같다:
di = (1 − u)·di-1 + u·(ri − ti)
u는 smoothing 계수이며, 해당 계수를 통해서 최근 딜레이 시간에 더 많은 가중치를 부여한다. 이를 통해 딜레이 분산(편차)의 추정치인 vi를 아래와 같이 계산한다:
vi = (1 − u)·vi-1 + u·|ri − ti − di|
이는 실제 딜레이 시간이 평균적인 딜레이 시간과 얼마나 차이나는지를 측정하기 위해서 사용된다. 이를 통해서 첫 번째 패킷의 재생 시각 pi를 계산하면 아래와 같다:
pi = ti + di + K·vi (K는 상수)
위에서 K·vi는 미래 시점으로 재생을 늦추는 역할을 하며, 이를 통해 지연된 패킷 중 대부분이 제시간에 도착하게 한다. 같은 talk spurt 내의 다음 패킷 j의 재생 시각 pj는 아래와 같이 계산된다:
qi = pi − ti (첫 패킷이 생성된 후 재생될 때까지 걸린 시간) pj = tj + qi
Recovering from Packet Loss
VoIP 애플리케이션은 jitter 외에도 패킷 손실에도 적절한 대응을 하여 수용 가능한 오디오 품질을 유지하여야 한다. 이렇게 패킷 손실이 발생했을 때도 수용 가능한 오디오 품질을 유지하려는 여러 기법들을 손실 복구 기법(loss recovery schemes)이라고 한다. 이때 패킷 손실은 수신자에게 도착하지 않은 패킷은 물론, 예정된 재생 시간 이후에 도착하여 무시된 패킷들을 포함한다.
VoIP에서 TCP 기반의 reliable date transfer 서비스를 이용하지 않는 이유는 재생 시점을 놓친 패킷을 재전송해봐야 의미가 없기 때문이다. 또한 congestion으로 인해 폐기된 패킷도 충분히 빨리 복구하기 어렵다. 따라서 VoIP 애플리케이션들은 미리 손실을 예측하고 대비하는 전략을 사용한다. 이를 손실 예측(loss anticipation) 기법[1]이라고 한다.
FEC(Forward Error Correction)

FEC(Forward Error Correction) 기법은 원래의 패킷 스트림에 중복 정보(redundant data)를 추가하여 손실된 패킷을 근사하게 혹은 완전히 복구할 수 있도록 하는 기법이다. 따라서 요구되는 데이터 전송률의 크기는 조금 증가되지만, 손실을 복구할 수 있게 되는 것이다.
FEC을 구현하는 방식 중 하나는 n개의 패킷마다 1개의 중복 패킷을 추가하는 것이다. 예를 들어, 3개의 오디오 청크마다 3개의 청크를 XOR하여서 만든 1개의 중복되는 청크를 생성하는 것이다. 이 경우 총 4개의 패킷 중 하나의 패킷은 손실되어도 완전히 복구를 할 수 있다.[2] 따라서 그룹의 크기(n + 1)을 작게 유지하면, 패킷 손실이 심하지 않은 경우 대부분 복구할 수 있다. 하지만 그룹 크기가 작을 수록 요구되는 전송률의 크기는 커진다. 예를 들어 n = 3이면 요구되는 전송률은 33% 증가한다. 또한 전체 그룹을 수신하기 전까지 재생을 하지 못하므로, 재생의 지연 시간이 증가한다는 단점이 있다.
FEC을 구현하는 또다른 방식은 저해상도의 오디오 스트림을 함께 전송하는 방식이다. 송신자는 두 종류의 고품질 스트림과, 저해상도의 스트림을 생성한다. 이를 위해서 각 n 번째 패킷은 고품질 스트림의 n 번째 청크와, 저해상도 스트림의 n - 1 번째 청크를 결합하여 전송한다. 이렇게 하면 비연속적인 패킷 손실(non-consecutive loss)이 발생했을 때, 다음 패킷에서 이전 청크의 저해상도 버전을 재생하여 손실을 은폐할 수 있다. 이는 손실이 발생하더라도 성공적으로 낮은 품질의 대체 음성을 제공할 수 있다는 점, 전체의 재생 품질이 개선된다는 점, 저해상도의 청크는 작기 때문에 추가적인 전송 부담이 적다는 점, 두 개의 패킷만 있으면 재생이 시작되므로 재생 지연이 작다는 점에서 장점을 가진다.
이 방식을 변형하여, (n−1)뿐 아니라 (n−2), (n−3) 등의 chunk도 함께 붙이면 연속되는 패킷의 손실까지 커버가 가능하다. 하지만 이는 요구되는 데이터 전송률과 재생 지연이 증가한다는 장점이 있다.
Interleaving

Interleaving은 오디오 데이터의 순서를 재배열하여 전송하는 방식이다. 이는 인접한 오디오 단위들을 멀리 떨어지게 배치하여 하나의 패킷이 손실되더라도 여러 지점에 짧은 간격의 손실만이 발생하도록 하는 원리이다. 예를 들어, 오디오 스트림이 5ms 단위의 데이터 조각으로 나뉘고, 하나의 청크가 20ms 분량이라고 가정하자. 이 경우 각 청크에 저장되는 오디오 스트림 단위의 번호는 아래와 같다:
위와 같이 청크를 구성하면 하나의 패킷이 손실되어도 하나의 큰 공백이 아니라, 여러 개의 작은 공백이 발생하므로 청감상의 QoS가 개선된다. 이는 대역폭 증가가 없으므로 낮은 오버헤드를 지닌다는 장점이 있지만, 재생 지연(latency)이 증가한다는 단점이 있다.[3]
Error Concealment
오류 은폐(Error Concealment) 기법은 수신자가 손실된 패킷을 유사하게 대체하여 재생하는 기법이다. 이는 음성 신호(사람의 말)은 단기적으로 자기 유사성(self-similarity)이 크므로 가능하다. 이는 패킷의 손실률이 낮고(15% 미만) 패킷의 크기가 짧을 때(4~40ms 분량) 유효하게 작동한다. 반면, 패킷이 손실된 길이가 5~100ms에 가까워 질수록 해당 기법이 유효하게 작동하기 힘들다.
이를 구현하기 위한 대표적인 기법 중 하나는 패킷 반복(Packet Repetition)이다. 이는 손실된 패킷을 직전에 도착한 패킷으로 대체하는 것이다. 이는 매우 간단하고 계산 비용이 낮으며, 생각보다 괜찮은 QoS를 제공한다는 장점이 있다.
또한 보간법(Interpolation)이라는 기법도 존재한다. 이는 손실된 구간의 앞뒤 오디오 데이터를 사용해 중간값을 생성하여 대체하는 것이다. 이는 패킷 반복 기법보다 더 좋은 QoS를 제공하지만, 계산량이 많아진다는 단점이 존재한다.