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

این راهنمای توسعه‌دهندگان نحوه افزودن پشتیبانی Google Cast به برنامه فرستنده iOS را بااستفاده از «کیت توسعه نرم‌افزار فرستنده iOS» شرح می‌دهد.

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

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

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

جریان برنامه

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

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

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

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

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

بافت «پخش محتوا» را مقداردهی اولیه کنید

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

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

روش -[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 iOS» این ابزارک‌ها را که با «فهرست وارسی طراحی Cast» مطابقت دارند ارائه می‌دهد:

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

  • دکمه پخش محتوا: از کیت توسعه نرم‌افزار فرستنده Cast iOS نسخه ۴.۶.۰، دکمه پخش محتوا همیشه وقتی دستگاه فرستنده به 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 را معرفی می‌کند که ایجاد آن مراحل اتصال به دستگاه، راه‌اندازی (یا پیوستن به) برنامه Web Receiver، اتصال به آن برنامه، و مقداردهی اولیه کانال کنترل رسانه را ترکیب می‌کند. برای کسب اطلاعات بیشتر درباره جلسات «پخش محتوا» و چرخه حیات «گیرنده وب»، به راهنمای چرخه حیات برنامه «گیرنده وب» مراجعه کنید.

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

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

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

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

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

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

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

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

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

چارچوب «پخش محتوا» منطق اتصال مجدد را اضافه می‌کند تا به‌طور خودکار اتصال مجدد را در بسیاری از موارد جزئی، مانند موارد زیر، مدیریت کند:

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

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

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

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

انتظار می‌رود نمونه GCKRemoteMediaClient توسط بخش‌های مختلف برنامه هم‌رسانی شود، و درواقع برخی‌از عناصر داخلی چارچوب مثل کادر گفتگوی «پخش محتوا» و کنترل‌های رسانه کوچک نمونه را هم‌رسانی می‌کنند. به همین منظور، 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 نشان داده می‌شوند.

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

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

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

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

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

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

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

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

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

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

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

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

از دکمه‌های فیزیکی میزان صدا در دستگاه فرستنده می‌توان برای تغییر میزان صدای جلسه «پخش محتوا» در «گیرنده وب» بااستفاده از پرچم physicalVolumeButtonsWillControlDeviceVolume در GCKCastOptions استفاده کرد که در 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];

مدیریت خطاها

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

توجه داشته باشید که برخی خطاها، ازجمله 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;

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