ادغام کردن «پخش محتوا» در برنامه iOS

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

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

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

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

جریان برنامه

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

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

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

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

فراخوانی روش‌ها از رشته اصلی

مقداردهی اولیه بافت Cast

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

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

روش -[application:didFinishLaunchingWithOptions:] همچنین مکان خوبی برای راه‌اندازی نماینده ثبت گزارش برای دریافت پیام‌های ثبت گزارش از چارچوب است. این اطلاعات می‌تواند برای اشکال‌زدایی و عیب‌یابی مفید باشد.

Swift
@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)
    }
  }
}
Objective-C

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

کیت توسعه نرم‌افزار Cast iOS این ابزاره‌ها را که با «بازبینه طراحی Cast» مطابقت دارند ارائه می‌دهد:

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

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

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

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

افزودن دکمه «پخش محتوا»

چارچوب، مؤلفه دکمه «پخش محتوا» را به‌عنوان زیرکلاس UIButton ارائه می‌دهد. می‌توان آن را با قرار دادن در UIBarButtonItem به نوار عنوان برنامه اضافه کرد. یک زیرکلاس معمولی UIViewController می‌تواند دکمه «پخش محتوا» را به‌صورت زیر نصب کند:

Swift
let castButton = GCKUICastButton(frame: CGRect(x: 0, y: 0, width: 24, height: 24))
castButton.tintColor = UIColor.gray
navigationItem.rightBarButtonItem = UIBarButtonItem(customView: castButton)
Objective-C
GCKUICastButton *castButton = [[GCKUICastButton alloc] initWithFrame:CGRectMake(0, 0, 24, 24)];
castButton.tintColor = [UIColor grayColor];
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:castButton];

به‌طور پیش‌فرض، ضربه زدن روی دکمه، کادر گفتگوی «پخش محتوا» را که توسط چارچوب ارائه شده است باز می‌کند.

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

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

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

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

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

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

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

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

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

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

انتقال جاری‌سازی

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

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

برای اطلاعات بیشتر، انتقال جاری‌سازی در «گیرنده وب» را ببینید.

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

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

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

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

If a Cast session is established with a Web Receiver app that supports the media namespace, an instance of GCKRemoteMediaClient will be created automatically by the framework; it can be accessed as the remoteMediaClient property of the GCKCastSession instance.

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

انتظار می‌رود که نمونه GCKRemoteMediaClient ممکن است توسط چندین بخش از برنامه هم‌رسانی شود، و درواقع برخی‌از مؤلفه‌های داخلی چارچوب مانند کادر گفتگوی Cast و کنترل‌های رسانه کوچک نمونه را هم‌رسانی می‌کنند. به همین منظور، GCKRemoteMediaClient از ثبت چند GCKRemoteMediaClientListener پشتیبانی می‌کند.

تنظیم فراداده‌های رسانه

کلاس GCKMediaMetadata اطلاعات مربوط به فایل رسانه‌ای را که می‌خواهید ارسال کنید نشان می‌دهد. مثال زیر نمونه GCKMediaMetadata جدیدی از فیلم ایجاد می‌کند و عنوان، زیرعنوان، نام استودیوی ضبط، و دو تصویر را تنظیم می‌کند.

Swift
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))
Objective-C
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 برای کنترل برنامه پخش‌کننده رسانه درحال اجرا در گیرنده استفاده کنید، مثلاً برای پخش، مکث، و توقف.

Swift
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
}
Objective-C
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 با مقادیر enum GCKVideoInfoHDRType نشان داده می‌شوند.

افزودن کنترل‌کننده‌های کوچک

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

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

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

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

دو روش برای افزودن کنترل‌کننده کوچک به برنامه فرستنده وجود دارد:

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

Wrap using the GCKUICastContainerViewController

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

این روش اول معمولاً در متد -[application:didFinishLaunchingWithOptions:] نماینده برنامه انجام می‌شود:

Swift
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()

  ...
}
Objective-C
- (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];
  ...

}
Swift
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
    }
  }
}
Objective-C

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 ، کنترل‌کننده کوچک را مستقیماً به کنترل‌کننده نمای موجود اضافه کنید و سپس آن را به‌عنوان نمای فرعی به کنترل‌کننده نمای محفظه اضافه کنید.

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

Swift
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
}
Objective-C
- (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 ایجاد کنید و آن را به‌عنوان زیرنما به کنترل‌کننده نمای محفظه اضافه کنید:

Swift
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)
    }
  }

...
Objective-C

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 به کنترل‌کننده نمای میزبان می‌گوید که کنترل‌کننده کوچک چه زمانی باید نمایان باشد:

Swift
  func miniMediaControlsViewController(_: GCKUIMiniMediaControlsViewController,
                                       shouldAppear _: Bool) {
    updateControlBarsVisibility()
  }
Objective-C
- (void)miniMediaControlsViewController:
            (GCKUIMiniMediaControlsViewController *)miniMediaControlsViewController
                           shouldAppear:(BOOL)shouldAppear {
  [self updateControlBarsVisibility];
}

افزودن کنترل‌کننده ازهم بازشده

«بازبینه طراحی Google Cast» از برنامه فرستنده می‌خواهد که برای رسانه درحال ارسال محتوا، کنترل‌کننده گسترده ارائه دهد. کنترل‌کننده گسترده‌شده نسخه تمام‌صفحه کنترل‌کننده کوچک است.

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

عملکرد این نما توسط کلاس GCKUIExpandedMediaControlsViewController پیاده‌سازی می‌شود.

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

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

  GCKCastContext.sharedInstance().useDefaultExpandedMediaControls = true

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

  [GCKCastContext sharedInstance].useDefaultExpandedMediaControls = YES;

  ..
}

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

Swift
func playSelectedItemRemotely() {
  GCKCastContext.sharedInstance().presentDefaultExpandedMediaControls()

  ...

  // Load your media
  sessionManager.currentSession?.remoteMediaClient?.loadMedia(mediaInformation)
}
Objective-C
- (void)playSelectedItemRemotely {
  [[GCKCastContext sharedInstance] presentDefaultExpandedMediaControls];

  ...

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

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

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

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

کنترل میزان صدا

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

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

The physical volume buttons on the sender device can be used to change the volume of the Cast session on the Web Receiver using the physicalVolumeButtonsWillControlDeviceVolume flag on the GCKCastOptions, which is set on the GCKCastContext.

Swift
let criteria = GCKDiscoveryCriteria(applicationID: kReceiverAppID)
let options = GCKCastOptions(discoveryCriteria: criteria)
options.physicalVolumeButtonsWillControlDeviceVolume = true
GCKCastContext.setSharedInstanceWith(options)
Objective-C
GCKDiscoveryCriteria *criteria = [[GCKDiscoveryCriteria alloc]
                                          initWithApplicationID:kReceiverAppID];
GCKCastOptions *options = [[GCKCastOptions alloc]
                                          initWithDiscoveryCriteria :criteria];
options.physicalVolumeButtonsWillControlDeviceVolume = YES;
[GCKCastContext setSharedInstanceWithOptions:options];

مدیریت کردن خطاها

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

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

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

ثبت

GCKLogger یک سینگلتون است که توسط چارچوب برای ثبت گزارش استفاده می‌شود. از GCKLoggerDelegate برای سفارشی‌سازی نحوه مدیریت پیام‌های گزارش استفاده کنید.

بااستفاده از GCKLogger، کیت توسعه نرم‌افزار برونداد ثبت‌شده را به‌صورت پیام‌های اشکال‌زدایی، خطا، و هشدار تولید می‌کند. این پیام‌های گزارش به اشکال‌زدایی کمک می‌کنند و برای عیب‌یابی و شناسایی مشکلات مفید هستند. به‌طور پیش‌فرض، خروجی گزارش سرکوب می‌شود، اما با اختصاص دادن GCKLoggerDelegate، برنامه فرستنده می‌تواند این پیام‌ها را از کیت توسعه نرم‌افزار دریافت کند و آن‌ها را در کنسول سیستم ثبت کند.

Swift
@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)
    }
  }
}
Objective-C

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

برای فعال کردن پیام‌های اشکال‌زدایی و پرجزئیات، این خط را پس‌از تنظیم نماینده (که قبلاً نشان داده شد) به کد اضافه کنید:

Swift
let filter = GCKLoggerFilter.init()
filter.minimumLevel = GCKLoggerLevel.verbose
GCKLogger.sharedInstance().filter = filter
Objective-C
GCKLoggerFilter *filter = [[GCKLoggerFilter alloc] init];
[filter setMinimumLevel:GCKLoggerLevelVerbose];
[GCKLogger sharedInstance].filter = filter;

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

Swift
let filter = GCKLoggerFilter.init()
filter.setLoggingLevel(GCKLoggerLevel.verbose, forClasses: ["GCKUICastButton",
                                                            "GCKUIImageCache",
                                                            "NSMutableDictionary"])
GCKLogger.sharedInstance().filter = filter
Objective-C
GCKLoggerFilter *filter = [[GCKLoggerFilter alloc] init];
[filter setLoggingLevel:GCKLoggerLevelVerbose
             forClasses:@[@"GCKUICastButton",
                          @"GCKUIImageCache",
                          @"NSMutableDictionary"
                          ]];
[GCKLogger sharedInstance].filter = filter;

نام کلاس‌ها می‌تواند نام‌های تحت‌اللفظی یا الگوهای glob باشد، برای مثال، GCKUI\* و GCK\*Session.