AVFoundation AVPlayer로 비디오를 루핑 하시겠습니까?


142

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에는 몇 가지 심각한 제한이 있기 때문에 사용하지 않습니다. 여러 비디오 스트림을 한 번에 재생할 수 있기를 원합니다.


1
실제 작업 코드에 대한 링크는 다음 답변을 참조하십시오. stackoverflow.com/questions/7822808/…
MoDJ

답변:


275

플레이어가 종료되면 알림을받을 수 있습니다. 검사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)
    }
}

스위프트 4+

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

6
... [p seekToTime : kCMTimeZero] ( "되감기"종류) 바로 다음에 재생하려면 [p play]를 다시 수행하십시오.
thomax

24
이것은 필요하지 않습니다 ... 당신이 avPlayer.actionAtItemEnd = AVPlayerActionAtItemEndNone;그것을 중지하지 않으면 다시 재생하도록 설정할 필요가 없습니다
Bastian

사운드를 다시 재생하려면 [player play];되감기 후에 전화해야합니다 .
Kenny Winker

12
이 솔루션은 작동하지만 완벽하지는 않습니다. 일시 정지가 매우 작습니다. 내가 뭔가 잘못하고 있습니까?
Joris van Liempd iDeveloper

3
@Praxiteles 당신은보기가 파괴되지 않을 때, 또는 비디오 플레이어를 제거하거나 당신이 무엇을 할 때 그것을 등록을 해제해야합니다) [[NSNotificationCenter defaultCenter] removeObserver:self];예를 들어, self알림을 듣고 있을 때 사용할 수 있습니다 .
Bastian

63

도움이된다면 iOS / tvOS 10에는 비디오의 매끄러운 반복 (Swift)을 만드는 데 사용할 수있는 새로운 AVPlayerLooper ()가 있습니다.

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

이것은 WWDC 2016에서 "AVFoundation 재생의 발전"에서 발표되었습니다 : https://developer.apple.com/videos/play/wwdc2016/503/

이 코드를 사용해도 Apple에 버그 보고서를 제출하고 다음과 같은 응답을 얻을 때까지 딸꾹질이있었습니다.

오디오 / 비디오 트랙보다 영화 지속 시간이 긴 영화 파일이 문제입니다. 오디오 플레이어 편집이 영화 재생 시간 (15.682 vs 15.787)보다 짧으므로 FigPlayer_File에서 끊김없는 전환을 비활성화하고 있습니다.

동영상 길이와 트랙 지속 시간이 동일한 길이를 갖도록 동영상 파일을 수정하거나 AVPlayerLooper의 시간 범위 매개 변수를 사용할 수 있습니다 (0에서 오디오 트랙의 지속 시간 설정)

Premiere에서 비디오와 길이가 약간 다른 오디오 트랙이있는 파일을 내보내고있는 것으로 나타났습니다. 필자의 경우 오디오를 완전히 제거하면 문제가 해결되었습니다.


2
아무것도 나를 위해 일하지 않았다. AVPlayerLooper를 사용하고 있으며이 버그가 발생하여 비디오 / 오디오 길이 간의 불일치를 수정하면 문제가 해결되었습니다.
Kevin Heap

1
Premiere에 대한 정보를 주셔서 감사합니다. 루퍼에 timeRange를 추가하고 "플래싱 비디오"문제를 해결했습니다.
Alexander Flenniken

@Nabha 비디오 내에서 특정 시간 동안 이것을 사용할 수 있습니까? 예를 들어 비디오는 60 초이지만 처음 10 초만 반복하고 싶습니다.
랜스 사마리아

29

에서 스위프트 :

플레이어가 종료되면 알림을받을 수 있습니다 ... AVPlayerItemDidPlayToEndTimeNotification 확인

플레이어를 설정할 때 :

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

스위프트 3

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

영화를 되감습니다.

플레이어를 놓을 때 알림을 등록 취소하는 것을 잊지 마십시오.


4
이 방법으로 루프 사이에 작은 딸꾹질이 보입니다. Adobe Premier에서 비디오를 열고 비디오에 중복 프레임이 없는지 확인 했으므로 짧은 딸꾹질이 확실히 재생되고 있습니다. 누구든지 신속하게 비디오 루프를 만드는 방법을 찾았습니까?
SpaceManGalaxy

@SpaceManGalaxy 나는 또한 딸꾹질을 보았습니다. 이 결함을 해결하는 방법을 찾았습니까?
랜스 사마리아

18

다음은 일시 중지 딸꾹질 문제를 방지하기 위해 수행 한 작업입니다.

빠른:

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 필요 .


2
@KostiaDombrovsky 실제 장치 나 다른 비디오를 사용해 보셨습니까?
이슬람 Q.

@IslamQ. MP4 파일을 녹음 한 다음 snapchat처럼 루프에서 재생하려고합니다.
코스 티아 Dombrovsky

@KostiaDombrovsky 재생을 스냅 챗과 나란히 비교 했습니까? 시작과 끝 프레임이 일치하지 않기 때문에 일시 중지 된 것처럼 보이지만 결코 멈추지 않습니다.
이슬람 Q.

나에게도 효과가 없었습니다. 나는 끊임없는 오디오를 가진 6 초
짜리

이 방법을 사용할 때 메모리 누수가 발생합니다. 이 [weakSelf.avPlayer seekToTime:kCMTimeZero]; [weakSelf.avPlayer play];줄 과 관련이 있습니다.이 줄을 주석 처리하면 더 이상 메모리 누수가 없습니다. 악기에서 이것을 프로파일했습니다.
Solsma Dev 16:16

3

AVQueuePlayer를 사용하여 비디오를 원활하게 반복하는 것이 좋습니다. 알림 관찰자 추가

AVPlayerItemDidPlayToEndTimeNotification

선택기에서 비디오를 반복

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

나는 이것을 시도했지만 @Bastian이 제안한 방법에 비해 개선되지 않았다. 딸꾹질을 완전히 제거 했습니까?
amadour

2
@amadour 할 수있는 작업은 초기화 할 때와 AVPlayerItemDidPlayToEndTimeNotification을 게시 할 때 동일한 비디오를 플레이어의 대기열에 추가하는 것입니다.
kevinnguy

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];


2

이것은 딸꾹질 문제없이 저에게 효과적이었습니다. 점은 seekToTime 메소드를 호출하기 전에 플레이어 를 일시 중지 시키는 것입니다.

  1. 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()
  2. 알림 등록

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

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

3
감사합니다 — 시도했지만 여전히 일시 중지되었습니다.
Nabha

2

스위프트 3 & 4

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

1

objective-c wth 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


1

스위프트 5 :

playerLayer에 추가하기 전에 playerItem을 대기열에 추가하는 것과 같은 이전 답변에서 약간 조정했습니다.

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의 속성으로 지정하십시오. 그렇지 않으면 비디오가 한 번만 재생 될 수 있습니다.


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;
    }

구현 파일 내에서 View Controller의 인터페이스 선언에서 :

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

이 코드를 사용하는 내 앱에서는 비디오의 끝과 시작 사이에 일시 중지가 없습니다. 실제로 비디오에 따라 비디오가 시작 부분에 다시 있다는 것을 알 수있는 방법이 없습니다. 타임 코드 표시를 저장하십시오.


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];
}

0

다음은 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)

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];
    }
}];

0

Xcode 10.1의 Swift 4.2

에 비디오를 반복 상대적으로 쉬운 방법이있다 AVKit/ AVFoundation사용AVQueuePlayer() , 키 - 값 관찰 (KVO) 기술과 그것을위한 토큰을.

이것은 CPU에 대한 부담을 최소화하면서 많은 H.264 / HEVC 비디오에서 작동합니다.

코드는 다음과 같습니다.

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

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

모든 컨트롤을 보여 주길 바랍니다.


-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을 사용하고 있습니다.


8
그것은 AVPlayer를위한 것이 아니라 오디오를위한 것입니다
Yariv Nissim
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.