iOS AVPlayer 예제
순서
0.스토리보드에서 뷰 그리기
1. 라이브러리 import
2. 변수 선언
#import "ViewController.h"
#import <AVFoundation/AVFoundation.h>
@interface ViewController ()
//비디오 뷰
@property (weak, nonatomic) IBOutlet UIView *videoView;
//AVPlayer 플레이어
@property (strong, nonatomic) AVPlayer *player;
//AVPlayerLayer
@property (strong, nonatomic) AVPlayerLayer *playerLayer;
//재생 여부 체크
@property (nonatomic, assign) BOOL isVideoPlaying;
//현재 시간 라벨
@property (weak, nonatomic) IBOutlet UILabel *currentTimeLabel;
//총길이 라벨
@property (weak, nonatomic) IBOutlet UILabel *durationLabel;
//타임 슬라이더
@property (weak, nonatomic) IBOutlet UISlider *timeSlider;
@end
3.AVPlayer 초기화 및 설정
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"viewDidLoad 진입");
//단계1 - 파일 주소 생성
NSURL *url = [[NSURL alloc]initWithString:@"https:/mp4?alt=media&token=dcef2268-bfb5-491e-9498-0a568833846a"];
//단계2 - 파일 주소로 AVPlayer 초기화
_player = [[AVPlayer alloc]initWithURL:url];
//옵저버 등록 - duration key 값 변경 감지
//observeValueForKeyPath를 사용하여 AVPlayer의 상태를 확인하는 방법!
//이론적으로는 KVO 등록 이전에도 플레이어의 상태가 변경 될 수 있으며 이는 더 이상 KVO 콜백이 생성되지 않음을 의미.
//KVO 등록을 수행 할 때 다음 옵션을 추가하는 것이 좋습니다 NSKeyValueObservingOptionInitial. 이렇게하면 초기 값에 대한 콜백도받을 수 있다.
[_player.currentItem addObserver:self forKeyPath:@"duration" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionInitial context:nil];
//단계3 - 볼륨 설정
[_player setVolume:0.0];
//단계 4 - AVPlayerLayer 생성 및 비디오 뷰에 넣어주기
_playerLayer = [AVPlayerLayer playerLayerWithPlayer:_player];
[_playerLayer setVideoGravity:kCAGravityResize];
[_videoView.layer addSublayer: _playerLayer];
_isVideoPlaying = NO;
}
4.뷰가 띄워질때 AVPlayerLayer 프레임 설정
- (void)viewWillAppear:(BOOL)animated{
NSLog(@"viewWillAppear 진입");
[self addTimeObserver];
}
//단계5 - 뷰가 띄워질때 AVPlayerLayer 프레임 설정
- (void)viewDidLayoutSubviews{
[super viewDidLayoutSubviews];
_playerLayer.frame = _videoView.bounds;
}
6.플레이 버튼 클릭 ,정지버튼 클릭
//단계6 - 버튼 셋팅
//플레이 버튼 클릭 / 정지버튼 클릭
- (IBAction)playPressed:(UIButton *)sender {
//재생중에 클릭했으면
if (_isVideoPlaying) {
//멈춤
[_player pause];
//타이틀 변경
[sender setTitle:@"Play" forState:UIControlStateNormal];
//값 변경
_isVideoPlaying = NO;
}else{
//멈춰있을 때 클릭 시 - 재생
[_player play];
//타이틀 변경
[sender setTitle:@"Pause" forState:UIControlStateNormal];
_isVideoPlaying = YES;
}
}
7.앞으로 버튼 클릭 함수
//단계 7 - 이후 버튼 클릭 설정
//이후 5초
- (IBAction)forwardPressed:(UIButton *)sender {
//전체 길이 cmtime
CMTime duration = _player.currentItem.duration;
//현재 시간초
Float64 currentTime = CMTimeGetSeconds([_player currentTime]);
//새로운 시간 구하기 : 현재시간 + 5초
Float64 newTime = currentTime + 5.0;
//전체시간 - 5초 보다 새로운 시간이 더 작다면
//예컨데, 전체시간(60초) - 5초 = 55초 에서 새로운 시간이 56초면 5초 뒤로 넘어갈 수 없다.
if (newTime < (CMTimeGetSeconds(duration) - 5.0)) {
CMTime time = CMTimeMake((newTime*1000), 1000);
//새로운 시간 위치로 이동
[_player seekToTime:time];
}
}
8.뒤로가기 버튼 클릭 함수
//단계8 - 이전 버튼 클릭 설정
//이전 5초
- (IBAction)backwordPressed:(UIButton *)sender {
//현재 시간 초
Float64 currentTime = CMTimeGetSeconds([_player currentTime]);
//새로운 시간 초 : 현재시간 초 - 5초
Float64 newTime = currentTime - 5.0;
//새로운 시간이 0보다 작으면 0
if (newTime < 0) {
newTime = 0;
}
CMTime time = CMTimeMake((newTime*1000), 1000);
[_player seekToTime:time];
}
9.슬라이더 변경 감지 함수
//단계9 - 슬라이더 변경 설정
//슬라이더 변경
- (IBAction)sliderValueChaged:(UISlider *)sender {
//sender.value에는 현재 슬라이더의 값이 들어가있다.
CMTime time = CMTimeMake((UInt64)sender.value * 1000, 1000);
[_player seekToTime:time];
}
10.옵저버 오버라이드 메소드 작성 - KVO
//단계10 - 옵저버 오버라이드 메소드 작성 - KVO
//currentItem의 전체 재생 시간 구하기
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
NSLog(@"observeValueForKeyPath 진입");
//등록한 옵저버의 key 가 'duration' 이면
if ([keyPath isEqualToString:@"duration"]) {
//현재 재생될 자산이 준비가 되었다면
NSLog(@"observeValueForKeyPath ready!");
//현재 재생자산의 전체길이 cmTime
CMTime durationCmTime = _player.currentItem.duration;
/*
CMTime이 유효하지 않거나 무기한이면 NaN이 반환됩니다.
CMTime이 무한대이면 +/- 무한대가 반환됩니다.
CMTime이 숫자이면 epoch는 무시되고 time.value / time.timescale이 반환됩니다.
나누기는 Float64에서 수행되므로 반환 된 결과에서 분수가 손실되지 않습니다.
*/
//현재 재생자산의 전체길이 초
Float64 duration = CMTimeGetSeconds(durationCmTime);
//전체 길이가 0 보다 크면 전체 재생 시간 text로 설정
if (duration > 0.0) {
//float 형을 시: 분 : 초 문자열로 변경
self.durationLabel.text = [self getTimeString:duration];
}
}
}
11.float 형을 시: 분 : 초 문자열로 변경 하는 함수 작성
//단계11 - float 형을 시: 분 : 초 문자열로 변경 하는 함수 작성
-(NSString *) getTimeString:(Float64) timeTotalSecond{
NSLog(@"getTimeString 진입");
NSLog(@"변경할 초 timeTotalSecond time : %f" , timeTotalSecond);
//시, 분, 초 계산
int hours = (unsigned int)(timeTotalSecond / 3600);
int minutes = (unsigned int)(timeTotalSecond / 60) % 60;
int secondes = (unsigned int)timeTotalSecond % 60;
//시간이 존재한다면
if (hours > 0) {
NSString *result = [[NSString alloc]initWithFormat:@"%i:%02i:%02i" , hours, minutes, secondes];
return result;
}else{
NSString *result = [[NSString alloc]initWithFormat:@"%02i:%02i" , minutes, secondes];
return result;
}
}
12.시간변경 감지 옵저버 작성
//단계12 시간변경 감지 옵저버 작성
-(void) addTimeObserver{
NSLog(@"addTimeObserver 진입");
CMTime interval = CMTimeMakeWithSeconds(0.5, NSEC_PER_SEC);
dispatch_queue_t mainQueue = dispatch_get_main_queue();
//여기서self를 카피
//ARC로 된 프로젝트에서 블럭코딩을 할 경우 블럭 밖에 선언된 변수가 블럭안에서 값이 변할때 위와같은 에러가 발생한다고 한다.
//__block ViewController *blockself = self;
__weak typeof(self) weakSelf = self;
[_player addPeriodicTimeObserverForInterval:interval queue:mainQueue usingBlock:^(CMTime time) {
AVPlayerItem *curentItem = weakSelf.player.currentItem;
NSLog(@"AVPlayerItem 상태값 : %ld" , (long)[curentItem status]);
if([curentItem status] == 1){
NSLog(@"AVPlay ready!!");
Float64 checkTIme = CMTimeGetSeconds(curentItem.duration);
NSLog(@"checkTIme : %f" , checkTIme);
NSLog(@"maximumValue : %f" , CMTimeGetSeconds(curentItem.duration));
if (checkTIme) {
NSLog(@"타임슬라이스 값 셋팅 완료 !");
weakSelf.timeSlider.maximumValue = CMTimeGetSeconds(curentItem.duration); //전체 길이
weakSelf.timeSlider.minimumValue = 0;
weakSelf.timeSlider.value = CMTimeGetSeconds(curentItem.currentTime);
weakSelf.currentTimeLabel.text = [weakSelf getTimeString:CMTimeGetSeconds(curentItem.currentTime)];
}
}else{
NSLog(@"AVPlay not ready!");
}
}];
}
@end
참고
KVO 참고 : https://mrgamza.tistory.com/448
Avfoundation / avkit 참고
https://baked-corn.tistory.com/118
https://wnstkdyu.github.io/2018/05/03/avfoundationprogrammingguide
https://hyerios.tistory.com/179
https://hyunsikwon.github.io/ios/iOS-AVFoundation
'아이폰 개발 > ios 개념&튜토리얼' 카테고리의 다른 글
iOS 공통 로그 예제 (0) | 2021.01.28 |
---|---|
디자인 패턴 - 싱글톤 패턴 예제 (0) | 2021.01.28 |
iOS AVAudioPlayer 예제 (0) | 2021.01.26 |
iOS AudioFileStream 예제 (0) | 2021.01.22 |
iOS 코어오디오 재생 예제자료 (0) | 2021.01.21 |