AVFundation AVPlayer를 사용한 비디오 루프
AVFundation에서 비디오를 비교적 쉽게 루프할 수 있는 방법이 있습니까?
다음과 같이 AVPlayer와 AVPLayer를 작성했습니다.
avPlayer = [[AVPlayer playerWithURL:videoUrl] retain];
avPlayerLayer = [[AVPlayerLayer playerLayerWithPlayer:avPlayer] retain];
avPlayerLayer.frame = contentView.layer.bounds;
[contentView.layer addSublayer: avPlayerLayer];
그리고 비디오를 재생합니다.
[avPlayer play];
비디오는 정상적으로 재생되지만, 마지막에 정지한다.에서는 MPMoviePlayerController를 .repeatMode
올바른 값으로 속성을 설정합니다.AVPlayer는 AVPlayer를 사용합니다.영화가 언제 끝났는지 알려주는 콜백도 없는 것 같아서 처음부터 다시 재생할 수 있어요.
MPMoviePlayerController는 심각한 제한이 있기 때문에 사용하지 않습니다.한 번에 여러 비디오 스트림을 재생할 수 있으면 좋겠습니다.
플레이어가 종료되면 알림을 받을 수 있습니다. 마크를 켜주세요.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)
}
}
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에서 "Advanceds in AVFundation Playback"에 소개되었습니다.https://developer.apple.com/videos/play/wwdc2016/503/
이 코드를 사용해도 문제가 있었습니다만, Apple에 버그 리포트를 제출하고, 다음의 회신을 받았습니다.
동영상 재생 시간이 오디오/비디오 트랙보다 길면 문제가 발생.FigPlayer_File은 오디오 트랙 편집 시간이 동영상 재생 시간(15.682 vs 15.787)보다 짧기 때문에 공백 없는 전환을 비활성화합니다.
동영상 지속 시간과 트랙 지속 시간이 같도록 동영상 파일을 수정하거나 AVPayerLooper의 시간 범위 매개 변수(오디오 트랙 시간 범위 0 ~ 지속 시간 설정)를 사용할 수 있습니다.
Premiere는 비디오와 약간 다른 길이의 오디오 트랙을 가진 파일을 내보내고 있었던 것으로 밝혀졌다.제 경우 오디오를 완전히 삭제해도 문제가 해결되지 않았습니다.
Swift의 경우:
플레이어가 종료되면 알림을 받을 수 있습니다.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)
이게 영화를 되감아줄 거예요.
플레이어를 해제할 때 알림 등록을 취소하는 것을 잊지 마십시오.
일시정지(pause-hiccup) 문제를 방지하기 위해 다음과 같이 했습니다.
신속:
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
요없없필
Swift 5:
플레이어를 추가하는 등 이전 답변에서 약간 수정했습니다.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의 자산으로 만듭니다.그렇지 않으면 비디오가 한 번만 재생될 수 있습니다.
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()
})
}
AVQue Player를 사용하여 동영상을 심리스하게 루프할 것을 권장합니다.통지 옵서버 추가
AVPlayerItemDidPlayToEndTimeNotification
그 셀렉터에서 비디오를 루프합니다.
AVPlayerItem *video = [[AVPlayerItem alloc] initWithURL:videoURL];
[self.player insertItem:video afterItem:nil];
[self.player play];
비디오를 되감을 때의 갭을 피하기 위해서, 같은 자산의 카피를 1개의 컴포지션으로 복수 사용하는 것이 나에게 있어서 효과적이었습니다.여기서 찾았습니다.www.developers-life.com/avplayer-looping-video-without-hiccupdelays.html (link now dead).
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];
딸꾹질 문제 없이 작동하며, 포인트는 seekToTime 메서드를 호출하기 전에 플레이어를 일시 중지하는 것입니다.
초기화 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() }
목적 c의 솔루션 - wth AVQuePlayer - AVPlayerItem을 복제하고 첫 번째 요소의 재생이 끝나면 즉시 다른 복사본을 추가해야 할 것 같습니다."Cody of"는 말이 되므로 문제없이 사용할 수 있습니다.
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
Swift 3 & 4의 경우
NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: self.avPlayer?.currentItem, queue: .main) { _ in
self.avPlayer?.seek(to: kCMTimeZero)
self.avPlayer?.play()
}
나는 답 중에서 해결책을 찾을 수 없었다.자산 지속 기간에 지정된 경계 시간을 관찰하면 유용합니다.옵서버가 트리거되면 시작을 찾아 재생합니다.
player?.addBoundaryTimeObserver(forTimes: [NSValue(time: asset.duration)], queue: .main) { [weak self] in
self?.player?.seek(to: .zero, completionHandler: { [weak self] _ in
self?.player?.play()
})
}
비디오를 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];
}];
}
viewWillDisplay 메서드:
if (_notificationToken) {
[[NSNotificationCenter defaultCenter] removeObserver:_notificationToken name:AVPlayerItemDidPlayToEndTimeNotification object:_player.currentItem];
_notificationToken = nil;
}
구현 파일 내의 뷰 컨트롤러 인터페이스 선언에서 다음을 수행합니다.
id _notificationToken;
시도하기 전에 이 가동 상황을 확인해야 합니까?다음 샘플 앱을 다운로드하여 실행합니다.
이 코드를 사용하고 있는 제 앱에서는, 동영상의 끝과 시작 사이에 전혀 정지가 없습니다.사실 동영상에 따라서는 동영상이 다시 시작임을 알 수 없습니다. 타임코드 표시를 저장하십시오.
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];
}
다음은 WK Web View의 Swift 4.1 WK Web View의 주요 부분 구성입니다.
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)
아래 코드와 같이 루프 재생을 했습니다.
[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];
}
}];
Xcode 10.1의 Swift 4.2.
네, 비교적 쉽게 비디오를 루핑할 수 있는 방법이 있습니다.AVKit
/AVFoundation
사용.AVQueuePlayer()
Key-Value Observation(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() }
}
}
}
}
스위프트 5
import UIKit
import AVKit
import AVFoundation
class VideoViewControler: UIViewController {
// init video background and its path
var player: AVPlayer?
let videoURL: NSURL = Bundle.main.url(forResource: "farmer_watering", withExtension: "mp4")! as NSURL
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
// begin implementing the avplayer
player = AVPlayer(url: videoURL as URL)
player?.actionAtItemEnd = .none
player?.isMuted = true
let playerLayer = AVPlayerLayer(player: player)
playerLayer.videoGravity = AVLayerVideoGravity.resizeAspect
playerLayer.zPosition = -1
playerLayer.frame = view.frame
view.layer.addSublayer(playerLayer)
player?.play()
// add observer to watch for video end in order to loop video
NotificationCenter.default.addObserver(
self,
selector: #selector(loopVideo),
name: .AVPlayerItemDidPlayToEndTime,
object: self.player?.currentItem
)
}
// if video ends, will restart
func playerItemDidReachEnd() {
player?.seek(to: CMTime.zero)
}
// add this loop at the end, after viewDidLoad
@objc func loopVideo() {
playerItemDidReachEnd()
player?.play()
}
}
SwiftUI의 예:
// VideoLooper.swift
import SwiftUI
import AVKit
struct VideoLooper: UIViewRepresentable {
private let player: AVQueuePlayer
private let videoURL: URL
init(resourceInBundle name: String, ofType type: String = "mp4") {
self.init(url: URL(fileURLWithPath: Bundle.main.path(forResource: name, ofType: type)!))
}
init(url: URL) {
self.videoURL = url
self.player = AVQueuePlayer()
}
func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<VideoLooper>) {
}
func makeUIView(context: Context) -> UIView {
return PlayerUIView(player: player, videoURL: videoURL)
}
}
class PlayerUIView: UIView {
private let playerLayer: AVPlayerLayer
private let playerItem: AVPlayerItem
private let playerLooper: AVPlayerLooper
init(player: AVQueuePlayer, videoURL: URL) {
playerLayer = AVPlayerLayer(player: player)
playerItem = AVPlayerItem(url: videoURL)
playerLooper = AVPlayerLooper(player: player, templateItem: playerItem)
super.init(frame: .zero)
playerLayer.player = player
layer.addSublayer(playerLayer)
player.play()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
playerLayer.frame = bounds
}
}
사용방법:
// Video-Resource in Bundle
VideoLooper(resourceInBundle: "moviename", ofType: "mp4")
// Video-Resource from URL
VideoLooper(url: URL(string: "https://my-video-server.local/christmas.mp4")!)
코드 아래의 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()
모든 컨트롤이 표시됩니다. 도움이 되길 바랍니다.
/* "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을 사용하고 있습니다.
언급URL : https://stackoverflow.com/questions/5361145/looping-a-video-with-avfoundation-avplayer
'programing' 카테고리의 다른 글
응용 프로그램에서 stdout을 파이프가 아닌 터미널로 생각하도록 속이는 무엇입니까? (0) | 2023.04.15 |
---|---|
TabControl의 탭 항목 내 제어 상태를 유지하는 방법 (0) | 2023.04.15 |
WPF 사용자 제어에서 Import된 리소스와 로컬 리소스를 결합하는 방법 (0) | 2023.04.15 |
WPF 페이지 또는 UserControl 개체에서 KeyDown 이벤트를 캡처하려면 어떻게 해야 합니까? (0) | 2023.04.15 |
디렉토리(중간 디렉토리 포함)를 안전하게 작성하려면 어떻게 해야 합니까? (0) | 2023.04.15 |