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

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

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

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

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

ขั้นตอนของแอป

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

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

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

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

วิธีการโทรจากเทรดหลัก

เริ่มต้นบริบทการแคสต์

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

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

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

สวิฟต์
วันที่
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, GCKLoggerDelegate {
  let kReceiverAppID = kGCKDefaultMediaReceiverApplicationID
  let kDebugLoggingEnabled = true

  var window: UIWindow?

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

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

    ...
  }

  // MARK: - GCKLoggerDelegate

  func logMessage(_ message: String,
                  at level: GCKLoggerLevel,
                  fromFunction function: String,
                  location: String) {
    if (kDebugLoggingEnabled) {
      print(function + " - " + message)
    }
  }
}
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 SDK มีวิดเจ็ตเหล่านี้ที่สอดคล้องกับการออกแบบของ Cast รายการตรวจสอบมีดังนี้

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

  • ปุ่ม "แคสต์": ตั้งแต่ SDK ผู้ส่ง iOS แคสต์ 4.6.0 เป็นต้นไป ปุ่ม "แคสต์" จะแสดงอยู่เสมอ เมื่ออุปกรณ์ของผู้ส่งเชื่อมต่อกับ Wi-Fi ครั้งแรกที่ผู้ใช้แตะ บนปุ่ม "แคสต์" หลังจากที่เริ่มเปิดแอปครั้งแรก กล่องโต้ตอบสิทธิ์ ปรากฏขึ้น เพื่อให้ผู้ใช้สามารถให้สิทธิ์แอปเข้าถึงเครือข่าย LAN ในอุปกรณ์ เครือข่าย ต่อมาเมื่อผู้ใช้แตะปุ่ม "แคสต์" จะปรากฏขึ้น ซึ่งจะแสดงรายการอุปกรณ์ที่พบ เมื่อผู้ใช้แตะ บนปุ่ม "แคสต์" ขณะที่อุปกรณ์เชื่อมต่ออยู่ ปุ่มจะแสดงหน้าจอปัจจุบัน ข้อมูลเมตาของสื่อ (เช่น ชื่อ ชื่อสตูดิโอบันทึกเสียง และภาพปก ) หรืออนุญาตให้ผู้ใช้ยกเลิกการเชื่อมต่อจากอุปกรณ์แคสต์ เมื่อผู้ใช้ แตะปุ่ม "แคสต์" ขณะที่ไม่มีอุปกรณ์ที่พร้อมใช้งาน หน้าจอ จะปรากฏขึ้นเพื่อให้ผู้ใช้ทราบถึงสาเหตุที่ไม่พบอุปกรณ์ และวิธีแก้ปัญหา

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

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

เพิ่มปุ่ม "แคสต์"

เฟรมเวิร์กนี้จะมีคอมโพเนนต์ปุ่ม "แคสต์" เป็นคลาสย่อย UIButton ช่วย ลงในแถบชื่อของแอปด้วยการรวมไว้ใน UIBarButtonItem ทั่วไป คลาสย่อย UIViewController จะติดตั้งปุ่ม "แคสต์" ได้ดังนี้

สวิฟต์
วันที่
let castButton = GCKUICastButton(frame: CGRect(x: 0, y: 0, width: 24, height: 24))
castButton.tintColor = UIColor.gray
navigationItem.rightBarButtonItem = UIBarButtonItem(customView: castButton)
Objective-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 นำเสนอแนวคิดของเซสชันการแคสต์ ซึ่งประกอบด้วยขั้นตอนการเชื่อมต่อกับอุปกรณ์ การเปิด (หรือการเข้าร่วม) เว็บ แอปตัวรับสัญญาณ การเชื่อมต่อกับแอปดังกล่าว และการเริ่มต้นใช้งานช่องทางควบคุมสื่อ ดูเว็บรีซีฟเวอร์ คำแนะนำเกี่ยวกับวงจรของแอปพลิเคชัน สำหรับข้อมูลเพิ่มเติมเกี่ยวกับเซสชันการแคสต์และวงจรชีวิตของตัวรับเว็บ

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

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

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

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

การโอนสตรีม

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

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

โปรดดู การโอนสตรีมใน Web Receiver เพื่อดูข้อมูลเพิ่มเติม

การเชื่อมต่ออีกครั้งอัตโนมัติ

เฟรมเวิร์กการแคสต์เพิ่มตรรกะการเชื่อมต่อใหม่เพื่อจัดการการเชื่อมต่ออีกครั้งโดยอัตโนมัติ ในหลายกรณี เช่น

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

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

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

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

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

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

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

สวิฟต์
วันที่
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 สำหรับควบคุมแอปมีเดียเพลเยอร์ที่ทำงานอยู่บนตัวรับสัญญาณ เช่น สำหรับเล่น หยุดชั่วคราว และหยุด

สวิฟต์
วันที่
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 มีแถบควบคุม GCKUIMiniMediaControlsViewController ซึ่งคุณสามารถเพิ่มลงในฉากที่คุณต้องการแสดงตัวควบคุมขนาดเล็กได้

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

โปรดดูที่ปรับแต่ง UI ผู้ส่งของ iOS เพื่อดูลักษณะ แอปผู้ส่งกำหนดค่ารูปลักษณ์ของวิดเจ็ตแคสต์ได้

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

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

ตัดโดยใช้ GCKUICastContainerViewController

วิธีแรกคือการใช้ GCKUICastContainerViewController ซึ่งจะรวมตัวควบคุมมุมมองอื่นและเพิ่ม GCKUIMiniMediaControlsViewController ที่ด้านล่าง วิธีการนี้มีข้อจำกัดอยู่ตรงที่คุณไม่สามารถปรับแต่ง ของภาพเคลื่อนไหว และกำหนดค่าลักษณะการทำงานของตัวควบคุมมุมมองคอนเทนเนอร์ไม่ได้

วิธีแรกมักจะทำใน -[application:didFinishLaunchingWithOptions:] วิธีในการมอบสิทธิ์แอป

สวิฟต์
วันที่
func applicationDidFinishLaunching(_ application: UIApplication) {
  ...

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

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

}
สวิฟต์
วันที่
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

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

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

ตั้งค่าตัวควบคุมการดูในการมอบสิทธิ์แอป

สวิฟต์
วันที่
func application(_ application: UIApplication,
                 didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
  ...

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

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

  ...

  return true
}
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 ในตัวควบคุมมุมมองรูท และเพิ่มในตัวควบคุมมุมมองคอนเทนเนอร์เป็นมุมมองย่อย

สวิฟต์
วันที่
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 บอกตัวควบคุมมุมมองของโฮสต์เมื่อตัวควบคุมขนาดเล็กควรมองเห็นได้:

สวิฟต์
วันที่
  func miniMediaControlsViewController(_: GCKUIMiniMediaControlsViewController,
                                       shouldAppear _: Bool) {
    updateControlBarsVisibility()
  }
Objective-C
- (void)miniMediaControlsViewController:
            (GCKUIMiniMediaControlsViewController *)miniMediaControlsViewController
                           shouldAppear:(BOOL)shouldAppear {
  [self updateControlBarsVisibility];
}

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

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

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

ฟังก์ชันการทำงานของมุมมองนี้ดำเนินการโดย GCKUIExpandedMediaControlsViewController

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

สวิฟต์
วันที่
func applicationDidFinishLaunching(_ application: UIApplication) {
  ..

  GCKCastContext.sharedInstance().useDefaultExpandedMediaControls = true

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

  [GCKCastContext sharedInstance].useDefaultExpandedMediaControls = YES;

  ..
}

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

สวิฟต์
วันที่
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 ของคุณ แอป เกี่ยวกับวิธีที่แอปผู้ส่งสามารถกำหนดค่าลักษณะที่ปรากฏของวิดเจ็ตแคสต์ได้

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

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

ตัวควบคุมระดับเสียงของปุ่มจริง

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

สวิฟต์
วันที่
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];

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

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

การบันทึก

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

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

สวิฟต์
วันที่
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, GCKLoggerDelegate {
  let kReceiverAppID = kGCKDefaultMediaReceiverApplicationID
  let kDebugLoggingEnabled = true

  var window: UIWindow?

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

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

    ...
  }

  // MARK: - GCKLoggerDelegate

  func logMessage(_ message: String,
                  at level: GCKLoggerLevel,
                  fromFunction function: String,
                  location: String) {
    if (kDebugLoggingEnabled) {
      print(function + " - " + message)
    }
  }
}
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

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

สวิฟต์
วันที่
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 กำหนดระดับการบันทึกขั้นต่ำต่อชั้นเรียน เช่น

สวิฟต์
วันที่
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