Hello friends! The question actually duplicates this post: https://stackoverflow.com/questions/64512330/avplayer-with-resourceloader-delegate-get-data-but-not-playing-video
My task is to play a video from a repository that requires a certificate. If I use a simple AVPlayer setup without setting a AVAssetResourceLoaderDelegate:
the player perfectly opens urls from the unprotected storage and plays the video. But on trying to play video from a protected folder, it gives an error: "The certificate for this server is invalid". This problem is solved by setting a resourceLoader delegate:
And the delegate method itself:
The data and the response come, the error is nil. The data is of the correct size. The response contains the status 206. The player's item has an observer of its state, but no one displays any errors - the player is just black and the download wheel is spinning.
But I went further. There is such a project: https://github.com/2ZGroupSolutionsArticles/Article_EZ002
I replaced my delegate with his delegate - identical behavior. But it also has the saving of the downloaded data to a local file. So, if after the completion of saving try to play this file - everything is OK, it is played.
My SwiftUI player:
I think I'm already out of ideas. I would be grateful for any help.
Upd. I printed out the state of the player and its currentItem before re-creating it to play the local file, and I got this:
My task is to play a video from a repository that requires a certificate. If I use a simple AVPlayer setup without setting a AVAssetResourceLoaderDelegate:
Code Block controller.player = AVPlayer(url: urlFromUnprotectedPlace)
the player perfectly opens urls from the unprotected storage and plays the video. But on trying to play video from a protected folder, it gives an error: "The certificate for this server is invalid". This problem is solved by setting a resourceLoader delegate:
Code Block let asset = AVURLAsset(url: customSchemeUrl) asset.resourceLoader.setDelegate(myDelegate, queue: .main) let item = AVPlayerItem(asset: asset) controller.player = AVPlayer(playerItem: item)
And the delegate method itself:
Code Block func resourceLoader( _ resourceLoader: AVAssetResourceLoader, shouldWaitForLoadingOfRequestedResource loadingRequest: AVAssetResourceLoadingRequest ) -> Bool { guard let url = url else { logger.log("nil url") return false } var request = URLRequest(url: url) request.httpMethod = "GET" request.setValue("bytes=0-", forHTTPHeaderField: "Range") let sessionTask = urlSession?.dataTask(with: request) { [weak self] data, response, error in if let error = error { self?.logger.log(error.localizedDescription) return } loadingRequest.contentInformationRequest?.contentType = AVFileType.mp4.rawValue loadingRequest.contentInformationRequest?.isByteRangeAccessSupported = true loadingRequest.response = response if let data = data { loadingRequest.contentInformationRequest?.contentLength = Int64(data.count) loadingRequest.dataRequest?.respond(with: data) } loadingRequest.finishLoading() } sessionTask?.resume() return true }
The data and the response come, the error is nil. The data is of the correct size. The response contains the status 206. The player's item has an observer of its state, but no one displays any errors - the player is just black and the download wheel is spinning.
But I went further. There is such a project: https://github.com/2ZGroupSolutionsArticles/Article_EZ002
I replaced my delegate with his delegate - identical behavior. But it also has the saving of the downloaded data to a local file. So, if after the completion of saving try to play this file - everything is OK, it is played.
My SwiftUI player:
Code Block final class MyVideoPlayer: UIViewControllerRepresentable { private let controller = AVPlayerViewController() private var playerItemObserver: MyPlayerItemStateObserver? private var resourceLoaderDelegate: EugeneResourceLoaderDelegate? private var playingStarted = false init(url: URL?) { guard let url = url else { return } resourceLoaderDelegate = EugeneResourceLoaderDelegate(withURL: url) resourceLoaderDelegate?.completion = downloadCompletion() controller.player = createPlayer() } func makeUIViewController(context: Context) -> AVPlayerViewController { controller } func updateUIViewController(_ uiViewController: AVPlayerViewController, context: Context) {} func play() -> some View { playingStarted = true controller.player?.play() return self } } extension MyVideoPlayer { private func createPlayer() -> AVPlayer? { guard let delegate = resourceLoaderDelegate, let streamingUrl = delegate.streamingAssetURL else { return nil } let asset = AVURLAsset(url: streamingUrl) asset.resourceLoader.setDelegate(delegate, queue: .main) playerItemObserver = MyPlayerItemStateObserver(asset: asset, logger: logger) if let item = playerItemObserver?.item { return AVPlayer(playerItem: item) } return nil } private func downloadCompletion() -> (URL?) -> Void { { [weak self] localFileURL in guard let self = self else { return } guard let localFileURL = localFileURL else { print("Failed to download media file.") return } print("Media file saved to: \(localFileURL)") let status = self.controller.player?.timeControlStatus if self.playingStarted && status?.rawValue == 1 { self.controller.player = AVPlayer(url: localFileURL) self.controller.player?.play() } } } }
I think I'm already out of ideas. I would be grateful for any help.
Upd. I printed out the state of the player and its currentItem before re-creating it to play the local file, and I got this:
Code Block player?.reasonForWaitingToPlay - AVPlayerWaitingWhileEvaluatingBufferingRateReason player?.currentItem?.isPlaybackBufferEmpty - true player?.currentItem?.duration - value: 0