מסגרת המולטימדיה של Android כוללת תמיכה בהפעלת מגוון של סוגי מדיה נפוצים, כדי
שאפשר לשלב בקלות אודיו, וידאו ותמונות באפליקציות שלכם. אפשר להשמיע אודיו או
וידאו מקובצי מדיה המאוחסנים במשאבי האפליקציה (משאבים גולמיים), מקבצים עצמאיים
במערכת הקבצים או ממקור נתונים שמגיע דרך חיבור לרשת, והכול באמצעות ממשקי API של MediaPlayer
.
במסמך הזה מוסבר איך משתמשים
MediaPlayer
כדי לכתוב הפעלה של מדיה
שמקיים אינטראקציה עם המשתמש ועם המערכת כדי להשיג ביצועים טובים
חוויית משתמש נעימה. לחלופין, אולי כדאי
כדי להשתמש ב-ExoPlayer, שהוא קוד פתוח שניתן להתאמה אישית.
ספרייה שתומכת בתכונות בעלות ביצועים גבוהים שלא זמינות בMediaPlayer
הערה: אפשר להשמיע את נתוני האודיו רק בפלט הרגיל. במכשיר. בשלב הזה, זה הרמקול של המכשיר הנייד או אוזניות Bluetooth. לא ניתן להשמיע צליל קבצים באודיו של השיחה במהלך שיחה.
העקרונות הבסיסיים
המחלקות הבאות משמשות להשמעת סרטונים וצלילים ב-framework של Android:
MediaPlayer
- הכיתה הזו היא ה-API הראשי להפעלת אודיו וסרטונים.
AudioManager
- הכיתה הזו מנהלת את מקורות האודיו ואת פלט האודיו במכשיר.
הצהרות מניפסט
לפני שמתחילים בפיתוח של האפליקציה באמצעות MediaPlayer, צריך לוודא שהמניפסט כולל את ההצהרות המתאימות כדי לאפשר שימוש בתכונות קשורות.
- הרשאת אינטרנט – אם אתה משתמש ב-MediaPlayer כדי לשדר פגישות מבוססות-רשת
התוכן, האפליקציה שלך חייבת לבקש גישה לרשת.
<uses-permission android:name="android.permission.INTERNET" />
- הרשאה לנעילת פעילות – אם אפליקציית הנגן שלך צריכה להשאיר את המסך
מעמעום או מצב המעבד ממצב שינה, או משתמשת ב-
MediaPlayer.setScreenOnWhilePlaying()
אוMediaPlayer.setWakeMode()
שיטות, צריך לבקש את ההרשאה הזו.<uses-permission android:name="android.permission.WAKE_LOCK" />
שימוש ב-MediaPlayer
אחד המרכיבים החשובים ביותר במסגרת המדיה הוא
MediaPlayer
בכיתה. אובייקט במחלקה הזו יכול לאחזר, לפענח ולהפעיל גם אודיו וגם וידאו
עם הגדרה מינימלית. הוא תומך בכמה מקורות מדיה שונים, כמו:
- משאבים מקומיים
- מזהי URI פנימיים, כמו כזה שאפשר לקבל מ-Content Processr
- כתובות URL חיצוניות (סטרימינג)
לרשימת פורמטים של מדיה שנתמכים ב-Android: למידע נוסף על מדיה נתמכת פורמטים.
כאן מוצגת דוגמה
של אופן השמעת האודיו שזמין בתור משאב גולמי מקומי (שנשמר
ספריית 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
במקרה הזה, מילת המפתח "גולמי" משאב הוא קובץ שהמערכת לא לנסות לנתח בדרך מסוימת. עם זאת, התוכן של משאב זה להיות אודיו גולמי. הוא צריך להיות קובץ מדיה בקידוד ובפורמט תקין של הפורמטים הנתמכים.
כך ניתן להשמיע מ-URI שזמין באופן מקומי במערכת (שקיבלתם דרך פותר בעיות תוכן, לדוגמה):
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();
הפעלה מכתובת URL מרוחקת באמצעות סטרימינג ב-HTTP נראית כך:
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();
הערה: אם אתם מעבירים כתובת URL כדי לשדר קובץ מדיה אונליין, הקובץ חייב להיות מסוגל לבצע הורדה הדרגתית.
זהירות: יש לצלם או להעביר
IllegalArgumentException
ו-IOException
כשמשתמשים
setDataSource()
, כי
ייתכן שהקובץ שאליו אתם מפנים לא קיים.
הכנה אסינכרונית
השימוש ב-MediaPlayer
יכול להיות פשוט כאשר
את העיקרון. עם זאת, חשוב לזכור שיש עוד כמה דברים
שנדרש כדי לשלב אותה בצורה נכונה עם אפליקציה אופיינית של Android. עבור
לדוגמה, הקריאה ל-prepare()
יכולה
לוקח הרבה זמן לבצע את זה,
הוא עשוי לכלול שליפה ופענוח של נתוני מדיה. כך שכמו בכל סוג של
שעשויה להימשך זמן רב, אל תקראו לה אף פעם
בשרשור של ממשק המשתמש של האפליקציה. פעולה שגורמת לממשק המשתמש להיתקע עד שה-method חוזרת.
חוויית משתמש גרועה שעלולה לגרום לשגיאת ANR (האפליקציה לא מגיבה). גם אם
שאתם מצפים שהמשאב שלכם ייטען במהירות, זכרו שכל מה שלוקח יותר מעשירה
של שנייה אחת להגיב בממשק המשתמש, גורמת להשהיה משמעותית
למשתמש רושם שהאפליקציה איטית.
כדי להימנע מתליית השרשור בממשק המשתמש, מומלץ לפתוח שרשור נוסף
להכין את MediaPlayer
ולשלוח הודעה לשרשור הראשי בסיום. אבל בעוד
אפשר לכתוב את לוגיקת השרשורים
הדפוס הזה נפוץ כל כך כשמשתמשים ב-MediaPlayer
, עד שה-framework
מספקת דרך נוחה לביצוע משימה זו באמצעות
prepareAsync()
. השיטה הזו
מתחיל להכין את קובץ המדיה ברקע וחוזר מיד. כשהמדיה
הסתיימה ההכנה, onPrepared()
של MediaPlayer.OnPreparedListener
, שהוגדרה דרך
קוראים לפונקציה setOnPreparedListener()
.
ניהול מצבים
היבט נוסף בMediaPlayer
שכדאי לזכור הוא
שהיא מבוססת מדינה. כלומר, ל-MediaPlayer
יש מצב פנימי
שצריך להיות מודעים אליו תמיד כשאתם כותבים את הקוד, מכיוון שפעולות מסוימות
תקפים רק כשהנגן נמצא במצבים ספציפיים. אם תבצעו פעולה בזמן
במצב שגוי, המערכת עלולה לגרום לחריגה או לגרום להתנהגויות לא רצויות אחרות.
התיעוד
במחלקה MediaPlayer
מוצג תרשים מצב מלא,
שמפרטות אילו שיטות מעבירות את MediaPlayer
ממצב אחד למצב אחר.
לדוגמה, כשיוצרים MediaPlayer
חדש, הוא נמצא במצב לא פעיל
. בשלב הזה, צריך לאתחל אותו על ידי התקשרות
setDataSource()
, מגיע/ה
למצב אתחול. לאחר מכן צריך להכין אותו באמצעות
prepare()
או
prepareAsync()
. מתי
בסיום ההכנה MediaPlayer
הוא נכנס מוכנות
כלומר אפשר להתקשר אל start()
כדי להפעיל את המדיה. בנקודה הזו, כמו שהתרשים ממחיש,
אפשר לעבור בין המצבים מופעל, מושהה והפעלה הושלם באמצעות
קריאה לשיטות כמו
start()
,
pause()
, וגם
seekTo()
,
בין היתר. אחרי ש
להתקשר אל stop()
, אבל שימו לב
לא ניתן להתקשר שוב אל start()
עד
מכינים את MediaPlayer
מחדש.
חשוב לשמור תמיד את תרשים המצב
כשאתם כותבים קוד שמקיים אינטראקציה עם
אובייקט MediaPlayer
, כי קריאה לשיטות שלו ממצב שגוי
היא סיבה נפוצה לבאגים.
שחרור של MediaPlayer
MediaPlayer
יכול לצרוך נתונים חשובים
במשאבי המערכת.
לכן, תמיד צריך לנקוט אמצעי זהירות נוספים כדי לוודא שלא
נצמדים למכונה של MediaPlayer
יותר מהנדרש. אחרי ש
סיימתם איתו, תמיד צריך להתקשר
release()
כדי לוודא
משאבי המערכת שהוקצו לו ישוחררו כמו שצריך. לדוגמה, אם
באמצעות MediaPlayer
והפעילות שלך מקבלת קריאה ל-onStop()
, עליך לשחרר את MediaPlayer
,
כי זה
אין שום היגיון לשמור אותו בזמן שהפעילות שלך לא פעילה
המשתמש (אלא אם אתם מפעילים מדיה ברקע, כפי שמוסבר בקטע הבא).
כמובן שכשהפעילות חוזרת או מופעלת מחדש, צריך
צריך ליצור קובץ MediaPlayer
חדש ולהכין אותו שוב לפני שממשיכים את ההפעלה.
כך צריך לבטל את MediaPlayer
ולאחר מכן לבטל את המינוי:
Kotlin
mediaPlayer?.release() mediaPlayer = null
Java
mediaPlayer.release(); mediaPlayer = null;
לדוגמה, נבחן את הבעיות שעלולות לקרות אם
שכחת לשחרר את הMediaPlayer
כשהפעילות שלך הופסקה, אבל ליצור
חדשה כשהפעילות מתחילה שוב. כפי שאולי ידוע לך, כשהמשתמש משנה את
כיוון המסך (או משנה את תצורת המכשיר בדרך אחרת),
המערכת מטפלת בכך על ידי הפעלה מחדש של הפעילות (כברירת מחדל), כך שייתכן
לצרוך את כל משאבי המערכת בתור המשתמש
מסובב את המכשיר הלוך ושוב בין תצוגה לאורך למצב לרוחב, מכיוון שבכל אחת
שינוי כיוון, יוצרים MediaPlayer
חדש שאף פעם לא
גרסה חדשה. (מידע נוסף על הפעלות מחדש של זמן ריצה זמין במאמר טיפול בשינויים בזמן ריצה).
אולי תהית מה קורה אם רוצים להמשיך לשחק
'מדיה ברקע' גם כשהמשתמש עוזב את הפעילות,
אופן הפעולה של אפליקציית Music המובנית. במקרה כזה, מה שנחוץ לך הוא
MediaPlayer
בשליטת שירות, כפי
בקטע הבא מוסבר איך עושים את זה.
שימוש ב-MediaPlayer בשירות
אם רוצים שהמדיה תופעל ברקע גם כשהאפליקציה
לא מופיע במסך. כלומר, אתם רוצים שהוא ימשיך לפעול בזמן שהמשתמש
אינטראקציה עם אפליקציות אחרות - עליכם להתחיל
שירות ושליטה
MediaPlayer
משם.
צריך להטמיע את
MediaPlayer בשירות MediaBrowserServiceCompat
ויש להם
יוצר אינטראקציה עם
MediaBrowserCompat
בפעילות אחרת.
עליך להפעיל שיקול דעת בעת הגדרת לקוח/שרת. יש ציפיות על האופן שבו שחקן שרץ בשירות ברקע יוצר אינטראקציה עם שאר המערכת. אם האפליקציה לא עומדת בציפיות האלה, המשתמש עשוי יהיה לך חוויה לא טובה. נקראו יצירה של אפליקציית אודיו כדי לקבל את הפרטים המלאים.
בקטע הזה מתוארות הוראות מיוחדות לניהול MediaPlayer כאשר הוא מוטמע בשירות.
הרצה באופן אסינכרוני
קודם כל, כמו Activity
, כל העבודה
הפעולה Service
מתבצעת בשרשור יחיד על ידי
כברירת מחדל - למעשה, אם מריצים פעילות וגם שירות מאותה אפליקציה,
להשתמש באותו שרשור ('ה-thread הראשי') כברירת מחדל. לכן, השירותים צריכים
לעבד Intents נכנסים במהירות
ואף פעם לא תבצעו חישובים ממושכים כשאתם מגיבים אליהם. אם כבדים
צפויות שיחות או שיחות חסומות, עליך לבצע את המשימות האלה באופן אסינכרוני:
שרשור אחר שאתם מטמיעים בעצמכם, או משתמשים במתקנים הרבים של המסגרת
לעיבוד אסינכרוני.
לדוגמה, כשמשתמשים ב-MediaPlayer
מה-thread הראשי,
עליך לקרוא ל-prepareAsync()
במקום
prepare()
, ומטמיעים
MediaPlayer.OnPreparedListener
כדי לקבל התראה כשההכנה הושלמה ואפשר להתחיל לשחק.
לדוגמה:
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(); } }
טיפול בשגיאות אסינכרוניות
בפעולות סינכרוניות, בדרך כלל השגיאות
עם חריג או קוד שגיאה, אבל בכל פעם שמשתמשים אסינכרוניים
עליך לוודא שהאפליקציה שלך מקבלת הודעה
כמה שגיאות. במקרה של MediaPlayer
,
אפשר לעשות זאת באמצעות הטמעת
MediaPlayer.OnErrorListener
והקבוצה
מגדירים אותו במכונה 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! } }
חשוב לזכור שכשמתרחשת שגיאה, MediaPlayer
למצב שגיאה (אפשר לעיין במסמכי התיעוד של
מחלקה MediaPlayer
לתרשים המצב המלא)
וצריך לאפס אותו לפני שאפשר יהיה להשתמש בו שוב.
שימוש בחסימות מצב שינה
במהלך עיצוב אפליקציות שמפעילות מדיה ברקע, ייתכן שהמכשיר יעבור למצב שינה בזמן שהשירות פועל. כי מערכת Android מנסה לשמר הסוללה בזמן שהמכשיר במצב שינה, המערכת מנסה לכבות כל של תכונות הטלפון שאין צורך, כולל המעבד וחומרת ה-Wi-Fi. עם זאת, אם השירות פועל או משדר מוזיקה, מומלץ למנוע למערכת ולא להפריע להפעלה.
כדי להבטיח שהשירות שלך ימשיך לפעול את המצבים האלה צריך להשתמש ב'חסימות מצב שינה'. התכונה 'נעילת מצב שינה' מאפשרת לך לאותת את המערכת שהאפליקציה שלך משתמשת בה בתכונה כלשהי נשארים זמינים גם כשהטלפון לא פעיל.
הערה: תמיד צריך להשתמש בחסימות מצב שינה באופן מוגבל ולהחזיק אותן רק למשך הזמן הנדרש באמת, מכיוון שהם מצמצמים באופן משמעותי את חיי הסוללה של במכשיר.
כדי להבטיח שהמעבד ימשיך לפעול בזמן שהMediaPlayer
מופעל, קוראים לשיטה setWakeMode()
בעת אתחול MediaPlayer
. אחרי שתעשו את זה,
לחיצה על MediaPlayer
מחזיקה את המנעול שצוין בזמן ההפעלה ומשחררת את המנעול
כשמשהים או עוצרים:
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);
עם זאת, נעילת מצב השינה שהתקבלה בדוגמה הזו מבטיחה רק שהמעבד (CPU) יישאר פעיל. אם המיקום
אתם משדרים מדיה דרך
ומשתמשים ב-Wi-Fi, כנראה שתרצו
WifiLock
בתור
וצריך להשיג ולפרסם באופן ידני. לכן, כשתתחילו להכין את
MediaPlayer
עם כתובת ה-URL המרוחקת, יש ליצור את נעילת ה-Wi-Fi ולקבל אותה.
לדוגמה:
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();
כשמשהים או מפסיקים את המדיה, או כשכבר אין צורך עליך לשחרר את הנעילה:
Kotlin
wifiLock.release()
Java
wifiLock.release();
ביצוע ניקוי
כפי שצוין קודם, אובייקט MediaPlayer
יכול לצרוך כמות גדולה של
כמות גדולה של משאבי מערכת, כך שמומלץ לשמור אותם רק לזמן שבו אתם צריכים
release()
בסיום התהליך. חשוב
לקרוא לשיטת הניקוי הזו באופן מפורש במקום להסתמך על איסוף אשפה של המערכת,
יכול להיות שיעבור זמן מה עד שאספנות האשפה יבקשו בחזרה את MediaPlayer
,
מכיוון שהוא רגיש רק לצורכי הזיכרון ולא למחסור במשאבים אחרים הקשורים למדיה.
לכן, במקרה שאתם משתמשים בשירות, עליכם תמיד לשנות את
אמצעי תשלום אחד (onDestroy()
) כדי לוודא שמפיצים
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(); } }
תמיד כדאי לחפש הזדמנויות אחרות להסרת MediaPlayer
גם, מלבד שחרורו לאחר כיבוי. לדוגמה, אם ציפיתם שלא
כדי שתוכלו להפעיל מדיה לפרק זמן ארוך (למשל, לאחר אובדן המיקוד באודיו),
עליך לשחרר את ה-MediaPlayer
הקיים וליצור אותו שוב
מאוחר יותר. ב
מצד שני, אם צפויה להפסיק את ההפעלה לזמן קצר מאוד, סביר להניח
כדאי לשמור על MediaPlayer
כדי להימנע מהתקורה שצריך ליצור ולהכין אותו
שוב.
ניהול זכויות דיגיטלי (DRM)
החל מ-Android 8.0 (רמת API 26), MediaPlayer
כולל ממשקי API
לתמוך בהפעלה של תוכן המוגן באמצעות DRM. הם דומים ל-API ברמה הנמוכה שמסופק על ידי
MediaDrm
, אבל הם פועלים ברמה גבוהה יותר ולא
לחשוף את אובייקטי החילוץ, ה-Drm והקריפטו.
למרות שממשק ה-API של MediaPlayer DRM לא מספק את הפונקציונליות המלאה של
MediaDrm
, הוא תומך בתרחישים לדוגמה הנפוצים ביותר.
ההטמעה הקיימת יכולה לטפל בסוגי התוכן הבאים:
- קובצי מדיה מקומית המוגנים ב-Widevine
- קובצי מדיה בסטרימינג או שלט רחוק שמוגנים ב-Widevine
קטע הקוד הבא מדגים איך להשתמש ב-DRM MediaPlayer החדש בהטמעה סינכרונית פשוטה.
כדי לנהל מדיה בשליטת DRM, עליך לכלול את השיטות החדשות לצד את הזרימה הרגילה של קריאות MediaPlayer, כפי שמוצג בהמשך:
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();
בשלב הראשון, צריך לאתחל את האובייקט MediaPlayer
ואת ההגדרה
המקור שלו באמצעות setDataSource()
,
כרגיל. לאחר מכן, כדי להשתמש ב-DRM, מבצעים את השלבים הבאים:
- אם רוצים שהאפליקציה תבצע הגדרות אישיות, צריך להגדיר
OnDrmConfigHelper
, ומצרפים אותו אל שחקן משתמשsetOnDrmConfigHelper()
. - התקשרות אל
prepare()
. - התקשרות אל
getDrmInfo()
. אם למקור יש DRM content, השיטה מחזירה ערך שאינו null ערך שלMediaPlayer.DrmInfo
.
אם MediaPlayer.DrmInfo
קיים:
- בודקים את המפה של מזהי UUID הזמינים ובוחרים אחד.
- צריך להכין את הגדרת ה-DRM למקור הנוכחי באמצעות קריאה ל-
prepareDrm()
. - אם יצרתם ורשמתם חשבון
קריאה חוזרת של
OnDrmConfigHelper
, היא נקראת בזמןprepareDrm()
מבצע הרצה. כך ניתן לבצע הגדרה מותאמת אישית של DRM מאפיינים לפני פתיחת הפעלת DRM. הקריאה החוזרת היא באופן סינכרוני בשרשורprepareDrm()
שפת תרגום לגשת לנכסי DRM, לקרואgetDrmPropertyString()
והקבוצהsetDrmPropertyString()
. יש להימנע מביצוע פעולות ממושכות. - אם עדיין לא הוקצה מכשיר,
prepareDrm()
גם ניגש לשרת הקצאת ההרשאות לניהול הקצאת המכשיר. הפעולה הזו עשויה להימשך זמן משתנה, בהתאם לקישוריות הרשת. - כדי לקבל מערך בייטים אטומים של בקשת מפתח לשליחה לשרת רישיונות,
getKeyRequest()
- כדי להודיע למנוע ה-DRM לגבי תגובת המפתח שהתקבלה משרת הרישיונות, יש להפעיל את
provideKeyResponse()
התוצאה תלויה בסוג הבקשה למפתח:- אם התשובה היא לבקשת מפתח אופליין, התוצאה תהיה מזהה של קבוצת מפתחות. אפשר להשתמש
המזהה הזה של קבוצת המפתחות עם
restoreKeys()
כדי לשחזר את המפתחות סשן. - אם התגובה היא לבקשה של סטרימינג או פרסום, התוצאה תהיה null.
- אם התשובה היא לבקשת מפתח אופליין, התוצאה תהיה מזהה של קבוצת מפתחות. אפשר להשתמש
המזהה הזה של קבוצת המפתחות עם
הפעלה של prepareDrm()
באופן אסינכרוני
כברירת מחדל, prepareDrm()
פועל באופן סינכרוני, וחוסם עד לסיום ההכנה. עם זאת,
הכנה ראשונה של DRM במכשיר חדש עשויה גם להצריך הקצאה,
מטופל באופן פנימי על ידי
prepareDrm()
, וגם
ייתכן שיחלוף זמן מה עד שהפעולה תסתיים, כי מדובר בפעולת הרשת. אפשר
להימנע מחסימה
prepareDrm()
על ידי
הגדרה והגדרה של MediaPlayer.OnDrmPreparedListener
.
כשמגדירים OnDrmPreparedListener
,
prepareDrm()
מבצע את ההקצאה (במקרה הצורך) ואת ההכנה ברקע. מתי
ההקצאה וההכנה הסתיימו, מתבצעת קריאה ל-listener. אתם צריכים
לא מניחים שום דבר לגבי רצף הקריאה או השרשור שבו
ה-listener פועל (אלא אם ה-listener רשום ב-thread של handler).
אפשר להתקשר למאזינים לפני או אחרי
prepareDrm()
החזרות.
הגדרה של DRM באופן אסינכרוני
תוכלו לאתחל את ה-DRM באופן אסינכרוני על ידי יצירה ורישום של
MediaPlayer.OnDrmInfoListener
להכנת DRM
MediaPlayer.OnDrmPreparedListener
כדי להפעיל את הנגן.
הם פועלים בשילוב עם
prepareAsync()
, כפי שמוצג בהמשך:
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(); }
טיפול במדיה מוצפנת
החל מגרסה 8.0 של Android (רמת API 26), MediaPlayer
יכול גם לפענח
Common Encryption schema (CENC) וכן
מדיה מוצפנת ברמת HLS (METHOD=Example-AES) לסוגי השידורים היסודיים
H.264 ו-AAC. בעבר הייתה תמיכה במדיה מוצפנת בקטע מלא (METHOD=AES-128).
אחזור מדיה מ-Contentresolver
תכונה נוספת שעשויה להיות שימושית ביישום של נגן מדיה היא היכולת
מאחזרים את המוזיקה של המשתמש במכשיר. אפשר לעשות זאת באמצעות שליחת שאילתה אל ContentResolver
לגבי מדיה חיצונית:
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()); }
כדי להשתמש בזה עם MediaPlayer
, אפשר:
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...
מידע נוסף
הדפים האלה כוללים נושאים שקשורים להקלטה, לאחסון ולהפעלה של אודיו ווידאו.