AVPlayerLoop 没有无缝循环 - Swift 4
AVPlayerLoop not seamlessly looping - Swift 4
我的问题是:我正在尝试进行无缝循环(我打算让我的 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) 。但是一点效果都没有。
任何帮助将不胜感激:)
如果最后播放试试
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 phone 类型和视频的长度以及播放的次数,所以如果你仍然得到那个在消除您可能被迫在用户体验中考虑的时间问题后出现滞后。
循环资产时要记住的一件事是音频和视频轨道可以有不同的偏移量和不同的持续时间,导致循环时 'blips'。这种微小的差异在记录的资产中很常见。
遍历轨道并打印时间范围有助于检测此类情况:for track in asset.tracks { print( track.mediaType); CMTimeRangeShow( track.timeRange); }
将trim个音频和视频轨道设置为相等的开始时间和相等的持续时间,获取轨道的共同时间范围,然后将这个时间范围从原始资产插入到新的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 上放了一个更完整的 trimming 实现:https://github.com/fluthaus/NHBAVAssetTrimming。它更健壮,可以处理多个轨道,保留更多属性,并且可以轻松集成到项目中或构建为独立的 macOS 命令行电影 trimming 实用程序。
@NoHalfBits 的回答很好,但我也找到了另一个解决方案。我基本上从 playerItem 的资产中获得了视频和声音媒体类型的交集时间范围。之后,我在调用时在参数中添加了 intersectionTimeRange 作为 timeRange:
playerLooper = AVPlayerLooper(playerQueue:_, playerItem:_, timeRange: intersectionTimeRange)
这行得通!要获取每个的 timeRanges,请为 playerItem 的资产设置一个 for 循环。
看来是.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()
我的问题是:我正在尝试进行无缝循环(我打算让我的 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) 。但是一点效果都没有。
任何帮助将不胜感激:)
如果最后播放试试
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 phone 类型和视频的长度以及播放的次数,所以如果你仍然得到那个在消除您可能被迫在用户体验中考虑的时间问题后出现滞后。
循环资产时要记住的一件事是音频和视频轨道可以有不同的偏移量和不同的持续时间,导致循环时 'blips'。这种微小的差异在记录的资产中很常见。
遍历轨道并打印时间范围有助于检测此类情况:for track in asset.tracks { print( track.mediaType); CMTimeRangeShow( track.timeRange); }
将trim个音频和视频轨道设置为相等的开始时间和相等的持续时间,获取轨道的共同时间范围,然后将这个时间范围从原始资产插入到新的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 上放了一个更完整的 trimming 实现:https://github.com/fluthaus/NHBAVAssetTrimming。它更健壮,可以处理多个轨道,保留更多属性,并且可以轻松集成到项目中或构建为独立的 macOS 命令行电影 trimming 实用程序。
@NoHalfBits 的回答很好,但我也找到了另一个解决方案。我基本上从 playerItem 的资产中获得了视频和声音媒体类型的交集时间范围。之后,我在调用时在参数中添加了 intersectionTimeRange 作为 timeRange:
playerLooper = AVPlayerLooper(playerQueue:_, playerItem:_, timeRange: intersectionTimeRange)
这行得通!要获取每个的 timeRanges,请为 playerItem 的资产设置一个 for 循环。
看来是.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()