iOS 和 tvOS 更新至 17.0 后 AVPlayer 出现意外行为

问题描述 投票:0回答:1

主线程被 HTTP(S) 资产尚未加载的属性 (assetProperty_MediaPlaybackValidation) 的同步属性查询阻塞。如果从慢速网络读取该资产,这可能会成为问题。

在 iOS 和 tvOS 更新后,我在 iPad 和 Apple TV 中使用 AVPlayer 播放视频时遇到此错误。

视频有时播放速度快了 2 倍,后来完全停止播放。

我试图避免在主线程中调用播放函数。

swift avplayer tvos ios17
1个回答
0
投票

我遇到了同样的问题,并在深入研究文档一段时间后解决了这个问题。据我所知,问题在于 play 方法获取了您尚未获取的播放资产所需的所有元数据。就我而言,这是一个视频。

这是 Apple 的相关文档。 https://developer.apple.com/documentation/avfoundation/media_assets/retriving_media_metadata

这是我用来加载元数据的实际函数。 https://developer.apple.com/documentation/avfoundation/avasynchronouskeyvalueloading/3747326-load

这是我拥有的视频播放器,它在尝试播放资产之前异步加载元数据。加载视频时可能会有一点延迟,视频播放器上会显示黑屏,但不会阻止任何 UI,警告也会消失。

import SwiftUI
import AVKit

struct VideoPlayerView: View {
    
    private var videoURL : URL
    @State private var player: AVPlayer?
    
    @State private var isMuted: Bool = true
    
    var showMuteButton: Bool
    
    init(url: URL, showMuteButton: Bool = true) {
        self.videoURL = url
        self.showMuteButton = showMuteButton
    }
    
    
    var body: some View {
        VideoPlayer(player: player)
        
            .onAppear() {
                Task {
                    player = AVPlayer()
                    await loadPlayerItem(self.videoURL)
                    
                    player?.isMuted = true
                    self.isMuted = player?.isMuted ?? false
                    
                    player?.play()
                    NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: player?.currentItem, queue: .main) { _ in
                        self.player?.seek(to: .zero)
                        self.player?.play()
                    }
                }//: Task
            }//: onAppear
            .onDisappear() {
                Task {
                    // Stop the player when the view disappears
                    player?.pause()
                    
                }
            }
            .onChange(of: videoURL) { oldValue, newValue in
                Task {
                    await loadPlayerItem(newValue)
                    
                }
            }
            .overlay(alignment: .topTrailing) {
                if showMuteButton {
                    Image(systemName: isMuted ? "speaker.slash.fill" : "speaker.wave.2.fill")
                        .font(.footnote)
                        .foregroundColor(.white)
                        .padding(8)
                        .background(Color.gray.opacity(0.3))
                        .clipShape(Circle())
                        .padding()
                        .onTapGesture {
                            player?.isMuted.toggle()
                            self.isMuted = player?.isMuted ?? false
                        }
                }
            }
            
    }
    
    
    func loadPlayerItem(_ videoURL: URL) async {
        
        let asset = AVAsset(url: videoURL)
        do {
            let (_, _, _) = try await asset.load(.tracks, .duration, .preferredTransform)
        } catch let error {
            print(error.localizedDescription)
        }
        
        let item = AVPlayerItem(asset: asset)
        player?.replaceCurrentItem(with: item)
        
    }
}

关键函数是“loadPlayerItem”。您实际上不需要加载函数的返回值,但加载它们后会缓存它们,以便播放函数拥有播放资产所需的一切。

我希望这有帮助。

© www.soinside.com 2019 - 2024. All rights reserved.