ה-framework של נתב המדיה של Android מאפשר ליצרנים לאפשר הפעלה במכשירים שלהם
באמצעות ממשק סטנדרטי שנקרא MediaRouteProvider
.
ספק מסלולים מגדיר ממשק משותף להפעלת מדיה במכשיר המקבל, וכך
אפשר להפעיל מדיה על הציוד שלך מכל אפליקציית Android שתומכת במדיה
למסלולים.
במדריך הזה מוסבר איך ליצור ספק של נתיב מדיה למכשיר המקבל ולהפוך אותו
שזמין לאפליקציות אחרות להפעלת מדיה שפועלות ב-Android. כדי להשתמש ב-API הזה,
צריך להכיר את סוגי המפתחות
MediaRouteProvider
MediaRouteProviderDescriptor
, וגם
RouteController
.
סקירה כללית
ה-framework של נתב המדיה של Android מאפשר למפתחים של אפליקציות מדיה ולמכשיר להפעלת מדיה
ליצרנים שונים כדי להתחבר באמצעות ממשק API משותף וממשק משתמש משותף. מפתחי אפליקציות ש
להטמיע ממשק MediaRouter
ואז להתחבר
framework והפעלת תוכן במכשירים שמשתתפים ב-framework של נתב המדיה. תמונות וסרטונים
יצרנים של מכשירי הפעלה יכולים להשתתף ב-framework על ידי פרסום MediaRouteProvider
שמאפשר לאפליקציות אחרות להתחבר אל
להפעיל מדיה במכשירים המקלטים. איור 1 ממחיש איך אפליקציה מתחברת למכשיר קול
המכשיר דרך ה-framework של נתב המדיה.
כשבונים ספק של נתיב מדיה למכשיר המקבל, הספק מציג את למטרות הבאות:
- תיאור ופרסום של היכולות של המכשיר המקבל כדי שאפליקציות אחרות יוכלו לגלות אותו ולהשתמש בתכונות ההפעלה שלו.
- גלישה בממשק התכנות של המכשיר המקבל והתקשורת שלו כדי להתאים את המכשיר למסגרת של נתב המדיה.
התפלגות של ספקי מסלולים
ספק של ניתוב מדיה מופץ כחלק מאפליקציה ל-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 דוגמה שממחישה איך ליצור ספק של ניתוב מדיה בהתאמה אישית.