ExoPlayer: Adaptive video streaming on Android

안녕하세요 저는 Android 팀의 올리버입니다

저는 Android용 애플리케이션 수준 미디어 플레이어인 ExoPlayer를 설명할 것입니다 Android의 로우 레벨 미디어 API를 기반으로 개발된 ExoPlayer는 Android의 빌트인 미디어 플레이어에 비해 여러 가지 장점을 가지고 있습니다 여러분은 이 플레이어가 DASH와 SmoothStreaming 적응형 재생을 모두 지원한다는 소식이 반가울 것입니다 구글은 이미 ExoPlayer를 YouTube와 Play 무비에 이용하고 있습니다 그리고 이제 이 플레이어의 소스를 공개하여 여러분의 앱에서 이용할 수 있게 했습니다

저는 ExoPlayer 개발의 기반이 된 낮은 수준의 API를 보여드리고 애플리케이션 수준의 미디어 플레이어가 가진 장점을 설명한 다음 ExoPlayer를 여러분의 앱에 통합하는 법을 보여드릴 것입니다 ExoPlayer를 살펴보기 전에 먼저 Android의 미디어 API를 보겠습니다 Jelly Bean 이전 Android에서는 하이 레벨 미디어 플레이어 API가 개발자들이 비디오를 애플리케이션에 연결하는 실질적인 방법이었습니다 이것은 쉽게 사용할 수 있습니다 이 예는 코드 단 두 줄만으로 비디오 재생을 시작합니다

첫째 줄은 플레이어를 만들고 둘째 줄은 재생을 시작합니다 이 코드 두 줄은 비디오 재생의 복잡한 부분을 개발자가 보지 못하도록 거의 모두 숨깁니다 하지만 구현된 미디어 플레이어는 이면에서 비디오를 재생하기 위해 많은 작업을 합니다 스트리밍을 위해 플레이어가 가장 먼저 해야 할 일은 네트워크를 통해 비디오 데이터를 로드하는 것입니다 그 다음으로 데이터는 언제, 얼마나 데이터를 더 로드할지 규정하는 정책에 따라 버퍼링되어야 합니다

비디오를 렌더링하려면 개별 오디오와 비디오 샘플을 샘플이 전달된 컨테이너 형식에서 추출해야 합니다 그 다음으로 이 샘플을 디코딩해야 합니다 마지막으로 디코더 샘플을 화면과 스피커로 렌더링합니다 플레이어는 약간 높은 수준에서 재생 위치와 재생의 현재 진행 여부와 같은 전반적인 상태를 추적해야 합니다 미디어 플레이어 API는 복잡한 부분은 모두 숨기고 있기 때문에 쉽게 사용할 수 있습니다

하지만 이러한 방식은 고급 개발자가 본인의 필요에 따라 기본적인 동작을 수정하거나 확장할 수 없다는 단점이 있습니다 예를 들어 개발자는 버퍼링 정책을 변경하거나 버퍼링된 미디어 데이터를 위한 영구 캐시를 추가할 수 없습니다 이러한 경우를 보다 잘 지원하기 위해 저희는 Android에 로우 레벨 미디어 API를 추가했습니다 Jelly Bean 이후 버전에서는 MediaExtractor API가 네트워킹과 버퍼링, 미디어 추출 기능을 제공합니다 저희는 또한 하드웨어 가속 비디오 디코더를 포함하여 디코더에 대한 접근을 제공하는 MediaCodec API를 추가했습니다

MediaCodec API는 비디오 프레임 렌더링을 직접 지원합니다 그리고 디코딩된 오디오 재생은 Cupcake부터 이용된 AudioTrack API로 처리 가능합니다 이러한 로우 레벨 API는 개발자에게 Android 하이 레벨 미디어 플레이어의 대안을 제공합니다 이 API는 플레이어의 로직이 애플리케이션 코드로 구현되는 동시에 자바로 작성된 애플리케이션 수준 미디어 플레이어의 구축이 가능하도록 합니다 이 코드는 보통 제공된 API를 호출하여 미디어 샘플을 로드 및 추출하고 디코딩하며 렌더링합니다

개발자는 애플리케이션 수준에서 이 기능을 더 많이 구현하도록 선택할 수도 있습니다 예를 들어 네트워킹과 버퍼링, 샘플 추출을 구현하여 개발자는 보다 쉽게 이러한 컴포넌트를 커스터마이즈할 수 있습니다 구글에서 저희는 ExoPlayer라는 애플리케이션 수준의 미디어 플레이어를 자체 개발했습니다 이 플레이어는 DASH와 SmoothStreaming 적응형 재생을 모두 지원하고 DRM 보호 컨텐츠를 재생하며 쉽게 수정하고 확장할 수 있도록 설계되었습니다 구글의 YouTube와 Play 무비 애플리케이션은 이미 최근의 몇몇 Android 장치에서 ExoPlayer를 이용하고 있습니다

그리고 몇 가지 뛰어난 성과를 얻었습니다 예를 들어 Play 무비에서 ExoPlayer를 이용한 DASH 재생은 재생 시작 레이턴시를 65% 줄이는 것으로 나타났습니다 미디어 플레이어보다 리버퍼링 가능성은 40% 줄었고 비디오는 평균 11% 높은 해상도로 제공됩니다 ExoPlayer는 애플리케이션 수준 미디어 플레이어이므로 Play Store 업데이트를 통해 새 기능을 추가하고 기존 기능을 수정할 수 있습니다 그리고 이를 통해 이러한 수치들이 더 개선되기를 바랍니다

저희는 ExoPlayer를 통해 애플리케이션을 개선할 수 있어서 정말 즐겁습니다 하지만 많은 개발자들이 이와 같은 방식을 채택하기에 장벽이 높다는 사실을 알고 있습니다 ExoPlayer는 약 16,000 줄로 구성된 코드입니다 Android의 로우 레벨 미디어 API는 사용하기 어려울 수도 있습니다 따라서 이러한 플레이어를 무에서 창조하려면 상당한 노력이 필요합니다

이러한 이유로, ExoPlayer 소스 공개를 결정했습니다 이를 통해 제삼자 개발자들의 장벽이 낮아지고 아무 것도 없는 상태에서 자체적으로 애플리케이션 수준 미디어 플레이어를 만드는 비용 부담 없이 저희와 같은 수준의 개선 효과를 얻기를 바랍니다 이제 ExoPlayer가 어떻게 설계되었는지 애플리케이션에서 어떻게 활용 가능한지 알아보겠습니다 ExoPlayer는 단일 자바 개체가 아닌 각각 플레이어에 필요한 동작 또는 기능 일부분을 담당하는 여러 가지 모듈 컴포넌트로 구성되어 있습니다 맨 위 수준에는 전반적인 플레이어 상태 관리와 함께 약간의 추가 기능을 담당하는 ExoPlayer 개체 자체가 있습니다

이 개체는 재생 중에 미디어 렌더링을 담당하는 TrackRenderer라는 컴포넌트를 호출합니다 ExoPlayer 라이브러리는 MediaCodec API를 통해 비디오 샘플을 디코딩하고 서피스로 렌더링하는 비디오 재생을 담당하는 TrackRenderer를 제공합니다 이 TrackRenderer는 기초 디코더를 가진 비디오 형식을 지원합니다 보통 H264가 이용됩니다

ExoPlayer 라이브러리는 또한 MediaCodec API를 통해 오디오 샘플을 디코딩하고 AudioTrack API로 재생하게 하는 오디오 재생을 담당하는 TrackRenderer를 제공합니다 오디오 및 비디오 TrackRenderer를 구현하려면 샘플을 얻을 수 있는 SampleSource 컴포넌트가 함께 제공되어야 합니다 네트워킹, 버퍼링, 샘플 추출 동작을 제어할 필요가 없는 개발자를 위해 ExoPlayer 라이브러리는 Android의 MediaExtractor API에 연결된 FrameworkSampleSource라는 구현을 제공합니다 이를 통해 라이브러리는 현재 작동 중인 Android 버전에서 지원되는 모든 컨테이너 형식을 지원합니다 여기에 해당하는 코드를 한 번 보겠습니다

먼저, 비디오가 있는 URI를 제공하는 샘플 소스를 만듭니다 그 다음으로 샘플 소스를 제공하는 오디오 및 비디오 렌더러를 만듭니다 마지막으로 두 렌더러를 제공하고 재생을 시작하는 ExoPlayer 인스턴스를 만듭니다 이것은 간단한 ExoPlayer 사용 방법의 예입니다 이제 좀 더 복잡한 경우인 ExoPlayer의 DASH 및 SmoothStreaming 적응형 재생 지원 방법을 알아보겠습니다

DASH와 SmoothStreaming을 위해 ExoPlayer 라이브러리는 Android의 MediaExtractor API를 완전히 대체하는 컴포넌트를 포함합니다 ChunkSampleSource라는 컴포넌트의 개별 인스턴스는 오디오와 비디오 샘플을 렌더러에 제공하는 데 사용됩니다 각 ChunkSampleSource에는 미디어의 청크, 특히 2초에서 10초 분량의 청크를 구할 수 있는 ChunkSource 컴포넌트가 필요합니다 여기서는 DASHMP4ChunkSource 컴포넌트를 이용하는 DASH 재생을 집중 설명합니다 마지막으로 각 ChunkSource는 데이터 소스를 필요로 합니다

이름에서 알 수 있는 것처럼 DataSource 컴포넌트는 실제 미디어 로딩을 담당합니다 이 경우 네트워크에서 데이터를 이동하는 HTTPDataSource를 이용합니다 DASH 오디오 및 비디오 재생은 보통 별개로 스트리밍되기 때문에 HTTPDataSource 컴포넌트가 하나가 아닌 두 개입니다 다시 돌아가서 코드를 보면 각 컴포넌트에 종속 컴포넌트를 어떻게 삽입했는지 알 수 있습니다 VideoRenderer에는 SampleSource를 삽입하고 SampleSource에는 ChunkSource를 삽입하며 ChunkSource에는 DataSource를 삽입했습니다

종속 항목 삽입을 통해 컴포넌트를 많이 활용하여 플레이어를 만드는 이러한 방식을 통해 애플리케이션 개발자는 컴포넌트의 전체나 일부를 자신의 커스텀 구현으로 쉽게 교체할 수 있습니다 지금까지 보여드린 것은 살짝 간소화한 개체 모델입니다 전체 예에는 컴포넌트가 약간 더 필요합니다 마지막으로 미디어 청크의 버퍼링을 관리하려면 LoadControl이라는 것이 필요합니다 이어서 적응형 재생에 가장 중요한 부분으로 각 ChunkSource는 FormatEvaluator라는 것이 필요합니다

FormatEvaluator는 각 미디어 청크가 요청되기 전에 이용 가능한 형식 중에서 선택을 합니다 여기서는 오디오를 위해 한 가지 형식을 고수하는 고정된 구현을 이용합니다 비디오의 경우 현재 네트워크 상태에 맞춰 버퍼링을 일으키지 않고 사용자에게 최대한 높은 품질을 제공하도록 형식을 선택하는 적응형 구현을 이용합니다 이제 ExoPlayer 라이브러리가 제공하는 표준 컴포넌트로 적응형 재생을 하는 ExoPlayer를 만드는 법을 알게 되었습니다 또한 애플리케이션 개발자가 특정 용도에 맞게 플레이어를 쉽게 커스터마이즈할 수 있음을 설명했습니다

이제 몇 가지 예를 보겠습니다 개발자가 버퍼링된 미디어 데이터를 위한 영구 캐시를 추가하여 사용자가 똑같은 비디오를 두 번 볼 경우 두 번째에는 캐시를 통해 재생되기를 원한다고 가정해봅시다 ExoPlayer 모델에서 이 작업은 쉽습니다 개발자는 플레이어를 만들 때 CacheDataSource 컴포넌트를 삽입하기만 하면 됩니다 CacheDataSource 컴포넌트는 ExoPlayer 라이브러리에서 제공됩니다

하지만 개발자 역시 자유롭게 자신만의 구현을 처음부터 작성할 수 있습니다 마찬가지로 개발자는 다른 버퍼링 동작을 만들거나 비디오 위에 오버레이를 렌더링하는 등과 같이 완전히 맞춤화된 TrackRenderer를 구현하기 위해 기본 LoadControl 구현을 교체할 수 있습니다 적용형 재생에 가장 많이 사용되는 기능으로 CustomFormatEvaluator 삽입 기능이 있습니다 이를 통해 개발자는 자신의 적응형 알고리즘을 시험할 수 있습니다 ExoPlayer를 확장하고 커스터마이즈하는 방법은 이외에도 많습니다

개발자들이 그런 방법으로 무엇을 만들지 정말 기대가 됩니다 정리하면 우리는 Android의 로우 레벨 미디어 API로 Android 표준 미디어 플레이어보다 개발자가 쉽게 업데이트하고 수정 및 확장할 수 있는 강력한 애플리케이션 수준의 미디어 플레이어를 개발하는 방법을 살펴봤습니다 구글은 ExoPlayer를 통해 구글 애플리케이션에 몇 가지 뛰어난 이점을 제공하고 있습니다 저희는 오픈 소스 ExoPlayer를 통해 다른 개발자도 이러한 성과를 거두기를 희망합니다 궁금한 점이나 더 알고 싶은 부분이 있다면 개발자 가이드를 살펴보세요

수많은 유용한 정보와 함께 여러분의 시작을 돕는 멋진 예제를 볼 수 있을 겁니다