คู่มือนักพัฒนาซอฟต์แวร์นี้อธิบายวิธีเพิ่มการรองรับ 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:]
ยังเหมาะสําหรับการตั้งค่าการมอบสิทธิ์การมอบสิทธิ์เพื่อรับข้อความการบันทึกจากเฟรมเวิร์กด้วย
เครื่องมือเหล่านี้อาจมีประโยชน์สําหรับการแก้ไขข้อบกพร่องและการแก้ปัญหา
@UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate, GCKLoggerDelegate { let kReceiverAppID = kGCKDefaultMediaReceiverApplicationID let kDebugLoggingEnabled = true var window: UIWindow? func applicationDidFinishLaunching(_ application: UIApplication) { let criteria = GCKDiscoveryCriteria(applicationID: kReceiverAppID) let options = GCKCastOptions(discoveryCriteria: criteria) GCKCastContext.setSharedInstanceWith(options) // Enable logger. GCKLogger.sharedInstance().delegate = self ... } // MARK: - GCKLoggerDelegate func logMessage(_ message: String, at level: GCKLoggerLevel, fromFunction function: String, location: String) { if (kDebugLoggingEnabled) { print(function + " - " + message) } } }
AppDelegate.h
@interface AppDelegate () <GCKLoggerDelegate> @end
AppDelegate.m
@implementation AppDelegate static NSString *const kReceiverAppID = @"AABBCCDD"; static const BOOL kDebugLoggingEnabled = YES; - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { GCKDiscoveryCriteria *criteria = [[GCKDiscoveryCriteria alloc] initWithApplicationID:kReceiverAppID]; GCKCastOptions *options = [[GCKCastOptions alloc] initWithDiscoveryCriteria:criteria]; [GCKCastContext setSharedInstanceWithOptions:options]; // Enable logger. [GCKLogger sharedInstance].delegate = self; ... return YES; } ... #pragma mark - GCKLoggerDelegate - (void)logMessage:(NSString *)message atLevel:(GCKLoggerLevel)level fromFunction:(NSString *)function location:(NSString *)location { if (kDebugLoggingEnabled) { NSLog(@"%@ - %@, %@", function, message, location); } } @end
วิดเจ็ต Cast UX
Cast iOS SDK มีวิดเจ็ตที่สอดคล้องกับรายการตรวจสอบการออกแบบแคสต์
การวางซ้อนเริ่มต้น: คลาส
GCKCastContext
มีวิธีpresentCastInstructionsViewControllerOnceWithCastButton
ซึ่งใช้เพื่อทําให้ปุ่ม "แคสต์" โดดเด่นในครั้งแรกที่ตัวรับเว็บพร้อมใช้งาน แอปผู้ส่งสามารถปรับแต่งข้อความ ตําแหน่งของข้อความชื่อ และปุ่มปิดปุ่มแคสต์: ตั้งแต่ SDK ผู้ส่งสําหรับ iOS 4.6.0 ไปจนถึงปุ่ม "แคสต์" จะแสดงขึ้นมาเสมอเมื่ออุปกรณ์ผู้ส่งเชื่อมต่อกับ Wi-Fi ครั้งแรกที่ผู้ใช้แตะปุ่ม "แคสต์" หลังจากเริ่มต้นแอปเป็นครั้งแรก กล่องโต้ตอบสิทธิ์จะปรากฏขึ้นเพื่อให้ผู้ใช้สามารถให้สิทธิ์เข้าถึงเครือข่ายในเครื่องแก่อุปกรณ์ในเครือข่ายได้ จากนั้น เมื่อผู้ใช้แตะปุ่มแคสต์ กล่องโต้ตอบการแคสต์จะปรากฏขึ้นเพื่อแสดงรายการอุปกรณ์ที่พบ เมื่อผู้ใช้แตะปุ่มแคสต์ขณะที่อุปกรณ์เชื่อมต่ออยู่ ระบบจะแสดงข้อมูลเมตาสื่อในปัจจุบัน (เช่น ชื่อ สตูดิโอบันทึกเสียง และภาพขนาดย่อ) หรืออนุญาตให้ผู้ใช้ยกเลิกการเชื่อมต่อจากอุปกรณ์แคสต์ เมื่อผู้ใช้แตะปุ่มแคสต์ขณะที่ไม่มีอุปกรณ์พร้อมใช้งาน หน้าจอจะแสดงข้อมูลเกี่ยวกับสาเหตุที่ไม่พบอุปกรณ์และวิธีแก้ปัญหาแก่ผู้ใช้
ตัวควบคุมขนาดเล็ก: เมื่อผู้ใช้แคสต์เนื้อหาและออกไปจากหน้าเนื้อหาปัจจุบันหรือตัวควบคุมที่ขยายไปยังหน้าจออื่นในแอปของผู้ส่ง ตัวควบคุมขนาดเล็กจะแสดงที่ด้านล่างของหน้าจอเพื่อให้ผู้ใช้ดูข้อมูลเมตาของสื่อที่กําลังแคสต์อยู่และควบคุมการเล่นได้
ตัวควบคุมที่ขยาย: เมื่อผู้ใช้แคสต์เนื้อหา เมื่อผู้ใช้คลิกการแจ้งเตือนสื่อหรือตัวควบคุมขนาดเล็ก ตัวควบคุมแบบขยายจะเปิดขึ้น ซึ่งจะแสดงข้อมูลเมตาของสื่อที่เล่นอยู่ในขณะนั้นและมีปุ่มต่างๆ เพื่อควบคุมการเล่นสื่อ
เพิ่มปุ่ม "แคสต์"
เฟรมเวิร์กนี้จะมีคอมโพเนนต์ปุ่ม "แคสต์" เป็นคลาสย่อย 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)
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 รูป
let metadata = GCKMediaMetadata() metadata.setString("Big Buck Bunny (2008)", forKey: kGCKMetadataKeyTitle) metadata.setString("Big Buck Bunny tells the story of a giant rabbit with a heart bigger than " + "himself. When one sunny day three rodents rudely harass him, something " + "snaps... and the rabbit ain't no bunny anymore! In the typical cartoon " + "tradition he prepares the nasty rodents a comical revenge.", forKey: kGCKMetadataKeySubtitle) metadata.addImage(GCKImage(url: URL(string: "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/images/BigBuckBunny.jpg")!, width: 480, height: 360))
GCKMediaMetadata *metadata = [[GCKMediaMetadata alloc] initWithMetadataType:GCKMediaMetadataTypeMovie]; [metadata setString:@"Big Buck Bunny (2008)" forKey:kGCKMetadataKeyTitle]; [metadata setString:@"Big Buck Bunny tells the story of a giant rabbit with a heart bigger than " "himself. When one sunny day three rodents rudely harass him, something " "snaps... and the rabbit ain't no bunny anymore! In the typical cartoon " "tradition he prepares the nasty rodents a comical revenge." forKey:kGCKMetadataKeySubtitle]; [metadata addImage:[[GCKImage alloc] initWithURL:[[NSURL alloc] initWithString:@"https://commondatastorage.googleapis.com/" "gtv-videos-bucket/sample/images/BigBuckBunny.jpg"] width:480 height:360]];
ดูส่วนการเลือกรูปภาพและการแคชในการใช้รูปภาพที่มีข้อมูลเมตาของสื่อ
โหลดสื่อ
หากต้องการโหลดรายการสื่อ ให้สร้างอินสแตนซ์ GCKMediaInformation
โดยใช้ข้อมูลเมตาของสื่อ จากนั้นรับ
GCKCastSession
ปัจจุบันและใช้
GCKRemoteMediaClient
เพื่อโหลดสื่อในแอปตัวรับ จากนั้นคุณสามารถใช้ GCKRemoteMediaClient
สําหรับควบคุมแอปโปรแกรมเล่นสื่อที่ทํางานในเครื่องรับ เช่น เล่น หยุดชั่วคราว และหยุด
let url = URL.init(string: "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4") guard let mediaURL = url else { print("invalid mediaURL") return } let mediaInfoBuilder = GCKMediaInformationBuilder.init(contentURL: mediaURL) mediaInfoBuilder.streamType = GCKMediaStreamType.none; mediaInfoBuilder.contentType = "video/mp4" mediaInfoBuilder.metadata = metadata; mediaInformation = mediaInfoBuilder.build() guard let mediaInfo = mediaInformation else { print("invalid mediaInformation") return } if let request = sessionManager.currentSession?.remoteMediaClient?.loadMedia(mediaInfo) { request.delegate = self }
GCKMediaInformationBuilder *mediaInfoBuilder = [[GCKMediaInformationBuilder alloc] initWithContentURL: [NSURL URLWithString:@"https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"]]; mediaInfoBuilder.streamType = GCKMediaStreamTypeNone; mediaInfoBuilder.contentType = @"video/mp4"; mediaInfoBuilder.metadata = metadata; self.mediaInformation = [mediaInfoBuilder build]; GCKRequest *request = [self.sessionManager.currentSession.remoteMediaClient loadMedia:self.mediaInformation]; if (request != nil) { request.delegate = self; }
นอกจากนี้โปรดดูส่วนการใช้แทร็กสื่อ
รูปแบบวิดีโอ 4K
หากต้องการทราบว่าวิดีโออยู่ในรูปแบบสื่อใด ให้ใช้พร็อพเพอร์ตี้ videoInfo
ของ GCKMediaStatus
เพื่อรับอินสแตนซ์ปัจจุบันของ GCKVideoInfo
อินสแตนซ์นี้มีประเภทรูปแบบ HDR TV รวมถึงความสูงและความกว้างเป็นพิกเซล ตัวแปรของรูปแบบ 4K ระบุในพร็อพเพอร์ตี้ hdrType
ด้วยค่า Enum GCKVideoInfoHDRType
เพิ่มตัวควบคุมขนาดเล็ก
ตามรายการตรวจสอบการออกแบบแคสต์ แอปของผู้ส่งควรให้การควบคุมอย่างต่อเนื่องที่เรียกว่าตัวควบคุมขนาดเล็กที่ควรปรากฏเมื่อผู้ใช้ออกจากหน้าเนื้อหาปัจจุบัน ตัวควบคุมขนาดเล็กเข้าถึงได้ทันทีและการช่วยเตือนที่มองเห็นได้สําหรับเซสชันการแคสต์ปัจจุบัน
เฟรมเวิร์กแคสต์มีแถบควบคุม GCKUIMiniMediaControlsViewController
ซึ่งสามารถเพิ่มไปยังฉากที่คุณต้องการแสดงตัวควบคุมขนาดเล็ก
เมื่อแอปผู้ส่งกําลังเล่นสตรีมแบบสดหรือวิดีโอหรือเสียง SDK จะแสดงปุ่มเล่น/หยุดโดยอัตโนมัติแทนปุ่มเล่น/หยุดชั่วคราวในตัวควบคุมขนาดเล็ก
โปรดดูวิธีกําหนดค่าวิดเจ็ตแคสต์ในส่วนปรับแต่ง UI ของผู้ส่งใน iOS
การเพิ่มตัวควบคุมขนาดเล็กลงในแอปของผู้ส่งทําได้ 2 วิธีดังนี้
- ให้เฟรมเวิร์ก "แคสต์" จัดการเลย์เอาต์ของตัวควบคุมขนาดเล็กโดยการรวมตัวควบคุมมุมมองที่มีอยู่กับตัวควบคุมมุมมองของตัวเอง
- จัดการเลย์เอาต์ของวิดเจ็ตตัวควบคุมขนาดเล็กได้ด้วยตนเองโดยการเพิ่มลงในตัวควบคุมมุมมองที่มีอยู่โดยระบุมุมมองย่อยในสตอรีบอร์ด
รวมโดยใช้ 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() ... }
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { ... // Wrap main view in the GCKUICastContainerViewController and display the mini controller. UIStoryboard *appStoryboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil]; UINavigationController *navigationController = [appStoryboard instantiateViewControllerWithIdentifier:@"MainNavigation"]; GCKUICastContainerViewController *castContainerVC = [[GCKCastContext sharedInstance] createCastContainerControllerForViewController:navigationController]; castContainerVC.miniMediaControlsItemEnabled = YES; self.window = [[UIWindow alloc] initWithFrame:UIScreen.mainScreen.bounds]; self.window.rootViewController = castContainerVC; [self.window makeKeyAndVisible]; ... }
var castControlBarsEnabled: Bool { set(enabled) { if let castContainerVC = self.window?.rootViewController as? GCKUICastContainerViewController { castContainerVC.miniMediaControlsItemEnabled = enabled } else { print("GCKUICastContainerViewController is not correctly configured") } } get { if let castContainerVC = self.window?.rootViewController as? GCKUICastContainerViewController { return castContainerVC.miniMediaControlsItemEnabled } else { print("GCKUICastContainerViewController is not correctly configured") return false } } }
AppDelegate.h
@interface AppDelegate : UIResponder <UIApplicationDelegate> @property (nonatomic, strong) UIWindow *window; @property (nonatomic, assign) BOOL castControlBarsEnabled; @end
AppDelegate.m
@implementation AppDelegate ... - (void)setCastControlBarsEnabled:(BOOL)notificationsEnabled { GCKUICastContainerViewController *castContainerVC; castContainerVC = (GCKUICastContainerViewController *)self.window.rootViewController; castContainerVC.miniMediaControlsItemEnabled = notificationsEnabled; } - (BOOL)castControlBarsEnabled { GCKUICastContainerViewController *castContainerVC; castContainerVC = (GCKUICastContainerViewController *)self.window.rootViewController; return castContainerVC.miniMediaControlsItemEnabled; } ... @end
ฝังในตัวควบคุมมุมมองที่มีอยู่
วิธีที่ 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 }
- (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) } } ...
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() }
- (void)miniMediaControlsViewController: (GCKUIMiniMediaControlsViewController *)miniMediaControlsViewController shouldAppear:(BOOL)shouldAppear { [self updateControlBarsVisibility]; }
เพิ่มตัวควบคุมแบบขยาย
รายการตรวจสอบการออกแบบ Google Cast กําหนดให้แอปของผู้ส่งต้องมีตัวควบคุมที่ขยายสําหรับสื่อที่กําลังแคสต์ ตัวควบคุมแบบขยายเป็นตัวควบคุมขนาดเล็ก เต็มหน้าจอ
ตัวควบคุมแบบขยายเป็นมุมมองเต็มหน้าจอที่ให้การควบคุมการเล่นสื่อระยะไกลอย่างเต็มรูปแบบ มุมมองนี้ควรอนุญาตให้แอปแคสต์จัดการทุกด้านของเซสชันการแคสต์ที่จัดการได้ ยกเว้นการควบคุมระดับเสียงของตัวรับสัญญาณบนเว็บและวงจรของเซสชัน (เชื่อมต่อ/หยุดแคสต์) และยังมีข้อมูลสถานะทั้งหมดเกี่ยวกับเซสชันสื่อ (อาร์ตเวิร์ก ชื่อ ชื่อรอง และอื่นๆ) ด้วย
ฟังก์ชัน GCKUIExpandedMediaControlsViewController
จะใช้ฟังก์ชันของมุมมองนี้
สิ่งแรกที่ต้องทําคือเปิดใช้ตัวควบคุมแบบขยายเริ่มต้นในบริบทแคสต์ แก้ไขการมอบสิทธิ์แอปเพื่อเปิดใช้ตัวควบคุมแบบขยายเริ่มต้น ดังนี้
func applicationDidFinishLaunching(_ application: UIApplication) { .. GCKCastContext.sharedInstance().useDefaultExpandedMediaControls = true ... }
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { ... [GCKCastContext sharedInstance].useDefaultExpandedMediaControls = YES; .. }
เพิ่มโค้ดต่อไปนี้ลงในตัวควบคุมการดูเพื่อโหลดตัวควบคุมแบบขยายเมื่อผู้ใช้เริ่มแคสต์วิดีโอ
func playSelectedItemRemotely() { GCKCastContext.sharedInstance().presentDefaultExpandedMediaControls() ... // Load your media sessionManager.currentSession?.remoteMediaClient?.loadMedia(mediaInformation) }
- (void)playSelectedItemRemotely { [[GCKCastContext sharedInstance] presentDefaultExpandedMediaControls]; ... // Load your media [self.sessionManager.currentSession.remoteMediaClient loadMedia:mediaInformation]; }
และจะเปิดตัวควบคุมแบบขยายโดยอัตโนมัติเมื่อผู้ใช้แตะตัวควบคุมขนาดเล็ก
เมื่อแอปผู้ส่งกําลังเล่นสตรีมแบบสดหรือวิดีโอหรือเสียง SDK จะแสดงปุ่มเล่น/หยุดโดยอัตโนมัติแทนปุ่มเล่น/หยุดชั่วคราวในตัวควบคุมแบบขยาย
ดูใช้รูปแบบที่กําหนดเองกับแอป iOS ของคุณว่าแอปผู้ส่งสามารถกําหนดค่าลักษณะที่ปรากฏของวิดเจ็ตแคสต์ได้อย่างไร
การควบคุมระดับเสียง
เฟรมเวิร์กแคสต์จะจัดการระดับเสียงสําหรับแอปของผู้ส่งโดยอัตโนมัติ เฟรมเวิร์กจะซิงค์กับปริมาณตัวรับเว็บโดยอัตโนมัติสําหรับวิดเจ็ต UI ที่ให้มา หากต้องการซิงค์แถบเลื่อนที่แอปมีให้ ให้ใช้ GCKUIDeviceVolumeController
การควบคุมระดับเสียง
ปุ่มปรับระดับเสียงบนอุปกรณ์ผู้ส่งอาจใช้เพื่อเปลี่ยนระดับเสียงของเซสชันการแคสต์ในอุปกรณ์รับเว็บโดยใช้แฟล็ก physicalVolumeButtonsWillControlDeviceVolume
บน GCKCastOptions
ซึ่งตั้งค่าไว้ใน GCKCastContext
let criteria = GCKDiscoveryCriteria(applicationID: kReceiverAppID) let options = GCKCastOptions(discoveryCriteria: criteria) options.physicalVolumeButtonsWillControlDeviceVolume = true GCKCastContext.setSharedInstanceWith(options)
GCKDiscoveryCriteria *criteria = [[GCKDiscoveryCriteria alloc] initWithApplicationID:kReceiverAppID]; GCKCastOptions *options = [[GCKCastOptions alloc] initWithDiscoveryCriteria :criteria]; options.physicalVolumeButtonsWillControlDeviceVolume = YES; [GCKCastContext setSharedInstanceWithOptions:options];
จัดการข้อผิดพลาด
แอปของผู้ส่งจําเป็นต้องใช้การจัดการการเรียกกลับสําหรับข้อผิดพลาดทั้งหมดและตัดสินใจว่าจะให้การตอบสนองที่ดีที่สุดสําหรับแต่ละขั้นตอนของวงจรการแคสต์ แอปจะแสดงกล่องโต้ตอบแสดงข้อผิดพลาดแก่ผู้ใช้ หรือจะตัดสินใจสิ้นสุดเซสชันการแคสต์ก็ได้
Logging
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) } } }
AppDelegate.h
@interface AppDelegate () <GCKLoggerDelegate> @end
AppDelegate.m
@implementation AppDelegate static NSString *const kReceiverAppID = @"AABBCCDD"; static const BOOL kDebugLoggingEnabled = YES; - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { ... // Enable logger. [GCKLogger sharedInstance].delegate = self; ... return YES; } ... #pragma mark - GCKLoggerDelegate - (void)logMessage:(NSString *)message atLevel:(GCKLoggerLevel)level fromFunction:(NSString *)function location:(NSString *)location { if (kDebugLoggingEnabled) { NSLog(@"%@ - %@, %@", function, message, location); } } @end
หากต้องการเปิดใช้การแก้ไขข้อบกพร่องและข้อความแบบละเอียดด้วย ให้เพิ่มบรรทัดนี้ในโค้ดหลังจากตั้งค่าผู้รับมอบสิทธิ์ (แสดงก่อนหน้านี้)
let filter = GCKLoggerFilter.init() filter.minimumLevel = GCKLoggerLevel.verbose GCKLogger.sharedInstance().filter = filter
GCKLoggerFilter *filter = [[GCKLoggerFilter alloc] init]; [filter setMinimumLevel:GCKLoggerLevelVerbose]; [GCKLogger sharedInstance].filter = filter;
นอกจากนี้ คุณยังกรองข้อความบันทึกที่จัดทําโดย GCKLogger
ได้ด้วย
กําหนดระดับการบันทึกขั้นต่ําต่อชั้นเรียน เช่น
let filter = GCKLoggerFilter.init() filter.setLoggingLevel(GCKLoggerLevel.verbose, forClasses: ["GCKUICastButton", "GCKUIImageCache", "NSMutableDictionary"]) GCKLogger.sharedInstance().filter = filter
GCKLoggerFilter *filter = [[GCKLoggerFilter alloc] init]; [filter setLoggingLevel:GCKLoggerLevelVerbose forClasses:@[@"GCKUICastButton", @"GCKUIImageCache", @"NSMutableDictionary" ]]; [GCKLogger sharedInstance].filter = filter;
ชื่อชั้นเรียนอาจเป็นชื่อตามตัวอักษรหรือรูปแบบ Glob ก็ได้ เช่น GCKUI\*
และ GCK\*Session