1- نظرة عامة
سيعلّمك هذا الدرس التطبيقي حول الترميز كيفية تعديل تطبيق فيديو حالي على 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. تشغيل نموذج التطبيق
لنرى أولاً كيف يبدو نموذج التطبيق المكتمل. التطبيق عبارة عن مشغّل فيديو أساسي. يمكن للمستخدم اختيار فيديو من قائمة ثم يمكنه تشغيل الفيديو محليًا على الجهاز أو إرساله إلى جهاز 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
.
وحدة تحكّم MediaView
تعرض وحدة التحكم في طريقة العرض هذه البيانات الوصفية حول فيديو معين وتسمح للمستخدم بتشغيل الفيديو محليًا على الجهاز الجوّال.
تستضيف وحدة التحكم في العرض LocalPlayerView
، وبعض عناصر التحكم في الوسائط، ومنطقة نص لعرض وصف الفيديو المحدد. يغطي المشغل الجزء العلوي من الشاشة، مع ترك مساحة للوصف التفصيلي للفيديو أسفل المستخدم. ويمكن للمستخدم التشغيل/الإيقاف المؤقت أو طلب تشغيل الفيديو المحلي.
الأسئلة الشائعة
5. إضافة زر الإرسال
يعرض التطبيق الذي يعمل بتكنولوجيا 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. إرسال محتوى الفيديو
سيتم توسيع نموذج التطبيق لتشغيل مقاطع الفيديو عن بُعد أيضًا على جهاز بث. ولإجراء ذلك، نحتاج إلى الاستماع إلى الأحداث المختلفة التي يتم إنشاؤها بواسطة إطار عمل الإرسال.
بث الوسائط
على مستوى عالٍ، إذا أردت تشغيل وسائط على جهاز بث، يجب تنفيذ ما يلي:
- يمكنك إنشاء كائن
GCKMediaInformation
من Cast SDK الذي يصمِّم عنصر وسائط. - يتصل المستخدم بجهاز البث لتشغيل تطبيق الاستقبال.
- حمِّل الكائن
GCKMediaInformation
في جهاز الاستقبال وشغِّل المحتوى. - تتبع حالة الوسائط.
- إرسال أوامر التشغيل إلى المُستلِم بناءً على تفاعلات المستخدم.
تكمن الخطوة الأولى في تعيين كائن إلى عنصر آخر، وهو 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 وحدة تحكُّم مصغَّرة لتظهر عندما ينتقل المستخدم بعيدًا عن صفحة المحتوى الحالية. وتوفّر وحدة التحكّم المصغّرة إمكانية الوصول الفوري وتذكير مرئي لجلسة البث الحالية.
توفِّر حزمة تطوير البرامج (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.
تتضمن فئة 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 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
- ابحث عن عنوان 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- تخصيص أدوات الإرسال
الإعداد
ابدأ بمجلد "تم الاطلاع عليه بواسطة التطبيق". أضِف ما يلي إلى طريقة 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.