$30 off During Our Annual Pro Sale. View Details »

드로이드나이츠 2024 - compose-video 라이브러리 개발기 (배포까지)

드로이드나이츠 2024 - compose-video 라이브러리 개발기 (배포까지)

드로이드나이츠 2024에서 발표한 "compose-video 라이브러리 개발기 (배포까지)" 의 발표 자료입니다.

Dora Lee

June 14, 2024
Tweet

More Decks by Dora Lee

Other Decks in Programming

Transcript

  1. INDEX 질문이 있다면 여기서! (slido) ❏ 1. compose-video 라이브러리 소개

    ❏ 2. compose-video 탄생 비화 ~ 개발 여정 ❏ 3. 라이브러리 배포하기 (compose-video 라이브러리로 예시) ❏ Step 0. JavaDoc 호환 가능 주석 달기 ❏ Step 1. 라이브러리 샘플 앱 만들기 ❏ Step 2. Maven Central Portal 가입 + Namespace 등록 ❏ Step 3. 라이브러리 서명하기 ❏ Step 4. 배포 스크립트 작성 + Maven Central 에 배포하기
  2. compose-video ❏ https://github.com/dsa28s/compose-video ❏ Jetpack Compose 환경에서 ExoPlayer 를 원활히

    사용하고 TextureView(PlayerView)를 사용하기 위해 탄생한 라이브러리 ❏ 라이브러리 내부에 ExoPlayer Player 인스턴스를 Lifecycle 에 알아서 핸들링하며, 다양한 기능들 지원
  3. compose-video 왜 만들게 되었나요? 유저가 비디오를 스트리밍 할 수 있어야

    하고 재생, 일시정지, 전체화면 다 되어야 하는 기능을 만들기로 함
  4. compose-video 요구사항 #1 ❏ Jetpack Compose 환경에서 ExoPlayer 기반 비디오

    플레이어 UI 컴포넌트 사용하기 + 영상 스트리밍 가능해야 함
  5. VideoPlayer( mediaItems = ..., handleLifecycle = true, autoPlay = true,

    usePlayerController = true, handleAudioFocus = true, controllerConfig = VideoPlayerControllerConfig( showSpeedAndPitchOverlay = false, showSubtitleButton = false, showCurrentTimeAndTotalTime = true, showBufferingProgress = false, showForwardIncrementButton = true, showBackwardIncrementButton = true, showBackTrackButton = true, ... compose-video Compose Component
  6. VideoPlayer( ..., showRepeatModeButton = true, controllerShowTimeMilliSeconds = 5_000, controllerAutoShow =

    true, ), volume = 0.5f, // volume 0.0f to 1.0f repeatMode = RepeatMode.NONE, // or RepeatMode.ALL, RepeatMode.ONE modifier = ... ) compose-video Compose Component
  7. compose-video 요구사항 #2 ❏ VideoPlayer 컴포넌트를 사용하면 알아서 전체화면 처리

    ❏ ExoPlayer.PlayerView 인스턴스 내부에 사용자가 전체화면 버튼을 눌렀을 때의 핸들러 (setControllerOnFullScreenModeChangedListener) 가 존재함
  8. compose-video 요구사항 #2 ❏ VideoPlayer 컴포넌트를 사용하면 알아서 전체화면 처리

    ❏ ExoPlayer.PlayerView 인스턴스 내부에 사용자가 전체화면 버튼을 눌렀을 때의 핸들러 (setControllerOnFullScreenModeChangedListener) 가 존재함 ❏ 전체화면 됐을 때, ExoPlayer가 전체화면을 알아서 핸들링 해주지 않음 → 전체화면 됐을 때 Compose Dialog를 현재 액티비티에 띄워서 전체화면 뷰 또한 Compose로 구현
  9. 해치웠나? QA 시작 상훈님~~! 영상은 잘 나오는데 다른 앱이랑 소리가

    겹쳐요 영상 처음에 틀 때는 자동 재생 되어야 해요
  10. 영상은 잘 나오는데 다른 앱이랑 소리가 겹쳐요 compose-video 요구사항 #3

    VideoPlayer( ..., handleAudioFocus = true, ..., ) VideoPlayer 컴포넌트에서 handleAudioFocus 라는 파라미터를 받게 하고,
  11. val player = remember(handleAudioFocus) { val httpDataSourceFactory = DefaultHttpDataSource.Factory() ExoPlayer.Builder(context)

    ... .setAudioAttributes( AudioAttributes.Builder() .setContentType(AUDIO_CONTENT_TYPE_MOVIE) .setUsage(C.USAGE_MEDIA) .build(), handleAudioFocus, // 아까 VideoPlayer 컴포넌트에서 받았던 props ) ... }
  12. 영상 처음에 틀 때는 자동 재생 되어야 해요 compose-video 요구사항

    #4 VideoPlayer( ..., autoPlay = true, ..., ) VideoPlayer 컴포넌트에서 autoPlay 라는 파라미터를 받게 하고, Player remember 블록에서 autoPlay 함수에 따라 player.play() 함수 호출
  13. 데이터 로깅이 됐으면 좋겠어요 compose-video 요구사항 #5 VideoPlayer( ..., onCurrentTimeChanged

    = { // long type, current player time (ms) Log.e("CurrentTime", it.toString()) }, ..., )
  14. VideoPlayer( ..., onCurrentTimeChanged = { // long type, current player

    time (ms) Log.e("CurrentTime", it.toString()) }, ..., ) 영상을 어디까지 봤는지는 데이터 로깅 사항에 포함되어 있었기 때문에, onCurrentTimeChanged 라는 콜백을 만들어서 1초에 한번씩 현재의 위치를 밀리초 (ms) 단위로 리턴하게 함
  15. 해치웠나? QA 해치웠나? 찐막 + 또다른 고민 완료.. 근데 데이터

    로깅은 아마 더 늘어날 수 있으니까 그냥 ExoPlayer에서 모든 정보를 줄 수 있게 하면 어떨까?
  16. compose-video 확장성 고려하기 VideoPlayer( ..., playerInstance = { // ExoPlayer

    instance (Experimental) addAnalyticsListener( object : AnalyticsListener { // player logger } ) }, ..., )
  17. VideoPlayer( ..., playerInstance = { // ExoPlayer instance (Experimental) addAnalyticsListener(

    object : AnalyticsListener { // player logger } ) }, ..., ) ExoPlayer에서 플레이어 자체의 모든 종류의 이벤트를 트래킹 할 수 있게 차라리 내부적으로 초기화 된 player 인스턴스를 사용할 수 있도록 제공
  18. 해치웠나? QA 해치웠나? 찐막 + 또다른 고민 + 재밌는데? 하다보니

    뭔가 재밌어짐 회사에서 올리는 콘텐츠가 조금씩 길어지고.. 전체화면으로만 보기에는 다른 앱 사용을 못해서 너무 번거로웠음 → PIP 만들어볼까?
  19. compose-video PIP 만들기 ❏ VideoPlayer 컴포넌트를 사용하고 백그라운드로 옮기면 PIP가

    호출되게 할 수는 없을까? ❏ ComposeView로 감싸진 PlayerView를 토대로 Android O (8.0) 부터 공식 지원하는 PIP를 구현함
  20. compose-video PIP 만들기 ❏ VideoPlayer 컴포넌트를 사용하고 백그라운드로 옮기면 PIP가

    호출되게 할 수는 없을까? ❏ ComposeView로 감싸진 PlayerView를 토대로 Android O (8.0) 부터 공식 지원하는 PIP를 구현함 ❏ 자세한 사항은 compose-video 라이브러리 내에 pip 패키지 / PictureInPicture.kt 파일과 샘플 앱 참조
  21. (번외) 안드로이드에서의 PIP ❏ PIP 원리 → 플로팅 뷰로 전환될

    때, 액티비티가 리사이즈 된다고 생각하면 편함 ❏ 액티비티가 리사이즈 된다 == PIP 플로팅 뷰에서 보여줘야 할 뷰만 냅두고 나머지는 숨기던 해야함 ❏ PIP가 될 때, 최상위 뷰에서 PlayerView만 보이게 하고, PIP가 트리거 될 때 Lifecycle에 맞춰서 재생하던 설정하면 됨
  22. 라이브러리를 배포하기 위한 절차 ❏ Step 1. JavaDoc 호환 가능

    주석 달기 ❏ Step 2. 라이브러리 샘플 앱 만들기 ( + 번외로 Baseline Profile 적용하기) ❏ Step 3. README.md 작성하기 ❏ Step 4. Maven Central 에 배포하기 ❏ Step 5-1. Maven Central Repository 가입 및 Namespace 인증 ❏ Step 5-2. GPG(GNU Privacy Guard) 프로젝트 서명 ❏ Step 5-3. 프로젝트에 Maven Central 배포 스크립트 추가 + 배포하기 ❏ Step 5. 배포 확인하기
  23. 라이브러리를 호스팅 하는 저장소 ❏ 여기서 다루는 배포 종류는 Maven

    Central로, 기존에는 jCenter도 유명했지만 지금은 서비스 종료 ❏ Git 기반으로 배포하는 jitpack도 있지만, 여기서는 다루지 않음
  24. Maven Central 배포 방식의 변화 ❏ Maven Central의 기존 배포

    방식은 Sonatype에서 관리하는 JIRA (프로젝트 관리 도구) 에서 직접 티켓을 생성하고, 인증하는 방식임 ❏ 이에 많은 사람들이 Maven Central에 배포하는 것을 어려워 함
  25. Maven Central 배포 방식의 변화 ❏ Maven Central의 기존 배포

    방식은 Sonatype에서 관리하는 JIRA (프로젝트 관리 도구) 에서 직접 티켓을 생성하고, 인증하는 방식임 ❏ 이에 많은 사람들이 Maven Central에 배포하는 것을 어려워 함 ❏ 그래서 이제 자동화 방식으로 변경되었으며, OSSRH → 중앙 포털로 배포하는 방식으로 변경되었음 ❏ https://central.sonatype.com/
  26. Maven Central 배포 방식의 변화 ❏ Maven Central의 기존 배포

    방식은 Sonatype에서 관리하는 JIRA (프로젝트 관리 도구) 에서 직접 티켓을 생성하고, 인증하는 방식임 ❏ 이에 많은 사람들이 Maven Central에 배포하는 것을 어려워 함 ❏ 그래서 이제 자동화 방식으로 변경되었으며, OSSRH → 중앙 포털로 배포하는 방식으로 변경되었음 ❏ https://central.sonatype.com/ ❏ 기존 유저만 OSSRH 접속 가능 + 사용 가능, 신규 유저 사용 불가
  27. 배포 #2. Maven Central Portal Namespace 등록하기 도메인의 역순으로 입력하기

    만약 Github 이라면 → io.github.<github_id> 개인 도메인이 sanghun.io 이라면 → io.sanghun ex) kakao.com → com.kakao 등등
  28. 배포 #2. Maven Central Portal Namespace 등록하기 ❏ 만약 Github

    이라면 → Verification Key와 동일한 이름의 Repository 만들기 ❏ io.sanghun 같은 도메인이라면 → 도메인 DNS TXT 레코드에 Verification Key 설정하기
  29. 배포 #3. JavaDoc 작성하기 ❏ Java / Kotlin 소스코드에서 /**

    summary */ 으로 시작하는 주석들 ❏ Java / Kotlin 소스코드에서 HTML 형태로 API 문서를 작성하는 것임 ❏ 컴파일 시점에서는 당연히 지워지는 영역
  30. 배포 #3. JavaDoc 작성하기 @SuppressLint("UnsafeOptInUsageError") @Composable internal fun VideoPlayerFullScreenDialog( player:

    ExoPlayer, currentPlayerView: PlayerView, fullScreenPlayerView: PlayerView.() -> Unit, controllerConfig: VideoPlayerControllerConfig, repeatMode: RepeatMode, resizeMode: ResizeMode, enablePip: Boolean, onDismissRequest: () -> Unit, securePolicy: SecureFlagPolicy, ) { ... }
  31. /** * ExoPlayer does not support full screen views by

    default. * So create a full screen modal that wraps the Compose Dialog. * * Delegate all functions of the video controller that were used just before * the full screen to the video controller managed by that component. * Conversely, if the full screen dismissed, it will restore all the functions it delegated * for synchronization with the video controller on the full screen and the video controller on the previous screen. * * @param player Exoplayer instance. * @param currentPlayerView [androidx.media3.ui.PlayerView] instance currently in use for playback. * @param fullScreenPlayerView Callback to return all features to existing video player controller. * @param controllerConfig Player controller config. You can customize the Video Player Controller UI. * @param repeatMode Sets the content repeat mode. * @param enablePip Enable PIP. * @param onDismissRequest Callback that occurs when modals are closed. * @param securePolicy Policy on setting [android.view.WindowManager.LayoutParams.FLAG_SECURE] on a full screen dialog window. */
  32. 배포 #3. JavaDoc 작성하기 ❏ Java / Kotlin 소스코드에서 /**

    summary */ 으로 시작하는 주석들 ❏ Java / Kotlin 소스코드에서 HTML 형태로 API 문서를 작성하는 것임 ❏ 컴파일 시점에서는 당연히 지워지는 영역 ❏ 라이브러리에서 사용 가능한 공개 API에 JavaDoc 포맷에 맞게 작성하기 ❏ 라이브러리 내부에서 사용하는 Private API 에도 달아도 상관은 없음 ❏ JavaDoc을 작성하면, dokka 같은 API 문서 생성기가 생성도 해줌
  33. 배포 #4. PGP 서명 ❏ Maven Central Portal에 라이브러리를 배포하기

    위해서는 라이브러리를 PGP 서명해야 함 ❏ GPG (GNU Privacy Guard)를 통해 서명할 수 있으며, 전자 서명에 사용되는 공개 키 암호화 도구임 ❏ GPG로 라이브러리를 서명 함으로써, 해당 라이브러리가 신뢰할 수 있는 것임을 증명함
  34. 배포 #4. PGP 서명 - 설치 ❏ macOS ❏ Homebrew

    기준 brew install gnupg 로 설치함 ❏ Linux ❏ 이미 시스템에 설치되어 있을 수도 있음 ❏ 안되어 있다면 sudo apt install gnupg 로 설치 ❏ Windows ❏ https://gnupg.org/download/index.html#sec-1-2 에서 설치
  35. 배포 #4. PGP 서명 - 설치 확인 $ gpg --version

    gpg: WARNING: unsafe permissions on homedir '/Users/<user_name>/.gnupg' gpg (GnuPG) 2.4.5 libgcrypt 1.10.3 Copyright (C) 2024 g10 Code GmbH License GNU GPL-3.0-or-later <https://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Home: /Users/dsa28s/.gnupg Supported algorithms: Pubkey: RSA, ELG, DSA, ECDH, ECDSA, EDDSA Cipher: IDEA, 3DES, CAST5, BLOWFISH, AES, AES192, AES256, TWOFISH, CAMELLIA128, CAMELLIA192, CAMELLIA256 Hash: SHA1, RIPEMD160, SHA256, SHA384, SHA512, SHA224 Compression: Uncompressed, ZIP, ZLIB, BZIP2
  36. 배포 #4. PGP 서명 - 키 만들기 $ gpg gen-key

    GnuPG needs to construct a user ID to identify your key. Real name: DoraDora Email address: [email protected] You selected this USER-ID: "DoraDora <[email protected]>"
  37. 배포 #4. PGP 서명 - 만들어진 키 확인 $ gpg

    --list-keys pub ed25519 2024-06-07 [SC] [expires: 2027-06-07] 1CDC3050BAC59E93546A4C82F5DF465CF024EF5C uid [ultimate] DoraDora <[email protected]> sub cv25519 2024-06-07 [E] [expires: 2027-06-07]
  38. 배포 #4. PGP 서명 - 만들어진 키 확인 $ gpg

    --list-keys pub ed25519 2024-06-07 [SC] [expires: 2027-06-07] 1CDC3050BAC59E93546A4C82F5DF465CF024EF5C uid [ultimate] DoraDora <[email protected]> sub cv25519 2024-06-07 [E] [expires: 2027-06-07] 뒤 8자리 = Key ID 배포할 때 필요!!
  39. 배포 #4. PGP 서명 - 공개 키 배포 $ gpg

    --keyserver keyserver.ubuntu.com --send-keys KEY_ID pub ed25519 2024-06-07 [SC] [expires: 2027-06-07] 1CDC3050BAC59E93546A4C82F5DF465CF024EF5C uid [ultimate] DoraDora <[email protected]> sub cv25519 2024-06-07 [E] [expires: 2027-06-07]
  40. pom { name = "compose-video" description = "Video UI Component

    for Jetpack Compose" inceptionYear = "2023" url = "https://github.com/dsa28s/compose-video" licenses { license { name = "The Apache License, Version 2.0" url = "https://www.apache.org/licenses/LICENSE-2.0.txt" distribution = "https://www.apache.org/licenses/LICENSE-2.0.txt" } } developers { developer { id = "dsa28s" name = "Dora Lee" url = "https://github.com/dsa28s" } } scm { url = "https://github.com/dsa28s/compose-video" connection = "scm:git:git://github.com/dsa28s" developerConnection = "scm:git:ssh://[email protected]/dsa28s" } }
  41. 배포 #6. 찐 배포 $ ./gradlew publishAllPublicationsToMavenCentralRepository ❏ 위 명령줄이

    완료된 후, Maven Central Portal에 들어가서 Deployments 확인하기 ❏ 확인 이후에는 Portal 에서 Publish 버튼을 누르면 최종 배포 완료!
  42. (번외) README.md도 쌈@뽕하게 작성하기 ❏ 라이브러리가 하는 핵심적인 역할 포함하기

    ❏ 라이브러리 설치 방법 포함하기 ❏ (있다면) 라이브러리 스크린샷 / 샘플 앱 스크린샷 써서 라이브러리가 하는 역할 명확히 하기 ❏ 라이브러리에서 제공하는 API 설명서 포함하기 ❏ 라이브러리에서 제공하는 기능 단위로 열거하기 ❏ 라이선스 명시하기
  43. compose-video 라이브러리의 미래 ❏ Kotlin Multiplatform 지원 하기 ❏ 하지만...

    완전 시스템 종속 기능 + 비디오 플레이어 뷰를 어떻게 호스팅 해야할지 고민 필요 ❏ 커스텀 플레이어 뷰 가능토록 하게 하기 ❏ 지금은 ExoPlayer에서 제공중인 SimplePlayerView 기반으로 만들어져 있음 ❏ 나중에는 Compose 기반으로 플레이어 뷰를 만들 수 있도록 제공 예정 ❏ Android TV 지원 해보기