user@namudi: ~

iOS로 왓챠 커스텀 비디오 플레이어 만들기: AVKit과 AVFoundation

>2024.05.03.
iOS에서 왓챠와 같은 커스텀 비디오 플레이어를 만드려면 어떻게 해야할까요?
iOS 14+ 환경에서는 AVKit에서 제공하는 VideoPlayer 뷰를 이용해 쉽고 간단하게 SwiftUI로 된 비디오 플레이어를 만들 수 있습니다. 하지만 우리가 만드려고 하는건 커스텀 비디오 플레이어이니 이를 만들기 위해서 AVFoundation과 AVKit에 대해서 공식문서를 통해 알아봅시다.

AVKit

공식문서를 살펴보면, 아래와 같은 설명이 나와있습니다.
미디어 재생을 위한 사용자 인터페이스를 생성하고, 여기에는 전송 컨트롤, 챕터 탐색, 화면 속 화면(PIP) 지원, 자막 및 폐쇄 캡션 표시(CC)가 포함됩니다.
음, 그렇지만 우리는 UI를 커스텀할 생각인데요. 한 번 더 찾아보겠습니다.
AVKit은 AVFoundation과 Core Media 위에 구축된 크로스 플랫폼 미디어 재생 UI 프레임워크입니다. Apple의 자체 앱과 동일한 사용자 인터페이스를 사용하여 AVPlayer 기반 미디어 콘텐츠를 쉽게 재생할 수 있습니다. UIKit 앱의 경우 AVKit은 AVPlayerViewController를 제공하는데, 이는 플레이어에서 콘텐츠를 표시하고 재생을 제어할 수 있는 기본 사용자 인터페이스를 제공합니다.
AVKit에서 제공하는 AVPlayerViewController는 기본적인 UI를 이미 제공한다고 합니다! 이렇게 보니 아무래도 간단하게 비디오 플레이어를 구현하는 경우에는 AVKit을 쓰는게 좋겠네요.
하지만 우리는 커스텀 비디오 플레이어를 만드려는 것이니 AVFoundation을 한 번 살펴봅시다.

AVFoundation

AVFoundation 공식문서의 내용은 방대한데요. 아무래도 제공하는 기능이 훨씬 많아서겠죠? 위의 AVKit의 설명에서도 AVKit은 AVFoundation 위에 구축되어 있는 프레임워크라고 합니다.
AVFoundation은 조금 더 Low-level한 프레임워크로 보입니다. 공식문서의 설명을 읽어보면,
AVFoundation은 iOS, macOS, watchOS 및 tvOS에서 시간 기반 시청각 미디어를 다루기 위한 완전한 기능의 프레임워크입니다. AVFoundation을 사용하면 QuickTime — Movies와 MPEG-4 파일을 쉽게 재생, 생성 및 편집하고, HLS 스트림을 재생하며, 앱에 강력한 미디어 기능을 구축할 수 있습니다.
“완전한 기능의 프레임워크”, “강력한 미디어 기능” 이라는 두 단어만 보아도 많은 기능을 제공하는 듯 합니다. 비디오 재생(Playback) 섹션을 찾아보면서 플레이어의 이동 행동 제어하기 문서를 찾아냈습니다.
AVFoundation은 로컬 및 원격 파일 기반 미디어와 HTTP 라이브 스트리밍으로 스트리밍되는 미디어를 포함하여 미디어 Asset 재생에 대한 포괄적인 지원을 제공합니다. 이 프레임워크는 AVAsset 클래스를 사용하여 미디어 Asset을 모델링하며, 이는 미디어의 유형이나 위치에 관계없이 미디어를 로드하고 검사할 수 있는 일관된 인터페이스를 제공합니다. AVPlayerItem 객체의 형태로 미디어 Asset을 재생하려면 AVPlayer 객체를 사용하십시오. 이 객체는 현재 시간(currentTime())과 같은 Asset의 동적 상태를 모델링합니다.
우리가 하려는 “(HTTP 라이브 스트리밍으로 스트리밍되는) 미디어의 재생”을 지원한다고 하니 잘 찾은 것 같습니다. AVAsset 클래스를 사용해 미디어의 유형이나 위치에 관계없이 미디어 Asset을 모델링하고, AVPlayerItem 객체의 형태로 미디어 Asset을 재생하려면 AVPlayer 객체를 사용하라고 하네요! currentTime()을 통해서는 미디어 Asset의 현재 시간과 같은 상태를 감지할 수 있는 것 같습니다.
AVKit과 AVFoundation 간의 관계도AVKit과 AVFoundation 간의 관계도

AVAsset

AVAsset부터 보면, “시간이 있는 시청각 매체를 모델링하는 개체”라고 합니다. 아마 오디오나 비디오와 같은 미디어를 모델링하는 객체겠죠?
Asset 모델은 QuickTime — Movies나 MP3 오디오 파일과 같은 파일 기반 미디어와 HTTP 라이브 스트리밍(HLS)을 사용하여 스트리밍되는 미디어를 포함합니다. Asset은 미디어의 균일하게 유형화된 트랙을 모델링하는 하나 이상의 AVAssetTrack 인스턴스에 대한 컨테이너 객체입니다. 가장 일반적으로 사용되는 트랙 유형은 오디오와 비디오이지만, Asset에는 자막, 자막 및 시간 메타데이터와 같은 보조 트랙도 포함될 수 있습니다.
AVAssetAVAsset
설명과 같이, 여러 개의 AVAssetTrack 인스턴스를 감싸는 컨테이너 객체가 AVAsset입니다. 오디오와 비디오도 트랙 유형으로 사용되지만, 자막 및 시간과 같은 메타데이터도 이 Asset에 포함된다고 합니다.

AVPlayer

이제 AVPlayer를 살펴봅시다. “플레이어의 이동 동작을 제어하는 인터페이스를 제공하는 객체” 라는 설명만 보아도 우리가 자주 쓰게 될 객체인 듯 합니다.
플레이어는 미디어 Asset의 재생 및 타이밍을 관리하는 컨트롤러 객체입니다. AVPlayer의 인스턴스를 사용하여 로컬 및 원격 파일 기반 미디어(예: QuickTime — Movies 및 MP3 오디오 파일)를 재생하고, HTTP 라이브 스트리밍을 통해 제공되는 시청각 미디어도 재생할 수 있습니다.
이 AVPlayer의 인스턴스를 로컬, 원격과 같은 미디어나 HLS를 통해 제공되는 미디어로 만들어서 재생하고 구간 이동이나 재생 속도 변경 등 미디어의 제어도 함께 수행할 수 있습니다. 우리가 만들 커스텀 비디오 플레이어에 필요한게 이런 기능들이니 AVPlayer를 활용하면 되겠네요!
AVPlayer 문서를 보면 여러 설명들이 나와있는데요.
AVPlayer를 사용해 미디어 Asset을 재생하며, AVFoundation은 AVAsset 클래스를 사용하여 이를 나타냅니다. AVAsset은 지속 시간이나 생성 날짜와 같은 미디어의 정적인 측면만 모델링하며, AVPlayer와 함께 재생하기에는 적합하지 않습니다. Asset을 재생하려면 AVPlayerItem에서 찾을 수 있는 동적 대응 객체의 인스턴스를 생성합니다. 이 객체는 AVPlayer의 인스턴스에서 재생되는 자산의 시간과 표시 상태를 모델링합니다. 자세한 내용은 AVPlayerItem을 참조하십시오.
AVPlayerItem에 대해서 찾아봐야겠네요! 그런데 그 전에, 설명을 조금만 더 봅시다.
AVPlayer는 상태가 지속적으로 변경되는 동적 객체입니다. 플레이어의 상태를 관찰하는 데 사용할 수 있는 두 가지 접근 방식이 있습니다:
  • 일반 상태 관찰: KVO를 사용하여 플레이어의 동적 속성 중 현재 항목(currentItem)이나 재생 속도(playback rate)와 같은 상태 변화를 관찰할 수 있습니다.
  • 시간 상태 관찰: KVO는 일반 상태 관찰에 잘 작동하지만 플레이어의 시간을 지속적으로 변경하는 상태를 관찰하기 위한 것이 아닙니다. AVPlayer는 시간 변화를 관찰하기 위한 두 가지 방법을 제공합니다: addPeriodicTimeObserver(forInterval:queue:using:) addBoundaryTimeObserver(forTimes:queue:using:)
AVPlayer 객체에서 add~Observer 함수들을 사용해서 currentItem의 상태 변화를 관찰해야겠네요. 커스텀 비디오 플레이어에서 하단 재생바(시커바)에 표시되는 부분들을 이 옵저버를 통해 업데이트해야 하니까요!
AVPlayer와 AVPlayerItem은 비시각적 객체로, 그 자체로는 Asset의 비디오를 화면에 표시할 수 없습니다. 화면에 비디오 콘텐츠를 표시하는 데 사용하는 두 가지 주요 접근 방식이 있습니다:
  • AVKit: 비디오 콘텐츠를 제공하는 가장 좋은 방법은 iOS와 tvOS의 AVKit 프레임워크의 AVPlayerViewController 클래스 또는 macOS의 AVPlayerView 클래스를 사용하는 것입니다. 이러한 클래스는 비디오 콘텐츠와 재생 제어 및 기타 미디어 기능을 함께 제공하여 완전한 재생 경험을 제공합니다. (SwiftUI에서는 VideoPlayer라는 뷰를 제공해줍니다.)
  • AVPlayerLayer: 플레이어를 위한 사용자 정의 인터페이스를 구축할 때 AVPlayerLayer를 사용하세요. 이 레이어를 뷰의 백업 레이어로 설정하거나 레이어 계층 구조에 직접 추가할 수 있습니다. AVPlayerView 및 AVPlayerViewController와 달리 플레이어 레이어는 재생 제어 기능을 제공하지 않으며 화면에 시각적 콘텐츠만 표시합니다. 미디어를 재생, 일시 중지 및 탐색할 수 있는 재생 전송 제어 기능을 구축하는 것은 귀하에게 달려 있습니다.
이제 핵심적인 부분들을 찾았습니다! 커스텀 비디오 플레이어를 구축할 때에는 AVPlayerLayer를 사용하라고 합니다. AVPlayer와 AVPlayerItem은 비시각적 객체라 비디오를 화면에 표시할 순 없다고 합니다.
위에서 말한 AVKit을 사용해 기본적인 UI가 포함되어 있는 비디오 플레이어를 가져다 쓸 수도 있고, 커스텀 UI를 구축하려면 AVPlayerLayer를 사용하면 됩니다.

AVPlayerLayer

플레이어 객체의 시각적 내용을 표시하는 객체.
아래처럼 UIView의 백업 레이어로 사용할 수 있다고 합니다.
/// A view that displays the visual contents of a player object.
class PlayerView: UIView {
 
    // Override the property to make AVPlayerLayer the view's backing layer.
    override static var layerClass: AnyClass { AVPlayerLayer.self }
    
    // The associated player object.
    var player: AVPlayer? {
        get { playerLayer.player }
        set { playerLayer.player = newValue }
    }
    
    private var playerLayer: AVPlayerLayer { layer as! AVPlayerLayer }
}
아무래도 UIKit을 사용해야겠네요!

Conclusion

AV Foundation 클래스들AV Foundation 클래스들
이번 포스팅의 내용을 정리하자면 위와 같은 구조가 됩니다.
이번에는 커스텀 비디오 플레이어를 만들기 위해서 그 기반이 되는 기술들을 공식 문서를 통해 찾아봤습니다.
다음 글에서는 이렇게 얻은 정보들을 바탕으로 직접 구현을 해보도록 할게요!

References