שליטה במדיה באמצעות MediaSession

1. מבוא

תאריך עדכון אחרון: 9 בספטמבר 2020

מהם היתרונות של הוספת MediaSession להפעלת סרטונים?

סשנים של מדיה הם קישור בלתי נפרד בין פלטפורמת Android לבין אפליקציות המדיה. הוא לא רק מודיע ל-Android שהמדיה מופעלת – כדי שתוכל להעביר פעולות מדיה לסשן הנכון – אלא גם מודיע לפלטפורמה מה מופעלת ואיך אפשר לשלוט בה.

לחשיפת MediaSession דרך האפליקציה יש יתרונות שונים שהמשתמשים ייהנו מהם. ריכזנו כאן כמה דוגמאות נהדרות.

Google Assistant

המשתמשים יכולים לבצע פעולות בקלות במדיה באפליקציה באמצעות פקודות קוליות כמו 'השהיה', 'המשך' ו'הבא'. אפשר גם להשתמש במטא-נתונים מהמדיה כדי לקבל תשובות לגבי מה שמופעל כרגע.

Android TV

כשמשתמשים באפליקציה בטלוויזיה, משתמשים עם טלוויזיות שתומכות ב-HDMI-CEC יכולים להשתמש בשלטים רחוקים רגילים. הפקודות שמתקבלות מהלחצנים 'הפעלה/השהיה', 'עצירה', 'הבא' ו'קודם' מועברות לאפליקציה.

פקדי מדיה במסך

החל מגרסה 4.0 של Android‏ (רמת API 14), המערכת יכולה לגשת למטא-נתונים ולמצב ההפעלה של סשן מדיה. התכונה הזו מאפשרת להציג במסך הנעילה בקרי מדיה וגרפיקה. ההתנהגות הזו משתנה בהתאם לגרסה של Android.

מדיה ברקע

אפשר לשלוט במדיה בכל אחד מהתרחישים האלה, גם אם האפליקציה שמפעילה את המדיה פועלת ברקע.

מחשוב סביבתי

חשיפה של המדיה עם נתונים על מה שמופעל ועל האופן שבו אפשר לשלוט בו יכולה ליצור גשרים בין מכשירים, כדי שמשתמשים יוכלו לקיים אינטראקציה איתו במגוון דרכים שמתאימות להם.

מה תפַתחו

בסדנת הקוד הזו, נרחיב את הדוגמה הקיימת של Exoplayer כדי להוסיף תמיכה בסשן מדיה. האפליקציה שלכם:

  • לשקף בצורה נכונה את המצב הפעיל של סשן המדיה
  • העברה של פקדי המדיה אל ExoPlayer
  • העברת המטא-נתונים של הפריטים בתור לסשן המדיה

מה תלמדו

  • למה סשנים של מדיה מספקים למשתמשים חוויה עשירה יותר
  • איך יוצרים סשן מדיה ומנהלים את המצב שלו
  • איך מחברים סשן מדיה ל-ExoPlayer
  • איך לכלול מטא-נתונים של פריטים בתור ההפעלה בסשן המדיה
  • איך מוסיפים פעולות נוספות (בהתאמה אישית)

בקודלאב הזה נסביר על MediaSession SDK. מושגים וקטעי קוד לא רלוונטיים, כולל פרטים על ההטמעה של ExoPlayer, לא נדונים במאמר הזה, אבל הם מוצגים כדי שתוכלו פשוט להעתיק ולהדביק אותם.

מה צריך להכין

  • גרסה עדכנית של Android Studio (3.5 ואילך)
  • ידע בסיסי בפיתוח אפליקציות ל-Android

2. תהליך ההגדרה

מהי נקודת ההתחלה שלנו?

נקודת ההתחלה שלנו היא הדגמה הראשית של ExoPlayer. הדגמה הזו מכילה סרטונים עם לחצני הפעלה במסך, אבל לא משתמשת בסשנים של מדיה מוכנים מראש. זה מקום מצוין להוסיף אותם.

קבלת הדוגמה של ExoPlayer

כדי להתחיל, נתחיל עם הדוגמה של ExoPlayer. משכפלים את המאגר ב-GitHub על ידי הרצת הקוד הבא.

git clone https://github.com/google/ExoPlayer.git

פתיחת ההדגמה

ב-Android Studio, פותחים את פרויקט הדגמה הראשי שנמצא בקטע demos/main.

ב-Android Studio תופיע בקשה להגדיר את נתיב ה-SDK. אם נתקלת בבעיות, כדאי לפעול לפי ההמלצות לעדכון הכלים של סביבת הפיתוח המשולבת ושל ה-SDK.

10e3b5c652186d57.png

אם תתבקשו להשתמש בגרסה האחרונה של Gradle, כדאי לעדכן אותה.

כדאי להקדיש כמה רגעים כדי להבין את העיצוב הבסיסי של האפליקציה. שימו לב שיש שתי פעילויות: SampleChooserActivity ו-PlayerActivity. בשאר הקודלאב נתמקד ב-PlayerActivity, שבה המדיה פועלת בפועל, לכן פותחים את הכיתה הזו וממשיכים לקטע הבא.

3. יצירת סשן מדיה וניהול המצב שלו

יצירת סשן המדיה

פתיחת PlayerActivity.java. הכיתה הזו יוצרת את ExoPlayer ומנהלת את הפונקציות שלו, כמו עיבוד וידאו למסך. בפעילות הזו, נקשר את ExoPlayer לסשן מדיה.

מגדירים את שני השדות הבאים בחלק העליון של הכיתה. נשתמש בשדות האלה לאורך הקטע הזה.

private MediaSessionCompat mediaSession;
private MediaSessionConnector mediaSessionConnector;

צריך להוסיף את יחסי התלות של הפרויקט extension-mediasession לקובץ build.gradle ברמת המודול של 'מודול: demo':

implementation project(path: ':extension-mediasession')

הערה: Android Studio יכול לעזור לכם להוסיף את התלות הזו באופן אוטומטי אם מעבירים את העכבר מעל השגיאה בפתרון MediaSessionConnector:

60055e4ad54fbb97.png

לבסוף, כדי לפתור את ייבוא הכיתות, מוסיפים את הפרטים הבאים:

import android.support.v4.media.session.MediaSessionCompat;
import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector;

כשיוצרים את הפעילות, צריך ליצור סשן מדיה ומחבר של סשן מדיה שישמש כמתווך בין סשן המדיה לבין ExoPlayer.

המקום האידיאלי להוספת הקוד הזה הוא גם המקום שבו נוצר ExoPlayer. באפליקציית הדגמה שלנו, אפשר לצרף את הקוד שלנו לסוף initializePlayer(). חשוב להוסיף את הלוגיקה הזו אחרי יצירת המופע של הנגן.

private void initializePlayer() {
  if (player == null) {
    ...
    player = ...
    ...
    mediaSession = new MediaSessionCompat(this, "sample");
    mediaSessionConnector = new MediaSessionConnector(mediaSession);
    mediaSessionConnector.setPlayer(player);
  }
  ...
}

שחרור סשן המדיה

משחררים את סשן המדיה כשאין בו יותר צורך. כשנוציא את ExoPlayer ב-releasePlayer(), נוכל לכלול גם את הקוד הבא כדי לעשות זאת:

private void releasePlayer() {
  if (mediaSession != null) {
    mediaSession.release();
  }
  ...
}

ניהול מצב סשן המדיה

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

כשהמשתמש מתחיל את הפעילות, סשן המדיה אמור להיות פעיל:

@Override
public void onStart() {
  ...
  if (mediaSession != null) {
    mediaSession.setActive(true);
  }
}

מכיוון שהאפליקציה שלנו לא מפעילה מדיה ברקע, חשוב לוודא שסשן המדיה הופך ללא פעיל כשהמשתמש יוצא מהפעילות:

@Override
public void onStop() {
  super.onStop();
  if (mediaSession != null) {
    mediaSession.setActive(false);
  }
  ...
}

מריצים את ההדגמה

  1. מחברים מכשיר Android או מפעילים אמולטור.
  2. מוודאים שהאפשרות 'demo' מסומנת כדי להריץ אותה מסרגל הכלים של Android Studio. cb1ec4e50886874f.png
  3. לוחצים על 9d8fb3a9ddf12827.png בסרגל הכלים של Android Studio.
  4. אחרי שהאפליקציה מופעלת במכשיר, בוחרים סרטון בסטרימינג שרוצים להפעיל.
  5. אחרי שההפעלה מתחילה, אפשר להשתמש בפקודות adb הבאות כדי לשלוט בסשן המדיה:
adb shell media dispatch pause
adb shell media dispatch play
adb shell media dispatch play-pause
adb shell media dispatch fast-forward
adb shell media dispatch rewind
  1. כדאי גם לבדוק איך מערכת Android רואה את סשן המדיה שלכם. באופן ספציפי, אפשר לבדוק אילו פעולות נתמכות על ידי בדיקה של שדה הפעולה. המספר שמוצג כאן הוא שילוב של מזהי פעולות, כפי שהוצהר באובייקט PlaybackState. כדי לראות את סשן המדיה פועל: adb shell dumpsys media_session
  2. אם אתם משתמשים במכשיר פיזי עם מיקרופון, נסו להפעיל את Google Assistant ולהשתמש בפקודות קוליות, כמו: "Pause" (השהיה). ‏"Resume" "Fast-forward 1 minute".

b8dda02a6fb0f6a4.pngדוגמה ל-ExoPlayer שפועל ב-Android TV.

4. הכללת מטא-נתונים של פריטים בתור ההפעלה

עכשיו אנחנו יכולים להרחיב את התכונות הנתמכות של סשן המדיה, שבו יצרנו בעבר את MediaSessionConnector ב-initializePlayer().

הוספת TimelineQueueNavigator

ExoPlayer מייצג את המבנה של המדיה כציר זמן. כדי להבין איך זה עובד, כדאי לעיין במאמר בנושא אובייקט ציר הזמן של ExoPlayer. בעזרת המבנה הזה, אנחנו יכולים לקבל עדכונים כשהתוכן משתנה ולחשוף מטא-נתונים של התוכן שמושמע כרגע כשאנחנו מבקשים.

כדי לעשות זאת, נגדיר את TimelineQueueNavigator. מאתרים את היצירה של MediaSessionConnector ב-initializePlayer() ומוסיפים הטמעה של TimelineQueueNavigator אחרי האיפוס של mediaSession.

mediaSessionConnector.setQueueNavigator(new TimelineQueueNavigator(mediaSession) {
  @Override
  public MediaDescriptionCompat getMediaDescription(Player player, int windowIndex) {
    return new MediaDescriptionCompat.Builder()
            .setTitle(player.getCurrentMediaItem().mediaMetadata.title)
            .setDescription("MediaDescription description for " + windowIndex)
            .setSubtitle("MediaDescription subtitle")
            .build();
  }
});

כדי לפתור את הבעיות בייבוא הכיתות, צריך להוסיף:

import android.support.v4.media.MediaDescriptionCompat;
import com.google.android.exoplayer2.ext.mediasession.TimelineQueueNavigator;

שימו לב שהפרמטר windowIndex תואם לפריט של המדד הזה בתור ההפעלה.

עכשיו, אחרי שהוספתם מטא-נתונים, תוכלו לבדוק אם Assistant מבינה מה מושמע. בזמן הצפייה בסרטון ב-Android TV, מפעילים את Assistant ושואלים "What's playing?‎" (מה מוצג עכשיו?).

6c7fc0cb853cbc38.png

5. התאמה אישית של פעולות

יכול להיות שהנגן לא תומך בפעולות מסוימות, או שאתם רוצים לכלול תמיכה בפעולות נוספות? עכשיו נתעמק קצת יותר בסשן המדיה שבו יצרנו את MediaSessionConnector ב-initializePlayer().

הצהרה על פעולות נתמכות

אפשר להשתמש ב-mediaSessionConnector.setEnabledPlaybackActions() כדי להתאים אישית את הפעולות שרוצים שתתאפשרנה בסשן המדיה.

שימו לב שהקבוצה המלאה היא:

mediaSessionConnector.setEnabledPlaybackActions(
        PlaybackStateCompat.ACTION_PLAY_PAUSE
                | PlaybackStateCompat.ACTION_PLAY
                | PlaybackStateCompat.ACTION_PAUSE
                | PlaybackStateCompat.ACTION_SEEK_TO
                | PlaybackStateCompat.ACTION_FAST_FORWARD
                | PlaybackStateCompat.ACTION_REWIND
                | PlaybackStateCompat.ACTION_STOP
                | PlaybackStateCompat.ACTION_SET_REPEAT_MODE
                | PlaybackStateCompat.ACTION_SET_SHUFFLE_MODE
);

שוב, נראה איך הנתונים האלה נחשפים לפלטפורמה:

  1. כמו קודם, מפעילים סרטון.
  2. כדי לבדוק איך Android רואה את המטא-נתונים מסשן המדיה, מריצים את הפקודה: adb shell dumpsys media_session
  3. מאתרים את השורה שמכילה את המטא-נתונים ומבחינים שהשם והתיאור כלולים ומשויכים ל-com.google.android.exoplayer2.demo/sample.

הוספת פעולות נוספות

אנחנו יכולים להרחיב את סשן המדיה שלנו עם כמה פעולות נוספות. בקטע הזה נוסיף תמיכה רק בכתוביות.

כתוביות תומכות

הוספת תמיכה בכתוביות לסשנים של מדיה מאפשרת למשתמשים להפעיל אותן או להשבית אותן באמצעות הקול. במקום שבו ביצעתם את האינטוליזציה של מחבר סשן המדיה, מוסיפים את הקוד הבא:

mediaSessionConnector.setCaptionCallback(new MediaSessionConnector.CaptionCallback() {
      @Override
      public void onSetCaptioningEnabled(Player player, boolean enabled) {
        Log.d("MediaSession", "onSetCaptioningEnabled: enabled=" + enabled);
      }

      @Override
      public boolean hasCaptions(Player player) {
        return true;
      }

      @Override
      public boolean onCommand(Player player, ControlDispatcher controlDispatcher, String command, Bundle extras, ResultReceiver cb) {
        return false;
      }
    }
);

לבסוף, פותרים את הבעיות בקשר לייבוא חסר.

כדי לבדוק את זה, מפעילים את Google Assistant ב-Android TV ואומרים "Enable captions" (הפעלת כתוביות). בודקים את ההודעות ב-Logcat כדי לראות איך הקריאה מתבצעת בקוד.

6. מזל טוב

הוספת בהצלחה סשנים של מדיה לדגימה.

קיבלתם כמות עצומה של פונקציונליות על ידי:

  • הוספת סשן מדיה,
  • חיבור סשנים של מדיה למכונה של ExoPlayer,
  • הוספת מטא-נתונים ופעולות נוספות.

עכשיו אתם יודעים מהם השלבים העיקריים שצריך לבצע כדי להעשיר אפליקציית מדיה ולהציע למשתמשים חוויה מגוונת יותר.

הערה סופית

סדנת הקוד הזו מבוססת על דוגמה מקוד המקור של ExoPlayer. אין צורך להשתמש ב-ExoPlayer מהמקור, ומומלץ במקום זאת למשוך את יחסי התלות של ExoPlayer ו-MediaSessionConnector כדי שיהיה קל יותר להתעדכן בגרסאות האחרונות.

כדי לעשות זאת, פשוט מחליפים את יחסי התלות של הפרויקט, כמו:

implementation project(modulePrefix + 'library-core')
implementation project(path: ':extension-mediasession')

כדי לשלוף ממאגרי Maven, כמו:

implementation 'com.google.android.exoplayer:exoplayer-core:2.+'
implementation 'com.google.android.exoplayer:extension-mediasession:2.+'

מסמכי עזרה