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

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

במדריך הזה מוסבר איך ליצור ספק של נתיב מדיה למכשיר המקבל ולהפוך אותו שזמין לאפליקציות אחרות להפעלת מדיה שפועלות ב-Android. כדי להשתמש ב-API הזה, צריך להכיר את סוגי המפתחות MediaRouteProvider MediaRouteProviderDescriptor, וגם RouteController.

סקירה כללית

ה-framework של נתב המדיה של Android מאפשר למפתחים של אפליקציות מדיה ולמכשיר להפעלת מדיה ליצרנים שונים כדי להתחבר באמצעות ממשק API משותף וממשק משתמש משותף. מפתחי אפליקציות ש להטמיע ממשק MediaRouter ואז להתחבר framework והפעלת תוכן במכשירים שמשתתפים ב-framework של נתב המדיה. תמונות וסרטונים יצרנים של מכשירי הפעלה יכולים להשתתף ב-framework על ידי פרסום MediaRouteProvider שמאפשר לאפליקציות אחרות להתחבר אל להפעיל מדיה במכשירים המקלטים. איור 1 ממחיש איך אפליקציה מתחברת למכשיר קול המכשיר דרך ה-framework של נתב המדיה.

איור 1. סקירה כללית של האופן שבו מחלקות של ספקי ניתוב מדיה מספקות תקשורת מאפליקציית מדיה למכשיר המקבל.

כשבונים ספק של נתיב מדיה למכשיר המקבל, הספק מציג את למטרות הבאות:

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

התפלגות של ספקי מסלולים

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

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

ספריית התמיכה של MediaRouter

ממשקי ה-API של נתב המדיה מוגדרים ספריית MediaRouter ב-AndroidX חובה להוסיף את הספרייה הזו לפרויקט פיתוח האפליקציה. למידע נוסף על הוספת ספריות תמיכה ראו הגדרה של ספריית התמיכה.

זהירות: הקפידו להשתמש ב-AndroidX של ה-framework של נתב המדיה. אין להשתמש בחבילה הישנה של android.media.

יצירת שירות לספק

ל-framework של נתב המדיה צריכה להיות אפשרות לגלות את ספק נתיב המדיה שלך ולהתחבר אליו כדי לאפשר לאפליקציות אחרות להשתמש במסלול שלך. כדי לעשות זאת, ה-framework של נתב המדיה מחפש אפליקציות שמוצהרות בהן פעולת Intent של ספק מסלול מדיה. כשאפליקציה אחרת מבקשת להתחבר לספק, ל-framework צריכה להיות אפשרות להפעיל אותו ולהתחבר אליו, ולכן הספק חייב להיות מוקף ב-Service.

הקוד לדוגמה הבא מציג את ההצהרה על שירות של ספק נתיב מדיה מסנן Intent במניפסט, שמאפשר לנתב המדיה לגלות אותו ולהשתמש בו framework:

<service android:name=".provider.SampleMediaRouteProviderService"
    android:label="@string/sample_media_route_provider_service"
    android:process=":mrp">
    <intent-filter>
        <action android:name="android.media.MediaRouteProviderService" />
    </intent-filter>
</service>

במניפסט הזה מוצהר על שירות שעוטף את המחלקות בפועל של ספקי ניתוב מדיה. ה-framework של נתב המדיה של Android מספק מחלקה MediaRouteProviderService לשימוש כ-wrapper של שירות עבור ספקים של נתיבי מדיה. הקוד לדוגמה הבא מדגים איך משתמשים ב-wrapper הזה class:

Kotlin

class SampleMediaRouteProviderService : MediaRouteProviderService() {

    override fun onCreateMediaRouteProvider(): MediaRouteProvider {
        return SampleMediaRouteProvider(this)
    }
}

Java

public class SampleMediaRouteProviderService extends MediaRouteProviderService {

    @Override
    public MediaRouteProvider onCreateMediaRouteProvider() {
        return new SampleMediaRouteProvider(this);
    }
}

ציון יכולות מסלול

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

ה-framework של נתב המדיה מאפשר להגדיר ולפרסם את היכולות של המדיה נתיב שחוצה IntentFilter אובייקטים, MediaRouteDescriptor אובייקטים ו-MediaRouteProviderDescriptor. בקטע הזה נסביר איך להשתמש שיעורים כדי לפרסם את הפרטים של מסלול המדיה שלכם באפליקציות אחרות.

קטגוריות מסלולים

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

  • CATEGORY_LIVE_AUDIO – פלט אודיו למכשיר פלט משני, כמו מערכת מוזיקה עם חיבור אלחוטי.
  • CATEGORY_LIVE_VIDEO — פלט וידאו למכשיר פלט משני, כמו מכשירי תצוגה אלחוטית.
  • CATEGORY_REMOTE_PLAYBACK — הפעלת וידאו או אודיו במכשיר נפרד שמטפל במדיה אחזור, פענוח והפעלה, כמו מכשירי Chromecast.

כדי לכלול את ההגדרות האלה בתיאור של מסלול המדיה, צריך להזין אותן בשדה אובייקט IntentFilter, שאותו אפשר להוסיף מאוחר יותר אובייקט MediaRouteDescriptor:

Kotlin

class SampleMediaRouteProvider(context: Context) : MediaRouteProvider(context) {

    companion object {
        private val CONTROL_FILTERS_BASIC: ArrayList<IntentFilter> = IntentFilter().run {
            addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)
            arrayListOf(this)
        }
    }
}

Java

public final class SampleMediaRouteProvider extends MediaRouteProvider {
    private static final ArrayList<IntentFilter> CONTROL_FILTERS_BASIC;
    static {
        IntentFilter videoPlayback = new IntentFilter();
        videoPlayback.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK);
        CONTROL_FILTERS_BASIC = new ArrayList<IntentFilter>();
        CONTROL_FILTERS_BASIC.add(videoPlayback);
    }
}

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

סוגי מדיה ופרוטוקולים

ספק לניתוב מדיה למכשיר להפעלה מרחוק חייב לציין את סוגי המדיה ולהעביר ופרוטוקולים נתמכים. כדי לציין את ההגדרות האלה אפשר להשתמש במאפיין IntentFilter הכיתה וגם addDataScheme() addDataType() methods של האובייקט הזה. קטע הקוד הבא מדגים איך להגדיר מסנן Intent לתמיכה בווידאו מרחוק להפעלה באמצעות http, https ו-Real Time Streaming Protocol (RTSP):

Kotlin

class SampleMediaRouteProvider(context: Context) : MediaRouteProvider(context) {

    companion object {

        private fun IntentFilter.addDataTypeUnchecked(type: String) {
            try {
                addDataType(type)
            } catch (ex: IntentFilter.MalformedMimeTypeException) {
                throw RuntimeException(ex)
            }
        }

        private val CONTROL_FILTERS_BASIC: ArrayList<IntentFilter> = IntentFilter().run {
            addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)
            addAction(MediaControlIntent.ACTION_PLAY)
            addDataScheme("http")
            addDataScheme("https")
            addDataScheme("rtsp")
            addDataTypeUnchecked("video/*")
            arrayListOf(this)
        }
    }
    ...
}

Java

public final class SampleMediaRouteProvider extends MediaRouteProvider {

    private static final ArrayList<IntentFilter> CONTROL_FILTERS_BASIC;

    static {
        IntentFilter videoPlayback = new IntentFilter();
        videoPlayback.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK);
        videoPlayback.addAction(MediaControlIntent.ACTION_PLAY);
        videoPlayback.addDataScheme("http");
        videoPlayback.addDataScheme("https");
        videoPlayback.addDataScheme("rtsp");
        addDataTypeUnchecked(videoPlayback, "video/*");
        CONTROL_FILTERS_BASIC = new ArrayList<IntentFilter>();
        CONTROL_FILTERS_BASIC.add(videoPlayback);
    }
    ...

    private static void addDataTypeUnchecked(IntentFilter filter, String type) {
        try {
            filter.addDataType(type);
        } catch (MalformedMimeTypeException ex) {
            throw new RuntimeException(ex);
        }
    }
}

פקדי הפעלה

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

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

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

Kotlin

class SampleMediaRouteProvider(context: Context) : MediaRouteProvider(context) {

    companion object {
        ...
        private val CONTROL_FILTERS_BASIC: ArrayList<IntentFilter> = run {
            val videoPlayback: IntentFilter = ...
            ...
            val playControls = IntentFilter().apply {
                addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)
                addAction(MediaControlIntent.ACTION_SEEK)
                addAction(MediaControlIntent.ACTION_GET_STATUS)
                addAction(MediaControlIntent.ACTION_PAUSE)
                addAction(MediaControlIntent.ACTION_RESUME)
                addAction(MediaControlIntent.ACTION_STOP)
            }
            arrayListOf(videoPlayback, playControls)
        }
    }
    ...
}

Java

public final class SampleMediaRouteProvider extends MediaRouteProvider {
    private static final ArrayList<IntentFilter> CONTROL_FILTERS_BASIC;
    static {
        ...
        IntentFilter playControls = new IntentFilter();
        playControls.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK);
        playControls.addAction(MediaControlIntent.ACTION_SEEK);
        playControls.addAction(MediaControlIntent.ACTION_GET_STATUS);
        playControls.addAction(MediaControlIntent.ACTION_PAUSE);
        playControls.addAction(MediaControlIntent.ACTION_RESUME);
        playControls.addAction(MediaControlIntent.ACTION_STOP);
        CONTROL_FILTERS_BASIC = new ArrayList<IntentFilter>();
        CONTROL_FILTERS_BASIC.add(videoPlayback);
        CONTROL_FILTERS_BASIC.add(playControls);
    }
    ...
}

מידע נוסף על ההגדרות הזמינות של פקדי ההפעלה זמין ב כיתה אחת (MediaControlIntent).

MediaRouteProviderDescriptor

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

הקוד לדוגמה הבא מדגים איך להוסיף את מסנני ה-Intent שנוצרו קודם לכן MediaRouteProviderDescriptor ומגדירים את המתאר לשימוש עד ה-framework של נתב המדיה:

Kotlin

class SampleMediaRouteProvider(context: Context) : MediaRouteProvider(context) {

    init {
        publishRoutes()
    }

    private fun publishRoutes() {
        val resources = context.resources
        val routeName: String = resources.getString(R.string.variable_volume_basic_route_name)
        val routeDescription: String = resources.getString(R.string.sample_route_description)
        // Create a route descriptor using previously created IntentFilters
        val routeDescriptor: MediaRouteDescriptor =
                MediaRouteDescriptor.Builder(VARIABLE_VOLUME_BASIC_ROUTE_ID, routeName)
                        .setDescription(routeDescription)
                        .addControlFilters(CONTROL_FILTERS_BASIC)
                        .setPlaybackStream(AudioManager.STREAM_MUSIC)
                        .setPlaybackType(MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE)
                        .setVolumeHandling(MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE)
                        .setVolumeMax(VOLUME_MAX)
                        .setVolume(mVolume)
                        .build()
        // Add the route descriptor to the provider descriptor
        val providerDescriptor: MediaRouteProviderDescriptor =
                MediaRouteProviderDescriptor.Builder()
                        .addRoute(routeDescriptor)
                        .build()

        // Publish the descriptor to the framework
        descriptor = providerDescriptor
    }
    ...
}

Java

public SampleMediaRouteProvider(Context context) {
    super(context);
    publishRoutes();
}

private void publishRoutes() {
    Resources r = getContext().getResources();
    // Create a route descriptor using previously created IntentFilters
    MediaRouteDescriptor routeDescriptor = new MediaRouteDescriptor.Builder(
            VARIABLE_VOLUME_BASIC_ROUTE_ID,
            r.getString(R.string.variable_volume_basic_route_name))
            .setDescription(r.getString(R.string.sample_route_description))
            .addControlFilters(CONTROL_FILTERS_BASIC)
            .setPlaybackStream(AudioManager.STREAM_MUSIC)
            .setPlaybackType(MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE)
            .setVolumeHandling(MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE)
            .setVolumeMax(VOLUME_MAX)
            .setVolume(mVolume)
            .build();
    // Add the route descriptor to the provider descriptor
    MediaRouteProviderDescriptor providerDescriptor =
            new MediaRouteProviderDescriptor.Builder()
            .addRoute(routeDescriptor)
            .build();

    // Publish the descriptor to the framework
    setDescriptor(providerDescriptor);
}

למידע נוסף על ההגדרות הזמינות של מתאר, עיינו במשאבי העזרה בשביל MediaRouteDescriptor ו-MediaRouteProviderDescriptor.

שליטה במסלולים

כשאפליקציה מתחברת לספק של נתיב המדיה, הספק מקבל הפעלה פקודות דרך ה-framework של נתב המדיה שנשלח למסלול שלך על ידי אפליקציות אחרות. כדי לטפל בתרחישים האלה בקשות, צריך לספק הטמעה של מחלקה MediaRouteProvider.RouteController, שמעבדת את הפקודות ומטפל בתקשורת עצמה עם המכשיר המקבל.

ב-framework של נתב המדיה מתבצעת קריאה ל-onCreateRouteController() של ספק הנתיבים כדי לקבל מופע של המחלקה הזו ואז לנתב אליו בקשות. אלה השיטות העיקריות למחלקה MediaRouteProvider.RouteController, שצריך להטמיע עבורן ספק נתיב המדיה שלך:

  • onSelect() — מתבצעת קריאה כשאפליקציה בוחרת את המסלול להפעלה. השיטה הזו משמשת אותך כדי: כל עבודת הכנה שעשויה להידרש לפני תחילת ההפעלה של המדיה.
  • onControlRequest() – שולח פקודות הפעלה ספציפיות למכשיר המקבל.
  • onSetVolume() – נשלחת למכשיר המקבל בקשה להגדיר את עוצמת הקול של ההפעלה בערך מסוים.
  • onUpdateVolume() – תישלח בקשה למכשיר המקבל לשנות את ההפעלה בנפח מסוים בסכום שצוין.
  • onUnselect() — מתקבלת שיחה כשאפליקציה מבטלת את הבחירה במסלול.
  • onRelease() — מתבצעת קריאה כשמסגרת ה-frame לא נחוצה יותר, וכך היא יכולה לשחרר את במשאבי אנוש.

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

Kotlin

private class SampleRouteController : MediaRouteProvider.RouteController() {
    ...

    override fun onControlRequest(
            intent: Intent,
            callback: MediaRouter.ControlRequestCallback?
    ): Boolean {
        return if (intent.hasCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)) {
            val action = intent.action
            when (action) {
                MediaControlIntent.ACTION_PLAY -> handlePlay(intent, callback)
                MediaControlIntent.ACTION_ENQUEUE -> handleEnqueue(intent, callback)
                MediaControlIntent.ACTION_REMOVE -> handleRemove(intent, callback)
                MediaControlIntent.ACTION_SEEK -> handleSeek(intent, callback)
                MediaControlIntent.ACTION_GET_STATUS -> handleGetStatus(intent, callback)
                MediaControlIntent.ACTION_PAUSE -> handlePause(intent, callback)
                MediaControlIntent.ACTION_RESUME -> handleResume(intent, callback)
                MediaControlIntent.ACTION_STOP -> handleStop(intent, callback)
                MediaControlIntent.ACTION_START_SESSION -> handleStartSession(intent, callback)
                MediaControlIntent.ACTION_GET_SESSION_STATUS ->
                    handleGetSessionStatus(intent, callback)
                MediaControlIntent.ACTION_END_SESSION -> handleEndSession(intent, callback)
                else -> false
            }.also {
                Log.d(TAG, sessionManager.toString())
            }
        } else {
            false
        }
    }
    ...
}

Java

private final class SampleRouteController extends
        MediaRouteProvider.RouteController {
    ...

    @Override
    public boolean onControlRequest(Intent intent, ControlRequestCallback callback) {

        String action = intent.getAction();

        if (intent.hasCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)) {
            boolean success = false;
            if (action.equals(MediaControlIntent.ACTION_PLAY)) {
                success = handlePlay(intent, callback);
            } else if (action.equals(MediaControlIntent.ACTION_ENQUEUE)) {
                success = handleEnqueue(intent, callback);
            } else if (action.equals(MediaControlIntent.ACTION_REMOVE)) {
                success = handleRemove(intent, callback);
            } else if (action.equals(MediaControlIntent.ACTION_SEEK)) {
                success = handleSeek(intent, callback);
            } else if (action.equals(MediaControlIntent.ACTION_GET_STATUS)) {
                success = handleGetStatus(intent, callback);
            } else if (action.equals(MediaControlIntent.ACTION_PAUSE)) {
                success = handlePause(intent, callback);
            } else if (action.equals(MediaControlIntent.ACTION_RESUME)) {
                success = handleResume(intent, callback);
            } else if (action.equals(MediaControlIntent.ACTION_STOP)) {
                success = handleStop(intent, callback);
            } else if (action.equals(MediaControlIntent.ACTION_START_SESSION)) {
                success = handleStartSession(intent, callback);
            } else if (action.equals(MediaControlIntent.ACTION_GET_SESSION_STATUS)) {
                success = handleGetSessionStatus(intent, callback);
            } else if (action.equals(MediaControlIntent.ACTION_END_SESSION)) {
                success = handleEndSession(intent, callback);
            }

            Log.d(TAG, sessionManager.toString());
            return success;
        }
        return false;
    }
    ...
}

חשוב להבין שהמחלקה MediaRouteProvider.RouteController נועדה לשמש כ-wrapper עבור ה-API לציוד להפעלת מדיה. ההטמעה של השיטות בכיתה הזו תלויה לחלוטין בממשק הפרוגרמטי שמספק המכשיר המקבל.

קוד לדוגמה

ה-MediaRouter דוגמה שממחישה איך ליצור ספק של ניתוב מדיה בהתאמה אישית.