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ặcMediaPlayer.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
IllegalArgumentException
và IOException
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ừng và Phá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()
và
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:
- 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ụngsetOnDrmConfigHelper()
- Gọi
prepare()
. - 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:
- 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).
- 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 khiprepareDrm()
đ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ọiprepareDrm()
. Người nhận truy cập các thuộc tính DRM, hãy gọigetDrmPropertyString()
và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. - Để 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()
. - Để 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.
- 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
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.