يسمح إطار عمل جهاز توجيه الوسائط لنظام التشغيل Android للشركات المصنّعة بتفعيل تشغيل المحتوى على أجهزتهم
من خلال واجهة موحّدة تُسمى MediaRouteProvider
.
يحدد موفِّر المسار واجهة شائعة لتشغيل الوسائط على جهاز الاستقبال، ما يجعل من الممكن
تشغيل الوسائط على جهازك من أي تطبيق Android يتوافق مع مسارات
الوسائط.
يناقش هذا الدليل كيفية إنشاء موفِّر مسار الوسائط لجهاز الاستقبال وإتاحتها لتطبيقات تشغيل الوسائط الأخرى التي تعمل على نظام التشغيل Android. لاستخدام واجهة برمجة التطبيقات هذه، يجب أن تكون
على دراية بالفئات الرئيسية
MediaRouteProvider
وMediaRouteProviderDescriptor
وRouteController
.
نظرة عامة
يتيح إطار عمل جهاز توجيه الوسائط Android لمطوّري تطبيقات الوسائط والشركات المصنّعة لأجهزة تشغيل الوسائط إمكانية الاتصال من خلال واجهة برمجة تطبيقات مشتركة وواجهة مستخدم مشتركة. يمكن لمطوّري التطبيقات الذين ينفّذون واجهة MediaRouter
الربط بإطار العمل وتشغيل المحتوى على الأجهزة التي تشارك في إطار عمل جهاز توجيه الوسائط. ويمكن للشركات المصنّعة لأجهزة تشغيل الوسائط المشاركة في إطار العمل من خلال نشر MediaRouteProvider
تتيح للتطبيقات الأخرى الاتصال بالوسائط وتشغيلها على أجهزة الاستقبال. يوضّح الشكل 1 كيفية اتصال أحد التطبيقات بجهاز الاستقبال من خلال إطار عمل جهاز توجيه الوسائط.
عند إنشاء موفِّر مسار الوسائط لجهاز الاستقبال، يخدم الموفر الأغراض التالية:
- يُرجى وصف إمكانات جهاز الاستقبال ونشرها حتى تتمكّن التطبيقات الأخرى من اكتشافه واستخدام ميزات التشغيل الخاصة به.
- لفّ واجهة البرمجة لجهاز الاستقبال وآليات نقل الاتصالات التابعة له لجعل الجهاز متوافقًا مع إطار عمل جهاز توجيه الوسائط.
توزيع مزوّدي المسارات
يتم توزيع موفِّر مسار الوسائط كجزء من تطبيق Android. ويمكن إتاحة
موفِّر التوجيه للتطبيقات الأخرى من خلال توسيع
نطاق
MediaRouteProviderService
أو إضافة تضمين
MediaRouteProvider
مع خدمتك الخاصة وإعلان فلتر
الأهداف لموفّر مسار الوسائط. تسمح هذه الخطوات للتطبيقات الأخرى باكتشاف
مسار الوسائط والاستفادة منه.
ملاحظة: يمكن أن يتضمّن التطبيق الذي يحتوي على موفِّر مسار الوسائط أيضًا واجهة MediaRouter لمقدِّم التوجيه، إلا أنّ هذا الإجراء غير مطلوب.
مكتبة دعم MediaRouter
يتم تحديد واجهات برمجة تطبيقات جهاز توجيه الوسائط في مكتبة AndroidX MediaRouter يجب إضافة هذه المكتبة إلى مشروع تطوير التطبيق. ولمزيد من المعلومات حول إضافة مكتبات الدعم إلى مشروعك، يُرجى الاطّلاع على إعداد مكتبة الدعم.
تنبيه: احرص على استخدام تطبيق AndroidX
لإطار عمل جهاز توجيه الوسائط.
لا تستخدم حزمة android.media
القديمة.
إنشاء خدمة الموفر
يجب أن يتمكن إطار عمل جهاز توجيه الوسائط من اكتشاف موفِّر مسار الوسائط والاتصال به
للسماح للتطبيقات الأخرى باستخدام مسارك. لإجراء ذلك، يبحث إطار عمل جهاز توجيه الوسائط
عن التطبيقات التي تعلن عن الإجراء المطلوب لمزوّد مسار الوسائط. عندما يريد تطبيق آخر الاتصال بمقدّم الخدمة، يجب أن يتمكن إطار العمل من استدعاءه والاتصال به، لذلك يجب تضمين الموفّر
في Service
.
يعرض الرمز في المثال التالي بيانًا عن خدمة مقدّم خدمة مسار الوسائط وفلتر الأهداف في أحد البيان، ما يتيح إمكانية العثور على هذه الخدمة واستخدامها من خلال إطار عمل جهاز توجيه الوسائط:
<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>
يعلن هذا المثال على البيان عن خدمة تضم فئات موفِّري مسار الوسائط الفعلي.
يوفّر إطار عمل جهاز توجيه الوسائط Android الفئة MediaRouteProviderService
لاستخدامها كبرنامج تضمين خدمة لموفّري مسارات الوسائط. يوضّح الرمز البرمجي التالي كيفية استخدام فئة برنامج التضمين هذه:
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); } }
تحديد إمكانيات المسارات
يمكن للتطبيقات المرتبطة بإطار عمل جهاز توجيه الوسائط اكتشاف مسار الوسائط من خلال بيانات البيان في التطبيق، ولكنها تحتاج أيضًا إلى معرفة إمكانات مسارات الوسائط التي تقدّمها. يمكن أن تكون مسارات الوسائط من أنواع مختلفة وتحتوي على ميزات مختلفة، ويجب أن تتمكّن التطبيقات الأخرى من اكتشاف هذه التفاصيل لتحديد ما إذا كانت متوافقة مع مسارك.
يتيح لك إطار عمل موجِّه الوسائط تحديد إمكانات مسار الوسائط ونشرها من خلال عناصر IntentFilter
وكائنات MediaRouteDescriptor
وMediaRouteProviderDescriptor
. يوضّح هذا القسم كيفية استخدام هذه
الصفوف لنشر تفاصيل مسار الوسائط للتطبيقات الأخرى.
فئات المسارات
كجزء من الوصف الآلي لموفّر مسار الوسائط، عليك تحديد ما إذا كان مقدّم الخدمة يتيح التشغيل عن بُعد أو إخراج ثانوي أو كليهما. في ما يلي فئات المسارات التي يوفّرها إطار عمل جهاز توجيه الوسائط:
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); } }
إذا حددت هدف CATEGORY_REMOTE_PLAYBACK
، عليك أيضًا تحديد أنواع الوسائط
وعناصر التحكّم في التشغيل المتوافقة مع مزوّد مسار الوسائط. يوضّح القسم التالي طريقة تحديد هذه الإعدادات لجهازك.
أنواع الوسائط والبروتوكولات الخاصة بها
يجب أن يحدّد موفِّر مسار الوسائط لجهاز تشغيل عن بُعد أنواع الوسائط وبروتوكولات النقل
المتوافقة معه. يمكنك تحديد هذه الإعدادات باستخدام الفئة IntentFilter
والطريقتَين addDataScheme()
وaddDataType()
لذلك الكائن. يوضّح مقتطف الرمز التالي كيفية تحديد فلتر أهداف لإتاحة تشغيل الفيديوهات عن بُعد باستخدام بروتوكول http وhttps وبروتوكول البث في الوقت الفعلي (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); } } }
عناصر التحكّم في التشغيل
بالنسبة إلى موفِّر مسار الوسائط، الذي يوفر إمكانية التشغيل عن بُعد، يجب أن يحدّد أنواع عناصر التحكّم في الوسائط المتوافقة. في ما يلي الأنواع العامة للتحكّم التي يمكن أن توفّرها مسارات الوسائط:
- عناصر التحكم في التشغيل، مثل التشغيل والإيقاف المؤقت والترجيع والتقديم السريع
- ميزات قائمة المحتوى التالي التي تسمح لتطبيق الإرسال بإضافة عناصر وإزالتها من قائمة تشغيل يديرها جهاز المستلِم.
- ميزات الجلسة: تمنع هذه الميزات إرسال التطبيقات من التدخّل في بعضها البعض من خلال ضبط جهاز المُستلِم على توفير معرّف جلسة للتطبيق الذي يطلبه، ثم التحقّق من ذلك المعرّف مع كل طلب لاحق للتحكّم في التشغيل.
يوضّح مثال الرمز التالي كيفية إنشاء فلتر أهداف لإتاحة عناصر التحكّم الأساسية في تشغيل مسار الوسائط:
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
.
أداة وصف MediaRouteProvider
بعد تحديد إمكانات مسار الوسائط باستخدام عناصر IntentFilter
، يمكنك إنشاء كائن واصف للنشر على إطار عمل جهاز توجيه الوسائط Android. يحتوي كائن الواصف هذا على تفاصيل إمكانيات مسار الوسائط حتى تتمكّن التطبيقات الأخرى من تحديد كيفية التفاعل مع مسار الوسائط.
يوضّح الرمز النموذجي التالي كيفية إضافة فلاتر الأهداف التي تم إنشاؤها سابقًا إلى
MediaRouteProviderDescriptor
وضبط الوصف لاستخدامه من خلال
إطار عمل جهاز توجيه الوسائط:
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
.
التحكم في المسارات
عندما يتصل تطبيق بمزوّد مسار الوسائط، يتلقى موفّر الخدمة أوامر التشغيل من خلال إطار عمل جهاز توجيه الوسائط الذي ترسله تطبيقات أخرى إلى مسارك. لمعالجة هذه الطلبات، يجب توفير فئة MediaRouteProvider.RouteController
تعالج الأوامر وتعالج الاتصال الفعلي بجهاز الاستقبال.
يستدعي إطار عمل جهاز توجيه الوسائط طريقة onCreateRouteController()
لموفّر المسار للحصول على مثيل من هذه الفئة ثم يوجِّه الطلبات إليه.
في ما يلي الطرق الرئيسية للفئة MediaRouteProvider.RouteController
، التي يجب تنفيذها مع موفِّر مسار الوسائط:
onSelect()
— يتم استدعاء هذا الإجراء عندما يختار تطبيق مسارك للتشغيل. ويمكنك استخدام هذه الطريقة لتنفيذ أي أعمال تحضيرية قد تكون مطلوبة قبل بدء تشغيل الوسائط.onControlRequest()
: لإرسال أوامر تشغيل محدّدة إلى الجهاز المستلِم.onSetVolume()
— لإرسال طلب إلى الجهاز المستلِم لضبط مستوى صوت التشغيل على قيمة محدّدةonUpdateVolume()
— لإرسال طلب إلى الجهاز المستلِم لتعديل مستوى صوت التشغيل بمقدار محدّدonUnselect()
— يتم استدعاؤها عندما يلغي أحد التطبيقات اختيار مسار ما.onRelease()
— يتم استدعاء هذا الإجراء عندما لا يعود إطار العمل بحاجة إلى المسار، ما يسمح بإخلاء موارده.
يتم توجيه جميع طلبات التحكّم في التشغيل إلى طريقة
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
مصمّمة ليكون بمثابة برنامج تضمين
لواجهة برمجة التطبيقات مع معدات تشغيل الوسائط. ويعتمد تنفيذ الطرق في هذه الفئة تمامًا على الواجهة الآلية التي يوفّرها الجهاز المستلِم.
نموذج التعليمات البرمجية
يعرض نموذج MediaRouter طريقة إنشاء موفِّر مسار مخصّص للوسائط.