다시보기를 위한 Node-Media-Server, FFMpeg 분석

서론

실시간 스트리밍 서비스에서 HLS(HLS, HTTP Live Streaming)는 널리 사용되는 프로토콜입니다. 그러나 다시보기 기능을 구현하기 위해서는 전체 m3u8 플레이리스트가 필요하며, 이는 세그먼트 파일이 분할되어 있는 기존 구조에서는 추가적인 처리가 필요합니다. 이번 글에서는 Node-Media-Server와 FFmpeg를 활용하여 이러한 문제를 해결하고, 세그먼트 길이를 고정하여 다시보기용 m3u8 파일을 생성하는 방법에 대해 다뤄보겠습니다.

본론

문제점 분석

기존 구조에서는 세그먼트 별로 재생 시간이 일정하지 않아 다시보기용 m3u8 파일을 생성하는 데 어려움이 있었습니다. 세그먼트 길이가 불규칙하면 클라이언트 측에서 이를 처리하기 위한 복잡한 로직이 필요하며, 이는 서비스의 안정성과 성능에 영향을 줄 수 있습니다.

FFmpeg 인코딩 옵션 탐구

세그먼트 길이를 일정하게 유지하기 위해 FFmpeg의 비디오 코덱 옵션인 vcParam을 활용할 수 있습니다. 주요 옵션은 다음과 같습니다:

  • g: 최대 GOP(Group of Pictures) 크기를 설정합니다.
  • sc_threshold: 씬 변경 감도를 감지하는 임계값을 설정합니다.
  • keyint_min: 최소 GOP 크기를 설정합니다.
  • force_key_frames: 키 프레임을 특정 시간 간격으로 강제로 삽입합니다. 예를 들어, expr:gte(t,n_forced*2)는 2초마다 키 프레임을 삽입합니다.

이러한 옵션을 조합하여 세그먼트 길이를 고정하면, 재생 시간의 일관성을 유지할 수 있습니다.

두 가지 접근 방법

  1. 키 프레임 고정 및 시작/종료 세그먼트 관리

    키 프레임 간격과 GOP 크기를 고정하여 세그먼트 길이를 일정하게 유지합니다. 방송 시작과 종료 시점의 세그먼트 번호를 저장하여, 방송 종료 시 다시보기용 m3u8 파일을 생성하고 스토리지에 저장합니다.

  2. 유동적인 키 프레임 설정 및 별도 플레이리스트 관리

    키 프레임을 유동적으로 설정하고, 라이브용과 다시보기용 m3u8 파일을 별도로 생성합니다. 다시보기용 m3u8 파일은 index{번호}.m3u8 형태로 분리되며, 클라이언트 측에서 이를 처리하는 로직이 필요합니다.

성능 및 품질 고려사항

  • 인코딩 부하: 화면 전환 감지를 활성화하면 인코딩 부하가 증가합니다. 이는 서버 자원에 부담을 줄 수 있습니다.
  • 영상 품질: 키 프레임을 강제로 고정하면 씬 변경 시 화질 저하가 발생할 수 있습니다.
  • 세그먼트 일정성: 세그먼트 길이가 일정하면 클라이언트 측에서의 처리 로직이 단순해집니다.

프로젝트의 요구사항과 서버의 성능을 고려하여 적절한 방법을 선택하는 것이 중요합니다.

최종 결정 및 구현 방안

화면 전환 감지 기능을 비활성화하고, GOP 크기를 조정하여 프레임 시간을 고정하는 방안을 선택했습니다. 이를 통해 세그먼트 길이를 일정하게 유지하면서 인코딩 부하는 최소화할 수 있습니다.

구현 단계는 다음과 같습니다:

  1. GOP 설정을 통한 키 프레임 간격 고정

    FFmpeg에서 -g 옵션을 사용하여 원하는 키 프레임 간격을 설정합니다.

  2. 세그먼트 번호 기반의 다시보기 m3u8 파일 생성

    처음 생성되는 세그먼트(번호 0)부터 현재 스토리지에 저장된 마지막 세그먼트까지의 정보를 사용하여 replay.m3u8 파일을 생성하고 업로드합니다.

  3. 메인 서버에서 m3u8 정보 관리

    RTMP 서버에서 세그먼트 파일만 처리하고, 메인 서버가 인메모리로 현재 방송의 m3u8 정보를 관리하여 클라이언트에게 제공합니다. 이를 통해 파일 입출력으로 인한 성능 저하를 방지할 수 있습니다.

결론

세그먼트 길이를 일정하게 유지하는 것은 다시보기 기능 구현과 클라이언트 측의 처리 로직 단순화에 큰 도움이 됩니다. 화면 전환 감지 기능을 비활성화하고 GOP 설정을 조정함으로써 인코딩 부하를 최소화하면서 원하는 목표를 달성할 수 있습니다.

이번 접근 방법은 서버 자원의 효율적인 활용과 서비스 품질 향상에 기여할 것으로 기대됩니다. 앞으로도 지속적인 모니터링과 최적화를 통해 더욱 안정적인 스트리밍 서비스를 제공하도록 노력하겠습니다.


참고 자료

기존 정리 ## 서론 기존에는 실시간으로 m3u8, hls 세그먼트 파일을 사용 다시보기를 위해서는 전체 m3u8이 필요 - 아니면 나눠진 m3u8을 클라이언트가 받아올 필요성이 있음 이를 해결하기 위해 기존 node-media-server와 ffmpeg 탐구 start ## 본론 기존에는 다시보기용 m3u8을 만들기 어려웠음 - 왜 why? 세그먼트 별로 플레이 시간이 달랐기 때문 그래서 좀 찾아보니 vcParam으로 비디오 코덱에 줄 수 있는 4가지 옵션 발견 - vcParam 사용법 - [ {옵션 1}, {값 1}, {옵션 2}, {값 2}, … ] - g - 최대 GOP를 설정할 수 있는 옵션 - GOP 관련 / [http://www.ktword.co.kr/test/view/view.php?no=3145](http://www.ktword.co.kr/test/view/view.php?no=3145) - 이거 공부하면 영상 처리 고도화 가능할듯 - sc_threshold - 씬 변경 감도를 감지하는threshold - keyint_min - 최소 GOP를 설정할 수 있는 옵션 - force_key_frames - 키 프레임 시간을 고정할 수 있는 옵션 - expr:gte(t,n_forced*{N}) - N초 만큼 키 프레임을 강제로 고정 위의 옵션을 잘 이리저리하면 세그먼트 길이 고정이 가능 - 그런데 영길님이랑 얘기해보니 강제로 키프레임 고정 하는게 괜찮나 생각이 듦 - 왜 와이 → 그만큼 프레임 처리가 많아 질 것 같다, 렌더링도 많이 될 것 같다. - 일단 아이디어는 확인했으니 차차 생각해볼 예정 위의 조사를 토대로 작성한 다시보기 데이터 생성 플로우 1. 키프레임을 고정하고, 방송의 시작/종료 세그먼트 번호를 저장한다. - 이를 통해 방송이 종료될 때 다시보기용 m3u8을 만들어서 스토리지에 저장 2. 키프레임을 유동적으로 설정하고 라이브용 m3u8, 다시보기용 m3u8을 만든다. - 이 때 다시보기용 m3u8은 index{번호}.m3u8로 분리 - 프론트에서 이 데이터를 처리하는 로직이 추가적으로 필요할 것 같음 - GPT 왈 - **첫 번째 방법**은 키프레임 간격과 GOP 크기를 고정하여 세그먼트 길이를 일정하게 유지하고 인코딩 부하를 줄이는 데 효과적입니다. 하지만 씬 변경 시 품질 저하가 발생할 수 있습니다. - **두 번째 방법**은 강제 키프레임 삽입과 씬 변경 감지를 통해 영상 품질을 향상시키지만, 인코딩 부하가 증가하고 세그먼트 길이가 변동될 수 있습니다. **따라서** 프로젝트의 요구 사항과 서버의 성능, 네트워크 환경, 영상 품질의 중요도 등을 고려하여 적절한 방법을 선택하시길 권장합니다. 아마 둘중 하나로 결정될 것 같은데 각 기능들이 서버에 주는 부하가 얼마나 될지 확인해보고 결정하는 게 좋을 듯 근데 생각해보면 화면 전환을 감지하는 게 더 리소스를 많이 먹는 작업이지 않나..? - 이건 진짜 모르겠음 - GPT왈왈 - **부하 측면에서**: 화면 전환 감지를 사용하는 것이 고정 키프레임을 사용하는 것보다 더 많은 **인코딩 부하**를 발생시킵니다. - **선택의 기준**: 인코딩 부하와 영상 품질, 압축 효율, 세그먼트 일정성 등 **프로젝트의 우선순위와 서버 자원**을 고려하여 적절한 방법을 선택하시기 바랍니다. 만약 영상 키프레임 고정이 가능하다면 rtmp 서버에서 m3u8을 계속 읽어서 오브젝트 스토리지로 안 보내도 될듯 - 멘토님께 여쭤보니 파일 읽고 쓰는건 생각 이상으로 비싼 작업이라서 뺄 수 있으면 좋다. - 그래서 그냥 rtmp 서버는 세그먼트만 읽어서 보내고, 차라리 메인서버가 인메모리로 현재 방송의 m3u8 정보를 계속 들고 있다가 쏴주는 방식은 어떨까 ## 결론 > 참고자료 >