Objective-C AVPlayer播放視頻的使用與封裝

寫在前面
弄了下個人站...防止內(nèi)容再次被鎖定...所有東西都在這里面
welcome~
個人博客

大致效果

不要介意。界面有點丑枪狂。危喉。。

AVPlayer封裝.gif

界面搭建

看下成員變量就知道我怎么搭建的了,這里我將video播放層的size作為參照量州疾,對所有控件的size按照其video的size寬高進行比例縮放

@interface VideoPlayerView()
@property (nonatomic,copy) NSString *path;                  //播放地址 自動判斷文件路徑和網(wǎng)址路徑
@property (nonatomic,strong) AVPlayer *player;              //播放類
@property (nonatomic,strong) AVPlayerLayer *playerlayer;    //顯示區(qū)域
@property (nonatomic,strong) UIButton *playBtn;             //播放暫停
@property (nonatomic,strong) UIButton *stopBtn;             // 停止
@property (nonatomic,strong) UIButton *fullScreenBtn;       //全屏
@property (nonatomic,strong) UISlider *playSlider;          //進度選擇
@property (nonatomic,strong) UIProgressView *progress;      //進度
@property (nonatomic,strong) UILabel *currentTimeLab;       //當前時間
@property (nonatomic,strong) UILabel *durationLab;          //總時間
@property (nonatomic,strong) UIView *toolView;              //工具欄view
@property (nonatomic,assign) BOOL isFullScreen;             //全屏判斷
@property (nonatomic,assign) CGFloat videoHeight;           //video高
@property (nonatomic,assign) CGFloat videoWidth;            //video寬
@end

所有控件使用懶加載 如下

//播放暫停
- (UIButton *)playBtn {
    if (!_playBtn) {
        _playBtn = [[UIButton alloc] initWithFrame:CGRectMake(kLrMargin, kUIy, kBtnWidth, kUIHeight)];
        _playBtn.backgroundColor  =[UIColor greenColor];
        _playBtn.selected = NO;
        _playBtn.enabled = NO;
        _playBtn.titleLabel.adjustsFontSizeToFitWidth = YES;
        [_playBtn setTitle:@"播放" forState:UIControlStateNormal];
        [_playBtn setTitle:@"暫停" forState:UIControlStateSelected];
        [_playBtn addTarget:self action:@selector(play) forControlEvents:UIControlEventTouchUpInside];
    }
    return _playBtn;
}

屏幕適配

由于涉及到屏幕的旋轉(zhuǎn)和適配辜限。我這里沒有使用第三方框架來做約束,而是使用最基本的按百分比設置frame严蓖。旋轉(zhuǎn)屏幕時通過調(diào)用本類- (void)resetFrame:(CGSize)size;方法來重設frame薄嫡。所以需要重設frame的控件在懶加載中設置frame氧急,調(diào)用時即刷新frame。

  • 先看下初始化 對video的size設置是時始終用最小的邊來確定高度毫深,寬度與屏幕當前寬度相當
//初始化
- (instancetype)initWithFrame:(CGRect)frame andPath:(NSString*)path {
    self = [super initWithFrame:frame];
    if (self) {
        self.backgroundColor = [UIColor clearColor];
        self.path = path;
        CGFloat width = [UIScreen mainScreen].bounds.size.width;
        CGFloat height = [UIScreen mainScreen].bounds.size.height;
        self.videoHeight = height > width ? width * 0.6 : height * 0.6;
        self.videoWidth = [UIScreen mainScreen].bounds.size.width-2*kLrMargin;
        [self.layer addSublayer:self.playerlayer];
        [self addSubview:self.toolView];
    }
    return self;
}
  • 屏幕旋轉(zhuǎn)時做一些事
//屏幕旋轉(zhuǎn)時觸發(fā) 這里寫在父類中
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {    
    [self.videoView resetFrame:size];
}
//旋轉(zhuǎn)屏幕重設frame
- (void)resetFrame:(CGSize)size {
    CGFloat width = size.width;
    CGFloat height = size.height;
    self.videoHeight = height > width ? width * 0.6 : height * 0.6;
    self.videoWidth = size.width - 2 * kLrMargin;
    if (self.isFullScreen) {
        //全屏時旋轉(zhuǎn)
        [self setPlayerWithPosition:CGPointZero andSize:size];
    } else {
        //普通旋轉(zhuǎn)
        [self setPlayerWithPosition:CGPointMake(kLrMargin, kTopMargin) andSize:CGSizeMake(self.videoWidth, self.videoHeight)];
        //刷新frame
        [self toolView];
        [self playSlider];
        [self progress];
    }    
}
  • 懶加載刷新frame
//進度懶加載 調(diào)用時只刷新frame
- (UIProgressView *)progress {
    if (!_progress) {
        _progress = [[UIProgressView alloc] init];
        _progress.progress = 0;
    }
    _progress.frame = CGRectMake(2, self.playSlider.frame.size.height/2, self.playSlider.frame.size.width-2-2, kUIHeight);
    return _progress;
}

AVPlayer的基本操作

基本操作包括 播放 吩坝、暫停、 停止哑蔫、 播放指定位置钉寝、緩存進度
播放網(wǎng)絡地址時 在info.plist中添加 App Transport Security Settings字典中添加Allow Arbitrary Loads元素 值為YES

添加項.png

使用AVPlayer播放視頻就必須用到AVPlayerlayer用來顯示播放視圖凫碌。

//加載顯示層
- (AVPlayerLayer*)playerlayer {
    if (!_playerlayer) {
        _playerlayer = [AVPlayerLayer playerLayerWithPlayer:self.player];
        _playerlayer.bounds = CGRectMake(0, 0, self.videoWidth, self.videoHeight);
        _playerlayer.anchorPoint = CGPointMake(0, 0);
        _playerlayer.position = CGPointMake(kLrMargin, kTopMargin);
        _playerlayer.backgroundColor = [UIColor blackColor].CGColor;
    }
    return _playerlayer;
}

//加載播放類
- (AVPlayer *)player {
    if (!_player) {
        _player = [AVPlayer playerWithURL:[self getUrlPath:self.path]];
        //kvo注冊
        [self addObservers];
    }
    return _player;
}
  • 使用KVO對狀態(tài)和緩存進行檢測享扔,添加KVO時養(yǎng)成習慣寫好移除操作
//注冊kvo
- (void)addObservers{
    [self.player.currentItem addObserver:self forKeyPath:kItemStatus options:NSKeyValueObservingOptionNew context:nil];
    [self.player.currentItem addObserver:self forKeyPath:kItemLoadedTimeRanges options:NSKeyValueObservingOptionNew context:nil];
}

//移除kvo
- (void)dealloc {
    [self.player.currentItem removeObserver:self forKeyPath:kItemStatus];
    [self.player.currentItem removeObserver:self forKeyPath:kItemLoadedTimeRanges];
}

//kvo回調(diào)
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    if ([keyPath isEqualToString:kItemStatus]) {
        AVPlayerItem *item = object;
        if (item.status == AVPlayerItemStatusReadyToPlay) {
            //準備播放
            [self readyToplayWithItem:item];
        }else if (item.status == AVPlayerItemStatusUnknown) {
            //播放失敗
           //這里寫個alertView提示下就行
        }else if (item.status == AVPlayerItemStatusFailed) {
            //播放失敗
            //同上
        }
    } else if ([keyPath isEqualToString:kItemLoadedTimeRanges]) {
        AVPlayerItem *item = object;
        [self getLoadedTimeRanges:item];
    }
}
  • 基礎功能
//播放 暫停
- (void)play {
    if (self.playBtn.selected) {
        self.playBtn.selected = NO;
        [self.player pause];
    } else {
        self.playBtn.selected = YES;
        [self.player play];
        [self timerStar];
    }
}

//停止
- (void)stop {
    [self.player pause];
    [self.player seekToTime:CMTimeMake(0, 1)];
    self.playBtn.selected = NO;
}
//全屏
- (void)fullScreen {
    self.toolView.hidden = YES;
    self.isFullScreen = YES;
    self.backgroundColor = [UIColor blackColor];
    self.playerlayer.bounds = [UIScreen mainScreen].bounds;
    self.playerlayer.anchorPoint = CGPointMake(0, 0);
    self.playerlayer.position = CGPointMake(0, 0);
}

//播放指定位置
- (void)playCurrentVideo {
    self.playBtn.selected = YES;
    NSTimeInterval second = self.playSlider.value;
    [self.player.currentItem seekToTime:CMTimeMake(second,1)];
    [self.player play];
    [self timerStar];
}
  • 具體操作
    • 包括格式化時間
    • 格式化路徑
    • 播放準備
    • 緩存計算
    • 觸摸關閉全屏
    • 設置video的大小位置
//設置video的frame
- (void)setPlayerWithPosition:(CGPoint)position andSize:(CGSize)size {
    self.playerlayer.anchorPoint = CGPointMake(0, 0);
    self.playerlayer.position = position;
    self.playerlayer.bounds = CGRectMake(0, 0, size.width, size.height);
}

//準備播放
- (void)readyToplayWithItem:(AVPlayerItem*)item {
    self.playBtn.enabled = YES;
    long long durationSecond = item.duration.value / item.duration.timescale;
    self.durationLab.text = [NSString stringWithFormat:@" / %@",[self getFormatDate:durationSecond]];
    self.playSlider.maximumValue = durationSecond;
    self.playSlider.minimumValue = 0;
    [self.playSlider addTarget:self action:@selector(playCurrentVideo) forControlEvents:UIControlEventValueChanged];

}

//獲得緩存
- (void)getLoadedTimeRanges:(AVPlayerItem*)item {
    NSValue *value = [item.loadedTimeRanges lastObject];
    CMTimeRange range = [value CMTimeRangeValue];
    long long cacheSecond = range.start.value/range.start.timescale + range.duration.value/range.duration.timescale;
    long long currentSecond = item.currentTime.value / item.currentTime.timescale;
    self.progress.progress = (currentSecond + cacheSecond) * 0.1;
  
}


//格式化時間
- (NSString*)getFormatDate:(NSTimeInterval)time {
    int seconds = (int)time % 60;
    int minutes = (int)(time / 60) % 60;
    int hours = (int)time / 3600;
    return [NSString stringWithFormat:@"%02d:%02d:%02d",hours,minutes,seconds];
}

//格式化url路徑
- (NSURL*)getUrlPath:(NSString*)path {
    NSURL *url;
    if ([self.path containsString:@"http"]) {
        url = [NSURL URLWithString:self.path];
    } else {
        url = [NSURL fileURLWithPath:self.path];
    }
    return url;
}

//開啟定時
- (void)timerStar {
    //定時回調(diào)
    __weak typeof(self) weakSelf = self;
    [self.player addPeriodicTimeObserverForInterval:CMTimeMake(1, 1) queue:NULL usingBlock:^(CMTime time) {
        long long current = weakSelf.player.currentItem.currentTime.value / weakSelf.player.currentItem.currentTime.timescale;
        weakSelf.playSlider.value = current;
        NSString *currentFormat = [weakSelf getFormatDate:current];
        weakSelf.currentTimeLab.text = [NSString stringWithFormat:@"%@",currentFormat];
    }];
    
}


//觸摸關閉全屏
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    
    if (self.isFullScreen) {
        self.toolView.hidden = NO;
        self.backgroundColor = [UIColor clearColor];
        [self setPlayerWithPosition:CGPointMake(kLrMargin, kTopMargin) andSize:CGSizeMake(self.videoWidth, self.videoHeight)];
        
        [self toolView];
        [self playSlider];
        [self progress];
        self.isFullScreen = NO;
    }
}

這樣一個簡單AVPlayer的封裝就做好了

Demo地址

https://github.com/gongxiaokai/AVPlayerDemo

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市汉匙,隨后出現(xiàn)的幾起案子稿黍,更是在濱河造成了極大的恐慌疹瘦,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,042評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件巡球,死亡現(xiàn)場離奇詭異言沐,居然都是意外死亡,警方通過查閱死者的電腦和手機酣栈,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評論 2 384
  • 文/潘曉璐 我一進店門险胰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人矿筝,你說我怎么就攤上這事起便。” “怎么了窖维?”我有些...
    開封第一講書人閱讀 156,674評論 0 345
  • 文/不壞的土叔 我叫張陵榆综,是天一觀的道長。 經(jīng)常有香客問我铸史,道長鼻疮,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,340評論 1 283
  • 正文 為了忘掉前任琳轿,我火速辦了婚禮判沟,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘崭篡。我一直安慰自己挪哄,他們只是感情好,可當我...
    茶點故事閱讀 65,404評論 5 384
  • 文/花漫 我一把揭開白布媚送。 她就那樣靜靜地躺著中燥,像睡著了一般。 火紅的嫁衣襯著肌膚如雪塘偎。 梳的紋絲不亂的頭發(fā)上疗涉,一...
    開封第一講書人閱讀 49,749評論 1 289
  • 那天拿霉,我揣著相機與錄音,去河邊找鬼咱扣。 笑死绽淘,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的闹伪。 我是一名探鬼主播沪铭,決...
    沈念sama閱讀 38,902評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼偏瓤!你這毒婦竟也來了杀怠?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,662評論 0 266
  • 序言:老撾萬榮一對情侶失蹤厅克,失蹤者是張志新(化名)和其女友劉穎赔退,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體证舟,經(jīng)...
    沈念sama閱讀 44,110評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡硕旗,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了女责。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片漆枚。...
    茶點故事閱讀 38,577評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖抵知,靈堂內(nèi)的尸體忽然破棺而出墙基,到底是詐尸還是另有隱情,我是刑警寧澤刷喜,帶...
    沈念sama閱讀 34,258評論 4 328
  • 正文 年R本政府宣布碘橘,位于F島的核電站,受9級特大地震影響吱肌,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜仰禽,卻給世界環(huán)境...
    茶點故事閱讀 39,848評論 3 312
  • 文/蒙蒙 一氮墨、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧吐葵,春花似錦规揪、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至凤藏,卻和暖如春奸忽,著一層夾襖步出監(jiān)牢的瞬間堕伪,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評論 1 264
  • 我被黑心中介騙來泰國打工栗菜, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留欠雌,地道東北人。 一個月前我還...
    沈念sama閱讀 46,271評論 2 360
  • 正文 我出身青樓疙筹,卻偏偏與公主長得像富俄,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子而咆,可洞房花燭夜當晚...
    茶點故事閱讀 43,452評論 2 348

推薦閱讀更多精彩內(nèi)容