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

1- نظرة عامة

شعار Google Cast

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

ما المقصود بـ Google Cast؟

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

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

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

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

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

ما ستتعرَّف عليه

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

المتطلبات

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

التجربة

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

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

قراءة فقط قراءة النموذج وإكمال التدريبات

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

نوفمبر متوسط خبير

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

نوفمبر متوسط محترف

2- الحصول على نموذج الشفرة

يمكنك إما تنزيل كل نماذج الشفرة إلى جهاز الكمبيوتر...

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

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 يشغِّل تطبيق CastVideos مع ظهور وحدة التحكّم المصغّرة في أسفل الشاشة

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

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

4. إعداد المشروع الافتتاحي

رسم توضيحي لجهاز iPhone يشغِّل تطبيق CastVideos

يجب إضافة 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.

وحدة تحكّم MediaView

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

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

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

5. إضافة زر الإرسال

رسم توضيحي للثلث العلوي من جهاز iPhone يشغِّل تطبيق CastVideos، يعرض زر الإرسال في أعلى يسار الشاشة

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

الإعدادات

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

الإعداد

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

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

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

عند تطوير تطبيقك الذي يستخدم تكنولوجيا 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، يعرض تفاصيل عن فيديو معيّن ("دموع فولاذ"). يظهر المشغّل المصغّر في الأسفل.

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

بث الوسائط

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

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

تكمن الخطوة الأولى في تعيين كائن إلى عنصر آخر، وهو GCKMediaInformation الذي يفهمه تطبيق Cast SDK وMediaItem هو غلاف تطبيقنا لعنصر الوسائط. ويمكننا بسهولة ربط MediaItem بـ GCKMediaInformation. لقد انتهينا من الخطوة 2 في القسم السابق. من السهل تنفيذ الخطوة 3 باستخدام Cast SDK.

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

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

private var playbackMode = PlaybackMode.none

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

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

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

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

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

تتم إدارة جلسات الإرسال بواسطة 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. وحدة تحكم صغيرة

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

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

توفِّر حزمة تطوير البرامج (SDK) للإرسال شريط تحكُّم 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()
  ...
}

أضف هذه الخاصية وseter/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 يشغِّل تطبيق CastVideos مع تركيب زر الإرسال، ويبرز زر الإرسال ويعرض الرسالة "المس لإرسال الوسائط إلى التلفزيون ومكبّرات الصوت".

تتضمن فئة 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 يشغِّل تطبيق CastVideos ويشغِّل فيديو مع ظهور وحدة التحكّم الموسّعة في أسفل الشاشة

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

يتم تنفيذ وظائف هذا الملف الشخصي من خلال الفئة 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 Connect على البنية الأساسية لتطبيق Cast، حيث يعمل تطبيق Android TV كجهاز استقبال.

العناصر التابعة

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

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

خيارات GCKLauncher

لتشغيل تطبيق Android TV، الذي يُشار إليه أيضًا باسم مستلم Android، نحتاج إلى تعيين علامة androidReceiverCompatible إلى true في الكائن 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) بيانات الاعتماد أعلاه تلقائيًا على الجلسة الجارية.

جارٍ اختبار Cast Connect

خطوات تثبيت 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- تخصيص أدوات الإرسال

الإعداد

ابدأ بمجلد "تم الاطلاع عليه بواسطة التطبيق". أضِف ما يلي إلى طريقة applicationDidFinishLaunchingWithOptions في ملف AppDelegate.swift.

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

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

styler.apply()

تخصيص طرق عرض الإرسال

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

styler.castViews.iconTintColor = .lightGray

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

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

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

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

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.