دمج البث في تطبيق iOS

يوضّح دليل المطوِّر هذا كيفية إضافة دعم Google Cast إلى جهاز iOS. تطبيق المُرسِل باستخدام حزمة تطوير البرامج (SDK) لمُرسِل iOS.

فإن الجهاز الجوّال أو الكمبيوتر المحمول هو المرسِل الذي يتحكم في تشغيل الفيديو جهاز Google Cast هو جهاز الاستقبال الذي يعرض المحتوى على التلفزيون.

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

يستخدم إطار عمل المرسِل تصميمًا غير متزامن لمعاودة الاتصال لإعلام المُرسِل للأحداث والانتقال بين الحالات المختلفة لحياة تطبيق Cast الدورة.

مسار التطبيق

تصف الخطوات التالية مسار التنفيذ عالي المستوى النموذجي للمرسل تطبيق iOS:

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

لتحديد مشاكل المُرسِل وحلّها، عليك تفعيل تسجيل الدخول.

للحصول على قائمة شاملة بجميع الفئات والأساليب والأحداث في Google Cast إطار عمل iOS، يُرجى مراجعة Google Cast iOS API. المرجع: تتناول الأقسام التالية الخطوات لدمج Cast في تطبيقك على iOS.

طرق الاتصال من سلسلة المحادثات الرئيسية

إعداد سياق البث

ويحتوي إطار العمل الخاص بالبث على كائن مفرد عمومي، GCKCastContext، وينسق جميع أنشطة إطار العمل. يجب إعداد هذا الكائن. في وقت مبكر من دورة حياة التطبيق، وعادةً في طريقة -[application:didFinishLaunchingWithOptions:] لتفويض التطبيق، لذلك أن الاستئناف التلقائي للجلسة عند إعادة تشغيل تطبيق المرسل يمكن أن يبدأ بشكل صحيح.

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

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

سريعة
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, GCKLoggerDelegate {
  let kReceiverAppID = kGCKDefaultMediaReceiverApplicationID
  let kDebugLoggingEnabled = true

  var window: UIWindow?

  func applicationDidFinishLaunching(_ application: UIApplication) {
    let criteria = GCKDiscoveryCriteria(applicationID: kReceiverAppID)
    let options = GCKCastOptions(discoveryCriteria: criteria)
    GCKCastContext.setSharedInstanceWith(options)

    // Enable logger.
    GCKLogger.sharedInstance().delegate = self

    ...
  }

  // MARK: - GCKLoggerDelegate

  func logMessage(_ message: String,
                  at level: GCKLoggerLevel,
                  fromFunction function: String,
                  location: String) {
    if (kDebugLoggingEnabled) {
      print(function + " - " + message)
    }
  }
}
الهدف-ج

AppDelegate.h

@interface AppDelegate () <GCKLoggerDelegate>
@end

AppDelegate.m

@implementation AppDelegate

static NSString *const kReceiverAppID = @"AABBCCDD";
static const BOOL kDebugLoggingEnabled = YES;

- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  GCKDiscoveryCriteria *criteria = [[GCKDiscoveryCriteria alloc]
                                    initWithApplicationID:kReceiverAppID];
  GCKCastOptions *options = [[GCKCastOptions alloc] initWithDiscoveryCriteria:criteria];
  [GCKCastContext setSharedInstanceWithOptions:options];

  // Enable logger.
  [GCKLogger sharedInstance].delegate = self;

  ...

  return YES;
}

...

#pragma mark - GCKLoggerDelegate

- (void)logMessage:(NSString *)message
           atLevel:(GCKLoggerLevel)level
      fromFunction:(NSString *)function
          location:(NSString *)location {
  if (kDebugLoggingEnabled) {
    NSLog(@"%@ - %@, %@", function, message, location);
  }
}

@end

تطبيقات Cast UX المصغّرة

توفِّر حزمة تطوير البرامج (SDK) لنظام التشغيل iOS لتكنولوجيا Cast هذه التطبيقات المصغّرة التي تتوافق مع تصميم Cast. قائمة التحقق:

  • محتوى تمهيدي: للفئة GCKCastContext طريقة، presentCastInstructionsViewControllerOnceWithCastButton، الذي يمكن استخدامه لتسليط الضوء على زر البث في المرة الأولى التي يقوم فيها مستقبل الويب متاح. يمكن لتطبيق المرسِل تخصيص النص وموضع العنوان النص وزر الإغلاق.

  • زر البث: بدءًا من الإصدار 4.6.0 من حزمة تطوير البرامج (SDK) للمرسِل في نظام التشغيل iOS، سيظهر زر البث دائمًا عندما يكون جهاز المُرسِل متصلاً بشبكة Wi-Fi. في المرة الأولى التي ينقر فيها المستخدم على الزر "إرسال" بعد بدء تشغيل التطبيق للمرة الأولى، سيظهر مربع حوار الأذونات حتى يتمكن المستخدم من منح التطبيق حق الوصول إلى الشبكة المحلية للأجهزة على الشبكة. بعد ذلك، عندما ينقر المستخدم على زر البث، سيظهر مربع حوار يضم الأجهزة التي تم اكتشافها. عندما ينقر المستخدم فوق باستخدام زر البث أثناء توصيل الجهاز، يتم عرض البيانات الوصفية للوسائط (مثل العنوان واسم استوديو التسجيل وصورة مصغّرة صورة) أو يسمح للمستخدم بقطع الاتصال بجهاز البث. عندما يجرّب ينقر على زر البث في حين عدم توفّر أي أجهزة، ستظهر سيتم عرضها لتزويد المستخدم بمعلومات عن سبب عدم العثور على الأجهزة. وكيفية استكشاف الأخطاء وإصلاحها.

  • وحدة تحكُّم صغيرة: عندما يبثّ المستخدم المحتوى وينتقل من المحتوى الحالي المحتوى أو وحدة التحكم الموسعة إلى شاشة أخرى في تطبيق المرسل، وحدة تحكم صغيرة أسفل الشاشة للسماح للمستخدم الاطّلاع على البيانات الوصفية للوسائط التي يتم بثها حاليًا والتحكّم في تشغيلها.

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

إضافة زر البث

يوفّر إطار العمل مكوّن زر البث كفئة فرعية UIButton. مُمْكِنْ ستتم إضافتها إلى شريط عناوين التطبيق من خلال التفافها في علامة UIBarButtonItem. نموذج يمكن للفئة الفرعية "UIViewController" تثبيت زر البث على النحو التالي:

سريعة
let castButton = GCKUICastButton(frame: CGRect(x: 0, y: 0, width: 24, height: 24))
castButton.tintColor = UIColor.gray
navigationItem.rightBarButtonItem = UIBarButtonItem(customView: castButton)
الهدف-ج
GCKUICastButton *castButton = [[GCKUICastButton alloc] initWithFrame:CGRectMake(0, 0, 24, 24)];
castButton.tintColor = [UIColor grayColor];
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:castButton];

سيؤدي النقر على الزر تلقائيًا إلى فتح مربّع حوار "البث" الذي توفّره إطار العمل.

GCKUICastButton يمكن أيضًا إضافته مباشرةً إلى مخطط القصة.

ضبط ميزة "اكتشاف الأجهزة"

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

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

آلية عمل إدارة الجلسات

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

يدير الصف الجلسات GCKSessionManager، وهي خاصية GCKCastContext يتم تمثيل الجلسات الفردية من خلال الفئات الفرعية للفئة. GCKSession: على سبيل المثال، GCKCastSession الجلسات التي تتضمن أجهزة بث. يمكنك الوصول إلى البثّ النشط حاليًا الجلسة (إن وُجدت)، باعتبارها السمة currentCastSession في GCKSessionManager.

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

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

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

نقل البث

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

للحصول على الجهاز الوجهة الجديد أثناء نقل البث، استخدِم GCKCastSession#device الموقع خلال فترة [sessionManager:didResumeCastSession:] معاودة الاتصال.

عرض نقل البث على جهاز الاستقبال على الويب لمزيد من المعلومات.

إعادة الاتصال تلقائيًا

يضيف إطار عمل Google Cast منطق إعادة الاتصال لمعالجة إعادة الاتصال تلقائيًا في العديد من الحالات الدقيقة، مثل:

  • استرداد البيانات بعد فقدان شبكة Wi-Fi مؤقتًا
  • استرداد البيانات بعد إيقاف وضع السكون للجهاز
  • استرداد البيانات بعد تشغيل التطبيق في الخلفية
  • استرداد الحساب في حال تعطُّل التطبيق

آلية عمل التحكّم في الوسائط

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

كل الطرق على GCKRemoteMediaClient التي تصدر طلبات إلى مستقبل الويب فستُرجع كائن GCKRequest الذي لتتبع هذا الطلب. حاسمة GCKRequestDelegate إلى هذا الكائن لتلقّي إشعارات حول الأحداث نتيجة العملية.

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

ضبط البيانات الوصفية للوسائط

تشير رسالة الأشكال البيانية GCKMediaMetadata الفئة معلومات حول عنصر الوسائط الذي تريد بثه. ما يلي: على سبيل المثال، ينشئ مثيل GCKMediaMetadata جديدًا لفيلم ويحدد العنوان، وعنوان فرعي واسم استوديو التسجيل وصورتين.

سريعة
let metadata = GCKMediaMetadata()
metadata.setString("Big Buck Bunny (2008)", forKey: kGCKMetadataKeyTitle)
metadata.setString("Big Buck Bunny tells the story of a giant rabbit with a heart bigger than " +
  "himself. When one sunny day three rodents rudely harass him, something " +
  "snaps... and the rabbit ain't no bunny anymore! In the typical cartoon " +
  "tradition he prepares the nasty rodents a comical revenge.",
                   forKey: kGCKMetadataKeySubtitle)
metadata.addImage(GCKImage(url: URL(string: "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/images/BigBuckBunny.jpg")!,
                           width: 480,
                           height: 360))
الهدف-ج
GCKMediaMetadata *metadata = [[GCKMediaMetadata alloc]
                                initWithMetadataType:GCKMediaMetadataTypeMovie];
[metadata setString:@"Big Buck Bunny (2008)" forKey:kGCKMetadataKeyTitle];
[metadata setString:@"Big Buck Bunny tells the story of a giant rabbit with a heart bigger than "
 "himself. When one sunny day three rodents rudely harass him, something "
 "snaps... and the rabbit ain't no bunny anymore! In the typical cartoon "
 "tradition he prepares the nasty rodents a comical revenge."
             forKey:kGCKMetadataKeySubtitle];
[metadata addImage:[[GCKImage alloc]
                    initWithURL:[[NSURL alloc] initWithString:@"https://commondatastorage.googleapis.com/"
                                 "gtv-videos-bucket/sample/images/BigBuckBunny.jpg"]
                    width:480
                    height:360]];

راجع قسم تحديد الصور التخزين المؤقت قسم حول استخدام الصور مع البيانات الوصفية للوسائط.

تحميل الوسائط

لتحميل ملف وسائط، أنشِئ GCKMediaInformation مثال باستخدام بيانات التعريف للوسائط. ثم احصل على GCKCastSession و استخدام GCKRemoteMediaClient لتحميل الوسائط على تطبيق الاستقبال. يمكنك بعد ذلك استخدام GCKRemoteMediaClient للتحكّم في تطبيق مشغّل وسائط قيد التشغيل على جهاز الاستقبال، مثلاً لتشغيله والتوقف والتوقف.

سريعة
let url = URL.init(string: "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4")
guard let mediaURL = url else {
  print("invalid mediaURL")
  return
}

let mediaInfoBuilder = GCKMediaInformationBuilder.init(contentURL: mediaURL)
mediaInfoBuilder.streamType = GCKMediaStreamType.none;
mediaInfoBuilder.contentType = "video/mp4"
mediaInfoBuilder.metadata = metadata;
mediaInformation = mediaInfoBuilder.build()

guard let mediaInfo = mediaInformation else {
  print("invalid mediaInformation")
  return
}

if let request = sessionManager.currentSession?.remoteMediaClient?.loadMedia(mediaInfo) {
  request.delegate = self
}
الهدف-ج
GCKMediaInformationBuilder *mediaInfoBuilder =
  [[GCKMediaInformationBuilder alloc] initWithContentURL:
   [NSURL URLWithString:@"https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"]];
mediaInfoBuilder.streamType = GCKMediaStreamTypeNone;
mediaInfoBuilder.contentType = @"video/mp4";
mediaInfoBuilder.metadata = metadata;
self.mediaInformation = [mediaInfoBuilder build];

GCKRequest *request = [self.sessionManager.currentSession.remoteMediaClient loadMedia:self.mediaInformation];
if (request != nil) {
  request.delegate = self;
}

اطّلع أيضًا على القسم الذي يتناول باستخدام مقاطع الوسائط

تنسيق فيديو بدقة 4K

لتحديد تنسيق الفيديو للوسائط، استخدِم السمة videoInfo GCKMediaStatus للحصول على المثيل الحالي GCKVideoInfo يحتوي هذا المثال على نوع تنسيق HDR TV والارتفاع والعرض في البكسل. يُشار إلى خيارات تنسيق 4K في السمة hdrType حسب التعداد. GCKVideoInfoHDRType.

إضافة وحدات تحكُّم مصغّرة

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

يوفّر إطار عمل Google Cast شريط تحكّم GCKUIMiniMediaControlsViewController، والذي يمكن إضافته إلى المشاهد التي تريد عرض وحدة التحكم المصغَّرة فيها.

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

راجِع تخصيص واجهة مستخدم iOS Sender لمعرفة طريقة تطبيق المرسل ضبط مظهر أدوات البث.

هناك طريقتان لإضافة وحدة التحكم المصغّرة إلى تطبيق المرسِل:

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

التفاف باستخدام GCKUICastContainerViewController

الطريقة الأولى هي استخدام GCKUICastContainerViewController والذي يلتف وحدة تحكم عرض أخرى ويضيف GCKUIMiniMediaControlsViewController في أسفل الصفحة وهذه الطريقة محدودة لأنه لا يمكنك تخصيص الرسوم المتحركة ولا يمكنه تهيئة سلوك وحدة التحكم في عرض الحاوية.

عادةً ما يتم تنفيذ هذه الطريقة الأولى في طريقة -[application:didFinishLaunchingWithOptions:] لتفويض التطبيق:

سريعة
func applicationDidFinishLaunching(_ application: UIApplication) {
  ...

  // Wrap main view in the GCKUICastContainerViewController and display the mini controller.
  let appStoryboard = UIStoryboard(name: "Main", bundle: nil)
  let navigationController = appStoryboard.instantiateViewController(withIdentifier: "MainNavigation")
  let castContainerVC =
          GCKCastContext.sharedInstance().createCastContainerController(for: navigationController)
  castContainerVC.miniMediaControlsItemEnabled = true
  window = UIWindow(frame: UIScreen.main.bounds)
  window!.rootViewController = castContainerVC
  window!.makeKeyAndVisible()

  ...
}
الهدف-ج
- (BOOL)application:(UIApplication *)application
        didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  ...

  // Wrap main view in the GCKUICastContainerViewController and display the mini controller.
  UIStoryboard *appStoryboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
  UINavigationController *navigationController =
          [appStoryboard instantiateViewControllerWithIdentifier:@"MainNavigation"];
  GCKUICastContainerViewController *castContainerVC =
          [[GCKCastContext sharedInstance] createCastContainerControllerForViewController:navigationController];
  castContainerVC.miniMediaControlsItemEnabled = YES;
  self.window = [[UIWindow alloc] initWithFrame:UIScreen.mainScreen.bounds];
  self.window.rootViewController = castContainerVC;
  [self.window makeKeyAndVisible];
  ...

}
سريعة
var castControlBarsEnabled: Bool {
  set(enabled) {
    if let castContainerVC = self.window?.rootViewController as? GCKUICastContainerViewController {
      castContainerVC.miniMediaControlsItemEnabled = enabled
    } else {
      print("GCKUICastContainerViewController is not correctly configured")
    }
  }
  get {
    if let castContainerVC = self.window?.rootViewController as? GCKUICastContainerViewController {
      return castContainerVC.miniMediaControlsItemEnabled
    } else {
      print("GCKUICastContainerViewController is not correctly configured")
      return false
    }
  }
}
الهدف-ج

AppDelegate.h

@interface AppDelegate : UIResponder <UIApplicationDelegate>

@property (nonatomic, strong) UIWindow *window;
@property (nonatomic, assign) BOOL castControlBarsEnabled;

@end

AppDelegate.m

@implementation AppDelegate

...

- (void)setCastControlBarsEnabled:(BOOL)notificationsEnabled {
  GCKUICastContainerViewController *castContainerVC;
  castContainerVC =
      (GCKUICastContainerViewController *)self.window.rootViewController;
  castContainerVC.miniMediaControlsItemEnabled = notificationsEnabled;
}

- (BOOL)castControlBarsEnabled {
  GCKUICastContainerViewController *castContainerVC;
  castContainerVC =
      (GCKUICastContainerViewController *)self.window.rootViewController;
  return castContainerVC.miniMediaControlsItemEnabled;
}

...

@end

التضمين في وحدة التحكّم في العرض الحالية

والطريقة الثانية هي إضافة وحدة التحكم الصغيرة مباشرة إلى طريقة العرض الحالية تحكم باستخدام createMiniMediaControlsViewController لإنشاء GCKUIMiniMediaControlsViewController المثيل ثم إضافته إلى وحدة التحكم في عرض الحاوية كطريقة عرض فرعية.

إعداد وحدة التحكم في العرض من خلال تفويض التطبيق:

سريعة
func application(_ application: UIApplication,
                 didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
  ...

  GCKCastContext.sharedInstance().useDefaultExpandedMediaControls = true
  window?.clipsToBounds = true

  let rootContainerVC = (window?.rootViewController as? RootContainerViewController)
  rootContainerVC?.miniMediaControlsViewEnabled = true

  ...

  return true
}
الهدف-ج
- (BOOL)application:(UIApplication *)application
    didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  ...

  [GCKCastContext sharedInstance].useDefaultExpandedMediaControls = YES;

  self.window.clipsToBounds = YES;

  RootContainerViewController *rootContainerVC;
  rootContainerVC =
      (RootContainerViewController *)self.window.rootViewController;
  rootContainerVC.miniMediaControlsViewEnabled = YES;

  ...

  return YES;
}

في وحدة التحكّم في العرض الجذر، يمكنك إنشاء GCKUIMiniMediaControlsViewController. مثيل وإضافته إلى وحدة التحكم في عرض الحاوية كطريقة عرض فرعية:

سريعة
let kCastControlBarsAnimationDuration: TimeInterval = 0.20

@objc(RootContainerViewController)
class RootContainerViewController: UIViewController, GCKUIMiniMediaControlsViewControllerDelegate {
  @IBOutlet weak private var _miniMediaControlsContainerView: UIView!
  @IBOutlet weak private var _miniMediaControlsHeightConstraint: NSLayoutConstraint!
  private var miniMediaControlsViewController: GCKUIMiniMediaControlsViewController!
  var miniMediaControlsViewEnabled = false {
    didSet {
      if self.isViewLoaded {
        self.updateControlBarsVisibility()
      }
    }
  }

  var overriddenNavigationController: UINavigationController?

  override var navigationController: UINavigationController? {

    get {
      return overriddenNavigationController
    }

    set {
      overriddenNavigationController = newValue
    }
  }
  var miniMediaControlsItemEnabled = false

  override func viewDidLoad() {
    super.viewDidLoad()
    let castContext = GCKCastContext.sharedInstance()
    self.miniMediaControlsViewController = castContext.createMiniMediaControlsViewController()
    self.miniMediaControlsViewController.delegate = self
    self.updateControlBarsVisibility()
    self.installViewController(self.miniMediaControlsViewController,
                               inContainerView: self._miniMediaControlsContainerView)
  }

  func updateControlBarsVisibility() {
    if self.miniMediaControlsViewEnabled && self.miniMediaControlsViewController.active {
      self._miniMediaControlsHeightConstraint.constant = self.miniMediaControlsViewController.minHeight
      self.view.bringSubview(toFront: self._miniMediaControlsContainerView)
    } else {
      self._miniMediaControlsHeightConstraint.constant = 0
    }
    UIView.animate(withDuration: kCastControlBarsAnimationDuration, animations: {() -> Void in
      self.view.layoutIfNeeded()
    })
    self.view.setNeedsLayout()
  }

  func installViewController(_ viewController: UIViewController?, inContainerView containerView: UIView) {
    if let viewController = viewController {
      self.addChildViewController(viewController)
      viewController.view.frame = containerView.bounds
      containerView.addSubview(viewController.view)
      viewController.didMove(toParentViewController: self)
    }
  }

  func uninstallViewController(_ viewController: UIViewController) {
    viewController.willMove(toParentViewController: nil)
    viewController.view.removeFromSuperview()
    viewController.removeFromParentViewController()
  }

  override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == "NavigationVCEmbedSegue" {
      self.navigationController = (segue.destination as? UINavigationController)
    }
  }

...
الهدف-ج

RootContainerViewController.h

static const NSTimeInterval kCastControlBarsAnimationDuration = 0.20;

@interface RootContainerViewController () <GCKUIMiniMediaControlsViewControllerDelegate> {
  __weak IBOutlet UIView *_miniMediaControlsContainerView;
  __weak IBOutlet NSLayoutConstraint *_miniMediaControlsHeightConstraint;
  GCKUIMiniMediaControlsViewController *_miniMediaControlsViewController;
}

@property(nonatomic, weak, readwrite) UINavigationController *navigationController;

@property(nonatomic, assign, readwrite) BOOL miniMediaControlsViewEnabled;
@property(nonatomic, assign, readwrite) BOOL miniMediaControlsItemEnabled;

@end

RootContainerViewController.m

@implementation RootContainerViewController

- (void)viewDidLoad {
  [super viewDidLoad];
  GCKCastContext *castContext = [GCKCastContext sharedInstance];
  _miniMediaControlsViewController =
      [castContext createMiniMediaControlsViewController];
  _miniMediaControlsViewController.delegate = self;

  [self updateControlBarsVisibility];
  [self installViewController:_miniMediaControlsViewController
              inContainerView:_miniMediaControlsContainerView];
}

- (void)setMiniMediaControlsViewEnabled:(BOOL)miniMediaControlsViewEnabled {
  _miniMediaControlsViewEnabled = miniMediaControlsViewEnabled;
  if (self.isViewLoaded) {
    [self updateControlBarsVisibility];
  }
}

- (void)updateControlBarsVisibility {
  if (self.miniMediaControlsViewEnabled &&
      _miniMediaControlsViewController.active) {
    _miniMediaControlsHeightConstraint.constant =
        _miniMediaControlsViewController.minHeight;
    [self.view bringSubviewToFront:_miniMediaControlsContainerView];
  } else {
    _miniMediaControlsHeightConstraint.constant = 0;
  }
  [UIView animateWithDuration:kCastControlBarsAnimationDuration
                   animations:^{
                     [self.view layoutIfNeeded];
                   }];
  [self.view setNeedsLayout];
}

- (void)installViewController:(UIViewController *)viewController
              inContainerView:(UIView *)containerView {
  if (viewController) {
    [self addChildViewController:viewController];
    viewController.view.frame = containerView.bounds;
    [containerView addSubview:viewController.view];
    [viewController didMoveToParentViewController:self];
  }
}

- (void)uninstallViewController:(UIViewController *)viewController {
  [viewController willMoveToParentViewController:nil];
  [viewController.view removeFromSuperview];
  [viewController removeFromParentViewController];
}

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
  if ([segue.identifier isEqualToString:@"NavigationVCEmbedSegue"]) {
    self.navigationController =
        (UINavigationController *)segue.destinationViewController;
  }
}

...

@end

تشير رسالة الأشكال البيانية GCKUIMiniMediaControlsViewControllerDelegate يخبر وحدة التحكم في عرض المضيف بالوقت الذي من المفترض أن تكون فيه وحدة التحكم المصغَّرة مرئية:

سريعة
  func miniMediaControlsViewController(_: GCKUIMiniMediaControlsViewController,
                                       shouldAppear _: Bool) {
    updateControlBarsVisibility()
  }
الهدف-ج
- (void)miniMediaControlsViewController:
            (GCKUIMiniMediaControlsViewController *)miniMediaControlsViewController
                           shouldAppear:(BOOL)shouldAppear {
  [self updateControlBarsVisibility];
}

إضافة وحدة تحكُّم موسّعة

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

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

يتم تنفيذ وظيفة طريقة العرض هذه بواسطة GCKUIExpandedMediaControlsViewController الصف.

أول شيء عليك فعله هو تمكين وحدة التحكم الموسعة الافتراضية في سياق البثّ. تعديل تفويض التطبيق لتفعيل وحدة التحكّم الموسّعة التلقائية:

سريعة
func applicationDidFinishLaunching(_ application: UIApplication) {
  ..

  GCKCastContext.sharedInstance().useDefaultExpandedMediaControls = true

  ...
}
الهدف-ج
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  ...

  [GCKCastContext sharedInstance].useDefaultExpandedMediaControls = YES;

  ..
}

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

سريعة
func playSelectedItemRemotely() {
  GCKCastContext.sharedInstance().presentDefaultExpandedMediaControls()

  ...

  // Load your media
  sessionManager.currentSession?.remoteMediaClient?.loadMedia(mediaInformation)
}
الهدف-ج
- (void)playSelectedItemRemotely {
  [[GCKCastContext sharedInstance] presentDefaultExpandedMediaControls];

  ...

  // Load your media
  [self.sessionManager.currentSession.remoteMediaClient loadMedia:mediaInformation];
}

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

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

راجع تطبيق أنماط مخصصة على جهاز iOS التطبيق للتعرّف على كيفية ضبط تطبيق المرسِل لمظهر Google Cast المصغّر.

التحكم في مستوى الصوت

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

التحكّم في مستوى الصوت للزر الخارجي

يمكن استخدام أزرار التحكّم بمستوى الصوت الفعلية على جهاز المُرسِل لتغيير مستوى صوت جلسة البث على جهاز استقبال الويب باستخدام علامة physicalVolumeButtonsWillControlDeviceVolume على GCKCastOptions، والذي يتم تعيينه على GCKCastContext

سريعة
let criteria = GCKDiscoveryCriteria(applicationID: kReceiverAppID)
let options = GCKCastOptions(discoveryCriteria: criteria)
options.physicalVolumeButtonsWillControlDeviceVolume = true
GCKCastContext.setSharedInstanceWith(options)
الهدف-ج
GCKDiscoveryCriteria *criteria = [[GCKDiscoveryCriteria alloc]
                                          initWithApplicationID:kReceiverAppID];
GCKCastOptions *options = [[GCKCastOptions alloc]
                                          initWithDiscoveryCriteria :criteria];
options.physicalVolumeButtonsWillControlDeviceVolume = YES;
[GCKCastContext setSharedInstanceWithOptions:options];

التعامل مع الأخطاء

من المهم جدًا لتطبيقات المرسل أن تتعامل مع جميع استدعاءات الأخطاء وأن تقرر أفضل استجابة لكل مرحلة من دورة حياة البثّ. يمكن للتطبيق عرض للمستخدمين أو يمكن أن يقرروا إنهاء جلسة البث.

التسجيل

GCKLogger هو مفردات يُستخدم للتسجيل بواسطة إطار العمل. يمكنك استخدام GCKLoggerDelegate لتخصيص طريقة تعاملك مع رسائل السجل.

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

سريعة
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, GCKLoggerDelegate {
  let kReceiverAppID = kGCKDefaultMediaReceiverApplicationID
  let kDebugLoggingEnabled = true

  var window: UIWindow?

  func applicationDidFinishLaunching(_ application: UIApplication) {
    ...

    // Enable logger.
    GCKLogger.sharedInstance().delegate = self

    ...
  }

  // MARK: - GCKLoggerDelegate

  func logMessage(_ message: String,
                  at level: GCKLoggerLevel,
                  fromFunction function: String,
                  location: String) {
    if (kDebugLoggingEnabled) {
      print(function + " - " + message)
    }
  }
}
الهدف-ج

AppDelegate.h

@interface AppDelegate () <GCKLoggerDelegate>
@end

AppDelegate.m

@implementation AppDelegate

static NSString *const kReceiverAppID = @"AABBCCDD";
static const BOOL kDebugLoggingEnabled = YES;

- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  ...

  // Enable logger.
  [GCKLogger sharedInstance].delegate = self;

  ...

  return YES;
}

...

#pragma mark - GCKLoggerDelegate

- (void)logMessage:(NSString *)message
           atLevel:(GCKLoggerLevel)level
      fromFunction:(NSString *)function
          location:(NSString *)location {
  if (kDebugLoggingEnabled) {
    NSLog(@"%@ - %@, %@", function, message, location);
  }
}

@end

لتفعيل رسائل تصحيح الأخطاء والرسائل المطوَّلة أيضًا، أضِف هذا السطر إلى الرمز البرمجي التالي إعداد المفوَّض (المعروض سابقًا):

سريعة
let filter = GCKLoggerFilter.init()
filter.minimumLevel = GCKLoggerLevel.verbose
GCKLogger.sharedInstance().filter = filter
الهدف-ج
GCKLoggerFilter *filter = [[GCKLoggerFilter alloc] init];
[filter setMinimumLevel:GCKLoggerLevelVerbose];
[GCKLogger sharedInstance].filter = filter;

ويمكنك أيضًا تصفية رسائل السجل الناتجة عن GCKLogger اضبط الحد الأدنى لمستوى التسجيل لكل صف، على سبيل المثال:

سريعة
let filter = GCKLoggerFilter.init()
filter.setLoggingLevel(GCKLoggerLevel.verbose, forClasses: ["GCKUICastButton",
                                                            "GCKUIImageCache",
                                                            "NSMutableDictionary"])
GCKLogger.sharedInstance().filter = filter
الهدف-ج
GCKLoggerFilter *filter = [[GCKLoggerFilter alloc] init];
[filter setLoggingLevel:GCKLoggerLevelVerbose
             forClasses:@[@"GCKUICastButton",
                          @"GCKUIImageCache",
                          @"NSMutableDictionary"
                          ]];
[GCKLogger sharedInstance].filter = filter;

ويمكن أن تكون أسماء الفئات إما أسماء حرفية أو أنماط كروية، على سبيل المثال، "GCKUI\*" وGCK\*Session"