يوضّح دليل المطوِّر هذا كيفية إضافة دعم Google Cast إلى جهاز Android. المستخدم المرسل باستخدام حزمة SDK التي تخص Android Sender.
فإن الجهاز الجوّال أو الكمبيوتر المحمول هو المرسِل الذي يتحكم في تشغيل الفيديو جهاز Google Cast هو جهاز الاستقبال الذي يعرض المحتوى على التلفزيون.
يشير إطار عمل المُرسِل إلى البرنامج الثنائي لمكتبة فئات Cast والربط الموارد الموجودة في وقت التشغيل لدى المرسل. تطبيق المرسِل أو تطبيق البث إلى تطبيق يعمل على المُرسِل أيضًا تطبيق WebRecipient يشير إلى تطبيق HTML الذي يعمل على الجهاز الذي يعمل بتكنولوجيا Google Cast
يستخدم إطار عمل المرسِل تصميمًا غير متزامن لمعاودة الاتصال لإعلام المُرسِل للأحداث والانتقال بين الحالات المختلفة لحياة تطبيق Cast الدورة.
مسار التطبيق
تصف الخطوات التالية مسار التنفيذ عالي المستوى النموذجي للمرسل تطبيق Android:
- يبدأ إطار عمل Google Cast تلقائيًا
MediaRouter
اكتشاف الجهاز استنادًا إلى دورة حياةActivity
. - عندما ينقر المستخدم على زر البث، يعرض إطار العمل خيار البث. مربّع حوار يتضمّن قائمة أجهزة البث التي تم اكتشافها
- عندما يختار المستخدم جهاز بث، يحاول إطار العمل تشغيل تطبيق WebRecipient على جهاز البث.
- يستدعي إطار العمل استدعاءات في تطبيق المرسِل للتأكد من أن الويب تم تشغيل تطبيق جهاز الاستقبال.
- يعمل هذا الإطار على إنشاء قناة اتصال بين المرسل والويب تطبيقات الاستقبال.
- يستخدم إطار العمل قناة الاتصال لتحميل الوسائط والتحكّم فيها في جهاز استقبال الويب.
- يعمل إطار العمل على مزامنة حالة تشغيل الوسائط بين المرسِل مستقبل الويب: عندما ينفِّذ المستخدم إجراءات على واجهة المستخدم للمرسل، يتم تمرير إطار العمل طلبات التحكم في الوسائط هذه إلى مستقبل الويب، وعندما يكون جهاز استقبال الويب يرسل تحديثات حالة الوسائط، يعدِّل إطار العمل حالة واجهة مستخدم المُرسِل.
- عندما ينقر المستخدم على زر البث لقطع الاتصال بجهاز البث، سيعمل إطار العمل على إلغاء ربط تطبيق المرسل من مستقبل الويب.
للحصول على قائمة شاملة بجميع الفئات والأساليب والأحداث في Google Cast حزمة تطوير البرامج (SDK) لنظام التشغيل Android، يُرجى الاطّلاع على مرجع واجهة برمجة التطبيقات Google Cast Sender API Android تتناول الأقسام التالية خطوات إضافة ميزة "البث" إلى تطبيق Android.
إعداد بيان Android
يتطلّب ملف AndroidManifest.xml في تطبيقك ضبط ما يلي: عناصر حزمة تطوير البرامج (SDK) للإرسال:
uses-sdk
يمكنك ضبط الحدّ الأدنى لمستويات واجهة برمجة تطبيقات Android التي تتوافق مع حزمة تطوير البرامج (SDK) الخاصة بالبث. الحد الأدنى حاليًا هو المستوى 23 من واجهة برمجة التطبيقات والهدف هو المستوى 34 من واجهة برمجة التطبيقات.
<uses-sdk
android:minSdkVersion="23"
android:targetSdkVersion="34" />
android:theme
يمكنك ضبط مظهر تطبيقك استنادًا إلى الحد الأدنى لإصدار حزمة تطوير البرامج (SDK) لنظام التشغيل Android. على سبيل المثال، إذا
لم تكن تنفّذ المظهر الخاص بك، يجب عليك استخدام صيغة من
Theme.AppCompat
عند استهداف إصدار أقل من حزمة تطوير البرامج (SDK) لنظام التشغيل Android
قبل Lollipop.
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/Theme.AppCompat" >
...
</application>
إعداد سياق البث
ويحتوي إطار العمل على كائن سينغلتون عام، CastContext
، وينسق
جميع تفاعلات إطار العمل.
يجب أن ينفِّذ تطبيقك
OptionsProvider
لتوفير الخيارات اللازمة لتهيئة
CastContext
سينغلتون. يقدم OptionsProvider
مثيلاً
CastOptions
والذي يحتوي على خيارات تؤثر في سلوك إطار العمل. الأكثر
من هذه الأشياء هو معرّف تطبيق جهاز استقبال الويب الذي يُستخدم لتصفية
نتائج الاكتشاف وتشغيل تطبيق "جهاز استقبال الويب" عندما تكون جلسة البث
البدء.
class CastOptionsProvider : OptionsProvider { override fun getCastOptions(context: Context): CastOptions { return Builder() .setReceiverApplicationId(context.getString(R.string.app_id)) .build() } override fun getAdditionalSessionProviders(context: Context): List<SessionProvider>? { return null } }
public class CastOptionsProvider implements OptionsProvider { @Override public CastOptions getCastOptions(Context context) { CastOptions castOptions = new CastOptions.Builder() .setReceiverApplicationId(context.getString(R.string.app_id)) .build(); return castOptions; } @Override public List<SessionProvider> getAdditionalSessionProviders(Context context) { return null; } }
يجب الإفصاح عن اسم OptionsProvider
الذي تم تنفيذه بالكامل.
كحقل للبيانات الوصفية في ملف AndroidManifest.xml لتطبيق المرسِل:
<application>
...
<meta-data
android:name=
"com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
android:value="com.foo.CastOptionsProvider" />
</application>
يتم إعداد CastContext
بشكل كسول عند استخدام CastContext.getSharedInstance()
البيانات.
class MyActivity : FragmentActivity() { override fun onCreate(savedInstanceState: Bundle?) { val castContext = CastContext.getSharedInstance(this) } }
public class MyActivity extends FragmentActivity { @Override public void onCreate(Bundle savedInstanceState) { CastContext castContext = CastContext.getSharedInstance(this); } }
تطبيقات Cast UX Widgets
يوفّر إطار عمل Google Cast التطبيقات المصغّرة التي تتوافق مع تصميم Cast. قائمة التحقق:
محتوى تمهيدي: ويوفر إطار العمل طريقة عرض مخصّصة
IntroductoryOverlay
، يظهر للمستخدم لجذب الانتباه إلى زر البث يكون الجهاز متاحًا لأوّل مرة. يمكن لتطبيق Sender تخصيص نص العنوان وموضعه النص.زر البث: يكون زر البث مرئيًا بغض النظر عمّا إذا كانت أجهزة البث متوفّرة. عندما ينقر المستخدم لأول مرة على زر البث، يتم عرض مربّع حوار البث. التي تسرد الأجهزة التي تم اكتشافها. عندما ينقر المستخدم على زر البث أثناء اتصال الجهاز، يعرض البيانات الوصفية الحالية للوسائط (مثل عنوان استوديو التسجيل واسمه وصورة مصغّرة) أو يسمح للمستخدم إلغاء الربط بجهاز البث. "زر البث" يُشار إليه أحيانًا كـ "رمز البث".
وحدة تحكُّم صغيرة: عندما يبثّ المستخدم المحتوى وينتقل من المحتوى الحالي المحتوى أو وحدة التحكم الموسعة إلى شاشة أخرى في تطبيق المرسل، وحدة تحكم صغيرة أسفل الشاشة للسماح للمستخدم الاطّلاع على البيانات الوصفية للوسائط التي يتم بثها حاليًا والتحكّم في تشغيلها.
وحدة تحكم موسّعة: عندما يبث المستخدم المحتوى، إذا نقر على إشعار الوسائط أو وحدة تحكم مصغرة، يتم إطلاق وحدة التحكم الموسعة، والتي تعرض بيانات تعريف الوسائط التي تشغِّل حاليًا البيانات الوصفية وتوفّر عدة أزرار للتحكّم تشغيل الوسائط.
الإشعار: على أجهزة Android فقط. عندما يبث المستخدم المحتوى وينتقل بعيدًا عن سيظهر إشعار وسائط يعرض المحتوى الذي يتم بثّه حاليًا البيانات الوصفية للوسائط وعناصر التحكم في التشغيل.
شاشة القفل: على أجهزة Android فقط. عندما يبث المستخدم المحتوى ويتنقل فيه (أو على الجهاز) مهلة) إلى شاشة القفل، فسيتم عرض عنصر تحكم في شاشة قفل الوسائط البيانات الوصفية للوسائط التي يتم بثها حاليًا وعناصر التحكّم في التشغيل.
يتضمن الدليل التالي أوصافًا حول كيفية إضافة هذه الأدوات إلى تطبيقك.
إضافة زر بث
نظام التشغيل Android
MediaRouter
تم تصميم واجهات برمجة التطبيقات لإتاحة عرض الوسائط وتشغيلها على الأجهزة الثانوية.
يجب أن تتضمّن تطبيقات Android التي تستخدم واجهة برمجة التطبيقات MediaRouter
زر البث كجزء من هذه الميزة.
من واجهة المستخدم، للسماح للمستخدمين باختيار مسار وسائط لتشغيل الوسائط عليه
جهاز ثانوي مثل جهاز بث
يجعل إطار العمل إضافة
MediaRouteButton
كـ
Cast button
أمرًا سهلاً للغاية. يجب أولاً إضافة صنف في القائمة أو MediaRouteButton
في ملف xml
ملف يحدد القائمة، ويمكنك استخدام
CastButtonFactory
لربطه بإطار العمل.
// To add a Cast button, add the following snippet.
// menu.xml
<item
android:id="@+id/media_route_menu_item"
android:title="@string/media_route_menu_title"
app:actionProviderClass="androidx.mediarouter.app.MediaRouteActionProvider"
app:showAsAction="always" />
// Then override the onCreateOptionMenu() for each of your activities. // MyActivity.kt override fun onCreateOptionsMenu(menu: Menu): Boolean { super.onCreateOptionsMenu(menu) menuInflater.inflate(R.menu.main, menu) CastButtonFactory.setUpMediaRouteButton( applicationContext, menu, R.id.media_route_menu_item ) return true }
// Then override the onCreateOptionMenu() for each of your activities. // MyActivity.java @Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); getMenuInflater().inflate(R.menu.main, menu); CastButtonFactory.setUpMediaRouteButton(getApplicationContext(), menu, R.id.media_route_menu_item); return true; }
بعد ذلك، إذا كانت Activity
مكتسبة من
FragmentActivity
,
يمكنك إضافة
MediaRouteButton
على التخطيط.
// activity_layout.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal" >
<androidx.mediarouter.app.MediaRouteButton
android:id="@+id/media_route_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:mediaRouteTypes="user"
android:visibility="gone" />
</LinearLayout>
// MyActivity.kt override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_layout) mMediaRouteButton = findViewById<View>(R.id.media_route_button) as MediaRouteButton CastButtonFactory.setUpMediaRouteButton(applicationContext, mMediaRouteButton) mCastContext = CastContext.getSharedInstance(this) }
// MyActivity.java @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_layout); mMediaRouteButton = (MediaRouteButton) findViewById(R.id.media_route_button); CastButtonFactory.setUpMediaRouteButton(getApplicationContext(), mMediaRouteButton); mCastContext = CastContext.getSharedInstance(this); }
لضبط مظهر زر البث باستخدام مظهر، راجع تخصيص زر البث
ضبط ميزة "اكتشاف الأجهزة"
تتم إدارة ميزة "اكتشاف الجهاز" بالكامل من خلال
CastContext
عند إعداد CastContext، يُحدِّد تطبيق المُرسِل جهاز استقبال الويب
معرّف التطبيق، ويمكنك اختياريًا طلب تصفية مساحة الاسم من خلال الإعداد
supportedNamespaces
بوصة
CastOptions
يحتوي CastContext
على إشارة إلى MediaRouter
داخليًا، وسيبدأ
عملية الاكتشاف في ظل الشروط التالية:
- استنادًا إلى خوارزمية مصمّمة لتحقيق التوازن بين وقت استجابة استكشاف الأجهزة استهلاكًا للبطارية، ستبدأ ميزة "رصد" في بعض الأحيان تلقائيًا عندما دخول تطبيق المرسِل في المقدّمة.
- مربع حوار البث مفتوح.
- تحاول حزمة تطوير البرامج (SDK) للإرسال استرداد جلسة الإرسال.
ستتوقف عملية الاكتشاف عند إغلاق مربع حوار البث أو دخول تطبيق المرسل في الخلفية.
class CastOptionsProvider : OptionsProvider { companion object { const val CUSTOM_NAMESPACE = "urn:x-cast:custom_namespace" } override fun getCastOptions(appContext: Context): CastOptions { val supportedNamespaces: MutableList<String> = ArrayList() supportedNamespaces.add(CUSTOM_NAMESPACE) return CastOptions.Builder() .setReceiverApplicationId(context.getString(R.string.app_id)) .setSupportedNamespaces(supportedNamespaces) .build() } override fun getAdditionalSessionProviders(context: Context): List<SessionProvider>? { return null } }
class CastOptionsProvider implements OptionsProvider { public static final String CUSTOM_NAMESPACE = "urn:x-cast:custom_namespace"; @Override public CastOptions getCastOptions(Context appContext) { List<String> supportedNamespaces = new ArrayList<>(); supportedNamespaces.add(CUSTOM_NAMESPACE); CastOptions castOptions = new CastOptions.Builder() .setReceiverApplicationId(context.getString(R.string.app_id)) .setSupportedNamespaces(supportedNamespaces) .build(); return castOptions; } @Override public List<SessionProvider> getAdditionalSessionProviders(Context context) { return null; } }
آلية عمل إدارة الجلسات
تقدِّم حزمة تطوير البرامج (SDK) لتكنولوجيا Cast مفهوم جلسة Google Cast، كل مجموعة تشمل خطوات الاتصال بجهاز أو إطلاق (أو الانضمام) شبكة ويب تطبيق الاستقبال والاتصال بهذا التطبيق وإعداد قناة التحكم في الوسائط الاطّلاع على مستقبل الويب دليل دورة حياة الطلبات لمزيد من المعلومات عن جلسات البث ودورة حياة مستقبل الويب.
يدير الصف الجلسات
SessionManager
،
التي يمكن لتطبيقك الوصول إليها من خلال
CastContext.getSessionManager()
يتم تمثيل الجلسات الفردية من خلال الفئات الفرعية للفئة.
Session
على سبيل المثال:
CastSession
الجلسات التي تتضمن أجهزة بث. يمكن لتطبيقك الوصول إلى واجهة برمجة التطبيقات النشطة حاليًا
بث الجلسة عبر
SessionManager.getCurrentCastSession()
يمكن لتطبيقك استخدام
SessionManagerListener
لمراقبة أحداث الجلسة، مثل الإنشاء والتعليق والاستئناف
أو الإنهاء. يحاول إطار العمل الاستئناف تلقائيًا من
إنهاء غير طبيعي/مفاجئ عندما كانت الجلسة نشطة.
يتم إنشاء الجلسات وإزالتها تلقائيًا استجابةً لإيماءات المستخدم
من مربّعات حوار MediaRouter
.
للتعرّف بشكل أفضل على أخطاء بدء البثّ، يمكن للتطبيقات استخدام
CastContext#getCastReasonCodeForCastStatusCode(int)
لتحويل خطأ بدء الجلسة إلى
CastReasonCodes
يُرجى ملاحظة أن هناك بعض أخطاء بدء الجلسة (على سبيل المثال، CastReasonCodes#CAST_CANCELLED
)
هي السلوك المقصود، وينبغي عدم تسجيلها كخطأ.
إذا كنت بحاجة إلى أن تدرك تغييرات الحالة للجلسة، يمكنك تنفيذ
SessionManagerListener
يستمع هذا المثال إلى توفر
CastSession
في Activity
.
class MyActivity : Activity() { private var mCastSession: CastSession? = null private lateinit var mCastContext: CastContext private lateinit var mSessionManager: SessionManager private val mSessionManagerListener: SessionManagerListener<CastSession> = SessionManagerListenerImpl() private inner class SessionManagerListenerImpl : SessionManagerListener<CastSession?> { override fun onSessionStarting(session: CastSession?) {} override fun onSessionStarted(session: CastSession?, sessionId: String) { invalidateOptionsMenu() } override fun onSessionStartFailed(session: CastSession?, error: Int) { val castReasonCode = mCastContext.getCastReasonCodeForCastStatusCode(error) // Handle error } override fun onSessionSuspended(session: CastSession?, reason Int) {} override fun onSessionResuming(session: CastSession?, sessionId: String) {} override fun onSessionResumed(session: CastSession?, wasSuspended: Boolean) { invalidateOptionsMenu() } override fun onSessionResumeFailed(session: CastSession?, error: Int) {} override fun onSessionEnding(session: CastSession?) {} override fun onSessionEnded(session: CastSession?, error: Int) { finish() } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) mCastContext = CastContext.getSharedInstance(this) mSessionManager = mCastContext.sessionManager mSessionManager.addSessionManagerListener(mSessionManagerListener, CastSession::class.java) } override fun onResume() { super.onResume() mCastSession = mSessionManager.currentCastSession } override fun onDestroy() { super.onDestroy() mSessionManager.removeSessionManagerListener(mSessionManagerListener, CastSession::class.java) } }
public class MyActivity extends Activity { private CastContext mCastContext; private CastSession mCastSession; private SessionManager mSessionManager; private SessionManagerListener<CastSession> mSessionManagerListener = new SessionManagerListenerImpl(); private class SessionManagerListenerImpl implements SessionManagerListener<CastSession> { @Override public void onSessionStarting(CastSession session) {} @Override public void onSessionStarted(CastSession session, String sessionId) { invalidateOptionsMenu(); } @Override public void onSessionStartFailed(CastSession session, int error) { int castReasonCode = mCastContext.getCastReasonCodeForCastStatusCode(error); // Handle error } @Override public void onSessionSuspended(CastSession session, int reason) {} @Override public void onSessionResuming(CastSession session, String sessionId) {} @Override public void onSessionResumed(CastSession session, boolean wasSuspended) { invalidateOptionsMenu(); } @Override public void onSessionResumeFailed(CastSession session, int error) {} @Override public void onSessionEnding(CastSession session) {} @Override public void onSessionEnded(CastSession session, int error) { finish(); } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mCastContext = CastContext.getSharedInstance(this); mSessionManager = mCastContext.getSessionManager(); mSessionManager.addSessionManagerListener(mSessionManagerListener, CastSession.class); } @Override protected void onResume() { super.onResume(); mCastSession = mSessionManager.getCurrentCastSession(); } @Override protected void onDestroy() { super.onDestroy(); mSessionManager.removeSessionManagerListener(mSessionManagerListener, CastSession.class); } }
نقل البث
يتم الاحتفاظ بحالة الجلسة أساس نقل البث، حيث يمكن للمستخدمين نقل ملفات الصوت والفيديو الحالية على جميع الأجهزة باستخدام الطلبات الصوتية وGoogle Home التطبيقات أو الشاشات الذكية. يتوقف تشغيل الوسائط على أحد الأجهزة (المصدر) ويستمر على جهاز آخر ( الوجهة). يمكن لأي جهاز بث يتضمّن أحدث البرامج الثابتة العمل كمصادر أو وجهات في نقل البث.
للحصول على جهاز الوجهة الجديد أثناء نقل البث أو توسيعه،
تسجيل
Cast.Listener
باستخدام
CastSession#addCastListener
ثم اتصل
CastSession#getCastDevice()
أثناء معاودة الاتصال على onDeviceNameChanged
.
عرض نقل البث على جهاز الاستقبال على الويب لمزيد من المعلومات.
إعادة الاتصال تلقائيًا
يوفر إطار العمل
ReconnectionService
والذي يمكن أن يفعّله تطبيق المرسل لمعالجة إعادة الاتصال بعدة طرق
الحالات الزاوية، مثل:
- استرداد البيانات بعد فقدان شبكة Wi-Fi مؤقتًا
- استرداد البيانات بعد إيقاف وضع السكون للجهاز
- استرداد البيانات بعد تشغيل التطبيق في الخلفية
- استرداد الحساب في حال تعطُّل التطبيق
تكون هذه الخدمة مفعّلة تلقائيًا، ويمكن إيقافها في
CastOptions.Builder
يمكن دمج هذه الخدمة تلقائيًا في بيان التطبيق في حال الدمج التلقائي في ملف Gradle الخاص بك.
سيبدأ إطار العمل الخدمة عند إجراء جلسة وسائط، ثم سيوقفها. عند انتهاء جلسة الوسائط.
آلية عمل "التحكّم في الوسائط"
يوقف إطار عمل Google Cast
RemoteMediaPlayer
فئة جديدة من Cast 2.x لصالح فئة جديدة
RemoteMediaClient
,
التي توفّر الوظائف نفسها في مجموعة من واجهات برمجة التطبيقات الأكثر ملاءمة
الاضطرار إلى اجتياز GoogleApiClient.
عندما يُنشئ تطبيقك
CastSession
بتطبيق WebRecipients الذي يدعم مساحة اسم الوسائط، وهي مثيل
سيتم إنشاء RemoteMediaClient
تلقائيًا من خلال إطار العمل. تطبيقك
الوصول إليه من خلال استدعاء طريقة getRemoteMediaClient()
على CastSession
مثال.
ستستخدم كل طرق RemoteMediaClient
التي تُصدر طلبات إلى مستقبل الويب
إرجاع كائن PendingResult الذي يمكن استخدامه لتتبع هذا الطلب.
من المتوقّع أن تتم مشاركة مثيل RemoteMediaClient
بواسطة
لأجزاء متعددة من تطبيقك، بل وبعض المكوّنات الداخلية
مثل وحدات التحكم الصغيرة الدائمة
خدمة الإشعارات
وتحقيقًا لهذه الغاية، يدعم هذا المثيل تسجيل مثيلات متعددة من
RemoteMediaClient.Listener
ضبط البيانات الوصفية للوسائط
تشير رسالة الأشكال البيانية
MediaMetadata
تمثل هذه السمة المعلومات حول عنصر الوسائط الذي تريد بثه. تشير رسالة الأشكال البيانية
في المثال التالي، يتم إنشاء مثيل MediaMetadata جديدًا لفيلم وتعيين
العنوان والعنوان الفرعي وصورتين.
val movieMetadata = MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE) movieMetadata.putString(MediaMetadata.KEY_TITLE, mSelectedMedia.getTitle()) movieMetadata.putString(MediaMetadata.KEY_SUBTITLE, mSelectedMedia.getStudio()) movieMetadata.addImage(WebImage(Uri.parse(mSelectedMedia.getImage(0)))) movieMetadata.addImage(WebImage(Uri.parse(mSelectedMedia.getImage(1))))
MediaMetadata movieMetadata = new MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE); movieMetadata.putString(MediaMetadata.KEY_TITLE, mSelectedMedia.getTitle()); movieMetadata.putString(MediaMetadata.KEY_SUBTITLE, mSelectedMedia.getStudio()); movieMetadata.addImage(new WebImage(Uri.parse(mSelectedMedia.getImage(0)))); movieMetadata.addImage(new WebImage(Uri.parse(mSelectedMedia.getImage(1))));
عرض اختيار الصور حول استخدام الصور مع بيانات التعريف للوسائط.
تحميل الوسائط
يمكن للتطبيق تحميل ملف وسائط، كما هو موضّح في الرمز التالي. أول استخدام
MediaInfo.Builder
مع بيانات التعريف لوسائل الإعلام لإنشاء
MediaInfo
مثال. الحصول على
RemoteMediaClient
من CastSession
الحالي، ثم حمِّل MediaInfo
في ذلك
RemoteMediaClient
استخدام "RemoteMediaClient
" للتشغيل والإيقاف المؤقت وغير ذلك
للتحكم في تطبيق مشغِّل وسائط قيد التشغيل على جهاز استقبال الويب.
val mediaInfo = MediaInfo.Builder(mSelectedMedia.getUrl()) .setStreamType(MediaInfo.STREAM_TYPE_BUFFERED) .setContentType("videos/mp4") .setMetadata(movieMetadata) .setStreamDuration(mSelectedMedia.getDuration() * 1000) .build() val remoteMediaClient = mCastSession.getRemoteMediaClient() remoteMediaClient.load(MediaLoadRequestData.Builder().setMediaInfo(mediaInfo).build())
MediaInfo mediaInfo = new MediaInfo.Builder(mSelectedMedia.getUrl()) .setStreamType(MediaInfo.STREAM_TYPE_BUFFERED) .setContentType("videos/mp4") .setMetadata(movieMetadata) .setStreamDuration(mSelectedMedia.getDuration() * 1000) .build(); RemoteMediaClient remoteMediaClient = mCastSession.getRemoteMediaClient(); remoteMediaClient.load(new MediaLoadRequestData.Builder().setMediaInfo(mediaInfo).build());
اطّلع أيضًا على القسم الذي يتناول باستخدام مقاطع الوسائط
تنسيق فيديو بدقة 4K
للاطّلاع على تنسيق الفيديو للوسائط، استخدِم
getVideoInfo()
في MediaStatus للحصول على المثيل الحالي
VideoInfo
يحتوي هذا المثال على نوع تنسيق HDR TV وارتفاع الشاشة
والعرض بالبكسل. يُشار إلى خيارات تنسيق 4K بالثوابت
HDR_TYPE_*
تلقّي إشعارات عن بُعد لأجهزة متعددة
عندما يُجري المستخدم بثًا، سيتم إرسال محتوى أجهزة Android الأخرى المتصلة بالشبكة نفسها إرسال إشعار للسماح لهم أيضًا بالتحكم في التشغيل. أي شخص لديه جهاز يمكن أن يتم إيقاف هذه الإشعارات على هذا الجهاز من خلال "الإعدادات" في Google > Google Cast > عرض إشعارات جهاز التحكّم عن بُعد (تتضمن الإشعارات اختصارًا لتطبيق "الإعدادات".) لمزيد من التفاصيل، يمكنك مراجعة بثّ إشعارات جهاز التحكّم عن بُعد
إضافة وحدة تحكّم مصغّرة
وفقًا لتصميم فريق Cast قائمة التحقّق يجب على تطبيق المرسل أن يوفر تحكمًا مستمرًا يُعرف باسم mini مسؤول التحكّم بالبيانات يجب أن تظهر عندما ينتقل المستخدم بعيدًا عن صفحة المحتوى الحالية في جزء آخر من تطبيق المرسل. توفِّر وحدة التحكّم المصغّرة تذكيرًا مرئيًا إلى مستخدم جلسة البث الحالية ومن خلال النقر على وحدة التحكم الصغيرة، يمكن للمستخدم الرجوع إلى طريقة عرض وحدة التحكم الموسّعة بملء الشاشة في البثّ.
ويوفر إطار العمل عرضًا مخصصًا، وهو MiniControllerFragment، الذي يمكنك إضافته إلى أسفل ملف التخطيط لكل نشاط تريد عرض وحدة تحكُّم مصغّرة.
<fragment
android:id="@+id/castMiniController"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:visibility="gone"
class="com.google.android.gms.cast.framework.media.widget.MiniControllerFragment" />
عندما يشغّل تطبيق المرسِل فيديو أو بثًا صوتيًا بشكل مباشر، تعمل حزمة تطوير البرامج (SDK) عرض زر التشغيل/الإيقاف تلقائيًا بدلاً من زر التشغيل/الإيقاف المؤقت في وحدة التحكم الصغيرة.
لضبط مظهر النص للعنوان والعنوان الفرعي لهذا العرض المخصص، ولاختيار الأزرار، راجع تخصيص وحدة التحكّم الصغيرة
إضافة وحدة تحكُّم موسّعة
تتطلب قائمة التحقق من تصميم Google Cast أن يوفر تطبيق المرسل نسخة موسّعة مسؤول التحكّم بالبيانات للوسائط التي تعمل بتكنولوجيا Google Cast وحدة التحكم الموسعة هي إصدار ملء الشاشة من وحدة التحكم الصغيرة.
توفّر حزمة تطوير البرامج (SDK) لتكنولوجيا Google Cast أداةًا لوحدة التحكم الموسّعة التي تسمى
ExpandedControllerActivity
هذه فئة مجردة يجب أن تتضمن فئة فرعية لإضافة زر البث.
أولاً، أنشئ ملف موارد قائمة جديدًا لوحدة التحكم الموسّعة لتقديمها زر البث:
<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>
أنشِئ صفًا جديدًا يمتد إلى ExpandedControllerActivity
.
class ExpandedControlsActivity : ExpandedControllerActivity() { override fun onCreateOptionsMenu(menu: Menu): Boolean { super.onCreateOptionsMenu(menu) menuInflater.inflate(R.menu.expanded_controller, menu) CastButtonFactory.setUpMediaRouteButton(this, menu, R.id.media_route_menu_item) return true } }
public class ExpandedControlsActivity extends ExpandedControllerActivity { @Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); getMenuInflater().inflate(R.menu.expanded_controller, menu); CastButtonFactory.setUpMediaRouteButton(this, menu, R.id.media_route_menu_item); return true; } }
يمكنك الآن الإعلان عن نشاطك الجديد في بيان التطبيق ضمن علامة application
:
<application>
...
<activity
android:name=".expandedcontrols.ExpandedControlsActivity"
android:label="@string/app_name"
android:launchMode="singleTask"
android:theme="@style/Theme.CastVideosDark"
android:screenOrientation="portrait"
android:parentActivityName="com.google.sample.cast.refplayer.VideoBrowserActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
</intent-filter>
</activity>
...
</application>
تعديل CastOptionsProvider
وتغيير NotificationOptions
و
CastMediaOptions
لضبط النشاط المستهدف على نشاطك الجديد:
override fun getCastOptions(context: Context): CastOptions? { val notificationOptions = NotificationOptions.Builder() .setTargetActivityClassName(ExpandedControlsActivity::class.java.name) .build() val mediaOptions = CastMediaOptions.Builder() .setNotificationOptions(notificationOptions) .setExpandedControllerActivityClassName(ExpandedControlsActivity::class.java.name) .build() return CastOptions.Builder() .setReceiverApplicationId(context.getString(R.string.app_id)) .setCastMediaOptions(mediaOptions) .build() }
public CastOptions getCastOptions(Context context) { NotificationOptions notificationOptions = new NotificationOptions.Builder() .setTargetActivityClassName(ExpandedControlsActivity.class.getName()) .build(); CastMediaOptions mediaOptions = new CastMediaOptions.Builder() .setNotificationOptions(notificationOptions) .setExpandedControllerActivityClassName(ExpandedControlsActivity.class.getName()) .build(); return new CastOptions.Builder() .setReceiverApplicationId(context.getString(R.string.app_id)) .setCastMediaOptions(mediaOptions) .build(); }
عدِّل طريقة loadRemoteMedia
LocalPlayerActivity
لعرض
نشاط جديد عند تحميل الوسائط البعيدة:
private fun loadRemoteMedia(position: Int, autoPlay: Boolean) { val remoteMediaClient = mCastSession?.remoteMediaClient ?: return remoteMediaClient.registerCallback(object : RemoteMediaClient.Callback() { override fun onStatusUpdated() { val intent = Intent(this@LocalPlayerActivity, ExpandedControlsActivity::class.java) startActivity(intent) remoteMediaClient.unregisterCallback(this) } }) remoteMediaClient.load( MediaLoadRequestData.Builder() .setMediaInfo(mSelectedMedia) .setAutoplay(autoPlay) .setCurrentTime(position.toLong()).build() ) }
private void loadRemoteMedia(int position, boolean autoPlay) { if (mCastSession == null) { return; } final RemoteMediaClient remoteMediaClient = mCastSession.getRemoteMediaClient(); if (remoteMediaClient == null) { return; } remoteMediaClient.registerCallback(new RemoteMediaClient.Callback() { @Override public void onStatusUpdated() { Intent intent = new Intent(LocalPlayerActivity.this, ExpandedControlsActivity.class); startActivity(intent); remoteMediaClient.unregisterCallback(this); } }); remoteMediaClient.load(new MediaLoadRequestData.Builder() .setMediaInfo(mSelectedMedia) .setAutoplay(autoPlay) .setCurrentTime(position).build()); }
عندما يشغّل تطبيق المرسِل فيديو أو بثًا صوتيًا بشكل مباشر، تعمل حزمة تطوير البرامج (SDK) عرض زر التشغيل/الإيقاف تلقائيًا بدلاً من زر التشغيل/الإيقاف المؤقت في وحدة التحكم الموسعة.
لضبط المظهر باستخدام المظاهر، اختر الأزرار التي سيتم عرضها، وإضافة أزرار مخصّصة، اطّلِع على تخصيص وحدة التحكّم الموسّعة
التحكم في مستوى الصوت
يدير إطار العمل تلقائيًا مستوى صوت تطبيق المرسِل. إطار العمل يعمل على مزامنة تطبيقات المرسل ومستقبل الويب تلقائيًا حتى يتمكن المرسل تعرض واجهة المستخدم دائمًا مستوى الصوت الذي يحدِّده جهاز استقبال الويب.
التحكّم في مستوى الصوت للزرّ الفعلي
في نظام Android، يمكن استخدام الأزرار المادية على جهاز المرسل لتغيير مستوى الصوت التلقائي لجلسة البث على جهاز استقبال الويب لأي جهاز يستخدم Jelly Bean أو إصدار أحدث.
التحكم في مستوى الصوت للزر الفعلي قبل Jelly Bean
لاستخدام مفاتيح مستوى الصوت الخارجية للتحكّم في مستوى صوت جهاز "جهاز استقبال الويب"
أجهزة Android الأقدم من Jelly Bean، يجب أن يلغي تطبيق المُرسِل
dispatchKeyEvent
في أنشطتهم واستدعاء
CastContext.onDispatchVolumeKeyEventBeforeJellyBean()
:
class MyActivity : FragmentActivity() { override fun dispatchKeyEvent(event: KeyEvent): Boolean { return (CastContext.getSharedInstance(this) .onDispatchVolumeKeyEventBeforeJellyBean(event) || super.dispatchKeyEvent(event)) } }
class MyActivity extends FragmentActivity { @Override public boolean dispatchKeyEvent(KeyEvent event) { return CastContext.getSharedInstance(this) .onDispatchVolumeKeyEventBeforeJellyBean(event) || super.dispatchKeyEvent(event); } }
إضافة عناصر التحكّم في الوسائط إلى الإشعارات وشاشة القفل
على أجهزة Android فقط، تتطلب قائمة التحقّق من تصميم Google Cast من تطبيق المرسِل أن
عناصر التحكّم في الوسائط
للإشعار
وفي القفل
الشاشة،
التي يقوم فيها المرسِل بالبث بدون التركيز على تطبيق المرسِل. تشير رسالة الأشكال البيانية
إطار عمل يوفر
MediaNotificationService
أو
MediaIntentReceiver
لمساعدة تطبيق المرسِل في إنشاء عناصر تحكّم في الوسائط في الإشعار وفي القفل
الشاشة.
يتم تشغيل "MediaNotificationService
" عندما يبث المُرسِل المحتوى ويعرض
إشعار يتضمّن صورة مصغّرة ومعلومات عن البثّ الحالي
وزر تشغيل/إيقاف مؤقت وزر إيقاف.
MediaIntentReceiver
هو BroadcastReceiver
الذي يعالج إجراءات المستخدم من
الإشعار.
يمكن للتطبيق ضبط الإشعارات والتحكّم في الوسائط من شاشة القفل من خلال
NotificationOptions
يمكن لتطبيقك ضبط أزرار التحكّم التي تظهر في الإشعار.
التي يتم فتحها من خلال "Activity
" عندما ينقر المستخدم على الإشعار. إذا كانت الإجراءات
بشكل صريح، القيم الافتراضية،
MediaIntentReceiver.ACTION_TOGGLE_PLAYBACK
و
سيتم استخدام MediaIntentReceiver.ACTION_STOP_CASTING
.
// Example showing 4 buttons: "rewind", "play/pause", "forward" and "stop casting". val buttonActions: MutableList<String> = ArrayList() buttonActions.add(MediaIntentReceiver.ACTION_REWIND) buttonActions.add(MediaIntentReceiver.ACTION_TOGGLE_PLAYBACK) buttonActions.add(MediaIntentReceiver.ACTION_FORWARD) buttonActions.add(MediaIntentReceiver.ACTION_STOP_CASTING) // Showing "play/pause" and "stop casting" in the compat view of the notification. val compatButtonActionsIndices = intArrayOf(1, 3) // Builds a notification with the above actions. Each tap on the "rewind" and "forward" buttons skips 30 seconds. // Tapping on the notification opens an Activity with class VideoBrowserActivity. val notificationOptions = NotificationOptions.Builder() .setActions(buttonActions, compatButtonActionsIndices) .setSkipStepMs(30 * DateUtils.SECOND_IN_MILLIS) .setTargetActivityClassName(VideoBrowserActivity::class.java.name) .build()
// Example showing 4 buttons: "rewind", "play/pause", "forward" and "stop casting". List<String> buttonActions = new ArrayList<>(); buttonActions.add(MediaIntentReceiver.ACTION_REWIND); buttonActions.add(MediaIntentReceiver.ACTION_TOGGLE_PLAYBACK); buttonActions.add(MediaIntentReceiver.ACTION_FORWARD); buttonActions.add(MediaIntentReceiver.ACTION_STOP_CASTING); // Showing "play/pause" and "stop casting" in the compat view of the notification. int[] compatButtonActionsIndices = new int[]{1, 3}; // Builds a notification with the above actions. Each tap on the "rewind" and "forward" buttons skips 30 seconds. // Tapping on the notification opens an Activity with class VideoBrowserActivity. NotificationOptions notificationOptions = new NotificationOptions.Builder() .setActions(buttonActions, compatButtonActionsIndices) .setSkipStepMs(30 * DateUtils.SECOND_IN_MILLIS) .setTargetActivityClassName(VideoBrowserActivity.class.getName()) .build();
يتم تفعيل عرض عناصر التحكّم في الوسائط من الإشعارات وشاشة القفل من خلال
تلقائيًا، ويمكن إيقافه من خلال طلب
setNotificationOptions
مع خالية في
CastMediaOptions.Builder
وفي الوقت الحالي، يتم تفعيل ميزة شاشة القفل طالما أنّ الإشعار
قيد التشغيل.
// ... continue with the NotificationOptions built above val mediaOptions = CastMediaOptions.Builder() .setNotificationOptions(notificationOptions) .build() val castOptions: CastOptions = Builder() .setReceiverApplicationId(context.getString(R.string.app_id)) .setCastMediaOptions(mediaOptions) .build()
// ... continue with the NotificationOptions built above CastMediaOptions mediaOptions = new CastMediaOptions.Builder() .setNotificationOptions(notificationOptions) .build(); CastOptions castOptions = new CastOptions.Builder() .setReceiverApplicationId(context.getString(R.string.app_id)) .setCastMediaOptions(mediaOptions) .build();
عندما يشغّل تطبيق المرسِل فيديو أو بثًا صوتيًا بشكل مباشر، تعمل حزمة تطوير البرامج (SDK) عرض زر التشغيل/الإيقاف تلقائيًا بدلاً من زر التشغيل/الإيقاف المؤقت في عنصر التحكم في الإشعارات ولكن ليس على عنصر التحكم في شاشة القفل.
ملاحظة: لعرض عناصر التحكّم في شاشة القفل على الأجهزة التي تعمل بإصدار سابق من Lollipop، يُرجى اتّباع الخطوات التالية:
سيطلب "RemoteMediaClient
" تلقائيًا التركيز على الصوت نيابةً عنك.
التعامل مع الأخطاء
من المهم جدًا لتطبيقات المرسل أن تتعامل مع جميع استدعاءات الأخطاء وأن تقرر أفضل استجابة لكل مرحلة من دورة حياة البثّ. يمكن للتطبيق عرض المستخدم أو قد يقرر قطع الاتصال مستقبِل الويب.