源码下载地址:https://github.com/renzifeng/ZFPlayer

之前自己实现过一个模仿百思不得姐的demo https://github.com/agelessman/FFmpegAndKxmovieDemo

由于有朋友推荐,看了下ZFPlayer,觉得功能和封装都写的很好,就把源码看了一遍,现在看源码已经养成了一个习惯,就是把自己在源码中不太熟悉的地方记录下来,还有就是尽量捕捉作者的思路。

打开demo,先看主控制器

主要的方法有两个:

// 哪些页面支持自动转屏
- (BOOL)shouldAutorotate// viewcontroller支持哪些转屏方向
- (UIInterfaceOrientationMask)supportedInterfaceOrientations

这两个方法也没什么好说的,只是在我们写app的时候,一般都是默认开启app支持旋转的,然后用代码实现支持哪些界面能够旋转。

这里作者使用了这样的代码

// 调用ZFPlayerSingleton单例记录播放状态是否锁定屏幕方向return !ZFPlayerShared.isLockScreen;

不看后边的代码,应该能够推断出整个播放器采用的是单例模式的设计,有且只有一个,这样就避免了反复创建的消耗。但不是说创建了就一直存在,完全可以在需要销毁的时候进行销毁。

接下来看这四个文件

不难看出,ZFSessionModel应该就是与下载的文件相关信息的一个模型,在这个模型中我们能够得到跟下载的文件相关的我们需要的所有信息

支持NSCoding 协议,说明这个类会被归档和解档,也就是说对本类或进行本地存储操作

从编码的属性看,并没有编码所有的属性,只编码了必要的信息。

我们用一张图表来看本类的所有信息

接下来我们说说下载管理器的问题

其实编程跟我们日常生活中的生活规律特别的像,比如,我需要一个下载管理器来管理我整个工程的下载任务,如果我的下载任务很重,很多,那么我就应该多弄几个管理器,各管各自的业务,最后向一个总的管理boss负责。这种思想很重要,我们完全可以在写代码之前想象出一个大概的职责列表,每一项职责都是一个属性或者方法。

这样的想法很奇妙,不如我们就按照现在的思路,想象一下,现实生活中,作为一个数据仓库的管理员都需要干什么呢?

大家可以对比一下这个日常生活中的做事习惯跟变成是不是很像

再和

文件对比下,看看是不是差不多,可能我们在写接口文件的时候并不能一开始写的很周到,但是在实现功能的过程中,会慢慢的想到需要添加哪些东西,除非很必要,应该暴露的东西越少越好。

由于作者的注释非常的详细,对所有的方法就不一一解释了,有点基础的都能看懂,

这个是更加安全的单例写法,不要只写最下边的那个方法。

在下载管理者的实现中 通过

NSURLSessionDataDelegate

处理了下载过程和下载完成后的逻辑,这个就不解释了,所有的下载代码都差不多是这样的,需要指出的是断点下载的实现,是下边的代码,在配置下载器的时候传入一个范围就可以了

好了现在重点来看看播放器的部分。

这个demo作者是没有加入边播边下载功能的,但是加了加载进度的缓存显示效果,这个效果主要是通过监听

loadedTimeRanges 实现的,

由于代码比较长,也都是一些业务逻辑上的问题,再次就一个个的进行说明了,作者也注释的清晰,

通过这个方法可以直接用在tableview类型的播放器中,这个还是比较方便的,看来作者也是想让别人用起来方便。

该demo提供的逻辑和功能还是很完善的,因为前段时间也自学了AVFoundation方面的知识,所以对这个还是很感兴趣的。

AVFoundation 提供了一系列很强大的功能

有兴趣的朋友可以下载这些demo看看,使用swift写的 http://code.cocoachina.com/u/373290

在这里也正好总结一些我对写一个类似这样播放器的看法。

作者是把整个功能使用UIView来实现的,而且额外提供了一些功能,可以让用户处理点击事件或者设置点击后的行为。

如果是我,我会把整个功能封装成一个NSObject(在一本书上学到的),把所有的功能封装进这个对象中去,就像这样

很简单,之暴露出来一个初始化方法,和一个实际播放的view

使用起来大概是这么使用

内部的实现是这样

  1 #import "THPlayerController.h"
  2 #import "THThumbnail.h"
  3 #import <AVFoundation/AVFoundation.h>
  4 #import "THTransport.h"
  5 #import "THPlayerView.h"
  6 #import "AVAsset+THAdditions.h"
  7 #import "UIAlertView+THAdditions.h"
  8 #import "THNotifications.h"
  9
 10 // AVPlayerItem's status property
 11 #define STATUS_KEYPATH @"status"
 12
 13 // Refresh interval for timed observations of AVPlayer
 14 #define REFRESH_INTERVAL 0.5f
 15
 16 // Define this constant for the key-value observation context.
 17 static const NSString *PlayerItemStatusContext;
 18
 19
 20 @interface THPlayerController () <THTransportDelegate>
 21
 22 @property (strong, nonatomic) AVAsset *asset;
 23 @property (strong, nonatomic) AVPlayerItem *playerItem;
 24 @property (strong, nonatomic) AVPlayer *player;
 25 @property (strong, nonatomic) THPlayerView *playerView;
 26
 27 @property (weak, nonatomic) id <THTransport> transport;
 28
 29 @property (strong, nonatomic) id timeObserver;
 30 @property (strong, nonatomic) id itemEndObserver;
 31 @property (assign, nonatomic) float lastPlaybackRate;
 32
 33 @property (strong, nonatomic) AVAssetImageGenerator *imageGenerator;
 34
 35 @end
 36
 37 @implementation THPlayerController
 38
 39 #pragma mark - Setup
 40
 41 - (id)initWithURL:(NSURL *)assetURL {
 42     self = [super init];
 43     if (self) {
 44         _asset = [AVAsset assetWithURL:assetURL];                           // 1
 45         [self prepareToPlay];
 46     }
 47     return self;
 48 }
 49
 50 - (void)prepareToPlay {
 51     NSArray *keys = @[
 52         @"tracks",
 53         @"duration",
 54         @"commonMetadata",
 55         @"availableMediaCharacteristicsWithMediaSelectionOptions"
 56     ];
 57     self.playerItem = [AVPlayerItem playerItemWithAsset:self.asset          // 2
 58                            automaticallyLoadedAssetKeys:keys];
 59
 60     [self.playerItem addObserver:self                                       // 3
 61                       forKeyPath:STATUS_KEYPATH
 62                          options:0
 63                          context:&PlayerItemStatusContext];
 64
 65     self.player = [AVPlayer playerWithPlayerItem:self.playerItem];          // 4
 66
 67     self.playerView = [[THPlayerView alloc] initWithPlayer:self.player];    // 5
 68     self.transport = self.playerView.transport;
 69     self.transport.delegate = self;
 70 }
 71
 72 - (void)observeValueForKeyPath:(NSString *)keyPath
 73                       ofObject:(id)object
 74                         change:(NSDictionary *)change
 75                        context:(void *)context {
 76
 77     if (context == &PlayerItemStatusContext) {
 78
 79         dispatch_async(dispatch_get_main_queue(), ^{                        // 1
 80
 81             [self.playerItem removeObserver:self forKeyPath:STATUS_KEYPATH];
 82
 83             if (self.playerItem.status == AVPlayerItemStatusReadyToPlay) {
 84
 85                 // Set up time observers.                                   // 2
 86                 [self addPlayerItemTimeObserver];
 87                 [self addItemEndObserverForPlayerItem];
 88
 89                 CMTime duration = self.playerItem.duration;
 90
 91                 // Synchronize the time display                             // 3
 92                 [self.transport setCurrentTime:CMTimeGetSeconds(kCMTimeZero)
 93                                       duration:CMTimeGetSeconds(duration)];
 94
 95                 // Set the video title.
 96                 [self.transport setTitle:self.asset.title];                 // 4
 97
 98                 [self.player play];                                         // 5
 99
100                 [self loadMediaOptions];
101                 [self generateThumbnails];
102
103             } else {
104                 [UIAlertView showAlertWithTitle:@"Error"
105                                         message:@"Failed to load video"];
106             }
107         });
108     }
109 }
110
111 - (void)loadMediaOptions {
112     NSString *mc = AVMediaCharacteristicLegible;                            // 1
113     AVMediaSelectionGroup *group =
114         [self.asset mediaSelectionGroupForMediaCharacteristic:mc];          // 2
115     if (group) {
116         NSMutableArray *subtitles = [NSMutableArray array];                 // 3
117         for (AVMediaSelectionOption *option in group.options) {
118             [subtitles addObject:option.displayName];
119         }
120         [self.transport setSubtitles:subtitles];                            // 4
121     } else {
122         [self.transport setSubtitles:nil];
123     }
124 }
125
126 - (void)subtitleSelected:(NSString *)subtitle {
127     NSString *mc = AVMediaCharacteristicLegible;
128     AVMediaSelectionGroup *group =
129         [self.asset mediaSelectionGroupForMediaCharacteristic:mc];          // 1
130     BOOL selected = NO;
131     for (AVMediaSelectionOption *option in group.options) {
132         if ([option.displayName isEqualToString:subtitle]) {
133             [self.playerItem selectMediaOption:option                       // 2
134                          inMediaSelectionGroup:group];
135             selected = YES;
136         }
137     }
138     if (!selected) {
139         [self.playerItem selectMediaOption:nil                              // 3
140                      inMediaSelectionGroup:group];
141     }
142 }
143
144
145 #pragma mark - Time Observers
146
147 - (void)addPlayerItemTimeObserver {
148
149     // Create 0.5 second refresh interval - REFRESH_INTERVAL == 0.5
150     CMTime interval =
151         CMTimeMakeWithSeconds(REFRESH_INTERVAL, NSEC_PER_SEC);              // 1
152
153     // Main dispatch queue
154     dispatch_queue_t queue = dispatch_get_main_queue();                     // 2
155
156     // Create callback block for time observer
157     __weak THPlayerController *weakSelf = self;                             // 3
158     void (^callback)(CMTime time) = ^(CMTime time) {
159         NSTimeInterval currentTime = CMTimeGetSeconds(time);
160         NSTimeInterval duration = CMTimeGetSeconds(weakSelf.playerItem.duration);
161         [weakSelf.transport setCurrentTime:currentTime duration:duration];  // 4
162     };
163
164     // Add observer and store pointer for future use
165     self.timeObserver =                                                     // 5
166         [self.player addPeriodicTimeObserverForInterval:interval
167                                                   queue:queue
168                                              usingBlock:callback];
169 }
170
171 - (void)addItemEndObserverForPlayerItem {
172
173     NSString *name = AVPlayerItemDidPlayToEndTimeNotification;
174
175     NSOperationQueue *queue = [NSOperationQueue mainQueue];
176
177     __weak THPlayerController *weakSelf = self;                             // 1
178     void (^callback)(NSNotification *note) = ^(NSNotification *notification) {
179         [weakSelf.player seekToTime:kCMTimeZero                             // 2
180                   completionHandler:^(BOOL finished) {
181             [weakSelf.transport playbackComplete];                          // 3
182         }];
183     };
184
185     self.itemEndObserver =                                                  // 4
186         [[NSNotificationCenter defaultCenter] addObserverForName:name
187                                                           object:self.playerItem
188                                                            queue:queue
189                                                       usingBlock:callback];
190 }
191
192 #pragma mark - THTransportDelegate Methods
193
194 - (void)play {
195     [self.player play];
196 }
197
198 - (void)pause {
199     self.lastPlaybackRate = self.player.rate;
200     [self.player pause];
201 }
202
203 - (void)stop {
204     [self.player setRate:0.0f];
205     [self.transport playbackComplete];
206 }
207
208 - (void)jumpedToTime:(NSTimeInterval)time {
209     [self.player seekToTime:CMTimeMakeWithSeconds(time, NSEC_PER_SEC)];
210 }
211
212 - (void)scrubbingDidStart {                                                 // 1
213     self.lastPlaybackRate = self.player.rate;
214     [self.player pause];
215     [self.player removeTimeObserver:self.timeObserver];
216 }
217
218 - (void)scrubbedToTime:(NSTimeInterval)time {                               // 2
219     [self.playerItem cancelPendingSeeks];
220     [self.player seekToTime:CMTimeMakeWithSeconds(time, NSEC_PER_SEC) toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero];
221 }
222
223 - (void)scrubbingDidEnd {                                                   // 3
224     [self addPlayerItemTimeObserver];
225     if (self.lastPlaybackRate > 0.0f) {
226         [self.player play];
227     }
228 }
229
230
231 #pragma mark - Thumbnail Generation
232
233 - (void)generateThumbnails {
234
235     self.imageGenerator =                                                   // 1
236         [AVAssetImageGenerator assetImageGeneratorWithAsset:self.asset];
237
238     // Generate the @2x equivalent
239     self.imageGenerator.maximumSize = CGSizeMake(200.0f, 0.0f);             // 2
240
241     CMTime duration = self.asset.duration;
242
243     NSMutableArray *times = [NSMutableArray array];                         // 3
244     CMTimeValue increment = duration.value / 20;
245     CMTimeValue currentValue = 2.0 * duration.timescale;
246     while (currentValue <= duration.value) {
247         CMTime time = CMTimeMake(currentValue, duration.timescale);
248         [times addObject:[NSValue valueWithCMTime:time]];
249         currentValue += increment;
250     }
251
252     __block NSUInteger imageCount = times.count;                            // 4
253     __block NSMutableArray *images = [NSMutableArray array];
254
255     AVAssetImageGeneratorCompletionHandler handler;                         // 5
256
257     handler = ^(CMTime requestedTime,
258                 CGImageRef imageRef,
259                 CMTime actualTime,
260                 AVAssetImageGeneratorResult result,
261                 NSError *error) {
262
263         if (result == AVAssetImageGeneratorSucceeded) {                     // 6
264             UIImage *image = [UIImage imageWithCGImage:imageRef];
265             id thumbnail =
266                 [THThumbnail thumbnailWithImage:image time:actualTime];
267             [images addObject:thumbnail];
268         } else {
269             NSLog(@"Error: %@", [error localizedDescription]);
270         }
271
272         // If the decremented image count is at 0, we're all done.
273         if (--imageCount == 0) {                                            // 7
274             dispatch_async(dispatch_get_main_queue(), ^{
275                 NSString *name = THThumbnailsGeneratedNotification;
276                 NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
277                 [nc postNotificationName:name object:images];
278             });
279         }
280     };
281
282     [self.imageGenerator generateCGImagesAsynchronouslyForTimes:times       // 8
283                                               completionHandler:handler];
284
285
286 }
287
288
289 #pragma mark - Housekeeping
290
291 - (UIView *)view {
292     return self.playerView;
293 }
294
295 - (void)dealloc {
296     if (self.itemEndObserver) {                                             // 5
297         NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
298         [nc removeObserver:self.itemEndObserver
299                       name:AVPlayerItemDidPlayToEndTimeNotification
300                     object:self.player.currentItem];
301         self.itemEndObserver = nil;
302     }
303 }
304
305 @end

本类只提供 AVFoundation中的关于视频的一些播放暂停等等的控制功能,

界面需要另外一个view来展示,

控制单元也就是界面 跟 播放控制器 之间的通信同过一个协议来实现

这样需要在控制界面添加功能 都是通过协议来通信的,即实现了功能,也保持了很好的独立性。

这样用户完全可以自定义一套界面 ,依然能够使用AVFoundation的功能。

好了 ,本片文章就到此为止了。由于个人能力有限,如有错误之处,请帮忙给与指出,不胜感谢啊 。

转载于:https://www.cnblogs.com/machao/p/5669613.html

ZFPlayer 源码解读相关推荐

  1. ios html zfplayer,【iOS】ZFPlayer源码解读中

    前言 本篇继ZFPlayer源码解读基础之上,主要解析说明控制层与播放器,因为在上篇文章至现在并未提及丝毫关于这两个类业务的实现. 首先说下这两个类各自的职责. 控制层:主要负责响应与用户之间的交互, ...

  2. Bert系列(二)——源码解读之模型主体

    本篇文章主要是解读模型主体代码modeling.py.在阅读这篇文章之前希望读者们对bert的相关理论有一定的了解,尤其是transformer的结构原理,网上的资料很多,本文内容对原理部分就不做过多 ...

  3. Bert系列(三)——源码解读之Pre-train

    https://www.jianshu.com/p/22e462f01d8c pre-train是迁移学习的基础,虽然Google已经发布了各种预训练好的模型,而且因为资源消耗巨大,自己再预训练也不现 ...

  4. linux下free源码,linux命令free源码解读:Procps free.c

    linux命令free源码解读 linux命令free源码解读:Procps free.c 作者:isayme 发布时间:September 26, 2011 分类:Linux 我们讨论的是linux ...

  5. nodeJS之eventproxy源码解读

    1.源码缩影 !(function (name, definition) { var hasDefine = typeof define === 'function', //检查上下文环境是否为AMD ...

  6. PyTorch 源码解读之即时编译篇

    点击上方"AI遇见机器学习",选择"星标"公众号 重磅干货,第一时间送达 作者丨OpenMMLab 来源丨https://zhuanlan.zhihu.com/ ...

  7. Alamofire源码解读系列(九)之响应封装(Response)

    本篇主要带来Alamofire中Response的解读 前言 在每篇文章的前言部分,我都会把我认为的本篇最重要的内容提前讲一下.我更想同大家分享这些顶级框架在设计和编码层次究竟有哪些过人的地方?当然, ...

  8. Feflow 源码解读

    Feflow 源码解读 Feflow(Front-end flow)是腾讯IVWEB团队的前端工程化解决方案,致力于改善多类型项目的开发流程中的规范和非业务相关的问题,可以让开发者将绝大部分精力集中在 ...

  9. spring-session源码解读 sesion

    2019独角兽企业重金招聘Python工程师标准>>> spring-session源码解读 sesion 博客分类: java spring 摘要: session通用策略 Ses ...

最新文章

  1. 用NumPy genfromtxt导入数据
  2. 常用排序算法 - 稳定性和复杂度分析
  3. 大数据量表中,增加一个NOT NULL的新列
  4. redis 亿级查询速度_亿级流量系统架构之如何保证百亿流量下的数据一致性(上)...
  5. 大数据哈希学习: 现状与趋势
  6. linux 给权限命令,Linux小白实用命令--权限设置
  7. HTTP安全随想,第一指导原则:不要相信任何用户的输入[]
  8. 爬虫、请求库requests
  9. c++ 清除list占用的内存_pytorch内存泄露-dataloader
  10. sybase 数据库空间使用情况
  11. 2022全新抖音二维码生成工具源码+亲测可用
  12. 肖申克的救赎-救赎自己的心灵
  13. MMpose代码讲解之关键点Heatmap可视化
  14. 游戏角色动画:从入门到商用(一)
  15. 自动提交flag-python
  16. 【爬虫】python爬虫从入门到放弃
  17. 一致性哈希原理详解(虚拟节点)
  18. Unity基础:图片根据文字内容自适应大小(ContentSizeFitter的使用小技巧)
  19. 特征平台概述及技术选型
  20. c++处处有杀机,不小心就要掉坑里

热门文章

  1. 关系代数运算详细解释
  2. linux开放端口命令tcp,linux开放端口命令
  3. 宝塔Linux搭建python行不,利用宝塔+python+搭建falsk项目
  4. Android 计步传感器的实现
  5. 在Vue中使用Tinymce富文本编辑器+上传图片到七牛
  6. 提取图片RGB三通道数据+用RGB恢复原始图片
  7. 不一样的Gradle多渠道配置总结2
  8. C语言的文件IO操作,非常详细!!
  9. Python获取《姜子牙》和《哪吒》的票房对比和评价分析
  10. LeetCode二叉树系列——145.二叉树的后序遍历