IOS开发学习日记(十二)

IOS消息机制

NSNotification

类与类之间的通信(系统调用/解耦)

 1.NSNotificationCenter

2.中心化管理

3.主要关注系统性事件而不是特有的对象

4.前后台切换 内存警告

使用Notification接收和处理播放状态通知

// 视频是否完成播放        
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_handlerPlayEnd) name:AVPlayerItemDidPlayToEndTimeNotification object:nil];

-(void)_handlerPlayEnd{
    [_playerLayer removeFromSuperlayer];
    _videoItem = nil;
    _avplayer = nil;
}

使用KVO监听和处理视频播放资源的状态变化

-(void)_tapToPlay{
    //    NSURL *videoUrl = [NSURL URLWithString:_videoUrl];
    NSString *path = [[NSBundle mainBundle] pathForResource:_videoUrl ofType:nil];
    NSURL *videoUrl = [NSURL fileURLWithPath:path]; //播放本地视频
    AVAsset *asset = [AVAsset assetWithURL:videoUrl];
    _videoItem = [AVPlayerItem playerItemWithAsset:asset];

    [_videoItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];    //监听状态
    
    
    _avplayer = [AVPlayer playerWithPlayerItem:_videoItem];
    _playerLayer = [AVPlayerLayer playerLayerWithPlayer:_avplayer];
    _playerLayer.frame = _coverView.bounds;
    [_coverView.layer addSublayer:_playerLayer];
    
}


#pragma mark - KVO

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
    if([keyPath isEqualToString:@"status"]){
        if(((NSNumber *)[change objectForKey:NSKeyValueChangeNewKey]).integerValue == AVPlayerItemStatusReadyToPlay){
            [_avplayer play];
        }
        else{
            //
        }
    }
}

监听视频播放的缓冲与进度

AVPlayer播放进度

        ·CMTimer;

        ·CMTimerMake(第几帧,帧率)

        ·duration/currentTime

        ·播放进度回调

-(void)_tapToPlay{
    //    NSURL *videoUrl = [NSURL URLWithString:_videoUrl];
    NSString *path = [[NSBundle mainBundle] pathForResource:_videoUrl ofType:nil];
    NSURL *videoUrl = [NSURL fileURLWithPath:path]; //播放本地视频
    AVAsset *asset = [AVAsset assetWithURL:videoUrl];
    _videoItem = [AVPlayerItem playerItemWithAsset:asset];
    [_videoItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
    [_videoItem addObserver:self forKeyPath:@"loadTimeRanges" options:NSKeyValueObservingOptionNew context:nil];
    
    CMTime videoDuration = _videoItem.duration;
    CGFloat duration = CMTimeGetSeconds(videoDuration);
    
    _avplayer = [AVPlayer playerWithPlayerItem:_videoItem];
    _playerLayer = [AVPlayerLayer playerLayerWithPlayer:_avplayer];
    _playerLayer.frame = _coverView.bounds;
    [_coverView.layer addSublayer:_playerLayer];
    
}

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
    if([keyPath isEqualToString:@"status"]){
        if(((NSNumber *)[change objectForKey:NSKeyValueChangeNewKey]).integerValue == AVPlayerItemStatusReadyToPlay){
            [_avplayer play];
        }
        else if([keyPath isEqualToString:@"loadTimeRanges"]){
            NSLog(@"缓存:%@", [change objectForKey:NSKeyValueChangeNewKey]);
        }
        
    }
}

使用单例模式重构播放器业务逻辑

创建简单的音视频播放器

        ·使用KVO监听播放状态

        ·使用Notification接收通知

        ·视频播放器的生命周期管理

        ·一般来说App内只能同时播放一个视频

        ·建议用单例进行管理

        ·内部处理播放器的销毁和播放状态切换

//
//  GSCVideoPlayer.h
//  GSCApp1
//
//  Created by gsc on 2024/6/5.
//

#import <Foundation/Foundation.h>
#import "UIKit/UIKit.h"

NS_ASSUME_NONNULL_BEGIN

@interface GSCVideoPlayer : NSObject
+(GSCVideoPlayer *)Player;

-(void)playWithVideoCoverUrl:(NSString *)videoUrl attachView:(UIView *)attachView;

@end

NS_ASSUME_NONNULL_END


//
//  GSCVideoPlayer.m
//  GSCApp1
//
//  Created by gsc on 2024/6/5.
//

#import "GSCVideoPlayer.h"
#import "AVFoundation/AVFoundation.h"

@interface GSCVideoPlayer()

@property(nonatomic, strong, readwrite) AVPlayer *avplayer;
@property(nonatomic, strong, readwrite) AVPlayerLayer *playerLayer;
@property(nonatomic, strong, readwrite) AVPlayerItem *videoItem;

@end


@implementation GSCVideoPlayer

+(GSCVideoPlayer *)Player{
    static GSCVideoPlayer *player;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        player = [[GSCVideoPlayer alloc] init];
    });
    return player;
}

-(void)playWithVideoCoverUrl:(NSString *)videoUrl attachView:(UIView *)attachView{
    [self _stopPlay];
    
    //    NSURL *videoUrl = [NSURL URLWithString:_videoUrl];
    NSString *path = [[NSBundle mainBundle] pathForResource:videoUrl ofType:nil];
    NSURL *videoLocalUrl = [NSURL fileURLWithPath:path]; //播放本地视频
    AVAsset *asset = [AVAsset assetWithURL:videoLocalUrl];
    _videoItem = [AVPlayerItem playerItemWithAsset:asset];
    [_videoItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
    [_videoItem addObserver:self forKeyPath:@"loadTimeRanges" options:NSKeyValueObservingOptionNew context:nil];
    
    CMTime videoDuration = _videoItem.duration;
    CGFloat duration = CMTimeGetSeconds(videoDuration);
    
    _avplayer = [AVPlayer playerWithPlayerItem:_videoItem];
    [_avplayer addPeriodicTimeObserverForInterval:CMTimeMake(1, 1) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
        NSLog(@"播放进度%@",@(CMTimeGetSeconds(time)));
    }];
    
    
    _playerLayer = [AVPlayerLayer playerLayerWithPlayer:_avplayer];
    _playerLayer.frame = attachView.bounds;
    [attachView.layer addSublayer:_playerLayer];
    
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_handlerPlayEnd) name:AVPlayerItemDidPlayToEndTimeNotification object:nil];
    
    NSLog(@"");
    
}
#pragma mark - private method
-(void)_stopPlay{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    [_playerLayer removeFromSuperlayer];
    [_videoItem removeObserver:self forKeyPath:@"status"];
    [_videoItem removeObserver:self forKeyPath:@"loadTimeRanges"];
    _videoItem = nil;
    _avplayer = nil;
}

-(void)_handlerPlayEnd{
    [_avplayer seekToTime:CMTimeMake(0, 1)];
    [_avplayer play];
}

#pragma mark - KVO

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
    if([keyPath isEqualToString:@"status"]){
        if(((NSNumber *)[change objectForKey:NSKeyValueChangeNewKey]).integerValue == AVPlayerItemStatusReadyToPlay){
            [_avplayer play];
        }
        else if([keyPath isEqualToString:@"loadTimeRanges"]){
            NSLog(@"缓存:%@", [change objectForKey:NSKeyValueChangeNewKey]);
        }
        
    }
}
@end

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我真的学不会了

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值