סקירה כללית של MediaPlayer

מסגרת המולטימדיה של 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, מבצעים את השלבים הבאים:

  1. אם רוצים שהאפליקציה תבצע הגדרות אישיות, צריך להגדיר OnDrmConfigHelper, ומצרפים אותו אל שחקן משתמש setOnDrmConfigHelper().
  2. התקשרות אל prepare().
  3. התקשרות אל getDrmInfo(). אם למקור יש DRM content, השיטה מחזירה ערך שאינו null ערך של MediaPlayer.DrmInfo.

אם MediaPlayer.DrmInfo קיים:

  1. בודקים את המפה של מזהי UUID הזמינים ובוחרים אחד.
  2. צריך להכין את הגדרת ה-DRM למקור הנוכחי באמצעות קריאה ל-prepareDrm().
    • אם יצרתם ורשמתם חשבון קריאה חוזרת של OnDrmConfigHelper, היא נקראת בזמן prepareDrm() מבצע הרצה. כך ניתן לבצע הגדרה מותאמת אישית של DRM מאפיינים לפני פתיחת הפעלת DRM. הקריאה החוזרת היא באופן סינכרוני בשרשור prepareDrm() שפת תרגום לגשת לנכסי DRM, לקרוא getDrmPropertyString() והקבוצה setDrmPropertyString(). יש להימנע מביצוע פעולות ממושכות.
    • אם עדיין לא הוקצה מכשיר, prepareDrm() גם ניגש לשרת הקצאת ההרשאות לניהול הקצאת המכשיר. הפעולה הזו עשויה להימשך זמן משתנה, בהתאם לקישוריות הרשת.
  3. כדי לקבל מערך בייטים אטומים של בקשת מפתח לשליחה לשרת רישיונות, getKeyRequest()
  4. כדי להודיע למנוע ה-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...

מידע נוסף

הדפים האלה כוללים נושאים שקשורים להקלטה, לאחסון ולהפעלה של אודיו ווידאו.