AVPlayer卡在视频播放,但音频正常。

7

我通过AVURLAsset创建AVPlayerItem,我的代码如下:

let asset = AVURLAsset(URL: safeURL, options: [AVURLAssetPreferPreciseDurationAndTimingKey: true])
    asset.loadValuesAsynchronouslyForKeys([assetKeyPlayable, self.assetKeyTracks, self.assetKeyHasProtectedContent]) {
        () -> Void in
        dispatch_async(dispatch_get_main_queue(), {
            () -> Void in
            // Use the AVAsset playable property to detect whether the asset can be played
            if !asset.playable {
                let localizedDescription = "Item cannot be played,Item cannot be played description"
                let localizedFailureReason = "The assets tracks were loaded, but could not be made playable,Item cannot be played failure reason"
                let userInfo = [NSLocalizedDescriptionKey: localizedDescription, NSLocalizedFailureReasonErrorKey: localizedFailureReason]
                let error = NSError(domain: "domain", code: 0, userInfo: userInfo)
                self.videoPlayerDelegate?.videoPlayer?(self, playerItemStatusDidFail: error)
                self.cleanPlayer()
                return
            }
            // At this point we're ready to set up for playback of the asset. Stop observing
            if let _ = self.player?.currentItem {
                self.cleanPlayer()
            }
            if asset.URL.absoluteString != safeURL.absoluteString {
                return
            }
            var error: NSError?
            let status = asset.statusOfValueForKey(self.assetKeyTracks, error: &error)
            var playerItem = AVPlayerItem(URL: safeURL)
            if status == .Loaded {
                playerItem = AVPlayerItem(asset: asset)
            } else {
                // You should deal with the error appropriately.If Loaded fails, create an AVPlayerItem directly from the URL
                playerItem = AVPlayerItem(URL: safeURL)
            }
            self.player = self.playerWithPlayerItem(playerItem)
            self.registerMonitoring()
            self.registerNotification()
            self.addTimeObserver()
            completionBlock?(loadURLString: playerURL.absoluteString)
        })
    }

我想在我的视图中添加AVPlayerLayer来显示视频,我的代码如下:

// MARK: - 属性

var player: AVPlayer? {
    get {
        return playerLayer.player
    }

    set {
        playerLayer.player = newValue
    }
}

var playerLayer: AVPlayerLayer {
    return layer as! AVPlayerLayer
}

视频加载完成后显示

self.videoPlayer?.loadPlayer({
        [weak self](loadURLString) in
        if let strongSelf = self {
            strongSelf.player = strongSelf.videoPlayer?.player
            strongSelf.startPlay()
        }
    })

调用seekToTime方法指定播放:

self.player?.currentItem?.seekToTime(CMTimeMakeWithSeconds(time, Int32(NSEC_PER_SEC)), toleranceBefore: kCMTimeZero, toleranceAfter: kCMTimeZero) {
        [weak self] finished in
        if let weakSelf = self {
            if weakSelf.isPlaying {
               weakSelf.videoPlayerDelegate?.videoPlayerDidplayerItemSeekToTime?(weakSelf)
            }
        }
    }

一些界面卡住的图片:
第一张图片中声音可听,但是界面卡住了。
在第二张图片中,视频可以播放,但是没有声音。
我的问题是:
当我完成调用“seekToTime”方法时,有时候视频有声音,但是界面被卡住了,偶尔视频会正常工作。我尝试调用“CALayer”的“setNeedsDisplay”方法来更新“AVPlayerLayer”的图像,但是这并没有起到任何帮助。我不知道该怎么办了,非常感谢您提供的任何帮助。

1
Shannon,我不确定,但也许你的AVURLAsset在这里超出了范围;AVPlayer是非常有弹性的代码片段;有时会让你感到无助而不是一片黑暗。 - user3069232
你应该创建一个非常简单的演示项目,添加一些平淡无奇的资产,并在那里复制问题。你可以将该项目发布到Dropbox等网站上,然后请求帮助进行调试。 - David H
3个回答

4

由于很多人都遇到了类似的问题但没有答案,我在这里回答一下。我尝试使用AVPlayer和AVPlayerLayer在UIView中播放视频,但当我将同一AVPlayer用于在AVPlayerViewController中加载视频时,视频卡住了而音频继续播放。

解决方法是在将同一AVPlayer用于其他地方之前先销毁AVPlayerLayer。真不敢相信我在这里找到了这个启示。

https://twitter.com/skumancer/status/294605708073263104

我是如何实现的呢?

我在我的主要故事板中创建了两个视图控制器。

1)视图控制器场景     i)其中有一个UIView,引用IBOutlet 'videoView'     ii)一个按钮,通过segue导航到“显示”AV Player View Controller Scene,引用IBOutlet 'fullscreen' 2)AV Player View Controller Scene

视频播放器的故事板。

// Code in your controller class.


// Make sure to reference your IBOutlet in your StoryBoard.
@IBOutlet weak var videoView : UIView!
@IBOutlet weak var fullscreen : UIButton!

var player:AVPlayer? = nil;
var playerLayer:AVPlayerLayer? = nil;

override func viewDidLoad() {
    super.viewDidLoad()

    // Your Url to play, could be hls type video
    let url = URL(string: "https://your_url.m3u8");

    let asset = AVAsset(url : url!)

    let playerItem = AVPlayerItem(asset: asset)

    player = AVPlayer(playerItem: playerItem)
}


override func viewDidAppear(_ animated: Bool) {
    playInUIView()
    pauseVideo()
}

// Creates An AVPlayerLayer and adds it as a sublayer in UIView.
func playInUIView(){
    // Creating player layer to render video.
    playerLayer = AVPlayerLayer(player: player)

    playerLayer?.frame = self.videoView.bounds

    playerLayer?.videoGravity = .resizeAspect

    // Adding a sub layer to display it in UIView
    self.videoView.layer.addSublayer(playerLayer!)
}

// Destroyes an AVPlayerLayer and Sublayer of your UIView
func destroyPlayInView(){
    self.videoView.layer.sublayers=nil
    playerLayer = nil
}
    
// On button click open AVPlayerViewController.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    let destination = segue.destination as! AVPlayerViewController

    // Destroy AVPlayerLayer before rendering video in AVPlayerViewController.
    destroyPlayInView()

    // Set same AVPlayer in AVPlayerViewController.
    destination.player = self.player

    self.present(destination, animated: true) {
        destination.player!.play()
    }
}

PS - 我目前尚未实施任何控件,并且由于我刚开始学习Swift和IOS开发几天,可能存在编码不规范的情况,所以请在我出错的地方告诉我。


1
哇哦,我到处寻找解决方案。仅将avPlayerLayer设置为nil就适用于我。做得好,非常感谢您的分享。我将我的avPlayerLayer嵌入到一个avPlayerViewController中,再将其放入一个UICollectionViewCell中。我简直不敢相信这个方法居然可行。请注意,我像应该做的那样在主线程上运行了所有内容等等。 - Joshua Wolff

3

我遇到了同样的问题,并通过这种方法解决了它,建议你也试一下。

player.pause()
player.currentItem?.seek(to: CMTime(), completionHandler: { (_) in
    player.play()
})

0

用户nferocious76的相同代码,但使用Objective C编写

[self.player pause];
AVPlayerItem *playerItem = [self.player currentItem];
__weak typeof(self) weakSelf = self;
[playerItem seekToTime:playerItem.currentTime completionHandler:^(BOOL finished){
    if (finished){   
        [weakSelf.player play];
    }
 }];

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接