제품 소식

미디어 재생 개선: Media3을 사용한 미리 로드 소개 - 1부

8분 읽기
Mayuri Khinvasara Khabya
개발자 관계팀 엔지니어

미디어를 중심으로 하는 오늘날의 앱에서는 원활하고 중단 없는 재생 환경을 제공하는 것이 즐거운 사용자 경험을 제공하는 데 핵심입니다. 사용자는 동영상이 즉시 시작되고 일시중지 없이 원활하게 재생되기를 기대합니다.

핵심 과제는 지연 시간입니다. 일반적으로 동영상 플레이어는 사용자가 재생할 항목을 선택한 후에만 연결, 다운로드, 파싱, 버퍼링과 같은 작업을 시작합니다. 이러한 반응형 접근 방식은 오늘날의 짧은 형식 동영상 컨텍스트에서는 느립니다. 해결 방법은 사전 대응하는 것입니다. 사용자가 다음에 시청할 콘텐츠를 예측하고 미리 콘텐츠를 준비해야 합니다. 이것이 미리 로드의 본질입니다.

미리 로드의 주요 이점은 다음과 같습니다.

  • 🚀 더 빠른 재생 시작: 동영상이 이미 준비되어 있어 항목 간 전환이 더 빠르고 즉시 시작됩니다.
  • 📉 버퍼링 감소: 데이터를 사전에 로드하므로 네트워크 문제 등으로 인해 재생이 중단될 가능성이 훨씬 적습니다.
  • ✨ 더 원활한 사용자 경험: 더 빠른 시작과 버퍼링 감소의 조합으로 사용자가 즐길 수 있는 더 유연하고 원활한 상호작용이 이루어집니다.

이 3부작 시리즈에서는 구성요소를 (미리) 로드하기 위한 Media3의 강력한 유틸리티를 소개하고 자세히 살펴봅니다.

  • 1부에서는 Media3에서 사용할 수 있는 다양한 미리 로드 전략을 이해하고, PreloadConfiguration을 사용 설정하고, DefaultPreloadManager를 설정하고, 앱에서 항목을 미리 로드할 수 있도록 하는 등 기본사항을 다룹니다. 이 블로그를 마치면 구성된 순위 및 재생 시간으로 미디어 항목을 미리 로드하고 재생할 수 있습니다.
  • 2부에서는 분석을 위한 리스너 사용, 슬라이딩 윈도우 패턴과 DefaultPreloadManager 및 ExoPlayer의 맞춤 공유 구성요소와 같은 프로덕션 준비가 완료된 권장사항 살펴보기 등 DefaultPreloadManager의 고급 주제를 다룹니다.
  • 3부에서는 DefaultPreloadManager를 사용하여 디스크 캐싱을 자세히 살펴봅니다.

미리 로드가 도와줍니다! 🦸‍♀️

미리 로드의 핵심 아이디어는 간단합니다. 미디어 콘텐츠를 필요하기 전에 로드하는 것입니다. 사용자가 다음 동영상으로 스와이프할 때 동영상의 첫 번째 세그먼트가 이미 다운로드되어 즉시 재생할 수 있습니다.

레스토랑을 생각해 보세요. 바쁜 주방에서는 주문이 들어올 때까지 양파를 썰기 시작하지 않습니다. 🧅 미리 준비 작업을 합니다. 미리 로드는 동영상 플레이어의 준비 작업입니다.

미리 로드를 사용 설정하면 사용자가 다음 항목으로 건너뛸 때 재생 버퍼가 다음 항목에 도달하기 전에 조인 지연 시간을 최소화하는 데 도움이 됩니다. 다음 창의 첫 번째 기간이 준비되고 동영상, 오디오, 텍스트 샘플이 버퍼링됩니다. 미리 로드 기간은 이후 버퍼링된 샘플을 즉시 사용할 수 있는 플레이어 큐에 추가되며 렌더링을 위해 코덱에 피드하도록 준비됩니다.

Media3에는 미리 로드를 위한 두 가지 기본 API가 있으며 각각 다른 사용 사례에 적합합니다. 올바른 API를 선택하는 것이 첫 번째 단계입니다.

1. PreloadConfiguration으로 재생목록 항목 미리 로드

재생 순서를 예측할 수 있는 재생목록과 같은 선형 순차 미디어에 유용한 간단한 접근 방식입니다 (예: 일련의 에피소드). ExoPlayer의 재생목록 API를 사용하여 플레이어에 미디어 항목의 전체 목록을 제공하고 플레이어의 PreloadConfiguration을 설정하면 구성된 대로 시퀀스의 다음 항목이 자동으로 미리 로드됩니다. 이 API는 재생 버퍼가 이미 다음 항목과 겹치기 전에 사용자가 다음 항목으로 건너뛸 때 조인 지연 시간을 최적화하려고 시도합니다.

미리 로드는 현재 재생을 위해 미디어가 로드되지 않을 경우에만 시작되므로 기본 재생과 동일한 대역폭을 사용하지 않습니다.

미리 로드가 필요한지 아직 잘 모르겠다면 이 API를 사용해 보는 것이 좋습니다.

player.preloadConfiguration =
    PreloadConfiguration(/* targetPreloadDurationUs= */ 5_000_000L)

위와 같은 PreloadConfiguration을 사용하면 플레이어가 재생목록에서 다음 항목 미디어의 5초 분량을 미리 로드하려고 시도합니다.

재생목록 미리 로드를 사용 설정한 후에는 PreloadConfiguration.DEFAULT를 사용하여 재생목록 미리 로드를 다시 사용 중지할 수 있습니다.

player.preloadConfiguration = PreloadConfiguration.DEFAULT

2. PreloadManager로 동적 목록 미리 로드

'다음' 항목이 사용자 상호작용에 의해 결정되는 세로 피드 또는 캐러셀과 같은 동적 UI의 경우 PreloadManager API가 적합합니다. 이는 사전에 미리 로드하도록 특별히 설계된 Media3 ExoPlayer 라이브러리 내의 강력한 새로운 독립형 구성요소입니다. 잠재적인 MediaSource 컬렉션을 관리하고 사용자의 현재 위치와의 근접성을 기준으로 우선순위를 지정하며 미리 로드할 항목을 세부적으로 제어할 수 있어 짧은 형식 동영상의 동적 피드와 같은 복잡한 시나리오에 적합합니다.

PreloadManager 설정

DefaultPreloadManager는 PreloadManager의 표준 구현입니다.

DefaultPreloadManager의 빌더는 DefaultPreloadManager와 미리 로드된 콘텐츠를 재생할 ExoPlayer 인스턴스를 모두 빌드할 수 있습니다. DefaultPreloadManager를 만들려면 미리 로드 관리자가 항목에 로드할 양을 확인하기 위해 쿼리할 수 있는 TargetPreloadStatusControl을 전달해야 합니다. 아래 섹션에서 TargetPreloadStatusControl의 예를 설명하고 정의합니다.

val preloadManagerBuilder =
DefaultPreloadManager.Builder(context, targetPreloadStatusControl)
val preloadManager = val preloadManagerBuilder.build()

// Build ExoPlayer with DefaultPreloadManager.Builder
val player = preloadManagerBuilder.buildExoPlayer()

ExoPlayer와 DefaultPreloadManager 모두에 동일한 빌더를 사용하는 것이 필요하며, 이를 통해 내부 구성요소가 올바르게 공유됩니다.

이상입니다 이제 관리자가 명령을 받을 준비가 되었습니다.

TargetPreloadStatusControl로 재생 시간 및 순위 구성

예를 들어 동영상 10초를 미리 로드하려면 어떻게 해야 할까요? 캐러셀에서 미디어 항목의 위치를 제공할 수 있으며, DefaultPreloadManager는 사용자가 현재 재생 중인 항목과의 근접성을 기준으로 항목 로드에 우선순위를 지정합니다.

항목의 미리 로드할 재생 시간을 제어하려면 DefaultPreloadManager.PreloadStatus를 반환하여 이를 알릴 수 있습니다.

예를 들면 다음과 같습니다.

  • 항목 'A'는 우선순위가 가장 높으며 동영상 5초를 로드합니다.
  • 항목 'B'는 우선순위가 중간이지만 항목에 도달하면 동영상 3초를 로드합니다.
  • 항목 'C'는 우선순위가 낮으며 트랙만 로드합니다.
  • 항목 'D'는 우선순위가 더 낮으며 준비만 합니다.
  • 다른 항목은 멀리 떨어져 있으므로 미리 로드하지 않습니다.

이러한 세부적인 제어를 통해 리소스 사용률을 최적화할 수 있으며, 이는 원활한 재생에 권장됩니다.

import androidx.media3.exoplayer.DefaultPreloadManager.PreloadStatus


class MyTargetPreloadStatusControl(
    currentPlayingIndex: Int = C.INDEX_UNSET
) : TargetPreloadStatusControl<Int,PreloadStatus> {


    // The app is responsible for updating this based on UI state
    override fun getTargetPreloadStatus(index: Int): PreloadStatus? {

        val distance = index - currentPlayingIndex

        // Adjacent items (Next): preload 5 seconds
        if (distance == 1) { 
        // Return a PreloadStatus that is labelled by STAGE_SPECIFIED_RANGE_LOADED and suggest loading // 5000ms from the default start position
                    return PreloadStatus.specifiedRangeLoaded(5000L)
                } 

        // Adjacent items (Previous): preload 3 seconds
        else if (distance == -1) { 
        // Return a PreloadStatus that is labelled by STAGE_SPECIFIED_RANGE_LOADED //and suggest loading 3000ms from the default start position
                    return PreloadStatus.specifiedRangeLoaded(3000L)
                } 

        // Items two positions away: just select tracks
        else if (distance) == 2) {
        // Return a PreloadStatus that is labelled by STAGE_TRACKS_SELECTED
                    return PreloadStatus.TRACKS_SELECTED
                } 

        // Items four positions away: just select prepare
        else if (abs(distance) <= 4) {
        // Return a PreloadStatus that is labelled by STAGE_SOURCE_PREPARED
                    return PreloadStatus.SOURCE_PREPARED
                }

             // All other items are too far away
             return null
            }
}

도움말: PreloadManager는 이전 항목과 다음 항목을 모두 미리 로드할 수 있지만 PreloadConfiguration은 다음 항목만 미리 로드합니다.

미리 로드 항목 관리

관리자를 만든 후에는 관리자에게 작업할 항목을 알려줄 수 있습니다. 사용자가 피드를 스크롤하면 예정된 동영상을 식별하고 관리자에 추가합니다. PreloadManager와의 상호작용은 UI와 미리 로드 엔진 간의 상태 기반 대화입니다.

1. 미디어 항목 추가

피드를 채울 때 관리자에게 추적해야 하는 미디어를 알려야 합니다. 시작하는 경우 미리 로드할 전체 목록을 추가할 수 있습니다. 그런 다음 필요할 때마다 목록에 단일 항목을 계속 추가할 수 있습니다. 미리 로드 목록에 있는 항목을 완전히 제어할 수 있으므로 관리자에서 추가되고 삭제되는 항목도 관리해야 합니다.

val initialMediaItems = pullMediaItemsFromService(/* count= */ 20)
for (index in 0 until initialMediaItems.size) {
    preloadManager.add(
        initialMediaItems.get(index),index)
    )
}

이제 관리자가 백그라운드에서 이 MediaItem의 데이터 가져오기를 시작합니다.

추가한 후 관리자에게 새 목록을 다시 평가하도록 지시합니다 (항목 추가/ 삭제 또는 사용자가 새 항목을 재생하도록 전환하는 등 변경사항이 있음을 나타냄).

preloadManager.invalidate()

2. 항목 검색 및 재생

여기에서 기본 재생 로직이 나옵니다. 사용자가 동영상을 재생하기로 결정한 경우 새 MediaSource를 만들 필요가 없습니다. 대신 PreloadManager에 이미 준비된 항목을 요청합니다. MediaItem을 사용하여 미리 로드 관리자에서 MediaSource를 검색할 수 있습니다.

PreloadManager에서 검색된 항목이 null이면 mediaItem이 아직 미리 로드되지 않았거나 PreloadMamager에 추가되지 않았음을 의미하므로 mediaItem을 직접 설정하도록 선택합니다.

// When a media item is about to displ​​ay on the screen
val mediaSource = preloadManager.getMediaSource(mediaItem)
if (mediaSource!= null) {
  player.setMediaSource(mediaSource)
} else {
  // If mediaSource is null, that mediaItem hasn't been added yet.
  // So, send it directly to the player.
  player.setMediaItem(mediaItem)
}
player.prepare()
// When the media item is displaying at the center of the screen
player.play()

PreloadManager에서 검색된 MediaSource를 준비하면 이미 메모리에 있는 데이터를 사용하여 미리 로드에서 재생으로 원활하게 전환할 수 있습니다. 이렇게 하면 시작 시간이 빨라집니다.

3. 현재 색인을 UI와 동기화

피드 / 목록이 동적일 수 있으므로 미리 로드 관리자가 항상 미리 로드를 위해 현재 색인에 가장 가까운 항목에 우선순위를 지정할 수 있도록 현재 재생 중인 색인을 미리 로드 관리자에게 알리는 것이 중요합니다.

preloadManager.setCurrentPlayingIndex(currentIndex)
// Need to call invalidate() to update the priorities
preloadManager.invalidate()

4. 항목 삭제

관리자를 효율적으로 유지하려면 사용자의 현재 위치에서 멀리 떨어진 항목과 같이 더 이상 추적할 필요가 없는 항목을 삭제해야 합니다.

// When an item is too far from the current playing index
preloadManager.remove(mediaItem)

모든 항목을 한 번에 삭제해야 하는 경우 preloadManager.reset()을 호출하면 됩니다.

5. 관리자 출시

PreloadManager가 더 이상 필요하지 않은 경우 (예: UI가 삭제된 경우) 리소스를 확보하기 위해 PreloadManager를 출시해야 합니다. 이를 수행하기에 적합한 위치는 이미 플레이어의 리소스를 출시하고 있는 곳입니다. 더 이상 미리 로드가 필요하지 않은 경우 플레이어가 계속 재생될 수 있으므로 플레이어 전에 관리자를 출시하는 것이 좋습니다.

// In your Activity's onDestroy() or Composable's onDispose
preloadManager.release()

데모 시현

실제로 작동하는지 확인해 보세요 👍

아래 데모에서는 미리 로드 관리자의 영향을 오른쪽에서 확인할 수 있습니다. 미리 로드 관리자는 로드 시간이 더 빠르지만 왼쪽에서는 기존 환경을 보여줍니다. 데모의 코드 샘플도 볼 수 있습니다. (보너스: 모든 동영상의 시작 지연 시간도 표시됨)

Demo-PreloadManager_2.webp

다음 단계

1부가 마무리되었습니다. 이제 동적 미리 로드 시스템을 빌드할 수 있는 도구가 있습니다. PreloadConfiguration을 사용하여 ExoPlayer에서 재생목록의 다음 항목을 미리 로드하거나 DefaultPreloadManager를 설정하고, 항목을 즉시 추가 및 삭제하고, 대상 미리 로드 상태를 구성하고, 재생을 위해 미리 로드된 콘텐츠를 올바르게 검색할 수 있습니다.

2부 에서는 DefaultPreloadManager를 자세히 살펴봅니다. 미리 로드 이벤트를 수신 대기하는 방법을 살펴보고, 메모리 문제를 방지하기 위해 슬라이딩 윈도우를 사용하는 것과 같은 권장사항을 논의하고, ExoPlayer 및 DefaultPreloadManager의 맞춤 공유 구성요소를 자세히 살펴봅니다.

공유할 의견이 있으신가요? 여러분의 의견을 기다립니다.

계속 지켜봐 주시고 앱을 더 빠르게 만들어 보세요. 🚀

작성자:

계속 읽기