1. نظرة عامة
ستتعرّف في هذا الدرس التطبيقي على كيفية تعديل تطبيق فيديو حالي على نظام التشغيل 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، ولكنّه اختياري لبقية خطوات Codelab. إذا لم يكن لديك حساب، يمكنك تخطّي خطوة إضافة ميزة Cast Connect في نهاية هذا الدليل التعليمي.
تجربة الاستخدام
- يجب أن تكون لديك معرفة سابقة بتطوير تطبيقات iOS.
- كما ستحتاج أيضًا إلى معرفة سابقة بكيفية مشاهدة التلفزيون :)
كيف ستستخدم هذا البرنامج التعليمي؟
ما مدى رضاك عن تجربة إنشاء تطبيقات iOS؟
كيف تقيّم تجربتك في مشاهدة التلفزيون؟
2. الحصول على رمز النموذج
يمكنك تنزيل جميع الرموز النموذجية على جهاز الكمبيوتر...
وفك ضغط ملف zip الذي تم تنزيله.
3- تشغيل نموذج التطبيق
أولاً، لنرى كيف يبدو نموذج التطبيق المكتمل. التطبيق هو مشغل فيديو أساسي. يمكن للمستخدم اختيار فيديو من قائمة ثم تشغيل الفيديو على الجهاز أو إرساله على جهاز Google Cast.
بعد تنزيل الرمز، توضّح التعليمات التالية كيفية فتح نموذج التطبيق المكتمل وتشغيله في Xcode:
الأسئلة الشائعة
إعداد CocoaPods
لإعداد CocoaPods، انتقِل إلى وحدة التحكّم وثبِّت أداة Ruby التلقائية المتاحة على نظام التشغيل macOS:
sudo gem install cocoapods
في حال واجهت أي مشاكل، يُرجى الرجوع إلى المستندات الرسمية لتنزيل مدير التبعية وتثبيته.
إعداد المشروع
- انتقِل إلى الوحدة الطرفية وانتقِل إلى دليل الدرس التطبيقي حول الترميز.
- ثبِّت التبعيات من ملف Podfile.
cd app-done pod update pod install
- افتح Xcode واختَر فتح مشروع آخر...
- اختَر ملف
CastVideos-ios.xcworkspace
من الدليلapp-done
في نموذج مجلد الرمز البرمجي.
تشغيل التطبيق
اختَر الجهاز المستهدَف والمحاكي، ثم شغِّل التطبيق:
من المفترض أن يظهر تطبيق الفيديو بعد بضع ثوانٍ.
تأكد من النقر على "السماح" عند ظهور إشعار حول قبول اتصالات الشبكات الواردة. لن يظهر رمز البث إذا لم يتم قبول هذا الخيار.
انقر على زر البث واختَر جهاز Google Cast.
اختَر فيديو وانقر على زر التشغيل.
سيبدأ تشغيل الفيديو على جهاز Google Cast.
سيتم عرض وحدة التحكّم الموسّعة. يمكنك استخدام زر التشغيل أو الإيقاف المؤقت للتحكم في التشغيل.
انتقِل مرة أخرى إلى قائمة الفيديوهات.
تظهر الآن وحدة تحكُّم مصغّرة في أسفل الشاشة.
انقر على زر الإيقاف المؤقت في وحدة التحكّم المصغّرة لإيقاف الفيديو مؤقتًا على جهاز الاستقبال. انقر على زر التشغيل في وحدة التحكّم المصغّرة لمواصلة تشغيل الفيديو مرة أخرى.
انقر على زر البث لإيقاف البث على جهاز Google Cast.
4. إعداد مشروع البدء
نحتاج إلى إتاحة استخدام Google Cast في تطبيق البدء الذي نزّلته. في ما يلي بعض المصطلحات المتعلّقة بخدمة Google Cast التي سنستخدمها في هذا الدليل التعليمي حول البرمجة:
- تشغيل تطبيق المرسِل على جهاز جوّال أو كمبيوتر محمول،
- تطبيق استقبال يعمل على جهاز Google Cast
إعداد المشروع
أنت الآن جاهز للتطوير على مشروع البداية باستخدام Xcode:
- انتقِل إلى الوحدة الطرفية وانتقِل إلى دليل الدرس التطبيقي حول الترميز.
- ثبِّت التبعيات من ملف Podfile.
cd app-start pod update pod install
- افتح Xcode واختَر فتح مشروع آخر...
- اختَر ملف
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- إضافة زرّ "البث"
يعرض التطبيق المتوافق مع Cast زر البث في كل وحدة تحكّم في العرض. يؤدي النقر على زر البث إلى عرض قائمة بأجهزة البث التي يمكن للمستخدم اختيارها. إذا كان المستخدم يشغِّل المحتوى محليًا على جهاز المُرسِل، سيؤدي اختيار جهاز بث إلى بدء التشغيل أو استئناف تشغيله على جهاز البث هذا. يمكن للمستخدم النقر على زر البث في أي وقت أثناء جلسة البث وإيقاف بث تطبيقك على جهاز البث. يجب أن يكون المستخدم قادرًا على الاتصال بجهاز البث أو إلغاء ربطه أثناء التواجد في أي شاشة لتطبيقك، كما هو موضح في قائمة التحقق من تصميم Google Cast.
الإعداد
يتطلب مشروع البدء إعدادات Xcode والتبعيات نفسها التي استخدمتها في نموذج التطبيق المكتمل. يمكنك الرجوع إلى هذا القسم واتّباع الخطوات نفسها لإضافة GoogleCast.framework
إلى مشروع التطبيق الأوّلي.
الإعداد
يحتوي إطار عمل Google Cast على كائن مفردات عام، يُعرف باسم GCKCastContext
، الذي ينسق جميع أنشطة إطار العمل. يجب إعداد هذا العنصر في وقت مبكر من دورة حياة التطبيق، عادةً من خلال طريقة application(_:didFinishLaunchingWithOptions:)
في تفويض التطبيق، حتى يتم الاستئناف التلقائي للجلسة عند إعادة تشغيل تطبيق المُرسِل بشكل صحيح ويمكن أن يبدأ البحث عن الأجهزة.
يجب تقديم عنصر GCKCastOptions
عند إعداد GCKCastContext
. تحتوي هذه الفئة على خيارات تؤثر في سلوك إطار العمل. وأهم هذه العناصر هو معرّف تطبيق المُستلِم، والذي يُستخدَم لفلترة نتائج استكشاف أجهزة البث وتشغيل تطبيق المُستلِم عند بدء جلسة بث.
تُعد طريقة application(_:didFinishLaunchingWithOptions:)
أيضًا مكانًا جيدًا لإعداد تفويض التسجيل بتلقي رسائل التسجيل من إطار عمل Cast. يمكن أن تكون هذه الإعدادات مفيدة لتصحيح الأخطاء وتحديد المشاكل وحلّها.
عند تطوير تطبيق متوافق مع Cast، عليك التسجيل كمطوّر على 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- بث محتوى الفيديو
سنوسّع نطاق تطبيق النموذج ليشمل أيضًا تشغيل الفيديوهات عن بُعد على جهاز Cast. لتنفيذ ذلك، نحتاج إلى الاستماع إلى الأحداث المختلفة التي أنشأها إطار العمل Cast.
بث الوسائط
بشكل عام، إذا أردت تشغيل محتوى وسائط على جهاز بث، يجب تنفيذ ما يلي:
- أنشئ عنصر
GCKMediaInformation
من حزمة تطوير البرامج (SDK) لتطبيق Cast الذي يمثّل عنصر وسائط. - يتصل المستخدم بجهاز البث لتشغيل تطبيق الاستقبال.
- ما عليك سوى تحميل كائن
GCKMediaInformation
إلى جهاز الاستقبال وتشغيل المحتوى. - تتبُّع حالة الوسائط
- إرسال أوامر التشغيل إلى المستلِم استنادًا إلى تفاعلات المستخدم
تمثل الخطوة 1 ارتباط كائن بآخر؛ 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
الذي يتيح تشغيل الوسائط، ستنشئ حزمة SDK مثيلًا من GCKRemoteMediaClient
تلقائيًا. يمكن الوصول إليها باعتبارها السمة 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- وحدة تحكُّم صغيرة
تتطلب قائمة التحقّق من تصميم البثّ أن توفّر جميع تطبيقات البثّ وحدة تحكّم مصغّرة لكي تظهر للمستخدم عند خروجه من صفحة المحتوى الحالية. توفّر وحدة التحكّم المصغّرة إمكانية الوصول الفوري إلى جلسة البث الحالية وتذكيرًا مرئيًا بها.
توفّر حزمة تطوير البرامج (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()
...
}
أضِف هذه السمة وطريقة الضبط/الحصول للتحكّم في مستوى ظهور وحدة التحكّم المصغرة (سنستخدمهما في قسم لاحق):
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.
تحتوي فئة 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 أن يقدّم تطبيق المُرسِل وحدة تحكّم موسّعة للوسائط التي يتم بثّها. وحدة التحكّم الموسّعة هي نسخة بملء الشاشة من وحدة التحكّم المصغّرة.
جهاز التحكّم الموسّع هو طريقة عرض بملء الشاشة تتيح التحكّم الكامل في تشغيل الوسائط عن بُعد. من المفترض أن تتيح طريقة العرض هذه لتطبيق البث إدارة كل جانب قابل للإدارة في جلسة البث، باستثناء التحكّم في مستوى صوت جهاز الاستقبال ودورة حياة الجلسة (الاتصال أو إيقاف البث). ويقدّم أيضًا جميع معلومات الحالة حول جلسة الوسائط (العمل الفني والعنوان والترجمة والشرح وما إلى ذلك).
يتم تنفيذ وظائف هذا العرض من خلال فئة 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
على 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 Receiver، أضِف الرمز البرمجي التالي في فئة MediaTableViewController.swift
ضمن الدالة loadSelectedItem
:
let mediaLoadRequestDataBuilder = GCKMediaLoadRequestDataBuilder()
...
mediaLoadRequestDataBuilder.credentials = credentials
...
استنادًا إلى تطبيق المُستلِم الذي يبثّه المُرسِل، ستطبِّق حزمة تطوير البرامج (SDK) تلقائيًا بيانات الاعتماد المذكورة أعلاه على الجلسة الجارية.
اختبار Cast Connect
خطوات تثبيت حزمة APK Android TV على جهاز "Chromecast مع Google TV"
- ابحث عن عنوان IP لجهاز Android TV. يتوفّر هذا الخيار عادةً ضمن الإعدادات > الشبكة والإنترنت > (اسم الشبكة التي يتصل بها جهازك). على يسار الصفحة، ستظهر التفاصيل وعنوان IP لجهازك على الشبكة.
- استخدِم عنوان IP لجهازك للاتصال به عبر ADB باستخدام وحدة التحكّم الطرفية:
$ adb connect <device_ip_address>:5555
- من النافذة الطرفية، انتقِل إلى مجلد المستوى الأعلى لعيّنات الدروس التطبيقية حول الترميز التي نزّلتها في بداية هذا الدرس التطبيقي حول الترميز. على سبيل المثال:
$ cd Desktop/ios_codelab_src
- ثبِّت ملف apk .في هذا المجلد على Android TV من خلال تنفيذ ما يلي:
$ adb -s <device_ip_address>:5555 install android-tv-app.apk
- ستتمكّن الآن من رؤية التطبيق باسم بث الفيديوهات في قائمة تطبيقاتك على جهاز Android TV.
- بعد الانتهاء، يمكنك إنشاء التطبيق وتشغيله على جهاز محاكاة أو جهاز جوّال. عند إنشاء جلسة بث باستخدام جهاز 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()
تخصيص طرق العرض على أجهزة البث
يمكنك تخصيص جميع طرق العرض التي يُديرها إطار عمل تطبيق البث من خلال توفير إرشادات تلقائية للأنماط في جميع طرق العرض. على سبيل المثال، لنقم بتغيير لون الأيقونة.
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. تهانينا
لديك الآن معلومات عن كيفية تفعيل ميزة "البث" في تطبيق فيديو باستخدام التطبيقات المصغّرة لحزمة تطوير البرامج (SDK) لميزة "البث" على أجهزة iOS.
لمزيد من التفاصيل، يُرجى الاطّلاع على دليل المطوّر الخاص بتطبيق iOS Sender.