Can't cast video via google cast correctly in

2019-04-30 02:19发布

问题:

I'm working on a project with custom video player, based on AVPlayer. Trying to integrate google cast. I've made integration based on google tuts: https://codelabs.developers.google.com/codelabs/cast-videos-ios/ But with conversion to swift. Everything seems to work fine, when cast, if video player opens, and there is connected device (or if I connect from panel), I form meta info for file, and it's passed to google cast - everything works fine.

But, i have strange behavior: 1) Start casting, open video, then another video, then third video. 2) Stop casting 3) Go to another video, enable casting, but it doesn't start this video. It start casting the first video I opened earlier....

I tried to find any method that clears cache or queue, but there is no.. Please, help

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
    }
}}

and my appdelegate:

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)")
}}

回答1:

On possible solution could be due to:

sessionManager?.add(self)

You add the delegate but at no point do you clear it. As a result, the VideoVC is never destroyed due to the retained reference from the session manager. When you reopen the VideoVC the session manager is still also accessing the delegate from the first time you loaded it.

Because of this, when the following is called:

func sessionManager(_ sessionManager: GCKSessionManager, didStart session: GCKSession) {

This is being called in your first instance of VideoVC which now has the wrong file information.

You can monitor this by putting a print(self) into the above method and look at the memory pointer value. Check that it also matches the same memory pointer value that is called in viewDidLoad

Update

To better manage the delegate change the following method: 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)
}