Nền tảng ứng dụng

Tối ưu hoá việc phát nội dung nghe nhìn và tối đa hoá phạm vi tiếp cận bằng API Jetpack Media3

Loa

Giới thiệu

Giới thiệu về bài thuyết trình

Việc sử dụng nội dung nghe nhìn chiếm phần lớn thời gian người dùng dành cho thiết bị di động, đòi hỏi trải nghiệm phát phải hoàn hảo, hiệu quả cao và tiết kiệm pin. Hơn nữa, người dùng mong muốn những trải nghiệm này chuyển đổi liền mạch trên các thiết bị của họ, từ điện thoại đến TV Living Room và hơn thế nữa.

Khám phá cách các bản cập nhật về cấu trúc trong Media3 và ExoPlayer giúp phát nội dung liền mạch trên nhiều kiểu dáng thiết bị. Nội dung thảo luận tập trung vào việc tạo giao diện thích ứng bằng bộ công cụ giao diện người dùng Media3 Compose, loại bỏ tình trạng tải vào vùng đệm trong nguồn cấp dữ liệu bằng PreloadManager mới, cũng như mở rộng ứng dụng sang màn hình Living Room và Android XR bằng CastPlayer và âm thanh không gian.

Tóm tắt

  • Media3 UI Compose cung cấp các thành phần kết hợp trình phát có sẵn và các khối dựng có thể tuỳ chỉnh. Bạn có thể ghi đè các vị trí điều khiển cụ thể trong khi cho phép thư viện quản lý trạng thái và quá trình chuyển đổi của trình phát.
  • CastPlayer mới được viết lại sẽ thay thế SDK Truyền độc lập. Lớp này triển khai giao diện trình phát chuẩn và tự động uỷ quyền phát giữa các thực thể ExoPlayer cục bộ và thiết bị Truyền từ xa.
  • Quy trình chỉnh sửa được xây dựng lại tách biệt với OpenGL và quản lý trực tiếp vùng đệm phần cứng. Phương pháp không sao chép này giúp giảm mức hao tổn bộ nhớ, đồng thời việc tách rời cho phép nhà phát triển tích hợp các khung đồ hoạ hiện đại như WebGPU.
  • Media3 bổ sung tính năng hỗ trợ sẵn cho Eclipsa Video và Eclipsa Audio trên Android 17 trở lên. Siêu dữ liệu HDR theo quy định điều chỉnh vùng sáng theo ánh sáng xung quanh, trong khi âm thanh không gian xử lý nhiều bản trình bày kết hợp.
  • Các tính năng tối ưu hoá tích hợp sẵn như giảm tải âm thanh PCM giúp giảm mức tiêu thụ điện năng khi tắt màn hình lên đến 20%. CodecDB sắp ra mắt sẽ sử dụng các chỉ số điểm chuẩn để chọn chế độ cài đặt bộ mã hoá tối ưu cho thiết bị.

Tài nguyên

Gemini Notebook

Chủ đề

  • jetpack-media3
  • exoplayer
  • jetpack-compose
  • google-cast
  • chỉnh sửa video
  • âm thanh
  • hiệu suất

Bản chép lời

Giới thiệu về Jetpack Media3

  • Được rồi.

  • Chào các bạn.

  • Tôi là Toni.

  • Tôi là kỹ sư thuộc nhóm truyền thông Android.

  • Cùng với Kristina và Donovan, chúng tôi sẽ nói về cách Jetpack Media3 có thể giúp bạn đáp ứng mọi nhu cầu về nội dung nghe nhìn trên Android.

  • Media3 là thư viện hỗ trợ của Android cho mọi nội dung liên quan đến nội dung nghe nhìn, cho dù đó là phát bằng ExoPlayer, chia sẻ nội dung nghe nhìn bằng phiên nội dung nghe nhìn và Cast, hay chỉnh sửa nội dung nghe nhìn bằng trình biến đổi, hoặc kiểm tra nội dung nghe nhìn, chẳng hạn như trích xuất khung hình hoặc siêu dữ liệu.

  • Media3 bao gồm toàn bộ không gian này trong một nhóm thư viện.

  • Là một thư viện hỗ trợ, thư viện này được tích hợp vào ứng dụng của bạn.

  • Điều đó có nghĩa là chúng tôi có thể đảm bảo tính nhất quán, để cùng một mã chạy trên tất cả phiên bản hệ điều hành và tất cả thiết bị.

  • Điều này cũng giúp ích cho khả năng cập nhật.

  • Mọi bản sửa lỗi mà chúng tôi thực hiện đều áp dụng cho API 24 và không liên quan đến bất kỳ chu kỳ phát hành hệ điều hành nào.

  • Và với tư cách là một thư viện hỗ trợ, chúng tôi có thể cho phép các lựa chọn tuỳ chỉnh rất phong phú, điều này không thể thực hiện được trong khung Android.

  • Và là một thư viện trên GitHub, chúng tôi ưu tiên nguồn mở.

  • Điều này có nghĩa là mọi thay đổi chúng tôi thực hiện đều có ngay trên GitHub.

  • Chúng tôi có một trình theo dõi sự cố đang hoạt động và chúng tôi chấp nhận các yêu cầu kéo.

  • Điều quan trọng cần nhấn mạnh ở đây là trong tất cả các trường hợp sử dụng này, chúng tôi thực sự quan tâm đến mọi cấp độ chi tiết, từ các phần tử trên giao diện người dùng cấp cao đến các API đơn giản, có sẵn, cũng như các tuỳ chọn tuỳ chỉnh sâu, khả năng hỗ trợ rộng rãi cho nhiều định dạng và tiêu chuẩn, cùng với hiệu suất và hiệu quả làm nền tảng cho tất cả.

  • Media3 bao gồm toàn bộ không gian này.

  • Hôm nay, chúng ta sẽ tìm hiểu sâu hơn về một số chủ đề này.

  • Chúng ta sẽ nói về mô-đun Giao diện người dùng Compose mới, CastPlayer mới giúp chia sẻ với phiên phát nội dung nghe nhìn dễ dàng hơn, quy trình chỉnh sửa được cải tiến với các hiệu ứng nâng cao mới, công cụ mới để truy xuất siêu dữ liệu và khung hình, một số công nghệ truyền phát trực tuyến mới, đặc biệt là video và âm thanh Eclipsa, cùng một số tiện ích chất lượng trải nghiệm để tải trước, kéo để tua và tiết kiệm pin.

Giao diện người dùng đa phương tiện bằng Jetpack Compose

  • Bây giờ, hãy cùng tìm hiểu ngay chủ đề đầu tiên, đó là cách Media3 có thể giúp xây dựng các phần tử trên giao diện người dùng cho nội dung nghe nhìn.

  • Gần đây, chúng tôi đã phát hành hai mô-đun mới: Media3 UI Compose và Media3 UI Compose Material 3.

  • Tại đây, bạn có thể thấy một thành phần kết hợp trình phát cấp cao, vì thành phần này được ra mắt trong Media3 1.10 mà không có thêm nội dung sửa đổi nào.

    @Composable
    fun videoScreen() {
      Player(viewModel.exoPlayer)
    }
  • Tính năng này hoạt động ngay lập tức.

  • Bạn chỉ cần một trình phát Media3.

  • Trong ví dụ này, đó là ExoPlayer, thường có trong mô hình khung hiển thị.

  • Tuy nhiên, đó có thể là một thành phần khác, chẳng hạn như CastPlayer, trình điều khiển nội dung nghe nhìn hoặc bất kỳ trình phát nào khác mà bạn có trong ứng dụng.

  • Cần lưu ý rằng ExoPlayer ở đây là nguồn đáng tin cậy.

  • Nó chứa tất cả thông tin và thành phần kết hợp trình phát sẽ đảm nhận việc hiển thị giao diện người dùng và kết nối mọi thứ.

  • Nhưng nếu bạn muốn tuỳ chỉnh giao diện người dùng này, thêm nút mới, thay đổi vị trí, kiểu, lớp phủ văn bản thì sao?

  • Thành phần kết hợp trình phát được tạo từ các khối dựng.

  • Bạn có thể thay thế các tệp này.

  • Bạn có thể tuỳ chỉnh từng khối riêng lẻ hoặc có thể sử dụng riêng các khối này ở nơi khác trong ứng dụng.

  • Ví dụ: bạn có thể tuỳ chỉnh riêng phần trên cùng, phần giữa và bottomControls.

  • Bạn cũng có thể tuỳ chỉnh giao diện của màn trập (hiển thị khi video không phát) hoặc lớp phủ lỗi.

  • Nhưng đây chỉ là lớp đầu tiên.

  • Trung tâm mặc định và bottomControls cũng có nhiều khe chi tiết hơn mà bạn có thể tuỳ chỉnh.

  • Ví dụ: bạn có thể hoán đổi nút trung tâm trong khi vẫn giữ nguyên mọi thứ khác hoặc thêm các phần tử ở trên hoặc dưới thanh trượt.

  • Media3 Compose cung cấp tất cả các thành phần cần thiết để bạn thực hiện việc này.

  • Ví dụ: chúng ta có một số nút, nút phát/tạm dừng, chế độ lặp lại, chế độ phát ngẫu nhiên, nút tắt micrô và các nút khác.

  • Sau đó là các chỉ báo tiến trình, chẳng hạn như thanh trượt và văn bản vị trí hiển thị trên màn hình.

  • Ngoài ra, còn có một số vùng chứa và lớp phủ.

  • Thành phần kết hợp trình phát cấp cao mà tôi đã giới thiệu là một ví dụ, nhưng còn nhiều ví dụ khác, chẳng hạn như lớp phủ lỗi hoặc phụ đề, v.v.

  • Ngoài ra, chúng tôi còn có các thành phần hữu ích khác, chẳng hạn như bộ điều khiển nhỏ này.

  • Bạn có thể đặt quảng cáo trong một trang tính dưới cùng khi người dùng đang di chuyển trong ứng dụng của bạn.

  • Và chúng tôi vẫn chưa dừng lại ở đó.

  • Chúng tôi đang tích cực mở rộng thư viện này, chẳng hạn như sắp ra mắt các chế độ điều khiển chỉnh sửa mới như thanh trượt cắt.

  • Bây giờ, hãy xem cách tuỳ chỉnh này hoạt động để biết cách sử dụng các khối bản dựng này.

    Player(viewModel.exoPlayer)
  • Giả sử chúng ta muốn di chuyển văn bản vị trí và thời lượng (hiện đang ở bên cạnh thanh trượt) lên phía trên thanh trượt.

    Player(
      player = viewModel.exoPlayer,
      bottomControls = {
        PlayerDefaults.BottomControls(
          player,
          left = {}, right = {},
          above = {
            PositionAndDurationText(
              player, separator = " • ")
          },
        )
      },
    )
  • Vì vậy, chúng ta có thể ghi đè các bottomControls này.

  • Chúng ta vẫn có thể sử dụng thành phần mặc định, nhưng chúng ta đặt khe bên trái và bên phải thành trống để làm cho chúng biến mất.

  • Thay vào đó, chúng ta đặt văn bản vị trí và thời lượng phía trên nó trong vùng phía trên bằng một dấu phân tách tuỳ chỉnh để trông thú vị hơn.

  • Bây giờ, giả sử chúng ta muốn xoá hoàn toàn lớp phủ centerControls và thay vào đó, đặt nút phát ngay phía trên thanh trượt có nút điều khiển tốc độ và nút tắt tiếng.

    Player(
      player = viewModel.exoPlayer,
      centerControls = {},
      bottomControls = { ...
          above = {
            Row(Modifier.fillMaxWidth()) {
              PositionAndDurationText(...)
              Spacer(Modifier.weight(1f))
              PlaybackSpeedToggleButton(player)
              MuteButton(player)
              PlayPauseButton(player)
            }
          },
        )
      }
  • Và đó cũng chính là ý tưởng tương tự.

  • Chúng ta có thể đặt centerControls thành trống để làm cho chúng biến mất, đồng thời có thể mở rộng vùng phía trên thanh trượt bằng một số không gian bổ sung và 3 nút mà chúng ta muốn hiển thị.

  • Xin lưu ý rằng các nút này là từ chính thư viện, vì vậy, bạn không cần phải thêm bất kỳ mã nào vào thư viện.

  • Chỉ cần vậy thôi.

  • Và bạn có thể tiếp tục theo cách này để tạo kiểu cho các phần tử này.

  • Nếu bạn muốn thay đổi biểu tượng nút tắt micrô, chẳng hạn, nhưng vẫn giữ nguyên chức năng của nút, thì cách làm cũng tương tự.

    Player(
      ...
      bottomControls = { ...
          above = {
              ...
              MuteButton(player,
                painter = { painterResource(
                  if (showMuted)
                    R.drawable.muted
                  else
                    R.drawable.unmuted )})
          }
      },
    )
  • Như bạn có thể thấy ở đây, bạn có thể xác định một trình vẽ tuỳ chỉnh cho nút và tự chọn biểu tượng.

  • Thành phần này cung cấp cho bạn một số siêu dữ liệu, vì vậy, bạn chỉ cần xem trạng thái, chẳng hạn như showMuted trong ví dụ này, để chọn biểu tượng phù hợp.

  • Và đây thực sự là lợi ích chính của Media3 Compose.

  • Bạn có thể tập trung vào giao diện của ứng dụng và Media3 sẽ lo phần còn lại.

  • Thành phần này đọc trạng thái từ trình phát, tương tác với trình phát và xử lý tất cả các hiệu ứng chuyển đổi và ảnh động ở giữa.

Chia sẻ chế độ phát bằng MediaSession

  • Bây giờ, hãy chuyển từ giao diện người dùng sang chia sẻ nội dung nghe nhìn.

  • Bạn có thể chia sẻ trạng thái phát của mình với các ứng dụng khác trên cùng một thiết bị hoặc kết nối với các thiết bị từ xa.

  • Cách chính để phát hành nội dung phát đang diễn ra là sử dụng MediaSession.

  • Thao tác này giúp người khác biết được trạng thái và siêu dữ liệu của quá trình phát, đồng thời cho phép họ kiểm soát quá trình phát.

  • Đây là cách Gemini, thông báo về nội dung nghe nhìn trên giao diện người dùng hệ thống, Cast, Wear OS hoặc các ứng dụng đồng hành của Android Auto giao tiếp với ứng dụng của bạn.

  • MediaSession gói một trình phát Media3 và bạn có thể dễ dàng tạo một trình phát sau khi có trình phát.

    MediaSession.Builder(context, player).build()
  • Xin nhắc lại, đây có thể là ExoPlayer hoặc bất kỳ trình phát nào khác mà bạn có trong ứng dụng, giống như với giao diện người dùng Compose.

  • Nếu đang phát trong nền, bạn thường sẽ lưu trữ nội dung này trong MediaSessionService hoặc MediaLibraryService. Các dịch vụ này cũng tự động xử lý toàn bộ vòng đời cho bạn và những nội dung như thông báo.

    class PlaybackService : MediaSessionService() {
    
      private lateinit var mediaSession:
            MediaSession
    
      override fun onGetSession(
            info: ControllerInfo) : MediaSession?
        = mediaSession
    }
  • Nhưng nếu có một ứng dụng đa phương tiện, bạn có thể đã biết tất cả những điều này.

  • Điều mới mẻ thú vị mà tôi muốn giới thiệu với bạn là CastPlayer mới được viết lại.

  • Google Cast cho phép bạn chuyển nội dung đang phát trên thiết bị của mình sang một thiết bị từ xa, chẳng hạn như Android TV, Chromecast hoặc loa thông minh.

  • Trước đây, chúng tôi đã phát hành một SDK Truyền riêng biệt, hiện đang ở chế độ bảo trì.

  • Thay vào đó, Media3 Cast thay thế hoàn toàn vì cung cấp khả năng tích hợp đơn giản và hợp lý hơn nhiều.

  • Thành phần quan trọng nhất ở đây là CastPlayer, triển khai cùng một giao diện trình phát tiêu chuẩn như ExoPlayer.

  • Mặc dù ExoPlayer dùng để phát trên thiết bị, nhưng CastPlayer là một thực thể uỷ quyền có thể phát trên thiết bị thông qua ExoPlayer hoặc phát từ xa thông qua giao thức Truyền.

  • Và nó sẽ lo mọi thứ.

  • Trình chuyển đổi đầu ra sẽ theo dõi các thay đổi về thiết bị đầu ra, thiết lập kết nối, đồng bộ hoá trạng thái và chuyển nội dung phát cho bạn khi cần.

  • Do đó, việc tích hợp CastPlayer vào ứng dụng của bạn là cực kỳ đơn giản.

    val player = ExoPlayer.Builder(context).build()
    
    mediaSession = MediaSession.Builder(context,player)
    .build()
  • Thay vì tạo một ExoPlayer và chia sẻ với MediaSession như thế này, bạn chỉ cần sử dụng CastPlayer.

    Cast.getSingletonInstance(context).initialize()
    
    val player = CastPlayer.Builder(context).build()
    
    mediaSession = MediaSession.Builder(context,player)
    .build()
  • Việc duy nhất bạn cần làm là khởi động hệ thống Cast trước khi sử dụng hệ thống này ở một nơi nào đó trong ứng dụng.

  • Xin lưu ý rằng CastPlayer này tạo một ExoPlayer nội bộ để phát trên thiết bị, vì vậy, để tuỳ chỉnh thêm trình phát trên thiết bị, bạn cũng có thể tạo ExoPlayer của riêng mình và truyền ExoPlayer đó dưới dạng trình phát trên thiết bị đến CastPlayer này.

    Cast.getSingletonInstance(context).initialize()
    val exoPlayer = ExoPlayer.Builder(context).build()
    val player = CastPlayer.Builder(context)
                    .setLocalPlayer(exoPlayer).build()
    mediaSession = MediaSession.Builder(context,player)
    .build()
  • Và đây cũng là nguyên tắc tương tự của Media3.

  • Chúng tôi cung cấp các khối xây dựng API đơn giản, có sẵn, cũng như tất cả các lựa chọn tuỳ chỉnh mà bạn cần để tuỳ chỉnh thêm.

  • Và bạn có thể thắc mắc về cách người dùng thực sự chuyển đổi giữa chế độ phát trên thiết bị và chế độ phát từ xa trong chế độ thiết lập này.

  • Tính năng này đã hoạt động ngay lập tức với hộp thoại trình chuyển đổi đầu ra của hệ thống trong thông báo nội dung nghe nhìn.

  • Đó là nút nhỏ ở góc đó.

  • Nhưng nếu bạn muốn hiển thị nút truyền trong màn hình phát để người dùng dễ tìm thấy hơn thì sao?

  • Bạn có nhớ giao diện người dùng Compose tuỳ chỉnh mà chúng ta vừa tạo cách đây vài phút không?

    Player(
      exoPlayer,
      centerControls = {}
      bottomControls = { /* customized */ },
    )
  • Media3 Cast cũng cung cấp một nút có sẵn mà bạn có thể đặt ở đó.

  • Ví dụ: bạn chỉ cần thêm MediaRouteButton có sẵn vào topControls và không cần thiết lập thêm tham số nào khác. Chỉ có vậy thôi.

    Player(
      castPlayer,
      centerControls = {}
      bottomControls = { /* customized */ },
      topControls = {
        PlayerDefaults.TopControls(castPlayer) {
          MediaRouteButton(
            Modifier.align(Alignment.TopEnd))
        }
      },
    )
  • Khi bạn nhấp vào nút này, hộp thoại trình chuyển đổi đầu ra hệ thống tương tự sẽ mở ra, sau đó CastPlayer sẽ chuyển nội dung phát cho bạn.

  • Và bạn cũng lưu ý rằng tôi đã kết nối CastPlayer với giao diện người dùng tại đây để đảm bảo mọi hoạt động từ xa đều được phản ánh trong giao diện người dùng cục bộ.

  • Bây giờ, Kristina sẽ trình bày về các trường hợp sử dụng tính năng chỉnh sửa.

API chỉnh sửa Media3

  • Cảm ơn bạn.

  • Chào bạn, giờ hãy chuyển sang phần chỉnh sửa và hiệu ứng nâng cao.

  • API Media3 bao gồm nhiều thành phần.

  • Trước tiên, Transformer là điểm truy cập chính cho các API chỉnh sửa của chúng tôi.

  • Transformer xử lý, chuyển mã và xuất nội dung nghe nhìn sang một tệp.

  • Tiếp theo là CompositionPlayer, trình kết xuất bản xem trước theo thời gian thực của các thành phần nội dung nghe nhìn.

  • Nhờ đó, bạn có thể xem trước và tinh chỉnh nội dung chỉnh sửa trước khi xuất bản chính thức.

  • Tiếp theo là hiệu ứng.

  • Ứng dụng này giúp bạn có toàn quyền kiểm soát quá trình xử lý từng khung hình mỗi khi áp dụng hiệu ứng hình ảnh đơn giản hoặc thêm lớp phủ đồ hoạ.

  • Cuối cùng, chúng ta có Muxer.

  • Đây là giai đoạn đầu ra cuối cùng trong quy trình của chúng tôi, lấy các bản âm thanh, video và siêu dữ liệu đã xử lý rồi ghi vào vùng chứa MP4 chuẩn hoá.

  • Trong năm qua, chúng tôi đã suy nghĩ lại và xây dựng lại quy trình chỉnh sửa video từ đầu. Hôm nay, tôi rất vui được chia sẻ những điểm cải tiến quan trọng.

  • Tại sao chúng tôi xây dựng lại quy trình?

  • Do đó, cấu trúc cũ đặt ra một số thách thức.

  • Thứ nhất, nó không thể truyền siêu dữ liệu qua tất cả các bước xử lý.

  • Nó được liên kết chặt chẽ với OpenGL, điều này làm hạn chế tính linh hoạt của các công cụ đồ hoạ hiện đại.

  • Ngoài ra, API phức tạp của thư viện này khiến việc tuỳ chỉnh và bảo trì trở thành một rào cản lớn đối với nhà phát triển.

  • Để giải quyết những thách thức này, chúng tôi đã xây dựng một quy trình xử lý hoàn toàn mới.

  • Thứ nhất, việc tập hợp các thành phần phức tạp gồm nhiều thành phần giờ đây đã đơn giản hơn nhiều.

  • Thứ hai, bằng cách tách khỏi OpenGL, bạn có toàn quyền sử dụng các khung đồ hoạ hiện đại, chẳng hạn như WebGPU.

  • Cuối cùng, Media3 hiện quản lý trực tiếp bộ giải mã và bộ mã hoá, đảm bảo tính nhất quán và hiệu quả cao hơn trên tất cả thiết bị.

  • Hãy cùng tìm hiểu kỹ hơn về cách hoạt động của quy trình này.

  • Bộ giải mã xuất khung hình trực tiếp vào vùng đệm phần cứng do Media3 quản lý.

  • Bộ tổng hợp khung hình điều phối các khung hình trên nhiều trình tự, cho phép bộ xử lý khung hình tuỳ chỉnh của bạn truy cập trực tiếp để đọc và xử lý các vùng đệm đó trước khi gửi đến bộ mã hoá.

  • Giao diện FrameProcessor đóng vai trò là điểm mở rộng chính cho quá trình xử lý ở cấp ứng dụng.

    public interface FrameProcessor
        extends AutoCloseable {
    
      interface Listener {
        void onWakeup();
        void onError(
             VideoFrameProcessingException exception);
        void onFrameProcessed(
             Frame frame,
             @Nullable SyncFenceWrapper onCompleteFence);
      }
    
      interface Factory {
        FrameProcessor create(
             FrameWriter output,
             Executor listenerExecutor,
             Listener listener);
      }
    
      boolean queue(List<AsyncFrame> frames);
    
      void signalEndOfStream();
    }
  • Thông qua giao diện trình nghe, ứng dụng của bạn sẽ thông báo cho Media3 bất cứ khi nào một khung hình được xử lý, truyền dọc theo các rào chắn đồng bộ hoá, nhờ đó Media3 biết chính xác thời điểm có thể sử dụng lại vùng đệm một cách an toàn.

  • Lưu ý về bộ nhớ hợp nhất: bằng cách cho phép Media3 nắm quyền sở hữu vùng đệm phần cứng, ứng dụng của bạn có thể đọc và sửa đổi dữ liệu ngay tại chỗ.

  • Phương pháp không sao chép này giúp giảm mức hao tổn bộ nhớ và duy trì tốc độ xử lý cực nhanh.

  • Adobe là một trong những nhà phát triển tận dụng các API chỉnh sửa mới.

  • Nhóm Adobe Premiere đã sử dụng các API chỉnh sửa Media3 làm nền tảng cho cơ sở hạ tầng chỉnh sửa nội dung nghe nhìn của họ để đảm bảo khả năng tương thích cao hơn trên hệ sinh thái Android, đồng thời duy trì tính linh hoạt cần thiết để sử dụng quy trình tuỳ chỉnh và WebGPU của riêng họ.

  • Tiếp theo, tôi rất vui được thông báo rằng chúng tôi sẽ phát hành các hiệu ứng mới trong bản phát hành Media3 1.12.

  • Bắt đầu bằng các tính năng cải tiến video.

  • Về mặt kỹ thuật, cấu trúc của chúng tôi dựa trên chế độ thiết lập mô-đun.

  • Ứng dụng của bạn tích hợp với Dịch vụ Play hiệu ứng Media3, giao diện với mô-đun Cải tiến Dịch vụ Play.

  • Mô-đun Cải thiện video bao gồm các tính năng ánh xạ tông màu, khử nhiễu, khử mờ và siêu phân giải.

  • Bạn chỉ cần vài dòng mã để tích hợp tính năng Cải thiện video vào quy trình của mình.

    val frameProcessorFactory = EnhancementSessionDecorator.Builder(/* context = */ this, baseGlFactory)
        .setTonemappingEnabled(true)
        .setDeblurAndDenoiseEnabled(true)
        .setUpscaleVideoEnabled(true)
        .build()
    
    val transformerBuilder = Transformer.Builder(/* context = */ this)
        .setFrameProcessorFactory(frameProcessorFactory)
  • Khi sử dụng EnhancementSessionDecorator.Builder, bạn có thể bật một cách có chọn lọc các khả năng như ánh xạ sắc độ, xoá mờ hoặc tăng độ phân giải, đồng thời truyền frameProcessorFactory thu được và trình trợ giúp vùng đệm phần cứng vào Transformer.Builder.

  • Một hiệu ứng thú vị khác sắp có trong Media3 1.12 là Âm thanh phòng thu.

  • Tính năng Âm thanh chuyên nghiệp tự động phân tích bản âm thanh trong video của bạn và tách thành các thành phần riêng lẻ như lời nói, tiếng ồn xung quanh và nhạc.

  • Nhà sáng tạo có thể dễ dàng giảm tiếng gió hoặc tiếng ồn của đám đông, đồng thời làm nổi bật lời thoại và nhạc.

  • Để tích hợp tính năng Âm thanh phòng thu, hãy truyền bộ xử lý âm thanh của tính năng Âm thanh phòng thu vào bên trong một đối tượng hiệu ứng.

    val editedMediaItem =
         EditedMediaItem.Builder(MediaItem.fromUri(media.uri))
           .setEffects(
             Effects(
               listOf(
                 soundSeparationAudioProcessor
               ),
               emptyList(), // video effects
             )
           )
           .build()
  • Thao tác này sẽ đính kèm bộ xử lý âm thanh vào mục nội dung nghe nhìn.

  • Khi tạo bộ xử lý âm thanh Studio Sound, hãy đính kèm lệnh gọi lại trình nghe để nhận thông tin liên kết kênh âm thanh đã phân tích thông qua onResult.

    val soundSeparationAudioProcessor =
          SoundSeparationAudioProcessor(
            getApplication(),
            mainExecutor,
            object : SoundSeparationAudioProcessor.Listener {
              override fun onResult(channelToAudioType: SparseIntArray) {
                for (i in 0 until channelToAudioType.size()) {
                  val channelIndex = channelToAudioType.keyAt(i)
                  val audioType = channelToAudioType.valueAt(i)
                  val label = toAudioTypeName(audioType)
                  detectedChannelVolumes.add(ChannelVolume(channelIndex, audioType, label))
                }
              }
            },
          )
  • Lặp lại qua từng kênh để trích xuất chỉ mục kênh, loại âm thanh được xác định và nhãn có thể đọc được khi có kết quả của thao tác.

  • Giờ thì hãy cùng nghe thử tính năng Âm thanh chuyên nghiệp.

  • Vậy nên, hiệu ứng đầu tiên. Em ơi, xin đừng khóc.

  • Chúng tôi đang cắt thịt cho bữa tối của bạn.

  • Bạn có thể nghe thấy tiếng em bé khóc ở phía sau và giờ đây, hiệu ứng đã được áp dụng.

  • Đúng vậy, như bạn có thể nghe thấy, không có tiếng em bé khóc trong nền nhờ hiệu ứng Âm thanh phòng thu.

  • Bây giờ, hãy cùng tìm hiểu về cách truy xuất siêu dữ liệu.

  • Chúng ta sẽ tìm hiểu cách Media3 giúp bạn dễ dàng đọc siêu dữ liệu vùng chứa và trích xuất khung hình.

  • Bắt đầu từ MetadataRetriever, thư viện này tích hợp liền mạch với coroutine Kotlin để tạo ra mã sạch, không chặn.

  • Thiết kế này gọn gàng hơn, ít lỗi hơn nhờ sử dụng một trình truy xuất không thể thay đổi cho mỗi mục nội dung nghe nhìn.

  • Thư viện này hoạt động nhất quán trên tất cả phiên bản API và thiết bị.

  • Thư viện này hỗ trợ nhiều định dạng nội dung nghe nhìn ngay từ đầu.

  • Sau đây là một ví dụ thực tế về cách sử dụng MetadataRetriever.

    MetadataRetriever.Builder(context, mediaItem).build().use { retriever ->
        // 1. Duration (in microseconds)
        val durationUs = retriever.retrieveDurationUs().await()
        // 2. Track formats & container metadata (reuses the same preparation pass)
        val trackGroups = retriever.retrieveTrackGroups().await()
        for (i in 0 until trackGroups.length) {
            val format = trackGroups[i].getFormat(0)
            // Video/Audio properties: format.width, format.height, format.colorInfo …
            // Rich metadata: SlowMotionData, MotionPhotoMetadata, Mp4TimestampData …
            val metadata = format.metadata
        }
        // 3. Stream structure & manifest info
        val timeline = retriever.retrieveTimeline().await()
    }
  • Khi sử dụng coroutine Kotlin, bạn có thể truy xuất thời lượng, kiểm tra định dạng bản nhạc và siêu dữ liệu một cách không đồng bộ, tất cả trong một khối duy nhất, rõ ràng.

  • Bây giờ, hãy nói về FrameExtractor.

  • Thư viện này giúp lấy khung hình video đã giải mã từ mục nội dung nghe nhìn một cách nhanh chóng và hiệu quả.

  • Thư viện này tự động quản lý tài nguyên cơ bản như bộ giải mã, cho phép bạn áp dụng hiệu ứng hình ảnh hoặc ánh xạ tông màu HDR, đồng thời cung cấp cho bạn quyền kiểm soát tua chi tiết để bạn có thể lấy chính xác khung hình mình cần.

  • Sau đây là cách trích xuất khung hình trong mã.

    FrameExtractor.Builder(context, mediaItem)
        .setSeekParameters(SeekParameters.CLOSEST_SYNC) // Fast keyframe seeking
        .setEffects(listOf(Presentation.createForHeight(360))) // GPU downscaling
        .setExtractHdrFrames(true) // Optional: HLG HDR or UltraHDR
        .build()
        .use { extractor ->
            // Representative thumbnail
            val thumbnail = extractor.getThumbnail().await()
            // Frame at specific timestamp (bitmap + actual presentationTimeMs)
            val frame = extractor.getFrame(positionMs = 5_000L).await()
        }
  • Bạn tạo một thực thể bằng các tuỳ chọn như tham số tua, giảm tỷ lệ GPU, hiệu ứng và hỗ trợ HDR, sau đó chờ hình thu nhỏ và khung hình cụ thể một cách không đồng bộ bằng các lệnh gọi tạm ngưng rõ ràng.

  • Giờ xin mời Donovan.

Tiêu chuẩn phát trực tuyến và định dạng nội dung nghe nhìn mới

  • Cảm ơn bạn, Kristina.

  • Tiếp theo, tôi muốn nói về một số công nghệ hỗ trợ truyền phát trực tuyến cơ bản mà chúng tôi đã thêm vào Media3, bao gồm cả việc hỗ trợ các định dạng như Eclipsa Video và Eclipsa Audio.

  • Vì vậy, nỗ lực cốt lõi trong Media3 là luôn đi trước các tiêu chuẩn phát trực tuyến để mỗi khi có thông số kỹ thuật mới, bạn không cần phải viết lại quy trình phát nội dung nghe nhìn.

  • Sau đây là một vài ví dụ về những tính năng chúng tôi đã ra mắt gần đây.

  • Chúng tôi có tính năng điều hướng nội dung, cho phép chuyển đổi linh hoạt các đường dẫn CDN phụ trợ giữa luồng.

  • Chúng tôi hỗ trợ tính năng chèn quảng cáo do máy chủ hướng dẫn để ghép quảng cáo được cá nhân hoá một cách liền mạch.

  • Chúng tôi cũng đã ra mắt tính năng hỗ trợ AV1 trên nhiều điểm truy cập, bao gồm cả tính năng hỗ trợ đầy đủ AV1 HDR.

  • Chúng tôi hỗ trợ video chuyên nghiệp trên Android để quay và chỉnh sửa video có độ chân thực cao.

  • Chúng tôi cũng sẽ hỗ trợ ảnh chuyển động ở cả định dạng JPEG và HEIC.

  • Và tất nhiên, chúng tôi cũng sẽ hỗ trợ Eclipsa Video và Eclipsa Audio. Tôi sẽ sớm chia sẻ thêm về hai công nghệ này.

  • Tin vui là tất cả những tính năng này đều được hỗ trợ tự động khi chúng tôi triển khai tính năng hỗ trợ thiết bị.

  • Bây giờ, hãy cùng tìm hiểu về bước tiến tiếp theo của HDR, Eclipsa Video và vấn đề cốt lõi mà công nghệ này đang cố gắng giải quyết.

  • Vì vậy, các định dạng HDR trước đây cung cấp siêu dữ liệu mô tả, nên chúng xác định giao diện của cảnh, nhưng không thực sự cho màn hình biết cách kết xuất cảnh đó.

  • Hãy cùng tìm hiểu về vấn đề này trước.

  • Đối với các môi trường khác nhau, bạn nên điều chỉnh độ sáng màn hình trung bình theo các mức khác nhau để có thể xem nội dung một cách rõ ràng và thoải mái.

  • Vì vậy, trong phòng tối, phạm vi đó sẽ nằm trong khoảng từ 100 đến 300 nit.

  • Và tất nhiên, khi bạn bước vào môi trường ngày càng sáng, màn hình phải theo kịp và sáng hơn để bạn có thể nhìn rõ nội dung. Khi bạn xem điện thoại dưới ánh nắng trực tiếp, màn hình có thể cần đạt đến 1.000, 2.000 hoặc thậm chí nhiều nit hơn để bạn có thể xem nội dung.

  • SDR truyền thống hoạt động trong phạm vi từ 100 đến 300 nit.

  • Và tất nhiên, HDR không nhằm mục đích làm sáng toàn bộ màn hình.

  • Bạn muốn mở rộng dải động để có các điểm sáng được bản địa hoá cho những thứ như lửa và ánh sáng mặt trời.

  • Tuy nhiên, điện thoại thông minh lại khiến mọi thứ trở nên phức tạp hơn một chút, vì chúng ta mang theo điện thoại đến mọi nơi, từ phòng tối trong nhà cho đến bãi biển đầy nắng.

  • Điều đó kết hợp với thực tế là nhiều ứng dụng đang kết hợp các phần tử trên giao diện người dùng SDR với nội dung SDR và nội dung HDR trong các nguồn cấp dữ liệu hỗn hợp, và điều đó có thể dẫn đến hiệu ứng chói mắt, khó chịu mà đôi khi bạn có thể gặp phải, đặc biệt là nếu bạn đang cuộn xem vào đêm muộn.

  • Và chúng ta có thể thấy lý do HDR đáng giá, phải không?

  • Điều này giúp nội dung của chúng ta trông đẹp mắt trong nhiều môi trường hơn.

  • Chúng tôi thậm chí còn nhận thấy mức độ tương tác của người dùng tăng lên khi bật HDR.

  • Tuy nhiên, vấn đề là các định dạng HDR hiện tại không tính đến những môi trường này.

  • Đó chính là điểm mạnh thực sự của Eclipsa Video.

  • Tạm dừng để cười.

  • Cảm ơn bạn.

  • Cảm ơn bạn.

  • Vì vậy, thay vì siêu dữ liệu mô tả, Eclipsa Video cung cấp siêu dữ liệu mang tính quy định.

  • Và điều đó thực sự được xây dựng dựa trên ba trụ cột chính.

  • Trụ cột đầu tiên là điểm neo màu trắng tham chiếu.

  • Vì vậy, chế độ này sẽ khoá độ sáng tiêu chuẩn ở điểm trắng SDR được điều chỉnh theo môi trường xung quanh của màn hình, giúp loại bỏ tình trạng độ sáng tăng đột ngột.

  • Yếu tố thứ hai là tiêu chuẩn SDR của nhà sáng tạo.

  • Điều này giúp giữ nguyên đường cong ánh xạ tông màu chính xác của nhà sáng tạo khi chuyển về SDR.

  • Thứ ba, chúng tôi có các đường cong khuếch đại thích ứng với khoảng không, giúp điều chỉnh tỷ lệ vùng sáng một cách tất định để phù hợp với khoảng không HDR theo thời gian thực của màn hình, nhờ đó nội dung phù hợp với môi trường xem.

  • Tính năng này có sẵn trong ExoPlayer trong Media3 1.11 trên các thiết bị chạy Android 17 trở lên.

  • Ngoài ra, Eclipsa Video được xây dựng dựa trên các tiêu chuẩn mở để tương thích với nhiều ngành.

  • Vì vậy, đây là thời điểm tốt nhất để hỗ trợ HDR.

  • Về âm thanh, chúng tôi có Eclipsa Audio, một định dạng âm thanh không gian 3D nguồn mở và miễn phí bản quyền.

  • Định dạng này dựa trên quy cách IAMF của Liên minh Truyền thông Mở.

  • Định dạng này kết hợp âm thanh Ambisonics dựa trên cảnh với âm thanh dựa trên kênh thành một luồng duy nhất có nhiều bản trình bày kết hợp.

  • Vì vậy, định dạng này cho phép thao tác giống như đối tượng, chẳng hạn như cải thiện lời thoại, và sẽ hoạt động trên mọi thiết bị, từ tai nghe âm thanh nổi đến hệ thống âm thanh vòm.

  • Thực tế có hai đường dẫn phát trong Media3.

  • Đối với Android 17 trở lên, ExoPlayer và Khung âm thanh Android sẽ tự động kết xuất Eclipsa Audio.

  • Còn đối với Android 16 trở xuống, bạn vẫn có một lựa chọn.

  • Bạn có thể bật tiện ích IAMF thông qua một thư viện tiện ích bổ sung Media3 không bắt buộc.

  • Về mặt sáng tạo, bạn có thể viết các bản âm thanh IAMF bằng bộ ghép kênh Media3.

Tối ưu hoá hiệu suất và pin

  • Nhưng việc hỗ trợ các định dạng chất lượng cao chỉ là một phần của câu chuyện.

  • Chúng ta phải cung cấp chất lượng đó một cách hiệu quả.

  • Bây giờ, tôi muốn nói về cách Media3 giúp quy trình của chúng tôi nhanh hơn và hiệu quả hơn.

  • Giống như nhiều điều chúng ta đã thảo luận hôm nay, hầu hết các điểm cải thiện hiệu suất này đều có sẵn ngay khi bạn cập nhật phiên bản Media3.

  • Một ví dụ về tính năng mà chúng tôi có là giảm tải âm thanh PCM, có thể định tuyến việc phát âm thanh đến một đơn vị xử lý công suất thấp.

  • Trong bản phân tích ban đầu, chúng tôi nhận thấy mức tiết kiệm điện năng lên đến 20% khi phát âm thanh lúc tắt màn hình.

  • Ngoài tính năng giảm tải âm thanh PCM, chúng tôi còn có tính năng lập lịch biểu động, giúp điều chỉnh hoạt động đánh thức vòng lặp kết xuất nội bộ của ExoPlayer cho phù hợp với VSYNC và khả năng cung cấp bộ đệm của bộ giải mã.

  • Chúng tôi cũng có tính năng khởi động trước trình kết xuất, cho phép bạn khởi động trước một trình kết xuất video phụ, giúp chuyển đổi danh sách phát tức thì và ghép quảng cáo dễ dàng hơn.

  • Chúng tôi cũng có tính năng phát hiện trình phát bị treo và lưu trước vào bộ nhớ đệm, tự động phát hiện các trình kết xuất và vùng đệm bị treo, đồng thời giới hạn bộ nhớ được lưu trước vào bộ nhớ đệm một cách rõ ràng.

  • Một chế độ tối ưu khác mà chúng tôi có là chế độ kéo để tua.

  • Và điều này dành cho những ứng dụng mà người dùng thường xuyên kéo để tua nội dung.

  • Vì vậy, khi bạn bật chế độ kéo để tua, thao tác kéo thanh tua sẽ mượt mà và nhạy hơn nhiều, như bạn có thể thấy ở đây.

  • Nhân tiện, đây là khi tắt chế độ tua.

  • Và ở bên phải, chúng ta thấy chế độ kéo để tua đang bật.

  • Cảm ơn bạn.

  • Cảm ơn bạn.

  • Được rồi.

  • Bây giờ, hãy cùng tìm hiểu về Tình trạng hệ thống Android.

  • Bạn biết đấy, chúng ta cần tôn trọng tài nguyên RAM, CPU và pin dùng chung. Vì vậy, khi Android thắt chặt các quy tắc về Tăng cường âm thanh và dịch vụ nền trước, các ứng dụng đang quản lý dịch vụ thô và bitmap của riêng chúng thường có thể gặp một số lỗi.

  • Một số trường hợp bạn có thể đã thấy là ngoại lệ không cho phép khởi động dịch vụ nền trước hoặc ngoại lệ hết bộ nhớ của trình liên kết.

  • Vì vậy, chúng tôi đã tăng cường phiên dịch vụ nội dung nghe nhìn và trình quản lý thông báo nội dung nghe nhìn cũng như quy trình bitmap theo một số cách, bao gồm cả việc ngăn chặn ý định dịch vụ nền trước cũ và chúng tôi liên kết bộ nhớ ảnh minh hoạ và những thứ tương tự.

  • Tuy nhiên, điều quan trọng là nếu bạn đang sử dụng Media3 và làm theo hướng dẫn tiêu chuẩn, thì tất cả các quy định hạn chế về trạng thái hệ thống này sẽ được xử lý mà không gây ảnh hưởng.

  • Cuối cùng, tôi muốn nói về một tính năng sắp ra mắt trong Media3.

  • Khi đến lúc mã hoá hoặc xuất video, sự đa dạng về phần cứng có thể là một trong những thách thức lớn nhất.

  • Nhiều ứng dụng sử dụng một cấu hình bộ mã hoá và giải mã chung cho mọi trường hợp, điều này dẫn đến chất lượng và hiệu suất không tối ưu.

  • Đó là lý do chúng tôi xây dựng CodecDB.

  • CodecDB được xây dựng dựa trên các phép đo lường video thực tế mở rộng, sử dụng các chỉ số chất lượng được chuẩn hoá và điểm chuẩn phần cứng trên các thiết bị Android.

  • Hãy xem ví dụ thực tế.

  • Ở bên trái, chúng ta có một khung hình của video được quay bằng điện thoại, trong đó chúng ta chỉ sử dụng một cấu hình video rất chung chung mà chúng ta có thể sử dụng trên nhiều thiết bị.

  • Còn ở bên phải, chúng tôi sử dụng cấu hình tối ưu ở cấp thiết bị mà chúng tôi lấy từ CodecDB.

  • Vậy thì điểm khác biệt nằm ở đâu?

  • Nếu nhìn vào những vùng có độ chi tiết cao, bạn sẽ thấy sự khác biệt lớn nhất về chất lượng.

  • Vì vậy, hãy xem độ rõ nét của cành cây và bụi cây hoặc độ sắc nét của mép toà nhà.

  • Vì vậy, chúng ta sẽ có trải nghiệm xem tốt hơn nhiều, tận dụng được các chức năng của một thiết bị cụ thể bằng cách sử dụng CodecDB.

  • Và việc sử dụng CodecDB trong mã khá đơn giản.

    val config = CodecDb.onDevice(context)
       .forEncodingFallingBackTo {
               constraints -> createFallbackConfig(constraints)
       }
       .findOptimalSettings()
       .forEditing()
       .withResolution(1920, 1080, /* frameRate= */ 30.0)
       .withAnyCodec()
       .targetingQuality(VideoEncoder.QUALITY_LEVEL_VERY_GOOD)
       .get()
  • Bạn sẽ sử dụng mẫu trình tạo giai đoạn thông thạo để hướng dẫn bạn thực hiện từng tham số bắt buộc.

  • Vì vậy, bạn sẽ chỉ định trường hợp sử dụng của mình.

  • Ở đây, chúng ta sẽ dùng để chỉnh sửa.

  • Thao tác này sẽ cho CodecDB biết rằng bạn đang sử dụng quy trình xuất hoặc chuyển mã Media3.

  • Một số trường hợp sử dụng khác mà bạn có thể đặt, chẳng hạn như chụp ảnh bằng camera nếu bạn đang sử dụng camera.

  • Bạn cũng sẽ truyền độ phân giải và tốc độ khung hình mục tiêu.

  • Sau đó, thay vì đoán xem video 1080p đó cần 8 megabit/giây hay 15 megabit/giây, bạn cho CodecDB biết bộ mã hoá và giải mã mà bạn muốn sử dụng và yêu cầu một mục tiêu chất lượng cảm nhận.

  • Trong trường hợp này, chúng ta sẽ sử dụng mức chất lượng rất tốt.

  • Trong thầm lặng, CodecDB sẽ tra cứu các đường cong điểm chuẩn thực nghiệm cho chính xác SOC đó và trả về các tham số bộ mã hoá và bộ mã hoá và giải mã lý tưởng.

  • Chúng tôi dự kiến sẽ sớm ra mắt CodecDB trong Media3 và khi ra mắt, CodecDB sẽ sử dụng các loại Media3 gốc.

  • Vì vậy, việc tích hợp thư viện này với mã Media3 hiện có sẽ rất dễ dàng.

Tóm tắt và kết luận

  • Được rồi.

  • Hôm nay, chúng ta đã thấy rất nhiều chức năng tuyệt vời trong Media3, vì vậy, hãy cùng tóm tắt lại.

  • Hôm nay, bạn đã tìm hiểu về các mô-đun Giao diện người dùng Compose mới, CastPlayer mới, quy trình chỉnh sửa được cải tiến và nhiều hiệu ứng mới mà bạn có thể sử dụng trong Media3.

  • Chúng ta cũng đã thấy các công cụ mới để truy xuất siêu dữ liệu và khung hình.

  • Chúng tôi cũng đã xem xét các tiện ích, tính năng nâng cao hiệu suất và hỗ trợ công nghệ truyền trực tuyến mới.

  • Media3 là một thư viện nguồn mở, vì vậy, bạn có thể xem cách chúng tôi triển khai tất cả các tính năng này trong kho lưu trữ GitHub.

  • Tính đến hôm nay, bản phát hành ổn định mới nhất của Media3 là 1.11.1.

  • Bạn cũng có thể xem phiên bản 1.12.0, hiện đang ở giai đoạn alpha.

  • Và hãy truy cập vào đường liên kết ngắn này để xem tài liệu dành cho nhà phát triển về Media3.

  • Cảm ơn các bạn rất nhiều và chúc các bạn có một ngày vui vẻ tại ADS.

Mã

Đoạn mã

Đoạn mã Kotlin ở phút 02:31 trong video "Giao diện người dùng đa phương tiện bằng Jetpack Compose"
@Composable
fun videoScreen() {
  Player(viewModel.exoPlayer)
}
Đoạn mã Kotlin ở phút 04:44 trong video "Giao diện người dùng đa phương tiện bằng Jetpack Compose"
Player(viewModel.exoPlayer)
Đoạn mã Kotlin ở phút 04:48 trong video "Giao diện người dùng đa phương tiện bằng Jetpack Compose"
Player(
  player = viewModel.exoPlayer,
  bottomControls = {
    PlayerDefaults.BottomControls(
      player,
      left = {}, right = {},
      above = {
        PositionAndDurationText(
          player, separator = " • ")
      },
    )
  },
)
Đoạn mã Kotlin ở phút 05:13 trong video "Giao diện người dùng của nội dung nghe nhìn bằng Jetpack Compose"
Player(
  player = viewModel.exoPlayer,
  centerControls = {},
  bottomControls = { ...
      above = {
        Row(Modifier.fillMaxWidth()) {
          PositionAndDurationText(...)
          Spacer(Modifier.weight(1f))
          PlaybackSpeedToggleButton(player)
          MuteButton(player)
          PlayPauseButton(player)
        }
      },
    )
  }
Đoạn mã Kotlin ở phút 05:51 trong video "Giao diện người dùng đa phương tiện bằng Jetpack Compose"
Player(
  ...
  bottomControls = { ...
      above = {
          ...
          MuteButton(player,
            painter = { painterResource(
              if (showMuted)
                R.drawable.muted
              else
                R.drawable.unmuted )})
      }
  },
)
Đoạn mã Kotlin ở phút 07:01 trong video "Chia sẻ chế độ phát bằng MediaSession"
MediaSession.Builder(context, player).build()
Đoạn mã Kotlin ở phút 07:17 trong video "Chia sẻ chế độ phát bằng MediaSession"
class PlaybackService : MediaSessionService() {

  private lateinit var mediaSession:
        MediaSession

  override fun onGetSession(
        info: ControllerInfo) : MediaSession?
    = mediaSession
}
Đoạn mã Kotlin ở phút 08:25 trong video "Chia sẻ chế độ phát bằng MediaSession"
val player = ExoPlayer.Builder(context).build()

mediaSession = MediaSession.Builder(context,player)
.build()
Đoạn mã Kotlin ở phút 08:29 trong video "Chia sẻ chế độ phát bằng MediaSession"
Cast.getSingletonInstance(context).initialize()

val player = CastPlayer.Builder(context).build()

mediaSession = MediaSession.Builder(context,player)
.build()
Đoạn mã Kotlin ở phút 08:45 trong video "Chia sẻ chế độ phát bằng MediaSession"
Cast.getSingletonInstance(context).initialize()
val exoPlayer = ExoPlayer.Builder(context).build()
val player = CastPlayer.Builder(context)
                .setLocalPlayer(exoPlayer).build()
mediaSession = MediaSession.Builder(context,player)
.build()
Đoạn mã Kotlin ở phút 09:32 trong video "Chia sẻ chế độ phát bằng MediaSession"
Player(
  exoPlayer,
  centerControls = {}
  bottomControls = { /* customized */ },
)
Đoạn mã Kotlin ở phút 09:40 trong video "Chia sẻ chế độ phát bằng MediaSession"
Player(
  castPlayer,
  centerControls = {}
  bottomControls = { /* customized */ },
  topControls = {
    PlayerDefaults.TopControls(castPlayer) {
      MediaRouteButton(
        Modifier.align(Alignment.TopEnd))
    }
  },
)
Đoạn mã Java ở phút 13:02 trong video "API chỉnh sửa Media3"
public interface FrameProcessor
    extends AutoCloseable {

  interface Listener {
    void onWakeup();
    void onError(
         VideoFrameProcessingException exception);
    void onFrameProcessed(
         Frame frame,
         @Nullable SyncFenceWrapper onCompleteFence);
  }

  interface Factory {
    FrameProcessor create(
         FrameWriter output,
         Executor listenerExecutor,
         Listener listener);
  }

  boolean queue(List<AsyncFrame> frames);

  void signalEndOfStream();
}
Đoạn mã Kotlin ở phút 14:45 trong video "API chỉnh sửa Media3"
val frameProcessorFactory = EnhancementSessionDecorator.Builder(/* context = */ this, baseGlFactory)
    .setTonemappingEnabled(true)
    .setDeblurAndDenoiseEnabled(true)
    .setUpscaleVideoEnabled(true)
    .build()

val transformerBuilder = Transformer.Builder(/* context = */ this)
    .setFrameProcessorFactory(frameProcessorFactory)
Đoạn mã Kotlin ở phút 15:36 trong video "API chỉnh sửa Media3"
val editedMediaItem =
     EditedMediaItem.Builder(MediaItem.fromUri(media.uri))
       .setEffects(
         Effects(
           listOf(
             soundSeparationAudioProcessor
           ),
           emptyList(), // video effects
         )
       )
       .build()
Đoạn mã Kotlin ở phút 15:50 trong video "API chỉnh sửa Media3"
val soundSeparationAudioProcessor =
      SoundSeparationAudioProcessor(
        getApplication(),
        mainExecutor,
        object : SoundSeparationAudioProcessor.Listener {
          override fun onResult(channelToAudioType: SparseIntArray) {
            for (i in 0 until channelToAudioType.size()) {
              val channelIndex = channelToAudioType.keyAt(i)
              val audioType = channelToAudioType.valueAt(i)
              val label = toAudioTypeName(audioType)
              detectedChannelVolumes.add(ChannelVolume(channelIndex, audioType, label))
            }
          }
        },
      )
Đoạn mã Kotlin ở phút 17:29 trong video "API chỉnh sửa Media3"
MetadataRetriever.Builder(context, mediaItem).build().use { retriever ->
    // 1. Duration (in microseconds)
    val durationUs = retriever.retrieveDurationUs().await()
    // 2. Track formats & container metadata (reuses the same preparation pass)
    val trackGroups = retriever.retrieveTrackGroups().await()
    for (i in 0 until trackGroups.length) {
        val format = trackGroups[i].getFormat(0)
        // Video/Audio properties: format.width, format.height, format.colorInfo …
        // Rich metadata: SlowMotionData, MotionPhotoMetadata, Mp4TimestampData …
        val metadata = format.metadata
    }
    // 3. Stream structure & manifest info
    val timeline = retriever.retrieveTimeline().await()
}
Đoạn mã Kotlin ở phút 18:06 trong video "API chỉnh sửa Media3"
FrameExtractor.Builder(context, mediaItem)
    .setSeekParameters(SeekParameters.CLOSEST_SYNC) // Fast keyframe seeking
    .setEffects(listOf(Presentation.createForHeight(360))) // GPU downscaling
    .setExtractHdrFrames(true) // Optional: HLG HDR or UltraHDR
    .build()
    .use { extractor ->
        // Representative thumbnail
        val thumbnail = extractor.getThumbnail().await()
        // Frame at specific timestamp (bitmap + actual presentationTimeMs)
        val frame = extractor.getFrame(positionMs = 5_000L).await()
    }
Đoạn mã Kotlin ở phút 28:26 trong video "Tối ưu hoá hiệu suất và pin"
val config = CodecDb.onDevice(context)
   .forEncodingFallingBackTo {
           constraints -> createFallbackConfig(constraints)
   }
   .findOptimalSettings()
   .forEditing()
   .withResolution(1920, 1080, /* frameRate= */ 30.0)
   .withAnyCodec()
   .targetingQuality(VideoEncoder.QUALITY_LEVEL_VERY_GOOD)
   .get()