안드로이드 유튜브 PIP - andeuloideu yutyubeu PIP

기존에 넷플릭스·디즈니플러스 등에선 해당 기능이 적용됐지만, 아이폰용 유튜브 앱에서는 이 기능 이용이 불가능했다. 불편하다는 지적이 나올 수밖에 없었다. 일부 아이폰 이용자들은 유튜브 영상을 재생하면서 앱 사용이 가능한 유튜브 웹사이트나 우회사이트를 이용하기도 했다.

유튜브는 2021년 6월 유튜브 프리미엄 가입자를 대상으로 PIP 모드를 도입해 올해 4월까지 시범 운영했다. 6월에는 정식 버전을 발표할 것이라고 예고했다.

유튜브의 새 기능을 이용하려면 유튜브 프리미엄을 구독해야 한다. 비구독자는 음악을 제외한 영상 콘텐츠에 한해 PIP 모드를 활용할 수 있다.

유튜브 측은 "많은 이용자가 PIP 모드를 요청했고 적용하는 데 시간이 걸렸다"며 "피드백을 공유한 사용자들에게 감사하다"고 밝혔다.

이유정 기자 [email protected]

Android 12에서는 새 setAutoEnterEnabled 플래그를 사용하여 동작 탐색 모드에서 위로 스와이프하여 홈으로 이동할 때 PIP 모드로 더 원활하게 전환할 수 있습니다. 이전에는 Android에서 위로 스와이프하여 홈으로 이동 애니메이션이 완료될 때까지 기다린 후 PIP 창을 페이드 인했습니다.

이 기능을 구현하는 방법은 다음과 같습니다.

  1. 다음과 같이 setAutoEnterEnabled를 사용하여 PictureInPictureParams.Builder를 구성합니다.

    setPictureInPictureParams(new PictureInPictureParams.Builder()
        .setAspectRatio(aspectRatio)
        .setSourceRectHint(sourceRectHint)
        .setAutoEnterEnabled(true)
        .build());
    
    참고: setAutoEnterEnabled를 사용 설정하면 onUserLeaveHint에서 enterPictureInPictureMode를 명시적으로 호출하지 않아도 됩니다.
  2. 최신 PictureInPictureParams를 사용하여

      setPictureInPictureParams(new PictureInPictureParams.Builder()
              .setSeamlessResizeEnabled(false)
              .build());
    
    0를 조기에 호출합니다. 앱은 Android 11에서와같이 onUserLeaveHint 콜백을 기다리지 않아야 합니다.

    예를 들어 앱은 맨 처음 재생과 가로세로 비율이 변경된 경우 후속 재생에서

      setPictureInPictureParams(new PictureInPictureParams.Builder()
              .setSeamlessResizeEnabled(false)
              .build());
    
    0를 호출하려고 할 수 있습니다.

  3. 필요에 따라

      setPictureInPictureParams(new PictureInPictureParams.Builder()
              .setSeamlessResizeEnabled(false)
              .build());
    
    3를 호출합니다. 예를 들어 현재 재생이 일시중지된 상태라면 동영상 앱이 PIP로 전환되지 않는 것이 좋을 수 있습니다.

동영상이 아닌 콘텐츠의 원활한 크기 조절을 사용 중지하는 새 API 플래그

Android 12에서는 PIP 창에서 동영상이 아닌 콘텐츠의 크기를 조절할 때 훨씬 더 매끄러운 크로스페이드 애니메이션을 제공하는

  setPictureInPictureParams(new PictureInPictureParams.Builder()
          .setSeamlessResizeEnabled(false)
          .build());
4 플래그를 추가합니다. 이전에는 PIP 창에서 동영상이 아닌 콘텐츠의 크기를 조절하면 시각적 아티팩트가 부자연스럽게 보일 수 있었습니다.

  setPictureInPictureParams(new PictureInPictureParams.Builder()
          .setSeamlessResizeEnabled(false)
          .build());
5 플래그는 이전 버전과의 호환성을 위해 기본적으로
  setPictureInPictureParams(new PictureInPictureParams.Builder()
          .setSeamlessResizeEnabled(false)
          .build());
6로 설정됩니다. 동영상 콘텐츠의 경우
  setPictureInPictureParams(new PictureInPictureParams.Builder()
          .setSeamlessResizeEnabled(false)
          .build());
6 설정으로 두고 동영상이 아닌 콘텐츠의 경우
  setPictureInPictureParams(new PictureInPictureParams.Builder()
          .setSeamlessResizeEnabled(false)
          .build());
8로 변경합니다.

동영상이 아닌 콘텐츠의 원활한 크기 조절을 사용 중지하려면 다음을 실행하세요.

  setPictureInPictureParams(new PictureInPictureParams.Builder()
          .setSeamlessResizeEnabled(false)
          .build());

PIP 모드를 종료할 때 더 매끄러운 애니메이션 지원

Android 12에서는 이제 PIP 모드를 종료할 때 더 매끄러운 애니메이션을 구현하기 위해

  setPictureInPictureParams(new PictureInPictureParams.Builder()
          .setSeamlessResizeEnabled(false)
          .build());
9 플래그가 재사용됩니다. 종료 시 시스템은 PIP 전환에 사용된 원래
  // Listener is called immediately after the user exits PIP but before animating.
  playerView.addOnLayoutChangeListener { _, left, top, right, bottom,
                       oldLeft, oldTop, oldRight, oldBottom ->
      if (left != oldLeft || right != oldRight || top != oldTop
              || bottom != oldBottom) {
         // The playerView's bounds changed, update the source hint rect to
         // reflect its new bounds.
         val sourceRectHint = Rect()
         playerView.getGlobalVisibleRect(sourceRectHint)
         setPictureInPictureParams(
             PictureInPictureParams.Builder()
                 .setSourceRectHint(sourceRectHint)
                 .build()
         )
      }
  }
0이든 앱에서 제공한 업데이트된
  // Listener is called immediately after the user exits PIP but before animating.
  playerView.addOnLayoutChangeListener { _, left, top, right, bottom,
                       oldLeft, oldTop, oldRight, oldBottom ->
      if (left != oldLeft || right != oldRight || top != oldTop
              || bottom != oldBottom) {
         // The playerView's bounds changed, update the source hint rect to
         // reflect its new bounds.
         val sourceRectHint = Rect()
         playerView.getGlobalVisibleRect(sourceRectHint)
         setPictureInPictureParams(
             PictureInPictureParams.Builder()
                 .setSourceRectHint(sourceRectHint)
                 .build()
         )
      }
  }
0이든 상관없이 현재 사용 가능한
  // Listener is called immediately after the user exits PIP but before animating.
  playerView.addOnLayoutChangeListener { _, left, top, right, bottom,
                       oldLeft, oldTop, oldRight, oldBottom ->
      if (left != oldLeft || right != oldRight || top != oldTop
              || bottom != oldBottom) {
         // The playerView's bounds changed, update the source hint rect to
         // reflect its new bounds.
         val sourceRectHint = Rect()
         playerView.getGlobalVisibleRect(sourceRectHint)
         setPictureInPictureParams(
             PictureInPictureParams.Builder()
                 .setSourceRectHint(sourceRectHint)
                 .build()
         )
      }
  }
2를 사용하여 애니메이션을 만듭니다.

이 기능을 구현하려면 다음과 같이 앱을 업데이트하세요.

  1. 매끄러운 들어가기 애니메이션을 위해

      // Listener is called immediately after the user exits PIP but before animating.
      playerView.addOnLayoutChangeListener { _, left, top, right, bottom,
                           oldLeft, oldTop, oldRight, oldBottom ->
          if (left != oldLeft || right != oldRight || top != oldTop
                  || bottom != oldBottom) {
             // The playerView's bounds changed, update the source hint rect to
             // reflect its new bounds.
             val sourceRectHint = Rect()
             playerView.getGlobalVisibleRect(sourceRectHint)
             setPictureInPictureParams(
                 PictureInPictureParams.Builder()
                     .setSourceRectHint(sourceRectHint)
                     .build()
             )
          }
      }
    
    2와
      // Listener is called immediately after the user exits PIP but before animating.
      playerView.addOnLayoutChangeListener { _, left, top, right, bottom,
                           oldLeft, oldTop, oldRight, oldBottom ->
          if (left != oldLeft || right != oldRight || top != oldTop
                  || bottom != oldBottom) {
             // The playerView's bounds changed, update the source hint rect to
             // reflect its new bounds.
             val sourceRectHint = Rect()
             playerView.getGlobalVisibleRect(sourceRectHint)
             setPictureInPictureParams(
                 PictureInPictureParams.Builder()
                     .setSourceRectHint(sourceRectHint)
                     .build()
             )
          }
      }
    
    4를 사용하여 PictureInPictureParams를 계속 구성합니다.

  2. 필요하다면 시스템에서 나가기 전환을 시작하기 전에

      // Listener is called immediately after the user exits PIP but before animating.
      playerView.addOnLayoutChangeListener { _, left, top, right, bottom,
                           oldLeft, oldTop, oldRight, oldBottom ->
          if (left != oldLeft || right != oldRight || top != oldTop
                  || bottom != oldBottom) {
             // The playerView's bounds changed, update the source hint rect to
             // reflect its new bounds.
             val sourceRectHint = Rect()
             playerView.getGlobalVisibleRect(sourceRectHint)
             setPictureInPictureParams(
                 PictureInPictureParams.Builder()
                     .setSourceRectHint(sourceRectHint)
                     .build()
             )
          }
      }
    
    2를 업데이트합니다. 시스템에서 PIP 모드를 종료하려고 하면 활동의 뷰 계층 구조가 대상 구성(예: 전체 화면)에 배치됩니다. 앱은 레이아웃 변경 리스너를 루트 뷰나 타겟 뷰(예: 동영상 플레이어 뷰)에 연결하여 이벤트를 감지하고 애니메이션이 시작되기 전에 sourceRectHint를 업데이트할 수 있습니다.