نظرة عامة على MediaRouter

لاستخدام إطار عمل MediaRouter في تطبيقك، يجب الحصول على مثيل الكائن MediaRouter وإرفاق كائن MediaRouter.Callback لرصد أحداث التوجيه. يمر المحتوى المُرسَل عبر مسار وسائط عبر MediaRouteProvider المرتبط بالمسار (باستثناء في بعض الحالات الخاصة، مثل جهاز إخراج بلوتوث). يقدّم الشكل 1 عرضًا شاملاً للفئات المستخدَمة لتوجيه المحتوى بين الأجهزة.

الشكل 1. نظرة عامة على فئات أجهزة توجيه الوسائط الرئيسية التي تستخدمها التطبيقات.

ملاحظة: إذا كنت تريد أن يتوافق تطبيقك مع أجهزة Google Cast، يجب استخدام حزمة SDK للإرسال وإنشاء تطبيقك باعتباره إرسال تطبيقات تعمل بتكنولوجيا Google Cast. اتّبِع التوجيهات الواردة في مستندات البث بدلاً من استخدام إطار عمل MediaRouter مباشرةً.

زر توجيه الوسائط

يجب أن تستخدم تطبيقات Android زر توجيه الوسائط للتحكّم في توجيه الوسائط. يوفر إطار عمل MediaRouter واجهة قياسية للزر، مما يساعد المستخدمين في التعرف على التوجيه واستخدامه عندما يكون متاحًا. يتم وضع زر توجيه الوسائط عادةً على الجانب الأيمن من شريط إجراءات التطبيق، كما هو موضح في الشكل 2.

الشكل 2. زر توجيه الوسائط في شريط الإجراءات.

عندما يضغط المستخدم على زر توجيه الوسائط، تظهر مسارات الوسائط المتاحة في قائمة كما هو موضح في الشكل 3.

الشكل 3. قائمة بمسارات الوسائط المتاحة، يتم عرضها بعد الضغط على زر توجيه الوسائط.

اتبع الخطوات التالية لإنشاء زر توجيه الوسائط:

  1. استخدام AppCompatActivity
  2. تحديد عنصر قائمة زر توجيه الوسائط
  3. إنشاء MediaRouteSelector
  4. إضافة زر توجيه الوسائط إلى شريط الإجراءات
  5. إنشاء وإدارة MediaRouter.طُرق معاودة الاتصال خلال مراحل نشاطك

يصف هذا القسم الخطوات الأربع الأولى. يوضح القسم التالي طرق معاودة الاتصال.

استخدام AppCompatActivity

عند استخدام إطار عمل جهاز توجيه الوسائط في نشاط معيّن، يجب توسيع النشاط من AppCompatActivity واستيراد حزمة androidx.appcompat.app. يجب إضافة مكتبات الدعم androidx.appcompat:appcompat وandroidx.mediarouter:mediarouter إلى مشروع تطوير التطبيقات. لمزيد من المعلومات حول إضافة مكتبات الدعم إلى مشروعك، يمكنك الاطّلاع على بدء استخدام Android Jetpack.

تنبيه: احرص على استخدام تطبيق androidx لإطار عمل جهاز توجيه الوسائط. لا تستخدم حزمة android.media القديمة.

يمكنك إنشاء ملف xml يحدّد عنصر قائمة لزر توجيه الوسائط. يجب أن يكون إجراء العنصر هو الفئة MediaRouteActionProvider. إليك مثال على ملف:

// myMediaRouteButtonMenuItem.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:app="http://schemas.android.com/apk/res-auto"
      >

    <item android:id="@+id/media_route_menu_item"
        android:title="@string/media_route_menu_title"
        app:actionProviderClass="androidx.mediarouter.app.MediaRouteActionProvider"
        app:showAsAction="always"
    />
</menu>

إنشاء MediaRouteSelector

يتم تحديد المسارات التي تظهر في قائمة أزرار توجيه الوسائط من خلال MediaRouteSelector. يجب توسيع نشاطك من AppCompatActivity وإنشاء أداة الاختيار عند إنشاء النشاط عن طريق استدعاء MediaRouteSelector.Builder من طريقة onCreate() كما هو موضّح في نموذج الرمز التالي. ملاحظة: يتم حفظ أداة الاختيار في متغيّر فئة، ويتم تحديد أنواع المسارات المسموح بها من خلال إضافة عناصر MediaControlIntent:

Kotlin

class MediaRouterPlaybackActivity : AppCompatActivity() {

    private var mSelector: MediaRouteSelector? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // Create a route selector for the type of routes your app supports.
        mSelector = MediaRouteSelector.Builder()
                // These are the framework-supported intents
                .addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)
                .build()
    }
}

Java

public class MediaRouterPlaybackActivity extends AppCompatActivity {
    private MediaRouteSelector mSelector;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Create a route selector for the type of routes your app supports.
        mSelector = new MediaRouteSelector.Builder()
                // These are the framework-supported intents
                .addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)
                .build();
    }
}

في معظم التطبيقات، نوع المسار الوحيد المطلوب هو CATEGORY_REMOTE_PLAYBACK. يتعامل نوع المسار هذا مع الجهاز الذي يشغّل تطبيقك على أنّه وحدة تحكّم عن بُعد. يعالج جهاز الاستقبال المتصل جميع عمليات استرداد بيانات المحتوى وفك ترميزها وتشغيلها. وهذه هي طريقة عمل التطبيقات التي تتوافق مع Google Cast، مثل Chromecast.

توفر بعض الشركات المصنِّعة خيارًا خاصًا للتوجيه يسمى "الإخراج الثانوي". من خلال عملية التوجيه هذه، يسترد تطبيق الوسائط الفيديوهات أو الموسيقى ويعرضها وبثها مباشرةً إلى الشاشة و/أو مكبّرات الصوت على جهاز الاستقبال عن بُعد المحدّد. استخدِم إخراجًا ثانويًا لإرسال المحتوى إلى أنظمة الموسيقى أو شاشات عرض الفيديو التي تُفعِّل لاسلكيًا. لتفعيل إمكانية اكتشاف هذه الأجهزة واختيارها، ينبغي إضافة فئة عنصر التحكّم CATEGORY_LIVE_AUDIO أو CATEGORY_LIVE_VIDEO إلى أداة MediaRouteSelector. يجب أيضًا إنشاء مربّع حوار Presentation خاص بك والتعامل معه.

إضافة زر توجيه الوسائط إلى شريط الإجراءات

باستخدام قائمة مسار الوسائط وتحديد MediaRouteSelector، يمكنك الآن إضافة زر توجيه الوسائط إلى نشاط. يمكنك إلغاء طريقة onCreateOptionsMenu() لكل نشاط من أنشطتك لإضافة قائمة خيارات.

Kotlin

override fun onCreateOptionsMenu(menu: Menu): Boolean {
    super.onCreateOptionsMenu(menu)

    // Inflate the menu and configure the media router action provider.
    menuInflater.inflate(R.menu.sample_media_router_menu, menu)

    // Attach the MediaRouteSelector to the menu item
    val mediaRouteMenuItem = menu.findItem(R.id.media_route_menu_item)
    val mediaRouteActionProvider =
            MenuItemCompat.getActionProvider(mediaRouteMenuItem) as MediaRouteActionProvider

    // Attach the MediaRouteSelector that you built in onCreate()
    selector?.also(mediaRouteActionProvider::setRouteSelector)

    // Return true to show the menu.
    return true
}

Java

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    super.onCreateOptionsMenu(menu);

    // Inflate the menu and configure the media router action provider.
    getMenuInflater().inflate(R.menu.sample_media_router_menu, menu);

    // Attach the MediaRouteSelector to the menu item
    MenuItem mediaRouteMenuItem = menu.findItem(R.id.media_route_menu_item);
    MediaRouteActionProvider mediaRouteActionProvider =
            (MediaRouteActionProvider)MenuItemCompat.getActionProvider(
            mediaRouteMenuItem);
    // Attach the MediaRouteSelector that you built in onCreate()
    mediaRouteActionProvider.setRouteSelector(selector);

    // Return true to show the menu.
    return true;
}

لمزيد من المعلومات حول تنفيذ شريط الإجراءات في تطبيقك، راجع دليل مطوّر برامج شريط الإجراءات.

يمكنك أيضًا إضافة زر توجيه الوسائط كزر MediaRouteButton في أي طريقة عرض. يجب إرفاق MediaRouteSelector بالزر باستخدام طريقة setRouteSelector(). يمكنك الاطّلاع على قائمة التحقق من تصميم Google Cast للحصول على إرشادات حول دمج زر توجيه الوسائط في تطبيقك.

استدعاءات MediaRouter

وتتشارك جميع التطبيقات التي يتم تشغيلها على الجهاز نفسه مثيل MediaRouter واحدًا ومساراته (تمّت فلترة كل تطبيق من خلال MediaRouteSelector في التطبيق). يتواصل كل نشاط مع جهاز MediaRouter باستخدام تنفيذه لطرق MediaRouter.Callback الخاص به. يستدعي جهاز MediaRouter طرق معاودة الاتصال كلما اختار المستخدم مسارًا أو غيّره أو يلغي ربطه.

تتوفّر عدة طرق في معاودة الاتصال يمكنك إلغاءها لتلقّي معلومات حول توجيه الأحداث. ويجب أن يؤدي تنفيذ الفئة MediaRouter.Callback على الأقل إلى إلغاء الترميزَين onRouteSelected() وonRouteUnselected().

بما أنّ MediaRouter هو مورد مشترك، يحتاج تطبيقك إلى إدارة استدعاءات MediaRouter استجابةً لعمليات استدعاء دورة حياة النشاط المعتادة:

  • عند إنشاء النشاط (onCreate(Bundle))، مرِّر مؤشر الماوس إلى MediaRouter واحتفظ به طوال فترة استخدام التطبيق.
  • إرفاق معاودة الاتصال بجهاز MediaRouter عندما يصبح النشاط مرئيًا (onStart())، وفصلها عندما يكون النشاط مخفيًا (onStop()).

يوضّح الرمز النموذجي التالي طريقة إنشاء كائن معاودة الاتصال وحفظه وكيفية الحصول على مثيل MediaRouter وكيفية إدارة عمليات الاستدعاء. لاحِظ استخدام علامة CALLBACK_FLAG_REQUEST_DISCOVERY عند إرفاق عمليات معاودة الاتصال في onStart(). يسمح هذا الإجراء لـ MediaRouteSelector بإعادة تحميل قائمة المسارات المتاحة لزر توجيه الوسائط.

Kotlin

class MediaRouterPlaybackActivity : AppCompatActivity() {

    private var mediaRouter: MediaRouter? = null
    private var mSelector: MediaRouteSelector? = null

    // Variables to hold the currently selected route and its playback client
    private var mRoute: MediaRouter.RouteInfo? = null
    private var remotePlaybackClient: RemotePlaybackClient? = null

    // Define the Callback object and its methods, save the object in a class variable
    private val mediaRouterCallback = object : MediaRouter.Callback() {

        override fun onRouteSelected(router: MediaRouter, route: MediaRouter.RouteInfo) {
            Log.d(TAG, "onRouteSelected: route=$route")
            if (route.supportsControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)) {
                // Stop local playback (if necessary)
                // ...

                // Save the new route
                mRoute = route

                // Attach a new playback client
                remotePlaybackClient =
                    RemotePlaybackClient(this@MediaRouterPlaybackActivity, mRoute)

                // Start remote playback (if necessary)
                // ...
            }
        }

        override fun onRouteUnselected(
                router: MediaRouter,
                route: MediaRouter.RouteInfo,
                reason: Int
        ) {
            Log.d(TAG, "onRouteUnselected: route=$route")
            if (route.supportsControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)) {

                // Changed route: tear down previous client
                mRoute?.also {
                    remotePlaybackClient?.release()
                    remotePlaybackClient = null
                }

                // Save the new route
                mRoute = route

                when (reason) {
                    MediaRouter.UNSELECT_REASON_ROUTE_CHANGED -> {
                        // Resume local playback (if necessary)
                        // ...
                    }
                }
            }
        }
    }


    // Retain a pointer to the MediaRouter
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // Get the media router service.
        mediaRouter = MediaRouter.getInstance(this)
        ...
    }

    // Use this callback to run your MediaRouteSelector to generate the
    // list of available media routes
    override fun onStart() {
        mSelector?.also { selector ->
            mediaRouter?.addCallback(selector, mediaRouterCallback,
                    MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY)
        }
        super.onStart()
    }

    // Remove the selector on stop to tell the media router that it no longer
    // needs to discover routes for your app.
    override fun onStop() {
        mediaRouter?.removeCallback(mediaRouterCallback)
        super.onStop()
    }
    ...
}

Java

public class MediaRouterPlaybackActivity extends AppCompatActivity {
    private MediaRouter mediaRouter;
    private MediaRouteSelector mSelector;

    // Variables to hold the currently selected route and its playback client
    private MediaRouter.RouteInfo mRoute;
    private RemotePlaybackClient remotePlaybackClient;

    // Define the Callback object and its methods, save the object in a class variable
    private final MediaRouter.Callback mediaRouterCallback =
            new MediaRouter.Callback() {

        @Override
        public void onRouteSelected(MediaRouter router, RouteInfo route) {
            Log.d(TAG, "onRouteSelected: route=" + route);

            if (route.supportsControlCategory(
                MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)){
                // Stop local playback (if necessary)
                // ...

                // Save the new route
                mRoute = route;

                // Attach a new playback client
                remotePlaybackClient = new RemotePlaybackClient(this, mRoute);

                // Start remote playback (if necessary)
                // ...
            }
        }

        @Override
        public void onRouteUnselected(MediaRouter router, RouteInfo route, int reason) {
            Log.d(TAG, "onRouteUnselected: route=" + route);

            if (route.supportsControlCategory(
                MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)){

                // Changed route: tear down previous client
                if (mRoute != null && remotePlaybackClient != null) {
                    remotePlaybackClient.release();
                    remotePlaybackClient = null;
                }

                // Save the new route
                mRoute = route;

                if (reason != MediaRouter.UNSELECT_REASON_ROUTE_CHANGED) {
                    // Resume local playback  (if necessary)
                    // ...
                }
            }
        }
    }


    // Retain a pointer to the MediaRouter
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Get the media router service.
        mediaRouter = MediaRouter.getInstance(this);
        ...
    }

    // Use this callback to run your MediaRouteSelector to generate the list of available media routes
    @Override
    public void onStart() {
        mediaRouter.addCallback(mSelector, mediaRouterCallback,
                MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY);
        super.onStart();
    }

    // Remove the selector on stop to tell the media router that it no longer
    // needs to discover routes for your app.
    @Override
    public void onStop() {
        mediaRouter.removeCallback(mediaRouterCallback);
        super.onStop();
    }
    ...
}

يوفّر إطار عمل جهاز توجيه الوسائط أيضًا فئة MediaRouteDiscoveryFragment تركّز على إضافة وإزالة معاودة الاتصال لنشاط ما.

ملاحظة: إذا كنت تكتب تطبيقًا لتشغيل الموسيقى وتريد في التطبيق تشغيل الموسيقى أثناء تشغيله في الخلفية، يجب إنشاء Service للتشغيل واستدعاء إطار عمل موجِّه الوسائط من خلال عمليات استدعاء مراحل نشاط الخدمة.

التحكم في مسار تشغيل عن بُعد

عند اختيار مسار تشغيل عن بُعد، يعمل تطبيقك كوحدة تحكّم عن بُعد. ويعالج الجهاز في الطرف الآخر من المسار جميع وظائف استرداد بيانات المحتوى وفك ترميزها وتشغيلها. تتواصل عناصر التحكّم في واجهة المستخدم الخاصة بتطبيقك مع جهاز الاستقبال باستخدام عنصر RemotePlaybackClient.

توفّر الفئة RemotePlaybackClient طرقًا إضافية لإدارة تشغيل المحتوى. إليك بعض طُرق التشغيل الأساسية من الفئة RemotePlaybackClient:

  • play(): يمكنك تشغيل ملف وسائط محدّد يتم تحديده من خلال Uri.
  • pause(): يمكنك إيقاف ملف الوسائط الذي يتم تشغيله حاليًا مؤقتًا.
  • resume(): يمكنك مواصلة تشغيل المقطع الصوتي الحالي بعد توجيه طلب إيقاف مؤقت.
  • seek(): الانتقال إلى موضع محدّد في المسار الحالي
  • release() — قطع الاتصال بين التطبيق وجهاز التشغيل عن بُعد

يمكنك استخدام هذه الطرق لإرفاق الإجراءات بعناصر التحكّم في التشغيل التي توفّرها في تطبيقك. وتتيح لك معظم هذه الطرق أيضًا تضمين عنصر استدعاء حتى تتمكّن من مراقبة مستوى تقدّم مهمة التشغيل أو طلب التحكّم.

تتيح الفئة RemotePlaybackClient أيضًا إضافة عدة عناصر وسائط إلى "قائمة المحتوى التالي" من أجل تشغيل قائمة انتظار الوسائط وإدارتها.

نموذج التعليمات البرمجية

يوضّح نموذجا Android BasicMediaRouter وMediaRouter استخدام واجهة برمجة التطبيقات MediaRouter.