AVFoundation | 封装一个好用的视频播放器

一:简介

AVFoundation是苹果在iOS和OS X系统中用于处理基于时间的媒体数据的Objective-C框架. 供使用者来开发媒体类型的应用程序。

video.png

上图是iOS系统下媒体应用的结构层. AVKit框架用于简化媒体应用的创建过程,如果你只需要看电影,那么使用这个就可以了, UKit可以支持简单的录制功能 . 而更多功能需要低层级框架支持.下面简单介绍下AVFoundation内最主要的支撑框架和其提供的功能。

  • CoreAudio : 处理所有音频事件.是由多个框架整合在一起的总称,为音频和MIDI内容的录制,播放和处理提供相应接口.设置可以针对音频信号进行完全控制,并通过Audio Units来构建一些复杂的音频处理.有兴趣的可以单独了解一下这个框架。

  • CoreMedia: 是AVFoundation所用到低层级媒体管道的一部分.提供音频样本和视频帧处理所需的低层级数据类型和接口。

  • CoreAnimation: 动画相关框架, 封装了支持OpenGL和OpenGL ES功能的ObjC各种类. AVFoundation可以利用CoreAnimation让开发者能够在视频的编辑和播放过程中添加动画和图片效果。

下面我就和大家详细的分享一下,如何使用AVFoundation来实现一个好用的视频播放器。如果好用或者对你有所帮助,不要忘了关注点个赞呦!!!

二:实现思路分析

  1. 视频控制界面实现

  2. 用于视频显示的View创建

  3. 视频数据下载器FBYVideoDownload创建

  4. 播放、暂停、横竖屏操作方法实现

  5. 监听播放进度

  6. 控制存储缓冲范围

  7. 拖动滑块,控制快进快退

  8. 定义临时文件路径

  9. 定义缓存文件夹路径

  10. 发起视频路径网路请求方法

  11. 播放结束设置

  12. 前后视频播放控制

三:实现源码分析

根据实现思路分析,一步步进行编码实现:

1. 视频控制界面实现

- (FBYVideoPlayerView *)videoPlayControl{
    if (!_videoPlayControl) {
        _videoPlayControl = [[FBYVideoPlayerView alloc] initWithFrame:self.backgroundView.bounds];
        [self.backgroundView addSubview:_videoPlayControl];
        
    }
    return _videoPlayControl;
}

2. 用于视频显示的View创建

- (UIView *)videoShowView{
    if (!_videoShowView) {
        _videoShowView = [[UIView alloc] init];
        _videoShowView.layer.masksToBounds = YES;
        [self.backgroundView addSubview:_videoShowView];
    }
    return _videoShowView;
}

3. 视频数据下载器FBYVideoDownload创建

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

//定义初始化方法 传入videoUrl参数(NSString)
- (instancetype)initWithURL:(NSString *)videoUrl withDelegate:(id)delegate;

//开始下载
- (void)start;
//暂停
- (void)suspend;
//关闭
- (void)cancel;

4. 播放、暂停、横竖屏操作方法实现

        //全屏
        [_videoPlayControl setFullScreenButtonClick_block:^{
            if (weakSelf.delegate && [weakSelf.delegate respondsToSelector:@selector(videoPlayerDidFullScreenButtonClick)]) {
                [weakSelf.delegate videoPlayerDidFullScreenButtonClick];
            }
        }];
        //播放/暂停
        [_videoPlayControl setPlayButtonClick_block:^(BOOL play) {
            if (play) {
                [weakSelf.player play];
            }else{
                [weakSelf.player pause];
            }
            weakSelf.playButtonState = !weakSelf.playButtonState;
        }];

5. 监听播放进度

self.timeObserve = [self.player addPeriodicTimeObserverForInterval:CMTimeMake(1, 1) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
        CGFloat current = CMTimeGetSeconds(time);
        CGFloat total = CMTimeGetSeconds(weakSelf.currentPlayerItem.duration);
        CGFloat progress = current / total;
        
        weakSelf.videoPlayControl.currentTime = current;
        weakSelf.videoPlayControl.playValue = progress;
        
        if (weakSelf.isCanToGetLocalTime) {
            weakSelf.localTime = [weakSelf getLocalTime];
        }
        NSInteger timeNow = [weakSelf getLocalTime];
        if (timeNow - weakSelf.localTime > 1.5) {
            [weakSelf.videoPlayControl videoPlayerDidBeginPlay];
            weakSelf.isCanToGetLocalTime = YES;
        }
    }];

6. 监听播放器的状态

[self.currentPlayerItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];

7. 监听当前的缓冲进度

[self.currentPlayerItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];

8. 监听到当前没有缓冲数据

[self.currentPlayerItem addObserver:self forKeyPath:@"playbackBufferEmpty" options:NSKeyValueObservingOptionNew context:nil];

9. 控制存储缓冲范围

- (void)playForActivity{
    if (self.playButtonState) {
        [self.player play];
    }
    self.isBufferEmpty = NO;
    self.isPlaying = YES;
    [self.videoPlayControl videoPlayerDidBeginPlay];
}

10. 拖动滑块,控制快进快退

//拖动滑块
[_videoPlayControl setSliderTouchEnd_block:^(CGFloat time) {
            [weakSelf seekToTimePlay:time];
        }];
        
//快进快退
[_videoPlayControl setFastFastForwardAndRewind_block:^(CGFloat time) {
            [weakSelf seekToTimePlay:time];
        }];

11. 定义临时文件路径

self.videoTempPath = [NSString tempFilePathWithFileName:videoName];

12. 定义缓存文件夹路径

self.videoCachePath = [NSString cacheFilePathWithName:videoName];

13. 发起视频路径网路请求方法

- (void)sendHttpRequst
{
    [_fileHandle seekToEndOfFile];
    NSURL *url = [NSURL URLWithString:_videoUrl];
    NSMutableURLRequest *requeset = [NSMutableURLRequest requestWithURL:url];
    
    //指定头信息  当前已下载的进度
    [requeset setValue:[NSString stringWithFormat:@"bytes=%ld-", _curruentLength] forHTTPHeaderField:@"Range"];
    
    //创建请求
    NSURLSessionDataTask *dataTask = [self.session dataTaskWithRequest:requeset];
    self.dataTask = dataTask;
    
    //发起请求
    [self.dataTask resume];
}

14. 播放结束设置

- (void)playerItemDidPlayToEnd:(NSNotification *)notification{
    //重新开始播放
    __weak typeof(self) weak_self = self;
    [self.player seekToTime:CMTimeMake(0, 1) completionHandler:^(BOOL finished) {
        __strong typeof(weak_self) strong_self = weak_self;
        if (!strong_self) return;
        [strong_self.player play];
    }];
}

15. 前后视频播放控制

//进入后台
- (void)appDidEnterBackground{
    if (self.stopWhenAppDidEnterBackground) {
        [self pauseVideo];
    }
}
//进入前台
- (void)appDidEnterForeground{
    [self playVideo];
}

五:项目实际使用

1. 下载demo

下载demo,将demo中FBYVideoData文件夹引入项目中。

2. 在项目中引入FBYVideoPlayer并继承FBYVideoPlayerDelegate代理

#import "FBYVideoPlayer.h"

@interface ViewController ()<FBYVideoPlayerDelegate>

@property (nonatomic ,strong) FBYVideoPlayer *videoPlayer;
@property (nonatomic ,strong) UIView *videoPlayBGView;
@property (nonatomic ,copy)   NSString*videoUrl;

@end

3. 初始化创建基层view

self.videoPlayBGView = [[UIView alloc] initWithFrame:CGRectMake(0, 90, SCREEN_WIDTH, SCREEN_WIDTH * 0.6)];
self.videoPlayBGView.backgroundColor = [UIColor blackColor];
    [self.view addSubview:self.videoPlayBGView];

4. 初始化调用视频播放方法

self.videoPlayer = [[FBYVideoPlayer alloc] init];
self.videoPlayer.delegate = self;
[self.videoPlayer playWithUrl:self.videoUrl showView:self.videoPlayBGView];

5. 初始化调用视频播放方法

self.videoPlayer = [[FBYVideoPlayer alloc] init];
self.videoPlayer.delegate = self;
[self.videoPlayer playWithUrl:self.videoUrl showView:self.videoPlayBGView];

本篇文章demo源码:

源码Demo获取方法

关注 【网罗开发】微信公众号,回复【97】便可领取。
网罗天下方法,方便你我开发,更多iOS技术干货等待领取,所有文档会持续更新,欢迎关注一起成长!

希望可以帮助大家,如有问题可加QQ技术交流群: 668562416
如果哪里有什么不对或者不足的地方,还望读者多多提意见或建议
如需转载请联系我,经过授权方可转载,谢谢

本篇已同步到个人博客:FBY展菲


欢迎关注我的公众号:网罗开发

image
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容