تفعيل بث تطبيق iOS

1. نظرة عامة

شعار Google Cast

ستتعرّف في هذا الدرس التطبيقي على كيفية تعديل تطبيق فيديو حالي على نظام التشغيل iOS لبثّ محتوى إلى جهاز يعمل بتكنولوجيا Google Cast.

ما المقصود بتكنولوجيا Google Cast؟

تتيح تكنولوجيا Google Cast للمستخدمين بث المحتوى من جهاز جوّال إلى التلفزيون. يمكن للمستخدمين بعد ذلك استخدام أجهزتهم الجوّالة كوحدة تحكم عن بُعد لتشغيل الوسائط على التلفزيون.

تتيح لك حزمة تطوير البرامج (SDK) لتكنولوجيا Google Cast توسيع نطاق تطبيقك للتحكم في الأجهزة التي تعمل بتكنولوجيا Google Cast (مثل التلفزيون أو نظام الصوت). تسمح لك حزمة تطوير البرامج (SDK) لتكنولوجيا Cast بإضافة مكونات واجهة المستخدم اللازمة بناءً على قائمة التحقق من تصميم Google Cast.

يتم توفير قائمة التحقّق من تصميم Google Cast لجعل تجربة مستخدم Google Cast بسيطة ويمكن توقُّعها على جميع الأنظمة الأساسية المتوافقة.

ما الذي سنبنيه؟

عند إكمال هذا الدليل التعليمي حول رموز البرامج، سيكون لديك تطبيق فيديو لنظام التشغيل iOS يمكنه بث الفيديوهات إلى جهاز Google Cast.

المُعطيات

  • كيفية إضافة حزمة تطوير البرامج (SDK) لتكنولوجيا Google Cast إلى نموذج فيديو
  • كيفية إضافة زر البث لاختيار جهاز Google Cast
  • كيفية الاتصال بجهاز بث وتشغيل جهاز استقبال وسائط
  • كيفية إرسال فيديو
  • كيفية إضافة وحدة تحكم مصغّرة للبث إلى تطبيقك.
  • كيفية إضافة وحدة تحكُّم موسّعة.
  • كيفية توفير تراكب تمهيدي
  • طريقة تخصيص التطبيقات المصغّرة للبث
  • كيفية دمج تطبيق Cast Connect

المتطلبات

  • أحدث إصدار من Xcode.
  • جهاز جوّال يعمل بنظام التشغيل iOS 9 أو إصدار أحدث (أو Xcode Simulator).
  • كابل بيانات USB لتوصيل جهازك الجوّال بجهاز كمبيوتر التطوير (في حال استخدام جهاز).
  • جهاز Google Cast، مثل Chromecast أو Android TV تم ضبطه على الاتصال بالإنترنت
  • تلفزيون أو شاشة مزوّدة بمنفذ إدخال HDMI.
  • يجب توفّر "Chromecast مع Google TV" لاختبار عملية دمج Cast Connect، ولكنه اختياري في بقية الدرس التطبيقي حول الترميز. إذا لم يكن لديك هذا البرنامج، يُرجى تخطّي خطوة إضافة دعم Cast Connect في نهاية هذا الدليل التوجيهي.

التجربة

  • يجب أن يكون لديك معرفة سابقة بتطوير تطبيقات iOS.
  • ستحتاج أيضًا إلى معرفة سابقة بمشاهدة التلفزيون :)

كيف ستستخدم هذا البرنامج التعليمي؟

قراءته فقط قراءته وإكمال التمارين

كيف تقيّم تجربتك في إنشاء تطبيقات iOS؟

مبتدئ متوسط متقدّم

كيف تقيّم تجربتك في مشاهدة التلفزيون؟

حديث متوسط بارع

2. الحصول على رمز النموذج

يمكنك تنزيل نموذج التعليمات البرمجية بالكامل على الكمبيوتر...

وفك ضغط ملف ZIP الذي تم تنزيله.

3- تشغيل نموذج التطبيق

شعار Apple iOS

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

مع تنزيل الرمز، تصف التعليمات التالية كيفية فتح نموذج التطبيق المكتمل وتشغيله في Xcode:

الأسئلة الشائعة

إعداد CocoaPods

لإعداد CocoaPods، انتقِل إلى وحدة التحكّم وثبِّت أداة Ruby التلقائية المتاحة على نظام التشغيل macOS:

sudo gem install cocoapods

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

إعداد المشروع

  1. انتقِل إلى الوحدة الطرفية وانتقِل إلى دليل الدرس التطبيقي حول الترميز.
  2. ثبِّت التبعيات من Podfile.
cd app-done
pod update
pod install
  1. افتح Xcode واختَر فتح مشروع آخر...
  2. اختَر ملف CastVideos-ios.xcworkspace من الدليل رمز المجلدapp-done في نموذج مجلد الرمز البرمجي.

تشغيل التطبيق

حدد الهدف والمحاكي، ثم شغِّل التطبيق:

شريط أدوات محاكي تطبيق XCode

من المفترض أن يظهر تطبيق الفيديو بعد بضع ثوانٍ.

تأكد من النقر على "السماح" عند ظهور إشعار حول قبول اتصالات الشبكات الواردة. لن يظهر رمز البث إذا لم يتم قبول هذا الخيار.

مربّع حوار للتأكيد يطلب الإذن لقبول اتصالات الشبكة الواردة

انقر على زر البث واختَر جهاز Google Cast.

اختَر فيديو، ثم انقر على زر التشغيل.

سيبدأ تشغيل الفيديو على جهاز Google Cast.

سيتم عرض وحدة التحكّم الموسّعة. يمكنك استخدام زر التشغيل أو الإيقاف المؤقت للتحكم في التشغيل.

انتقِل مرة أخرى إلى قائمة الفيديوهات.

تظهر الآن وحدة تحكُّم مصغّرة في أسفل الشاشة.

صورة توضيحية لجهاز iPhone يشغّل تطبيق Cast Videos، مع ظهور وحدة التحكّم الصغيرة في الأسفل

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

انقر على زر البث لإيقاف البث على جهاز Google Cast.

4. تجهيز مشروع البدء

صورة توضيحية لجهاز iPhone يشغّل تطبيق Cast Videos

نحتاج إلى إمكانية استخدام Google Cast في تطبيق البدء الذي نزّلته. في ما يلي بعض مصطلحات Google Cast التي سنستخدمها في هذا الدرس التطبيقي حول الترميز:

  • تشغيل تطبيق المرسِل على جهاز جوّال أو كمبيوتر محمول،
  • تشغيل تطبيق مستلِم على جهاز Google Cast

إعداد المشروع

أنت الآن جاهز للبناء على مشروع المبتدئين باستخدام Xcode:

  1. انتقِل إلى الوحدة الطرفية وانتقِل إلى دليل الدرس التطبيقي حول الترميز.
  2. ثبِّت التبعيات من Podfile.
cd app-start
pod update
pod install
  1. افتح Xcode واختَر فتح مشروع آخر...
  2. اختَر ملف CastVideos-ios.xcworkspace من الدليل رمز المجلدapp-start في نموذج مجلد الرمز البرمجي.

تصميم التطبيقات

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

يتألّف التطبيق من وحدتَي تحكّم رئيسيتَين في العرض: MediaTableViewController وMediaViewController..

MediaTableViewController

تعرِض UITableViewController قائمة بالفيديوهات من مثيل MediaListModel. إنّ قائمة الفيديوهات والبيانات الوصفية المرتبطة بها مستضافة على خادم بعيد كملف JSON. يجلب MediaListModel ملف JSON هذا ويعالجه لإنشاء قائمة بكائنات MediaItem.

يصمّم عنصر MediaItem فيديو والبيانات الوصفية المرتبطة به، مثل عنوانه ووصفه وعنوان URL للصورة وعنوان URL للبث.

ينشئ MediaTableViewController مثيل MediaListModel ثم يسجِّل نفسه على أنّه MediaListModelDelegate ليتم إعلامك عند تنزيل البيانات الوصفية للوسائط من أجل تحميل العرض في جدول.

يتم عرض قائمة بالصور المصغّرة للفيديو مع وصف موجز لكل فيديو. عند اختيار عنصر، يتم تمرير MediaItem المقابل إلى MediaViewController.

MediaViewController

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

تستضيف وحدة التحكّم في العرض LocalPlayerView وبعض عناصر التحكّم في الوسائط ومنطقة نص لعرض وصف الفيديو المحدَّد. يغطي المشغل الجزء العلوي من الشاشة، مما يترك مكانًا للوصف التفصيلي للفيديو أسفل المستخدم. يمكن للمستخدم التشغيل/الإيقاف المؤقت أو البحث عن تشغيل الفيديو المحلي.

الأسئلة الشائعة

5- إضافة زر البث

صورة توضيحية للثلث العلوي من جهاز iPhone يشغّل تطبيق Cast Videos، ويظهر زر البث في أعلى يمين الشاشة

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

الإعداد

يتطلب مشروع البدء نفس التبعيات وإعداد Xcode كما فعلت لعينة التطبيق المكتمل. ارجع إلى ذلك القسم واتّبِع الخطوات نفسها لإضافة GoogleCast.framework إلى مشروع بدء التطبيق.

الإعداد

يحتوي إطار عمل Google Cast على كائن مفردات عام، يُعرف باسم GCKCastContext، الذي ينسق جميع أنشطة إطار العمل. يجب إعداد هذا العنصر في وقت مبكر من دورة حياة التطبيق، عادةً من خلال طريقة application(_:didFinishLaunchingWithOptions:) في تفويض التطبيق، حتى يتم الاستئناف التلقائي للجلسة عند إعادة تشغيل تطبيق المُرسِل بشكل صحيح ويمكن أن يبدأ البحث عن الأجهزة.

يجب توفير كائن GCKCastOptions عند إعداد GCKCastContext. تحتوي هذه الفئة على خيارات تؤثر في سلوك إطار العمل. وأهم هذه العوامل هو رقم تعريف تطبيق جهاز الاستقبال الذي يُستخدم لفلترة نتائج اكتشاف جهاز البث وتشغيل تطبيق جهاز الاستقبال عند بدء جلسة بث.

تُعد طريقة application(_:didFinishLaunchingWithOptions:) أيضًا مكانًا جيدًا لإعداد تفويض التسجيل بتلقي رسائل التسجيل من إطار عمل Cast. ويمكن أن تكون هذه الميزات مفيدة لتصحيح الأخطاء وتحديد المشاكل وحلّها.

عند تطوير تطبيقك الخاص الذي يعمل بتكنولوجيا Google Cast، عليك التسجيل كمطوِّر لتكنولوجيا Google Cast ثم الحصول على معرّف تطبيق لتطبيقك. سنستخدم نموذج رقم تعريف تطبيق في هذا الدرس التطبيقي حول الترميز.

أضِف الرمز التالي إلى AppDelegate.swift لإعداد GCKCastContext باستخدام معرّف التطبيق من الإعدادات التلقائية للمستخدم، وأضِف سجل لإطار عمل Google Cast:

import GoogleCast

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
  fileprivate var enableSDKLogging = true

  ...

  func application(_: UIApplication,
                   didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

    ...
    let options = GCKCastOptions(discoveryCriteria: GCKDiscoveryCriteria(applicationID: kReceiverAppID))
    options.physicalVolumeButtonsWillControlDeviceVolume = true
    GCKCastContext.setSharedInstanceWith(options)

    window?.clipsToBounds = true
    setupCastLogging()
    ...
  }
  ...
  func setupCastLogging() {
    let logFilter = GCKLoggerFilter()
    let classesToLog = ["GCKDeviceScanner", "GCKDeviceProvider", "GCKDiscoveryManager", "GCKCastChannel",
                        "GCKMediaControlChannel", "GCKUICastButton", "GCKUIMediaController", "NSMutableDictionary"]
    logFilter.setLoggingLevel(.verbose, forClasses: classesToLog)
    GCKLogger.sharedInstance().filter = logFilter
    GCKLogger.sharedInstance().delegate = self
  }
}

...

// MARK: - GCKLoggerDelegate

extension AppDelegate: GCKLoggerDelegate {
  func logMessage(_ message: String,
                  at _: GCKLoggerLevel,
                  fromFunction function: String,
                  location: String) {
    if enableSDKLogging {
      // Send SDK's log messages directly to the console.
      print("\(location): \(function) - \(message)")
    }
  }
}

زر الإرسال

الآن بعد إعداد GCKCastContext، علينا إضافة زر البث للسماح للمستخدم باختيار جهاز بث. توفّر حزمة تطوير البرامج (SDK) الخاصة بالبث مكوّن زر البث يُسمى GCKUICastButton باعتباره فئة فرعية UIButton. ويمكن إضافة هذا الرمز إلى شريط العناوين في التطبيق من خلال التفافه في UIBarButtonItem. علينا إضافة الزرّ "بث" إلى كلّ من "MediaTableViewController" و"MediaViewController".

أضِف الرمز التالي إلى MediaTableViewController.swift وMediaViewController.swift:

import GoogleCast

@objc(MediaTableViewController)
class MediaTableViewController: UITableViewController, GCKSessionManagerListener,
  MediaListModelDelegate, GCKRequestDelegate {
  private var castButton: GCKUICastButton!
  ...
  override func viewDidLoad() {
    print("MediaTableViewController - viewDidLoad")
    super.viewDidLoad()

    ...
    castButton = GCKUICastButton(frame: CGRect(x: CGFloat(0), y: CGFloat(0),
                                               width: CGFloat(24), height: CGFloat(24)))
    // Overwrite the UIAppearance theme in the AppDelegate.
    castButton.tintColor = UIColor.white
    navigationItem.rightBarButtonItem = UIBarButtonItem(customView: castButton)

    ...
  }
  ...
}

بعد ذلك، أضِف الرمز التالي إلى MediaViewController.swift:

import GoogleCast

@objc(MediaViewController)
class MediaViewController: UIViewController, GCKSessionManagerListener, GCKRemoteMediaClientListener,
  LocalPlayerViewDelegate, GCKRequestDelegate {
  private var castButton: GCKUICastButton!
  ...
  override func viewDidLoad() {
    super.viewDidLoad()
    print("in MediaViewController viewDidLoad")
    ...
    castButton = GCKUICastButton(frame: CGRect(x: CGFloat(0), y: CGFloat(0),
                                               width: CGFloat(24), height: CGFloat(24)))
    // Overwrite the UIAppearance theme in the AppDelegate.
    castButton.tintColor = UIColor.white
    navigationItem.rightBarButtonItem = UIBarButtonItem(customView: castButton)

    ...
  }
  ...
}

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

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

6- بث محتوى الفيديو

رسم توضيحي لهاتف iPhone يعمل بتطبيق CastVideos الذي يعرض تفاصيل عن فيديو معيّن (Tears of Steel) في أسفل الشاشة، يظهر المشغّل المصغّر.

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

بث الوسائط

بشكل عام، إذا أردت تشغيل محتوى وسائط على جهاز بث، يجب تنفيذ ما يلي:

  1. أنشئ كائن GCKMediaInformation من حزمة تطوير البرامج (SDK) لتكنولوجيا Cast لإنشاء نموذج لعنصر وسائط.
  2. يتصل المستخدم بجهاز البث لتشغيل تطبيق الاستقبال.
  3. ما عليك سوى تحميل كائن GCKMediaInformation إلى جهاز الاستقبال وتشغيل المحتوى.
  4. تتبُّع حالة الوسائط
  5. إرسال أوامر التشغيل إلى المستلِم استنادًا إلى تفاعلات المستخدم

تؤدي الخطوة الأولى إلى ربط عنصر بآخر، وGCKMediaInformation هو عنصر يمكن لحزمة تطوير البرامج (SDK) لأجهزة البث فهمه، وMediaItem هو عنصر يُستخدم في تطبيقنا لتضمين عنصر وسائط، ويمكننا بسهولة ربط MediaItem بـ GCKMediaInformation. لقد أكملنا الخطوة 2 في القسم السابق. يسهل تنفيذ الخطوة الثالثة باستخدام حزمة تطوير البرامج (SDK) الخاصة بالبث.

يميّز نموذج التطبيق MediaViewController حاليًا بين التشغيل المحلي والتشغيل عن بُعد باستخدام هذا التعداد:

enum PlaybackMode: Int {
  case none = 0
  case local
  case remote
}

private var playbackMode = PlaybackMode.none

ليس من المهم في هذا الدرس التطبيقي حول الترميز التعرّف بدقة على آلية عمل نموذج منطق المشغّل بالكامل. من المهم أن تفهم أنه يجب تعديل مشغّل الوسائط في تطبيقك ليكون على علم بموقعي التشغيل بطريقة مماثلة.

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

إدارة جلسة البث

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

ستبدأ جلسة البث تلقائيًا عندما يختار المستخدم جهازًا من خلال زر البث، وستتوقف تلقائيًا عند قطع اتصال المستخدم. يعالج إطار عمل Cast أيضًا عملية إعادة الاتصال بجلسة جهاز الاستقبال بسبب مشاكل في الشبكة.

تتم إدارة جلسات البث بواسطة GCKSessionManager، والتي يمكن الوصول إليها من خلال GCKCastContext.sharedInstance().sessionManager. يمكن استخدام عمليات معاودة الاتصال "GCKSessionManagerListener" لمراقبة أحداث الجلسة، مثل الإنشاء والتعليق والاستئناف والإنهاء.

أولاً، نحتاج إلى تسجيل مستمع الجلسة وبدء بعض المتغيّرات:

class MediaViewController: UIViewController, GCKSessionManagerListener,
  GCKRemoteMediaClientListener, LocalPlayerViewDelegate, GCKRequestDelegate {

  ...
  private var sessionManager: GCKSessionManager!
  ...

  required init?(coder: NSCoder) {
    super.init(coder: coder)

    sessionManager = GCKCastContext.sharedInstance().sessionManager

    ...
  }

  override func viewWillAppear(_ animated: Bool) {
    ...

    let hasConnectedSession: Bool = (sessionManager.hasConnectedSession())
    if hasConnectedSession, (playbackMode != .remote) {
      populateMediaInfo(false, playPosition: 0)
      switchToRemotePlayback()
    } else if sessionManager.currentSession == nil, (playbackMode != .local) {
      switchToLocalPlayback()
    }

    sessionManager.add(self)

    ...
  }

  override func viewWillDisappear(_ animated: Bool) {
    ...

    sessionManager.remove(self)
    sessionManager.currentCastSession?.remoteMediaClient?.remove(self)
    ...
    super.viewWillDisappear(animated)
  }

  func switchToLocalPlayback() {
    ...

    sessionManager.currentCastSession?.remoteMediaClient?.remove(self)

    ...
  }

  func switchToRemotePlayback() {
    ...

    sessionManager.currentCastSession?.remoteMediaClient?.add(self)

    ...
  }


  // MARK: - GCKSessionManagerListener

  func sessionManager(_: GCKSessionManager, didStart session: GCKSession) {
    print("MediaViewController: sessionManager didStartSession \(session)")
    setQueueButtonVisible(true)
    switchToRemotePlayback()
  }

  func sessionManager(_: GCKSessionManager, didResumeSession session: GCKSession) {
    print("MediaViewController: sessionManager didResumeSession \(session)")
    setQueueButtonVisible(true)
    switchToRemotePlayback()
  }

  func sessionManager(_: GCKSessionManager, didEnd _: GCKSession, withError error: Error?) {
    print("session ended with error: \(String(describing: error))")
    let message = "The Casting session has ended.\n\(String(describing: error))"
    if let window = appDelegate?.window {
      Toast.displayMessage(message, for: 3, in: window)
    }
    setQueueButtonVisible(false)
    switchToLocalPlayback()
  }

  func sessionManager(_: GCKSessionManager, didFailToStartSessionWithError error: Error?) {
    if let error = error {
      showAlert(withTitle: "Failed to start a session", message: error.localizedDescription)
    }
    setQueueButtonVisible(false)
  }

  func sessionManager(_: GCKSessionManager,
                      didFailToResumeSession _: GCKSession, withError _: Error?) {
    if let window = UIApplication.shared.delegate?.window {
      Toast.displayMessage("The Casting session could not be resumed.",
                           for: 3, in: window)
    }
    setQueueButtonVisible(false)
    switchToLocalPlayback()
  }

  ...
}

في MediaViewController، يهمّنا تلقّي إشعارات عندما نتصل بجهاز البث أو عندما نقطع الاتصال به لنتمكّن من التبديل إلى المشغّل المحلي أو منه. يُرجى العِلم أنّه يمكن أن يتم إيقاف الاتصال بالإنترنت ليس فقط من خلال مثيل التطبيق الذي يعمل على جهازك الجوّال، ولكن يمكن أيضًا أن ينقطع الاتصال به بسبب تشغيل مثيل آخر من التطبيق (أو التطبيق الآخر) على جهاز جوّال آخر.

يمكن الوصول إلى الجلسة النشطة حاليًا باسم GCKCastContext.sharedInstance().sessionManager.currentCastSession. يتم إنشاء الجلسات وإزالتها تلقائيًا استجابةً لإيماءات المستخدم من مربعات حوار البث.

جارٍ تحميل الوسائط

في حزمة تطوير البرامج (SDK) لتطبيقات البث، يوفّر GCKRemoteMediaClient مجموعة من واجهات برمجة التطبيقات الملائمة لإدارة تشغيل الوسائط عن بُعد على جهاز الاستقبال. بالنسبة إلى GCKCastSession الذي يتيح تشغيل الوسائط، سيتم تلقائيًا إنشاء مثيل لـ GCKRemoteMediaClient بواسطة حزمة SDK. يمكن الوصول إليها باعتبارها السمة remoteMediaClient لمثيل GCKCastSession.

أضِف الرمز التالي إلى MediaViewController.swift لتحميل الفيديو المحدّد حاليًا على جهاز الاستقبال:

@objc(MediaViewController)
class MediaViewController: UIViewController, GCKSessionManagerListener,
  GCKRemoteMediaClientListener, LocalPlayerViewDelegate, GCKRequestDelegate {
  ...

  @objc func playSelectedItemRemotely() {
    loadSelectedItem(byAppending: false)
  }

  /**
   * Loads the currently selected item in the current cast media session.
   * @param appending If YES, the item is appended to the current queue if there
   * is one. If NO, or if
   * there is no queue, a new queue containing only the selected item is created.
   */
  func loadSelectedItem(byAppending appending: Bool) {
    print("enqueue item \(String(describing: mediaInfo))")
    if let remoteMediaClient = sessionManager.currentCastSession?.remoteMediaClient {
      let mediaQueueItemBuilder = GCKMediaQueueItemBuilder()
      mediaQueueItemBuilder.mediaInformation = mediaInfo
      mediaQueueItemBuilder.autoplay = true
      mediaQueueItemBuilder.preloadTime = TimeInterval(UserDefaults.standard.integer(forKey: kPrefPreloadTime))
      let mediaQueueItem = mediaQueueItemBuilder.build()
      if appending {
        let request = remoteMediaClient.queueInsert(mediaQueueItem, beforeItemWithID: kGCKMediaQueueInvalidItemID)
        request.delegate = self
      } else {
        let queueDataBuilder = GCKMediaQueueDataBuilder(queueType: .generic)
        queueDataBuilder.items = [mediaQueueItem]
        queueDataBuilder.repeatMode = remoteMediaClient.mediaStatus?.queueRepeatMode ?? .off

        let mediaLoadRequestDataBuilder = GCKMediaLoadRequestDataBuilder()
        mediaLoadRequestDataBuilder.mediaInformation = mediaInfo
        mediaLoadRequestDataBuilder.queueData = queueDataBuilder.build()

        let request = remoteMediaClient.loadMedia(with: mediaLoadRequestDataBuilder.build())
        request.delegate = self
      }
    }
  }
  ...
}

يمكنك الآن تعديل طرق حالية متنوعة لاستخدام منطق "جلسة البث" لإتاحة التشغيل عن بُعد:

required init?(coder: NSCoder) {
  super.init(coder: coder)
  ...
  castMediaController = GCKUIMediaController()
  ...
}

func switchToLocalPlayback() {
  print("switchToLocalPlayback")
  if playbackMode == .local {
    return
  }
  setQueueButtonVisible(false)
  var playPosition: TimeInterval = 0
  var paused: Bool = false
  var ended: Bool = false
  if playbackMode == .remote {
    playPosition = castMediaController.lastKnownStreamPosition
    paused = (castMediaController.lastKnownPlayerState == .paused)
    ended = (castMediaController.lastKnownPlayerState == .idle)
    print("last player state: \(castMediaController.lastKnownPlayerState), ended: \(ended)")
  }
  populateMediaInfo((!paused && !ended), playPosition: playPosition)
  sessionManager.currentCastSession?.remoteMediaClient?.remove(self)
  playbackMode = .local
}

func switchToRemotePlayback() {
  print("switchToRemotePlayback; mediaInfo is \(String(describing: mediaInfo))")
  if playbackMode == .remote {
    return
  }
  // If we were playing locally, load the local media on the remote player
  if playbackMode == .local, (_localPlayerView.playerState != .stopped), (mediaInfo != nil) {
    print("loading media: \(String(describing: mediaInfo))")
    let paused: Bool = (_localPlayerView.playerState == .paused)
    let mediaQueueItemBuilder = GCKMediaQueueItemBuilder()
    mediaQueueItemBuilder.mediaInformation = mediaInfo
    mediaQueueItemBuilder.autoplay = !paused
    mediaQueueItemBuilder.preloadTime = TimeInterval(UserDefaults.standard.integer(forKey: kPrefPreloadTime))
    mediaQueueItemBuilder.startTime = _localPlayerView.streamPosition ?? 0
    let mediaQueueItem = mediaQueueItemBuilder.build()

    let queueDataBuilder = GCKMediaQueueDataBuilder(queueType: .generic)
    queueDataBuilder.items = [mediaQueueItem]
    queueDataBuilder.repeatMode = .off

    let mediaLoadRequestDataBuilder = GCKMediaLoadRequestDataBuilder()
    mediaLoadRequestDataBuilder.queueData = queueDataBuilder.build()

    let request = sessionManager.currentCastSession?.remoteMediaClient?.loadMedia(with: mediaLoadRequestDataBuilder.build())
    request?.delegate = self
  }
  _localPlayerView.stop()
  _localPlayerView.showSplashScreen()
  setQueueButtonVisible(true)
  sessionManager.currentCastSession?.remoteMediaClient?.add(self)
  playbackMode = .remote
}

/* Play has been pressed in the LocalPlayerView. */
func continueAfterPlayButtonClicked() -> Bool {
  let hasConnectedCastSession = sessionManager.hasConnectedCastSession
  if mediaInfo != nil, hasConnectedCastSession() {
    // Display an alert box to allow the user to add to queue or play
    // immediately.
    if actionSheet == nil {
      actionSheet = ActionSheet(title: "Play Item", message: "Select an action", cancelButtonText: "Cancel")
      actionSheet?.addAction(withTitle: "Play Now", target: self,
                             selector: #selector(playSelectedItemRemotely))
    }
    actionSheet?.present(in: self, sourceView: _localPlayerView)
    return false
  }
  return true
}

الآن، شغِّل التطبيق على جهازك الجوّال. اتصل بجهاز البث وابدأ في تشغيل الفيديو. من المفترض أن ترى الفيديو قيد التشغيل على جهاز الاستقبال.

7. وحدة تحكّم صغيرة

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

صورة توضيحية للجزء السفلي من جهاز iPhone يشغّل تطبيق Cast Videos، مع التركيز على وحدة التحكّم الصغيرة

توفّر حزمة تطوير البرامج (SDK) لتقنية Cast شريط التحكّم GCKUIMiniMediaControlsViewController الذي يمكن إضافته إلى المشاهد التي تريد عرض عناصر التحكّم الدائمة فيها.

في نموذج التطبيق، سنستخدم GCKUICastContainerViewController الذي يلفّ عنصر تحكّم آخر في العرض ويضيف GCKUIMiniMediaControlsViewController في أسفل الصفحة.

عدِّل ملف AppDelegate.swift وأضِف الرمز التالي لشرط if useCastContainerViewController بالطريقة التالية:

func application(_: UIApplication,
                 didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
  ...
  let appStoryboard = UIStoryboard(name: "Main", bundle: nil)
  guard let navigationController = appStoryboard.instantiateViewController(withIdentifier: "MainNavigation")
    as? UINavigationController else { return false }
  let castContainerVC = GCKCastContext.sharedInstance().createCastContainerController(for: navigationController)
    as GCKUICastContainerViewController
  castContainerVC.miniMediaControlsItemEnabled = true
  window = UIWindow(frame: UIScreen.main.bounds)
  window?.rootViewController = castContainerVC
  window?.makeKeyAndVisible()
  ...
}

أضف هذه السمة وsetter/getter للتحكم في مستوى رؤية وحدة التحكم المصغَّرة (سنستخدمها في قسم لاحق):

var isCastControlBarsEnabled: Bool {
    get {
      if useCastContainerViewController {
        let castContainerVC = (window?.rootViewController as? GCKUICastContainerViewController)
        return castContainerVC!.miniMediaControlsItemEnabled
      } else {
        let rootContainerVC = (window?.rootViewController as? RootContainerViewController)
        return rootContainerVC!.miniMediaControlsViewEnabled
      }
    }
    set(notificationsEnabled) {
      if useCastContainerViewController {
        var castContainerVC: GCKUICastContainerViewController?
        castContainerVC = (window?.rootViewController as? GCKUICastContainerViewController)
        castContainerVC?.miniMediaControlsItemEnabled = notificationsEnabled
      } else {
        var rootContainerVC: RootContainerViewController?
        rootContainerVC = (window?.rootViewController as? RootContainerViewController)
        rootContainerVC?.miniMediaControlsViewEnabled = notificationsEnabled
      }
    }
  }

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

8. تراكب تمهيدي

تتطلب قائمة التحقق من تصميم Google Cast أن يقوم أحد تطبيقات المرسل بتقديم زر البث للمستخدمين الحاليين لإبلاغهم بأن تطبيق المرسل يتوافق الآن مع البث ويساعد أيضًا المستخدمين الجدد على Google Cast.

صورة توضيحية لهاتف iPhone يشغّل تطبيق Cast Videos مع زر "إرسال" يظهر على الشاشة

تحتوي فئة GCKCastContext على طريقة presentCastInstructionsViewControllerOnce يمكن استخدامها لتمييز زر البث عند عرضه للمستخدمين لأول مرة. أضِف الرمز التالي إلى MediaViewController.swift وMediaTableViewController.swift:

override func viewDidLoad() {
  ...

  NotificationCenter.default.addObserver(self, selector: #selector(castDeviceDidChange),
                                         name: NSNotification.Name.gckCastStateDidChange,
                                         object: GCKCastContext.sharedInstance())
}

@objc func castDeviceDidChange(_: Notification) {
  if GCKCastContext.sharedInstance().castState != .noDevicesAvailable {
    // You can present the instructions on how to use Google Cast on
    // the first time the user uses you app
    GCKCastContext.sharedInstance().presentCastInstructionsViewControllerOnce(with: castButton)
  }
}

شغِّل التطبيق على جهازك الجوّال وستظهر لك الصورة التمهيدية.

9. تم توسيع وحدة التحكُّم

تتطلب قائمة التحقق من تصميم Google Cast أن يوفر تطبيق المرسل وحدة تحكم موسّعة للوسائط التي يتم بثها. وحدة التحكم الموسعة هي إصدار ملء الشاشة من وحدة التحكم المصغَّرة.

صورة توضيحية لهاتف iPhone يشغّل تطبيق Castفيديو ويشغّل فيديو مع ظهور وحدة التحكّم الموسَّعة في أسفل الشاشة

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

تنفّذ الفئة GCKUIExpandedMediaControlsViewController وظيفة هذا العرض.

إنّ أول إجراء عليك تنفيذه هو تفعيل وحدة التحكّم الموسّعة التلقائية في سياق البث. تعديل AppDelegate.swift لتفعيل وحدة التحكّم الموسّعة التلقائية:

import GoogleCast

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
  ...

  func application(_: UIApplication,
                   didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    ...
    // Add after the setShareInstanceWith(options) is set.
    GCKCastContext.sharedInstance().useDefaultExpandedMediaControls = true
    ...
  }
  ...
}

أضِف الرمز التالي إلى MediaViewController.swift لتحميل وحدة التحكّم الموسّعة عندما يبدأ المستخدم ببثّ فيديو:

@objc func playSelectedItemRemotely() {
  ...
  appDelegate?.isCastControlBarsEnabled = false
  GCKCastContext.sharedInstance().presentDefaultExpandedMediaControls()
}

سيتم أيضًا تشغيل وحدة التحكّم الموسّعة تلقائيًا عندما ينقر المستخدم على وحدة التحكّم المصغّرة.

شغِّل التطبيق وأرسِل فيديو. من المفترض أن تظهر لك وحدة التحكّم الموسّعة. انتقل مرة أخرى إلى قائمة الفيديوهات وعند النقر على وحدة التحكم المصغَّرة، سيتم تحميل وحدة التحكم الموسعة مرة أخرى.

10. إضافة دعم Cast Connect

تسمح مكتبة Cast Connect لتطبيقات المُرسِلين الحالية بالتواصل مع تطبيقات Android TV عبر بروتوكول Cast. تعتمد خدمة Cast Connect على البنية الأساسية للبث، ويعمل تطبيق Android TV كوحدة استقبال.

التبعيات

في Podfile، تأكَّد من أنّ google-cast-sdk يشير إلى 4.4.8 أو إصدار أحدث كما هو موضّح أدناه. إذا أجريت تعديلاً على الملف، شغِّل "pod update" من وحدة التحكّم لمزامنة التغيير مع مشروعك.

pod 'google-cast-sdk', '>=4.4.8'

GCKLaunchOptions

لتشغيل تطبيق Android TV، الذي يُشار إليه أيضًا باسم "مستقبل Android"، نحتاج إلى ضبط علامة androidReceiverCompatible على "صحيح" في كائن GCKLaunchOptions. يوضح كائن GCKLaunchOptions هذا كيفية تشغيل جهاز الاستقبال وتمريره إلى GCKCastOptions الذي يتم ضبطه في المثيل المشترك باستخدام GCKCastContext.setSharedInstanceWith.

أضِف الأسطر التالية إلى AppDelegate.swift:

let options = GCKCastOptions(discoveryCriteria:
                          GCKDiscoveryCriteria(applicationID: kReceiverAppID))
...
/** Following code enables CastConnect */
let launchOptions = GCKLaunchOptions()
launchOptions.androidReceiverCompatible = true
options.launchOptions = launchOptions

GCKCastContext.setSharedInstanceWith(options)

ضبط بيانات اعتماد الإطلاق

من جانب المُرسِل، يمكنك تحديد GCKCredentialsData لتمثيل المستخدمين المنضمين إلى الجلسة. credentials هو سلسلة يمكن تحديدها للمستخدم، طالما أن تطبيق ATV يمكنه فهمها. لا يتم تمرير GCKCredentialsData إلى تطبيق Android TV إلا أثناء التشغيل أو وقت الانضمام. وفي حال إعادة ضبطه أثناء الاتصال، لن يتم تمريره إلى تطبيق Android TV.

لضبط بيانات اعتماد الإطلاق، يجب ضبط GCKCredentialsData في أي وقت بعد ضبط GCKLaunchOptions. لتوضيح ذلك، يمكننا إضافة منطق لزر Creds لضبط بيانات الاعتماد التي سيتم تمريرها عند إنشاء الجلسة. أضِف الرمز التالي إلى MediaTableViewController.swift:

class MediaTableViewController: UITableViewController, GCKSessionManagerListener, MediaListModelDelegate, GCKRequestDelegate {
  ...
  private var credentials: String? = nil
  ...
  override func viewDidLoad() {
    ...
    navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Creds", style: .plain,
                                                       target: self, action: #selector(toggleLaunchCreds))
    ...
    setLaunchCreds()
  }
  ...
  @objc func toggleLaunchCreds(_: Any){
    if (credentials == nil) {
        credentials = "{\"userId\":\"id123\"}"
    } else {
        credentials = nil
    }
    Toast.displayMessage("Launch Credentials: "+(credentials ?? "Null"), for: 3, in: appDelegate?.window)
    print("Credentials set: "+(credentials ?? "Null"))
    setLaunchCreds()
  }
  ...
  func setLaunchCreds() {
    GCKCastContext.sharedInstance()
        .setLaunch(GCKCredentialsData(credentials: credentials))
  }
}

ضبط بيانات الاعتماد عند طلب التحميل

للتعامل مع credentials على كل من تطبيقَي الويب وجهاز استقبال Android TV، أضِف الرمز التالي في فئة MediaTableViewController.swift ضمن الدالة loadSelectedItem:

let mediaLoadRequestDataBuilder = GCKMediaLoadRequestDataBuilder()
...
mediaLoadRequestDataBuilder.credentials = credentials
...

استنادًا إلى تطبيق المُستلِم الذي يرسِل إليه المُرسِل، ستطبِّق حزمة تطوير البرامج (SDK) تلقائيًا بيانات الاعتماد الواردة أعلاه على الجلسة الجارية.

اختبار الاتصال بالبث

خطوات تثبيت حزمة APK Android TV على جهاز "Chromecast مع Google TV"

  1. ابحث عن عنوان IP لجهاز Android TV. وعادةً ما تكون متاحة ضمن الإعدادات > الشبكة الإنترنت > (اسم الشبكة التي يتصل بها جهازك) على يمين الشاشة، ستعرض هذه النافذة التفاصيل وعنوان IP لجهازك على الشبكة.
  2. استخدِم عنوان IP لجهازك للاتصال به عبر ADB باستخدام الوحدة الطرفية:
$ adb connect <device_ip_address>:5555
  1. من النافذة الطرفية، انتقِل إلى مجلد المستوى الأعلى لعيّنات الدروس التطبيقية حول الترميز التي نزّلتها في بداية هذا الدرس التطبيقي حول الترميز. على سبيل المثال:
$ cd Desktop/ios_codelab_src
  1. ثبِّت ملف apk .في هذا المجلد على Android TV من خلال تنفيذ ما يلي:
$ adb -s <device_ip_address>:5555 install android-tv-app.apk
  1. من المفترض أن تتمكّن الآن من رؤية أحد التطبيقات باسم بث الفيديوهات في قائمة تطبيقاتك على جهاز Android TV.
  2. وبعد الانتهاء من ذلك، أنشئ التطبيق وشغِّله على محاكي أو جهاز جوّال. عند إنشاء جلسة بث باستخدام جهاز Android TV، من المفترض أن يبدأ تشغيل تطبيق جهاز استقبال Android على Android TV. عند تشغيل فيديو من جهاز الإرسال الجوّال الذي يعمل بنظام التشغيل iOS، من المفترض أن يتم تشغيل الفيديو في جهاز الاستقبال الذي يعمل بنظام التشغيل Android وأن يسمح لك بالتحكم في التشغيل باستخدام جهاز التحكّم عن بُعد لجهاز Android TV.

11. تخصيص التطبيقات المصغّرة للبث

الإعداد

ابدأ بمجلد App-Done (تم التطبيق). أضِف ما يلي إلى طريقة applicationDidFinishLaunchingWithOptions في ملف AppDelegate.swift.

func application(_: UIApplication,
                 didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
  ...
  let styler = GCKUIStyle.sharedInstance()
  ...
}

بعد الانتهاء من تطبيق تخصيص واحد أو أكثر كما هو مذكور في باقي هذا الدرس التطبيقي، اتّبِع الأنماط من خلال استدعاء الرمز أدناه

styler.apply()

تخصيص طرق عرض Cast

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

styler.castViews.iconTintColor = .lightGray

يمكنك إلغاء الإعدادات التلقائية حسب كل شاشة إذا لزم الأمر. على سبيل المثال، لإلغاء اللون lightGrayColor للون الخفيف للرمز لوحدة التحكم في الوسائط الموسعة فقط.

styler.castViews.mediaControl.expandedController.iconTintColor = .green

تغيير الألوان

ويمكنك تخصيص لون الخلفية لجميع طرق العرض (أو بشكل فردي لكل طريقة عرض). يضبط الرمز التالي لون الخلفية على اللون الأزرق لجميع طرق العرض التي يوفّرها إطار عمل تطبيق Cast.

styler.castViews.backgroundColor = .blue
styler.castViews.mediaControl.miniController.backgroundColor = .yellow

تغيير الخطوط

يمكنك تخصيص الخطوط للتصنيفات المختلفة التي تظهر ضمن طرق عرض البث. لنضبط كل الخطوط على "Courier-Oblique" لأغراض التوضيح.

styler.castViews.headingTextFont = UIFont.init(name: "Courier-Oblique", size: 16) ?? UIFont.systemFont(ofSize: 16)
styler.castViews.mediaControl.headingTextFont = UIFont.init(name: "Courier-Oblique", size: 6) ?? UIFont.systemFont(ofSize: 6)

تغيير صور الأزرار التلقائية

أضف صورك المخصصة إلى المشروع، وعيّن الصور إلى الأزرار لتصميمها.

let muteOnImage = UIImage.init(named: "yourImage.png")
if let muteOnImage = muteOnImage {
  styler.castViews.muteOnImage = muteOnImage
}

تغيير مظهر زر البث

يمكنك أيضًا اختيار مظهر "التطبيقات المصغّرة للبث" باستخدام بروتوكول UIالمظهر. تظهر مظاهر التعليمات البرمجية التالية GCKUICastButton في جميع طرق العرض التي تظهر:

GCKUICastButton.appearance().tintColor = UIColor.gray

12. تهانينا

لقد تعرفت الآن على كيفية تفعيل ميزة Google Cast في تطبيق فيديو باستخدام أدوات Cast SDK على نظام التشغيل iOS.

لمزيد من التفاصيل، يُرجى الاطّلاع على دليل المطوِّر لمُرسِلي نظام التشغيل iOS.