avplayer创建 ios
2017-03-26 22:09:00 weixin_33794672 阅读数 9

前言:AVPlayer是用于管理媒体资产的播放和定时控制器对象它提供了控制播放器的有运输行为的接口,如它可以在媒体的时限内播放,暂停,和改变播放的速度,并有定位各个动态点的能力。可以使用AVPlayer来播放本地和远程的视频媒体文件,如QuickTime影片和MP3音频文件,以及视听媒体使用HTTP流媒体直播服务。

一个普通播放器的组成


img_e9ffded73e195edac8c305c22b627ab7.png



苹果在视频播放方面提供了多个框架供我们选择使用。分别为:

基于mediaPlayer类库的MPMediaPlayerController(iOS9后遭到废弃,被AVPlayerViewController所替代)

基于AVFounditon类库的AVPlayer

基于AVKit类库的AVPlayerViewController(iOS8后才可使用)

正文

AVPlayer与MPMediaPlayerController比较:

AVplayer有比较灵活,当然,也需要你去自定义构建UI。还有一大优势,例如其扩展的AVQueuePlayer,可以实现视频无缝队列播放、多视频同时播放、视频转换、编解码等功能。

MPMediaPlayerController实际上是基于AVPlayer的简单UI封装,对于一般的播放要求,几行代码就可实现,省心省事。

因为MPMediaPlayerController是对AVPlayer进行的单例封装,所以不能进行多视频播放

播放器Demo(全屏)已实现功能点:

push到播放器页面,横屏显示。

单机隐藏or显示上方标题栏与下方操作栏。

呼出右侧设置栏。

视频播放操作与进度条设置。

在屏幕上左右拖动,进行视频快进与快退。

在屏幕左侧上下拖动,进行亮度调整。

在屏幕右侧上下拖动,进行音量调整。

想到但是暂未实现的功能点:(大多为优化或与业务相关)

屏幕或进度条拖动快进操作时,添加提示框进行快进时间的实时提示。

用户无操作两三秒之后自动隐藏上下View。

视频清晰度调整按钮。(更换视频源)

操作加锁按钮。(加锁后未进行解锁操作之前不可进行操作)

弹幕相关。

用户允许横屏状态下,横屏竖屏自动进行页面切换与动画效果等。

网络视频的缓存、下载等。

软硬解码模式切换等。

笔者Demo选择使用了AVPlayer进行视频播放器的构建。由于UI的代码实现,加上略蛋疼的逻辑代码,播放器页面的代码量达到400多行,之后有时间的话会再进行优化。这里只贴出部分代码,想要查看或借鉴完整Demo,可以到本人github去下载。

使用AVPlayer构建播放器

1.导入头文件

#import<AVFoundation/AVFoundation.h>

2.其实没什么可说的,很简单,先初始化AVPlayer,然后添加到AVPlayerLayer,最后将其添加到视图的layer层。

#pragma mark - Demo中此视图的属性

#define TopViewHeight 55

#define BottomViewHeight 72

#define mainWidth [UIScreen mainScreen].bounds.size.width

#define mainHeight [UIScreen mainScreen].bounds.size.height

//上层建筑@property(nonatomic,strong)UIView*topView;

@property(nonatomic,strong)UIButton*backBtn;

@property(nonatomic,strong)UILabel*titleLabel;

@property(nonatomic,strong)UIButton*settingsBtn;

@property(nonatomic,strong)UIView*bottomView;

@property(nonatomic,strong)UIButton*playBtn;

@property(nonatomic,strong)UILabel*textLabel;

@property(nonatomic,assign)BOOLisPlay;

@property(nonatomic,strong)UISlider*movieProgressSlider;//进度条@property(nonatomic,assign)CGFloatProgressBeginToMove;

@property(nonatomic,assign)CGFloattotalMovieDuration;//视频总时间@property(nonatomic,strong)AVPlayer*player;

@property(nonatomic,strong)UIView*settingsView;

@property(nonatomic,strong)UIView*rightView;

@property(nonatomic,strong)UIButton*setTestBtn;

//touch evens

@property(nonatomic,assign)BOOLisShowView;

@property(nonatomic,assign)BOOLisSettingsViewShow;

@property(nonatomic,assign)BOOLisSlideOrClick;

@property(nonatomic,strong)UISlider*volumeViewSlider;

@property(nonatomic,assign)floatsystemVolume;//系统音量值@property(nonatomic,assign)floatsystemBrightness;//系统亮度@property(nonatomic,assign)CGPointstartPoint;//起始位置坐标@property(nonatomic,assign)BOOLisTouchBeganLeft;//起始位置方向@property(nonatomic,copy)NSString*isSlideDirection;//滑动方向@property(nonatomic,assign)floatstartProgress;//起始进度条

#pragma mark - 播放器

- (void)createAvPlayer{

//设置静音状态也可播放声音

AVAudioSession*audioSession = [AVAudioSessionsharedInstance]; 

  [audioSession setCategory:AVAudioSessionCategoryPlaybackerror:nil];

CGRectplayerFrame =CGRectMake(0,0,self.view.layer.bounds.size.height,self.view.layer.bounds.size.width);AVURLAsset  *asset = [AVURLAssetassetWithURL: _url]; 

  Float64 duration = CMTimeGetSeconds(asset.duration);

//获取视频总时长

_totalMovieDuration = duration;

AVPlayerItem*playerItem = [AVPlayerItemplayer ItemWithAsset: asset]; 

_player = [[AVPlayer  alloc]initWithPlayerItem:playerItem];

AVPlayerLayer  *playerLayer = [AVPlayerLayer  playerLayerWithPlayer:_player];    playerLayer.frame = playerFrame;  

playerLayer.videoGravity =AVLayerVideoGravityResizeAspect; 

  [self.view.layer addSublayer:playerLayer];

//需要设置自动播放的直接play即可

//[_player play];

}

屏幕单击手势与视频快进

屏幕单击

1.符合条件的情况下(手指按下后离开屏幕,并且没有拖动)通过BOOL值判断,隐藏或显示上下View

- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event{

CGPointpoint = [[touches anyObject] locationInView:self.view];

if(_isShowView) {

//上下View为显示状态,此时点击上下View直接return

if((point.y>CGRectGetMinY(self.topView.frame)&&point.yCGRectGetMinY(self.bottomView.frame))) {

return

}   

    _isShowView =NO;    

  [UIView  animateWithDuration:0.5  animations:^{   

        _topView.alpha =0;       

    _bottomView.alpha =0; 

      }];

  }else{    

  _isShowView =YES;     

  [UIView  animateWithDuration:0.5  animations:^{    

      _topView.alpha =1;       

    _bottomView.alpha =1;   

    }]; 

  }

}

2.右侧View显示的状态下,点击屏幕左半空白区域,隐藏右侧View

if(_isSettingsViewShow) {

if(point.x>CGRectGetMinX(_rightView.frame)&&point.x< CGRectGetMaxX(_rightView.frame)) {   

    return;

  }

_settingsView.alpha =0;

_isSettingsViewShow= NO;

}

拖动快进

1.计算后得出拖动方向为横向拖动。

CGPoint location = [[touches  anyObject] locationInView:self.view];

CGFloatchangeY = location.y - _startPoint.y;

CGFloat  changeX = location.x - _startPoint.x;

if(fabs(changeX) > fabs(changeY)){ 

  _isSlideDirection =@"横向";//设置为横向

}else if(fabs(changeY)>fabs(changeX)){

  _isSlideDirection =@"纵向";//设置为纵向

}else{ 

  _isSlideOrClick =NO;

NSLog(@"不在五行中。");

}

2.根据手指按下与离开屏幕后,横向位移的坐标值,对视频播放进度进行刷新。

if(_isSlideOrClick) { 

      _isSlideDirection =@"";  

    _isSlideOrClick =NO;

CGFloatchangeY = point.y - _startPoint.y;

CGFloatchangeX = point.x - _startPoint.x;

//如果位置改变 刷新进度条

if(fabs(changeX) > fabs(changeY)){    

      [selfscrubberIsScrolling];  

    }

return;

    }

//拖动进度条

-(void)scrubberIsScrolling{

//计算出拖动的当前秒数(总长*当前百分比)

NSInteger  dragedSeconds = floorf(_totalMovieDuration * _movieProgressSlider.value);

CMTime  newCMTime =CMTimeMake(dragedSeconds,1);   

[_player seekToTime:newCMTime completionHandler:^(BOOLfinished) {  

    [_player play];   

    [_playBtn setTitle:@"暂停"forState:UIControlStateNormal];  

}];

}

MPMediaPlayerController与AVPlayerViewController的使用介绍

MPMediaPlayerController与AVPlayerViewController,两者都是基于AVPlayer的简单UI封装,如果只是需要简单的视频播放功能,可以使用这两个类快速的构建视频播放器。

MPMediaPlayerController

1.导入头文件

#import<MediaPlayer/MediaPlayer.h>

2.初始化mp,几行代码既可以实现。

@property(nonatomic,strong)MPMoviePlayerController *mp;

NSURL*url1 = [[NSBundle  mainBundle]URLForResource:@"chenyifaer"withExtension:@"mp4"];

_mp = [[MPMoviePlayerController  alloc] initWithContentURL:url1];

_mp.controlStyle =MPMovieControlStyleNone;

_mp.view.frame = CGRectMake(0,0,self.view.layer.bounds.size.height,self.view.layer.bounds.size.width);[self.view addSubview:_mp.view];

[_mp play];

controlStyle属性有三个值:

MPMovieControlStyleNone,      //无控制

MPMovieControlStyleEmbedded,  //有全屏按钮与控制

MPMovieControlStyleFullscreen, // 默认全屏,有退出和控制

当然还有一些其他属性,有需要可以自行进行设置。

AVPlayerViewController

1.导入框架与头文件

#import<AVKit/AVKit.h>

2.初始化AVPlayerViewController,创建一个AVPlayer添加上。然后将其添加到视图上,再将View添加到self.View上,然后play即可

NSURL*url1 = [[NSBundle mainBundle]URLForResource:@"chenyifaer"withExtension:@"mp4"];

AVPlayer* player = [AVPlayer playerWithURL:url1];

AVPlayerViewController *playerController = [[AVPlayerViewControlleralloc]init];

playerController.player = player;

[self addChildViewController:playerController];

[self.view addSubview:playerController.view];

playerController.view.frame =CGRectMake(0,0,self.view.layer.bounds.size.height,self.view.layer.bounds.size.width);[player  play];

同样几行代码,即可实现。

音量调整

1.导入头文件

#import<MediaPlayer/MediaPlayer.h>

2.借助MPVolumeView类来获取到其音量进度条,进而进行音量获取与控制

@property(nonatomic,strong)UISlider*movieProgressSlider;//进度条MPVolumeView*volumeView = [[MPVolumeViewalloc] init];

_volumeViewSlider =nil;

for(UIView*view  in [volumeView subviews]){

if([view.class.description isEqualToString:@"MPVolumeSlider"]){        _volumeViewSlider = (UISlider*)view;

break;

}

3.触摸屏幕时,记录手指按下的位置、获取按下时系统的音量(实现touchesBegan方法)

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{        _startProgress = _movieProgressSlider.value;

}

4.手指在规定行为下(手指按下位置为视图右半区,且纵向滑动)持续滑动时,动态改变系统音量(实现touchesMoved方法)

//手指持续滑动,此方法会持续调用

- (void)touchesMoved:(NSSet *)toucheswithEvent:(UIEvent *)event{  

CGPoint location = [[touches anyObject]locationInView:self.view];

intindex = location.y - _startPoint.y;if(index>0){        [_volumeViewSlidersetValue:_systemVolume - (abs(index)/10*0.05)animated:YES];        [_volumeViewSlidersendActionsForControlEvents:UIControlEventTouchUpInside];    }else{  

  [_volumeViewSlidersetValue:_systemVolume + (abs(index)/10*0.05)animated:YES];        [_volumeViewSlidersendActionsForControlEvents:UIControlEventTouchUpInside];    }

}

亮度调整

1.触摸屏幕时,记录手指按下的位置、按下时屏幕的亮度(实现touchesBegan方法)

2.手指在规定行为下(手指按下位置为视图左半区,且纵向滑动)持续滑动时,不断动态处理(实现touchesMoved方法)

3.改变屏幕亮度:[UIScreen mainScreen].brightness = X (0~1);

//手指持续滑动,此方法会持续调用

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{    CGPointlocation= [[touches anyObject] locationInView:self.view];

int index= location.y - _startPoint.y;

if(index>0){     

 [UIScreen mainScreen].brightness = _systemBrightness -abs(index)/10*0.01; 

  }else{   

    _movieProgressSlider.value = _startProgress -abs(index)/10*0.008;   

}

}

屏幕旋转

1.设置应用支持横屏(默认支持)。

2.在根视图中设置默认竖屏(Nav、TabBar、VC基类)

- (BOOL)shouldAutorotate{returnNO;}- (UIInterfaceOrientationMask)supportedInterfaceOrientations{

returnUIInterfaceOrientationMaskPortrait;

}

3.在需要横屏的VC中重写下列方法即可

//允许横屏旋转

- (BOOL)shouldAutorotate{returnYES;

}

//支持左右旋转

-(UIInterfaceOrientationMask)supportedInterfaceOrientations{

return UIInterfaceOrientationMaskLandscapeRight|UIInterfaceOrientationMaskLandscapeLeft;

}//默认为右旋转

-(UIInterfaceOrientation)preferredInterfaceOrientationForPresentation{

returnUIInterfaceOrientationLandscapeRight;

}

参考:

1.http://www.techotopia.com/index.php/iOS8AVPlayerAndPlayerViewController

2.http://stackoverflow.com/questions/8146942/avplayer-and-mpmovieplayercontroller-differences

3.http://www.jianshu.com/p/e64fe3c7f9ab

4.http://www.th7.cn/Program/IOS/201504/439086.shtml

2017-03-26 22:09:00 weixin_34038293 阅读数 28

前言:AVPlayer是用于管理媒体资产的播放和定时控制器对象它提供了控制播放器的有运输行为的接口,如它可以在媒体的时限内播放,暂停,和改变播放的速度,并有定位各个动态点的能力。可以使用AVPlayer来播放本地和远程的视频媒体文件,如QuickTime影片和MP3音频文件,以及视听媒体使用HTTP流媒体直播服务。

一个普通播放器的组成


5220087-b25af4b576c383f0.png



苹果在视频播放方面提供了多个框架供我们选择使用。分别为:

基于mediaPlayer类库的MPMediaPlayerController(iOS9后遭到废弃,被AVPlayerViewController所替代)

基于AVFounditon类库的AVPlayer

基于AVKit类库的AVPlayerViewController(iOS8后才可使用)

正文

AVPlayer与MPMediaPlayerController比较:

AVplayer有比较灵活,当然,也需要你去自定义构建UI。还有一大优势,例如其扩展的AVQueuePlayer,可以实现视频无缝队列播放、多视频同时播放、视频转换、编解码等功能。

MPMediaPlayerController实际上是基于AVPlayer的简单UI封装,对于一般的播放要求,几行代码就可实现,省心省事。

因为MPMediaPlayerController是对AVPlayer进行的单例封装,所以不能进行多视频播放

播放器Demo(全屏)已实现功能点:

push到播放器页面,横屏显示。

单机隐藏or显示上方标题栏与下方操作栏。

呼出右侧设置栏。

视频播放操作与进度条设置。

在屏幕上左右拖动,进行视频快进与快退。

在屏幕左侧上下拖动,进行亮度调整。

在屏幕右侧上下拖动,进行音量调整。

想到但是暂未实现的功能点:(大多为优化或与业务相关)

屏幕或进度条拖动快进操作时,添加提示框进行快进时间的实时提示。

用户无操作两三秒之后自动隐藏上下View。

视频清晰度调整按钮。(更换视频源)

操作加锁按钮。(加锁后未进行解锁操作之前不可进行操作)

弹幕相关。

用户允许横屏状态下,横屏竖屏自动进行页面切换与动画效果等。

网络视频的缓存、下载等。

软硬解码模式切换等。

笔者Demo选择使用了AVPlayer进行视频播放器的构建。由于UI的代码实现,加上略蛋疼的逻辑代码,播放器页面的代码量达到400多行,之后有时间的话会再进行优化。这里只贴出部分代码,想要查看或借鉴完整Demo,可以到本人github去下载。

使用AVPlayer构建播放器

1.导入头文件

#import<AVFoundation/AVFoundation.h>

2.其实没什么可说的,很简单,先初始化AVPlayer,然后添加到AVPlayerLayer,最后将其添加到视图的layer层。

#pragma mark - Demo中此视图的属性

#define TopViewHeight 55

#define BottomViewHeight 72

#define mainWidth [UIScreen mainScreen].bounds.size.width

#define mainHeight [UIScreen mainScreen].bounds.size.height

//上层建筑@property(nonatomic,strong)UIView*topView;

@property(nonatomic,strong)UIButton*backBtn;

@property(nonatomic,strong)UILabel*titleLabel;

@property(nonatomic,strong)UIButton*settingsBtn;

@property(nonatomic,strong)UIView*bottomView;

@property(nonatomic,strong)UIButton*playBtn;

@property(nonatomic,strong)UILabel*textLabel;

@property(nonatomic,assign)BOOLisPlay;

@property(nonatomic,strong)UISlider*movieProgressSlider;//进度条@property(nonatomic,assign)CGFloatProgressBeginToMove;

@property(nonatomic,assign)CGFloattotalMovieDuration;//视频总时间@property(nonatomic,strong)AVPlayer*player;

@property(nonatomic,strong)UIView*settingsView;

@property(nonatomic,strong)UIView*rightView;

@property(nonatomic,strong)UIButton*setTestBtn;

//touch evens

@property(nonatomic,assign)BOOLisShowView;

@property(nonatomic,assign)BOOLisSettingsViewShow;

@property(nonatomic,assign)BOOLisSlideOrClick;

@property(nonatomic,strong)UISlider*volumeViewSlider;

@property(nonatomic,assign)floatsystemVolume;//系统音量值@property(nonatomic,assign)floatsystemBrightness;//系统亮度@property(nonatomic,assign)CGPointstartPoint;//起始位置坐标@property(nonatomic,assign)BOOLisTouchBeganLeft;//起始位置方向@property(nonatomic,copy)NSString*isSlideDirection;//滑动方向@property(nonatomic,assign)floatstartProgress;//起始进度条

#pragma mark - 播放器

- (void)createAvPlayer{

//设置静音状态也可播放声音

AVAudioSession*audioSession = [AVAudioSessionsharedInstance]; 

  [audioSession setCategory:AVAudioSessionCategoryPlaybackerror:nil];

CGRectplayerFrame =CGRectMake(0,0,self.view.layer.bounds.size.height,self.view.layer.bounds.size.width);AVURLAsset  *asset = [AVURLAssetassetWithURL: _url]; 

  Float64 duration = CMTimeGetSeconds(asset.duration);

//获取视频总时长

_totalMovieDuration = duration;

AVPlayerItem*playerItem = [AVPlayerItemplayer ItemWithAsset: asset]; 

_player = [[AVPlayer  alloc]initWithPlayerItem:playerItem];

AVPlayerLayer  *playerLayer = [AVPlayerLayer  playerLayerWithPlayer:_player];    playerLayer.frame = playerFrame;  

playerLayer.videoGravity =AVLayerVideoGravityResizeAspect; 

  [self.view.layer addSublayer:playerLayer];

//需要设置自动播放的直接play即可

//[_player play];

}

屏幕单击手势与视频快进

屏幕单击

1.符合条件的情况下(手指按下后离开屏幕,并且没有拖动)通过BOOL值判断,隐藏或显示上下View

- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event{

CGPointpoint = [[touches anyObject] locationInView:self.view];

if(_isShowView) {

//上下View为显示状态,此时点击上下View直接return

if((point.y>CGRectGetMinY(self.topView.frame)&&point.yCGRectGetMinY(self.bottomView.frame))) {

return

}   

    _isShowView =NO;    

  [UIView  animateWithDuration:0.5  animations:^{   

        _topView.alpha =0;       

    _bottomView.alpha =0; 

      }];

  }else{    

  _isShowView =YES;     

  [UIView  animateWithDuration:0.5  animations:^{    

      _topView.alpha =1;       

    _bottomView.alpha =1;   

    }]; 

  }

}

2.右侧View显示的状态下,点击屏幕左半空白区域,隐藏右侧View

if(_isSettingsViewShow) {

if(point.x>CGRectGetMinX(_rightView.frame)&&point.x< CGRectGetMaxX(_rightView.frame)) {   

    return;

  }

_settingsView.alpha =0;

_isSettingsViewShow= NO;

}

拖动快进

1.计算后得出拖动方向为横向拖动。

CGPoint location = [[touches  anyObject] locationInView:self.view];

CGFloatchangeY = location.y - _startPoint.y;

CGFloat  changeX = location.x - _startPoint.x;

if(fabs(changeX) > fabs(changeY)){ 

  _isSlideDirection =@"横向";//设置为横向

}else if(fabs(changeY)>fabs(changeX)){

  _isSlideDirection =@"纵向";//设置为纵向

}else{ 

  _isSlideOrClick =NO;

NSLog(@"不在五行中。");

}

2.根据手指按下与离开屏幕后,横向位移的坐标值,对视频播放进度进行刷新。

if(_isSlideOrClick) { 

      _isSlideDirection =@"";  

    _isSlideOrClick =NO;

CGFloatchangeY = point.y - _startPoint.y;

CGFloatchangeX = point.x - _startPoint.x;

//如果位置改变 刷新进度条

if(fabs(changeX) > fabs(changeY)){    

      [selfscrubberIsScrolling];  

    }

return;

    }

//拖动进度条

-(void)scrubberIsScrolling{

//计算出拖动的当前秒数(总长*当前百分比)

NSInteger  dragedSeconds = floorf(_totalMovieDuration * _movieProgressSlider.value);

CMTime  newCMTime =CMTimeMake(dragedSeconds,1);   

[_player seekToTime:newCMTime completionHandler:^(BOOLfinished) {  

    [_player play];   

    [_playBtn setTitle:@"暂停"forState:UIControlStateNormal];  

}];

}

MPMediaPlayerController与AVPlayerViewController的使用介绍

MPMediaPlayerController与AVPlayerViewController,两者都是基于AVPlayer的简单UI封装,如果只是需要简单的视频播放功能,可以使用这两个类快速的构建视频播放器。

MPMediaPlayerController

1.导入头文件

#import<MediaPlayer/MediaPlayer.h>

2.初始化mp,几行代码既可以实现。

@property(nonatomic,strong)MPMoviePlayerController *mp;

NSURL*url1 = [[NSBundle  mainBundle]URLForResource:@"chenyifaer"withExtension:@"mp4"];

_mp = [[MPMoviePlayerController  alloc] initWithContentURL:url1];

_mp.controlStyle =MPMovieControlStyleNone;

_mp.view.frame = CGRectMake(0,0,self.view.layer.bounds.size.height,self.view.layer.bounds.size.width);[self.view addSubview:_mp.view];

[_mp play];

controlStyle属性有三个值:

MPMovieControlStyleNone,      //无控制

MPMovieControlStyleEmbedded,  //有全屏按钮与控制

MPMovieControlStyleFullscreen, // 默认全屏,有退出和控制

当然还有一些其他属性,有需要可以自行进行设置。

AVPlayerViewController

1.导入框架与头文件

#import<AVKit/AVKit.h>

2.初始化AVPlayerViewController,创建一个AVPlayer添加上。然后将其添加到视图上,再将View添加到self.View上,然后play即可

NSURL*url1 = [[NSBundle mainBundle]URLForResource:@"chenyifaer"withExtension:@"mp4"];

AVPlayer* player = [AVPlayer playerWithURL:url1];

AVPlayerViewController *playerController = [[AVPlayerViewControlleralloc]init];

playerController.player = player;

[self addChildViewController:playerController];

[self.view addSubview:playerController.view];

playerController.view.frame =CGRectMake(0,0,self.view.layer.bounds.size.height,self.view.layer.bounds.size.width);[player  play];

同样几行代码,即可实现。

音量调整

1.导入头文件

#import<MediaPlayer/MediaPlayer.h>

2.借助MPVolumeView类来获取到其音量进度条,进而进行音量获取与控制

@property(nonatomic,strong)UISlider*movieProgressSlider;//进度条MPVolumeView*volumeView = [[MPVolumeViewalloc] init];

_volumeViewSlider =nil;

for(UIView*view  in [volumeView subviews]){

if([view.class.description isEqualToString:@"MPVolumeSlider"]){        _volumeViewSlider = (UISlider*)view;

break;

}

3.触摸屏幕时,记录手指按下的位置、获取按下时系统的音量(实现touchesBegan方法)

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{        _startProgress = _movieProgressSlider.value;

}

4.手指在规定行为下(手指按下位置为视图右半区,且纵向滑动)持续滑动时,动态改变系统音量(实现touchesMoved方法)

//手指持续滑动,此方法会持续调用

- (void)touchesMoved:(NSSet *)toucheswithEvent:(UIEvent *)event{  

CGPoint location = [[touches anyObject]locationInView:self.view];

intindex = location.y - _startPoint.y;if(index>0){        [_volumeViewSlidersetValue:_systemVolume - (abs(index)/10*0.05)animated:YES];        [_volumeViewSlidersendActionsForControlEvents:UIControlEventTouchUpInside];    }else{  

  [_volumeViewSlidersetValue:_systemVolume + (abs(index)/10*0.05)animated:YES];        [_volumeViewSlidersendActionsForControlEvents:UIControlEventTouchUpInside];    }

}

亮度调整

1.触摸屏幕时,记录手指按下的位置、按下时屏幕的亮度(实现touchesBegan方法)

2.手指在规定行为下(手指按下位置为视图左半区,且纵向滑动)持续滑动时,不断动态处理(实现touchesMoved方法)

3.改变屏幕亮度:[UIScreen mainScreen].brightness = X (0~1);

//手指持续滑动,此方法会持续调用

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{    CGPointlocation= [[touches anyObject] locationInView:self.view];

int index= location.y - _startPoint.y;

if(index>0){     

 [UIScreen mainScreen].brightness = _systemBrightness -abs(index)/10*0.01; 

  }else{   

    _movieProgressSlider.value = _startProgress -abs(index)/10*0.008;   

}

}

屏幕旋转

1.设置应用支持横屏(默认支持)。

2.在根视图中设置默认竖屏(Nav、TabBar、VC基类)

- (BOOL)shouldAutorotate{returnNO;}- (UIInterfaceOrientationMask)supportedInterfaceOrientations{

returnUIInterfaceOrientationMaskPortrait;

}

3.在需要横屏的VC中重写下列方法即可

//允许横屏旋转

- (BOOL)shouldAutorotate{returnYES;

}

//支持左右旋转

-(UIInterfaceOrientationMask)supportedInterfaceOrientations{

return UIInterfaceOrientationMaskLandscapeRight|UIInterfaceOrientationMaskLandscapeLeft;

}//默认为右旋转

-(UIInterfaceOrientation)preferredInterfaceOrientationForPresentation{

returnUIInterfaceOrientationLandscapeRight;

}

参考:

1.http://www.techotopia.com/index.php/iOS8AVPlayerAndPlayerViewController

2.http://stackoverflow.com/questions/8146942/avplayer-and-mpmovieplayercontroller-differences

3.http://www.jianshu.com/p/e64fe3c7f9ab

4.http://www.th7.cn/Program/IOS/201504/439086.shtml

2015-11-26 23:58:51 hxpp777 阅读数 1601


目录

  1. 导入框架
    导入

NSString *str = @"http://vmovier.qiniudn.com/559b918dbf717.mp4";
NSString *urlStr =[str stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];//链接接口中的汉字会导致请求失败
NSURL *url=[NSURL URLWithString:urlStr];
AVPlayerItem *playerItem=[AVPlayerItem playerItemWithURL:url];//主要创建方法,创建媒体资源管理对象
AVPlayer *player = [AVPlayer playerWithPlayerItem:playerItem];//根据媒体资源管理对象创建AVPlayer对象
AVPlayerLayer *playerLayer = [AVPlayerLayer playerLayerWithPlayer:player];//AVPlayer要显示视频,必须创建一个播放器层AVPlayerLayer用于展示

(2)视频的播放、暂停功能。
这也是最基本的功能,AVPlayer对应着两个方法play、pause来实现。
[player play];//播放
[player pause];//暂停
通常我们会需要得知当前的播放状态来进行主动地改变播放状态,问题的关键是如何判断当前视频是否在播放。很不幸的是AVPlayer没有这样的状态属性,但是很幸运的是我们可以通过判断播放器的播放速度来获得播放状态。AVPlayer类包含一个极其重要的属性:

**@property (nonatomic) float rate;**
  • 1

其代码注释为:/* indicates the current rate of playback; 0.0 means “stopped”, 1.0 means “play at the natural rate of the current item” */
解释为:”表示当前的播放速度;0表示“停止”,1表示“在当前的PlayerItem下以正常速率播放”。
通过监控这个属性,可以间接获取当前播放状态,获取这个属性值对于视频播放器的功能完善具有不可代替的作用。

(3)播放进度及缓存进度
MPMoviePlayerController拥有自己的进度条,AVPlayer想要要展示播放进度就没有那么简单了。视频播放器通常是使用通知来获得播放器的当前状态,媒体加载状态等,但是无论是AVPlayer还是AVPlayerItem(AVPlayer有一个属性currentItem是AVPlayerItem类型,表示当前播放的视频对象)都无法获得这些信息。当然AVPlayerItem是有通知的,但是对于获得播放状态和加载状态有用的通知只有一个:播放完成通知AVPlayerItemDidPlayToEndTimeNotification。
在播放视频时,特别是播放网络视频往往需要知道视频加载情况、缓冲情况、播放情况,这些信息可以通过KVO监控AVPlayerItem的status、loadedTimeRanges属性来获得。当AVPlayerItem的status属性为AVPlayerStatusReadyToPlay是说明正在播放,只有处于这个状态时才能获得视频时长等信息;当loadedTimeRanges的改变时(每缓冲一部分数据就会更新此属性)可以获得本次缓冲加载的视频范围(包含起始时间、本次加载时长),这样一来就可以实时获得缓冲情况。然后就是依靠AVPlayer的

- (id)addPeriodicTimeObserverForInterval:(CMTime)interval queue:(dispatch_queue_t)queue usingBlock:(void (^)(CMTime time))block
  • 1

方法获得播放进度,这个方法会在设定的时间间隔内定时更新播放进度,通过time参数通知客户端。有了这些视频信息,播放进度就不成问题了,事实上通过这些信息就算是平时看到的其他播放器的缓冲进度显示以及拖动播放的功能也可以顺利的实现。

  1. 具体实现
    具体实现的效果图:
    exp_1
    exp_2

具体代码:
(注:自知自己水平有限,代码优化处理方面做的也不是太好,但是幸好实现效果勉强可以,请各位读者勿喷就好)
(1)ViewController里的调用情况:

ViewController.h文件

#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
#import "AVPlayer_S.h"

@interface ViewController : UIViewController
**//AVPlayer_S 为我自己创建的类,继承于UIView,用于承载AVPlayer**
@property (nonatomic, retain) AVPlayer_S *myVideoPlayer;
@end

ViewController.m文件

#import "ViewController.h"
@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];    
    self.view.backgroundColor = [UIColor whiteColor];

    //AVPlayer_S 为我自己创建的类,继承于UIView,用于承载AVPlayer,此处用了固定的frame,自己在使用时可以根据需要设定具体的frame
    self.myVideoPlayer = [[AVPlayer_S alloc] initWithFrame:CGRectMake(20, 60, 335, 280) videoURL:@"http://vmovier.qiniudn.com/559b918dbf717.mp4"];

    [self.view addSubview:self.myVideoPlayer];
}
- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
@end

AVPlayer_S.h文件

 #import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
#import "MBProgressHUD.h"//为视频添加缓冲时候的菊花旋转,此第三方可以再Github上下载,直接搜索MBProgressHUD即可下载使用,假如你不想用可以把与之相关的全部删除
//附加MBProgressHUD的下载地址:https://github.com/jdg/MBProgressHUD

@interface AVPlayer_S : UIView<MBProgressHUDDelegate>

@property (nonatomic, retain) AVPlayer *player;//AVPlayer对象
@property (nonatomic, retain) AVPlayerLayer *playerLayer;//视频播放器的layer层视图,若不明白为何创建,请看目录2.AVPlayer类介绍

@property (nonatomic, retain) NSString *video_url;//接收视频连接
@property (nonatomic, assign) CMTime totalTime;//记录视频总时长
@property (nonatomic, assign) BOOL hidenBar;//视频UI控件是否显示的标识
@property (nonatomic, assign) BOOL didPlay;//缓冲结束立即播放的标识
@property (nonatomic, assign) BOOL isFullScreen;//全屏状态标识
@property (nonatomic, assign) CGRect originalFrame;//记录播放器原始frame

@property (nonatomic, retain) UIImageView *backIamge;//背景图
@property (nonatomic, retain) UILabel *didTimeLabel;//显示已完成的播放时长
@property (nonatomic, retain) UILabel *totalTimeLabel;//显示视频的总播放时长
@property (nonatomic, retain) UIButton *playButton;//播放暂停按钮
@property (nonatomic, retain) UIProgressView *playProgress;//缓冲条
@property (nonatomic, retain) UISlider *playSlider;//播放进度条
@property (nonatomic, retain) UIButton *fullScreenButton;
@property (nonatomic, retain) UIView *barView;//承载上述控件的UIView
@property (nonatomic, retain) UIView *superView;//记录下全屏前的父视图,便于退出全屏后视频处在正确的位置

- (instancetype)initWithFrame:(CGRect)frame videoURL:(NSString *)url;//自定义初始化方法,只需要给定frame和视频连接即可返回一个具有该frame的承载视频播放器的UIView
- (void)removeNotification;//移除通知
- (void)removeObserverFromPlayerItem:(AVPlayerItem *)playerItem;//移除playerItem的观察者

@end

AVPlayer_S.m文件

#import "AVPlayer_S.h"
@implementation AVPlayer_S

//自定义初始化方法
- (instancetype)initWithFrame:(CGRect)frame videoURL:(NSString *)url
{
    self = [super initWithFrame:frame];
    if (self) {
        self.didPlay = NO;
        self.originalFrame = frame;
        self.hidenBar = NO;
        self.isFullScreen = NO;
        self.video_url = url;
        self.backgroundColor = [UIColor blackColor];
        [self creatView:frame];//创建UI视图
        [self createPlayer:self.originalFrame];//创建layer层,用于播放视频
        [MBProgressHUD showHUDAddedTo:self animated:YES];//添加小菊花,若不想用就删除与之相关的东西
    }

    return self;
}

- (void)createPlayer:(CGRect)frame {

    NSString *urlStr =[self.video_url stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    NSURL *url=[NSURL URLWithString:urlStr];
    //开辟线程执行AVPlayerItem的创建,若不开辟线程则会占用主线程,造成视图加载完成后暂时无法对页面进行操作的卡顿现象
    dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);
    dispatch_async(globalQueue, ^{
    //创建AVPlayerItem
         AVPlayerItem *playerItem=[AVPlayerItem playerItemWithURL:url];
        dispatch_async(dispatch_get_main_queue(), ^{
            if (playerItem) {
                if (!_player) {
                    _player = [AVPlayer playerWithPlayerItem:playerItem];
                    if (_player) {
                    //保证AVPlayer对象存在,然后再为其添加进度观察,以便获取当前播放进度
                        [self addProgressObserver];
                        //给AVPlayerItem添加监控(最重要的监控)
                        [self addObserverToPlayerItem:playerItem];
                    }
                }
            } else {
                UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"提示" message:@"无法连接到视频资源" delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"确定", nil];
                [alert show];
                [self removeFromSuperview];
            }

        });
    });
    //创建播放层(layer层)
    self.playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player];
    self.playerLayer.frame = CGRectMake(0, 0, frame.size.width, frame.size.height);
    [self.layer addSublayer:self.playerLayer];
}


#pragma mark - 通知
//添加通知

- (void)addNotification {
    //添加 播放结束 通知
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playbackFinished:) name:AVPlayerItemDidPlayToEndTimeNotification object:self.player.currentItem];
}

//移除所有通知
- (void)removeNotification {
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

// 播放完成通知执行的方法:移除播放视图及小菊花假如想执行此操作可以解除注释

- (void)playbackFinished:(NSNotification *)notification {
/*
    [MBProgressHUD hideHUDForView:self animated:YES];
    [self removeFromSuperview];
*/
}

#pragma mark - 监控
// 给播放器添加进度更新

- (void)addProgressObserver {

    UISlider *slider = self.playSlider;//获取当前滑动条
    __block AVPlayer_S *av_s = self;

    //这里设置每秒执行一次
    [self.player addPeriodicTimeObserverForInterval:CMTimeMake(1.0, 10.0) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {

        float current = CMTimeGetSeconds(time);//获取当前的播放时间

        if (current) {
            [slider setValue:current animated:YES];//实时设置滑动条当前值
            int currentTime = (int)current;
            int m = currentTime / 60;
            int s = currentTime % 60;
            NSString *strM = nil;
            NSString *strS = nil;
            if (m < 10) {
                strM = [NSString stringWithFormat:@"0%d", m];
            } else {
                strM = [NSString stringWithFormat:@"%d", m];
            }
            if (s < 10) {
                strS = [NSString stringWithFormat:@"0%d", s];
            } else {
                strS = [NSString stringWithFormat:@"%d", s];
            }
            //设置UILabel视图显示当前播放时长
            av_s.didTimeLabel.text = [NSString stringWithFormat:@"%@:%@", strM, strS];
        }

    }];
}
// 给AVPlayerItem添加监控

- (void)addObserverToPlayerItem:(AVPlayerItem *)playerItem {
    //监控状态属性,注意AVPlayer也有一个status属性,通过监控它的status也可以获得播放状态
    [playerItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
    //监控网络加载情况属性
    [playerItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];

}

//通知移除方法
- (void)removeObserverFromPlayerItem:(AVPlayerItem *)playerItem {
    [playerItem removeObserver:self forKeyPath:@"status"];
    [playerItem removeObserver:self forKeyPath:@"loadedTimeRanges"];
}

/*
 *  通过KVO监控播放器状态
 *  @param keyPath 监控属性
 *  @param object  监视器
 *  @param change  状态改变
 *  @param context 上下文
 */
 //通知所执行的方法
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {

    AVPlayerItem *playerItem = object;//获取监控对象
    if ([keyPath isEqualToString:@"status"]) {
        //获取status值
        AVPlayerStatus status = [[change objectForKey:@"new"] intValue];
        //暂停状态
        if (status == AVPlayerStatusReadyToPlay) {
            self.totalTime = playerItem.duration;//获取视频总时长
            [self customVideoSlider:playerItem.duration];//设置播放进度最大值
            int totalTime = (int)CMTimeGetSeconds(playerItem.duration);
            int m = totalTime / 60;
            int s = totalTime % 60;
            NSString *strM = nil;
            NSString *strS = nil;
            if (m < 10) {
                strM = [NSString stringWithFormat:@"0%d", m];
            } else {
                strM = [NSString stringWithFormat:@"%d", m];
            }
            if (s < 10) {
                strS = [NSString stringWithFormat:@"0%d", s];
            } else {
                strS = [NSString stringWithFormat:@"%d", s];
            }
            //设置视频时长的UILabel视图显示值
            self.totalTimeLabel.text = [NSString stringWithFormat:@"%@:%@", strM, strS];
        }
    } else if ([keyPath isEqualToString:@"loadedTimeRanges"]) {
    //播放状态
        [self addNotification];//添加播放完成通知
        NSArray *array = playerItem.loadedTimeRanges;
        CMTimeRange timeRange = [array.firstObject CMTimeRangeValue];//本次缓冲时间范围
        float startSeconds = CMTimeGetSeconds(timeRange.start);
        float durationSeconds = CMTimeGetSeconds(timeRange.duration);
        NSTimeInterval totalBuffer = startSeconds + durationSeconds;//缓冲总长度
        //设置缓存条
        [self.playProgress setProgress:totalBuffer/CMTimeGetSeconds(self.totalTime) animated:YES];
        float currentPlayTime =  CMTimeGetSeconds([self.player currentTime]);//获取当前播放时长
        //设定当缓冲总时长超过播放时长3秒时开始播放视频
        if (totalBuffer - currentPlayTime > 3.0) {
            if (!self.didPlay) {
                [self createPlayer:self.frame];
                [self.player play];
                [MBProgressHUD hideHUDForView:self animated:YES];
                [self bringSubviewToFront:self.barView];
                [self.playButton setImage:[UIImage imageNamed:@"pause_64"] forState:UIControlStateNormal];
                self.didPlay = YES;
            }
        } else {
            [self.player pause];
            [MBProgressHUD showHUDAddedTo:self animated:YES];
            self.didPlay = NO;
            [self.playButton setImage:[UIImage imageNamed:@"play_64"] forState:UIControlStateNormal];
        }

    }
}
//播放视图的创建
- (void)creatView:(CGRect)frame {

    self.backIamge = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, frame.size.width, frame.size.height)];
    self.backIamge.image = [UIImage imageNamed:@"back_Image.png"];

    self.barView = [[UIView alloc] initWithFrame:CGRectMake(0, frame.size.height-40, frame.size.width, 40)];
    self.barView.backgroundColor = [UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:0.5];

    self.playProgress = [[UIProgressView alloc] initWithFrame:CGRectMake(50, 15, self.barView.frame.size.width-100, 10)];
    [self.playProgress setProgressTintColor:[UIColor orangeColor]];

    self.playSlider = [[UISlider alloc] initWithFrame:CGRectMake(47, 11.2, self.playProgress.frame.size.width+10, 10)];
    [self.playSlider setThumbImage:[UIImage imageNamed:@"ball_16"] forState:UIControlStateNormal];

    self.didTimeLabel = [[UILabel alloc] initWithFrame:CGRectMake(50, 25, 50, 10)];
    self.didTimeLabel.font = [UIFont systemFontOfSize:14];
    self.totalTimeLabel = [[UILabel alloc] initWithFrame:CGRectMake(self.barView.frame.size.width-100, 25, 50, 10)];
    self.totalTimeLabel.font = [UIFont systemFontOfSize:14];
    //=================
    UIGraphicsBeginImageContextWithOptions((CGSize){ 2, 2 }, NO, 0.0f);
    UIImage *transparentImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    [self.playSlider setMinimumTrackTintColor:[UIColor blueColor]];
    [self.playSlider setMaximumTrackImage:transparentImage forState:UIControlStateNormal];
    //=================
    [self.playSlider addTarget:self action:@selector(sliderAction:) forControlEvents:UIControlEventValueChanged];

    self.playButton = [UIButton buttonWithType:UIButtonTypeCustom];
    self.playButton.frame = CGRectMake(5, 5, 30, 30);
    [self.playButton addTarget:self action:@selector(playButtonAction:) forControlEvents:UIControlEventTouchUpInside];
    [self.playButton setBackgroundColor:[UIColor clearColor]];
    [self.playButton setImage:[UIImage imageNamed:@"play_64.png"] forState:UIControlStateNormal];

    self.fullScreenButton = [UIButton buttonWithType:UIButtonTypeCustom];
    self.fullScreenButton.frame = CGRectMake(self.barView.frame.size.width - 40, 5, 30, 30);
    [self.fullScreenButton addTarget:self action:@selector(fullScreenButtonAction:) forControlEvents:UIControlEventTouchUpInside];
    [self.fullScreenButton setBackgroundColor:[UIColor clearColor]];
    [self.fullScreenButton setImage:[UIImage imageNamed:@"fullScreen_64.png"] forState:UIControlStateNormal];

    [self.barView addSubview:self.playButton];
    [self.barView addSubview:self.playProgress];
    [self.barView addSubview:self.playSlider];
    [self.barView addSubview:self.didTimeLabel];
    [self.barView addSubview:self.totalTimeLabel];
    [self.barView addSubview:self.fullScreenButton];
    [self addSubview:self.barView];

    UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapAction:)];
    [self addGestureRecognizer:tap];
}

- (void)tapAction:(UITapGestureRecognizer *)tap {

    if (self.hidenBar) {
        self.hidenBar = NO;
        [self bringSubviewToFront:self.barView];
    } else {
        self.hidenBar = YES;
    }
    self.barView.hidden = self.hidenBar;
}

- (void)playButtonAction:(UIButton *)button
{
    NSLog(@"%f", self.player.rate);
    if(self.player.rate == 0){ //说明是暂停
        [button setImage:[UIImage imageNamed:@"pause_64"] forState:UIControlStateNormal];
        self.backIamge.hidden = YES;
        if (self.didPlay) {
            [self.player play];
        }
    }else if(self.player.rate == 1){//正在播放
        [self.player pause];
        [button setImage:[UIImage imageNamed:@"play_64"] forState:UIControlStateNormal];
    }
}

//全屏设置
- (void)fullScreenButtonAction:(UIButton *)button {

    if (self.isFullScreen) {
        self.isFullScreen = NO;
        [self.fullScreenButton setImage:[UIImage imageNamed:@"fullScreen_64.png"] forState:UIControlStateNormal];

        [UIView animateWithDuration:0.2 animations:^{

            self.transform = CGAffineTransformRotate(self.transform, -M_PI_2);

            self.frame = self.originalFrame;
            [self.playerLayer removeFromSuperlayer];
            [self createPlayer:self.originalFrame];

            self.barView.frame = CGRectMake(0, self.originalFrame.size.height-40, self.originalFrame.size.width, 40);
            [self bringSubviewToFront:self.barView];

            self.center = CGPointMake(self.originalFrame.origin.x + self.originalFrame.size.width/2.0, self.originalFrame.origin.y + self.originalFrame.size.height/2.0);

            self.backIamge.frame = CGRectMake(0, 0, self.originalFrame.size.width, self.originalFrame.size.height);
            self.playProgress.frame = CGRectMake(50, 15, self.barView.frame.size.width-100, 10);
            self.playSlider.frame = CGRectMake(45, 11, self.playProgress.frame.size.width+10, 10);
            self.playButton.frame = CGRectMake(5, 5, 30, 30);
            self.fullScreenButton.frame = CGRectMake(self.barView.frame.size.width - 40, 5, 30, 30);

            [self.superView addSubview:self];
        }];

    } else {
        self.isFullScreen = YES;
        self.superView = self.superview;

        [self.fullScreenButton setImage:[UIImage imageNamed:@"exitFullScreen_64.png"] forState:UIControlStateNormal];
        [self.window addSubview:self];
        [self.window bringSubviewToFront:self];

        [UIView animateWithDuration:0.2 animations:^{
            self.frame = CGRectMake(20, 0, [UIScreen mainScreen].bounds.size.height-20, [UIScreen mainScreen].bounds.size.width);
            [self.playerLayer removeFromSuperlayer];

            [self createPlayer:self.frame];

            self.barView.frame = CGRectMake(0, [UIScreen mainScreen].bounds.size.width-40, [UIScreen mainScreen].bounds.size.height-20, 40);
            [self bringSubviewToFront:self.barView];
            self.backIamge.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.height-20, [UIScreen mainScreen].bounds.size.width);

            self.transform = CGAffineTransformRotate(self.transform, M_PI_2);
            self.center = CGPointMake([UIScreen mainScreen].bounds.size.width/2.0, ([UIScreen mainScreen].bounds.size.height+20)/2.0);
            self.playProgress.frame = CGRectMake(50, 15, self.barView.frame.size.width-100, 10);
            self.playSlider.frame = CGRectMake(45, 11, self.playProgress.frame.size.width+10, 10);
            self.playButton.frame = CGRectMake(5, 5, 30, 30);
            self.fullScreenButton.frame = CGRectMake(self.barView.frame.size.width - 40, 5, 30, 30);
            self.totalTimeLabel.frame = CGRectMake(self.barView.frame.size.width-100, 25, 50, 10);
        }];

    }
}

//UISlider基本设置
- (void)customVideoSlider:(CMTime)duration {

    self.playSlider.maximumValue = CMTimeGetSeconds(duration);
    self.playSlider.minimumValue = 0.0;
}

//拖动进度条到任何位置播放
- (void)sliderAction:(UISlider *)slider {

    if (self.player.rate == 0) {
        [self.player seekToTime:CMTimeMake((int)slider.value*10, 10.0)];
        [self.player play];
        [self.playButton setImage:[UIImage imageNamed:@"pause_64.png"] forState:UIControlStateNormal];
    } else if(self.player.rate == 1) {
        [self.player pause];
        [self.player seekToTime:CMTimeMake((int)slider.value*10, 10.0)];
        [self.player play];
        [self.playButton setImage:[UIImage imageNamed:@"pause_64.png"] forState:UIControlStateNormal];
    }
}

@end

Demo :http://download.csdn.net/detail/yangxinca/8949003

2015-07-31 11:33:37 yangxinca 阅读数 1326

目录

  1. 导入框架
    导入

NSString *str = @"http://vmovier.qiniudn.com/559b918dbf717.mp4";
NSString *urlStr =[str stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];//链接接口中的汉字会导致请求失败
NSURL *url=[NSURL URLWithString:urlStr];
AVPlayerItem *playerItem=[AVPlayerItem playerItemWithURL:url];//主要创建方法,创建媒体资源管理对象
AVPlayer *player = [AVPlayer playerWithPlayerItem:playerItem];//根据媒体资源管理对象创建AVPlayer对象
AVPlayerLayer *playerLayer = [AVPlayerLayer playerLayerWithPlayer:player];//AVPlayer要显示视频,必须创建一个播放器层AVPlayerLayer用于展示

(2)视频的播放、暂停功能。
这也是最基本的功能,AVPlayer对应着两个方法play、pause来实现。
[player play];//播放
[player pause];//暂停
通常我们会需要得知当前的播放状态来进行主动地改变播放状态,问题的关键是如何判断当前视频是否在播放。很不幸的是AVPlayer没有这样的状态属性,但是很幸运的是我们可以通过判断播放器的播放速度来获得播放状态。AVPlayer类包含一个极其重要的属性:

**@property (nonatomic) float rate;**

其代码注释为:/* indicates the current rate of playback; 0.0 means “stopped”, 1.0 means “play at the natural rate of the current item” */
解释为:”表示当前的播放速度;0表示“停止”,1表示“在当前的PlayerItem下以正常速率播放”。
通过监控这个属性,可以间接获取当前播放状态,获取这个属性值对于视频播放器的功能完善具有不可代替的作用。

(3)播放进度及缓存进度
MPMoviePlayerController拥有自己的进度条,AVPlayer想要要展示播放进度就没有那么简单了。视频播放器通常是使用通知来获得播放器的当前状态,媒体加载状态等,但是无论是AVPlayer还是AVPlayerItem(AVPlayer有一个属性currentItem是AVPlayerItem类型,表示当前播放的视频对象)都无法获得这些信息。当然AVPlayerItem是有通知的,但是对于获得播放状态和加载状态有用的通知只有一个:播放完成通知AVPlayerItemDidPlayToEndTimeNotification。
在播放视频时,特别是播放网络视频往往需要知道视频加载情况、缓冲情况、播放情况,这些信息可以通过KVO监控AVPlayerItem的status、loadedTimeRanges属性来获得。当AVPlayerItem的status属性为AVPlayerStatusReadyToPlay是说明正在播放,只有处于这个状态时才能获得视频时长等信息;当loadedTimeRanges的改变时(每缓冲一部分数据就会更新此属性)可以获得本次缓冲加载的视频范围(包含起始时间、本次加载时长),这样一来就可以实时获得缓冲情况。然后就是依靠AVPlayer的

- (id)addPeriodicTimeObserverForInterval:(CMTime)interval queue:(dispatch_queue_t)queue usingBlock:(void (^)(CMTime time))block

方法获得播放进度,这个方法会在设定的时间间隔内定时更新播放进度,通过time参数通知客户端。有了这些视频信息,播放进度就不成问题了,事实上通过这些信息就算是平时看到的其他播放器的缓冲进度显示以及拖动播放的功能也可以顺利的实现。

  1. 具体实现
    具体实现的效果图:
    exp_1
    exp_2

具体代码:
(注:自知自己水平有限,代码优化处理方面做的也不是太好,但是幸好实现效果勉强可以,请各位读者勿喷就好)
(1)ViewController里的调用情况:

ViewController.h文件

#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
#import "AVPlayer_S.h"

@interface ViewController : UIViewController
**//AVPlayer_S 为我自己创建的类,继承于UIView,用于承载AVPlayer**
@property (nonatomic, retain) AVPlayer_S *myVideoPlayer;
@end

ViewController.m文件

#import "ViewController.h"
@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];    
    self.view.backgroundColor = [UIColor whiteColor];

    //AVPlayer_S 为我自己创建的类,继承于UIView,用于承载AVPlayer,此处用了固定的frame,自己在使用时可以根据需要设定具体的frame
    self.myVideoPlayer = [[AVPlayer_S alloc] initWithFrame:CGRectMake(20, 60, 335, 280) videoURL:@"http://vmovier.qiniudn.com/559b918dbf717.mp4"];

    [self.view addSubview:self.myVideoPlayer];
}
- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
@end

AVPlayer_S.h文件

 #import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
#import "MBProgressHUD.h"//为视频添加缓冲时候的菊花旋转,此第三方可以再Github上下载,直接搜索MBProgressHUD即可下载使用,假如你不想用可以把与之相关的全部删除
//附加MBProgressHUD的下载地址:https://github.com/jdg/MBProgressHUD

@interface AVPlayer_S : UIView<MBProgressHUDDelegate>

@property (nonatomic, retain) AVPlayer *player;//AVPlayer对象
@property (nonatomic, retain) AVPlayerLayer *playerLayer;//视频播放器的layer层视图,若不明白为何创建,请看目录2.AVPlayer类介绍

@property (nonatomic, retain) NSString *video_url;//接收视频连接
@property (nonatomic, assign) CMTime totalTime;//记录视频总时长
@property (nonatomic, assign) BOOL hidenBar;//视频UI控件是否显示的标识
@property (nonatomic, assign) BOOL didPlay;//缓冲结束立即播放的标识
@property (nonatomic, assign) BOOL isFullScreen;//全屏状态标识
@property (nonatomic, assign) CGRect originalFrame;//记录播放器原始frame

@property (nonatomic, retain) UIImageView *backIamge;//背景图
@property (nonatomic, retain) UILabel *didTimeLabel;//显示已完成的播放时长
@property (nonatomic, retain) UILabel *totalTimeLabel;//显示视频的总播放时长
@property (nonatomic, retain) UIButton *playButton;//播放暂停按钮
@property (nonatomic, retain) UIProgressView *playProgress;//缓冲条
@property (nonatomic, retain) UISlider *playSlider;//播放进度条
@property (nonatomic, retain) UIButton *fullScreenButton;
@property (nonatomic, retain) UIView *barView;//承载上述控件的UIView
@property (nonatomic, retain) UIView *superView;//记录下全屏前的父视图,便于退出全屏后视频处在正确的位置

- (instancetype)initWithFrame:(CGRect)frame videoURL:(NSString *)url;//自定义初始化方法,只需要给定frame和视频连接即可返回一个具有该frame的承载视频播放器的UIView
- (void)removeNotification;//移除通知
- (void)removeObserverFromPlayerItem:(AVPlayerItem *)playerItem;//移除playerItem的观察者

@end

AVPlayer_S.m文件

#import "AVPlayer_S.h"
@implementation AVPlayer_S

//自定义初始化方法
- (instancetype)initWithFrame:(CGRect)frame videoURL:(NSString *)url
{
    self = [super initWithFrame:frame];
    if (self) {
        self.didPlay = NO;
        self.originalFrame = frame;
        self.hidenBar = NO;
        self.isFullScreen = NO;
        self.video_url = url;
        self.backgroundColor = [UIColor blackColor];
        [self creatView:frame];//创建UI视图
        [self createPlayer:self.originalFrame];//创建layer层,用于播放视频
        [MBProgressHUD showHUDAddedTo:self animated:YES];//添加小菊花,若不想用就删除与之相关的东西
    }

    return self;
}

- (void)createPlayer:(CGRect)frame {

    NSString *urlStr =[self.video_url stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    NSURL *url=[NSURL URLWithString:urlStr];
    //开辟线程执行AVPlayerItem的创建,若不开辟线程则会占用主线程,造成视图加载完成后暂时无法对页面进行操作的卡顿现象
    dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);
    dispatch_async(globalQueue, ^{
    //创建AVPlayerItem
         AVPlayerItem *playerItem=[AVPlayerItem playerItemWithURL:url];
        dispatch_async(dispatch_get_main_queue(), ^{
            if (playerItem) {
                if (!_player) {
                    _player = [AVPlayer playerWithPlayerItem:playerItem];
                    if (_player) {
                    //保证AVPlayer对象存在,然后再为其添加进度观察,以便获取当前播放进度
                        [self addProgressObserver];
                        //给AVPlayerItem添加监控(最重要的监控)
                        [self addObserverToPlayerItem:playerItem];
                    }
                }
            } else {
                UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"提示" message:@"无法连接到视频资源" delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"确定", nil];
                [alert show];
                [self removeFromSuperview];
            }

        });
    });
    //创建播放层(layer层)
    self.playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player];
    self.playerLayer.frame = CGRectMake(0, 0, frame.size.width, frame.size.height);
    [self.layer addSublayer:self.playerLayer];
}


#pragma mark - 通知
//添加通知

- (void)addNotification {
    //添加 播放结束 通知
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playbackFinished:) name:AVPlayerItemDidPlayToEndTimeNotification object:self.player.currentItem];
}

//移除所有通知
- (void)removeNotification {
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

// 播放完成通知执行的方法:移除播放视图及小菊花假如想执行此操作可以解除注释

- (void)playbackFinished:(NSNotification *)notification {
/*
    [MBProgressHUD hideHUDForView:self animated:YES];
    [self removeFromSuperview];
*/
}

#pragma mark - 监控
// 给播放器添加进度更新

- (void)addProgressObserver {

    UISlider *slider = self.playSlider;//获取当前滑动条
    __block AVPlayer_S *av_s = self;

    //这里设置每秒执行一次
    [self.player addPeriodicTimeObserverForInterval:CMTimeMake(1.0, 10.0) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {

        float current = CMTimeGetSeconds(time);//获取当前的播放时间

        if (current) {
            [slider setValue:current animated:YES];//实时设置滑动条当前值
            int currentTime = (int)current;
            int m = currentTime / 60;
            int s = currentTime % 60;
            NSString *strM = nil;
            NSString *strS = nil;
            if (m < 10) {
                strM = [NSString stringWithFormat:@"0%d", m];
            } else {
                strM = [NSString stringWithFormat:@"%d", m];
            }
            if (s < 10) {
                strS = [NSString stringWithFormat:@"0%d", s];
            } else {
                strS = [NSString stringWithFormat:@"%d", s];
            }
            //设置UILabel视图显示当前播放时长
            av_s.didTimeLabel.text = [NSString stringWithFormat:@"%@:%@", strM, strS];
        }

    }];
}
// 给AVPlayerItem添加监控

- (void)addObserverToPlayerItem:(AVPlayerItem *)playerItem {
    //监控状态属性,注意AVPlayer也有一个status属性,通过监控它的status也可以获得播放状态
    [playerItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
    //监控网络加载情况属性
    [playerItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];

}

//通知移除方法
- (void)removeObserverFromPlayerItem:(AVPlayerItem *)playerItem {
    [playerItem removeObserver:self forKeyPath:@"status"];
    [playerItem removeObserver:self forKeyPath:@"loadedTimeRanges"];
}

/*
 *  通过KVO监控播放器状态
 *  @param keyPath 监控属性
 *  @param object  监视器
 *  @param change  状态改变
 *  @param context 上下文
 */
 //通知所执行的方法
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {

    AVPlayerItem *playerItem = object;//获取监控对象
    if ([keyPath isEqualToString:@"status"]) {
        //获取status值
        AVPlayerStatus status = [[change objectForKey:@"new"] intValue];
        //暂停状态
        if (status == AVPlayerStatusReadyToPlay) {
            self.totalTime = playerItem.duration;//获取视频总时长
            [self customVideoSlider:playerItem.duration];//设置播放进度最大值
            int totalTime = (int)CMTimeGetSeconds(playerItem.duration);
            int m = totalTime / 60;
            int s = totalTime % 60;
            NSString *strM = nil;
            NSString *strS = nil;
            if (m < 10) {
                strM = [NSString stringWithFormat:@"0%d", m];
            } else {
                strM = [NSString stringWithFormat:@"%d", m];
            }
            if (s < 10) {
                strS = [NSString stringWithFormat:@"0%d", s];
            } else {
                strS = [NSString stringWithFormat:@"%d", s];
            }
            //设置视频时长的UILabel视图显示值
            self.totalTimeLabel.text = [NSString stringWithFormat:@"%@:%@", strM, strS];
        }
    } else if ([keyPath isEqualToString:@"loadedTimeRanges"]) {
    //播放状态
        [self addNotification];//添加播放完成通知
        NSArray *array = playerItem.loadedTimeRanges;
        CMTimeRange timeRange = [array.firstObject CMTimeRangeValue];//本次缓冲时间范围
        float startSeconds = CMTimeGetSeconds(timeRange.start);
        float durationSeconds = CMTimeGetSeconds(timeRange.duration);
        NSTimeInterval totalBuffer = startSeconds + durationSeconds;//缓冲总长度
        //设置缓存条
        [self.playProgress setProgress:totalBuffer/CMTimeGetSeconds(self.totalTime) animated:YES];
        float currentPlayTime =  CMTimeGetSeconds([self.player currentTime]);//获取当前播放时长
        //设定当缓冲总时长超过播放时长3秒时开始播放视频
        if (totalBuffer - currentPlayTime > 3.0) {
            if (!self.didPlay) {
                [self createPlayer:self.frame];
                [self.player play];
                [MBProgressHUD hideHUDForView:self animated:YES];
                [self bringSubviewToFront:self.barView];
                [self.playButton setImage:[UIImage imageNamed:@"pause_64"] forState:UIControlStateNormal];
                self.didPlay = YES;
            }
        } else {
            [self.player pause];
            [MBProgressHUD showHUDAddedTo:self animated:YES];
            self.didPlay = NO;
            [self.playButton setImage:[UIImage imageNamed:@"play_64"] forState:UIControlStateNormal];
        }

    }
}
//播放视图的创建
- (void)creatView:(CGRect)frame {

    self.backIamge = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, frame.size.width, frame.size.height)];
    self.backIamge.image = [UIImage imageNamed:@"back_Image.png"];

    self.barView = [[UIView alloc] initWithFrame:CGRectMake(0, frame.size.height-40, frame.size.width, 40)];
    self.barView.backgroundColor = [UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:0.5];

    self.playProgress = [[UIProgressView alloc] initWithFrame:CGRectMake(50, 15, self.barView.frame.size.width-100, 10)];
    [self.playProgress setProgressTintColor:[UIColor orangeColor]];

    self.playSlider = [[UISlider alloc] initWithFrame:CGRectMake(47, 11.2, self.playProgress.frame.size.width+10, 10)];
    [self.playSlider setThumbImage:[UIImage imageNamed:@"ball_16"] forState:UIControlStateNormal];

    self.didTimeLabel = [[UILabel alloc] initWithFrame:CGRectMake(50, 25, 50, 10)];
    self.didTimeLabel.font = [UIFont systemFontOfSize:14];
    self.totalTimeLabel = [[UILabel alloc] initWithFrame:CGRectMake(self.barView.frame.size.width-100, 25, 50, 10)];
    self.totalTimeLabel.font = [UIFont systemFontOfSize:14];
    //=================
    UIGraphicsBeginImageContextWithOptions((CGSize){ 2, 2 }, NO, 0.0f);
    UIImage *transparentImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    [self.playSlider setMinimumTrackTintColor:[UIColor blueColor]];
    [self.playSlider setMaximumTrackImage:transparentImage forState:UIControlStateNormal];
    //=================
    [self.playSlider addTarget:self action:@selector(sliderAction:) forControlEvents:UIControlEventValueChanged];

    self.playButton = [UIButton buttonWithType:UIButtonTypeCustom];
    self.playButton.frame = CGRectMake(5, 5, 30, 30);
    [self.playButton addTarget:self action:@selector(playButtonAction:) forControlEvents:UIControlEventTouchUpInside];
    [self.playButton setBackgroundColor:[UIColor clearColor]];
    [self.playButton setImage:[UIImage imageNamed:@"play_64.png"] forState:UIControlStateNormal];

    self.fullScreenButton = [UIButton buttonWithType:UIButtonTypeCustom];
    self.fullScreenButton.frame = CGRectMake(self.barView.frame.size.width - 40, 5, 30, 30);
    [self.fullScreenButton addTarget:self action:@selector(fullScreenButtonAction:) forControlEvents:UIControlEventTouchUpInside];
    [self.fullScreenButton setBackgroundColor:[UIColor clearColor]];
    [self.fullScreenButton setImage:[UIImage imageNamed:@"fullScreen_64.png"] forState:UIControlStateNormal];

    [self.barView addSubview:self.playButton];
    [self.barView addSubview:self.playProgress];
    [self.barView addSubview:self.playSlider];
    [self.barView addSubview:self.didTimeLabel];
    [self.barView addSubview:self.totalTimeLabel];
    [self.barView addSubview:self.fullScreenButton];
    [self addSubview:self.barView];

    UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapAction:)];
    [self addGestureRecognizer:tap];
}

- (void)tapAction:(UITapGestureRecognizer *)tap {

    if (self.hidenBar) {
        self.hidenBar = NO;
        [self bringSubviewToFront:self.barView];
    } else {
        self.hidenBar = YES;
    }
    self.barView.hidden = self.hidenBar;
}

- (void)playButtonAction:(UIButton *)button
{
    NSLog(@"%f", self.player.rate);
    if(self.player.rate == 0){ //说明是暂停
        [button setImage:[UIImage imageNamed:@"pause_64"] forState:UIControlStateNormal];
        self.backIamge.hidden = YES;
        if (self.didPlay) {
            [self.player play];
        }
    }else if(self.player.rate == 1){//正在播放
        [self.player pause];
        [button setImage:[UIImage imageNamed:@"play_64"] forState:UIControlStateNormal];
    }
}

//全屏设置
- (void)fullScreenButtonAction:(UIButton *)button {

    if (self.isFullScreen) {
        self.isFullScreen = NO;
        [self.fullScreenButton setImage:[UIImage imageNamed:@"fullScreen_64.png"] forState:UIControlStateNormal];

        [UIView animateWithDuration:0.2 animations:^{

            self.transform = CGAffineTransformRotate(self.transform, -M_PI_2);

            self.frame = self.originalFrame;
            [self.playerLayer removeFromSuperlayer];
            [self createPlayer:self.originalFrame];

            self.barView.frame = CGRectMake(0, self.originalFrame.size.height-40, self.originalFrame.size.width, 40);
            [self bringSubviewToFront:self.barView];

            self.center = CGPointMake(self.originalFrame.origin.x + self.originalFrame.size.width/2.0, self.originalFrame.origin.y + self.originalFrame.size.height/2.0);

            self.backIamge.frame = CGRectMake(0, 0, self.originalFrame.size.width, self.originalFrame.size.height);
            self.playProgress.frame = CGRectMake(50, 15, self.barView.frame.size.width-100, 10);
            self.playSlider.frame = CGRectMake(45, 11, self.playProgress.frame.size.width+10, 10);
            self.playButton.frame = CGRectMake(5, 5, 30, 30);
            self.fullScreenButton.frame = CGRectMake(self.barView.frame.size.width - 40, 5, 30, 30);

            [self.superView addSubview:self];
        }];

    } else {
        self.isFullScreen = YES;
        self.superView = self.superview;

        [self.fullScreenButton setImage:[UIImage imageNamed:@"exitFullScreen_64.png"] forState:UIControlStateNormal];
        [self.window addSubview:self];
        [self.window bringSubviewToFront:self];

        [UIView animateWithDuration:0.2 animations:^{
            self.frame = CGRectMake(20, 0, [UIScreen mainScreen].bounds.size.height-20, [UIScreen mainScreen].bounds.size.width);
            [self.playerLayer removeFromSuperlayer];

            [self createPlayer:self.frame];

            self.barView.frame = CGRectMake(0, [UIScreen mainScreen].bounds.size.width-40, [UIScreen mainScreen].bounds.size.height-20, 40);
            [self bringSubviewToFront:self.barView];
            self.backIamge.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.height-20, [UIScreen mainScreen].bounds.size.width);

            self.transform = CGAffineTransformRotate(self.transform, M_PI_2);
            self.center = CGPointMake([UIScreen mainScreen].bounds.size.width/2.0, ([UIScreen mainScreen].bounds.size.height+20)/2.0);
            self.playProgress.frame = CGRectMake(50, 15, self.barView.frame.size.width-100, 10);
            self.playSlider.frame = CGRectMake(45, 11, self.playProgress.frame.size.width+10, 10);
            self.playButton.frame = CGRectMake(5, 5, 30, 30);
            self.fullScreenButton.frame = CGRectMake(self.barView.frame.size.width - 40, 5, 30, 30);
            self.totalTimeLabel.frame = CGRectMake(self.barView.frame.size.width-100, 25, 50, 10);
        }];

    }
}

//UISlider基本设置
- (void)customVideoSlider:(CMTime)duration {

    self.playSlider.maximumValue = CMTimeGetSeconds(duration);
    self.playSlider.minimumValue = 0.0;
}

//拖动进度条到任何位置播放
- (void)sliderAction:(UISlider *)slider {

    if (self.player.rate == 0) {
        [self.player seekToTime:CMTimeMake((int)slider.value*10, 10.0)];
        [self.player play];
        [self.playButton setImage:[UIImage imageNamed:@"pause_64.png"] forState:UIControlStateNormal];
    } else if(self.player.rate == 1) {
        [self.player pause];
        [self.player seekToTime:CMTimeMake((int)slider.value*10, 10.0)];
        [self.player play];
        [self.playButton setImage:[UIImage imageNamed:@"pause_64.png"] forState:UIControlStateNormal];
    }
}

@end

Demo :http://download.csdn.net/detail/yangxinca/8949003

2016-04-26 21:40:00 weixin_34176694 阅读数 6

AVPlayer

在iOS开发中,播放视频通常有两种方式,一种是使用MPMoviePlayerController(需要导入MediaPlayer.Framework),还有一种是使用AVPlayer。AVPlay既可以用来播放音频也可以用来播放视频,AVPlay在播放音频方面可以直接用来播放网络上的音频。在使用AVPlay的时候我们需要导入AVFoundation.framework框架,再引入头文件#import<AVFound
ation/AVFoundation.h>。

AVPlayer视频播放

在开发中,单纯的使用AVPlayer类是无法现实视频的,要将视频添加到AVPlayerLayer中,这样才能将视频现实出来,所以我们先在@interface中添加一下属性

@property (strong, nonatomic)AVPlayer *myPlayer;//播放器
@property (strong, nonatomic)AVPlayerItem *item;//播放单元
@property (strong, nonatomic)AVPlayerLayer *playerLayer;//播放界面(layer)

我们简单介绍一下这三个属性之间的关系吧
首先我们之所以能够看到视频是因为AVPlayerLayer帮我们把视频呈现出来了,可以说是AVPlayerLayer就是一个视频播放器的载体,它负责需要播放的画面。用MVC比喻,就是AVPlayerLayer属于V层,负责对用户的呈现。从AVPlayerLayer的便利构造器方法中可以看出我们在创建一个AVPlayerLayer的时候需要一个AVPlayer类型的参数。所以在创建AVPlayerLayer的时候,我们需要先有一个AVPlayer,它用MVC来分类的话就相当于MVC中的C层,负责播放单元和播放界面的协调工作,我们在它的便利构造器方法中可以看到他需要我们传入一个AVPlayerItem也就是播放单元,所谓的播放单元就是给播放器提供了一个数据的来源,用MVC来类比的话,它就属于M层,在创建一个播放单元的时候,我们首先得需要一个网址。
综上所述,将一个视频播放出来可分为如下几个步骤。
第一步:首先我们需要一个播放的网址

    NSURL *mediaURL = [NSURL URLWithString:@"http://baobab.wdjcdn.com”]

第二步:初始化一个播放单元

 self.item = [AVPlayerItem playerItemWithURL:mediaURL];

第三步:初始化一个播放器对象

self.myPlayer = [AVPlayer playerWithPlayerItem:self.item];

第四步:初始化一个播放器的Layer

self.playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.myPlayer];
self.playerLayer.frame = CGRectMake(0, 0, self.view.bounds.size.width, 500);
[self.view.layer addSublayer:self.playerLayer];

第五步:开始播放

[self.myPlayer play];

只要顺序执行上面的五个步骤,就可以将一个视频播放到界面上。当然现在还只是能够播放了而已,要具体实现视频的快进快退功能,请继续往下看。

AVPlayer视频快进快退

播放进度 CMTime类型一般是用来表示视频或者动画的时间类型。CMTime对象的Value属性是用来得到当前视频或者动画一共有多少帧,timescale指的是每秒多少帧;timescale指的是每秒多少帧,value/timescale = 视频的总时长(秒)
视频的快进快退,首先我们声明了两个属性

@property (strong, nonatomic)UISlider *avSlider;//用来现实视频的播放进度,并且通过它来控制视频的快进快退。
@property (assign, nonatomic)BOOL isReadToPlay;//用来判断当前视频是否准备好播放。

在这里我们将上面前四个步骤封装到一个方法中,并且在这个方法中给播放单元也就是AVPlayerItem添加一个监听,来监听它的状态。

-(void)avPlayerMethod{
//构建播放网址
    NSURL *mediaURL = [NSURL URLWithString:@"http://baobab.wdjcdn.com/1455782903700jy.mp4"];
//构建播放单元
    self.item = [AVPlayerItem playerItemWithURL:mediaURL];
//构建播放器对象
    self.myPlayer = [AVPlayer playerWithPlayerItem:self.item];
//构建播放器的layer
    self.playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.myPlayer];
    self.playerLayer.frame = CGRectMake(0, 66, self.view.bounds.size.width, 300);
    [self.view.layer addSublayer:self.playerLayer];
    //通过KVO来观察status属性的变化,来获得播放之前的错误信息
    [self.item addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
}

KVO的回调方法

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:
(NSDictionary<NSString *,id> *)change context:(void *)context{
    if ([keyPath isEqualToString:@"status"]) {
        //取出status的新值
        AVPlayerItemStatus status = [change[NSKeyValueChangeNewKey]intValue];
        switch (status) {
            case AVPlayerItemStatusFailed:
                NSLog(@"item 有误");
                self.isReadToPlay = NO;
                break;
            case AVPlayerItemStatusReadyToPlay:
                NSLog(@"准好播放了");
                self.isReadToPlay = YES;
                self.avSlider.maximumValue = self.item.duration.value / self.item.duration.timescale;
                break;
            case AVPlayerItemStatusUnknown:
                NSLog(@"视频资源出现未知错误");
                self.isReadToPlay = NO;
                break;
            default:
                break;
        }
    }
    //移除监听(观察者)
    [object removeObserver:self forKeyPath:@"status"];
}

视频的播放按钮

    UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
    button.frame = CGRectMake(250, 600, 100, 100);
    button.backgroundColor = [UIColor redColor];
    [button setTitle:@"按钮" forState:UIControlStateNormal];
    [button addTarget:self action:@selector(playAction) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:button];

button的回调方法

- (void)playAction{
    if ( self.isReadToPlay) {
        [self.myPlayer play];
    }else{
        NSLog(@"视频正在加载中");
    }
}

创建一个进度条来控制视频的进度
使用懒加载的方式,将进度条添加到View上面

- (UISlider *)avSlider{
    if (!_avSlider) {
        _avSlider = [[UISlider alloc]initWithFrame:CGRectMake(0, 55, self.view.bounds.size.width, 30)];
        [self.view addSubview:_avSlider];
    }return _avSlider;
}

在viewDidLoad中个slider(进度条)添加一个回调。

[self.avSlider addTarget:self action:@selector(avSliderAction) forControlEvents:
UIControlEventTouchUpInside|UIControlEventTouchCancel|UIControlEventTouchUpOutside];

回调方法

- (void)avSliderAction{
    //slider的value值为视频的时间
    float seconds = self.avSlider.value;
    //让视频从指定的CMTime对象处播放。
    CMTime startTime = CMTimeMakeWithSeconds(seconds, self.item.currentTime.timescale);
    //让视频从指定处播放
    [self.myPlayer seekToTime:startTime completionHandler:^(BOOL finished) {
        if (finished) {
            [self playAction];
        }
    }];
}

以上就是实现一个视频的播放和快进快退的方法与步骤。

转载于:https://www.jianshu.com/p/746cec2c3759

没有更多推荐了,返回首页