欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

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。

ios开发-搭建自定义的视频播放器