停止AVPlayer并在缓存阶段立即播放其它视频闪退问题及解决方案

简介: 停止AVPlayer并在缓存阶段立即播放其它视频闪退问题及解决方案

1.清除KVO;

2.暂停;

3.清空缓存区;

4.从父视图移除播放器;

5.移除播放器的全部视图;

6.把播放器置为nil。

具体代码:


-(void)hiddenPlayer
{
    if(self.isPlay && self.player)
    {
        [self.player.currentItem  removeObserver:self forKeyPath:@"status"];
        [self.player.currentItem  removeObserver:self forKeyPath:@"loadedTimeRanges"];
        [self.player pause];
        self.currentPlayerItem = nil;
        [self.player replaceCurrentItemWithPlayerItem:nil];//清除播放缓存
        for (CALayer *layer in self.photoImageView.layer.sublayers) {
            if ([layer class] == [AVPlayerLayer class]) {
                [layer removeFromSuperlayer];
            }
        }
        [self.player removeAllSubView];
        self.photoImageView.hidden = YES;
        self.isPlay = NO;
        self.player = nil;
//                [self.playButton setImage:[UIImage imageNamed:@"播放"] forState:UIControlStateNormal];
        [self updatePlayBtn];
    }
}

相关声明:

#import <AVFoundation/AVFoundation.h>

@interface PPChooseSongHeadView()<SDCycleScrollViewDelegate>
@property (nonatomic, strong) UILabel *describeTitleLabel;
@property (nonatomic, strong) UILabel *idLabel;
@property (nonatomic, strong) SDCycleScrollView *imagesScrollView;
@property (nonatomic, strong) UIView *backgroundView;
@property (nonatomic, strong) UIImageView *photoImageView;
@property (nonatomic, strong) UIButton *playButton;
@property (nonatomic, assign) BOOL isPlay;
@property (nonatomic, strong) JCTBannerEntity *entity;
@property (nonatomic,strong)AVPlayer *player;//播放器对象
@property (nonatomic,strong)AVPlayerItem *currentPlayerItem;
@end
@interface PPChooseSongHeadView : BGBaseView

//@property (nonatomic, strong) UILabel *backLabel;
@property (nonatomic, strong) JCTProjectListEntity *model;
@property (nonatomic, strong) PPRoomCateListEntity *roomCateListEntity;

@property (nonatomic, copy) void(^endEditingBlock)(void);

- (instancetype)initWithFrame:(CGRect)frame;

-(void)hiddenPlayer;

@end

视频建立与播放,按钮显示相关代码:

//2.添加属性观察
- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context {
//    AVPlayerItem *playerItem = (AVPlayerItem *)object;
    if ([keyPath isEqualToString:@"status"]) {
        //获取playerItem的status属性最新的状态
        AVPlayerStatus status = [[change objectForKey:@"new"] intValue];
        switch (status) {
            case AVPlayerStatusReadyToPlay:{
//                //获取视频长度
//                CMTime duration = playerItem.duration;
//                //更新显示:视频总时长(自定义方法显示时间的格式)
//                self.totalNeedPlayTimeLabel.text = [self formatTimeWithTimeInterVal:CMTimeGetSeconds(duration)];
//                //开启滑块的滑动功能
//                self.sliderView.enabled = YES;
//                //关闭加载Loading提示
//                [self showaAtivityInDicatorView:NO];
                //开始播放视频
                [self.player play];
                break;
            }
            case AVPlayerStatusFailed:{//视频加载失败,点击重新加载
//                [self showaAtivityInDicatorView:NO];//关闭Loading视图
//                self.playerInfoButton.hidden = NO; //显示错误提示按钮,点击后重新加载视频
//                [self.playerInfoButton setTitle:@"资源加载失败,点击继续尝试加载" forState: UIControlStateNormal];
                [[BITNoticeView currentNotice] showErrorNotice:@"资源加载失败,请继续尝试加载"];
                [self hiddenPlayer];
                break;
            }
            case AVPlayerStatusUnknown:{
                NSLog(@"加载遇到未知问题:AVPlayerStatusUnknown");
                [self hiddenPlayer];
                break;
            }
            default:
                break;
        }
    } else if ([keyPath isEqualToString:@"loadedTimeRanges"]) {
//        //获取视频缓冲进度数组,这些缓冲的数组可能不是连续的
//        NSArray *loadedTimeRanges = playerItem.loadedTimeRanges;
//        //获取最新的缓冲区间
//        CMTimeRange timeRange = [loadedTimeRanges.firstObject CMTimeRangeValue];
//        //缓冲区间的开始的时间
//        NSTimeInterval loadStartSeconds = CMTimeGetSeconds(timeRange.start);
//        //缓冲区间的时长
//        NSTimeInterval loadDurationSeconds = CMTimeGetSeconds(timeRange.duration);
//        //当前视频缓冲时间总长度
//        NSTimeInterval currentLoadTotalTime = loadStartSeconds + loadDurationSeconds;
//        //NSLog(@"开始缓冲:%f,缓冲时长:%f,总时间:%f", loadStartSeconds, loadDurationSeconds, currentLoadTotalTime);
//        //更新显示:当前缓冲总时长
//        _currentLoadTimeLabel.text = [self formatTimeWithTimeInterVal:currentLoadTotalTime];
//        //更新显示:视频的总时长
//        _totalNeedLoadTimeLabel.text = [self formatTimeWithTimeInterVal:CMTimeGetSeconds(self.player.currentItem.duration)];
//        //更新显示:缓冲进度条的值
//        _progressView.progress = currentLoadTotalTime/CMTimeGetSeconds(self.player.currentItem.duration);
    }
}

-(void)updatePlayBtn
{
    if([[FFSingleObject sharedInstance] achiveStringWithWeb:self.entity.file])
    {
        self.playButton.hidden = NO;
        self.photoImageView.hidden = YES;
        [self.playButton setImage:[UIImage imageNamed:@"播放"] forState:UIControlStateNormal];
//        [self.photoImageView sd_setImageWithURL:[NSURL URLWithString:self.entity.pic]];
    }
    else
    {
        self.playButton.hidden = YES;
        self.photoImageView.hidden = YES;
    }
}

- (UIImageView *)photoImageView {
    if(!_photoImageView)
    {
        _photoImageView = [self createImageView];
        [_photoImageView.layer setMasksToBounds:YES];
        _photoImageView.contentMode = UIViewContentModeScaleAspectFill;//等比缩放图片把整个ImageView填充满,所以可能会出现图片部分显示不出来
        _photoImageView.hidden = YES;
        _photoImageView.backgroundColor = [UIColor whiteColor];
        _photoImageView.userInteractionEnabled = YES;
    }
    return _photoImageView;
}

- (UIButton *)playButton {
    
    if (!_playButton)
    {
        _playButton = [[UIButton alloc] init];
        [_playButton setTitle:@"" forState:UIControlStateNormal];
        [_playButton setImage:[UIImage imageNamed:@"播放"] 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.isPlay)
            {
                [self hiddenPlayer];
            }
            else
            {
                [self.playButton setImage:[UIImage imageNamed:@"暂停"] forState:UIControlStateNormal];
                self.isPlay = YES;
                self.photoImageView.hidden = NO;
                self.playButton.hidden = NO;
                if([[FFSingleObject sharedInstance] achiveStringWithWeb:self.entity.pic])
                {
                    [self.photoImageView sd_setImageWithURL:[NSURL URLWithString:self.entity.pic]];
                }
                //网络视频路径
                NSString *webVideoPath = self.entity.file;//@"http://api.junqingguanchashi.net/yunpan/bd/c.php?vid=/junqing/1129.mp4";
                NSURL *webVideoUrl = [NSURL URLWithString:webVideoPath];
                AVPlayerItem *playerItem = [[AVPlayerItem alloc] initWithURL:webVideoUrl];
                self.currentPlayerItem = playerItem;
                self.player = [[AVPlayer alloc] initWithPlayerItem:playerItem];
                AVPlayerLayer *avLayer = [AVPlayerLayer playerLayerWithPlayer:self.player];
                avLayer.videoGravity = AVLayerVideoGravityResizeAspect;
                avLayer.frame = self.photoImageView.bounds;
                [self.photoImageView.layer addSublayer:avLayer];
                //1.注册观察者,监测播放器属性
                //观察Status属性,可以在加载成功之后得到视频的长度
                [self.player.currentItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
                //观察loadedTimeRanges,可以获取缓存进度,实现缓冲进度条
                [self.player.currentItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];
            }
        }];
    }
    return _playButton;
}

页面离开处理:

-(void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:NO];
    [self.view endEditing:YES];
    [self.collectionView.refreshView endAnimating];
    [self.chooseSongHeadView hiddenPlayer];
}

点击按钮进行播放和暂停,进入后台停止播放:

    @weakify(self);
    self.imagesScrollView.displayBlock = ^(JCTBannerEntity *entity) {
        @strongify(self);
        self.entity = entity;
        if(!self.isPlay)
        {
            [self updatePlayBtn];
        }
    };
    
    [[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIApplicationDidEnterBackgroundNotification object:nil] subscribeNext:^(id x) {
        @strongify(self);
        [self hiddenPlayer];
    }];
目录
相关文章
|
6月前
|
存储 缓存 Android开发
安卓Jetpack Compose+Kotlin, 使用ExoPlayer播放多个【远程url】音频,搭配Okhttp库进行下载和缓存,播放完随机播放下一首
这是一个Kotlin项目,使用Jetpack Compose和ExoPlayer框架开发Android应用,功能是播放远程URL音频列表。应用会检查本地缓存,如果文件存在且大小与远程文件一致则使用缓存,否则下载文件并播放。播放完成后或遇到异常,会随机播放下一首音频,并在播放前随机设置播放速度(0.9到1.2倍速)。代码包括ViewModel,负责音频管理和播放逻辑,以及UI层,包含播放和停止按钮。
|
6月前
|
缓存 NoSQL Java
避免缓存失效的三大杀手:缓存击穿、穿透与雪崩的解决方案
避免缓存失效的三大杀手:缓存击穿、穿透与雪崩的解决方案
118 0
|
2月前
|
缓存 NoSQL 关系型数据库
缓存穿透以及解决方案
缓存穿透以及解决方案
35 0
|
5月前
|
缓存 NoSQL Redis
使用Redis实现缓存穿透的解决方案
使用Redis实现缓存穿透的解决方案
|
6月前
|
缓存 Java 微服务
Springboot微服务整合缓存的时候报循环依赖的错误 两种解决方案
Springboot微服务整合缓存的时候报循环依赖的错误 两种解决方案
78 1
|
6月前
|
存储 缓存 NoSQL
Redis是一种高性能的内存数据库,常用于高并发环境下的缓存解决方案
【6月更文挑战第18天】**Redis摘要:** 高性能内存数据库,擅长高并发缓存。数据存内存,访问迅速;支持字符串、列表等多元数据类型;具备持久化防止数据丢失;丰富命令集便于操作;通过节点集群实现数据分片与负载均衡,增强可用性和扩展性。理想的缓存解决方案。
84 1
|
5月前
|
缓存 NoSQL Redis
使用Redis实现缓存穿透的解决方案
使用Redis实现缓存穿透的解决方案
|
6月前
|
缓存 NoSQL Java
高并发场景下缓存+数据库双写不一致问题分析与解决方案设计
高并发场景下缓存+数据库双写不一致问题分析与解决方案设计
|
7月前
|
缓存 监控 数据库
分布式系统中缓存穿透问题与解决方案
在分布式系统中,缓存技术被广泛应用以提高系统性能和响应速度。然而,缓存穿透是一个常见而严重的问题,特别是在面对大规模请求时。本文将深入探讨缓存穿透的原因、影响以及一些有效的解决方案,以确保系统在面对这一问题时能够保持稳定和高效。
108 13
|
7月前
|
缓存 NoSQL 搜索推荐
Redis缓存雪崩穿透等解决方案
本文讨论了缓存使用中可能出现的问题及其解决方案。首先,缓存穿透是指查询数据库中不存在的数据,导致请求频繁到达数据库。解决方法包括数据校验、缓存空值和使用BloomFilter。其次,缓存击穿是大量请求同一失效缓存项,可采取监控、限流或加锁策略。再者,缓存雪崩是大量缓存同时失效,引发数据库压力。应对措施是避免同一失效时间,分散缓存过期。接着,文章介绍了Spring Boot中Redis缓存的配置,包括缓存null值以防止穿透,并展示了自定义缓存过期时间的实现,以避免雪崩效应。最后,提供了在`application.yml`中配置不同缓存项的个性化过期时间的方法。
65 1