Cast را در برنامه iOS خود ادغام کنید

این راهنمای توسعه‌دهنده نحوه‌ی افزودن پشتیبانی گوگل کست به برنامه‌ی فرستنده‌ی iOS شما را با استفاده از iOS Sender SDK شرح می‌دهد.

دستگاه تلفن همراه یا لپ‌تاپ فرستنده‌ای است که پخش را کنترل می‌کند و دستگاه Google Cast گیرنده‌ای است که محتوا را در تلویزیون نمایش می‌دهد.

چارچوب فرستنده به فایل‌های باینری کتابخانه کلاس Cast و منابع مرتبط موجود در زمان اجرا روی فرستنده اشاره دارد. برنامه فرستنده یا برنامه Cast به برنامه‌ای اشاره دارد که روی فرستنده اجرا می‌شود. برنامه گیرنده وب به برنامه HTML که روی گیرنده وب اجرا می‌شود اشاره دارد.

چارچوب فرستنده از یک طراحی فراخوانی ناهمزمان برای اطلاع‌رسانی به برنامه فرستنده از رویدادها و انتقال بین حالت‌های مختلف چرخه حیات برنامه Cast استفاده می‌کند.

جریان برنامه

مراحل زیر جریان اجرای سطح بالای معمول برای یک برنامه فرستنده iOS را شرح می‌دهد:

  • چارچوب Cast GCKDiscoveryManager را بر اساس ویژگی‌های ارائه شده در GCKCastOptions برای شروع اسکن دستگاه‌ها، آغاز می‌کند.
  • وقتی کاربر روی دکمه‌ی Cast کلیک می‌کند، فریم‌ورک، پنجره‌ی محاوره‌ای Cast را با فهرستی از دستگاه‌های Cast کشف‌شده نمایش می‌دهد.
  • وقتی کاربر یک دستگاه Cast را انتخاب می‌کند، چارچوب تلاش می‌کند تا برنامه گیرنده وب را روی دستگاه Cast اجرا کند.
  • این چارچوب، فراخوانی‌های برگشتی را در برنامه فرستنده فراخوانی می‌کند تا تأیید کند که برنامه گیرنده وب راه‌اندازی شده است.
  • این چارچوب یک کانال ارتباطی بین برنامه‌های فرستنده و گیرنده وب ایجاد می‌کند.
  • این چارچوب از کانال ارتباطی برای بارگذاری و کنترل پخش رسانه در گیرنده وب استفاده می‌کند.
  • این چارچوب، وضعیت پخش رسانه را بین فرستنده و گیرنده وب همگام‌سازی می‌کند: وقتی کاربر اقدامات رابط کاربری فرستنده را انجام می‌دهد، چارچوب آن درخواست‌های کنترل رسانه را به گیرنده وب ارسال می‌کند و وقتی گیرنده وب به‌روزرسانی‌های وضعیت رسانه را ارسال می‌کند، چارچوب وضعیت رابط کاربری فرستنده را به‌روزرسانی می‌کند.
  • وقتی کاربر برای قطع ارتباط از دستگاه Cast روی دکمه Cast کلیک می‌کند، فریم‌ورک، برنامه فرستنده را از گیرنده وب جدا می‌کند.

برای عیب‌یابی فرستنده، باید قابلیت گزارش‌گیری را فعال کنید.

برای فهرست جامعی از تمام کلاس‌ها، متدها و رویدادهای موجود در چارچوب Google Cast iOS، به مرجع API Google Cast iOS مراجعه کنید. بخش‌های زیر مراحل ادغام Cast در برنامه iOS شما را پوشش می‌دهند.

فراخوانی متدها از نخ اصلی

مقداردهی اولیه‌ی زمینه‌ی تبدیل (Cast context)

چارچوب Cast یک شیء سینگلتون سراسری به GCKCastContext دارد که تمام فعالیت‌های چارچوب را هماهنگ می‌کند. این شیء باید در اوایل چرخه حیات برنامه، معمولاً در متد -[application:didFinishLaunchingWithOptions:] از نماینده برنامه، مقداردهی اولیه شود تا از سرگیری خودکار جلسه پس از راه‌اندازی مجدد برنامه فرستنده به درستی انجام شود.

هنگام مقداردهی اولیه GCKCastContext باید یک شیء GCKCastOptions ارائه شود. این کلاس شامل گزینه‌هایی است که بر رفتار چارچوب تأثیر می‌گذارند. مهمترین آنها شناسه برنامه گیرنده وب است که برای فیلتر کردن نتایج کشف و راه‌اندازی برنامه گیرنده وب هنگام شروع جلسه Cast استفاده می‌شود.

متد -[application:didFinishLaunchingWithOptions:] ‎ همچنین جای خوبی برای تنظیم یک نماینده ثبت وقایع (logging delegate) برای دریافت پیام‌های ثبت وقایع از فریم‌ورک است. این موارد می‌توانند برای اشکال‌زدایی و عیب‌یابی مفید باشند.

سویفت
@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 iOS این ابزارک‌ها را که با چک‌لیست طراحی Cast مطابقت دارند، ارائه می‌دهد:

  • پوشش مقدماتی : کلاس GCKCastContext دارای متدی به نام presentCastInstructionsViewControllerOnceWithCastButton است که می‌تواند برای نمایش دکمه‌ی ارسال در اولین باری که یک گیرنده‌ی وب در دسترس قرار می‌گیرد، استفاده شود. برنامه‌ی فرستنده می‌تواند متن، موقعیت متن عنوان و دکمه‌ی رد کردن را سفارشی کند.

  • دکمه‌ی ارسال : با شروع از SDK 4.6.0 برای فرستنده‌ی Cast iOS، دکمه‌ی ارسال همیشه زمانی که دستگاه فرستنده به Wi-Fi متصل است، قابل مشاهده است. اولین باری که کاربر پس از شروع اولیه‌ی برنامه، روی دکمه‌ی ارسال ضربه می‌زند، یک پنجره‌ی مجوز ظاهر می‌شود تا کاربر بتواند به برنامه دسترسی شبکه‌ی محلی به دستگاه‌های موجود در شبکه را اعطا کند. متعاقباً، هنگامی که کاربر روی دکمه‌ی ارسال ضربه می‌زند، یک پنجره‌ی ارسال نمایش داده می‌شود که دستگاه‌های کشف شده را فهرست می‌کند. هنگامی که کاربر در حالی که دستگاه متصل است روی دکمه‌ی ارسال ضربه می‌زند، فراداده‌های رسانه‌ی فعلی (مانند عنوان، نام استودیوی ضبط و تصویر کوچک) نمایش داده می‌شود یا به کاربر اجازه می‌دهد از دستگاه ارسال جدا شود. هنگامی که کاربر در حالی که هیچ دستگاهی در دسترس نیست روی دکمه‌ی ارسال ضربه می‌زند، صفحه‌ای نمایش داده می‌شود که اطلاعاتی در مورد دلیل پیدا نشدن دستگاه‌ها و نحوه‌ی عیب‌یابی به کاربر می‌دهد.

  • مینی کنترلر : وقتی کاربر در حال ارسال محتوا است و از صفحه محتوای فعلی یا کنترلر گسترش‌یافته به صفحه دیگری در برنامه فرستنده می‌رود، مینی کنترلر در پایین صفحه نمایش داده می‌شود تا به کاربر امکان مشاهده فراداده‌های رسانه در حال ارسال و کنترل پخش را بدهد.

  • کنترل‌کننده‌ی گسترش‌یافته : وقتی کاربر در حال پخش محتوا است، اگر روی اعلان رسانه یا کنترل‌کننده‌ی کوچک کلیک کند، کنترل‌کننده‌ی گسترش‌یافته اجرا می‌شود که فراداده‌ی رسانه‌ی در حال پخش را نمایش می‌دهد و چندین دکمه برای کنترل پخش رسانه ارائه می‌دهد.

دکمه‌ی Cast را اضافه کنید

این فریم‌ورک یک کامپوننت دکمه Cast را به عنوان یک زیرکلاس UIButton ارائه می‌دهد. می‌توان آن را با قرار دادن در یک UIBarButtonItem به نوار عنوان برنامه اضافه کرد. یک زیرکلاس معمولی UIViewController می‌تواند یک دکمه Cast را به صورت زیر نصب کند:

سویفت
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];

به طور پیش‌فرض، ضربه زدن روی دکمه، پنجره‌ی محاوره‌ای Cast که توسط فریم‌ورک ارائه شده است را باز می‌کند.

GCKUICastButton می‌توان مستقیماً به استوری‌بورد نیز اضافه کرد.

پیکربندی کشف دستگاه

در این چارچوب، کشف دستگاه به طور خودکار اتفاق می‌افتد. نیازی به شروع یا توقف صریح فرآیند کشف نیست، مگر اینکه یک رابط کاربری سفارشی پیاده‌سازی کنید.

کشف در چارچوب توسط کلاس GCKDiscoveryManager مدیریت می‌شود که یک ویژگی از GCKCastContext است. این چارچوب یک کامپوننت محاوره‌ای Cast پیش‌فرض برای انتخاب و کنترل دستگاه ارائه می‌دهد. لیست دستگاه‌ها بر اساس نام دستگاه به صورت لغوی مرتب شده است.

نحوه کار مدیریت جلسه

کیت توسعه نرم‌افزاری Cast مفهوم یک جلسه Cast را معرفی می‌کند که ایجاد آن مراحل اتصال به یک دستگاه، راه‌اندازی (یا پیوستن) به یک برنامه گیرنده وب، اتصال به آن برنامه و مقداردهی اولیه یک کانال کنترل رسانه را ترکیب می‌کند. برای اطلاعات بیشتر در مورد جلسات Cast و چرخه عمر گیرنده وب، به راهنمای چرخه عمر برنامه گیرنده وب مراجعه کنید.

جلسات توسط کلاس GCKSessionManager مدیریت می‌شوند که یک ویژگی از GCKCastContext است. جلسات جداگانه توسط زیرکلاس‌های کلاس GCKSession نمایش داده می‌شوند: برای مثال، GCKCastSession جلساتی را با دستگاه‌های Cast نشان می‌دهد. شما می‌توانید به جلسه Cast فعال فعلی (در صورت وجود) به عنوان ویژگی currentCastSession از GCKSessionManager دسترسی داشته باشید.

رابط GCKSessionManagerListener می‌تواند برای نظارت بر رویدادهای جلسه، مانند ایجاد، تعلیق، از سرگیری و خاتمه جلسه، مورد استفاده قرار گیرد. این چارچوب به طور خودکار جلسات را هنگامی که برنامه فرستنده به پس‌زمینه می‌رود، به حالت تعلیق در می‌آورد و هنگامی که برنامه به پیش‌زمینه برمی‌گردد (یا پس از خاتمه غیرعادی/ناگهانی برنامه در حالی که یک جلسه فعال بوده است، دوباره راه‌اندازی می‌شود) سعی می‌کند آنها را از سر بگیرد.

اگر از کادر محاوره‌ای Cast استفاده شود، sessionها به طور خودکار در پاسخ به حرکات کاربر ایجاد و حذف می‌شوند. در غیر این صورت، برنامه می‌تواند sessionها را به طور صریح از طریق متدهای موجود در GCKSessionManager شروع و پایان دهد.

اگر برنامه نیاز به انجام پردازش ویژه در پاسخ به رویدادهای چرخه عمر جلسه داشته باشد، می‌تواند یک یا چند نمونه GCKSessionManagerListener در GCKSessionManager ثبت کند. GCKSessionManagerListener پروتکلی است که فراخوانی‌هایی را برای رویدادهایی مانند شروع جلسه، پایان جلسه و غیره تعریف می‌کند.

انتقال جریان

حفظ وضعیت جلسه، اساس انتقال جریان است، که در آن کاربران می‌توانند جریان‌های صوتی و تصویری موجود را با استفاده از دستورات صوتی، برنامه Google Home یا نمایشگرهای هوشمند، بین دستگاه‌ها جابجا کنند. پخش رسانه در یک دستگاه (منبع) متوقف می‌شود و در دستگاه دیگر (مقصد) ادامه می‌یابد. هر دستگاه Cast با جدیدترین سیستم عامل می‌تواند به عنوان منبع یا مقصد در انتقال جریان عمل کند.

برای دریافت دستگاه مقصد جدید در حین انتقال استریم، از ویژگی GCKCastSession#device در حین فراخوانی [sessionManager:didResumeCastSession:] استفاده کنید.

برای اطلاعات بیشتر به بخش انتقال جریان در گیرنده وب مراجعه کنید.

اتصال مجدد خودکار

چارچوب Cast منطق اتصال مجدد را اضافه می‌کند تا اتصال مجدد را در بسیاری از موارد ظریف و حساس، مانند موارد زیر، به طور خودکار مدیریت کند:

  • بازیابی پس از قطع موقت وای فای
  • بازیابی از حالت خواب دستگاه
  • بازیابی از پس‌زمینه شدن برنامه
  • بازیابی در صورت خرابی برنامه

نحوه‌ی عملکرد کنترل رسانه

اگر یک جلسه‌ی Cast با یک برنامه‌ی Web Receiver که از فضای نام رسانه پشتیبانی می‌کند، برقرار شود، یک نمونه از GCKRemoteMediaClient به طور خودکار توسط چارچوب ایجاد می‌شود؛ می‌توان به آن به عنوان ویژگی remoteMediaClient از نمونه‌ی GCKCastSession دسترسی داشت.

تمام متدهای GCKRemoteMediaClient که درخواست‌هایی را به گیرنده وب ارسال می‌کنند، یک شیء GCKRequest برمی‌گردانند که می‌تواند برای ردیابی آن درخواست استفاده شود. می‌توان یک GCKRequestDelegate را به این شیء اختصاص داد تا اعلان‌هایی در مورد نتیجه نهایی عملیات دریافت کند.

انتظار می‌رود که نمونه‌ی GCKRemoteMediaClient بین بخش‌های مختلف برنامه به اشتراک گذاشته شود، و در واقع برخی از اجزای داخلی چارچوب مانند کادر محاوره‌ای Cast و کنترل‌های mini media این نمونه را به اشتراک می‌گذارند. برای این منظور، 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 و ارتفاع و عرض بر حسب پیکسل است. انواع فرمت 4K در ویژگی hdrType با مقادیر شمارشی GCKVideoInfoHDRType نشان داده شده‌اند.

اضافه کردن مینی کنترلرها

طبق چک لیست طراحی Cast ، یک برنامه فرستنده باید یک کنترل دائمی به نام مینی کنترلر ارائه دهد که هنگام خروج کاربر از صفحه محتوای فعلی ظاهر شود. مینی کنترلر دسترسی فوری و یادآوری قابل مشاهده برای جلسه Cast فعلی را فراهم می‌کند.

چارچوب Cast یک نوار کنترل به نام GCKUIMiniMediaControlsViewController ارائه می‌دهد که می‌تواند به صحنه‌هایی که می‌خواهید مینی کنترلر را در آنها نمایش دهید، اضافه شود.

وقتی برنامه فرستنده شما در حال پخش یک پخش زنده ویدیویی یا صوتی است، SDK به طور خودکار یک دکمه پخش/توقف را به جای دکمه پخش/مکث در مینی کنترلر نمایش می‌دهد.

برای اطلاع از نحوه‌ی پیکربندی ظاهر ویجت‌های Cast توسط برنامه‌ی فرستنده، به بخش «سفارشی‌سازی رابط کاربری فرستنده‌ی iOS» مراجعه کنید.

دو راه برای اضافه کردن مینی کنترلر به برنامه فرستنده وجود دارد:

  • اجازه دهید فریم‌ورک Cast با قرار دادن کنترلر نمای موجود شما در کنترلر نمای خودش، طرح‌بندی مینی کنترلر را مدیریت کند.
  • با اضافه کردن ویجت مینی کنترلر به کنترلر نمای موجود خود از طریق ایجاد یک زیرنما در استوری‌بورد، طرح‌بندی آن را خودتان مدیریت کنید.

با استفاده از GCKUICastContainerViewController آن را پوشش دهید

راه اول استفاده از GCKUICastContainerViewController است که یک view controller دیگر را در بر می‌گیرد و یک GCKUIMiniMediaControlsViewController را در پایین اضافه می‌کند. این رویکرد محدود است زیرا شما نمی‌توانید انیمیشن را سفارشی کنید و نمی‌توانید رفتار container view controller را پیکربندی کنید.

این روش اول معمولاً در متد -[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 ایجاد کنید و سپس آن را به عنوان یک زیرنما (subview) به کنترلر نمای کانتینر اضافه کنید.

کنترلر نمای خود را در نماینده برنامه تنظیم کنید:

سویفت
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 ایجاد کنید و آن را به عنوان یک زیرنما (subview) به کنترلر نمای کانتینر اضافه کنید:

سویفت
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 به host view controller اعلام می‌کند که چه زمانی مینی کنترلر باید قابل مشاهده باشد:

سویفت
  func miniMediaControlsViewController(_: GCKUIMiniMediaControlsViewController,
                                       shouldAppear _: Bool) {
    updateControlBarsVisibility()
  }
هدف-سی
- (void)miniMediaControlsViewController:
            (GCKUIMiniMediaControlsViewController *)miniMediaControlsViewController
                           shouldAppear:(BOOL)shouldAppear {
  [self updateControlBarsVisibility];
}

اضافه کردن کنترلر توسعه‌یافته

چک لیست طراحی گوگل کست به یک برنامه فرستنده نیاز دارد تا یک کنترل‌کننده گسترش‌یافته برای رسانه‌ای که قرار است پخش شود، فراهم کند. این کنترل‌کننده گسترش‌یافته، نسخه تمام‌صفحه‌ای از کنترل‌کننده کوچک است.

کنترل‌کننده‌ی گسترش‌یافته، یک نمای تمام‌صفحه است که کنترل کامل پخش رسانه از راه دور را ارائه می‌دهد. این نما باید به یک برنامه‌ی پخش اجازه دهد تا هر جنبه‌ی قابل مدیریت یک جلسه‌ی پخش را مدیریت کند، به استثنای کنترل صدای گیرنده‌ی وب و چرخه‌ی عمر جلسه (اتصال/توقف پخش). همچنین تمام اطلاعات وضعیت مربوط به جلسه‌ی رسانه (طرح هنری، عنوان، زیرنویس و غیره) را ارائه می‌دهد.

عملکرد این view توسط کلاس 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 به طور خودکار یک دکمه پخش/توقف را به جای دکمه پخش/مکث در کنترلر گسترش‌یافته نمایش می‌دهد.

برای اطلاع از نحوه‌ی پیکربندی ظاهر ویجت‌های Cast توسط برنامه‌ی فرستنده، به بخش «اعمال سبک‌های سفارشی در برنامه‌ی iOS» مراجعه کنید.

کنترل صدا

چارچوب Cast به طور خودکار میزان صدا را برای برنامه فرستنده مدیریت می‌کند. این چارچوب به طور خودکار با میزان صدای Web Receiver برای ویجت‌های رابط کاربری ارائه شده همگام‌سازی می‌کند. برای همگام‌سازی اسلایدر ارائه شده توسط برنامه، از GCKUIDeviceVolumeController استفاده کنید.

دکمه فیزیکی برای کنترل صدا

دکمه‌های فیزیکی تنظیم صدا روی دستگاه فرستنده می‌توانند برای تغییر میزان صدای جلسه Cast در گیرنده وب با استفاده از پرچم 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];

مدیریت خطاها

برای برنامه‌های فرستنده بسیار مهم است که تمام خطاهای فراخوانی را مدیریت کنند و بهترین پاسخ را برای هر مرحله از چرخه حیات Cast انتخاب کنند. برنامه می‌تواند دیالوگ‌های خطا را به کاربر نمایش دهد یا می‌تواند تصمیم بگیرد که جلسه Cast را پایان دهد.

توجه داشته باشید که برخی از خطاها، از جمله GCKErrorCode GCKErrorCodeCancelled ، رفتارهای از پیش تعیین‌شده هستند.

سعی نکنید اتصالی را که با GCKErrorCodeCancelled ناموفق بوده است، دوباره امتحان کنید. انجام این کار ممکن است منجر به رفتار غیرمنتظره‌ای شود.

ثبت وقایع

GCKLogger یک singleton است که توسط فریم‌ورک برای ثبت وقایع (logging) استفاده می‌شود. از 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 .