รวมการแคสต์ลงในแอป iOS

จัดทุกอย่างให้เป็นระเบียบอยู่เสมอด้วยคอลเล็กชัน บันทึกและจัดหมวดหมู่เนื้อหาตามค่ากำหนดของคุณ

คู่มือนักพัฒนาซอฟต์แวร์นี้อธิบายวิธีเพิ่มการรองรับ Google Cast ในแอปผู้ส่ง iOS โดยใช้ iOS Sender SDK

อุปกรณ์เคลื่อนที่หรือแล็ปท็อปคือผู้ส่งที่ควบคุมการเล่น และอุปกรณ์ Google Cast คือตัวรับที่จะแสดงเนื้อหาบนทีวี

เฟรมเวิร์กผู้ส่งหมายถึงไบนารีของไลบรารีคลาสแคสต์และแหล่งข้อมูลที่เกี่ยวข้องขณะรันไทม์กับผู้ส่ง แอปของผู้ส่งหรือแอปแคสต์ หมายถึงแอปที่ทํางานเกี่ยวกับผู้ส่งด้วย แอปตัวรับเว็บ หมายถึงแอปพลิเคชัน HTML ที่ทํางานในเครื่องรับเว็บ

เฟรมเวิร์กผู้ส่งใช้การออกแบบการเรียกกลับแบบไม่พร้อมกันเพื่อแจ้งแอปของผู้ส่งเหตุการณ์และเพื่อสลับระหว่างสถานะต่างๆ ของวงจรแอปแคสต์

โฟลว์แอป

ขั้นตอนต่อไปนี้จะอธิบายขั้นตอนการดําเนินการระดับสูงโดยทั่วไปของแอป iOS ของผู้ส่ง

  • เฟรมเวิร์กการแคสต์เริ่มต้น GCKDiscoveryManager โดยอิงตามพร็อพเพอร์ตี้ที่มีให้ใน GCKCastOptions เพื่อเริ่มสแกนหาอุปกรณ์
  • เมื่อผู้ใช้คลิกปุ่มแคสต์ เฟรมเวิร์กจะแสดงกล่องโต้ตอบแคสต์ที่มีรายการอุปกรณ์แคสต์ที่ตรวจพบ
  • เมื่อผู้ใช้เลือกอุปกรณ์แคสต์ เฟรมเวิร์กจะพยายามเปิดแอป Web Recipient ในอุปกรณ์ Cast
  • เฟรมเวิร์กจะเรียกใช้การเรียกกลับในแอปของผู้ส่งเพื่อยืนยันว่าได้เปิดตัวแอป Web Receiver แล้ว
  • เฟรมเวิร์กจะสร้างช่องทางการสื่อสารระหว่างแอปผู้ส่งกับเว็บผู้รับเว็บ
  • เฟรมเวิร์กนี้ใช้ช่องทางการสื่อสารเพื่อโหลดและควบคุมการเล่นสื่อบน Web Receiver
  • เฟรมเวิร์กจะซิงค์ข้อมูลสถานะการเล่นสื่อระหว่างผู้ส่งกับเว็บผู้รับ กล่าวคือเมื่อผู้ใช้ดําเนินการ UI ของผู้ส่ง เฟรมเวิร์กจะส่งคําขอการควบคุมสื่อเหล่านั้นไปยังเว็บผู้รับ และเมื่อผู้รับเว็บส่งการอัปเดตสถานะสื่อ เฟรมเวิร์กจะอัปเดตสถานะของ UI ของผู้ส่ง
  • เมื่อผู้ใช้คลิกปุ่มแคสต์เพื่อยกเลิกการเชื่อมต่อจากอุปกรณ์แคสต์ เฟรมเวิร์กจะยกเลิกการเชื่อมต่อแอปผู้ส่งกับตัวรับสัญญาณบนเว็บ

หากต้องการแก้ปัญหาผู้ส่ง คุณต้องเปิดใช้การบันทึก

ดูรายการคลาส เมธอด และเหตุการณ์ทั้งหมดในเฟรมเวิร์ก Google Cast สําหรับ iOS ได้ที่ข้อมูลอ้างอิง Google API สําหรับ iOS ส่วนต่อไปนี้พูดถึงขั้นตอนในการผสานรวม Cast ในแอป iOS

วิธีการโทรจากชุดข้อความหลัก

เริ่มต้นบริบทของแคสต์

เฟรมเวิร์ก "แคสต์" มีออบเจ็กต์ Singleton ทั่วโลกที่มีชื่อว่า GCKCastContext ซึ่งจะประสานงานกิจกรรมทั้งหมดของเฟรมเวิร์ก ออบเจ็กต์นี้จะต้องเริ่มต้นก่อนในวงจรของแอปพลิเคชัน ซึ่งโดยปกติจะใช้ในเมธอด -[application:didFinishLaunchingWithOptions:] ของการมอบสิทธิ์แอป เพื่อให้การเรียกใช้เซสชันอัตโนมัติอีกครั้งเกี่ยวกับการรีสตาร์ทแอปของผู้ส่งเกิดขึ้นได้อย่างถูกต้อง

ต้องระบุออบเจ็กต์ GCKCastOptions เมื่อเริ่มต้น GCKCastContext คลาสนี้มีตัวเลือกที่มีผลต่อการทํางานของเฟรมเวิร์ก สิ่งสําคัญที่สุดคือรหัสแอปพลิเคชัน Web Receiver ที่ใช้ในการกรองผลการค้นหา Discovery และเปิดแอป Web Receiver เมื่อเริ่มเซสชันการแคสต์

เมธอด -[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)
    }
  }
}
วัตถุประสงค์-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 SDK มีวิดเจ็ตที่สอดคล้องกับรายการตรวจสอบการออกแบบแคสต์

  • การวางซ้อนเริ่มต้น: คลาส GCKCastContext มีวิธี presentCastInstructionsViewControllerOnceWithCastButton ซึ่งใช้เพื่อทําให้ปุ่ม "แคสต์" โดดเด่นในครั้งแรกที่ตัวรับเว็บพร้อมใช้งาน แอปผู้ส่งสามารถปรับแต่งข้อความ ตําแหน่งของข้อความชื่อ และปุ่มปิด

  • ปุ่มแคสต์: ตั้งแต่ SDK ผู้ส่งสําหรับ iOS 4.6.0 ไปจนถึงปุ่ม "แคสต์" จะแสดงขึ้นมาเสมอเมื่ออุปกรณ์ผู้ส่งเชื่อมต่อกับ 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)
วัตถุประสงค์-C
GCKUICastButton *castButton = [[GCKUICastButton alloc] initWithFrame:CGRectMake(0, 0, 24, 24)];
castButton.tintColor = [UIColor grayColor];
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:castButton];

โดยค่าเริ่มต้น การแตะปุ่มจะเป็นการเปิดกล่องโต้ตอบการแคสต์ที่เฟรมเวิร์กระบุไว้

GCKUICastButton สามารถเพิ่มลงในสตอรีบอร์ดได้โดยตรงด้วย

กําหนดค่าการค้นพบอุปกรณ์

ในเฟรมเวิร์ก การค้นพบอุปกรณ์จะเกิดขึ้นโดยอัตโนมัติ คุณไม่จําเป็นต้องเริ่มหรือหยุดกระบวนการค้นพบอย่างชัดแจ้ง เว้นแต่คุณจะใช้ UI ที่กําหนดเอง

การสํารวจในเฟรมเวิร์กได้รับการจัดการโดยชั้นเรียน GCKDiscoveryManager ซึ่งเป็นพร็อพเพอร์ตี้ของ GCKCastContext เฟรมเวิร์กมีคอมโพเนนต์กล่องโต้ตอบการแคสต์เริ่มต้นสําหรับการเลือกและควบคุมอุปกรณ์ รายชื่ออุปกรณ์จะเรียงลําดับตามภาษาตามชื่ออุปกรณ์

วิธีการทํางานของการจัดการเซสชัน

Cast SDK แนะนําแนวคิดของเซสชันการแคสต์ ซึ่งเป็นการรวมขั้นตอนการเชื่อมต่อเข้ากับอุปกรณ์ การเปิด (หรือเข้าร่วม) แอป Web Receiver การเชื่อมต่อกับแอปดังกล่าว และเริ่มต้นช่องทางการควบคุมสื่อ ดูข้อมูลเพิ่มเติมเกี่ยวกับเซสชันการแคสต์และอายุการใช้งานของตัวรับสัญญาณบนเว็บได้ในตัวรับข้อมูลเว็บ

เซสชันจะได้รับการจัดการโดยชั้นเรียน GCKSessionManager ซึ่งเป็นพร็อพเพอร์ตี้ของ GCKCastContext เซสชันแต่ละรายการจะแสดงโดยชั้นเรียนย่อย GCKSession ตัวอย่างเช่น GCKCastSession แสดงถึงเซสชันที่มีอุปกรณ์แคสต์ คุณสามารถเข้าถึงเซสชันการแคสต์ที่กําลังใช้งานอยู่ (หากมี) เป็นพร็อพเพอร์ตี้ currentCastSession ของ GCKSessionManager

อินเทอร์เฟซ GCKSessionManagerListener สามารถใช้เพื่อตรวจสอบเหตุการณ์ของเซสชัน เช่น การสร้างเซสชัน การระงับ การรีสตาร์ท และการสิ้นสุด เฟรมเวิร์กจะระงับเซสชันโดยอัตโนมัติเมื่อแอปของผู้ส่งทํางานอยู่เบื้องหลังและพยายามกลับมาทํางานอีกครั้งเมื่อแอปทํางานอยู่เบื้องหน้า (หรือจะเปิดอีกครั้งหลังจากการสิ้นสุดแอปที่ผิดปกติ/ขัดข้องในขณะที่เซสชันทํางานอยู่)

หากมีการใช้กล่องโต้ตอบ "แคสต์" ระบบจะสร้างเซสชันขึ้นและแยกลงโดยอัตโนมัติเพื่อตอบสนองต่อท่าทางสัมผัสของผู้ใช้ ไม่เช่นนั้น แอปจะเริ่มและสิ้นสุดเซสชันอย่างชัดแจ้งผ่านเมธอดใน GCKSessionManager

หากแอปต้องดําเนินการประมวลผลพิเศษเพื่อตอบสนองต่อเหตุการณ์ในวงจรของเซสชัน จะสามารถลงทะเบียนอินสแตนซ์ GCKSessionManagerListener อย่างน้อย 1 รายการกับ GCKSessionManager ได้ GCKSessionManagerListener เป็นโปรโตคอลที่กําหนดการเรียกกลับสําหรับเหตุการณ์ดังกล่าว เช่น การเริ่มต้นเซสชัน การสิ้นสุดเซสชัน และอื่นๆ

การโอนสตรีม

การคงเซสชันเซสชันไว้คือพื้นฐานของการโอนสตรีม ซึ่งผู้ใช้สามารถย้ายสตรีมเสียงและวิดีโอที่มีอยู่ในอุปกรณ์ต่างๆ โดยใช้คําสั่งเสียง, แอป Google Home หรือจออัจฉริยะ สื่อจะหยุดเล่นในอุปกรณ์หนึ่ง (แหล่งที่มา) และเล่นต่อในอุปกรณ์อื่น (ปลายทาง) อุปกรณ์แคสต์ที่มีเฟิร์มแวร์ล่าสุดจะใช้เป็นต้นทางหรือปลายทางในการโอนสตรีมได้

หากต้องการอุปกรณ์ปลายทางใหม่ในระหว่างการโอนสตรีม ให้ใช้พร็อพเพอร์ตี้ GCKCastSession#device ในระหว่างการเรียกกลับ [sessionManager:didResumeCastSession:]

ดูข้อมูลเพิ่มเติมที่หัวข้อการโอนสตรีมบนตัวรับสัญญาณเว็บ

การเชื่อมต่อใหม่โดยอัตโนมัติ

เฟรมเวิร์กของ Cast จะเพิ่มตรรกะการเชื่อมต่ออีกครั้งเพื่อจัดการการเชื่อมต่อใหม่โดยอัตโนมัติในมุมเล็กๆ หลายมุม เช่น

  • กู้คืนจากการสูญเสีย Wi-Fi ชั่วคราว
  • กู้คืนจากโหมดสลีปของอุปกรณ์
  • กู้คืนในเบื้องหลังของแอป
  • กู้คืนหากแอปขัดข้อง

วิธีการทํางานของการควบคุมสื่อ

หากเซสชันการแคสต์สร้างขึ้นด้วยแอป Web Receiver ที่รองรับเนมสเปซสื่อ ระบบจะสร้างอินสแตนซ์ของ GCKRemoteMediaClient โดยอัตโนมัติด้วยเฟรมเวิร์ก คุณสามารถเข้าถึงเป็นพร็อพเพอร์ตี้ remoteMediaClient ของอินสแตนซ์ GCKCastSession ได้

เมธอดทั้งหมดใน GCKRemoteMediaClient ที่ออกคําขอไปยัง Web Receiver จะแสดงออบเจ็กต์ GCKRequest ซึ่งใช้เพื่อติดตามคําขอนั้นได้ คุณกําหนด GCKRequestDelegate ให้กับออบเจ็กต์นี้เพื่อรับการแจ้งเตือนเกี่ยวกับผลที่ตามมาของการดําเนินการได้

เราคาดว่าอินสแตนซ์ของ GCKRemoteMediaClient อาจมีการแชร์ส่วนต่างๆ ของแอป และคอมโพเนนต์ภายในของเฟรมเวิร์กบางอย่าง เช่น กล่องโต้ตอบการแคสต์และการควบคุมสื่อขนาดเล็กจะแชร์อินสแตนซ์ด้วย ด้วยเหตุนี้ GCKRemoteMediaClient จึงรองรับการลงทะเบียนของ GCKRemoteMediaClientListener หลายรายการ

ตั้งค่าข้อมูลเมตาของสื่อ

คลาส GCKMediaMetadata แสดงถึงข้อมูลเกี่ยวกับรายการสื่อที่ต้องการแคสต์ ตัวอย่างต่อไปนี้สร้างอินสแตนซ์ GCKMediaMetadata ใหม่ของภาพยนตร์ และตั้งชื่อ คําบรรยาย ชื่อสตูดิโอบันทึกเสียง และรูปภาพ 2 รูป

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))
วัตถุประสงค์-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
}
วัตถุประสงค์-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 TV รวมถึงความสูงและความกว้างเป็นพิกเซล ตัวแปรของรูปแบบ 4K ระบุในพร็อพเพอร์ตี้ hdrType ด้วยค่า Enum GCKVideoInfoHDRType

เพิ่มตัวควบคุมขนาดเล็ก

ตามรายการตรวจสอบการออกแบบแคสต์ แอปของผู้ส่งควรให้การควบคุมอย่างต่อเนื่องที่เรียกว่าตัวควบคุมขนาดเล็กที่ควรปรากฏเมื่อผู้ใช้ออกจากหน้าเนื้อหาปัจจุบัน ตัวควบคุมขนาดเล็กเข้าถึงได้ทันทีและการช่วยเตือนที่มองเห็นได้สําหรับเซสชันการแคสต์ปัจจุบัน

เฟรมเวิร์กแคสต์มีแถบควบคุม GCKUIMiniMediaControlsViewController ซึ่งสามารถเพิ่มไปยังฉากที่คุณต้องการแสดงตัวควบคุมขนาดเล็ก

เมื่อแอปผู้ส่งกําลังเล่นสตรีมแบบสดหรือวิดีโอหรือเสียง SDK จะแสดงปุ่มเล่น/หยุดโดยอัตโนมัติแทนปุ่มเล่น/หยุดชั่วคราวในตัวควบคุมขนาดเล็ก

โปรดดูวิธีกําหนดค่าวิดเจ็ตแคสต์ในส่วนปรับแต่ง UI ของผู้ส่งใน iOS

การเพิ่มตัวควบคุมขนาดเล็กลงในแอปของผู้ส่งทําได้ 2 วิธีดังนี้

  • ให้เฟรมเวิร์ก "แคสต์" จัดการเลย์เอาต์ของตัวควบคุมขนาดเล็กโดยการรวมตัวควบคุมมุมมองที่มีอยู่กับตัวควบคุมมุมมองของตัวเอง
  • จัดการเลย์เอาต์ของวิดเจ็ตตัวควบคุมขนาดเล็กได้ด้วยตนเองโดยการเพิ่มลงในตัวควบคุมมุมมองที่มีอยู่โดยระบุมุมมองย่อยในสตอรีบอร์ด

รวมโดยใช้ 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()

  ...
}
วัตถุประสงค์-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
    }
  }
}
วัตถุประสงค์-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

ฝังในตัวควบคุมมุมมองที่มีอยู่

วิธีที่ 2 คือเพิ่มตัวควบคุมขนาดเล็กไปยังตัวควบคุมมุมมองที่มีอยู่โดยตรง โดยใช้ 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
}
วัตถุประสงค์-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)
    }
  }

...
วัตถุประสงค์-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()
  }
วัตถุประสงค์-C
- (void)miniMediaControlsViewController:
            (GCKUIMiniMediaControlsViewController *)miniMediaControlsViewController
                           shouldAppear:(BOOL)shouldAppear {
  [self updateControlBarsVisibility];
}

เพิ่มตัวควบคุมแบบขยาย

รายการตรวจสอบการออกแบบ Google Cast กําหนดให้แอปของผู้ส่งต้องมีตัวควบคุมที่ขยายสําหรับสื่อที่กําลังแคสต์ ตัวควบคุมแบบขยายเป็นตัวควบคุมขนาดเล็ก เต็มหน้าจอ

ตัวควบคุมแบบขยายเป็นมุมมองเต็มหน้าจอที่ให้การควบคุมการเล่นสื่อระยะไกลอย่างเต็มรูปแบบ มุมมองนี้ควรอนุญาตให้แอปแคสต์จัดการทุกด้านของเซสชันการแคสต์ที่จัดการได้ ยกเว้นการควบคุมระดับเสียงของตัวรับสัญญาณบนเว็บและวงจรของเซสชัน (เชื่อมต่อ/หยุดแคสต์) และยังมีข้อมูลสถานะทั้งหมดเกี่ยวกับเซสชันสื่อ (อาร์ตเวิร์ก ชื่อ ชื่อรอง และอื่นๆ) ด้วย

ฟังก์ชัน GCKUIExpandedMediaControlsViewController จะใช้ฟังก์ชันของมุมมองนี้

สิ่งแรกที่ต้องทําคือเปิดใช้ตัวควบคุมแบบขยายเริ่มต้นในบริบทแคสต์ แก้ไขการมอบสิทธิ์แอปเพื่อเปิดใช้ตัวควบคุมแบบขยายเริ่มต้น ดังนี้

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

  GCKCastContext.sharedInstance().useDefaultExpandedMediaControls = true

  ...
}
วัตถุประสงค์-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)
}
วัตถุประสงค์-C
- (void)playSelectedItemRemotely {
  [[GCKCastContext sharedInstance] presentDefaultExpandedMediaControls];

  ...

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

และจะเปิดตัวควบคุมแบบขยายโดยอัตโนมัติเมื่อผู้ใช้แตะตัวควบคุมขนาดเล็ก

เมื่อแอปผู้ส่งกําลังเล่นสตรีมแบบสดหรือวิดีโอหรือเสียง SDK จะแสดงปุ่มเล่น/หยุดโดยอัตโนมัติแทนปุ่มเล่น/หยุดชั่วคราวในตัวควบคุมแบบขยาย

ดูใช้รูปแบบที่กําหนดเองกับแอป iOS ของคุณว่าแอปผู้ส่งสามารถกําหนดค่าลักษณะที่ปรากฏของวิดเจ็ตแคสต์ได้อย่างไร

การควบคุมระดับเสียง

เฟรมเวิร์กแคสต์จะจัดการระดับเสียงสําหรับแอปของผู้ส่งโดยอัตโนมัติ เฟรมเวิร์กจะซิงค์กับปริมาณตัวรับเว็บโดยอัตโนมัติสําหรับวิดเจ็ต UI ที่ให้มา หากต้องการซิงค์แถบเลื่อนที่แอปมีให้ ให้ใช้ GCKUIDeviceVolumeController

การควบคุมระดับเสียง

ปุ่มปรับระดับเสียงบนอุปกรณ์ผู้ส่งอาจใช้เพื่อเปลี่ยนระดับเสียงของเซสชันการแคสต์ในอุปกรณ์รับเว็บโดยใช้แฟล็ก physicalVolumeButtonsWillControlDeviceVolume บน GCKCastOptions ซึ่งตั้งค่าไว้ใน GCKCastContext

Swift
let criteria = GCKDiscoveryCriteria(applicationID: kReceiverAppID)
let options = GCKCastOptions(discoveryCriteria: criteria)
options.physicalVolumeButtonsWillControlDeviceVolume = true
GCKCastContext.setSharedInstanceWith(options)
วัตถุประสงค์-C
GCKDiscoveryCriteria *criteria = [[GCKDiscoveryCriteria alloc]
                                          initWithApplicationID:kReceiverAppID];
GCKCastOptions *options = [[GCKCastOptions alloc]
                                          initWithDiscoveryCriteria :criteria];
options.physicalVolumeButtonsWillControlDeviceVolume = YES;
[GCKCastContext setSharedInstanceWithOptions:options];

จัดการข้อผิดพลาด

แอปของผู้ส่งจําเป็นต้องใช้การจัดการการเรียกกลับสําหรับข้อผิดพลาดทั้งหมดและตัดสินใจว่าจะให้การตอบสนองที่ดีที่สุดสําหรับแต่ละขั้นตอนของวงจรการแคสต์ แอปจะแสดงกล่องโต้ตอบแสดงข้อผิดพลาดแก่ผู้ใช้ หรือจะตัดสินใจสิ้นสุดเซสชันการแคสต์ก็ได้

Logging

GCKLogger คือ Singleton ที่ใช้สําหรับการบันทึกโดยเฟรมเวิร์ก ใช้ GCKLoggerDelegate เพื่อปรับแต่งวิธีจัดการข้อความบันทึก

SDK ดังกล่าวใช้ GCKLogger จะสร้างเอาต์พุตการบันทึกในรูปแบบข้อความการแก้ไขข้อบกพร่อง ข้อผิดพลาด และคําเตือน บันทึกเหล่านี้จะช่วยในการแก้ไขข้อบกพร่องและมีประโยชน์สําหรับการแก้ปัญหาและระบุปัญหาได้ โดยค่าเริ่มต้น ระบบจะหยุดเอาต์พุตบันทึก แต่เมื่อคุณกําหนด GCKLoggerDelegate แอปของผู้ส่งจะได้รับข้อความเหล่านี้จาก SDK และบันทึกในคอนโซลระบบ

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)
    }
  }
}
วัตถุประสงค์-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
วัตถุประสงค์-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
วัตถุประสงค์-C
GCKLoggerFilter *filter = [[GCKLoggerFilter alloc] init];
[filter setLoggingLevel:GCKLoggerLevelVerbose
             forClasses:@[@"GCKUICastButton",
                          @"GCKUIImageCache",
                          @"NSMutableDictionary"
                          ]];
[GCKLogger sharedInstance].filter = filter;

ชื่อชั้นเรียนอาจเป็นชื่อตามตัวอักษรหรือรูปแบบ Glob ก็ได้ เช่น GCKUI\* และ GCK\*Session