流媒体播放器

简介: 流媒体播放器

显示下载进度条,支持边下载边播放。想支持边下边播放需要把视频文件放到指定类型的服务器上,服务器不支持流媒体类型无法支持该功能。

支持整屏播放和缩小屏幕播放。

整屏播放示例:

    BGVideoPlayerContainerView *vpcView = [[BGVideoPlayerContainerView alloc]initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.height, [UIScreen mainScreen].bounds.size.width)];
    [self.view addSubview:vpcView];
    [vpcView updateWithWaitTime:[self.entity.watch_video_minutes integerValue] urlVideo:self.entity.part_course_video logo:nil playImage:[UIImage imageNamed:@"play_icon"] isShowPlayTime:NO isStartPlay:YES];

缩小局部播放代码示例:

-(BGVideoPlayerContainerView *)vpcView{
    if (!_vpcView) {
        _vpcView = [[BGVideoPlayerContainerView alloc]initWithFrame:CGRectMake(0, 0, sCommonUnitFullWidth(), 217.5*FULL_WIDTH/375)];
        [self.contentView addSubview:_vpcView];
    }
    return _vpcView;
}

[self.vpcView updateWithWaitTime:waitTime urlVideo:videoUrl logo:logo playImage:[UIImage imageNamed:@"play_icon"] isShowPlayTime:YES isStartPlay:NO];

BGVideoPlayerToolsView.h

#import <UIKit/UIKit.h>
#define kVideoPlayerMainBottomHeightShift  ({ float space = 0.0; if (@available(iOS 11.0, *)) space = (([[UIApplication sharedApplication] delegate].window.safeAreaInsets.bottom)); (space); })

@protocol BGVideoPlayerToolsViewDelegate <NSObject>

-(void)playButtonWithStates:(BOOL)state;

@end

@interface BGVideoPlayerToolsView : UIView

@property (nonatomic, strong) UIButton *bCheck;//播放暂停按钮
@property (nonatomic, strong) UISlider *progressSr;//进度条
@property (nonatomic, strong) UIProgressView *bufferPV;//缓冲条
@property (nonatomic, strong) UILabel *lTime;//时间进度和总时长
@property (nonatomic, strong) UIActivityIndicatorView * activityIndicator;

@property (nonatomic, weak) id<BGVideoPlayerToolsViewDelegate> delegate;

@end

BGVideoPlayerToolsView.m

#import "BGVideoPlayerToolsView.h"

@interface BGVideoPlayerToolsView ()

@end

@implementation BGVideoPlayerToolsView

-(instancetype)initWithFrame:(CGRect)frame{
    
    self = [super initWithFrame:frame];
    if (self) {
        self.backgroundColor = [UIColor colorWithWhite:0 alpha:0.01];
        [self createUI];//创建UI
    }
    return self;
    
}

#pragma mark - 创建UI
-(void)createUI{
    [self addSubview:self.bCheck];//开始暂停按钮
    [self addSubview:self.bufferPV];//缓冲条
    [self addSubview:self.progressSr];//创建进度条
    [self addSubview:self.lTime];//视频时间
    [self addSubview:self.activityIndicator];
}

#pragma mark - 视频时间
-(UILabel *)lTime{
    
    if (!_lTime) {
        _lTime = [UILabel new];
        _lTime.frame = CGRectMake(CGRectGetMaxX(_progressSr.frame) + 20, 0, self.frame.size.width - CGRectGetWidth(_progressSr.frame) - 40 - CGRectGetWidth(_bCheck.frame), self.frame.size.height);
        _lTime.text = @"00:00/00:00";
        _lTime.textColor = [UIColor whiteColor];
        _lTime.textAlignment = NSTextAlignmentCenter;
        _lTime.font = [UIFont systemFontOfSize:12];
        _lTime.adjustsFontSizeToFitWidth = YES;
    }
    return _lTime;
    
}

#pragma mark - 创建进度条
-(UISlider *)progressSr{
    
    if (!_progressSr) {
        _progressSr = [UISlider new];
        _progressSr.frame = CGRectMake(CGRectGetMinX(_bufferPV.frame) - 2, CGRectGetMidY(_bufferPV.frame) - 10, CGRectGetWidth(_bufferPV.frame) - 4+10, 20);
        _progressSr.maximumTrackTintColor = [UIColor clearColor];
        _progressSr.minimumTrackTintColor = [UIColor whiteColor];
        [_progressSr setThumbImage:[UIImage imageNamed:@"point"] forState:0];
    }
    return _progressSr;
    
}

#pragma mark - 缓冲条
-(UIProgressView *)bufferPV{
    
    if (!_bufferPV) {
        _bufferPV = [UIProgressView new];
        _bufferPV.frame = CGRectMake(CGRectGetMaxX(_bCheck.frame) + 20, CGRectGetMidY(_bCheck.frame) - 2, 200, 4);
        _bufferPV.trackTintColor = [UIColor grayColor];
        _bufferPV.progressTintColor = [UIColor cyanColor];
    }
    return _bufferPV;
    
}

-(UIActivityIndicatorView *)activityIndicator{
    
    if (!_activityIndicator) {
//        _activityIndicator = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:(UIActivityIndicatorViewStyleWhite)];
        _activityIndicator = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:(UIActivityIndicatorViewStyleGray)];
        
        //设置小菊花的frame
        _activityIndicator.frame= CGRectMake(80, 10, 20, 20);
        //设置小菊花颜色
        _activityIndicator.color = [UIColor redColor];
        //设置背景颜色
        _activityIndicator.backgroundColor = [UIColor clearColor];
        //刚进入这个界面会显示控件,并且停止旋转也会显示,只是没有在转动而已,没有设置或者设置为YES的时候,刚进入页面不会显示
        _activityIndicator.hidesWhenStopped = NO;
    }
    return _activityIndicator;
    
}

#pragma mark - 开始暂停按钮
-(UIButton *)bCheck{
    
    if (!_bCheck) {
        _bCheck = [UIButton new];
        _bCheck.frame = CGRectMake(50, 0, self.frame.size.height, self.frame.size.height);
        [_bCheck setImage:[UIImage imageNamed:@"pause"] forState:0];
        [_bCheck addTarget:self action:@selector(btnCheckSelect:) forControlEvents:UIControlEventTouchUpInside];
    }
    return _bCheck;
    
}

-(void)btnCheckSelect:(UIButton *)sender{
    
    sender.selected = !sender.isSelected;
    
    if (sender.selected) {
        [_bCheck setImage:[UIImage imageNamed:@"play"] forState:0];
    }else{
        [_bCheck setImage:[UIImage imageNamed:@"pause"] forState:0];
    }
    
    if ([_delegate respondsToSelector:@selector(playButtonWithStates:)]) {
        [_delegate playButtonWithStates:sender.selected];
    }
    
}

@end

BGVideoPlayerContainerView.h

#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN

@interface BGVideoPlayerContainerView : UIView

@property (nonatomic, strong) NSString *urlVideo;

@property (nonatomic, strong) UIImage *playImage;
//@property (nonatomic, strong) NSString *videoUrl;
@property (nonatomic, strong) NSString *logo;
@property (nonatomic, assign) NSInteger waitTime;
@property (nonatomic, assign) BOOL isShowPlayTime;
@property (nonatomic, assign) BOOL isStartPlay;

-(void)dealloc;

- (void)updateWithWaitTime:(NSInteger)waitTime urlVideo:(NSString *)urlVideo logo:(NSString *)logo playImage:(UIImage *)playImage isShowPlayTime:(BOOL)isShowPlayTime isStartPlay:(BOOL)isStartPlay;
@end

NS_ASSUME_NONNULL_END

BGVideoPlayerContainerView.m

#import "BGVideoPlayerContainerView.h"
#import <AVKit/AVKit.h>
#import <SDAutoLayout/SDAutoLayout.h>
#import <ReactiveCocoa/ReactiveCocoa.h>
#import <SDWebImage/SDWebImage.h>
#import "BITNoticeView.h"
#import "BGVideoPlayerToolsView.h"

@interface BGVideoPlayerContainerView ()<BGVideoPlayerToolsViewDelegate>

@property (nonatomic, strong) AVPlayer *player;
@property (nonatomic, strong) AVPlayerLayer *playerLayer;
//@property (nonatomic, strong) NetworkSpeedMonitor *speedMonitor;//网速监听
@property (nonatomic, strong) UILabel *speedTextLabel;//显示网速Label
@property (nonatomic, strong) BGVideoPlayerToolsView *vpToolsView;//工具条

@property (nonatomic, strong) id playbackObserver;
@property (nonatomic) BOOL buffered;//是否缓冲完毕
@property (nonatomic) BOOL isLoad;
@property (nonatomic) BOOL isStartBuffer;

@property (nonatomic, strong) UILabel *describeTitleLabel;
@property (nonatomic, strong) UIView *bigBackgroundView;
@property (nonatomic, strong) UIImageView *photoImageView;
@property (nonatomic, strong) UIImageView *iconImageView;
@property (nonatomic, strong) UIButton *playButton;
@property (nonatomic, assign) BOOL isPlay;
@property (nonatomic, assign) BOOL isAuditionOver;
@property (nonatomic, assign) float progress;
@end

@implementation BGVideoPlayerContainerView

设置播放地址
//-(void)setUrlVideo:(NSString *)urlVideo{
//
//    [self.player seekToTime:CMTimeMakeWithSeconds(0, NSEC_PER_SEC) toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero];
//    [self.player play];//开始播放视频
    [self updatePlayBtn];
    [self.speedMonitor startNetworkSpeedMonitor];//开始监听网速
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkSpeedChanged:) name:NetworkDownloadSpeedNotificationKey object:nil];
//    _urlVideo = urlVideo;
//    AVPlayerItem *item = [AVPlayerItem playerItemWithURL:[NSURL URLWithString:urlVideo]];
//    [self vpc_addObserverToPlayerItem:item];
//    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//        [self.player replaceCurrentItemWithPlayerItem:item];
//        [self vpc_playerItemAddNotification];
//    });
//
//}


//设置播放地址
-(void)startBuffer
{
    [self.player seekToTime:CMTimeMakeWithSeconds(0, NSEC_PER_SEC) toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero];
    [self.player play];//开始播放视频
//    [self updatePlayBtn];
//    [self.speedMonitor startNetworkSpeedMonitor];//开始监听网速
//    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkSpeedChanged:) name:NetworkDownloadSpeedNotificationKey object:nil];
//    _urlVideo = urlVideo;
    AVPlayerItem *item = [AVPlayerItem playerItemWithURL:[NSURL URLWithString:self.urlVideo]];
    [self vpc_addObserverToPlayerItem:item];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [self.player replaceCurrentItemWithPlayerItem:item];
        [self vpc_playerItemAddNotification];
    });
    self.isStartBuffer = YES;
    [self.vpToolsView.activityIndicator startAnimating];
}


-(instancetype)initWithFrame:(CGRect)frame{
    
    self = [super initWithFrame:frame];
    if (self) {
        
        self.backgroundColor = [UIColor blackColor];//[UIColor groupTableViewBackgroundColor];
        [self.layer addSublayer:self.playerLayer];
        [self addSubview:self.speedTextLabel];
        [self addSubview:self.vpToolsView];
        [self setupViewWithFrame:frame];
    }
    return self;
    
}
-(void)setupViewWithFrame:(CGRect)frame
{
    self.backgroundColor = [UIColor blackColor];
//    self.bigBackgroundView = [[UIView alloc]init];
//    self.bigBackgroundView.backgroundColor = [UIColor clearColor];
//    [self addSubview:self.bigBackgroundView];
    self.frame = frame;

    [self addSubview:self.photoImageView];
    [self addSubview:self.playButton];
    [self addSubview:self.iconImageView];
    [self addSubview:self.describeTitleLabel];
    [self unitsSdLayout];
    
//    @weakify(self);
//    [[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIApplicationDidEnterBackgroundNotification object:nil] subscribeNext:^(id x) {
//        @strongify(self);
//        [self.player pause];
//        self.isPlay = NO;
//        self.vpToolsView.hidden = !self.isStartPlay;
//        [self updatePlayBtn];
//        [[BITNoticeView currentNotice] showErrorNotice:@"试听结束"];
//    }];

//    [BGCommonCustomTimer sharedInstance].scanningTimerCallBack = ^{
//        @strongify(self);
//        if(self.waitTime >0)
//        {
//            long long timeInterval = (self.startTime + self.waitTime*60000 - (long long)([[NSDate date] timeIntervalSince1970]*1000 + [BITSingleObject sharedInstance].localServerDifferenceTime))/1000 -113;
//            NSLog(@"timeInterval:%lld, self.waitTime:%d", timeInterval, self.waitTime);
//            if(timeInterval <= 0)
//            {
                self.isNotFirstPlay = YES;
//                [self stopTime];
//            }
//            else if(timeInterval > self.waitTime*60000)
//            {
                self.isNotFirstPlay = YES;
//                [self stopTime];
//            }
//            else
//            {
                long long minute = timeInterval / 60;
                self.describeTitleLabel.text = [NSString stringWithFormat:@"%lld:%lld", minute, timeInterval % 60];
//
//            }
//        }
//        else
//        {
//            self.hidden = YES;
//        }
//    };
}

-(void)unitsSdLayout
{
//    self.bigBackgroundView.sd_layout
//    .topSpaceToView(self, 0)
//    .leftSpaceToView(self, 0)
//    .widthIs(self.frame.size.width)
//    .heightIs(self.frame.size.height);
    
    self.photoImageView.sd_layout
    .topSpaceToView(self, 0)
    .leftSpaceToView(self, 0)
    .widthIs(self.frame.size.width)
    .heightIs(self.frame.size.height);
    
    self.iconImageView.sd_layout
    .centerYEqualToView(self)
    .centerXEqualToView(self)
    .widthIs(80)
    .heightIs(80);
//    self.playButton.backgroundColor = [UIColor redColor];
    self.playButton.sd_layout
    .topSpaceToView(self, 0)
    .leftSpaceToView(self, 0)
    .widthIs(self.frame.size.width)
    .heightIs(self.frame.size.height-50);

    self.describeTitleLabel.sd_layout
    .topSpaceToView(self.iconImageView, 10)
    .leftSpaceToView(self, 15)
    .rightSpaceToView(self, 15)
    .heightIs(14);
//    [self.describeTitleLabel setSingleLineAutoResizeWithMaxWidth:200];
}

- (void)updateWithWaitTime:(NSInteger)waitTime urlVideo:(NSString *)urlVideo logo:(NSString *)logo playImage:(UIImage *)playImage isShowPlayTime:(BOOL)isShowPlayTime isStartPlay:(BOOL)isStartPlay
{
    if(self.waitTime > 0 && [self achiveStringWithWeb:urlVideo])
    {
        if(!self.isLoad)
        {
            [self.layer addSublayer:self.playerLayer];
            [self addSubview:self.speedTextLabel];
            [self addSubview:self.vpToolsView];
            self.isLoad = YES;
        }
    }
    self.waitTime = waitTime;
    self.logo = logo;
    self.playImage = playImage;
    self.isShowPlayTime = isShowPlayTime;
    self.isStartPlay = isStartPlay;
    self.isStartBuffer = NO;
    if([self achiveStringWithWeb:urlVideo])
    {
//        self.bigBackgroundView.hidden = isStartPlay;
        self.urlVideo = urlVideo;
        if(isStartPlay)
        {
            [self startBuffer];
            //            self.playButton.hidden = YES;
            self.photoImageView.hidden = YES;
            self.iconImageView.hidden = YES;
            self.describeTitleLabel.hidden = YES;
            self.isPlay = YES;
        }
        else
        {
            self.isPlay = NO;
            [self updatePlayBtn];
            [self.player pause];
        }
        self.vpToolsView.hidden = !isStartPlay;
    }
    else
    {
        self.vpToolsView.hidden = YES;
        self.isPlay = NO;
//        self.playButton.hidden = YES;
        self.photoImageView.hidden = YES;
        self.iconImageView.hidden = YES;
        self.describeTitleLabel.hidden = YES;
    }
}

- (BOOL)achiveStringWithWeb:(NSString *)infor
{
    if([infor isKindOfClass:[NSNull class]] || infor == nil || ![infor isKindOfClass:[NSString class]])
    {
        return NO;
    }
    NSString *emailRegex = @"[a-zA-z]+://.*";
    NSPredicate *emailTest = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", emailRegex];
    return [emailTest evaluateWithObject:infor];
}

-(void)setPlayImage:(UIImage *)playImage
{
    if(playImage)
    {
        _playImage = playImage;
        self.iconImageView.image = playImage;
        self.iconImageView.sd_layout
        .centerYEqualToView(self)
        .centerXEqualToView(self)
        .widthIs(self.playImage.size.width)
        .heightIs(self.playImage.size.width);
        [self.iconImageView updateLayout];
        [self.describeTitleLabel updateLayout];
    }
}

-(void)setIsShowPlayTime:(BOOL)isShowPlayTime
{
    _isShowPlayTime = isShowPlayTime;
    self.describeTitleLabel.hidden = !isShowPlayTime;
    if(isShowPlayTime)
    {
        if(self.waitTime == 10000000)
        {
            self.describeTitleLabel.hidden = YES;
        }
        else
        {
            self.describeTitleLabel.hidden = NO;
            self.describeTitleLabel.text = [NSString stringWithFormat:@"可试听%ld分钟", self.waitTime];
        }
        [self.iconImageView updateLayout];
        [self.describeTitleLabel updateLayout];
    }
}

-(void)setLogo:(NSString *)logo
{
    if([self achiveStringWithWeb:logo])
    {
        [self.photoImageView sd_setImageWithURL:[NSURL URLWithString:logo]];
//        self.photoImageView.hidden = NO;
        if([self achiveStringWithWeb:logo])
        {
//            [self.photoImageView sd_setImageWithURL:[NSURL URLWithString:logo]];
            self.photoImageView.hidden = NO;
        }
        else
        {
            self.photoImageView.hidden = YES;
        }
    }
    else
    {
        self.photoImageView.hidden = YES;
    }
    _logo = logo;
}

- (UILabel *)describeTitleLabel {

    if(!_describeTitleLabel)
    {
        _describeTitleLabel = [self createLabel];
        _describeTitleLabel.backgroundColor = [UIColor clearColor];
        _describeTitleLabel.text = @"可试听5分钟";//[NSString stringWithFormat:@"可试听%ld分钟", self.waitTime];
        _describeTitleLabel.font = [UIFont systemFontOfSize:14];
        _describeTitleLabel.textColor = [UIColor whiteColor];
        _describeTitleLabel.textAlignment = NSTextAlignmentCenter;
        _describeTitleLabel.hidden = YES;
    }

    return _describeTitleLabel;
}

- (UIButton *)playButton {
    
    if (!_playButton)
    {
        _playButton = [[UIButton alloc] init];
        [_playButton setTitle:@"" forState:UIControlStateNormal];
//        [_playButton setImage:[UIImage imageNamed:@"play_icon"] forState:UIControlStateNormal];
        _playButton.backgroundColor = [UIColor clearColor];
        _playButton.userInteractionEnabled = YES;
//        _playButton.hidden = YES;
        @weakify(self);
        [[_playButton rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) {
            @strongify(self);
            if(self.isAuditionOver)
            {
                [self.player pause];
                self.isPlay = NO;
                self.vpToolsView.hidden = !self.isStartPlay;
                [self updatePlayBtn];
                [[BITNoticeView currentNotice] showErrorNotice:@"试听结束"];
                return;
            }
            if (self.isPlay) {
                [self.player pause];
                self.isPlay = NO;
                self.vpToolsView.hidden = !self.isStartPlay;
            }else{
                self.vpToolsView.hidden = NO;
                if(!self.isStartBuffer)
                {
                    [self startBuffer];
                }
                else
                {
                    [self.player play];
                }
                self.isPlay = YES;
            }
            [self updatePlayBtn];
//            if(self.isNotFirstPlay || self.waitTime <= 0)
//            [self startPlay];
        }];
    }
    return _playButton;
}

- (UIImageView *)photoImageView {
    if(!_photoImageView)
    {
        _photoImageView = [self createImageView];
//        [_photoImageView.layer setMasksToBounds:YES];
        _photoImageView.contentMode = UIViewContentModeScaleAspectFit;//等比缩放把图片整体显示在ImageView中,所以可能会出现ImageView有空白部分                ;//等比缩放图片把整个ImageView填充满,所以可能会出现图片部分显示不出来
        _photoImageView.clipsToBounds = YES;
//        _photoImageView.contentMode = UIViewContentModeScaleAspectFill;//等比缩放图片把整个ImageView填充满,所以可能会出现图片部分显示不出来
        _photoImageView.hidden = NO;
        _photoImageView.backgroundColor = [UIColor clearColor];
        _photoImageView.userInteractionEnabled = YES;
    }
    return _photoImageView;
}
-(UILabel *)createLabel
{
    UILabel *label = [[UILabel alloc] init];
    label.backgroundColor = [UIColor clearColor];
    label.textAlignment = NSTextAlignmentLeft;
    label.font = [UIFont systemFontOfSize:14];
    label.textColor = [UIColor colorWithRed:51/255.0 green:51/255.0 blue:51/255.0 alpha:1];
    label.text = @"";
    return label;
}

- (UIImageView *)createImageView {
    UIImageView *imageView = [[UIImageView alloc] init];
    imageView.contentMode = UIViewContentModeScaleAspectFit;//等比缩放把图片整体显示在ImageView中,所以可能会出现ImageView有空白部分 UIViewContentModeScaleAspectFill;//等比缩放图片把整个ImageView填充满,所以可能会出现图片部分显示不出来
    imageView.clipsToBounds = YES;
    imageView.backgroundColor = [UIColor whiteColor];
    return imageView;
}
- (UIImageView *)iconImageView {
    if(!_iconImageView)
    {
        _iconImageView= [[UIImageView alloc] init];
        _iconImageView.contentMode = UIViewContentModeScaleAspectFit;//等比缩放把图片整体显示在ImageView中,所以可能会出现ImageView有空白部分 UIViewContentModeScaleAspectFill;//等比缩放图片把整个ImageView填充满,所以可能会出现图片部分显示不出来
        _iconImageView.clipsToBounds = YES;
        _iconImageView.backgroundColor = [UIColor clearColor];
        _iconImageView.image = [UIImage imageNamed:@"play_icon"];
//        [_iconImageView addCornerWithCornerRadius:75.0/2];
    }
    return _iconImageView;
}

-(void)updatePlayBtn
{
    if([self achiveStringWithWeb:self.urlVideo])
    {
        self.playButton.hidden = NO;
        if([self achiveStringWithWeb:self.logo])
        {
//            [self.photoImageView sd_setImageWithURL:[NSURL URLWithString:logo]];
            self.photoImageView.hidden = NO;
            self.photoImageView.hidden = self.isPlay;
        }
        else
        {
            self.photoImageView.hidden = YES;
        }
//        self.photoImageView.hidden = NO;
        self.iconImageView.hidden = self.isPlay;
        self.describeTitleLabel.hidden = self.isPlay;
//        [self.playButton setImage:[UIImage imageNamed:@"play_icon"] forState:UIControlStateNormal];
//        [self.photoImageView sd_setImageWithURL:[NSURL URLWithString:self.entity.pic]];
    }
    else
    {
        self.playButton.hidden = YES;
        self.photoImageView.hidden = YES;
        self.iconImageView.hidden = YES;
        self.describeTitleLabel.hidden = YES;
    }
}

//- (void)networkSpeedChanged:(NSNotification *)sender {
//    NSString *downloadSpped = [sender.userInfo objectForKey:NetworkSpeedNotificationKey];
//    self.speedTextLabel.text = downloadSpped;
//}

#pragma mark - 工具条
-(BGVideoPlayerToolsView *)vpToolsView{
    
    if (!_vpToolsView) {
        
        _vpToolsView = [[BGVideoPlayerToolsView alloc]initWithFrame:CGRectMake(0, CGRectGetHeight(self.frame) - (40+kVideoPlayerMainBottomHeightShift), CGRectGetWidth(self.frame), 40)];
        _vpToolsView.delegate = self;
//        _vpToolsView.progressSr.backgroundColor = [UIColor redColor];
        [_vpToolsView.progressSr addTarget:self action:@selector(vpc_sliderTouchBegin:) forControlEvents:UIControlEventTouchDown];
        [_vpToolsView.progressSr addTarget:self action:@selector(vpc_sliderValueChanged:) forControlEvents:UIControlEventValueChanged];
        [_vpToolsView.progressSr addTarget:self action:@selector(vpc_sliderTouchEnd:) forControlEvents:UIControlEventTouchUpInside];
        
    }
    return _vpToolsView;
    
}

-(void)playButtonWithStates:(BOOL)state{
    
//    if (state) {
//        [self.player pause];
//    }else{
//        [self.player play];
//    }
    
}

- (void)vpc_sliderTouchBegin:(UISlider *)sender {
    if(self.isAuditionOver)
    {
        [self.player pause];
        self.isPlay = NO;
        self.vpToolsView.hidden = !self.isStartPlay;
        [self updatePlayBtn];
        [[BITNoticeView currentNotice] showErrorNotice:@"试听结束"];
        return;
    }
    if (self.isPlay) {
        [self.player pause];
        self.isPlay = NO;
        self.vpToolsView.hidden = !self.isStartPlay;
    }else{
        self.vpToolsView.hidden = NO;
        if(!self.isStartBuffer)
        {
            [self startBuffer];
        }
        else
        {
            [self.player play];
        }
        self.isPlay = YES;
    }
    [self updatePlayBtn];
}

#pragma mark - 播放进度
//- (void)updateProgress:(UISlider *)slider{
//    [self.player seekToTime:CMTimeMakeWithSeconds(slider.value, self.player.currentItem.currentTime.timescale)];
//}

- (void)vpc_sliderValueChanged:(UISlider *)sender {

    NSTimeInterval currentTime = CMTimeGetSeconds(self.player.currentItem.duration) * _vpToolsView.progressSr.value;
    NSInteger currentMin = currentTime / 60;
    NSInteger currentSec = (NSInteger)currentTime % 60;
    _vpToolsView.lTime.text = [NSString stringWithFormat:@"%02ld:%02ld",currentMin,currentSec];
    NSLog(@"time:%@, _vpToolsView.progressSr.value:%f", [NSString stringWithFormat:@"%02ld:%02ld",currentMin,currentSec], _vpToolsView.progressSr.value);
//    if(self.isAuditionOver || currentMin >= self.waitTime)
//    {
//        if(!self.isAuditionOver)
//        {
//            [self.player pause];
//            self.isPlay = NO;
//            self.vpToolsView.hidden = !self.isStartPlay;
//            [self updatePlayBtn];
//            [[BITNoticeView currentNotice] showErrorNotice:@"试听结束"];
//            return;
//        }
        self.isAuditionOver = YES;
//        if (self.isPlay) {
//            [self.player pause];
//            self.isPlay = NO;
//            self.vpToolsView.hidden = !self.isStartPlay;
//        }else{
//            self.vpToolsView.hidden = NO;
//            if(!self.isStartBuffer)
//            {
//                [self startBuffer];
//            }
//            else
//            {
//                [self.player play];
//            }
//            self.isPlay = YES;
//        }
//        [self updatePlayBtn];
//    }
    
}

- (void)vpc_sliderTouchEnd:(UISlider *)sender {
    if(self.isAuditionOver)
    {
        [self.player pause];
        self.isPlay = NO;
        self.vpToolsView.hidden = !self.isStartPlay;
        [self updatePlayBtn];
        [[BITNoticeView currentNotice] showErrorNotice:@"试听结束"];
        return;
    }
    NSTimeInterval currentTime = CMTimeGetSeconds(self.player.currentItem.duration) * _vpToolsView.progressSr.value;
    NSInteger currentMin = currentTime / 60;
    NSInteger currentSec = (NSInteger)currentTime % 60;
    _vpToolsView.lTime.text = [NSString stringWithFormat:@"%02ld:%02ld",currentMin,currentSec];
    NSLog(@"time:%@, _vpToolsView.progressSr.value:%f", [NSString stringWithFormat:@"%02ld:%02ld",currentMin,currentSec], _vpToolsView.progressSr.value);
    if(self.isAuditionOver || currentMin >= self.waitTime)
    {
        [self.player pause];
        self.isPlay = NO;
        self.vpToolsView.hidden = !self.isStartPlay;
        [self updatePlayBtn];
        [[BITNoticeView currentNotice] showErrorNotice:@"试听结束"];
        return;
//        if(!self.isAuditionOver)
//        {
//            [self.player pause];
//            self.isPlay = NO;
//            self.vpToolsView.hidden = !self.isStartPlay;
//            [self updatePlayBtn];
//            [[BITNoticeView currentNotice] showErrorNotice:@"试听结束"];
//            return;
//        }
//        self.isAuditionOver = YES;

    }
    else if(self.progress <=  _vpToolsView.progressSr.value)
    {
//        if (self.isPlay) {
//            [self.player pause];
//            self.isPlay = NO;
//            self.vpToolsView.hidden = !self.isStartPlay;
//        }else{
            self.vpToolsView.hidden = NO;
            if(!self.isStartBuffer)
            {
                [self startBuffer];
            }
            else
            {
                [self.player play];
            }
            self.isPlay = YES;
//        }
        [self updatePlayBtn];
        return;
    }
    
    self.isPlay = YES;
    NSTimeInterval slideTime = CMTimeGetSeconds(self.player.currentItem.duration) * _vpToolsView.progressSr.value;
    if (slideTime == CMTimeGetSeconds(self.player.currentItem.duration)) {
        slideTime -= 0.5;
    }
    [self.player seekToTime:CMTimeMakeWithSeconds(slideTime, NSEC_PER_SEC) toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero];
//    [self.player play];
    self.vpToolsView.hidden = NO;
    if(!self.isStartBuffer)
    {
        [self startBuffer];
    }
    else
    {
        [self.player play];
    }
    self.isPlay = YES;
    [self updatePlayBtn];
    
}

//#pragma mark - 网速监听器
//- (NetworkSpeedMonitor *)speedMonitor {
//    if (!_speedMonitor) {
//        _speedMonitor = [[NetworkSpeedMonitor alloc] init];
//    }
//    return _speedMonitor;
//}

#pragma mark - 显示网速Label
- (UILabel *)speedTextLabel {
    
    if (!_speedTextLabel) {
        _speedTextLabel = [UILabel new];
        _speedTextLabel.frame = CGRectMake(0, 0, self.frame.size.width, 20);
        _speedTextLabel.textColor = [UIColor whiteColor];
        _speedTextLabel.font = [UIFont systemFontOfSize:12.0];
        _speedTextLabel.textAlignment = NSTextAlignmentCenter;
        _speedTextLabel.hidden = YES;
        _speedTextLabel.backgroundColor = [UIColor colorWithWhite:0 alpha:0.5];
    }
    return _speedTextLabel;
    
}

#pragma mark - AVPlayer
-(AVPlayer *)player{
    
    if (!_player) {
        _player = [[AVPlayer alloc] init];
        __weak typeof(self) weakSelf = self;
        // 每秒回调一次
        self.playbackObserver = [_player addPeriodicTimeObserverForInterval:CMTimeMake(1, 1) queue:NULL usingBlock:^(CMTime time) {
            [weakSelf vpc_setTimeLabel];
            NSTimeInterval totalTime = CMTimeGetSeconds(weakSelf.player.currentItem.duration);//总时长
            NSTimeInterval currentTime = time.value / time.timescale;//当前时间进度
            weakSelf.vpToolsView.progressSr.value = currentTime / totalTime;
        }];
    }
    return _player;
    
}

#pragma mark - AVPlayerLayer
-(AVPlayerLayer *)playerLayer{
    
    if (!_playerLayer) {
        _playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player];
        _playerLayer.frame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height);
    }
    return _playerLayer;
    
}

#pragma mark ---------华丽的分割线---------

#pragma mark - lTime
- (void)vpc_setTimeLabel {
    
    NSTimeInterval totalTime = CMTimeGetSeconds(self.player.currentItem.duration);//总时长
    NSTimeInterval currentTime = CMTimeGetSeconds(self.player.currentTime);//当前时间进度
    
    // 切换视频源时totalTime/currentTime的值会出现nan导致时间错乱
    if (!(totalTime >= 0) || !(currentTime >= 0)) {
        totalTime = 0;
        currentTime = 0;
    }
    
    NSInteger totalMin = totalTime / 60;
    NSInteger totalSec = (NSInteger)totalTime % 60;
    NSString *totalTimeStr = [NSString stringWithFormat:@"%02ld:%02ld",totalMin,totalSec];
    
    NSInteger currentMin = currentTime / 60;
    NSInteger currentSec = (NSInteger)currentTime % 60;
    NSString *currentTimeStr = [NSString stringWithFormat:@"%02ld:%02ld",currentMin,currentSec];
    
    _vpToolsView.lTime.text = [NSString stringWithFormat:@"%@/%@",currentTimeStr,totalTimeStr];
    NSLog(@"time:%@", [NSString stringWithFormat:@"%@/%@",currentTimeStr,totalTimeStr]);
    if(self.isAuditionOver || currentMin >= self.waitTime)
    {
        if(!self.isAuditionOver)
        {
            [[BITNoticeView currentNotice] showErrorNotice:@"试听结束"];
        }
        self.isAuditionOver = YES;
        if (self.isPlay) {
            [self.player pause];
            self.isPlay = NO;
            self.vpToolsView.hidden = !self.isStartPlay;
        }else{
            self.vpToolsView.hidden = NO;
            if(!self.isStartBuffer)
            {
                [self startBuffer];
            }
            else
            {
                [self.player play];
            }
            self.isPlay = YES;
        }
        [self updatePlayBtn];
    }
    
}

#pragma mark - 观察者
- (void)vpc_playerItemAddNotification {
    // 播放完成通知
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(vpc_playbackFinished:) name:AVPlayerItemDidPlayToEndTimeNotification object:self.player.currentItem];
}

-(void)vpc_playbackFinished:(NSNotification *)noti{
    [_vpToolsView.progressSr setValue:1.0];
    [self.player pause];
}

- (void)vpc_addObserverToPlayerItem:(AVPlayerItem *)playerItem {
    // 监听播放状态
    [playerItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
    // 监听缓冲进度
    [playerItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];
}

- (void)vpc_playerItemRemoveNotification {
    [[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:self.player.currentItem];
}

- (void)vpc_playerItemRemoveObserver {
    [self.player.currentItem removeObserver:self forKeyPath:@"status"];
    [self.player.currentItem removeObserver:self forKeyPath:@"loadedTimeRanges"];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
    if ([keyPath isEqualToString:@"status"]) {
        AVPlayerStatus status= [[change objectForKey:@"new"] intValue];
        if (status == AVPlayerStatusReadyToPlay) {
            self.isPlay = YES;
            [self vpc_setTimeLabel];
        }
    } else if ([keyPath isEqualToString:@"loadedTimeRanges"]) {
        NSArray *array = self.player.currentItem.loadedTimeRanges;
        CMTimeRange timeRange = [array.firstObject CMTimeRangeValue];//本次缓冲时间范围
        NSTimeInterval startSeconds = CMTimeGetSeconds(timeRange.start);//本次缓冲起始时间
        NSTimeInterval durationSeconds = CMTimeGetSeconds(timeRange.duration);//缓冲时间
        NSTimeInterval totalBuffer = startSeconds + durationSeconds;//缓冲总长度
        float totalTime = CMTimeGetSeconds(self.player.currentItem.duration);//视频总长度
        float progress = totalBuffer/totalTime;//缓冲进度
        NSLog(@"progress = %lf, !self.buffered:%d, (progress == 1.0):%d",progress, !self.buffered, (progress == 1.0));
        self.progress  = progress;
        
        //如果缓冲完了,拖动进度条不需要重新显示缓冲条
        if (!self.buffered) {
            if (fabs(1.0 - progress) <= 0.001) {
                self.buffered = YES;
                [self.vpToolsView.activityIndicator stopAnimating];
                self.vpToolsView.activityIndicator.hidden = YES;
            }
            [self.vpToolsView.bufferPV setProgress:progress];
        }
        NSLog(@"yon = %@",self.buffered ? @"yes" : @"no");
    }
}

- (void)dealloc {
    
//    [self.speedMonitor stopNetworkSpeedMonitor];
//    [[NSNotificationCenter defaultCenter] removeObserver:self name:NetworkDownloadSpeedNotificationKey object:nil];
//    
    [self.player removeTimeObserver:self.playbackObserver];
    [self vpc_playerItemRemoveObserver];
    [self.player replaceCurrentItemWithPlayerItem:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    
}

@end
相关实践学习
使用ROS创建VPC和VSwitch
本场景主要介绍如何利用阿里云资源编排服务,定义资源编排模板,实现自动化创建阿里云专有网络和交换机。
阿里云专有网络VPC使用教程
专有网络VPC可以帮助您基于阿里云构建出一个隔离的网络环境,并可以自定义IP 地址范围、网段、路由表和网关等;此外,也可以通过专线/VPN/GRE等连接方式实现云上VPC与传统IDC的互联,构建混合云业务。 产品详情:https://www.aliyun.com/product/vpc
目录
相关文章
|
缓存 API 开发工具
几款优秀的点播、RTSP/RTMP直播播放器介绍
介绍:Ijkplayer 是Bilibili发布的基于 FFplay 的轻量级 Android/iOS 视频播放器。实现了跨平台功能,API 易于集成;编译配置可裁剪,方便控制安装包大小;支持硬件加速解码,更加省电;提供 Android 平台下应用弹幕集成的解决方案。
1301 0
|
4月前
|
Linux 开发工具 图形学
Unity下实现跨平台的RTMP推流|轻量级RTSP服务|RTMP播放|RTSP播放低延迟解决方案
自2018年起,我们成功实现了Unity环境下的低延迟RTSP|RTMP播放,达到毫秒级延迟,获得业界广泛认可。现已覆盖Windows、Android、iOS与Linux平台的RTMP推送、轻量级RTSP服务及RTSP|RTMP播放。通过高效采集Unity窗口或摄像头数据,并利用原生SDK进行编码与推送,确保了数据传输的高速性。此外,播放器支持多路视频同时播放,适应不同分辨率,并保持长时间运行稳定。更多技术细节和技术博文,请参考相关链接。
265 1
|
4月前
|
编解码 网络协议 开发工具
Android平台RTSP|RTMP直播播放器技术接入说明
大牛直播SDK自2015年发布RTSP、RTMP直播播放模块,迭代从未停止,SmartPlayer功能强大、性能强劲、高稳定、超低延迟、超低资源占用。无需赘述,全自研内核,行业内一致认可的跨平台RTSP、RTMP直播播放器。本文以Android平台为例,介绍下如何集成RTSP、RTMP播放模块。
179 0
|
编解码 开发工具 Android开发
rtmp/rtsp直播播放器选择
相信大家在做rtmp、rtsp直播的时候,最大的困惑就是选个靠谱的播放器,直播的延迟,一定意义上说,90%的取决于播放器的好坏。
283 0
|
网络协议 算法 网络性能优化
【流媒体】推流与拉流简介
【流媒体】推流与拉流简介
511 0
|
7月前
|
存储 缓存 编解码
C++ 音视频流媒体浅谈
C++ 音视频流媒体浅谈
223 0
C++ 音视频流媒体浅谈
|
Web App开发 API
ZLMediaKit webrtc录像
ZLMediaKit webrtc录像
|
编解码 监控 开发工具
RTMP/RTSP直播播放器的几种选择
播放网络视频,通常解析库我们可以有多个选择,如FFMPEG,Daniulive SDK 或者 vitamio。
268 1
|
缓存 网络协议 应用服务中间件
流媒体及直播相关知识
流媒体及直播相关知识
193 0
|
编解码 网络协议 Android开发
Android平台RTMP|RTSP直播播放器功能进阶探讨
很多开发者在跟我聊天的时候,经常问我,为什么一个RTMP或RTSP播放器,你们需要设计那么多的接口,真的有必要吗?带着这样的疑惑,我们今天聊聊Android平台RTMP、RTSP播放器常规功能,如软硬解码设置、实时音量调节、实时快照、实时录像、视频view翻转和旋转、画面填充模式设定、解码后YUV、RGB数据回调等:
198 0