ภาพรวม MediaRouter

หากต้องการใช้เฟรมเวิร์ก MediaRouter ภายในแอป คุณต้องมีอินสแตนซ์ ของออบเจ็กต์ MediaRouter และแนบ MediaRouter.Callback ออบเจ็กต์สำหรับฟังเหตุการณ์การกำหนดเส้นทาง เนื้อหาที่ส่งผ่านเส้นทางสื่อจะต้องผ่าน MediaRouteProviderที่เกี่ยวข้อง (ยกเว้นในบางกรณีพิเศษ เช่น อุปกรณ์เอาต์พุตบลูทูธ) รูปที่ 1 แสดงมุมมองระดับสูงของ คลาสที่ใช้ในการกำหนดเส้นทางเนื้อหาระหว่างอุปกรณ์

รูปที่ 1 ภาพรวมของคลาสของเราเตอร์สื่อหลักๆ ที่แอปใช้

หมายเหตุ: หากต้องการให้แอปรองรับ อุปกรณ์ Google Cast คุณควรใช้ Cast SDK และสร้างแอปในฐานะผู้ส่ง Cast ทำตามคำแนะนำใน เอกสารประกอบเกี่ยวกับการแคสต์ แทนการใช้เฟรมเวิร์ก MediaRouter โดยตรง

ปุ่มเส้นทางสื่อ

แอป Android ควรใช้ปุ่มเส้นทางสื่อเพื่อควบคุมการกำหนดเส้นทางสื่อ เฟรมเวิร์ก MediaRouter มีอินเทอร์เฟซมาตรฐานสำหรับปุ่ม ซึ่งช่วยให้ผู้ใช้จดจำและใช้การกำหนดเส้นทาง เมื่อพร้อมให้บริการ ปุ่มเส้นทางสื่อมักจะอยู่ที่ด้านขวาของ แถบการดำเนินการของแอปตามที่แสดงในรูปที่ 2

รูปที่ 2 ปุ่มเส้นทางสื่อใน แถบการดำเนินการ

เมื่อผู้ใช้กดปุ่มเส้นทางสื่อ เส้นทางสื่อที่ใช้ได้จะปรากฏในรายการตามที่แสดงในรูปที่ 3

รูปที่ 3 รายการเส้นทางสื่อที่ใช้ได้ ซึ่งแสดงหลังจากกดปุ่มเส้นทางสื่อ

ทำตามขั้นตอนต่อไปนี้เพื่อสร้างปุ่มเส้นทางสื่อ

  1. ใช้ AppCompatActivity
  2. รายการในเมนูบนปุ่มเส้นทางสื่อ
  3. สร้าง MediaRouteSelector
  4. เพิ่มปุ่มเส้นทางสื่อลงในแถบการทำงาน
  5. สร้างและจัดการเมธอด MediaRouter.Callback ในวงจรของกิจกรรม

ส่วนนี้จะอธิบาย 4 ขั้นตอนแรก ส่วนถัดไปจะอธิบายถึงเมธอด Callback

ใช้ 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 เพื่อดูแนวทางในการรวมปุ่มเส้นทางสื่อไว้ในแอปพลิเคชันของคุณ

Callback ของ MediaRouter

แอปทั้งหมดที่ทำงานในอุปกรณ์เดียวกันแชร์อินสแตนซ์ MediaRouter เดียวและเส้นทางของอินสแตนซ์ (กรองตาม MediaRouteSelector ของแอปตาม MediaRouteSelector) แต่ละกิจกรรมจะสื่อสารกับ MediaRouter โดยใช้การใช้งาน MediaRouter.Callback ของตนเอง MediaRouter จะเรียกเมธอด Callback เมื่อใดก็ตามที่ผู้ใช้เลือก เปลี่ยน หรือยกเลิกการเชื่อมต่อเส้นทาง

มีหลายวิธีใน Callback ที่คุณสามารถลบล้างเพื่อรับข้อมูล การกำหนดเส้นทางเหตุการณ์ อย่างน้อยที่สุด การใช้คลาส MediaRouter.Callback ของคุณควรลบล้าง onRouteSelected() และ onRouteUnselected()

เนื่องจาก MediaRouter เป็นทรัพยากรที่แชร์ร่วมกัน แอปของคุณจึงต้องจัดการ Callback ของ MediaRouter เพื่อตอบสนอง Callback ในวงจรของกิจกรรมปกติ:

  • เมื่อสร้างกิจกรรม (onCreate(Bundle)) ให้ชี้ไปที่ MediaRouter ค้างไว้ตลอดอายุของแอป
  • แนบ Callback กับ MediaRouter เมื่อกิจกรรมปรากฏให้เห็น (onStart()) และปลดออกเมื่อซ่อนไว้ (onStop())

ตัวอย่างโค้ดต่อไปนี้จะแสดงวิธีการ สร้างและบันทึกออบเจ็กต์ Callback วิธีการ รับอินสแตนซ์ของ MediaRouter และวิธีจัดการ Callback โปรดสังเกตการใช้ Flag CALLBACK_FLAG_REQUEST_DISCOVERY เมื่อแนบ Callback ใน 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 ซึ่งจะดูแลการเพิ่มและ ซึ่งเป็นการนำ Callback ของกิจกรรมออก

หมายเหตุ: หากคุณเขียนแอปเล่นเพลงและต้องการให้แอปเล่น เล่นเพลงขณะอยู่เบื้องหลัง คุณต้องสร้าง Service สำหรับการเล่น และเรียกใช้เฟรมเวิร์กของเราเตอร์สื่อจาก Callback วงจรของบริการ

การควบคุมเส้นทางการเล่นระยะไกล

เมื่อคุณเลือกเส้นทางการเล่นระยะไกล แอปของคุณจะทำหน้าที่เป็นรีโมตคอนโทรล อุปกรณ์ที่อยู่ปลายเส้นทาง จัดการฟังก์ชันการดึงข้อมูล การถอดรหัส และการเล่นข้อมูลทั้งหมด การควบคุมใน UI ของแอปสื่อสารกับอุปกรณ์ที่เป็นผู้รับโดยใช้ ออบเจ็กต์ RemotePlaybackClient รายการ

คลาส RemotePlaybackClient มีวิธีเพิ่มเติม ในการจัดการการเล่นเนื้อหา วิธีเล่นหลักบางส่วนจากคลาส RemotePlaybackClient มีดังนี้

  • play() — เปิด ไฟล์สื่อที่ระบุโดย Uri
  • pause() — หยุด กำลังเล่นแทร็กสื่อ
  • resume() — ดำเนินการต่อ เล่นแทร็กปัจจุบันหลังจากคำสั่งหยุดชั่วคราว
  • seek() — ย้ายไปยัง ในแทร็กปัจจุบัน
  • release() — ฉีก จากแอปของคุณไปยังอุปกรณ์เล่นระยะไกล

คุณสามารถใช้วิธีการเหล่านี้เพื่อแนบการกระทำกับตัวควบคุมการเล่นที่ระบุไว้ใน แอป วิธีการเหล่านี้ส่วนใหญ่ยังให้คุณใส่ออบเจ็กต์ Callback เพื่อตรวจสอบ ความคืบหน้าของงานการเล่นหรือคำขอการควบคุม

คลาส RemotePlaybackClient ยังรองรับการจัดคิวของ รายการสื่อหลายรายการสำหรับการเล่นและจัดการคิวสื่อ

โค้ดตัวอย่าง

Android BasicMediaRouter และ MediaRouter ตัวอย่างเพิ่มเติมจะสาธิตการใช้งาน MediaRouter API