بدء استخدام CastPlayer

CastPlayer هو تنفيذ مشغّل في Jetpack Media3 يتيح التشغيل على الجهاز وإرسال المحتوى إلى جهاز بعيد متوافق مع Cast. CastPlayer تسهّل إضافة وظيفة البث إلى تطبيقك وتوفّر ميزات غنية تتيح التبديل بسلاسة بين التشغيل على الجهاز والتشغيل عن بُعد. يوضّح لك هذا الدليل كيفية دمج CastPlayer في تطبيق الوسائط.

لدمج Cast مع منصات أخرى، يُرجى الاطّلاع على حزمة تطوير البرامج (SDK) الخاصة بـ Cast.

الحصول على جهاز متوافق مع تكنولوجيا Google Cast

لاختبار CastPlayer، يجب أن يكون لديك جهاز متوافق مع Cast. تشمل الخيارات أجهزة Android TV وChromecast ومكبّرات الصوت الذكية والشاشات الذكية. تأكَّد من إعداد جهازك واتصاله بشبكة Wi-Fi نفسها التي يتصل بها جهازك الجوّال المخصّص للتطوير، وذلك لكي يتمكّن من رصد الجهاز.

إضافة تبعيات الإصدار

لبدء استخدام CastPlayer، أضِف تبعيتَي AndroidX Media3 وCastPlayer إلى ملف build.gradle الخاص بوحدة تطبيقك.

Kotlin

implementation("androidx.media3:media3-exoplayer:1.9.2")
implementation("androidx.media3:media3-ui:1.9.2")
implementation("androidx.media3:media3-session:1.9.2")
implementation("androidx.media3:media3-cast:1.9.2")

Groovy

implementation "androidx.media3:media3-exoplayer:1.9.2"
implementation "androidx.media3:media3-ui:1.9.2"
implementation "androidx.media3:media3-session:1.9.2"
implementation "androidx.media3:media3-cast:1.9.2"

ضبط CastPlayer

لضبط CastPlayer، عدِّل ملف AndroidManifest.xml باستخدام موفّر خيارات.

موفّر الخيارات

يتطلّب CastPlayer توفُّر موفّر خيارات لضبط سلوكه. لإجراء عملية إعداد أساسية، يمكنك استخدام DefaultCastOptionsProvider من خلال إضافته إلى ملف AndroidManifest.xml. يستخدم هذا الإجراء الإعدادات التلقائية، بما في ذلك تطبيق الاستقبال التلقائي.

<application>
  ...
  <meta-data
    android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
    android:value="androidx.media3.cast.DefaultCastOptionsProvider" />
  ...
</application>

لتخصيص الإعدادات، نفِّذ OptionsProvider مخصّصًا. اطّلِع على دليل خيارات الإرسال للتعرّف على كيفية إجراء ذلك.

إضافة جهاز استقبال لعمليات نقل الوسائط

تتيح إضافة MediaTransferReceiver إلى ملف البيان لواجهة مستخدم النظام إمكانية اكتشاف الأجهزة المتوافقة مع Cast على الشبكة وإعادة توجيه الوسائط بدون فتح نشاط التطبيق. على سبيل المثال، يمكن للمستخدم تغيير الجهاز الذي يتم تشغيل وسائط تطبيقك عليه من خلال إشعار الوسائط.

<application>
  ...
  <receiver android:name="androidx.mediarouter.media.MediaTransferReceiver" />
  ...
</application>

إنشاء CastPlayer

لإتاحة التشغيل عن بُعد باستخدام Cast، يجب أن يتمكّن تطبيقك من إدارة التشغيل حتى عندما لا يتفاعل المستخدم مع نشاط من تطبيقك، مثلاً من خلال إشعار الوسائط في النظام. لهذا السبب، عليك إنشاء مثيلات ExoPlayer (للتشغيل المحلي) وCastPlayer (للتشغيل عن بُعد) في إحدى الخدمات، مثل MediaSessionService أو MediaLibraryService. أولاً، أنشئ مثيل ExoPlayer، ثم اضبط ExoPlayer ليكون مثيل اللاعب المحلي عند إنشاء مثيل CastPlayer. يمكنك بعد ذلك التبديل بين تشغيل الوسائط على جهازك الجوّال والجهاز المتوافق مع البث من خلال إشعار الوسائط أو إشعار شاشة القفل. تستخدم Media3 ميزة مبدّل الإخراج للتعامل مع عمليات نقل المشغّل عند تغيير مسار الإخراج من محلي إلى بعيد أو من بعيد إلى محلي.

لقطة شاشة تعرض واجهة مستخدم &quot;مبدّل الإخراج&quot; في الإشعارات
الشكل 1: (أ) شريحة الجهاز في إشعار الوسائط (ب) الأجهزة المتوافقة مع ميزة "البث" التي تظهر عند النقر على شريحة الجهاز (ج) شريحة الجهاز في إشعار شاشة القفل

Kotlin

override fun onCreate() {
  super.onCreate()

  val exoPlayer = ExoPlayer.Builder(context).build()
  val castPlayer = CastPlayer.Builder(context)
      .setLocalPlayer(exoPlayer)
      .build()

  mediaSession = MediaSession.Builder(context, castPlayer).build()
}

Java

@Override
public void onCreate() {
  super.onCreate();

  ExoPlayer exoPlayer = new ExoPlayer.Builder(context).build();
  CastPlayer castPlayer = new CastPlayer.Builder(context)
      .setLocalPlayer(exoPlayer)
      .build();

  mediaSession = new MediaSession.Builder(
    /* context= */ context, /* player= */ castPlayer).build();
}

إضافة عناصر واجهة المستخدم

أضِف MediaRouteButton إلى واجهة مستخدم تطبيقك. يؤدي النقر على MediaRouteButton إلى فتح مربّع حوار يعرض قائمة بالأجهزة المتوافقة مع Cast والمتاحة على الشبكة. عندما يختار المستخدم جهازًا، يتم نقل تشغيل الوسائط من الجهاز الجوّال إلى جهاز الاستقبال الذي تم اختياره. يوضّح لك هذا القسم كيفية إضافة الزر والاستماع إلى الأحداث لتعديل واجهة المستخدم عند التبديل بين الأجهزة المحلية والأجهزة البعيدة.

ضبط MediaRouteButton

هناك أربع طرق لإضافة MediaRouteButton إلى واجهة المستخدم الخاصة بنشاطك. ويعتمد الخيار الأفضل على تصميم تطبيقك ومتطلباته.

  • Compose UI: أضِف عنصرًا قابلاً للإنشاء خاصًا بالزر.
  • واجهة مستخدم "المشاهدات":
    • أضِف الزر إلى قائمة شريط التطبيق.
    • أضِف الزر داخل PlayerView.
    • أضِف الزر كـ View عادي.
لقطة شاشة تعرض MediaRouteButton في واجهة المستخدم
الشكل 2: (أ) MediaRouteButton في شريط القوائم، (ب) كطريقة عرض، (ج) في PlayerView، و (د) مربّع حوار للأجهزة المتوافقة مع ميزة "البث".

إضافة Composable MediaRouteButton إلى "مشغّل الفيديو"

يمكنك إضافة عنصر MediaRouteButton القابل للإنشاء إلى واجهة مستخدم المشغّل. لمزيد من المعلومات، راجِع دليل إنشاء.

Kotlin

import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.media3.cast.MediaRouteButton

@Composable
fun PlayerComposeView(player: Player, modifier: Modifier = Modifier) {
  var controlsVisible by remember { mutableStateOf(false) }

  Box(
    modifier = modifier.clickable { controlsVisible = true },
    contentAlignment = Alignment.Center,
  ) {
    PlayerSurface(player = player, modifier = modifier)
    AnimatedVisibility(visible = controlsVisible, enter = fadeIn(), exit = fadeOut()) {
      Box(modifier = Modifier.fillMaxSize()) {
        MediaRouteButton(modifier = Modifier.align(Alignment.TopEnd))
        PrimaryControls(player = player, modifier = Modifier.align(Alignment.Center))
      }
    }
  }
}

@Composable
fun PrimaryControls(player: Player, modifier: Modifier = Modifier) {
  ...
}

إضافة MediaRouteButton إلى PlayerView

يمكنك إضافة MediaRouteButton مباشرةً ضمن عناصر تحكّم واجهة المستخدم في PlayerView. بعد ضبط MediaController كمشغّل PlayerView، قدِّم MediaRouteButtonViewProvider لعرض زر البث على المشغّل.

Kotlin

override fun onStart() {
  super.onStart()

  playerView.player = mediaController
  playerView.setMediaRouteButtonViewProvider(MediaRouteButtonViewProvider())
}

Java

@Override
public void onStart() {
  super.onStart();

  playerView.setPlayer(mediaController);
  playerView.setMediaRouteButtonViewProvider(new MediaRouteButtonViewProvider());
}

أضِف MediaRouteButton إلى قائمة شريط التطبيق

لإعداد MediaRouteButton في قائمة شريط التطبيق، أنشئ قائمة XML وأعِد تعريف onCreateOptionsMenu في Activity.

<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:showAsAction="always"
    app:actionProviderClass="androidx.mediarouter.app.MediaRouteActionProvider"/>
</menu>

Kotlin

override fun onCreateOptionsMenu(menu: Menu): Boolean {
    ...
    menuInflater.inflate(R.menu.sample_media_route_button_menu, menu)
    val menuItemFuture: ListenableFuture<MenuItem> =
        MediaRouteButtonFactory.setUpMediaRouteButton(
            context, menu, R.id.media_route_menu_item)
    Futures.addCallback(
        menuItemFuture,
        object : FutureCallback<MenuItem> {
            override fun onSuccess(menuItem: MenuItem?) {
                // Do something with the menu item.
            }

            override fun onFailure(t: Throwable) {
                // Handle the failure.
            }
        },
        executor)
    ...
}

Java

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    ...
    getMenuInflater().inflate(R.menu.sample_media_route_button_menu, menu);
    ListenableFuture<MenuItem> menuItemFuture =
        MediaRouteButtonFactory.setUpMediaRouteButton(
          context, menu, R.id.media_route_menu_item);
    Futures.addCallback(
        menuItemFuture,
        new FutureCallback<MenuItem>() {
          @Override
          public void onSuccess(MenuItem menuItem) {
            // Do something with the menu item.
          }

          @Override
          public void onFailure(Throwable t) {
            // Handle the failure.
          }
        },
        executor);
    ...
}

إضافة MediaRouteButton كطريقة عرض

يمكنك إعداد MediaRouteButton في ملف activity layout.xml.

  <androidx.mediarouter.app.MediaRouteButton
      android:id="@+id/media_route_button"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      app:mediaRouteButtonTint="@android:color/white" />

لإكمال عملية إعداد MediaRouteButton، استخدِم MediaRouteButtonFactory Media3 Cast في رمز Activity.

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
  super.onCreate(savedInstanceState)

  findViewById<MediaRouteButton>(R.id.media_route_button)?.also {
    val unused = MediaRouteButtonFactory.setUpMediaRouteButton(context, it)
  }
}

Java

@Override
public void onCreate(Bundle savedInstanceState) {
    ...
    MediaRouteButton button = findViewById(R.id.media_route_button);
    ListenableFuture<Void> setUpFuture =
        MediaRouteButtonFactory.setUpMediaRouteButton(context, button);
}

أداة معالجة النشاط

أنشئ Player.Listener في Activity للاستماع إلى التغييرات في الموقع الجغرافي لتشغيل الوسائط. عندما يتغيّر playbackType بين PLAYBACK_TYPE_LOCAL وPLAYBACK_TYPE_REMOTE، يمكنك تعديل واجهة المستخدم حسب الحاجة. لمنع تسرُّب الذاكرة وحصر نشاط أداة معالجة الأحداث في الحالات التي يكون فيها تطبيقك مرئيًا فقط، عليك تسجيل أداة معالجة الأحداث في onStart وإلغاء تسجيلها في onStop:

Kotlin

import androidx.media3.common.DeviceInfo
import androidx.media3.common.Player

private val playerListener: Player.Listener =
  object : Player.Listener {
    override fun onDeviceInfoChanged(deviceInfo: DeviceInfo) {
      if (deviceInfo.playbackType == DeviceInfo.PLAYBACK_TYPE_LOCAL) {
        // Add UI changes for local playback.
      } else if (deviceInfo.playbackType == DeviceInfo.PLAYBACK_TYPE_REMOTE) {
        // Add UI changes for remote playback.
      }
    }
  }

override fun onStart() {
  super.onStart()
  mediaController.addListener(playerListener)
}

override fun onStop() {
  super.onStop()
  mediaController.removeListener(playerListener)
}

Java

import androidx.media3.common.DeviceInfo;
import androidx.media3.common.Player;

private Player.Listener playerListener =
    new Player.Listener() {
      @Override
      public void onDeviceInfoChanged(DeviceInfo deviceInfo) {
        if (deviceInfo.playbackType == DeviceInfo.PLAYBACK_TYPE_LOCAL) {
          // Add UI changes for local playback.
        } else if (deviceInfo.playbackType == DeviceInfo.PLAYBACK_TYPE_REMOTE) {
          // Add UI changes for remote playback.
        }
      }
    };

@Override
protected void onStart() {
  super.onStart();
  mediaController.addListener(playerListener);
}

@Override
protected void onStop() {
  super.onStop();
  mediaController.removeListener(playerListener);
}

لمزيد من المعلومات حول الاستماع إلى أحداث التشغيل والاستجابة لها، يُرجى الاطّلاع على دليل أحداث المشغّل.