نمای کلی MediaRouteProvider

چارچوب روتر رسانه اندروید به سازندگان اجازه می دهد تا از طریق یک رابط استاندارد به نام MediaRouteProvider ، پخش را در دستگاه های خود فعال کنند. یک ارائه‌دهنده مسیر، یک رابط مشترک برای پخش رسانه در دستگاه گیرنده تعریف می‌کند، که امکان پخش رسانه روی تجهیزات شما را از هر برنامه Android که از مسیرهای رسانه پشتیبانی می‌کند، ممکن می‌سازد.

این راهنما نحوه ایجاد یک ارائه دهنده مسیر رسانه برای دستگاه گیرنده و در دسترس قرار دادن آن برای سایر برنامه های پخش رسانه ای که در Android اجرا می شوند را مورد بحث قرار می دهد. برای استفاده از این API، باید با کلاس های کلیدی MediaRouteProvider ، MediaRouteProviderDescriptor و RouteController آشنا باشید.

نمای کلی

چارچوب مسیریاب رسانه اندروید به توسعه دهندگان برنامه های رسانه و سازندگان دستگاه های پخش رسانه امکان می دهد از طریق یک API مشترک و رابط کاربری مشترک متصل شوند. توسعه‌دهندگان برنامه‌ای که رابط MediaRouter را پیاده‌سازی می‌کنند، می‌توانند به فریم‌ورک متصل شوند و محتوا را با دستگاه‌هایی که در چارچوب روتر رسانه مشارکت دارند پخش کنند. سازندگان دستگاه‌های پخش رسانه می‌توانند با انتشار MediaRouteProvider در چارچوب شرکت کنند که به برنامه‌های کاربردی دیگر اجازه می‌دهد به دستگاه‌های گیرنده متصل شده و رسانه را پخش کنند. شکل 1 نشان می دهد که چگونه یک برنامه از طریق چارچوب روتر رسانه به دستگاه گیرنده متصل می شود.

شکل 1. بررسی اجمالی نحوه ارتباط کلاس های ارائه دهنده مسیر رسانه از یک برنامه رسانه به یک دستگاه گیرنده.

هنگامی که یک ارائه دهنده مسیر رسانه برای دستگاه گیرنده خود می سازید، ارائه دهنده اهداف زیر را انجام می دهد:

  • قابلیت‌های دستگاه گیرنده را توصیف و منتشر کنید تا سایر برنامه‌ها بتوانند آن را کشف کرده و از ویژگی‌های پخش آن استفاده کنند.
  • رابط برنامه نویسی دستگاه گیرنده و مکانیسم های انتقال ارتباط آن را بپیچید تا دستگاه با چارچوب روتر رسانه سازگار شود.

توزیع ارائه دهندگان مسیر

یک ارائه دهنده مسیر رسانه به عنوان بخشی از یک برنامه Android توزیع می شود. ارائه‌دهنده مسیر شما می‌تواند با گسترش MediaRouteProviderService یا بسته‌بندی اجرای MediaRouteProvider با سرویس خود و اعلام فیلتر قصد برای ارائه‌دهنده مسیر رسانه در دسترس سایر برنامه‌ها قرار گیرد. این مراحل به سایر برنامه‌ها امکان می‌دهد مسیر رسانه شما را کشف کرده و از آن استفاده کنند.

توجه: برنامه حاوی ارائه‌دهنده مسیر رسانه نیز می‌تواند یک رابط MediaRouter برای ارائه‌دهنده مسیر داشته باشد، اما این مورد نیاز نیست.

کتابخانه پشتیبانی MediaRouter

API های روتر رسانه در کتابخانه MediaRouter AndroidX تعریف شده اند. شما باید این کتابخانه را به پروژه توسعه برنامه خود اضافه کنید. برای اطلاعات بیشتر در مورد افزودن کتابخانه های پشتیبانی به پروژه خود، به راه اندازی کتابخانه پشتیبانی مراجعه کنید.

احتیاط: حتماً از اجرای 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>

این مثال مانیفست سرویسی را اعلام می‌کند که کلاس‌های ارائه‌دهنده مسیر رسانه واقعی را می‌پیچد. چارچوب مسیریاب رسانه اندروید، کلاس MediaRouteProviderService را برای استفاده به عنوان پوشش سرویس برای ارائه دهندگان مسیر رسانه فراهم می کند. کد مثال زیر نحوه استفاده از این کلاس wrapper را نشان می دهد:

کاتلین

class SampleMediaRouteProviderService : MediaRouteProviderService() {

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

جاوا

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 اضافه می‌کنید:

کاتلین

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

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

جاوا

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) را نشان می دهد:

کاتلین

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)
        }
    }
    ...
}

جاوا

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);
        }
    }
}

کنترل های پخش

ارائه‌دهنده مسیر رسانه‌ای که پخش از راه دور ارائه می‌دهد باید انواع کنترل‌های رسانه‌ای را که پشتیبانی می‌کند مشخص کند. اینها انواع کلی کنترلی هستند که مسیرهای رسانه می توانند ارائه دهند:

  • کنترل‌های پخش ، مانند پخش، مکث، عقب و جلو بردن سریع.
  • ویژگی‌های صف ، که به برنامه ارسال اجازه می‌دهد مواردی را از فهرست پخشی که توسط دستگاه گیرنده نگهداری می‌شود، اضافه و حذف کند.
  • ویژگی‌های جلسه ، که با ارائه شناسه جلسه به برنامه درخواست‌کننده و سپس بررسی آن شناسه با هر درخواست کنترل پخش بعدی، از تداخل برنامه‌های ارسال با یکدیگر جلوگیری می‌کند.

مثال کد زیر نحوه ساخت یک فیلتر هدف برای پشتیبانی از کنترل‌های اصلی پخش مسیر رسانه را نشان می‌دهد:

کاتلین

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)
        }
    }
    ...
}

جاوا

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 ، سپس می توانید یک شی توصیف کننده برای انتشار در چارچوب روتر رسانه اندروید ایجاد کنید. این شی توصیفگر شامل ویژگی‌های قابلیت‌های مسیر رسانه شما است تا سایر برنامه‌ها بتوانند نحوه تعامل با مسیر رسانه شما را تعیین کنند.

کد مثال زیر نحوه اضافه کردن فیلترهای قصد ایجاد شده قبلی را به MediaRouteProviderDescriptor و تنظیم توصیفگر برای استفاده توسط چارچوب روتر رسانه را نشان می دهد:

کاتلین

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
    }
    ...
}

جاوا

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() هدایت می شوند. اجرای شما از این روش باید درخواست های کنترل را تجزیه کند و به آنها پاسخ مناسب دهد. در اینجا نمونه ای از پیاده سازی این روش است که دستورات را برای یک مسیر رسانه پخش از راه دور پردازش می کند:

کاتلین

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
        }
    }
    ...
}

جاوا

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 به عنوان پوششی برای API برای تجهیزات پخش رسانه شما در نظر گرفته شده است. پیاده سازی متدهای این کلاس کاملاً به رابط برنامه نویسی ارائه شده توسط دستگاه گیرنده شما بستگی دارد.

کد نمونه

نمونه MediaRouter نحوه ایجاد یک ارائه دهنده مسیر رسانه سفارشی را نشان می دهد.