라이브 스트리밍을 위한 HLS 재생 개선

Picture of bsp-admin-1
bsp-admin-1
블로그 플레이스홀더 이미지

브라이트코브는 브라이트코브 플레이어 내에서 HLS 재생을 더 빠르고, 더 안정적으로 개선하기 위해 노력하고 있습니다. 이를 위해 우리는 가정을 버리고 아무런 선입견 없이 직면한 문제를 바라보기 시작했습니다.

도전 과제

미디어 소스 확장(MSE)을 활용하는 모든 재생 엔진의 주요 책임 중 하나는 주어진 시간에 서버에 요청할 동영상 데이터(세그먼트 또는 조각이라고 함)에 대한 결정을 내리는 것입니다.

주문형 비디오(VOD) HLS 소스의 경우 결정은 매우 간단합니다. 모든 세그먼트와 (대략적인) 길이를 알고 있기 때문입니다. 이러한 정보를 바탕으로 어떤 세그먼트를 다운로드할지 선택하는 것은 간단합니다.

안타깝게도 라이브 HLS 스트림에서는 상황이 그렇게 간단하지 않습니다. 세그먼트의 전체 히스토리가 부족할 뿐만 아니라 PROGRAM-DATETIME 태그(최근 HLS 사양에 추가됨)를 지원하지 않기 때문에 다양한 재생 목록에서 세그먼트를 상호 연관시킬 수 있는 쉬운 방법도 없습니다. 플레이어에게 남은 유일한 옵션은 미디어의 내부 타임스탬프를 사용하기 위해 세그먼트를 추측적으로 다운로드하는 것입니다.

즉, 라이브 재생의 문제점은 '알 수 없는' 부분이 많아서 처음에 올바른 세그먼트를 선택하는 것이 어려울 때가 있다는 것입니다.

가져오기 알고리즘

가져오기 알고리즘이 잘못된 세그먼트를 선택하는 경향을 방지하기 위해 제어 이론에서 몇 가지 개념을 차용했습니다. 이전에는 가져오기 알고리즘이 그랬죠:

  • 제한된 정보를 바탕으로 가능한 최선의 추측을 하세요.
  • 추측이 틀렸다면 해당 요청에서 얻은 정보를 사용하여 더 나은 추측을 합니다('오류'를 낮추세요).
  • 반복

알고리즘이 반복적으로 개선되어 결국 올바른 세그먼트를 다운로드할 수 있기를 바랐습니다. 문제는 '오류'가 무엇인지 생각하기 시작할 때 발생합니다. 저희 알고리즘에서는 오류를 비디오 버퍼에서 데이터가 누락된 영역으로 정의했습니다.

여기서 생각해 볼 수 있는 것은 세그먼트 A를 가져온 다음 세그먼트 C를 가져온 경우, 채워야 할 B 크기의 간격이 생겼을 것입니다. 그러면 알고리즘은 역추적하여 이 오류를 채우고 세그먼트 B를 선택한 다음 D로 계속 진행해야 합니다.

가져오기 알고리즘

좋은 소식은 99%의 경우 이 방법이 제대로 작동하고 꽤 잘 작동한다는 것입니다. 나쁜 소식은 1%의 경우 본질적으로 메울 수 없는 간격을 메우려다 멈추는 경우가 있었다는 것입니다. 이런 문제가 발생하는 경우는 대개 재생하는 소스의 특성 때문이었습니다. 일부 HLS 소스는 세그먼트가 제대로 분할되지 않아 오디오와 비디오가 모든 변형에서 동일한 시점에 분할되지 않아 갭이 발생했습니다. 일부 HLS 소스에는 프레임(오디오 또는 비디오)이 손상되거나 누락되어 갭이 발생하는 경우도 있습니다.

원인이 무엇이든, 버퍼의 채울 수 없는 부분 때문에 알고리즘이 버퍼를 채우려다 멈추는 상황이 발생했습니다. 결국 저희는 페처가 멈추지 않도록 여러 가지 접근 방식을 도입했습니다:

  • 아주 작은 차이는 소스에 내재된 것으로 간주하고 무시하세요.
  • 알고리즘이 마지막 반복 중에 동일한 세그먼트를 가져오려고 시도한 적이 있는 경우 하나 이상의 세그먼트를 앞으로 가져오도록 강제합니다.
  • 불필요한 대역폭 낭비를 방지하기 위해 버퍼에 90% 이상의 바운드가 표시된 세그먼트는 로드된 것으로 간주합니다.

이러한 각 전략의 문제점은 고장이 발생하는 매우 구체적인 상황이 있다는 것입니다. '수정'을 시도할 때마다 코너 케이스의 수는 점점 늘어났습니다. 많은 경우, 이전에는 작동하던 가져오기 알고리즘을 조금만 변경해도 이상한 시나리오에서 실패하는 것을 발견했습니다.

새로 시작하기

이러한 모든 문제를 통해 피할 수 없는 결론에 도달했습니다: 접근 방식에 과감한 변화가 필요했습니다. 문제를 조사한 결과, 가져오기 알고리즘이 작동하는 방식에 대한 가정이 너무 많아 문제를 더욱 어렵게 만들고 있다는 사실을 깨달았습니다.

이러한 가정 중 하나는 가져오기 알고리즘이 항상 이미 버퍼링된 세그먼트의 요청을 피해야 한다는 것이었습니다. 문제는 탐색, 버퍼 가비지 수집(MSE가 백그라운드에서 자동으로 수행하는 작업), 자연스럽게 간격을 발생시키는 소스 등의 효과를 결합하면 버퍼의 상태를 추론하기가 매우 어렵다는 점입니다. 결국, 우리 알고리즘은 끊임없이 변화하는 MSE의 버퍼에 절대적으로 의존할 수밖에 없다는 뜻이었습니다.

새로운 페처는 가능한 한 단순화하기 위해 이 가정과 다른 많은 가정을 제거했습니다. 예를 들어, 이제 플레이어는 모든 탐색 후에 버퍼를 정리하여 버퍼의 상태를 더 쉽게 추론하고 버퍼에 이미 존재하는 세그먼트가 로드되지 않도록 방지합니다.

걷는 길

가정을 재검토한 결과, 100% 정확하게 추측하는 것은 불가능하지만 100% 보수적으로 추측하는 것은 전적으로 가능하다는 것을 깨달았습니다. 보수적인 추측이란 원하는 세그먼트의 앞이나 뒤에 있는 세그먼트를 추측하는 것입니다. 보수적으로 추측한다는 것은 재생 목록의 세그먼트를 앞으로 이동하기만 하면 항상 올바른 세그먼트를 찾을 수 있다는 것을 의미합니다.

이러한 이해를 바탕으로 문제의 성격을 크게 바꿨습니다. 이제 우리는 항상 초기 추측을 한 후 인접한 영역을 가져옵니다. 즉, 버퍼의 상태(간격)에 대한 세부 사항은 정의상 가져오기 알고리즘의 동작으로 인한 것이 아니라 미디어에 내재된 것이므로 더 이상 걱정할 필요가 없습니다.

새로운 세그먼트 페처

남은 유일한 질문은 라이브 재생 목록에서 세그먼트와 시간 사이의 변형을 어떻게 연관시킬 것인가 하는 것이었습니다. 이를 위해 "동기화 지점"이라는 개념을 도입했습니다. 동기화 지점은 세그먼트 인덱스와 표시 시간 사이의 알려진 매핑, 즉 다음을 호출하여 얻은 시간으로 정의됩니다. player.currentTime().

새로운 가져오기 알고리즘의 작동 모드는 단 세 가지입니다:

  • 다운로드를 시작할 세그먼트를 보수적으로 추측하는 방법
  • 재생 목록을 통해 앞으로 걸어가기만 하면
  • 동기화 지점 생성을 시도 중입니다.

마지막 상태는 페처가 저장된 정보를 사용하여 추측할 수 없는 경우에만 입력됩니다. 드문 경우이지만 이런 일이 발생하면 세그먼트를 다운로드하고 미디어의 내부 타임스탬프를 활용하여 '동기화 지점'을 생성한 다음 가져오기 알고리즘이 앞으로 나아가기 전에 보수적인 추측을 하는 데 사용할 수 있는 '동기화 지점'을 생성해야 합니다.

이러한 변경 사항의 최종 결과는 향상된 HLS 재생 환경입니다. 특히 라이브 재생이 더 빠르게 시작되고 더 안정적으로 재생됩니다.

브라이트코브는 한 진단 장비 제조업체가 수업 시간과 비용을 줄이면서 성공을 개선할 수 있도록 지원했습니다.
브라이트코브는 가장 유명한 자동차 시장에서 방대한 레거시 비디오 라이브러리를 관리하고 수익을 창출할 수 있도록 지원합니다....
브랜드 무결성을 유지하기 위해 리테일 브랜드는 색상, 글꼴 등을 조정할 수 있는 맞춤형 동영상 플레이어가 필요합니다.

시작할 준비가 되셨나요?

브라이트코브로 영상 마케팅 성과와 ROI를 높이는 방법을 알아보세요. 지금 문의하시면 자세히 상담해드립니다.