大部分代码来源于一个Demo中的一个功能点, Demo已经给不知道地址了, 囧。
主要代码如下: 播放器组件类
//
// MZPlayView.m
// 视频播放器常规功能
//
// Created by yubo on 16/7/27.
//
#import "MZPlayView.h"
#import
#import
#import
/**
* Description
关于视频这一块的业务操作全部封装在这个里面。 对外就提供一个NSUrl属性。
*/
typedef NS_ENUM(NSInteger,PanDirection){
PanDirectionHorizontalMoved, //横向移动
PanDirectionVerticalMoved //纵向移动
};
@interface MZPlayView()
@property (nonatomic,strong) AVPlayer *player;
@property (nonatomic,strong) AVPlayerItem *playerItem;
@property (nonatomic,strong) AVPlayerLayer *playerLayer;
@property (weak, nonatomic) IBOutlet UIButton *backBtn;
@property (weak, nonatomic) IBOutlet UILabel *forwardLabel;
@property (weak, nonatomic) IBOutlet UILabel *currentTimeLabel;
@property (weak, nonatomic) IBOutlet UIProgressView *progressView;
@property (weak, nonatomic) IBOutlet UILabel *totalTimeLabel;
@property (weak, nonatomic) IBOutlet UIButton *fullScreanBtn;
@property (weak, nonatomic) IBOutlet UISlider *sliderView;
@property (weak, nonatomic) IBOutlet UIButton *startBtn;
@property (weak, nonatomic) IBOutlet UIButton *lockBtn;
@property (nonatomic,assign) bool playViewFlag; // 隐藏和显示标识
@property (nonatomic,strong) NSTimer *timer;
@property (nonatomic,assign) bool lockFlag; // 锁标识
@property (nonatomic,assign) PanDirection panDirection;
@property (nonatomic, assign) BOOL isVolume; // 是否调节音量
@property (nonatomic, strong) UISlider *volumeViewSlider;
@property (nonatomic, assign) CGFloat sumTime; // 用来保存的总时长
@end
@implementation MZPlayView
- (void)awakeFromNib{
[super awakeFromNib];
[_sliderView setThumbImage:[UIImage imageNamed:@"slider"] forState:UIControlStateNormal];
[self insertSubview:_progressView belowSubview:_sliderView];
// 滑块左边的颜色
_sliderView.minimumTrackTintColor = [UIColor orangeColor];
// 滑块右边的颜色
_sliderView.maximumTrackTintColor = [UIColor clearColor];
}
- (void)setUrl:(NSURL *)url{
_playerItem = [AVPlayerItem playerItemWithURL:url];
_player = [AVPlayer playerWithPlayerItem:_playerItem];
_playerLayer = [AVPlayerLayer playerLayerWithPlayer:_player];
[self.layer insertSublayer:_playerLayer atIndex:0];
[_player play];
_forwardLabel.hidden = YES;
// 添加播完通知
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(moviePlayDidEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:_player.currentItem];
// 添加手势
[self createGesture];
//获取系统音量
[self configureVolume];
// 添加事件
[self sliderAddEvent];
// 通过定时器获取 时间,
_timer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:@selector(playerTimerAction) userInfo:nil repeats:YES];
// 计算缓冲
[_playerItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];
[_playerItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
}
#pragma mark 滑动事件添加
- (void)sliderAddEvent{
// slider开始滑动事件
[_sliderView addTarget:self action:@selector(progressSliderTouchBegan:) forControlEvents:UIControlEventTouchDown];
// slider滑动中事件
[_sliderView addTarget:self action:@selector(progressSliderValueChanged:) forControlEvents:UIControlEventValueChanged];
// slider结束滑动事件
[_sliderView addTarget:self action:@selector(progressSliderTouchEnded:) forControlEvents:UIControlEventTouchUpInside];
}
// 开始滑动
- (void)progressSliderTouchBegan:(UISlider *)slider{
// 取消延迟
// [NSObject cancelPreviousPerformRequestsWithTarget:self];
}
// 开始变化
- (void)progressSliderValueChanged:(UISlider *)slider{
// 拖动改变视频播放进度
if (_player.status == AVPlayerItemStatusReadyToPlay) {
// 总秒数
CGFloat total = (CGFloat)_playerItem.duration.value / _playerItem.duration.timescale;
// 已经拖动到了现在的秒数了。
NSInteger dragedSeconds = floorf(total * slider.value);
//转换成CMTime才能给player来控制播放进度---这个类用来控制时间的。
CMTime dragedCMTime = CMTimeMake(dragedSeconds, 1);
[_player pause];
[_player seekToTime:dragedCMTime completionHandler:^(BOOL finished) {
[_player play];
}];
}
}
#pragma mark slider事件结束
- (void)progressSliderTouchEnded:(UISlider *)slider{
}
#pragma mark 重新布局 frame
- (void)layoutSubviews{
_playerLayer.frame = self.bounds;
};
#pragma mark 添加手势, 点击隐藏视频所有按钮
- (void)createGesture{
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(playViewHanlder:)];
tap.numberOfTapsRequired = 1;
[self addGestureRecognizer:tap];
}
#pragma mark 手势回调处理
- (void)playViewHanlder:(UITapGestureRecognizer *)recognizer{
if (!_lockFlag) {
if (recognizer.state == UIGestureRecognizerStateRecognized) {
if (!_playViewFlag) {
[self hiddlePlayView];
}else{
[self showPlayView];
}
}
}
}
#pragma mark 隐藏组件
- (void)hiddlePlayView{
[UIView animateWithDuration:0.5 animations:^{
_backBtn.alpha = 0;
_currentTimeLabel.alpha = 0;
_progressView.alpha = 0;
_totalTimeLabel.alpha = 0;
_fullScreanBtn.alpha = 0;
_sliderView.alpha = 0;
_startBtn.alpha = 0;
} completion:^(BOOL finished) {
_playViewFlag = !_playViewFlag;
}];
}
#pragma mark 显示组件
- (void)showPlayView{
[UIView animateWithDuration:0.5 animations:^{
_backBtn.alpha = 1;
_currentTimeLabel.alpha = 1;
_progressView.alpha = 1;
_totalTimeLabel.alpha = 1;
_fullScreanBtn.alpha = 1;
_sliderView.alpha = 1;
_startBtn.alpha = 1;
} completion:^(BOOL finished) {
_playViewFlag = !_playViewFlag;
}];
}
#pragma mark 播放完后---通知处理
- (void)moviePlayDidEnd:(NSNotification *)notification{
}
#pragma mark 播放或者暂停
- (IBAction)startAction:(id)sender {
UIButton *btn = (UIButton *)sender;
if (btn.selected) {
[_player play];
}else{
[_player pause];
}
btn.selected = !btn.selected;
}
#pragma mark 全屏按钮事件
- (IBAction)fullScreanAction:(id)sender {
UIDeviceOrientation orientation = [UIDevice currentDevice].orientation;
UIInterfaceOrientation interfaceOrientation = (UIInterfaceOrientation)orientation;
switch (interfaceOrientation) {
case UIInterfaceOrientationPortraitUpsideDown: // 电池栏在下
[self interfaceOrientation:UIInterfaceOrientationLandscapeRight];
break;
case UIInterfaceOrientationPortrait: // 电池栏在上
[self interfaceOrientation:UIInterfaceOrientationLandscapeRight];
break;
case UIInterfaceOrientationLandscapeLeft: // 电池栏在右边
[self interfaceOrientation:UIInterfaceOrientationPortrait];
break;
case UIInterfaceOrientationLandscapeRight: // 电池栏左边
[self interfaceOrientation:UIInterfaceOrientationPortrait];
break;
default:
break;
}
}
#pragma mark 强制转屏相关
- (void)interfaceOrientation:(UIInterfaceOrientation)orientation{
// arc下
if ([[UIDevice currentDevice] respondsToSelector:@selector(setOrientation:)]) {
SEL selector = NSSelectorFromString(@"setOrientation:");
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[UIDevice instanceMethodSignatureForSelector:selector]];
[invocation setSelector:selector];
[invocation setTarget:[UIDevice currentDevice]];
int val = orientation;
[invocation setArgument:&val atIndex:2];
[invocation invoke];
}
}
#pragma mark 通过定时器获取视频的时间问题
- (void)playerTimerAction{
if (_playerItem.duration.timescale != 0) {
_sliderView.maximumValue = 1;//音乐总共时长
_sliderView.value = CMTimeGetSeconds([_playerItem currentTime]) / (_playerItem.duration.value / _playerItem.duration.timescale);//当前进度
//duration 总时长
NSInteger durMin = (NSInteger)_playerItem.duration.value / _playerItem.duration.timescale;//总秒
NSInteger durSec = (NSInteger)_playerItem.duration.value / _playerItem.duration.timescale / 60;//总分钟
_totalTimeLabel.text = [NSString stringWithFormat:@"%02ld:%02ld",durSec,durMin];
//当前时长进度progress
NSInteger proMin = (NSInteger)CMTimeGetSeconds([_playerItem currentTime]);//当前秒
NSInteger proSec = (NSInteger)CMTimeGetSeconds([_playerItem currentTime]) / 60;//当前分
_currentTimeLabel.text = [NSString stringWithFormat:@"%02ld:%02ld", proSec, proMin];
}
}
#pragma mark 计算出总时间
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
if (object == _playerItem) {
if ([keyPath isEqualToString:@"status"]) { // 这里代表添加完成
if (_playerItem.status == AVPlayerItemStatusReadyToPlay) {
// 添加滑动手势
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(panDirection:)];
[self addGestureRecognizer:pan];
}
}else if([keyPath isEqualToString:@"loadedTimeRanges"]){
NSTimeInterval timeInterval = [self availableDuration];// 计算缓冲进度
CMTime duration = _playerItem.duration;
CGFloat totalDuration = CMTimeGetSeconds(duration);
[_progressView setProgress:timeInterval / totalDuration animated:NO];
}
}
}
#pragma mark 计算缓存
- (NSTimeInterval)availableDuration {
NSArray *loadedTimeRanges = [[_player currentItem] loadedTimeRanges];
CMTimeRange timeRange = [loadedTimeRanges.firstObject CMTimeRangeValue];// 获取缓冲区域
float startSeconds = CMTimeGetSeconds(timeRange.start);
float durationSeconds = CMTimeGetSeconds(timeRange.duration);
NSTimeInterval result = startSeconds + durationSeconds;// 计算缓冲总进度
return result;
}
#pragma mark 锁屏
- (IBAction)lockAction:(id)sender {
UIButton *btn = (UIButton *)sender;
btn.selected = !btn.selected;
_lockFlag = btn.selected;
if (btn.selected) {
[btn setImage:[UIImage imageNamed:@"lock-nor"] forState:UIControlStateNormal];
}else{
[btn setImage:[UIImage imageNamed:@"unlock-nor"] forState:UIControlStateNormal];
}
}
#pragma mark 平移手势
- (void)panDirection:(UIPanGestureRecognizer *)pan{
// 根据在Uiview的位置,调节音量或者亮度
CGPoint locationPoint = [pan locationInView:self];
NSLog(@"当前点的位置--->%@",NSStringFromCGPoint(locationPoint));
// 拖动的速度---->返回值就是你拖动时X和Y轴上的速度,速度是矢量,有方向
CGPoint veloctyPoint = [pan velocityInView:self];
NSLog(@"拖动的速度--->%@",NSStringFromCGPoint(veloctyPoint));
// 判断是垂直移动还是水平移动
switch (pan.state) {
case UIGestureRecognizerStateBegan:{ // 开始移动
CGFloat x = fabs(veloctyPoint.x);
CGFloat y = fabs(veloctyPoint.y);
if (x > y) { // 水平移动
_panDirection = PanDirectionHorizontalMoved;
CMTime time = _player.currentTime;
_sumTime = time.value/time.timescale;
NSLog(@"===%f",_sumTime);
}else if(x < y){ // 垂直移动
_panDirection = PanDirectionVerticalMoved;
if (locationPoint.x > self.bounds.size.width/2) { // 右边是控制音量
_isVolume = true;
}else{ // 左边是控制亮度
_isVolume = false;
}
}
}
break;
case UIGestureRecognizerStateChanged:{ // 正在移动
if (self.panDirection == PanDirectionHorizontalMoved) { // 水平
[self horizontalMoved:veloctyPoint.x]; // 水平移动的方法只要x方向的值
}else{ // 垂直
[self verticalMoved:veloctyPoint.y]; // 垂直移动方法只要y方向的值
}
}
break;
case UIGestureRecognizerStateEnded:{
NSLog(@"移动停止l......");
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// 隐藏视图
_forwardLabel.hidden = YES;
});
if (self.panDirection == PanDirectionHorizontalMoved) {
//转换成CMTime才能给player来控制播放进度
CMTime dragedCMTime = CMTimeMake(self.sumTime, 1);
[_player pause];
[_player seekToTime:dragedCMTime completionHandler:^(BOOL finish){
[_player play];
}];
// 把sumTime滞空,不然会越加越多
self.sumTime = 0;
}else{
}
}
break;
default:
break;
}
}
#pragma mark 垂直移动处理。
- (void)verticalMoved:(CGFloat)value{
if (_isVolume) { // 控制音量
// 更改系统的音量
self.volumeViewSlider.value -= value / 10000; // 越小幅度越小
// 这个就对手机有效果, 最大的值为 1 。
NSLog(@"声音----->%lf", self.volumeViewSlider.value);
}else{ // 调节亮度
[UIScreen mainScreen].brightness -= value / 10000;
NSString *brightness = [NSString stringWithFormat:@"亮度%.0f%%",[UIScreen mainScreen].brightness/1.0*100];
_forwardLabel.hidden = NO;
_forwardLabel.text = brightness;
}
}
//获取系统音量
- (void)configureVolume
{
// 系统声音控制
MPVolumeView *volumeView = [[MPVolumeView alloc] init];
_volumeViewSlider = nil;
for (UIView *view in [volumeView subviews]){
if ([view.class.description isEqualToString:@"MPVolumeSlider"]){
_volumeViewSlider = (UISlider *)view;
break;
}
}
// 使用这个category的应用不会随着手机静音键打开而静音,可在手机静音下播放声音
NSError *setCategoryError = nil;
BOOL success = [[AVAudioSession sharedInstance]
setCategory: AVAudioSessionCategoryPlayback
error: &setCategoryError];
if (!success) { /* handle the error in setCategoryError */ }
}
#pragma mark - pan水平移动的方法
- (void)horizontalMoved:(CGFloat)value{
// 快进快退的方法
NSString *style = @"";
if (value < 0) {
style = @"<<";
}
else if (value > 0){
style = @">>";
}
// 每次滑动需要叠加时间
self.sumTime += value / 200;
// 需要限定sumTime的范围
CMTime totalTime = self.playerItem.duration;
CGFloat totalMovieDuration = (CGFloat)totalTime.value/totalTime.timescale;
if (self.sumTime > totalMovieDuration) {
self.sumTime = totalMovieDuration;
}else if (self.sumTime < 0){
self.sumTime = 0;
}
// 当前快进的时间
NSString *nowTime = [self durationStringWithTime:(int)self.sumTime];
// 总时间
NSString *durationTime = [self durationStringWithTime:(int)totalMovieDuration];
// 给label赋值
_forwardLabel.text = [NSString stringWithFormat:@"%@ %@ / %@",style, nowTime, durationTime];
_forwardLabel.hidden = NO;
}
#pragma mark - 根据时长求出字符串
- (NSString *)durationStringWithTime:(int)time
{
// 获取分钟
NSString *min = [NSString stringWithFormat:@"%02d",time / 60];
// 获取秒数
NSString *sec = [NSString stringWithFormat:@"%02d",time % 60];
return [NSString stringWithFormat:@"%@:%@", min, sec];
}
@end
视图只设置了 6+ 的适配, 其他的没做处理。
调用
self.view.backgroundColor = [UIColor blackColor];
NSURL *url = [NSURL URLWithString:@"http://baobab.wdjcdn.com/14562919706254.mp4"];
// NSURL *url = [[NSBundle mainBundle] URLForResource:@"minion_01" withExtension:@"mp4"];
[_playView setUrl:url];
效果:
实现了, 暂停, 拖放,声音,亮度, 锁屏。 这一些基本简单操作。