AVPlayer 视频播放器常规功能

//

//  MZPlayView.m

//  视频播放器常规功能

//

//  Created by yubo on 16/7/27.

//

#import "MZPlayView.h"

#import 

#import 

#import 

/**

*  Description

关于视频这一块的业务操作全部封装在这个里面。 对外就提供一个NSUrl属性。

*/

typedefNS_ENUM(NSInteger,PanDirection){

PanDirectionHorizontalMoved,//横向移动

PanDirectionVerticalMoved//纵向移动

};

@interfaceMZPlayView()

@property(nonatomic,strong)AVPlayer*player;

@property(nonatomic,strong)AVPlayerItem*playerItem;

@property(nonatomic,strong)AVPlayerLayer*playerLayer;

@property(weak,nonatomic) IBOutletUIButton*backBtn;

@property(weak,nonatomic) IBOutletUILabel*forwardLabel;

@property(weak,nonatomic) IBOutletUILabel*currentTimeLabel;

@property(weak,nonatomic) IBOutletUIProgressView*progressView;

@property(weak,nonatomic) IBOutletUILabel*totalTimeLabel;

@property(weak,nonatomic) IBOutletUIButton*fullScreanBtn;

@property(weak,nonatomic) IBOutletUISlider*sliderView;

@property(weak,nonatomic) IBOutletUIButton*startBtn;

@property(weak,nonatomic) IBOutletUIButton*lockBtn;

@property(nonatomic,assign)boolplayViewFlag;// 隐藏和显示标识

@property(nonatomic,strong)NSTimer*timer;

@property(nonatomic,assign)boollockFlag;// 锁标识

@property(nonatomic,assign) PanDirection panDirection;

@property(nonatomic, assign)BOOLisVolume;// 是否调节音量

@property(nonatomic,strong)UISlider*volumeViewSlider;

@property(nonatomic, assign) CGFloat sumTime;// 用来保存的总时长

@end

@implementationMZPlayView

- (void)awakeFromNib{

[superawakeFromNib];

[_sliderViewsetThumbImage:[UIImageimageNamed:@"slider"]forState:UIControlStateNormal];

[selfinsertSubview:_progressViewbelowSubview:_sliderView];

// 滑块左边的颜色

_sliderView.minimumTrackTintColor= [UIColororangeColor];

// 滑块右边的颜色

_sliderView.maximumTrackTintColor= [UIColorclearColor];

}

- (void)setUrl:(NSURL*)url{

_playerItem = [AVPlayerItemplayerItemWithURL:url];

_player = [AVPlayerplayerWithPlayerItem:_playerItem];

_playerLayer = [AVPlayerLayerplayerLayerWithPlayer:_player];

[self.layerinsertSublayer:_playerLayeratIndex:0];

[_playerplay];

_forwardLabel.hidden=YES;

// 添加播完通知

[[NSNotificationCenterdefaultCenter]addObserver:selfselector:@selector(moviePlayDidEnd:)name:AVPlayerItemDidPlayToEndTimeNotificationobject:_player.currentItem];

// 添加手势

[selfcreateGesture];

//获取系统音量

[selfconfigureVolume];

// 添加事件

[selfsliderAddEvent];

// 通过定时器获取 时间,

_timer = [NSTimerscheduledTimerWithTimeInterval:1.0ftarget:selfselector:@selector(playerTimerAction)userInfo:nilrepeats:YES];

// 计算缓冲

[_playerItemaddObserver:selfforKeyPath:@"loadedTimeRanges"options:NSKeyValueObservingOptionNewcontext:nil];

[_playerItemaddObserver:selfforKeyPath:@"status"options:NSKeyValueObservingOptionNewcontext:nil];

}

#pragma mark  滑动事件添加

- (void)sliderAddEvent{

// slider开始滑动事件

[_sliderViewaddTarget:selfaction:@selector(progressSliderTouchBegan:)forControlEvents:UIControlEventTouchDown];

// slider滑动中事件

[_sliderViewaddTarget:selfaction:@selector(progressSliderValueChanged:)forControlEvents:UIControlEventValueChanged];

// slider结束滑动事件

[_sliderViewaddTarget:selfaction:@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);

[_playerpause];

[_playerseekToTime:dragedCMTimecompletionHandler:^(BOOLfinished) {

[_playerplay];

}];

}

}

#pragma mark slider事件结束

- (void)progressSliderTouchEnded:(UISlider*)slider{

}

#pragma mark 重新布局 frame

- (void)layoutSubviews{

_playerLayer.frame=self.bounds;

};

#pragma mark 添加手势, 点击隐藏视频所有按钮

- (void)createGesture{

UITapGestureRecognizer*tap = [[UITapGestureRecognizeralloc]initWithTarget:selfaction:@selector(playViewHanlder:)];

tap.numberOfTapsRequired=1;

[selfaddGestureRecognizer:tap];

}

#pragma mark 手势回调处理

- (void)playViewHanlder:(UITapGestureRecognizer*)recognizer{

if(!_lockFlag) {

if(recognizer.state== UIGestureRecognizerStateRecognized) {

if(!_playViewFlag) {

[selfhiddlePlayView];

}else{

[selfshowPlayView];

}

}

}

}

#pragma mark 隐藏组件

- (void)hiddlePlayView{

[UIViewanimateWithDuration:0.5animations:^{

_backBtn.alpha=0;

_currentTimeLabel.alpha=0;

_progressView.alpha=0;

_totalTimeLabel.alpha=0;

_fullScreanBtn.alpha=0;

_sliderView.alpha=0;

_startBtn.alpha=0;

}completion:^(BOOLfinished) {

_playViewFlag = !_playViewFlag;

}];

}

#pragma mark 显示组件

- (void)showPlayView{

[UIViewanimateWithDuration:0.5animations:^{

_backBtn.alpha=1;

_currentTimeLabel.alpha=1;

_progressView.alpha=1;

_totalTimeLabel.alpha=1;

_fullScreanBtn.alpha=1;

_sliderView.alpha=1;

_startBtn.alpha=1;

}completion:^(BOOLfinished) {

_playViewFlag = !_playViewFlag;

}];

}

#pragma mark  播放完后---通知处理

- (void)moviePlayDidEnd:(NSNotification*)notification{

}

#pragma mark 播放或者暂停

- (IBAction)startAction:(id)sender {

UIButton*btn = (UIButton*)sender;

if(btn.selected) {

[_playerplay];

}else{

[_playerpause];

}

btn.selected= !btn.selected;

}

#pragma mark 全屏按钮事件

- (IBAction)fullScreanAction:(id)sender {

UIDeviceOrientation orientation = [UIDevicecurrentDevice].orientation;

UIInterfaceOrientation interfaceOrientation = (UIInterfaceOrientation)orientation;

switch(interfaceOrientation) {

caseUIInterfaceOrientationPortraitUpsideDown:// 电池栏在下

[selfinterfaceOrientation:UIInterfaceOrientationLandscapeRight];

break;

caseUIInterfaceOrientationPortrait:// 电池栏在上

[selfinterfaceOrientation:UIInterfaceOrientationLandscapeRight];

break;

caseUIInterfaceOrientationLandscapeLeft:// 电池栏在右边

[selfinterfaceOrientation:UIInterfaceOrientationPortrait];

break;

caseUIInterfaceOrientationLandscapeRight:// 电池栏左边

[selfinterfaceOrientation:UIInterfaceOrientationPortrait];

break;

default:

break;

}

}

#pragma mark  强制转屏相关

- (void)interfaceOrientation:(UIInterfaceOrientation)orientation{

// arc下

if([[UIDevicecurrentDevice]respondsToSelector:@selector(setOrientation:)]) {

SELselector = NSSelectorFromString(@"setOrientation:");

NSInvocation*invocation = [NSInvocationinvocationWithMethodSignature:[UIDeviceinstanceMethodSignatureForSelector:selector]];

[invocationsetSelector:selector];

[invocationsetTarget:[UIDevicecurrentDevice]];

intval = orientation;

[invocationsetArgument:&valatIndex:2];

[invocationinvoke];

}

}

#pragma mark 通过定时器获取视频的时间问题

- (void)playerTimerAction{

if(_playerItem.duration.timescale!=0) {

_sliderView.maximumValue=1;//音乐总共时长

_sliderView.value= CMTimeGetSeconds([_playerItemcurrentTime]) / (_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= [NSStringstringWithFormat:@"%02ld:%02ld",durSec,durMin];

//当前时长进度progress

NSInteger proMin = (NSInteger)CMTimeGetSeconds([_playerItemcurrentTime]);//当前秒

NSInteger proSec = (NSInteger)CMTimeGetSeconds([_playerItemcurrentTime]) /60;//当前分

_currentTimeLabel.text= [NSStringstringWithFormat:@"%02ld:%02ld", proSec,proMin];

}

}

#pragma mark 计算出总时间

- (void)observeValueForKeyPath:(NSString*)keyPathofObject:(id)objectchange:(NSDictionary *)changecontext:(voidvoid*)context{

if(object == _playerItem) {

if([keyPathisEqualToString:@"status"]) {// 这里代表添加完成

if(_playerItem.status== AVPlayerItemStatusReadyToPlay) {

// 添加滑动手势

UIPanGestureRecognizer*pan = [[UIPanGestureRecognizeralloc]initWithTarget:selfaction:@selector(panDirection:)];

[selfaddGestureRecognizer:pan];

}

}elseif([keyPathisEqualToString:@"loadedTimeRanges"]){

NSTimeInterval timeInterval = [selfavailableDuration];// 计算缓冲进度

CMTime  duration = _playerItem.duration;

CGFloat totalDuration = CMTimeGetSeconds(duration);

[_progressViewsetProgress:timeInterval / totalDurationanimated:NO];

}

}

}

#pragma mark  计算缓存

- (NSTimeInterval)availableDuration {

NSArray*loadedTimeRanges = [[_playercurrentItem]loadedTimeRanges];

CMTimeRange timeRange = [loadedTimeRanges.firstObjectCMTimeRangeValue];// 获取缓冲区域

floatstartSeconds = CMTimeGetSeconds(timeRange.start);

floatdurationSeconds = CMTimeGetSeconds(timeRange.duration);

NSTimeInterval result = startSeconds + durationSeconds;// 计算缓冲总进度

returnresult;

}

#pragma mark 锁屏

- (IBAction)lockAction:(id)sender {

UIButton*btn = (UIButton*)sender;

btn.selected= !btn.selected;

_lockFlag = btn.selected;

if(btn.selected) {

[btnsetImage:[UIImageimageNamed:@"lock-nor"]forState:UIControlStateNormal];

}else{

[btnsetImage:[UIImageimageNamed:@"unlock-nor"]forState:UIControlStateNormal];

}

}

#pragma mark   平移手势

- (void)panDirection:(UIPanGestureRecognizer*)pan{

// 根据在Uiview的位置,调节音量或者亮度

CGPoint locationPoint = [panlocationInView:self];

NSLog(@"当前点的位置--->%@",NSStringFromCGPoint(locationPoint));

// 拖动的速度---->返回值就是你拖动时X和Y轴上的速度,速度是矢量,有方向

CGPoint veloctyPoint = [panvelocityInView:self];

NSLog(@"拖动的速度--->%@",NSStringFromCGPoint(veloctyPoint));

// 判断是垂直移动还是水平移动

switch(pan.state) {

caseUIGestureRecognizerStateBegan:{// 开始移动

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);

}elseif(x < y){// 垂直移动

_panDirection = PanDirectionVerticalMoved;

if(locationPoint.x>self.bounds.size.width/2) {// 右边是控制音量

_isVolume =true;

}else{// 左边是控制亮度

_isVolume =false;

}

}

}

break;

caseUIGestureRecognizerStateChanged:{// 正在移动

if(self.panDirection== PanDirectionHorizontalMoved) {// 水平

[selfhorizontalMoved:veloctyPoint.x];// 水平移动的方法只要x方向的值

}else{// 垂直

[selfverticalMoved:veloctyPoint.y];// 垂直移动方法只要y方向的值

}

}

break;

caseUIGestureRecognizerStateEnded:{

NSLog(@"移动停止l......");

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(11* NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

// 隐藏视图

_forwardLabel.hidden=YES;

});

if(self.panDirection== PanDirectionHorizontalMoved) {

//转换成CMTime才能给player来控制播放进度

CMTime dragedCMTime = CMTimeMake(self.sumTime,1);

[_playerpause];

[_playerseekToTime:dragedCMTimecompletionHandler:^(BOOLfinish){

[_playerplay];

}];

// 把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{// 调节亮度

[UIScreenmainScreen].brightness-= value /10000;

NSString*brightness = [NSStringstringWithFormat:@"亮度%.0f%%",[UIScreenmainScreen].brightness/1.0*100];

_forwardLabel.hidden=NO;

_forwardLabel.text= brightness;

}

}

//获取系统音量

- (void)configureVolume

{

// 系统声音控制

MPVolumeView*volumeView = [[MPVolumeViewalloc]init];

_volumeViewSlider =nil;

for(UIView*view in [volumeViewsubviews]){

if([view.class.descriptionisEqualToString:@"MPVolumeSlider"]){

_volumeViewSlider = (UISlider*)view;

break;

}

}

// 使用这个category的应用不会随着手机静音键打开而静音,可在手机静音下播放声音

NSError*setCategoryError =nil;

BOOLsuccess = [[AVAudioSessionsharedInstance]

setCategory: AVAudioSessionCategoryPlayback

error: &setCategoryError];

if(!success) {/* handle the error in setCategoryError */}

}

#pragma mark - pan水平移动的方法

- (void)horizontalMoved:(CGFloat)value{

// 快进快退的方法

NSString*style =@"";

if(value <0) {

style =@"<<";

}

elseif(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;

}elseif(self.sumTime<0){

self.sumTime=0;

}

// 当前快进的时间

NSString*nowTime = [selfdurationStringWithTime:(int)self.sumTime];

// 总时间

NSString*durationTime = [selfdurationStringWithTime:(int)totalMovieDuration];

// 给label赋值

_forwardLabel.text= [NSStringstringWithFormat:@"%@ %@ / %@",style, nowTime,durationTime];

_forwardLabel.hidden=NO;

}

#pragma mark - 根据时长求出字符串

- (NSString*)durationStringWithTime:(int)time

{

// 获取分钟

NSString*min = [NSStringstringWithFormat:@"%02d",time /60];

// 获取秒数

NSString*sec = [NSStringstringWithFormat:@"%02d",time %60];

return[NSStringstringWithFormat:@"%@:%@", min,sec];

}

@end

视图只设置了   6+ 的适配, 其他的没做处理。

调用

self.view.backgroundColor = [UIColor blackColor];

[objc]view plaincopy

NSURL*url = [NSURLURLWithString:@"http://baobab.wdjcdn.com/14562919706254.mp4"];

NSURL*url = [[NSBundlemainBundle]URLForResource:@"minion_01"withExtension:@"mp4"];

[_playViewsetUrl:url];

效果:

实现了,  暂停, 拖放,声音,亮度, 锁屏。 这一些基本简单操作。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,319评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,801评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,567评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,156评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,019评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,090评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,500评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,192评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,474评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,566评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,338评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,212评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,572评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,890评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,169评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,478评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,661评论 2 335

推荐阅读更多精彩内容