⌨️ 개요
2025.08.19 - [개발] - 실시간 스트리밍 서버 공부 및 개발
2026.01.19 - [프로젝트/Side-Project] - [Media] Spring Boot로 구현하는 미디어 스트리밍
2026.01.24 - [프로젝트/Side-Project] - [Media] 미디어 스프링 서버 Spring vs Cpp
[Media] 미디어 스프링 서버 Spring vs Cpp
2026.01.19 - [프로젝트/Side-Project] - [Media] Spring Boot로 구현하는 미디어 스트리밍 [Media] Spring Boot로 구현하는 미디어 스트리밍0. Repohttps://github.com/ckaanf/media-lab GitHub - ckaanf/media-lab: From HTTP Range Request to
romanc3.tistory.com
이런 것들을 거쳐서 이제 스트리밍 서버를 RTMP에서 HLS로 전환하려고 한다.
엄밀히 말하면 RTMP에서 HLS로의 전환보다 전송받은 데이터를 HLS로 송출해주는 것이 맞는 표현이겠다.
[왜 HLS인가?]
RTMP/SRT/WebRTC는 실시간 저지연에는 충분히 괜찮고 매력적인 프로토콜이지만,
VOD 서비스나 이런 곳에서는 한 명의 연결이 하나의 커넥션(일반적으로)을 맺는 비용이 큰 통신이다.
나는 처음에는 실시간 스트리밍이니까 당연히 전자의 프로토콜을 생각했으나, 이 도메인에 대한 이해가 깊어질수록
결과적으로 실시간 스트리밍도 빠르게 인코딩된 VOD 조각들을 시청하는 것이라는 걸 이해하게 됐다.
HLS의 장점
HLS은 결과적으로는 정적 데이터다(많은 걸 생략했지만..)
바꿔 말하면 캐싱도 가능하고 CDN에 미리 올려서 전 세계 사용자들에게 트래픽을 분산해서 제공해 줄 수 있다.
그렇다 보니 사용자 네트워크 환경에 맞는 ABR(적응형 비트레이트)를 적용하기도 쉽다.
실시간 스트리밍에서의 HLS
결과적으로는 실시간(실시간 아님) 스트리밍은 RTMP/RTS/WebRTC 등으로 초 저지연(이건 괜찮다 왜냐면 스트리머 하나가 빠르게 영상을 올리는 것이니까)으로 올린 영상을 Ingest/Transcoding 서버에서 처리를 해서 사용자에서 2~10초 이하의 저지연의 영상으로 서빙해 주는 것이다. 일단 나의 정의는 이렇다. 물론 더 고도의 튜닝도 있고 그렇겠지만, 우선 혼자 고민한 결과는 이거다
결과적으로는 실시간으로 전송 받은 일정 분량의 영상을 빠르게 HLS로 인코딩하여 사용자들에게 전달해 주는 것이다.
[RTMP에서 HLS 전환에서 삽질]
1. 0바이트 파일
상황: FFmpeg를 실행하자마자 브라우저를 켰는데, 재생 버튼이 비활성화되며 먹통
원인: FFmpeg가 m3u8 파일을 생성하는 순간에 Nginx가 이를 읽어 시청자에게 배달
-> 파일 작성이 아직 완료되기 전에 서빙
대처: 파일 작성이 완료되고 나서 읽도록 동기화
2. 짧은 시간 내의 계속적인 네트워크 요청
상황: 네트워크 탭에 굉장히 빠른 간격으로 요청이 계속 쌓임
원인: 응답 헤더에 MIME Type이 누락되어, 플레이어가 재시도를 끊임없이 한 것 (혹은 제대로 된 영상 파일이 아니라고 인식)
대처: Nginx에 MIME Type 지정과, CORS 허용 헤더 작성
3. 지원하지 않는 오디오 포맷
상황: 아무 문제도 없는 데 (육안 상), 플레이어가 영상을 제대로 읽지 못함
원인: 원본 영상(FLV)의 오디오 소스가 비표준 규격이라 지원하지 않았던 것
대처: 원본 영상을 오디오와 비디오로 분리하여 지원하는 오디오 포맷으로 인코딩
[가장 골치 아팠던 것은 오디오 포맷]
단순히 원본 소스를 복사해서 송출하는 것이 아닌, 브라우저에서 재생하기 용이한 소스롤 Transcoding이 필요
-> Transcoding 서버의 필요성과 중요성에 대해 생각 해보는 경험이 됨
nginx 설정
location /hls {
types {
application/vnd.apple.mpegurl m3u8; # 지도의 규격 정의
video/mp2t ts; # 조각의 규격 정의
}
add_header 'Access-Control-Allow-Origin' '*' always; # CORS 해제
add_header Cache-Control no-cache;
}
FFmpeg 실행 수정
ffmpeg -re -i [입력] \
-vcodec libx264 -preset ultrafast \
-acodec aac -ac 2 -ar 44100 -b:a 128k \ # ★ 핵심: AAC 2채널 강제 교정
-f hls -hls_time 2 -hls_list_size 6 \
/tmp/hls/index.m3u8

[결론]
- 역시 로그가 제일 중요합니다.
어떻게든 돌아가게 만드는 것은 쉬운데, 막상 바이브 코딩이다 뭐다 하면서 어찌 됐든 돌아가면 원인 파악이 힘들었습니다.
일부러라도 불편하게 하나하나 만들어가니까 오히려 깊은 이해가 가능해졌습니다.
빠른 개발과 학습도 중요하지만 꼭, 무분별하게 주입 받은 지식이 그대로 흘러가지 않도록 한 번씩 갈무리하고 정리하는 시간이 필요한 것 같습니다.