无法通过 google 在 ios 应用中正确投射视频
Can't cast video via google cast correctly in ios app
我正在开发一个基于 AVPlayer 的自定义视频播放器项目。尝试整合 google 投射。我已经根据 google tuts 进行了集成:https://codelabs.developers.google.com/codelabs/cast-videos-ios/
但是转换为 swift。一切似乎都工作正常,投射时,如果视频播放器打开,并且有连接的设备(或者如果我从面板连接),我形成文件的元信息,并将其传递给 google 投射 - 一切正常。
但是,我有奇怪的行为:
1) 开始投射,打开视频,然后是另一个视频,然后是第三个视频。
2) 停止铸造
3) 转到另一个视频,启用投射,但它不会启动该视频。它开始投放我之前打开的第一个视频....
我试图找到任何清除缓存或队列的方法,但是没有..求助
class VideoVC: UIViewController, UIGestureRecognizerDelegate, GCKSessionManagerListener {
var filmTitle: String!
var toPass: String!
var film: MovieDetails!
var filmDetails: Movie!
var sessionManager: GCKSessionManager?
var castSession: GCKCastSession?
var castMediaController: GCKUIMediaController?
var checkPlayed = 0
override func viewDidLoad() {
super.viewDidLoad()
sessionManager = GCKCastContext.sharedInstance().sessionManager
sessionManager?.add(self)
castMediaController = GCKUIMediaController()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if let videoURL = toPass {
if let video = URL(string: videoURL) {
player = AVPlayer(url: video)
player.allowsExternalPlayback = true
player.usesExternalPlaybackWhileExternalScreenIsActive = true
playerController.player = player
self.addChildViewController(playerController)
self.view.addSubview(playerController.view)
playerController.view.frame = self.view.frame
self.view.sendSubview(toBack: playerController.view)
}
}
if isCastEnabled() {
playSelectedItemRemotely()
}
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
player.replaceCurrentItem(with: nil)
}
func buildMediaInformation() -> GCKMediaInformation {
let metaData = GCKMediaMetadata(metadataType: GCKMediaMetadataType(rawValue: 1)!)
metaData.setString(filmTitle, forKey: kGCKMetadataKeyTitle)
if let imageUrl = URL(string: filmDetails.poster_cast!) {
let image = GCKImage(url: imageUrl, width: 340, height: 450)
metaData.addImage(image)
}
if let episode = film.serial_episode, let season = film.serial_season, season != "", episode != "", let title = film.title, title != "" {
let subtitle = "\(title) \(episode) серия \(season) сезон"
metaData.setString(subtitle, forKey: kGCKMetadataKeySubtitle)
}
let duration = Double(film.duration!)
let mediaInfo = GCKMediaInformation(contentID: toPass!,
streamType: GCKMediaStreamType.buffered,
contentType: film.contentType!,
metadata: metaData as GCKMediaMetadata,
streamDuration: duration,
mediaTracks: nil,
textTrackStyle: nil,
customData: nil)
print("toPass: \(toPass!)")
print("duration: \(duration)")
return mediaInfo
}
func playSelectedItemRemotely() {
let castSession = GCKCastContext.sharedInstance().sessionManager.currentCastSession
if (castSession != nil) {
castSession?.remoteMediaClient?.loadMedia(self.buildMediaInformation(), autoplay: true)
self.dismiss(animated: true, completion: nil)
}
else {
print("no castSession!")
}
}
func sessionManager(_ sessionManager: GCKSessionManager, didStart session: GCKSession) {
playSelectedItemRemotely()
}
func sessionManager(_ sessionManager: GCKSessionManager, didResumeSession session: GCKSession) {
}
func sessionManager(_ sessionManager: GCKSessionManager, didEnd session: GCKSession, withError error: Error?) {
let castSession = GCKCastContext.sharedInstance().sessionManager.currentCastSession
castSession?.endAndStopCasting(true)
}
func sessionManager(_ sessionManager: GCKSessionManager, didFailToStart session: GCKSession, withError error: Error) {
Utils.showOverAnyVC("Ошибка подключения", message: "Попробуйте еще раз!")
}
func isCastEnabled() -> Bool {
switch GCKCastContext.sharedInstance().castState {
case GCKCastState.connected:
print("cast connected")
return true
case GCKCastState.connecting:
print("cast connecting")
return true
case GCKCastState.notConnected:
print("cast notConnected")
return false
case GCKCastState.noDevicesAvailable:
print("cast noDevicesAvailable")
return false
}
}}
和我的应用代表:
class AppDelegate: UIResponder, UIApplicationDelegate, GCKLoggerDelegate, UNUserNotificationCenterDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
let options = GCKCastOptions(receiverApplicationID: "F443E49F")
GCKCastContext.setSharedInstanceWith(options)
GCKLogger.sharedInstance().delegate = self
let appStoryboard = UIStoryboard(name: "NewMain", bundle: nil)
let navigationController = appStoryboard.instantiateViewController(withIdentifier: "MainNavigation")
let castContainerVC: GCKUICastContainerViewController = GCKCastContext.sharedInstance().createCastContainerController(for: navigationController)
castContainerVC.miniMediaControlsItemEnabled = true
self.window = UIWindow(frame: UIScreen.main.bounds)
self.window?.rootViewController = castContainerVC
self.window?.makeKeyAndVisible()
GCKCastContext.sharedInstance().useDefaultExpandedMediaControls = true
return true
}
func logMessage(_ message: String, fromFunction function: String) {
print("message: \(function)")
}}
可能的解决方案可能是由于:
sessionManager?.add(self)
您添加了委托,但从未清除它。因此,VideoVC
永远不会由于会话管理器的保留引用而被销毁。当您重新打开 VideoVC
时,会话管理器仍在访问您第一次加载它时的委托。
正因为如此,调用下面的时候:
func sessionManager(_ sessionManager: GCKSessionManager, didStart session: GCKSession) {
这是在您 VideoVC
的第一个实例中调用的,它现在有错误的文件信息。
您可以通过将 print(self) 放入上述方法并查看内存指针值来监控这一点。检查它是否也匹配在 viewDidLoad
中调用的相同内存指针值
更新
为了更好地管理委托更改以下方法:viewDidDisappear()
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
player.replaceCurrentItem(with: nil)
//this stops the session manager sending callbacks to your VideoVC
sessionManager.remove(self)
}
我正在开发一个基于 AVPlayer 的自定义视频播放器项目。尝试整合 google 投射。我已经根据 google tuts 进行了集成:https://codelabs.developers.google.com/codelabs/cast-videos-ios/ 但是转换为 swift。一切似乎都工作正常,投射时,如果视频播放器打开,并且有连接的设备(或者如果我从面板连接),我形成文件的元信息,并将其传递给 google 投射 - 一切正常。
但是,我有奇怪的行为: 1) 开始投射,打开视频,然后是另一个视频,然后是第三个视频。 2) 停止铸造 3) 转到另一个视频,启用投射,但它不会启动该视频。它开始投放我之前打开的第一个视频....
我试图找到任何清除缓存或队列的方法,但是没有..求助
class VideoVC: UIViewController, UIGestureRecognizerDelegate, GCKSessionManagerListener {
var filmTitle: String!
var toPass: String!
var film: MovieDetails!
var filmDetails: Movie!
var sessionManager: GCKSessionManager?
var castSession: GCKCastSession?
var castMediaController: GCKUIMediaController?
var checkPlayed = 0
override func viewDidLoad() {
super.viewDidLoad()
sessionManager = GCKCastContext.sharedInstance().sessionManager
sessionManager?.add(self)
castMediaController = GCKUIMediaController()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if let videoURL = toPass {
if let video = URL(string: videoURL) {
player = AVPlayer(url: video)
player.allowsExternalPlayback = true
player.usesExternalPlaybackWhileExternalScreenIsActive = true
playerController.player = player
self.addChildViewController(playerController)
self.view.addSubview(playerController.view)
playerController.view.frame = self.view.frame
self.view.sendSubview(toBack: playerController.view)
}
}
if isCastEnabled() {
playSelectedItemRemotely()
}
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
player.replaceCurrentItem(with: nil)
}
func buildMediaInformation() -> GCKMediaInformation {
let metaData = GCKMediaMetadata(metadataType: GCKMediaMetadataType(rawValue: 1)!)
metaData.setString(filmTitle, forKey: kGCKMetadataKeyTitle)
if let imageUrl = URL(string: filmDetails.poster_cast!) {
let image = GCKImage(url: imageUrl, width: 340, height: 450)
metaData.addImage(image)
}
if let episode = film.serial_episode, let season = film.serial_season, season != "", episode != "", let title = film.title, title != "" {
let subtitle = "\(title) \(episode) серия \(season) сезон"
metaData.setString(subtitle, forKey: kGCKMetadataKeySubtitle)
}
let duration = Double(film.duration!)
let mediaInfo = GCKMediaInformation(contentID: toPass!,
streamType: GCKMediaStreamType.buffered,
contentType: film.contentType!,
metadata: metaData as GCKMediaMetadata,
streamDuration: duration,
mediaTracks: nil,
textTrackStyle: nil,
customData: nil)
print("toPass: \(toPass!)")
print("duration: \(duration)")
return mediaInfo
}
func playSelectedItemRemotely() {
let castSession = GCKCastContext.sharedInstance().sessionManager.currentCastSession
if (castSession != nil) {
castSession?.remoteMediaClient?.loadMedia(self.buildMediaInformation(), autoplay: true)
self.dismiss(animated: true, completion: nil)
}
else {
print("no castSession!")
}
}
func sessionManager(_ sessionManager: GCKSessionManager, didStart session: GCKSession) {
playSelectedItemRemotely()
}
func sessionManager(_ sessionManager: GCKSessionManager, didResumeSession session: GCKSession) {
}
func sessionManager(_ sessionManager: GCKSessionManager, didEnd session: GCKSession, withError error: Error?) {
let castSession = GCKCastContext.sharedInstance().sessionManager.currentCastSession
castSession?.endAndStopCasting(true)
}
func sessionManager(_ sessionManager: GCKSessionManager, didFailToStart session: GCKSession, withError error: Error) {
Utils.showOverAnyVC("Ошибка подключения", message: "Попробуйте еще раз!")
}
func isCastEnabled() -> Bool {
switch GCKCastContext.sharedInstance().castState {
case GCKCastState.connected:
print("cast connected")
return true
case GCKCastState.connecting:
print("cast connecting")
return true
case GCKCastState.notConnected:
print("cast notConnected")
return false
case GCKCastState.noDevicesAvailable:
print("cast noDevicesAvailable")
return false
}
}}
和我的应用代表:
class AppDelegate: UIResponder, UIApplicationDelegate, GCKLoggerDelegate, UNUserNotificationCenterDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
let options = GCKCastOptions(receiverApplicationID: "F443E49F")
GCKCastContext.setSharedInstanceWith(options)
GCKLogger.sharedInstance().delegate = self
let appStoryboard = UIStoryboard(name: "NewMain", bundle: nil)
let navigationController = appStoryboard.instantiateViewController(withIdentifier: "MainNavigation")
let castContainerVC: GCKUICastContainerViewController = GCKCastContext.sharedInstance().createCastContainerController(for: navigationController)
castContainerVC.miniMediaControlsItemEnabled = true
self.window = UIWindow(frame: UIScreen.main.bounds)
self.window?.rootViewController = castContainerVC
self.window?.makeKeyAndVisible()
GCKCastContext.sharedInstance().useDefaultExpandedMediaControls = true
return true
}
func logMessage(_ message: String, fromFunction function: String) {
print("message: \(function)")
}}
可能的解决方案可能是由于:
sessionManager?.add(self)
您添加了委托,但从未清除它。因此,VideoVC
永远不会由于会话管理器的保留引用而被销毁。当您重新打开 VideoVC
时,会话管理器仍在访问您第一次加载它时的委托。
正因为如此,调用下面的时候:
func sessionManager(_ sessionManager: GCKSessionManager, didStart session: GCKSession) {
这是在您 VideoVC
的第一个实例中调用的,它现在有错误的文件信息。
您可以通过将 print(self) 放入上述方法并查看内存指针值来监控这一点。检查它是否也匹配在 viewDidLoad
更新
为了更好地管理委托更改以下方法:viewDidDisappear()
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
player.replaceCurrentItem(with: nil)
//this stops the session manager sending callbacks to your VideoVC
sessionManager.remove(self)
}