Tổng quan về MediaPlayer

Khung đa phương tiện của Android bao gồm tính năng hỗ trợ phát nhiều loại phương tiện phổ biến, vì vậy, để bạn có thể dễ dàng tích hợp âm thanh, video và hình ảnh vào ứng dụng của mình. Bạn có thể phát âm thanh hoặc video từ các tệp đa phương tiện được lưu trữ trong tài nguyên của ứng dụng (tài nguyên thô), từ các tệp độc lập trong hệ thống tệp hoặc từ một luồng dữ liệu đến qua kết nối mạng, tất cả đều sử dụng API MediaPlayer.

Tài liệu này hướng dẫn bạn cách sử dụng MediaPlayer để ghi một nội dung nghe nhìn đang phát tương tác với người dùng và hệ thống để có được hiệu suất tốt và trải nghiệm người dùng thú vị. Ngoài ra, bạn có thể muốn để sử dụng ExoPlayer, một nguồn mở có thể tuỳ chỉnh thư viện hỗ trợ các tính năng hiệu suất cao không có trong MediaPlayer

Lưu ý: Bạn chỉ có thể phát lại dữ liệu âm thanh về đầu ra tiêu chuẩn thiết bị. Hiện tại, đó là loa trên thiết bị di động hoặc tai nghe Bluetooth. Bạn không thể phát âm thanh các tệp trong âm thanh cuộc trò chuyện trong khi gọi điện.

Thông tin cơ bản

Các lớp sau được dùng để phát âm thanh và video trong khung Android:

MediaPlayer
Lớp học này là API chính để phát âm thanh và video.
AudioManager
Lớp này quản lý các nguồn âm thanh và đầu ra âm thanh trên một thiết bị.

Nội dung khai báo trong tệp kê khai

Trước khi bắt đầu phát triển ứng dụng bằng MediaPlayer, hãy đảm bảo tệp kê khai của bạn đã các nội dung khai báo phù hợp để cho phép sử dụng các tính năng có liên quan.

  • Quyền Internet – Nếu bạn đang sử dụng MediaPlayer để phát trực tuyến dựa trên mạng thì ứng dụng của bạn phải yêu cầu quyền truy cập mạng.
    <uses-permission android:name="android.permission.INTERNET" />
    
  • Quyền khoá chế độ thức – Nếu ứng dụng trình phát của bạn cần giữ màn hình giảm độ sáng hoặc bộ xử lý khỏi chế độ ngủ, hoặc sử dụng MediaPlayer.setScreenOnWhilePlaying() hoặc MediaPlayer.setWakeMode(), bạn phải yêu cầu quyền này.
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    

Sử dụng MediaPlayer

Một trong những thành phần quan trọng nhất của khung truyền thông là MediaPlayer . Một đối tượng của lớp này có thể tìm nạp, giải mã và phát cả âm thanh lẫn video khi thiết lập rất ít. Nền tảng này hỗ trợ nhiều nguồn nội dung nghe nhìn như:

  • Tài nguyên địa phương
  • Các URI nội bộ, chẳng hạn như URI bạn có thể lấy từ Trình phân giải nội dung
  • URL bên ngoài (phát trực tuyến)

Để biết danh sách các định dạng nội dung nghe nhìn mà Android hỗ trợ, hãy xem Phương tiện được hỗ trợ Định dạng.

Sau đây là ví dụ về cách phát âm thanh có sẵn dưới dạng tài nguyên thô cục bộ (được lưu trong tệp Thư mục res/raw/):

Kotlin

var mediaPlayer = MediaPlayer.create(context, R.raw.sound_file_1)
mediaPlayer.start() // no need to call prepare(); create() does that for you

Java

MediaPlayer mediaPlayer = MediaPlayer.create(context, R.raw.sound_file_1);
mediaPlayer.start(); // no need to call prepare(); create() does that for you

Trong trường hợp này, giá trị "thô" tài nguyên là một tệp mà hệ thống không cố gắng phân tích cú pháp theo bất kỳ cách cụ thể nào. Tuy nhiên, nội dung của tài nguyên này không được trở thành âm thanh thô. Đó phải là một tệp đa phương tiện được mã hoá và định dạng đúng cách trong một trong số các định dạng được hỗ trợ.

Và đây là cách bạn có thể phát từ một URI có sẵn cục bộ trong hệ thống (ví dụ: video bạn nhận được thông qua Trình phân giải nội dung):

Kotlin

val myUri: Uri = .... // initialize Uri here
val mediaPlayer = MediaPlayer().apply {
    setAudioAttributes(
        AudioAttributes.Builder()
            .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
            .setUsage(AudioAttributes.USAGE_MEDIA)
            .build()
    )
    setDataSource(applicationContext, myUri)
    prepare()
    start()
}

Java

Uri myUri = ....; // initialize Uri here
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioAttributes(
    new AudioAttributes.Builder()
        .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
        .setUsage(AudioAttributes.USAGE_MEDIA)
        .build()
);
mediaPlayer.setDataSource(getApplicationContext(), myUri);
mediaPlayer.prepare();
mediaPlayer.start();

Phát từ một URL từ xa qua tính năng truyền trực tuyến HTTP sẽ có dạng như sau:

Kotlin

val url = "http://........" // your URL here
val mediaPlayer = MediaPlayer().apply {
    setAudioAttributes(
        AudioAttributes.Builder()
            .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
            .setUsage(AudioAttributes.USAGE_MEDIA)
            .build()
    )
    setDataSource(url)
    prepare() // might take long! (for buffering, etc)
    start()
}

Java

String url = "http://........"; // your URL here
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioAttributes(
    new AudioAttributes.Builder()
        .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
        .setUsage(AudioAttributes.USAGE_MEDIA)
        .build()
);
mediaPlayer.setDataSource(url);
mediaPlayer.prepare(); // might take long! (for buffering, etc)
mediaPlayer.start();

Lưu ý: Nếu bạn đang chuyển một URL để truyền trực tuyến tệp nội dung nghe nhìn trực tuyến, thì tệp đó phải có khả năng tải xuống tăng dần.

Thận trọng: Bạn phải bắt hoặc vượt qua IllegalArgumentExceptionIOException khi sử dụng setDataSource(), vì tệp mà bạn đang tham chiếu có thể không tồn tại.

Chuẩn bị không đồng bộ

Việc sử dụng MediaPlayer có thể rất đơn giản trong nguyên tắc. Tuy nhiên, bạn cần lưu ý một vài điều nữa để tích hợp chính xác với ứng dụng Android thông thường. Cho Ví dụ: lệnh gọi đến prepare() có thể mất nhiều thời gian để thực thi, bởi vì nó có thể liên quan đến việc tìm nạp và giải mã dữ liệu nội dung nghe nhìn. Vì vậy, như trường hợp với bất kỳ nhưng có thể mất nhiều thời gian để thực thi, bạn nên không bao giờ gọi phương thức này từ luồng giao diện người dùng của ứng dụng. Làm như vậy sẽ khiến giao diện người dùng bị treo cho đến khi phương thức này trả về. trải nghiệm người dùng rất kém và có thể gây ra lỗi ANR (Ứng dụng không phản hồi). Ngay cả khi bạn muốn tài nguyên của mình tải nhanh chóng, hãy nhớ rằng bất cứ thứ gì mất hơn 1/10 một giây để phản hồi trong giao diện người dùng sẽ gây ra một khoảng thời gian tạm dừng đáng kể và khiến người dùng có cảm giác rằng ứng dụng của bạn chậm.

Để tránh bị treo luồng giao diện người dùng, hãy tạo một luồng khác để chuẩn bị MediaPlayer và thông báo cho luồng chính khi hoàn tất. Tuy nhiên, trong khi bạn có thể viết logic phân luồng mẫu này rất phổ biến khi sử dụng MediaPlayer đến mức khung cung cấp một cách thuận tiện để thực hiện tác vụ này bằng cách sử dụng prepareAsync(). Phương thức này bắt đầu chuẩn bị nội dung đa phương tiện trong nền và quay lại ngay lập tức. Khi nội dung nghe nhìn đã chuẩn bị xong thì onPrepared() của MediaPlayer.OnPreparedListener, được định cấu hình thông qua setOnPreparedListener() sẽ được gọi.

Quản lý trạng thái

Một khía cạnh khác của MediaPlayer mà bạn nên lưu ý là dựa trên trạng thái. Tức là MediaPlayer có trạng thái nội bộ mà bạn phải luôn lưu ý khi viết mã, vì các thao tác nhất định chỉ hợp lệ khi người chơi ở những trạng thái cụ thể. Nếu bạn thực hiện thao tác trong khi sai trạng thái, hệ thống có thể gửi ra một ngoại lệ hoặc gây ra các hành vi không mong muốn khác.

Tài liệu trong Lớp MediaPlayer hiển thị một sơ đồ trạng thái hoàn chỉnh, giúp xác định những phương thức di chuyển MediaPlayer từ trạng thái này sang trạng thái khác. Ví dụ: khi bạn tạo một MediaPlayer mới, nó sẽ nằm trong Trạng thái rảnh trạng thái. Tại thời điểm đó, bạn nên khởi chạy bằng cách gọi setDataSource(), mang đến sang trạng thái Đã khởi chạy. Sau đó, bạn phải chuẩn bị tài khoản bằng cách sử dụng prepare() hoặc prepareAsync(). Thời gian MediaPlayer đã chuẩn bị xong, nó sẽ chuyển vào tệp Đã chuẩn bị trạng thái, có nghĩa là bạn có thể gọi start() phát nội dung đa phương tiện. Vào thời điểm đó, như minh hoạ trong sơ đồ, bạn có thể di chuyển giữa các trạng thái Đã bắt đầu, Đã tạm dừngPhát lại đã hoàn tất bằng cách gọi các phương thức như vậy, chẳng hạn như start(), pause()seekTo(), trong số những thứ khác. Khi gọi stop(). Tuy nhiên, hãy lưu ý rằng bạn không thể gọi lại start() cho đến khi bạn chuẩn bị lại MediaPlayer.

Luôn giữ biểu đồ trạng thái khi viết mã tương tác với Đối tượng MediaPlayer, vì việc gọi các phương thức của đối tượng này từ trạng thái không chính xác là một nguyên nhân phổ biến gây ra lỗi.

Phát hành MediaPlayer

MediaPlayer có thể tiêu thụ nội dung có giá trị tài nguyên hệ thống. Do đó, bạn phải luôn thực hiện các biện pháp phòng ngừa bổ sung để đảm bảo bạn không tiếp cận thực thể MediaPlayer lâu hơn mức cần thiết. Khi đã xong, bạn phải luôn gọi release() để đảm bảo bất kỳ tài nguyên hệ thống được phân bổ cho nó được giải phóng đúng cách. Ví dụ: nếu bạn đang bằng cách sử dụng MediaPlayer và hoạt động của bạn sẽ nhận được lệnh gọi đến onStop(), bạn phải huỷ bỏ MediaPlayer, vì nó theo dõi nó trong khi hoạt động không tương tác là rất khó khăn người dùng (trừ phi bạn đang phát nội dung nghe nhìn trong nền, điều này sẽ được thảo luận trong phần tiếp theo). Tất nhiên, khi hoạt động của bạn được tiếp tục hoặc khởi động lại, hãy tạo một MediaPlayer mới rồi chuẩn bị lại trước khi tiếp tục phát.

Dưới đây là cách bạn nên phát hành và sau đó rỗng MediaPlayer:

Kotlin

mediaPlayer?.release()
mediaPlayer = null

Java

mediaPlayer.release();
mediaPlayer = null;

Ví dụ: hãy xem xét các vấn đề có thể xảy ra nếu bạn quên phát hành MediaPlayer khi hoạt động của bạn bị dừng, nhưng hãy tạo một phạm vi mới khi hoạt động bắt đầu lại. Như bạn có thể biết, khi người dùng thay đổi hướng màn hình (hoặc thay đổi cấu hình thiết bị theo cách khác), hệ thống xử lý việc đó bằng cách khởi động lại hoạt động (theo mặc định), do đó bạn có thể nhanh chóng tiêu thụ tất cả tài nguyên hệ thống với tư cách là người dùng xoay thiết bị qua lại giữa chế độ dọc và chế độ ngang vì tại mỗi nút thay đổi hướng, bạn sẽ tạo một MediaPlayer mới mà bạn không bao giờ bản phát hành. (Để biết thêm thông tin về việc khởi động lại thời gian chạy, hãy xem phần Xử lý thay đổi thời gian chạy.)

Bạn có thể muốn biết điều gì sẽ xảy ra nếu bạn muốn tiếp tục phát "nội dung nghe nhìn trong nền" ngay cả khi người dùng rời khỏi hoạt động của bạn, hầu như không thay đổi mà ứng dụng Nhạc tích hợp sẵn hoạt động. Trong trường hợp này, điều bạn cần là MediaPlayer do một Dịch vụ kiểm soát, như được thảo luận trong phần tiếp theo

Sử dụng MediaPlayer trong dịch vụ

Nếu bạn muốn nội dung nghe nhìn phát trong nền ngay cả khi ứng dụng của bạn không trên màn hình – tức là bạn muốn màn hình tiếp tục phát trong khi người dùng tương tác với các ứng dụng khác—khi đó bạn phải bắt đầu một Phục vụ và kiểm soát Thực thể MediaPlayer từ đó. Bạn cần nhúng MediaPlayer trong dịch vụ MediaBrowserServiceCompat và có thiết bị này tương tác với một MediaBrowserCompat trong một hoạt động khác.

Bạn nên cẩn thận khi thiết lập máy khách/máy chủ này. Có kỳ vọng về cách người chơi chạy trong dịch vụ nền tương tác với phần còn lại của hệ thống. Nếu ứng dụng của bạn không đáp ứng được những kỳ vọng đó, người dùng có thể có trải nghiệm không hay. Đã đọc Xây dựng ứng dụng âm thanh để biết toàn bộ thông tin chi tiết.

Phần này mô tả các hướng dẫn đặc biệt để quản lý MediaPlayer khi triển khai MediaPlayer trong dịch vụ.

Chạy không đồng bộ

Trước hết, chẳng hạn như Activity, tất cả đều hoạt động trong một Service được thực hiện trong một luồng duy nhất bằng cách mặc định – trên thực tế, nếu bạn đang chạy một hoạt động và một dịch vụ từ cùng một ứng dụng, chúng sử dụng cùng một luồng ("luồng chính") theo mặc định. Do đó, các dịch vụ cần phải xử lý nhanh chóng các ý định đến và không bao giờ thực hiện các phép tính mất nhiều thời gian khi phản hồi các phép tính đó. Nếu có quá nhiều dự kiến sẽ có lệnh gọi công việc hoặc lệnh gọi chặn, bạn phải thực hiện các tác vụ đó một cách không đồng bộ: từ một luồng khác mà bạn tự triển khai hoặc sử dụng nhiều trang thiết bị của khung để xử lý không đồng bộ.

Ví dụ: khi sử dụng MediaPlayer từ luồng chính, bạn nên gọi prepareAsync() thay vì prepare() và triển khai MediaPlayer.OnPreparedListener để nhận thông báo khi quá trình chuẩn bị hoàn tất và bạn có thể bắt đầu chơi. Ví dụ:

Kotlin

private const val ACTION_PLAY: String = "com.example.action.PLAY"

class MyService: Service(), MediaPlayer.OnPreparedListener {

    private var mMediaPlayer: MediaPlayer? = null

    override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
        ...
        val action: String = intent.action
        when(action) {
            ACTION_PLAY -> {
                mMediaPlayer = ... // initialize it here
                mMediaPlayer?.apply {
                    setOnPreparedListener(this@MyService)
                    prepareAsync() // prepare async to not block main thread
                }

            }
        }
        ...
    }

    /** Called when MediaPlayer is ready */
    override fun onPrepared(mediaPlayer: MediaPlayer) {
        mediaPlayer.start()
    }
}

Java

public class MyService extends Service implements MediaPlayer.OnPreparedListener {
    private static final String ACTION_PLAY = "com.example.action.PLAY";
    MediaPlayer mediaPlayer = null;

    public int onStartCommand(Intent intent, int flags, int startId) {
        ...
        if (intent.getAction().equals(ACTION_PLAY)) {
            mediaPlayer = ... // initialize it here
            mediaPlayer.setOnPreparedListener(this);
            mediaPlayer.prepareAsync(); // prepare async to not block main thread
        }
    }

    /** Called when MediaPlayer is ready */
    public void onPrepared(MediaPlayer player) {
        player.start();
    }
}

Xử lý lỗi không đồng bộ

Trong các thao tác đồng bộ, lỗi thường được báo hiệu bằng ngoại lệ hoặc mã lỗi, nhưng bất cứ khi nào bạn sử dụng chế độ không đồng bộ hãy đảm bảo rằng đơn đăng ký của bạn được thông báo lỗi một cách thích hợp. Trong trường hợp MediaPlayer, bạn có thể thực hiện điều này bằng cách triển khai MediaPlayer.OnErrorListener và đặt thuộc tính này trong thực thể MediaPlayer:

Kotlin

class MyService : Service(), MediaPlayer.OnErrorListener {

    private var mediaPlayer: MediaPlayer? = null

    fun initMediaPlayer() {
        // ...initialize the MediaPlayer here...
        mediaPlayer?.setOnErrorListener(this)
    }

    override fun onError(mp: MediaPlayer, what: Int, extra: Int): Boolean {
        // ... react appropriately ...
        // The MediaPlayer has moved to the Error state, must be reset!
    }
}

Java

public class MyService extends Service implements MediaPlayer.OnErrorListener {
    MediaPlayer mediaPlayer;

    public void initMediaPlayer() {
        // ...initialize the MediaPlayer here...
        mediaPlayer.setOnErrorListener(this);
    }

    @Override
    public boolean onError(MediaPlayer mp, int what, int extra) {
        // ... react appropriately ...
        // The MediaPlayer has moved to the Error state, must be reset!
    }
}

Điều quan trọng cần nhớ là khi xảy ra lỗi, MediaPlayer chuyển sang trạng thái Lỗi (xem tài liệu về lỗi MediaPlayer cho biểu đồ trạng thái đầy đủ) và bạn phải đặt lại mật khẩu thì mới có thể sử dụng lại.

Sử dụng tính năng khóa chế độ thức

Khi thiết kế ứng dụng có phát nội dung nghe nhìn trong nền, thiết bị có thể chuyển sang chế độ ngủ trong khi dịch vụ đang chạy. Do hệ thống Android cố gắng bảo toàn pin trong khi thiết bị đang ngủ, hệ thống sẽ cố gắng tắt mọi những tính năng có trên điện thoại không cần thiết, bao gồm CPU và phần cứng Wi-Fi. Tuy nhiên, nếu dịch vụ của bạn đang phát hoặc phát nhạc trực tuyến, bạn nên ngăn hệ thống không ảnh hưởng đến quá trình phát của bạn.

Để đảm bảo rằng dịch vụ của bạn tiếp tục chạy trong trong những điều kiện đó, bạn phải sử dụng "khoá chế độ thức". Khoá chế độ thức là một cách để phát tín hiệu cho hệ thống mà ứng dụng của bạn đang sử dụng một tính năng nào đó luôn có mặt ngay cả khi điện thoại ở trạng thái rảnh.

Lưu ý: Bạn nên luôn thận trọng sử dụng khóa chế độ thức và giữ chúng khi thực sự cần thiết, vì chúng làm giảm đáng kể thời lượng pin của thiết bị.

Để đảm bảo CPU tiếp tục chạy trong khi MediaPlayer đang phát, hãy gọi phương thức setWakeMode() khi khởi tạo MediaPlayer. Sau khi thực hiện, MediaPlayer giữ khoá đã chỉ định trong khi phát và thả khoá khi bị tạm dừng hoặc dừng:

Kotlin

mediaPlayer = MediaPlayer().apply {
    // ... other initialization here ...
    setWakeMode(applicationContext, PowerManager.PARTIAL_WAKE_LOCK)
}

Java

mediaPlayer = new MediaPlayer();
// ... other initialization here ...
mediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);

Tuy nhiên, khoá chế độ thức có được trong ví dụ này chỉ đảm bảo rằng CPU vẫn hoạt động. Nếu bạn đang phát trực tuyến nội dung nghe nhìn qua và bạn đang sử dụng Wi-Fi, bạn có thể muốn giữ một WifiLock dưới dạng mà bạn phải có được và phát hành theo cách thủ công. Vì vậy, khi bắt đầu chuẩn bị MediaPlayer với URL từ xa, bạn nên tạo và mua khoá Wi-Fi. Ví dụ:

Kotlin

val wifiManager = getSystemService(Context.WIFI_SERVICE) as WifiManager
val wifiLock: WifiManager.WifiLock =
    wifiManager.createWifiLock(WifiManager.WIFI_MODE_FULL, "mylock")

wifiLock.acquire()

Java

WifiLock wifiLock = ((WifiManager) getSystemService(Context.WIFI_SERVICE))
    .createWifiLock(WifiManager.WIFI_MODE_FULL, "mylock");

wifiLock.acquire();

Khi bạn tạm dừng/dừng nội dung nghe nhìn hoặc khi không còn cần mạng, bạn nên mở khoá:

Kotlin

wifiLock.release()

Java

wifiLock.release();

Đang dọn dẹp

Như đã đề cập trước đó, đối tượng MediaPlayer có thể sử dụng đáng kể lượng tài nguyên hệ thống, nên bạn chỉ nên giữ lại tài nguyên đó khi cần và gọi release() khi bạn hoàn tất. Quan trọng là để gọi phương thức dọn dẹp này một cách rõ ràng thay vì dựa vào tính năng thu thập rác của hệ thống vì có thể mất chút thời gian trước khi trình thu gom rác lấy lại MediaPlayer, vì bộ nhớ chỉ nhạy cảm với nhu cầu về bộ nhớ và không thiếu các tài nguyên khác liên quan đến nội dung truyền thông. Vì vậy, trong trường hợp đang sử dụng một dịch vụ, bạn phải luôn ghi đè onDestroy() để đảm bảo bạn đang phát hành MediaPlayer:

Kotlin

class MyService : Service() {

    private var mediaPlayer: MediaPlayer? = null
    // ...

    override fun onDestroy() {
        super.onDestroy()
        mediaPlayer?.release()
    }
}

Java

public class MyService extends Service {
   MediaPlayer mediaPlayer;
   // ...

   @Override
   public void onDestroy() {
       super.onDestroy();
       if (mediaPlayer != null) mediaPlayer.release();
   }
}

Bạn phải luôn tìm các cơ hội khác để phát hành MediaPlayer của mình ngoài việc phát hành ứng dụng khi bị tắt. Ví dụ: nếu bạn muốn không có thể phát nội dung nghe nhìn trong một khoảng thời gian dài (ví dụ: sau khi mất quyền phát âm thanh), thì chắc chắn bạn nên phát hành MediaPlayer hiện có rồi tạo lại sau. Trên mặt khác, nếu bạn chỉ muốn dừng phát lại trong một khoảng thời gian rất ngắn, có lẽ bạn nên giữ lại MediaPlayer để tránh chi phí tạo và chuẩn bị một lần nữa.

Quản lý quyền kỹ thuật số (DRM)

Kể từ Android 8.0 (API cấp 26), MediaPlayer bao gồm các API hỗ trợ phát nội dung được bảo vệ bằng DRM. Chúng tương tự như API cấp thấp do MediaDrm, nhưng chúng hoạt động ở cấp cao hơn và không hiển thị trình trích xuất, drm và đối tượng mật mã cơ bản.

Mặc dù MediaPlayer DRM API không cung cấp đầy đủ chức năng của MediaDrm, hỗ trợ các trường hợp sử dụng phổ biến nhất. Chiến lược phát hành đĩa đơn phương thức triển khai hiện tại có thể xử lý các loại nội dung sau:

  • Tệp nội dung nghe nhìn cục bộ được bảo vệ bằng Widevine
  • Tệp nội dung nghe nhìn từ xa/phát trực tuyến được bảo vệ bằng Widevine

Đoạn mã sau đây minh hoạ cách dùng DRM MediaPlayer mới trong quá trình triển khai đồng bộ đơn giản.

Để quản lý nội dung nghe nhìn do DRM kiểm soát, bạn cần đưa các phương thức mới vào cùng với quy trình thông thường của lệnh gọi MediaPlayer, như minh hoạ dưới đây:

Kotlin

mediaPlayer?.apply {
    setDataSource()
    setOnDrmConfigHelper() // optional, for custom configuration
    prepare()
    drmInfo?.also {
        prepareDrm()
        getKeyRequest()
        provideKeyResponse()
    }

    // MediaPlayer is now ready to use
    start()
    // ...play/pause/resume...
    stop()
    releaseDrm()
}

Java

setDataSource();
setOnDrmConfigHelper(); // optional, for custom configuration
prepare();
if (getDrmInfo() != null) {
  prepareDrm();
  getKeyRequest();
  provideKeyResponse();
}

// MediaPlayer is now ready to use
start();
// ...play/pause/resume...
stop();
releaseDrm();

Bắt đầu bằng cách khởi tạo đối tượng và chế độ cài đặt MediaPlayer nguồn của nó bằng setDataSource(), như thường lệ. Sau đó, để sử dụng DRM, hãy thực hiện các bước sau:

  1. Nếu bạn muốn ứng dụng của mình thực hiện cấu hình tuỳ chỉnh, hãy xác định một Giao diện OnDrmConfigHelper và đính kèm nó vào người chơi đang sử dụng setOnDrmConfigHelper()
  2. Gọi prepare().
  3. Gọi getDrmInfo(). Nếu nguồn có DRM thì phương thức sẽ trả về một giá trị khác rỗng Giá trị MediaPlayer.DrmInfo.

Nếu MediaPlayer.DrmInfo tồn tại:

  1. Kiểm tra sơ đồ các mã nhận dạng duy nhất (UUID) hiện có rồi chọn một mã nhận dạng duy nhất (UUID).
  2. Chuẩn bị cấu hình DRM cho nguồn hiện tại bằng cách gọi prepareDrm().
    • Nếu bạn đã tạo và đăng ký Lệnh gọi lại OnDrmConfigHelper, phương thức này được gọi trong khi prepareDrm() đang thực thi. Điều này cho phép bạn thực hiện việc định cấu hình tuỳ chỉnh DRM trước khi mở phiên DRM. Lệnh gọi lại được gọi một cách đồng bộ trong luồng đã gọi prepareDrm(). Người nhận truy cập các thuộc tính DRM, hãy gọi getDrmPropertyString()setDrmPropertyString() Tránh thực hiện các thao tác dài.
    • Nếu thiết bị chưa được cấp phép, Ngoài ra, prepareDrm() truy cập vào máy chủ cấp phép để cấp phép thiết bị. Quá trình này có thể mất trong khoảng thời gian thay đổi, tuỳ thuộc vào kết nối mạng.
  3. Để lấy một mảng byte yêu cầu khoá mờ gửi đến máy chủ cấp phép, hãy gọi getKeyRequest().
  4. Để thông báo cho công cụ DRM về phản hồi khoá đã nhận được từ máy chủ cấp phép, hãy gọi provideKeyResponse(). Kết quả phụ thuộc vào loại yêu cầu khoá:
    • Nếu phản hồi là dành cho một yêu cầu khoá ngoại tuyến, thì kết quả sẽ là giá trị nhận dạng bộ khoá. Bạn có thể sử dụng giá trị nhận dạng tập hợp khoá này bằng restoreKeys() để khôi phục các khoá thành một phiên hoạt động.
    • Nếu phản hồi là dành cho một yêu cầu truyền trực tuyến hoặc phát hành, thì kết quả sẽ rỗng.

Chạy prepareDrm() không đồng bộ

Theo mặc định, prepareDrm() chạy đồng bộ, chặn cho đến khi quá trình chuẩn bị hoàn tất. Tuy nhiên, chính là việc chuẩn bị DRM đầu tiên trên một thiết bị mới cũng có thể cần được cấp phép, tức là xử lý nội bộ bởi prepareDrm() và có thể mất chút thời gian để hoàn tất do có liên quan đến hoạt động mạng. Bạn có thể tránh chặn trên prepareDrm() bởi xác định và cài đặt MediaPlayer.OnDrmPreparedListener.

Khi đặt một OnDrmPreparedListener, prepareDrm() thực hiện việc cung cấp (nếu cần) và chuẩn bị trong nền. Thời gian quá trình cấp phép và chuẩn bị hoàn tất, trình nghe sẽ được gọi. Bạn nên không đưa ra bất kỳ giả định nào về trình tự gọi hoặc luồng mà trong đó trình nghe sẽ chạy (trừ phi trình nghe được đăng ký với một luồng của trình xử lý). Trình nghe có thể được gọi trước hoặc sau prepareDrm() lợi nhuận.

Thiết lập DRM không đồng bộ

Bạn có thể khởi chạy DRM một cách không đồng bộ bằng cách tạo và đăng ký MediaPlayer.OnDrmInfoListener để chuẩn bị DRM và MediaPlayer.OnDrmPreparedListener để bắt đầu trình phát. Các giải pháp này hoạt động cùng với prepareAsync(), như minh hoạ dưới đây:

Kotlin

setOnPreparedListener()
setOnDrmInfoListener()
setDataSource()
prepareAsync()
// ...

// If the data source content is protected you receive a call to the onDrmInfo() callback.
override fun onDrmInfo(mediaPlayer: MediaPlayer, drmInfo: MediaPlayer.DrmInfo) {
    mediaPlayer.apply {
        prepareDrm()
        getKeyRequest()
        provideKeyResponse()
    }
}

// When prepareAsync() finishes, you receive a call to the onPrepared() callback.
// If there is a DRM, onDrmInfo() sets it up before executing this callback,
// so you can start the player.
override fun onPrepared(mediaPlayer: MediaPlayer) {
    mediaPlayer.start()
}

Java

setOnPreparedListener();
setOnDrmInfoListener();
setDataSource();
prepareAsync();
// ...

// If the data source content is protected you receive a call to the onDrmInfo() callback.
onDrmInfo() {
  prepareDrm();
  getKeyRequest();
  provideKeyResponse();
}

// When prepareAsync() finishes, you receive a call to the onPrepared() callback.
// If there is a DRM, onDrmInfo() sets it up before executing this callback,
// so you can start the player.
onPrepared() {

start();
}

Xử lý nội dung nghe nhìn đã mã hoá

Kể từ Android 8.0 (API cấp 26), MediaPlayer cũng có thể giải mã Lược đồ mã hoá chung (CENC) và Nội dung nghe nhìn đã mã hoá cấp mẫu HLS (METHOD=SAMPLE-AES) cho các loại luồng cơ bản H.264 và AAC. Nội dung nghe nhìn được mã hoá đầy đủ (METHOD=AES-128) trước đây đã được hỗ trợ.

Truy xuất nội dung nghe nhìn từ ContentResolver

Một tính năng khác có thể hữu ích trong ứng dụng trình phát đa phương tiện là khả năng truy xuất nhạc mà người dùng có trên thiết bị. Bạn có thể thực hiện việc này bằng cách truy vấn ContentResolver dành cho phương tiện bên ngoài:

Kotlin

val resolver: ContentResolver = contentResolver
val uri = android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
val cursor: Cursor? = resolver.query(uri, null, null, null, null)
when {
    cursor == null -> {
        // query failed, handle error.
    }
    !cursor.moveToFirst() -> {
        // no media on the device
    }
    else -> {
        val titleColumn: Int = cursor.getColumnIndex(android.provider.MediaStore.Audio.Media.TITLE)
        val idColumn: Int = cursor.getColumnIndex(android.provider.MediaStore.Audio.Media._ID)
        do {
            val thisId = cursor.getLong(idColumn)
            val thisTitle = cursor.getString(titleColumn)
            // ...process entry...
        } while (cursor.moveToNext())
    }
}
cursor?.close()

Java

ContentResolver contentResolver = getContentResolver();
Uri uri = android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
Cursor cursor = contentResolver.query(uri, null, null, null, null);
if (cursor == null) {
    // query failed, handle error.
} else if (!cursor.moveToFirst()) {
    // no media on the device
} else {
    int titleColumn = cursor.getColumnIndex(android.provider.MediaStore.Audio.Media.TITLE);
    int idColumn = cursor.getColumnIndex(android.provider.MediaStore.Audio.Media._ID);
    do {
       long thisId = cursor.getLong(idColumn);
       String thisTitle = cursor.getString(titleColumn);
       // ...process entry...
    } while (cursor.moveToNext());
}

Để sử dụng phương thức này với MediaPlayer, bạn có thể làm như sau:

Kotlin

val id: Long = /* retrieve it from somewhere */
val contentUri: Uri =
    ContentUris.withAppendedId(android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, id )

mediaPlayer = MediaPlayer().apply {
    setAudioAttributes(
        AudioAttributes.Builder()
            .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
            .setUsage(AudioAttributes.USAGE_MEDIA)
            .build()
    )
    setDataSource(applicationContext, contentUri)
}

// ...prepare and start...

Java

long id = /* retrieve it from somewhere */;
Uri contentUri = ContentUris.withAppendedId(
        android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, id);

mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioAttributes(
    new AudioAttributes.Builder()
        .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
        .setUsage(AudioAttributes.USAGE_MEDIA)
        .build()
);
mediaPlayer.setDataSource(getApplicationContext(), contentUri);

// ...prepare and start...

Tìm hiểu thêm

Các trang này bao gồm các chủ đề liên quan đến việc ghi âm, lưu trữ và phát âm thanh cũng như video.