用AVFoundation AVPlayer循环播放视频?

yqkkidmi  于 2022-09-19  发布在  Swift
关注(0)|答案(20)|浏览(262)

在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,因为它有一些严重的限制。我希望能够一次回放多个视频流。

sshcrbum

sshcrbum1#

当玩家结束时,你可以得到一个通知。检查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];
}

这将使电影倒带。

别忘了在释放玩家的时候取消注册通知。

SWIFT

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)
    }
}
rwqw0loc

rwqw0loc2#

在iOS/TVOS 10中,有一个新的AVPlayerLooper(),可以用来创建视频的无缝循环(Swift):

player = AVQueuePlayer()
playerLayer = AVPlayerLayer(player: player)
playerItem = AVPlayerItem(url: videoURL)
playerLooper = AVPlayerLooper(player: player, templateItem: playerItem)
player.play()

这是在2016年WWDC上发表的《AVFoundation回放的进展》:https://developer.apple.com/videos/play/wwdc2016/503/

即使使用这段代码,我也遇到了一个问题,直到我向苹果提交了一份错误报告,并得到了以下回复:
电影持续时间长于音频/视频轨道的电影文件是问题所在。图播放器_文件正在禁用无缝过渡,因为音频曲目编辑比电影持续时间短(15.682比15.787)。

你需要修复电影文件,使电影时长和音轨时长相同,或者你可以使用AVPlayerLooper的时间范围参数(设置时间范围从0到音轨的时长)

事实证明,Premiere一直在导出音轨长度与视频略有不同的文件。在我的例子中,完全删除音频是可以的,这解决了问题。

wpx232ag

wpx232ag3#

SWIFT中:

当玩家结束时,您可以收到通知...检查AVPlayerItemDidPlayToEndTimeNotify

设置播放器时:

avPlayer.actionAtItemEnd = AVPlayerActionAtItemEnd.None

NSNotificationCenter.defaultCenter().addObserver(self, 
                                                 selector: "playerItemDidReachEnd:", 
                                                 name: AVPlayerItemDidPlayToEndTimeNotification, 
                                                 object: avPlayer.currentItem)

这将防止玩家在结束时停顿。

在通知中:

func playerItemDidReachEnd(notification: NSNotification) {
    if let playerItem: AVPlayerItem = notification.object as? AVPlayerItem {
        playerItem.seekToTime(kCMTimeZero)
    }
}

Swift3

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

这将使电影倒带。

在释放播放器时,不要忘记注销通知。

irtuqstp

irtuqstp4#

以下是我最终为防止停顿-打嗝问题所做的事情:

SWIFT:

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,因为它不需要。

jecbmhm3

jecbmhm35#

斯威夫特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设置为您的UIView控制器的属性,否则视频可能只播放一次。

mwecs4sa

mwecs4sa6#

我建议使用AVQueuePlayer来无缝循环您的视频。添加通知观察者

AVPlayerItemDidPlayToEndTimeNotification

在其选择器中,循环播放您的视频

AVPlayerItem *video = [[AVPlayerItem alloc] initWithURL:videoURL];
[self.player insertItem:video afterItem:nil];
[self.player play];
r8xiu3jd

r8xiu3jd7#

为了避免视频倒带时的间隙,在一个构图中使用同一资源的多个副本对我来说效果很好。我在这里找到了它: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];
0pizxfdo

0pizxfdo8#

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()
    })
}
ej83mcc0

ej83mcc09#

这对我来说很有效,没有打嗝的问题,要点是在调用earkToTime方法之前暂停播放器

1.初始化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()

1.登记通知

NSNotificationCenter.defaultCenter().addObserver(self, selector: "videoLoop", name: AVPlayerItemDidPlayToEndTimeNotification, object: self.backgroundPlayer!.currentItem)

1.Video oLoop函数

func videoLoop() {
  self.backgroundPlayer?.pause()
  self.backgroundPlayer?.currentItem?.seekToTime(kCMTimeZero)
  self.backgroundPlayer?.play()
}
zqdjd7g9

zqdjd7g910#

我在Objective中的解决方案--使用AVQueuePlayerc--似乎必须复制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

rfbsl7qr

rfbsl7qr11#

对于SWIFT 3和4

NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: self.avPlayer?.currentItem, queue: .main) { _ in
     self.avPlayer?.seek(to: kCMTimeZero)
     self.avPlayer?.play()
}
9gm1akwq

9gm1akwq12#

我在答案中找不到我的解决方案。观察指定给资产期限的边界时间可能会很有帮助。当观察者被触发时,寻求开始并重播。

player?.addBoundaryTimeObserver(forTimes: [NSValue(time: asset.duration)], queue: .main) { [weak self] in
    self?.player?.seek(to: .zero, completionHandler: { [weak self] _ in
        self?.player?.play()
    })
}
1cklez4t

1cklez4t13#

将视频加载到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];
    }];
}

在您的view WillDis现方法中:

if (_notificationToken) {
        [[NSNotificationCenter defaultCenter] removeObserver:_notificationToken name:AVPlayerItemDidPlayToEndTimeNotification object:_player.currentItem];
        _notificationToken = nil;
    }

在实现文件内的视图控制器的接口声明中:

id _notificationToken;

在你尝试之前,需要先看看它的启动和运行情况吗?下载并运行此示例应用程序:

Https://developer.apple.com/library/prerelease/ios/samplecode/AVBasicVideoOutput/Listings/AVBasicVideoOutput_APLViewController_m.html#//apple_ref/doc/uid/DTS40013109-AVBasicVideoOutput_APLViewController_m-DontLinkElementID_8

在我的应用程序中,它使用的正是这段代码,在视频的结尾和开头之间没有任何停顿。事实上,根据视频的不同,除了时间码显示之外,我无法判断视频是从头开始的。

nkcskrwz

nkcskrwz14#

您可以添加一个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];
}
8fq7wneg

8fq7wneg15#

以下是SWIFT 4.1中WKWebView的主要部分WKWebView配置中的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)

相关问题