ios开发-搭建自定义的视频播放器
程序员文章站
2023-03-28 09:49:08
本来有avplayer和mpplayerviewcontrller的。后者现在被弃用了,所以就用avplayerviewcontrller原生的开发
#import
@interface v...
本来有avplayer和mpplayerviewcontrller的。后者现在被弃用了,所以就用avplayerviewcontrller原生的开发
#import @interface viewcontroller : uiviewcontroller @property(nonatomic,retain)avplayerviewcontroller * avc; @property(nonatomic,retain)avplayersetview * setview; @interface viewcontroller () { avplayer * player; cgfloat _viewlength; float currenttime; bool isfullscreen; nsurl * videlurl; } @property(nonatomic,retain)avplayeritem * playitem; @end 首先。上面有一个setview,这个是我用来放置avplayerviewcontrller的view,相当于替代他的avplaylayer,这个setview里面用懒加载写了一个uiprogressview和uislider,两个label和一个uibutton, ![左边的button是暂停按钮,靠右的是progress和uislider用来显示网络加载视频的缓冲进度和滑动条,两个label用来显示当前播放时间和总时长](https://img.blog.csdn.net/20160808174043119) 首先,要定义好avplayer这个播放器的显示范围,他是由avplayerviewcontrller实例化生成的,那么对应的他就有自己的view,我们让这个view=自己写的view就可以了,还有当屏幕旋转时要设置好setview的各个控件的方向大小,别弄错了就行, 这里关于setview的控件初始化懒加载部分我就不写了,直接来主要的 -(void)initsomething{ [self.avc.view setframe:cgrectmake(0, 0, self.view.bounds.size.width, 300)]; [self.setview setframe:self.avc.view.frame]; [self.avc.view addsubview:self.setview]; [self.setview.progress addtarget:self action:@selector(changeprogress:) forcontrolevents:uicontroleventvaluechanged]; [self.setview.pause addtarget:self action:@selector(pausevideo:) forcontrolevents:uicontroleventtouchupinside]; videlurl=[nsurl urlwithstring:@"https://ys-l.ys168.com/566310237/ulvjgkn3k3n1k75hgo5l/music.mp4"]; nsstring * url1=[[nsbundle mainbundle]pathforresource:@"duanpian" oftype:@"mp4"]; //确定视频资源 一个视频用一个item self.playitem=[avplayeritem playeritemwithurl:[nsurl videlurl]]; //确定视频视频框架 player = [avplayer playerwithplayeritem: self.playitem]; [self addobserver]; self.avc.player=player; // 隐藏系统自带的进度条播放界面 self.avc.showsplaybackcontrols = no; [self.view addsubview:self.avc.view]; player.externalplaybackvideogravity=avlayervideogravityresizeaspectfill; } -(void)addobserver{ //获取视频信息,要用item添加kvo,编程的时候最好使用item 的status,会准确点。 [ self.playitem addobserver:self forkeypath:@"status" options:nskeyvalueobservingoptionnew context:nil]; [self.playitem addobserver:self forkeypath:@"loadedtimeranges" options:nskeyvalueobservingoptionnew context:nil]; } -(void)removevideokvo{ [self.playitem removeobserver:self forkeypath:@"status"]; [self.playitem removeobserver:self forkeypath:@"loadedtimeranges"]; } -(void)observevalueforkeypath:(nsstring *)keypath ofobject:(id)object change:(nsdictionary *)change context:(void *)context{ if ([keypath isequaltostring:@"status"]) { switch (self.playitem.status) { case avplayeritemstatusreadytoplay: { //视频总长度 _viewlength=self.playitem.duration.value*1/ self.playitem.duration.timescale; // 设置播放进度ui [self showplayeriteminformation:self.playitem]; [player play]; } break; case avplayeritemstatusfailed: break; default: break; } } if ([keypath isequaltostring:@"loadedtimeranges"]) { if (videlurl) { nstimeinterval timeinterval = [self availableduration];// 计算缓冲进度 nslog(@"time interval:%f",timeinterval); cmtime duration = self.playitem.duration; cgfloat totalduration = cmtimegetseconds(duration); self.setview.schedule.tracktintcolor=[uicolor graycolor]; self.setview.schedule.progress=timeinterval/totalduration; } } } -(void)showplayeriteminformation:(avplayeritem *)item{ __weak typeof(self) weakself = self; //设置检查频率,1s更新一次这个block cmtime time = cmtimemake(1.0, 1.0); [self.avc.player addperiodictimeobserverforinterval:time queue:dispatch_get_main_queue() usingblock:^(cmtime time) { //当前播放时间 currenttime =item.currenttime.value/item.currenttime.timescale; weakself.setview.progress.value=currenttime/_viewlength; //显示时间的俩label weakself.setview.goforward.text=[nsstring stringwithformat:@"%@",[weakself getvideolengthfromtimelength:item.duration.value*1/ item.duration.timescale]]; weakself.setview.backforward.text=[nsstring stringwithformat:@"%@",[weakself getstringfromcmtime:item.currenttime.value/item.currenttime.timescale]]; }]; } -(void)changeprogress:(uislider *)sender{ cgfloat currt= sender.value * _viewlength; [self.avc.player seektotime:cmtimemake(currt, 1)]; } //点击按钮暂停播放,再点击继续播放 bool ispause=no; -(void)pausevideo:(uibutton *)sender{ ispause=!ispause; if (ispause) { [self.avc.player pause]; }else{ [self.avc.player play]; } } #pragma mark 工具方法 //计算缓冲进度 - (nstimeinterval)availableduration { nsarray *loadedtimeranges = [[self.avc.player currentitem] loadedtimeranges]; cmtimerange timerange = [loadedtimeranges.firstobject cmtimerangevalue];// 获取缓冲区域 float startseconds = cmtimegetseconds(timerange.start); float durationseconds = cmtimegetseconds(timerange.duration); nstimeinterval result = startseconds + durationseconds;// 计算缓冲总进度 return result; } //将当前进度转换成分秒 - (nsstring *)getstringfromcmtime:(cgfloat)time { // float currenttimevalue = (cgfloat)time.value/time.timescale;//得到当前的播放时 nsdate * currentdate = [nsdate datewithtimeintervalsince1970:time]; nscalendar *calendar = [[nscalendar alloc] initwithcalendaridentifier:nscalendaridentifiergregorian]; nsinteger unitflags = nscalendarunithour | nscalendarunitminute | nscalendarunitsecond ; nsdatecomponents *components = [calendar components:unitflags fromdate:currentdate]; if (time >= 3600 ) { return [nsstring stringwithformat:@"%02ld:%02ld:%02ld",components.hour,components.minute,components.second]; } else { return [nsstring stringwithformat:@"%02ld:%02ld",components.minute,components.second]; }} //将总时长转换成时分秒 - (nsstring *)getvideolengthfromtimelength:(float)timelength { nsdate * date = [nsdate datewithtimeintervalsince1970:timelength]; nscalendar *calendar = [[nscalendar alloc] initwithcalendaridentifier:nscalendaridentifiergregorian]; nsinteger unitflags = nscalendarunithour | nscalendarunitminute | nscalendarunitsecond ; nsdatecomponents *components = [calendar components:unitflags fromdate:date]; if (timelength >= 3600 ) { return [nsstring stringwithformat:@"%02ld时:%02ld分:%02ld秒",components.hour,components.minute,components.second]; } else { return [nsstring stringwithformat:@"%02ld分:%02ld秒",components.minute,components.second]; } } //判断设备是否支持旋转 -(void)lisendevicerotated{ [[uidevice currentdevice]begingeneratingdeviceorientationnotifications]; [[nsnotificationcenter defaultcenter]addobserver:self selector:@selector(ondeviceorientationchange) name:uideviceorientationdidchangenotification object:nil]; } - (void)ondeviceorientationchange { uideviceorientation oriention = [uidevice currentdevice].orientation; uiinterfaceorientation interfaceoriention = (uiinterfaceorientation)oriention; switch (interfaceoriention) { case uiinterfaceorientationunknown: nslog(@"位置方向"); break; case uiinterfaceorientationportrait: nslog(@"第0个旋转方向---电池栏在上"); [self backorientationportrait]; break; case uiinterfaceorientationportraitupsidedown: nslog(@"第3个旋转方向---电池栏在下"); [self backorientationportrait]; break; case uiinterfaceorientationlandscapeleft: [self setdeviceorientationlandscapeleft]; nslog(@"第2个旋转方向---电池栏在右"); break; case uiinterfaceorientationlandscaperight: [self setdeviceorientationlandscaperight]; nslog(@"第1个旋转方向---电池栏在左"); break; default: break; } } //隐藏navigation tabbar 电池栏 -(bool)prefersstatusbarhidden{ return yes; } //返回小屏幕 -(void)backorientationportrait{ [uiview animatewithduration:.5f animations:^{ [self.avc.view setframe:cgrectmake(0, 0, self.view.bounds.size.width, 300)]; [self.setview setframe:self.avc.view.frame]; [self.avc.view settransform:cgaffinetransformidentity]; }completion:^(bool finished) { }]; } -(void)setdeviceorientationlandscaperight{ //全屏(横屏frame计算) cgfloat height = [[uiscreen mainscreen] bounds].size.width; cgfloat width = [[uiscreen mainscreen] bounds].size.height; cgrect frame = cgrectmake(0, 0, height, width); [uiview animatewithduration:0.3f animations:^{ self.avc.view.frame = frame; self.setview.frame=frame; // [self.avc.view settransform:cgaffinetransformmakerotation(m_pi)]; } completion:^(bool finished) { }]; } - (void)setdeviceorientationlandscapeleft { //全屏(横屏frame计算) cgfloat height = [[uiscreen mainscreen] bounds].size.width; cgfloat width = [[uiscreen mainscreen] bounds].size.height; cgrect frame = cgrectmake(0, 0, height, width); [uiview animatewithduration:0.3f animations:^{ self.avc.view.frame = frame; self.setview.frame=frame; // [self.avc.view settransform:cgaffinetransformmakerotation(-m_pi)]; } completion:^(bool finished) { }]; } 到这里 就可以播放了,没怎么太完善功能,只是初学用的,其中[self.avc.player addperiodictimeobserverforinterval:time queue:dispatch_get_main_queue() usingblock:^(cmtime time) { //当前播放时间 currenttime =item.currenttime.value/item.currenttime.timescale; weakself.setview.progress.value=currenttime/_viewlength; //显示时间的俩label weakself.setview.goforward.text=[nsstring stringwithformat:@"%@",[weakself getvideolengthfromtimelength:item.duration.value*1/ item.duration.timescale]]; weakself.setview.backforward.text=[nsstring stringwithformat:@"%@",[weakself getstringfromcmtime:item.currenttime.value/item.currenttime.timescale]]; }]; 这个点我觉得是重要的,cmtimemake(1,1)指的是1/1这个时间走一次block。