在AVFoundation中有一种相对简单的循环视频方式吗?
我已经创建了我的AVPlayer和AVPlayerLayer:
avPlayer = [[AVPlayer playerWithURL:videoUrl] retain];
avPlayerLayer = [[AVPlayerLayer playerLayerWithPlayer:avPlayer] retain];
avPlayerLayer.frame = contentView.layer.bounds;
[contentView.layer addSublayer: avPlayerLayer];
然后我播放我的视频:
[avPlayer play];
视频播放正常,但最后会停止播放。使用MPMoviePlayerController,您只需将其repeatMode
属性设置为正确的值即可。 AVPlayer似乎没有类似的属性。似乎没有一个回调可以告诉我电影什么时候结束,所以我可以寻找开头再玩一次。
我没有使用MPMoviePlayerController,因为它有一些严重的限制。我希望能够一次播放多个视频流。
答案 0 :(得分:262)
玩家结束时可以获得通知。检查AVPlayerItemDidPlayToEndTimeNotification
设置播放器时:
<强> ObjC 强>
avPlayer.actionAtItemEnd = AVPlayerActionAtItemEndNone;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(playerItemDidReachEnd:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:[avPlayer currentItem]];
这会阻止玩家在结束时暂停。
通知中的:
- (void)playerItemDidReachEnd:(NSNotification *)notification {
AVPlayerItem *p = [notification object];
[p seekToTime:kCMTimeZero];
}
这将回放电影。
不要忘记在释放播放器时取消注册通知。
<强>夫特强>
avPlayer?.actionAtItemEnd = .none
NotificationCenter.default.addObserver(self,
selector: #selector(playerItemDidReachEnd(notification:)),
name: .AVPlayerItemDidPlayToEndTime,
object: avPlayer?.currentItem)
@objc func playerItemDidReachEnd(notification: Notification) {
if let playerItem = notification.object as? AVPlayerItem {
playerItem.seek(to: kCMTimeZero)
}
}
Swift 4 +
@objc func playerItemDidReachEnd(notification: Notification) {
if let playerItem = notification.object as? AVPlayerItem {
playerItem.seek(to: CMTime.zero, completionHandler: nil)
}
}
答案 1 :(得分:48)
如果有帮助,在iOS / tvOS 10中,有一个新的AVPlayerLooper()可以用来创建无缝的视频循环(Swift):
player = AVQueuePlayer()
playerLayer = AVPlayerLayer(player: player)
playerItem = AVPlayerItem(url: videoURL)
playerLooper = AVPlayerLooper(player: player, templateItem: playerItem)
player.play()
这是在WWDC 2016的“AVFoundation Playback的进展”中提出的: https://developer.apple.com/videos/play/wwdc2016/503/
即使使用此代码,在我向Apple提交错误报告并获得此回复之前,我还有一个小问题:
电影持续时间长于音频/视频曲目的电影文件是 问题。 FigPlayer_File正在禁用无间隙转换,因为 音轨编辑比电影持续时间短(15.682 vs. 15.787)。
您需要修复影片文件才能拥有影片持续时间 跟踪持续时间长度相同,或者您可以使用时间范围 AVPlayerLooper的参数(设置时间范围从0到持续时间 音轨)
事实证明,Premiere导出的文件的音轨长度与视频略有不同。在我的情况下,完全删除音频是很好的,这解决了问题。
答案 2 :(得分:16)
以下是我为防止暂停打嗝问题而采取的措施:
<强>夫特:强>
NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime,
object: nil,
queue: nil) { [weak self] note in
self?.avPlayer.seek(to: kCMTimeZero)
self?.avPlayer.play()
}
目标C:
__weak typeof(self) weakSelf = self; // prevent memory cycle
NSNotificationCenter *noteCenter = [NSNotificationCenter defaultCenter];
[noteCenter addObserverForName:AVPlayerItemDidPlayToEndTimeNotification
object:nil
queue:nil
usingBlock:^(NSNotification *note) {
[weakSelf.avPlayer seekToTime:kCMTimeZero];
[weakSelf.avPlayer play];
}];
注意:我没有使用avPlayer.actionAtItemEnd = AVPlayerActionAtItemEndNone
,因为它不需要。
答案 3 :(得分:3)
我建议使用AVQueuePlayer无缝循环播放视频。添加通知观察器
AVPlayerItemDidPlayToEndTimeNotification
并在其选择器中循环播放视频
AVPlayerItem *video = [[AVPlayerItem alloc] initWithURL:videoURL];
[self.player insertItem:video afterItem:nil];
[self.player play];
答案 4 :(得分:3)
为了避免视频倒带时的差距,在合成中使用同一资产的多个副本对我来说效果很好。我在这里找到了它: www.developers-life.com/avplayer-looping-video-without-hiccupdelays.html (链接已经死了)。
AVURLAsset *tAsset = [AVURLAsset assetWithURL:tURL];
CMTimeRange tEditRange = CMTimeRangeMake(CMTimeMake(0, 1), CMTimeMake(tAsset.duration.value, tAsset.duration.timescale));
AVMutableComposition *tComposition = [[[AVMutableComposition alloc] init] autorelease];
for (int i = 0; i < 100; i++) { // Insert some copies.
[tComposition insertTimeRange:tEditRange ofAsset:tAsset atTime:tComposition.duration error:nil];
}
AVPlayerItem *tAVPlayerItem = [[AVPlayerItem alloc] initWithAsset:tComposition];
AVPlayer *tAVPlayer = [[AVPlayer alloc] initWithPlayerItem:tAVPlayerItem];
答案 5 :(得分:2)
对于Swift 3&amp; 4
NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: self.avPlayer?.currentItem, queue: .main) { _ in
self.avPlayer?.seek(to: kCMTimeZero)
self.avPlayer?.play()
}
答案 6 :(得分:1)
这对我来说没有打嗝问题,点在暂停播放器之前调用seekToTime方法:
init AVPlayer
let url = NSBundle.mainBundle().URLForResource("loop", withExtension: "mp4")
let playerItem = AVPlayerItem(URL: url!)
self.backgroundPlayer = AVPlayer(playerItem: playerItem)
let playerLayer = AVPlayerLayer(player: self.backgroundPlayer)
playerLayer.frame = CGRectMake(0, 0, UIScreen.mainScreen().bounds.width, UIScreen.mainScreen().bounds.height)
self.layer.addSublayer(playerLayer)
self.backgroundPlayer!.actionAtItemEnd = .None
self.backgroundPlayer!.play()
注册通知
NSNotificationCenter.defaultCenter().addObserver(self, selector: "videoLoop", name: AVPlayerItemDidPlayToEndTimeNotification, object: self.backgroundPlayer!.currentItem)
videoLoop功能
func videoLoop() {
self.backgroundPlayer?.pause()
self.backgroundPlayer?.currentItem?.seekToTime(kCMTimeZero)
self.backgroundPlayer?.play()
}
答案 7 :(得分:1)
迅速5:
我对以前的答案做了一些细微的调整,例如将playerItem添加到队列,然后再将其添加到playerLayer。
let playerItem = AVPlayerItem(url: url)
let player = AVQueuePlayer(playerItem: playerItem)
let playerLayer = AVPlayerLayer(player: player)
playerLooper = AVPlayerLooper(player: player, templateItem: playerItem)
playerLayer.frame = cell.eventImage.bounds
playerLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
// Add the playerLayer to a UIView.layer
player.play()
并将playerLooper设置为UIViewController的属性,否则视频只能播放一次。
答案 8 :(得分:0)
我在答案中找不到我的解决方案。观察为资产持续时间指定的边界时间会很有帮助。当观察者被触发时,寻找开始并重播。
player?.addBoundaryTimeObserver(forTimes: [NSValue(time: asset.duration)], queue: .main) { [weak self] in
self?.player?.seek(to: .zero, completionHandler: { [weak self] _ in
self?.player?.play()
})
}
答案 9 :(得分:0)
SWIFT 5:
private var player: AVPlayer?
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self,
selector: #selector(restartVideo),
name: .AVPlayerItemDidPlayToEndTime,
object: self.player?.currentItem)
}
@objc func restartVideo() {
player?.pause()
player?.currentItem?.seek(to: CMTime.zero, completionHandler: { _ in
self.player?.play()
})
}
答案 10 :(得分:0)
Swift 4.2 。
是,有一种相对简单的方法,可以使用AVKit
,键值观察(KVO)技术和在AVFoundation
/ AVQueuePlayer()
中循环播放视频一个令牌。
这绝对适用于一堆H.264 / HEVC视频,而CPU负担最小。
这是一个代码:
import UIKit
import AVFoundation
import AVKit
class ViewController: UIViewController {
private let player = AVQueuePlayer()
let clips = ["01", "02", "03", "04", "05", "06", "07"]
private var token: NSKeyValueObservation?
var avPlayerView = AVPlayerViewController()
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
self.addAllVideosToPlayer()
present(avPlayerView, animated: true, completion: { self.player.play() })
}
func addAllVideosToPlayer() {
avPlayerView.player = player
for clip in clips {
let urlPath = Bundle.main.path(forResource: clip, ofType: "m4v")!
let url = URL(fileURLWithPath: urlPath)
let playerItem = AVPlayerItem(url: url)
player.insert(playerItem, after: player.items().last)
token = player.observe(\.currentItem) { [weak self] player, _ in
if self!.player.items().count == 1 { self?.addAllVideosToPlayer() }
}
}
}
}
答案 11 :(得分:0)
以下在Swift 4.1的WKWebView中为我工作 WKwebviewConfiguration中WKWebView的主要部分
wkwebView.navigationDelegate = self
wkwebView.allowsBackForwardNavigationGestures = true
self.wkwebView = WKWebView(frame: CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: self.view.frame.size.height))
let config = WKWebViewConfiguration()
config.allowsInlineMediaPlayback = true
wkwebView = WKWebView(frame: wkwebView.frame, configuration: config)
self.view.addSubview(wkwebView)
self.wkwebView.load(NSURLRequest(url: URL(string: self.getUrl())!) as URLRequest)
答案 12 :(得分:0)
我所做的就是让它循环播放,就像下面的代码一样:
[player addPeriodicTimeObserverForInterval:CMTimeMake(1.0, 1.0)
queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
float current = CMTimeGetSeconds(time);
float total = CMTimeGetSeconds([playerItem duration]);
if (current >= total) {
[[self.player currentItem] seekToTime:kCMTimeZero];
[self.player play];
}
}];
答案 13 :(得分:0)
我在Objective-c和AVQueuePlayer中的解决方案 - 似乎你必须复制AVPlayerItem并在完成第一个元素的播放后立即添加另一个副本。 “有点”是有道理的,对我有用而没有任何打嗝
NSURL *videoLoopUrl;
// as [[NSBundle mainBundle] URLForResource:@"assets/yourVideo" withExtension:@"mp4"]];
AVQueuePlayer *_loopVideoPlayer;
+(void) nextVideoInstance:(NSNotification*)notif
{
AVPlayerItem *currItem = [AVPlayerItem playerItemWithURL: videoLoopUrl];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(nextVideoInstance:)
name:AVPlayerItemDidPlayToEndTimeNotification
object: currItem];
[_loopVideoPlayer insertItem:currItem afterItem:nil];
[_loopVideoPlayer advanceToNextItem];
}
+(void) initVideoPlayer {
videoCopy1 = [AVPlayerItem playerItemWithURL: videoLoopUrl];
videoCopy2 = [AVPlayerItem playerItemWithURL: videoLoopUrl];
NSArray <AVPlayerItem *> *dummyArray = [NSArray arrayWithObjects: videoCopy1, videoCopy2, nil];
_loopVideoPlayer = [AVQueuePlayer queuePlayerWithItems: dummyArray];
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector(nextVideoInstance:)
name: AVPlayerItemDidPlayToEndTimeNotification
object: videoCopy1];
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector(nextVideoInstance:)
name: AVPlayerItemDidPlayToEndTimeNotification
object: videoCopy2];
}
https://gist.github.com/neonm3/06c3b5c911fdd3ca7c7800dccf7202ad
答案 14 :(得分:0)
你可以添加一个AVPlayerItemDidPlayToEndTimeNotification观察者并重播视频 从选择器开始,代码如下
//add observer
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playbackFinished:) name:AVPlayerItemDidPlayToEndTimeNotification
object:_aniPlayer.currentItem];
-(void)playbackFinished:(NSNotification *)notification{
[_aniPlayer seekToTime:CMTimeMake(0, 1)];//replay from start
[_aniPlayer play];
}
答案 15 :(得分:0)
将视频加载到AVPlayer后(当然是通过AVPlayerItem):
[self addDidPlayToEndTimeNotificationForPlayerItem:item];
addDidPlayToEndTimeNotificationForPlayerItem方法:
- (void)addDidPlayToEndTimeNotificationForPlayerItem:(AVPlayerItem *)item
{
if (_notificationToken)
_notificationToken = nil;
/*
Setting actionAtItemEnd to None prevents the movie from getting paused at item end. A very simplistic, and not gapless, looped playback.
*/
_player.actionAtItemEnd = AVPlayerActionAtItemEndNone;
_notificationToken = [[NSNotificationCenter defaultCenter] addObserverForName:AVPlayerItemDidPlayToEndTimeNotification object:item queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
// Simple item playback rewind.
[[_player currentItem] seekToTime:kCMTimeZero];
}];
}
在viewWillDisappear方法中:
if (_notificationToken) {
[[NSNotificationCenter defaultCenter] removeObserver:_notificationToken name:AVPlayerItemDidPlayToEndTimeNotification object:_player.currentItem];
_notificationToken = nil;
}
在视图控制器的实现文件中的接口声明中:
id _notificationToken;
在尝试之前需要看到这个正常运行吗?下载并运行此示例应用程序:
在我的应用程序中,使用这个代码,在视频结束和开头之间没有任何暂停。事实上,根据视频的不同,我无法再将视频告诉视频,保存时间码显示。
答案 16 :(得分:-1)
使用AVPlayerViewController下面的代码,它为我工作
let type : String! = "mp4"
let targetURL : String? = NSBundle.mainBundle().pathForResource("Official Apple MacBook Air Video YouTube", ofType: "mp4")
let videoURL = NSURL(fileURLWithPath:targetURL!)
let player = AVPlayer(URL: videoURL)
let playerController = AVPlayerViewController()
playerController.player = player
self.addChildViewController(playerController)
self.playView.addSubview(playerController.view)
playerController.view.frame = playView.bounds
player.play()
要显示的所有控件,希望它有用
答案 17 :(得分:-2)
/* "numberOfLoops" is the number of times that the sound will return to the beginning upon reaching the end.
A value of zero means to play the sound just once.
A value of one will result in playing the sound twice, and so on..
Any negative number will loop indefinitely until stopped.
*/
@property NSInteger numberOfLoops;
此属性已在AVAudioPlayer
内定义。希望这可以帮到你。
我正在使用Xcode 6.3。