2

我的问题是:我正在尝试进行无缝循环(我打算让我的 AVPlayer 或 AVPlayerQueue,循环播放之间没有任何延迟)。因此,例如,如果我制作一个视频并进行播放,它应该无限循环,中间没有任何光点或循环延迟。

我已经编写了下面的代码(它也直接来自示例代码):

    var playerQQ: AVQueuePlayer!
    var playerLayur: AVPlayerLayer!
    var playerEyetem: AVPlayerItem!
    var playerLooper: AVPlayerLooper!

    func playRecordedVideo(videoURL: URL) {

        playerQQ = AVQueuePlayer()
        playerLayur = AVPlayerLayer(player: playerQQ)
        playerLayur.frame = (camBaseLayer?.bounds)! 
       camBaseLayer?.layer.insertSublayer(playerLayur, above: previewLayer) 

       playerEyetem = AVPlayerItem(url: videoURL)
        playerLooper = AVPlayerLooper(player: playerQQ, templateItem: playerEyetem)
        playerQQ.play()

    }

上面的代码没有无缝循环;它在当前播放器的结尾和下一个播放器之间有光点。我已经尝试了很多找到问题并在网上搜索并没有找到解决方案。此外,我一直在尝试 NSNotifications 和其他方法,包括在播放器完成播放时设置 Player.seek(to: zero)。但根本没有任何效果。

任何帮助,将不胜感激 :)

4

4 回答 4

1

看起来像 .mp4 文件问题,将 .mp4 文件转换为 .mov 文件。带有 .mp4 文件的 AVPlayer 或 AVQueuePlayer 都可以正常工作。这是我的代码:

NotificationCenter.default.addObserver(forName: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: nil, queue: nil) { [weak self] (noty) in
   self?.player?.seek(to: CMTime.zero)
   self?.player?.play() }

或者

let asset = AVAsset(url: URL(fileURLWithPath: movPath))
let playerItem = AVPlayerItem(asset: asset)
let player = AVQueuePlayer(playerItem: playerItem)
playerLooper = AVPlayerLooper(player: player, templateItem: playerItem)
plyerLayer.frame = CGRect(x: 0, y: 88, width: kWidth, height: kWidth * 0.75)
plyerLayer.videoGravity = .resizeAspectFill
plyerLayer.player = player
view.layer.addSublayer(plyerLayer)
player.play()
于 2020-07-28T09:09:18.620 回答
0

循环资产时要记住的一件事是,音频和视频轨道可以有不同的偏移量和不同的持续时间,从而导致循环时出现“闪烁”。这种微小的差异在记录资产中很常见。

遍历轨迹并打印时间范围有助于检测此类情况:for track in asset.tracks { print( track.mediaType); CMTimeRangeShow( track.timeRange); }

要将音频和视频轨道修剪为相同的开始时间和相同的持续时间,请获取轨道的公共时间范围,然后将此时间范围从原始资产插入到新的AVMutableComposition. 通常,您还希望保留视频轨道的方向等属性:

let asset: AVAsset = (your asset initialization here)

let videoTrack: AVAssetTrack = asset.tracks(withMediaType: .video).first!
let audioTrack: AVAssetTrack = asset.tracks(withMediaType: .audio).first!

// calculate common time range of audio and video track
let timeRange: CMTimeRange = CMTimeRangeGetIntersection( (videoTrack.timeRange), (audioTrack.timeRange))

let composition: AVMutableComposition = AVMutableComposition()

try composition.insertTimeRange(timeRange, of: asset, at: kCMTimeZero)

// preserve orientation
composition.tracks(withMediaType: .video).first!.preferredTransform = videoTrack.preferredTransform

由于 AVMutableComposition 是 AVAsset 的子类,它可以用于AVPlayerLooper基于 - 的循环播放,或使用AVAssetExportSession.

我在 github 上放了一个更完整的修剪实现:https ://github.com/fluthaus/NHBAVAssetTrimming 。它更强大,可以处理多个轨道,保留更多属性,并且可以轻松集成到项目中或构建为独立的 macOS 命令行电影修剪实用程序。

于 2018-12-24T00:47:29.977 回答
0

如果在最后播放尝试

NotificationCenter.default.addObserver(self,
                                       selector: #selector(playerItemDidReachEnd(notification:)),
                                       name: Notification.Name.AVPlayerItemDidPlayToEndTime,
                                       object: avPlayer?.currentItem)

 @objc func playerItemDidReachEnd(notification: Notification) {
        if let playerItem: AVPlayerItem = notification.object as? AVPlayerItem {
            playerItem.seek(to: kCMTimeZero, completionHandler: nil)
        }
    }

如果没有,我建议使用 dTime 管理你自己的(触发 NSTimer 1/30 秒或其他东西)并设置玩这样的东西

    player.seekToTime(seekTimeInProgress, toleranceBefore: kCMTimeZero,
            toleranceAfter: kCMTimeZero, completionHandler: ...

kCMTimeZero 非常重要,否则时间将不准确。最后,我发现重新启动视频时有一个加载时间,具体取决于 iOS 手机类型和视频长度以及播放次数,所以如果在消除时间问题后仍然存在延迟可能会被迫在您的用户体验中考虑。

于 2018-12-20T23:25:56.957 回答
0

@NoHalfBits 的答案效果很好,但我也找到了另一个解决方案。我基本上从 playerItem 的资产中得到了视频和声音媒体类型的交集时间范围。之后,我在调用时在参数中添加了intersectionTimeRange 作为timeRange:

playerLooper = AVPlayerLooper(playerQueue:_, playerItem:_, timeRange: intersectionTimeRange)

这会奏效!要获取每个的 timeRanges,请为 playerItem 的资产设置一个 for 循环。

于 2018-12-24T01:51:28.340 回答