본문 바로가기
아이폰 개발/ios 개념&튜토리얼

iOS AVPlayer 영상 재생 예제

by 인생여희 2021. 2. 18.

iOS AVPlayer 영상 재생 예제

 

 

 

 

CustomPlayerController.h

 

스토리보드에서 만든 버튼 및 뷰, 슬라이더를 컨트롤러와 이어주고 필요한 메소드 선언.

 

#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
NS_ASSUME_NONNULL_BEGIN

@interface CustomPlayerController : UIViewController

@property (strong, nonatomic) IBOutlet UIButton *doneButton;        //닫기
@property (strong, nonatomic) IBOutlet UIView *controlView;         //컨트롤러 뷰
@property (strong, nonatomic) IBOutlet UIButton *playBackButton;     //재생 버튼

@property (strong, nonatomic) IBOutlet UIButton *seekBackButton;    //뒤가기 버튼
@property (strong, nonatomic) IBOutlet UIButton *seekFwdButton;     //앞으로 가기 버튼


@property (strong, nonatomic) IBOutlet UILabel *progressLabel;      //재생중시간
@property (strong, nonatomic) IBOutlet UILabel *totalTimeLabel;     //전체 러닝 타임
@property (strong, nonatomic) IBOutlet UISlider *timeSlider;       //타임 슬라이더

@property (nonatomic, assign) BOOL isPlaying;                   //재생 여부

@property (nonatomic, strong) NSTimer *controlTimer;             //타이머[1]
@property (nonatomic, strong) NSTimer *updateTimer;              //타이머[2]


@property (nonatomic, strong) AVPlayer *player;                //플레이어
@property (nonatomic, strong) AVPlayerItem *playerItem;         //플레이할 아이템

@property (nonatomic, assign) BOOL controlsVisible;

//액션 메소드

- (IBAction)seekForward:(id)sender;
- (IBAction)seekBack:(id)sender;
- (IBAction)toglePlayback:(id)sender;
- (IBAction)sliderChange:(UISlider *)sender;
- (IBAction)done:(id)sender;

-(void) play;

-(id)initWithContentURL:(NSURL *) contentURL;


@end

NS_ASSUME_NONNULL_END

 

 

CustomPlayerController.m

 

재생, 앞으로 감기, 뒤로감기, 정지, 슬라이더 이동관련 함수 작성

 

#import "CustomPlayerController.h"

@interface CustomPlayerController ()

@end

@implementation CustomPlayerController

//1.초기자 메소드
-(id)initWithContentURL:(NSURL *)contentURL
{
    
    
    //스토리 보드 -> CustomPlayerController 접근 (identifier 명 지정 필수!)
    UIStoryboard *storyBoard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
    
    CustomPlayerController *myViewController = [storyBoard instantiateViewControllerWithIdentifier:@"CustomPlayerController"];
    
    self = myViewController;
    
    if (self) {
        
        //플레이어 초기화
        self.playerItem = [[AVPlayerItem alloc] initWithURL:contentURL];
        self.player = [AVPlayer playerWithPlayerItem:self.playerItem];
        
        //KVO - 플레이어 상태 계속 체크
        [self.player addObserver:self forKeyPath:@"status" options:0 context:nil];
        
        self.isPlaying = NO;
        
    }
    
    return self;
}


- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
}


- (IBAction)toglePlayback:(id)sender {

    //플레이어 정지 시키기
    if (self.isPlaying) {
        
        
        [self.player pause];
        
        [self.playBackButton setTitle:@"재생" forState:UIControlStateNormal];
        
        //[self showControlles:nil];
        
        [self.updateTimer invalidate];
        
        
    //플레이어 재생
    }else{
        
        [self.player play];
        
        [self.playBackButton setTitle:@"정지" forState:UIControlStateNormal];
        
        self.updateTimer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:@selector(updateProcess) userInfo:nil repeats:YES];
        
    }
    
    self.isPlaying = !self.isPlaying;

}


//프로그래스바 정보 업데이트
-(void)updateProcess{
    
    //1.전체 러닝 타임 [분 : 초]
    int durationMinutes = CMTimeGetSeconds([self.playerItem duration]) / 60;
    int durationSeconds = CMTimeGetSeconds([self.playerItem duration]) - durationMinutes * 60;
    
    
    //2.현재 시간 [분 : 초]
    int currentTimeMinutes = CMTimeGetSeconds([self.playerItem currentTime]) / 60;
    int currentTImeSeconds = CMTimeGetSeconds([self.playerItem currentTime]) - currentTimeMinutes * 60;
    
    
    //3.타임 슬라이스 이동
    self.timeSlider.value = CMTimeGetSeconds([self.playerItem currentTime]) / CMTimeGetSeconds([self.playerItem duration]);
    
    //+ 0.0f;
    
    
    //4.현재 러닝 타임
    self.progressLabel.text = [NSString stringWithFormat:@"%02i:%02i",
                               currentTimeMinutes , currentTImeSeconds];
    
    //5.전체 러닝 타임
    self.totalTimeLabel.text = [NSString stringWithFormat:@"%02i:%02i",
                                durationMinutes , durationSeconds];
    
    
    
}

//컨트롤러 숨기게
-(void)hideControls:(id)sender{
    
    
    NSLog(@"hideControls - ");
    NSLog(@"self.controlsVisible : %d" , self.controlsVisible);
    
    
    if (self.controlsVisible) {
        
        [self.view sendSubviewToBack:self.controlView];
        self.controlView.userInteractionEnabled = NO;
        self.controlsVisible = NO;
        
    }else{
        
        [self showControlles:nil];
        
    }
    
}


//컨트롤러 보이게
-(void)showControlles:(id) sender{
    
    NSLog(@"showControlles - ");
    NSLog(@"self.controlsVisible : %d" , self.controlsVisible);
    NSLog(@"self.controlTimer: %@" , self.controlTimer);
    
    
    if(self.controlTimer != nil){
        [self.controlTimer invalidate];
    }
    
    //컨트롤러 뷰 앞으로 가져오기
    [self.view bringSubviewToFront:self.controlView];
    self.controlView.userInteractionEnabled = YES;
    
    //컨트롤러 2초뒤 종료
    self.controlTimer = [NSTimer scheduledTimerWithTimeInterval:2.0f target:self selector:@selector(hideControls:) userInfo:nil repeats:NO];
    
    //보여있는 상태
    self.controlsVisible = YES;
    
    
    
}


//KVO
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    //관찰하려는 key 가 player 의 status 일때
    if (object == self.player && [keyPath isEqualToString:@"status"]) {
        
        //플레이어가 준비된 상태이면
        if (self.player.status == AVPlayerStatusReadyToPlay) {
            
            NSLog(@"AVPlayerStatusReadyToPlay - 준비완료");
            
            //플레이어 레이어 설정
            AVPlayerLayer *playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player];
            playerLayer.frame = self.view.bounds;
            playerLayer.videoGravity = AVLayerVideoGravityResizeAspect;
            [self.view.layer addSublayer:playerLayer];
            
            
            playerLayer.needsDisplayOnBoundsChange = YES;
            self.view.layer.needsDisplayOnBoundsChange = YES;
            
            
            //컨트롤러 보이기
            //[self showControlles:nil];
            
            //2초뒤 숨기기
            //self.controlTimer = [NSTimer scheduledTimerWithTimeInterval:2.0f target:self selector:@selector(hideControls:) userInfo:nil repeats:NO];
            
            
            NSDictionary *userDict = [NSDictionary dictionaryWithObject:@"ready" forKey:@"status"];
            
            [[NSNotificationCenter defaultCenter] postNotificationName:@"customPlayerLoadStateChanged" object:self userInfo:userDict];
            

        }else{
            
            NSDictionary *userDict = [NSDictionary dictionaryWithObject:@[@"error" , @"unable to load file"] forKey:@[@"status" , @"reason"]];
    
            [[NSNotificationCenter defaultCenter] postNotificationName:@"customPlayerLoadStateChanged" object:self userInfo:userDict];
            
        }
        
        
        [self.player removeObserver:self forKeyPath:@"status"];
        
    }
    
    
}


//앞으로 감기
- (IBAction)seekForward:(id)sender {
    
    //전체 시간
    Float64 duration =CMTimeGetSeconds([self.playerItem duration]);
    
    //현재 시간
    Float64 currentTime = CMTimeGetSeconds([self.playerItem currentTime]);
    
    //현재 시간 + 5
    Float64 desiredTime = currentTime + 5.0;
    
    //
    if (desiredTime < duration) {
            
        //이동할 시간
        CMTime seekTime = CMTimeMake(desiredTime, 1);
        
        
        [self.player seekToTime:seekTime];
        
        //프로그레스바 업데이트
        [self updateProcess];
        
    }
    
}


//뒤로 감기
- (IBAction)seekBack:(id)sender {

    //현재 시간
    Float64 currentTime = CMTimeGetSeconds([self.playerItem currentTime]);
    
    //현재시간 - 5
    Float64 desiredTime = currentTime - 5.0;
    
    if (desiredTime < 0) {
        desiredTime = 0;
    }
        
    
    CMTime seekTime = CMTimeMake(desiredTime, 1);
        
    
    [self.player seekToTime:seekTime];
    
    //프로그레스바 업데이트
    [self updateProcess];
}


//슬라이더 체인지
- (IBAction)sliderChange:(UISlider *)sender {
    
    //이동시킨 프로그레스바 값
    float progress = sender.value;
    
    NSLog(@"progress.value : %f" , progress);
    
    //전체시간
    NSInteger durationSeconds = CMTimeGetSeconds([self.playerItem duration]);
    
    NSLog(@"전체 시간 : %ld" , (long)durationSeconds);
    
    //이동시간 프로그레스바 값 * 플레이어 전체시간
    float result = durationSeconds * progress;
    
    NSLog(@"result : %f" , result);
    
    
    //찾을 시간
    CMTime seekTime = CMTimeMakeWithSeconds(result, 1);
    
    
    //플레이어의 특정시간값을 이용해서 영상재생 위치 찾기
    [self.player seekToTime:seekTime];
    
}



//닫기
- (IBAction)done:(id)sender {

    [self dismissViewControllerAnimated:YES completion:nil];

}
@end

 

 

ViewController 에서 호출해서 사용

 

//플레이어 컨트롤러 호출
- (IBAction)showPlayerVC:(id)sender {
    
    //플레이어 세그 호출
    //[self performSegueWithIdentifier:@"PlayerSegue" sender:sender];
    
    //파일 경로
    NSBundle *bundle = [NSBundle mainBundle];
    NSURL *moveURL = [bundle URLForResource:@"a" withExtension:@"mov"];
    
    //플레이어 객체 생성
    self.moviePlayer = [[CustomPlayerController alloc] initWithContentURL:moveURL];
    
    //full screen 으로 열기
    [self.moviePlayer setModalPresentationStyle:UIModalPresentationFullScreen];
    
    //플레이어 열기
    [self presentViewController:self.moviePlayer animated:NO completion:^{
        NSLog(@"플레이어 열림");
    }];
    
}

 

 

CustomVD 2.zip
0.07MB

drive.google.com/file/d/1knad3mYhVSqRA1-5l4a_Sybx9jhhupTa/view?usp=sharing

 

 

참고

wnstkdyu.github.io/2018/05/03/avfoundationprogrammingguide/

baked-corn.tistory.com/m/118