WebRTC에서 사용되는 비디오 코덱(VP8, VP9, H.264)과 오디오 코덱(Opus, G.711)을 실무 관점에서 비교하고, 크롬 내부 페이지로 실제 코덱을 확인하는 방법까지 다룹니다. AV1, AV2 등 차세대 코덱도 함께 살펴봅니다.
WebRTC 박살내기 첫번째 시리즈 입니다. WebRTC 기본 개념부터 시그널링, Offer/Answer(SDP), Trickle ICE, STUN/TURN, NAT, 그리고 Mesh·SFU·MCU 아키텍처까지 한 번에 정리합니다.
WebRTC 박살내기 두번째 시리즈 입니다. WebRTC의 MediaStream과 MediaStreamTrack을 깊이 이해하고, getUserMedia부터 트랙 제어, 품질 관리까지 실전 예제와 함께 알아봅니다.
WebRTC 박살내기 세번째 시리즈 입니다. WebRTC의 핵심 RTCPeerConnection을 완벽하게 이해하고, 연결 생성부터 이벤트 처리, 품질 관리, 연결 복구까지 실전 예제와 함께 알아봅니다.
WebRTC로 실시간 영상 상담 서비스를 개발하면서 항상 궁금했던 질문이 있었습니다.
"왜 같은 네트워크 환경인데, 어떤 통화는 선명하고 어떤 통화는 화질이 떨어질까?"답은 코덱(Codec)에 있었습니다.
같은 화상 통화라도 어떤 코덱을 사용하느냐에 따라 화질, 데이터 사용량, 기기 부하(발열/배터리)가 크게 달라질 수 있습니다.
이번 글에서는 WebRTC에서 사용되는 미디어 코덱을 실무 관점에서 정리했습니다.
복잡한 압축 알고리즘 이론은 제외하고, 실무에 바로 도움이 되는 핵심 포인트만 담았습니다.
HD 영상(1920×1080) 1초 분량을 단순 계산해보겠습니다.
(대략치이며, 단순화를 위해 RGBA 4바이트, 30프레임, 10진 단위 표기를 사용합니다.)
1920 × 1080 픽셀 × 4바이트 × 30프레임
= 약 249MB/초
30분 화상 통화라면 약 450GB의 데이터가 필요합니다.
이를 실시간으로 전송하려면 약 2Gbps 수준의 대역폭이 필요한데, 일반 가정용 인터넷(예: 100Mbps)으로는 어렵습니다.
💡 코덱(Codec)이란?
Coder + Decoder의 합성어입니다.
영상과 음성을 압축(인코딩)하고 다시 푸는(디코딩) 기술을 의미합니다.
같은 HD 영상을 코덱으로 압축하면 대략 이런 수준으로 내려갈 수 있습니다.
(콘텐츠/장면 복잡도/인코더 설정/목표 지연/손실률에 따라 편차가 매우 큽니다.)
| 상황 | 필요 대역폭 | 압축률 |
|---|---|---|
| 압축 없음 | 2,000Mbps | - |
| H.264 압축 | 2~4Mbps | 약 1/500~1/1,000 |
| VP9 압축 | 1~3Mbps | 약 1/700~1/1,500 |

💡 손실 압축 vs 무손실 압축
손실 압축: 원본과 완전히 동일하진 않지만, 사람이 인지하기 어려운 수준으로 압축 (H.264, VP9 등)
무손실 압축: 원본과 100% 동일하게 복원 가능 (예: FLAC)WebRTC는 실시간성이 중요하기 때문에 손실 압축을 주로 사용합니다.
1초에 전송되는 데이터의 양을 의미합니다.
| 화질 | 비트레이트 | 특징 |
|---|---|---|
| 360p | 300~800Kbps | 저화질 |
| 480p | 600Kbps~1.5Mbps | 일반 |
| 720p | 1.5~3Mbps | HD |
| 1080p | 3~6Mbps | Full HD |
비트레이트가 높을수록 화질은 좋아질 가능성이 크지만, 네트워크 부담과 데이터 사용량, 기기 부하가 함께 증가합니다.
영상의 가로×세로 픽셀 수를 나타냅니다.
| 이름 | 해상도 | 주 사용처 |
|---|---|---|
| HD | 1280×720 | 일반 화상 통화 |
| Full HD | 1920×1080 | 고화질 통화/상담 |
| QHD | 2560×1440 | 고급 모니터 |
WebRTC 실무에서는 대개 720p를 기본 목표로 두는 경우가 많습니다.
1080p는 네트워크/발열/배터리 부담이 커서, 서비스 성격에 따라 720p로도 충분한 경우가 많습니다.
1초에 표시되는 프레임(장) 수를 의미합니다.
| FPS | 체감 | 주 사용처 |
|---|---|---|
| 15 | 약간 끊김 | 저속 네트워크 |
| 24 | 영화 같은 느낌 | 일반 영상 |
| 30 | 자연스러움 | 일반 화상 통화 |
| 60 | 매우 부드러움 | 게임/고프레임 |
WebRTC는 프레임레이트를 "표준값"으로 고정하지 않습니다.
실무에서는 24~30fps를 목표로 두는 경우가 많지만, 실제 값은 네트워크/기기 성능에 따라 내려가거나 적응적으로 변합니다.
인코딩(Encoding): 원본 영상을 압축하는 과정
디코딩(Decoding): 압축된 영상을 재생 가능한 형태로 복원하는 과정

발신자(SENDER): 미디어 -> 인코딩(Encoding) -> 전송
수신자(RECEIVER): 수신 -> 디코딩(Decoding) -> 화면 표시
양쪽 모두 같은 코덱을 지원해야 통신이 가능합니다.
💡 여기서 "지원"은
단순히 목록에 있다는 뜻뿐 아니라, 정책/가속 여부/협상 결과까지 포함해 실무에서는 더 넓게 봐야 합니다.
2010년 Google이 On2 Technologies를 인수하면서 공개한 코덱입니다.
특징| 항목 | 범위 |
|---|---|
| 720p | 1.5~3Mbps |
| 1080p | 3~6Mbps |
💡 코덱 지연 팁
지연은 코덱만으로 결정되지 않고, 지터 버퍼/패킷 손실/재전송/인코딩 설정의 영향을 크게 받습니다.
장점: 호환성/안정성, 기기 부하가 비교적 낮은 편
단점: 같은 체감 화질 대비 데이터 사용량이 더 많아질 수 있음
2013년 공개된 VP8의 후속입니다.
같은 체감 화질을 더 적은 데이터로 만들려는 목표를 가집니다(효과는 장면/설정 의존)
| 항목 | VP8 | VP9 | 비고 |
|---|---|---|---|
| 비트레이트 | 더 높음 | 더 낮아질 수 있음 | 장면/설정 의존 |
| CPU 사용률 | 중간 | 높아질 수 있음 | 기기 의존 |
| 인코딩 속도 | 빠름 | 느려질 수 있음 | 설정 의존 |
장점: 데이터 사용량 감소(조건이 맞으면 효과 큼)
단점: 인코딩 부담 증가, 오래된 기기에서 성능 저하 가능
2003년 표준화된, 매우 널리 쓰이는 비디오 코덱입니다.
특징| 항목 | 범위 |
|---|---|
| 720p | 1.5~3.5Mbps |
| 1080p | 3~6Mbps |
| 하드웨어 가속 | 환경에 따라 매우 유리 |
| CPU 사용률 | 하드웨어 사용 시 낮아질 수 있음 |
💡 라이선스 관련
H.264는 특허 라이선스 체계가 있어, 인코더/디코더의 배포 방식과 서비스 형태에 따라 비용/조건이 달라질 수 있습니다.
| 프로파일 | 특징 | 용도 |
|---|---|---|
| Baseline | 단순, 낮은 복잡도 | 오래된 모바일 |
| Main | 중간 복잡도 | 일반 방송/저장 |
| High | 높은 효율/화질 | 고화질 저장/스트리밍 |
| Constrained Baseline | 실시간 호환 고려 | 실시간 통신에서 자주 언급 |

2012년 표준화된 오디오 코덱으로, WebRTC에서 핵심 오디오 코덱으로 필수 구현(MTI)에 포함됩니다.
특징| 비트레이트 | Opus | G.711 |
|---|---|---|
| 6~8 Kbps | 통화 가능 수준 | 어려움 |
| 16 Kbps | 음성에 충분 | 어려움 |
| 32 Kbps | 음성 깔끔 | 어려움 |
| 64 Kbps | 고품질 | 전화 수준 |
| 용도 | 비트레이트 | 샘플레이트 |
|---|---|---|
| 음성 통화 | 16~24 Kbps | 16 kHz |
| 고품질 음성 | 32~40 Kbps | 24 kHz |
| 음악(모노) | 48~64 Kbps | 48 kHz |
| 음악(스테레오) | 96~128 Kbps | 48 kHz |
1972년 표준화된 오래된 오디오 코덱으로, 전통 전화망과 호환성이 큽니다.
WebRTC 호환 구현에서는 Opus와 함께 G.711(PCMU/PCMA)도 필수 구현(MTI)으로 요구됩니다.
| 항목 | G.711 | Opus |
|---|---|---|
| 비트레이트 | 64 Kbps 고정 | 낮은 값부터 가변 운용 |
| 샘플레이트 | 8 kHz | 16~48 kHz(구성 가능) |
| 음질 | 전화 수준 | 더 높은 품질 가능 |
💡 체감 지연 메모
코덱 처리 지연이 낮아도, 실제 통화 지연은 지터 버퍼/패킷 손실/라우팅 영향이 훨씬 큽니다.
WebRTC 연결 중에 새 탭에서 아래 주소로 접속합니다.

화질이 떨어질 때 "코덱 탓"으로 결론 내리기 전에, 아래를 같이 보면 원인 추적이 빨라집니다.
packetsLost / packetsReceived: 손실이 실제로 늘었는지jitter: 수신 간격이 흔들리는지roundTripTime(또는 currentRoundTripTime): 왕복 지연이 튀는지framesDropped / framesDecoded: 디코딩이 밀리고 있는지(기기 부하 신호)nackCount / pliCount / firCount: 재전송/키프레임 요청이 늘었는지(손실/불안정 신호)검색창에 "codec" 입력

codec 통계 항목에서 mimeType 확인
예시

💡 해석
96, 97, 98은 코덱 "우선순위 번호"가 아니라 페이로드 타입(payload type) 입니다.
일반적으로 앞에 둔 코덱을 선호하도록 "제안"하는 경우가 많지만, 최종 선택은 양쪽 지원 교집합 + 응답(answer) + 브라우저 정책/가속 가능 여부에 의해 바뀔 수 있습니다.
아래는 "코덱 설정"이 아니라 연결(ICE) 설정 예시입니다.
아래는 "코덱 변경"이 아니라 입력 해상도/FPS 제한으로 트래픽과 부하를 줄이는 방식입니다.
브라우저가 지원하는 경우, 트랜시버에서 선호 코덱을 정렬/필터링할 수 있습니다.
(협상에 영향을 주지만 "강제 확정"은 아닙니다.)
💡 참고
이 방식은 "브라우저가 제공하는 협상 로직" 위에서 동작합니다.
SDP를 직접 편집하는 방식도 존재하지만, 유지보수 리스크가 커서 팀/프로덕트 성격에 따라 신중하게 선택하는 편이 좋습니다.
화면공유는 카메라 영상과 품질이 저하되는 양상이 다릅니다.
그래서 화면공유는 고프레임보다 가독성이 더 중요한 경우가 많습니다.
즉, 네트워크가 불안정하면 해상도를 깎기보다 프레임을 낮추는 방향이 결과가 좋은 경우가 많습니다.
💡 contentHint 활용 팁
contentHint는 "보장"이 아니라 "힌트"입니다.
contentHint는 브라우저/인코더가 트랙의 성격을 추정하는 데 참고하는 값입니다.
환경에 따라 효과가 달라질 수 있습니다.화면공유에서 텍스트/도형 가독성을 우선하고 싶다면
"text"또는"detail"을 고려할 수 있습니다.
SFU 방식의 다자간 통화에서는 코덱 선택만큼이나 누구에게 어떤 품질을 보낼지가 핵심입니다.
1:1에서 좋았던 고화질 단일 스트림 설정이 다자간에서 흔들리는 이유는 간단합니다.
이때 고화질 하나만 보내면, 느린 수신자는 끊김/프레임 드랍/강제 해상도 저하를 겪기 쉽습니다.
그래서 다자간에서는 다음 전략이 자주 쓰입니다.
정리하면, 다자간 품질은 "코덱"만으로 결정되지 않고 레이어링(시뮬캐스트/SVC) + SFU의 선택/구독 정책(큰 화면/작은 화면, 발표자 우선 등)에 의해 체감이 크게 갈립니다.
2018년 Alliance for Open Media가 발표한 차세대 코덱입니다.
특징AV2는 AV1의 후속으로 개발 중인 차세대 코덱입니다.
특징프롤로그에서 던졌던 질문, "왜 같은 네트워크인데 화질이 다를까?"의 답을 함께 찾아보았습니다.
코덱은 WebRTC 품질을 결정짓는 여러 변수 중 하나입니다.
같은 코덱이라도 네트워크 상태, 기기 성능, 설정 방식에 따라 결과가 달라질 수 있습니다.
실무에서는 "완벽한 코덱"을 찾기보다, 상황에 맞는 선택과 폴백 전략이 더 중요한 경우가 많습니다.
이 글이 필요하신 분들에게 도움이 되었으면 합니다.
끝까지 읽어주셔서 감사합니다.
호환성이 최우선인 경우
안정적인 기본 코덱이 필요한 경우
대역폭 절약이 중요한 경우
최신 환경(브라우저/기기) 중심인 경우
모바일 배터리/발열이 중요한 경우
하드웨어 가속에 기대고 싶은 경우
레거시 호환이 중요한 경우
전화 시스템 연동이 중요한 경우
레거시 호환이 필요한 경우
chrome://webrtc-internals
inbound-rtp (video)
├─ codecId: 사용 중인 코덱 참조(id)
├─ frameWidth / frameHeight: 해상도
├─ framesPerSecond: 초당 프레임 수
└─ bytesReceived: 수신 데이터량
{
"type": "codec",
"mimeType": "video/VP8",
"clockRate": 90000,
"payloadType": 96
}
m=video 9 UDP/TLS/RTP/SAVPF 96 97 98
a=rtpmap:96 VP8/90000
a=rtpmap:97 VP9/90000
a=rtpmap:98 H264/90000
const configuration = {
iceServers: [{ urls: "stun:stun.l.google.com:19302" }]
};
const constraints = {
video: {
width: { ideal: 640 },
height: { ideal: 480 },
frameRate: { ideal: 15 }
},
audio: {
echoCancellation: true,
noiseSuppression: true
}
};
const pc = new RTCPeerConnection(configuration);
// 예: 비디오 트랜시버를 만든 뒤 선호 코덱을 정렬
const transceiver = pc.addTransceiver("video");
const caps = RTCRtpReceiver.getCapabilities("video");
const codecs = caps.codecs;
// VP9 → H.264 → VP8 순으로 선호
const preferred = ["video/VP9", "video/H264", "video/VP8"];
const sorted = codecs
.filter((c) => preferred.includes(c.mimeType))
.sort((a, b) => preferred.indexOf(a.mimeType) - preferred.indexOf(b.mimeType));
transceiver.setCodecPreferences(sorted);
// screenTrack: getDisplayMedia()로 얻은 비디오 트랙이라고 가정
screenTrack.contentHint = "text"; // 또는 "detail"