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

2022年の動画再生アプリの作り方

 2022年の動画再生アプリの作り方

MIXI ENGINEERS
PRO

October 05, 2022
Tweet

More Decks by MIXI ENGINEERS

Other Decks in Technology

Transcript

  1. 2022 DroidKaigi 2022 MIXI

  2. @oidy 2014 SearchBar Ex : 80 DL 2017 ( :

    MIXI) Android 2018 KARASTA Android / iOS
  3. 2022 ExoPlayer Jetpack Media3 , Picture-in-Picture, 3 2 1

  4. 2022

  5. 2008 : MediaPlayer MediaPlayer VideoView MediaPlayer View SurfaceView API Level

    1 Android 1.0 App MediaPlayer DataSource
  6. 2014 : MediaSession Google I/O Android Wear, Auto, TV API

    Level 21 Android 5.0
  7. MediaSession App Metadata , Player MediaSession Headset Android Auto Wear

    OS Android TV Command , Google Assistant
  8. 2017 : Picture-in-Picture Activity Android 7.0 7.0 Android TV :

    Big Buck Bunny, © 2008, Blender Foundation / www.bigbuckbunny.org API Level 26 Android 8.0
  9. 2020 : Large screens & foldables Android 10 Jetpack WindowManager

    : Big Buck Bunny, © 2008, Blender Foundation / www.bigbuckbunny.org
  10. Material Design 3 guidelines https://m3.material.io/foundations/adaptive-design/overview UI App quality guidelines https://developer.android.com/docs/quality-guidelines/large-

    screen-app-quality
  11. MediaSession Picture-in-Picture ,

  12. ExoPlayer & Jetpack Media3

  13. ExoPlayer https://github.com/google/ExoPlayer MediaPlayer • HLS, DASH, SmoothStreaming • • UI

  14. ExoPlayer PlayerView ExoPlayer

  15. Jetpack Media2 VideoView MediaPlayer ExoPlayer PlayerView ExoPlayer

  16. Jetpack Media2 VideoView MediaPlayer MediaSession ExoPlayer PlayerView ExoPlayer

  17. ExoPlayer Extensions Connector Jetpack Media2 VideoView MediaPlayer MediaSession ExoPlayer PlayerView

    ExoPlayer
  18. Jetpack Media3 2021 10 Media ExoPlayer Jetpack Media3 PlayerView ExoPlayer

    MediaSession androidx.media3:media3-ui androidx.media3:media3-exoplayer androidx.media3:media3-session ...
  19. Jetpack Media3 ExoPlayer Media3 com.google.android.exoplayer:exoplayer androidx.media3:media3-exoplayer

  20. Jetpack Media3 • OK https://developer.android.com/guide/topics/media/media3/exoplayer/mappings androidx.media3.exoplayer.ExoPlayer androidx.media3.ui.PlayerView androidx.media3.common.Player com.google.android.exoplayer2.ExoPlayer com.google.android.exoplayer2.ui.StyledPlayerView

    com.google.android.exoplayer2.Player • Migration guide: https://developer.android.com/guide/topics/media/media3/getting-started/migration-guide
  21. Unstable APIs Media3 2022 9 1.0.0-beta02 API Unstable Unstable API

    @OptIn @OptIn(UnstableApi::class) private fun methodUsingUnstableApis() { ... }
  22. API • • Extension

  23. API • • Extension Jetpack Media3

  24. None
  25. Activity PlayerView ExoPlayer

  26. Activity class PlayerActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?)

    { super.onCreate(savedInstanceState) } }
  27. Activity PlayerView class PlayerActivity : AppCompatActivity() { override fun onCreate(savedInstanceState:

    Bundle?) { super.onCreate(savedInstanceState) val playerView = PlayerView(this) setContentView(playerView) } }
  28. Activity PlayerView ExoPlayer class PlayerActivity : AppCompatActivity() { override fun

    onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val playerView = PlayerView(this) setContentView(playerView) val player = ExoPlayer.Builder(this).build() } }
  29. Activity PlayerView ExoPlayer class PlayerActivity : AppCompatActivity() { override fun

    onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val playerView = PlayerView(this) setContentView(playerView) val player = ExoPlayer.Builder(this).build() playerView.player = player } }
  30. Activity PlayerView ExoPlayer // Uri val mediaItem = MediaItem.fromUri(...) player.setMediaItem(mediaItem)

    // player.prepare() player.play() MediaItem
  31. override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // Player player =

    ... } override fun onDestroy() { super.onDestroy() // Player player.release() }
  32. Activity Player Activity Player : , Activity PlayerView ExoPlayer Activity

    Service Player
  33. Foreground Service Activity PlayerView ExoPlayer Service • ExoPlayer Service •

    • PlayerView Player
  34. Foreground Service Activity PlayerView ExoPlayer Service MediaSessionService • ExoPlayer Service

    • • PlayerView Player
  35. Activity PlayerView MediaSessionService ExoPlayer

  36. Activity PlayerView MediaSessionService ExoPlayer MediaSession

  37. Activity PlayerView MediaController MediaSessionService ExoPlayer MediaSession Session Token

  38. Activity PlayerView MediaController MediaSessionService ExoPlayer MediaSession implements Player setMediaItem() play()

    pause() ...
  39. Activity PlayerView MediaController MediaSessionService ExoPlayer MediaSession

  40. : DefaultMediaNotificationProvider NotificationProvider setMediaNotificationProvider() • • •

  41. MediaSessionService PlayerService class PlayerService : MediaSessionService() { }

  42. MediaSessionService PlayerService class PlayerService : MediaSessionService() { private lateinit var

    player: ExoPlayer private lateinit var mediaSession: MediaSession override fun onCreate() { super.onCreate() player = ExoPlayer.Builder(this).build() mediaSession = MediaSession.Builder(this, player).build() } } ExoPlayer MediaSession
  43. MediaSessionService PlayerService class PlayerService : MediaSessionService() { private lateinit var

    player: ExoPlayer private lateinit var mediaSession: MediaSession override fun onCreate() { super.onCreate() player = ExoPlayer.Builder(this).build() mediaSession = MediaSession.Builder(this, player).build() } override fun onGetSession(info: ControllerInfo): MediaSession { return mediaSession } } ExoPlayer MediaSession
  44. MediaSessionService PlayerService class PlayerService : MediaSessionService() { ... override fun

    onDestroy() { // player.release() mediaSession.release() } } ExoPlayer MediaSession
  45. MediaController val component = ComponentName(this, PlayerService::class.java) val token = SessionToken(this,

    component) Activity PlayerView MediaController
  46. MediaController val component = ComponentName(this, PlayerService::class.java) val token = SessionToken(this,

    component) val controllerFuture = MediaController.Builder(this, token) .buildAsync() controllerFuture.addListener({ val mediaController = controllerFuture.get() }, MoreExecutors.directExecutor()) Activity PlayerView MediaController
  47. MediaController val component = ComponentName(this, PlayerService::class.java) val token = SessionToken(this,

    component) val controllerFuture = MediaController.Builder(this, token) .buildAsync() controllerFuture.addListener({ val mediaController = controllerFuture.get() playerView.player = mediaController }, MoreExecutors.directExecutor()) Activity PlayerView MediaController
  48. MediaItem Activity PlayerView MediaController MediaSessionService ExoPlayer MediaSession MediaItem setUri()

  49. MediaItem Activity PlayerView MediaController MediaSessionService ExoPlayer MediaSession MediaItem setUri() Uri

    MediaItem ( )
  50. MediaItem val requestMetadata = MediaItem.RequestMetadata.Builder() .setMediaUri(uri) .build() val mediaItem =

    MediaItem.Builder() .setRequestMetadata(requestMetadata) .build() mediaController.setMediaItem(mediaItem) Activity MediaController MediaItem setMediaUri() RequestMetadata 1 2 3 1 2 3
  51. MediaItem val mediaSessionCallback = object : MediaSession.Callback { override fun

    onAddMediaItems( mediaSession: MediaSession, controllerInfo: ControllerInfo, mediaItems: List<MediaItem> ) = Futures.immediateFuture(mediaItems.map { mediaItem -> mediaItem.buildUpon() .setUri(mediaItem.requestMetadata.mediaUri) .build() }) } Service
  52. MediaItem val mediaSessionCallback = ... mediaSession = MediaSession.Builder(this, player) .setCallback(mediaSessionCallback)

    .build() Service
  53. Activity PlayerView MediaController MediaSessionService ExoPlayer MediaSession Activity PlayerView ExoPlayer

  54. Pitcure-in-Picture (PiP) AndroidManifest Activity Activity PiP Activity PiP UI 3

    2 1
  55. AndroidManifest android:supportsPictureInPicture Activity PiP android:configChanges PiP Activity <activity android:name=".PlayerActivity" android:supportsPictureInPicture="true"

    android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation" /> 1
  56. PiP val params = PictureInPictureParams.Builder() // PiP .setAspectRatio(Rational(width, height)) //

    PiP .setActions(actions) .build() // PiP enterPictureInPictureMode(params) 2
  57. PiP val params = PictureInPictureParams.Builder() // PiP .setAspectRatio(Rational(width, height)) //

    PiP .setActions(actions) .build() // PiP enterPictureInPictureMode(params) 2 : Big Buck Bunny, © 2008, Blender Foundation / www.bigbuckbunny.org
  58. PiP 2 button.setOnClickListener { enterPictureInPictureMode(params) } override fun onUserLeaveHint() {

    enterPictureInPictureMode(params) } : Big Buck Bunny, © 2008, Blender Foundation / www.bigbuckbunny.org
  59. PiP if (packageManager.hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) { enterPictureInPictureMode(params) } 2 PiP

  60. PiP if (packageManager.hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) { try { enterPictureInPictureMode(params) } catch (e:

    IllegalStateException) { // Device doesn't support PiP mode. } } 2 FEATURE_PICTURE_IN_PICTURE
  61. PiP UI private val pipListener = Consumer<PictureInPictureModeChangedInfo> { info ->

    if (info.isInPictureInPictureMode) { // PlayerView UI } else { // PlayerView UI } } 3 PiP
  62. PiP UI override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) addOnPictureInPictureModeChangedListener(pipListener) }

    override fun onDestroy() { super.onDestroy() removeOnPictureInPictureModeChangedListener(pipListener) } androidx.activity 3
  63. : Big Buck Bunny, © 2008, Blender Foundation / www.bigbuckbunny.org

  64. lifecycleScope.launch(Dispatchers.Main) { lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { WindowInfoTracker.getOrCreate(this@PlayerActivity) .windowLayoutInfo(this@PlayerActivity) .mapNotNull { layoutInfo ->

    layoutInfo.displayFeatures .filterIsInstance<FoldingFeature>() .firstOrNull() } .collect { foldingFeature -> // UI } } }
  65. lifecycleScope.launch(Dispatchers.Main) { lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { WindowInfoTracker.getOrCreate(this@PlayerActivity) .windowLayoutInfo(this@PlayerActivity) .mapNotNull { layoutInfo ->

    layoutInfo.displayFeatures .filterIsInstance<FoldingFeature>() .firstOrNull() } .collect { foldingFeature -> // UI } } } WindowLayoutInfo Flow
  66. lifecycleScope.launch(Dispatchers.Main) { lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { WindowInfoTracker.getOrCreate(this@PlayerActivity) .windowLayoutInfo(this@PlayerActivity) .mapNotNull { layoutInfo ->

    layoutInfo.displayFeatures .filterIsInstance<FoldingFeature>() .firstOrNull() } .collect { foldingFeature -> // UI } } } WindowLayoutInfo FoldingFeature
  67. lifecycleScope.launch(Dispatchers.Main) { lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { WindowInfoTracker.getOrCreate(this@PlayerActivity) .windowLayoutInfo(this@PlayerActivity) .mapNotNull { layoutInfo ->

    layoutInfo.displayFeatures .filterIsInstance<FoldingFeature>() .firstOrNull() } .collect { foldingFeature -> // UI } } } state orientation occlusionType isSeparating
  68. FoldingFeature UI if (foldingFeature.state == FLAT) { // } else

    { if (foldingFeature.orientation == HORIZONTAL) { // foldingFeature.bounds.top } else { // foldingFeature.bounds.left } }
  69. FoldingFeature UI ConstraintLayout PlayerView View UI Compose Composable PlayerView Composable

    Modifier PlayerView
  70. FoldingFeature UI ConstraintLayout PlayerView View UI Compose Composable PlayerView Composable

    Modifier PlayerView val playerView = remember { PlayerView(context) } DisposableEffect( AndroidView(factory = { playerView }) ) { onDispose { mediaController.release() } }
  71. https://github.com/oidy/VideoPlayerSample • Jetpack Media3 • MediaSessionService • Picture-in-Picture • Jetpack

    Compose • : Big Buck Bunny, © 2008, Blender Foundation / www.bigbuckbunny.org
  72. https://github.com/androidx/media/tree/release/demos Media3 Jetpack Media3 https://github.com/android/compose-samples/tree/main/Jetcaster UI Jetpack Compose

  73. Android 2022 Jetpack Media3 Picture-in-Picture Activity Jetpack WindowManager 2022