Commit b3e3deb0cbd3f5614218b7ae79c97c4e14d4c70e
1 parent
10a65a4e
no message
Showing
31 changed files
with
1507 additions
and
3020 deletions
Too many changes to show.
To preserve performance only 31 of 78 files are displayed.
CNLivePlayer.podspec
| @@ -9,7 +9,7 @@ | @@ -9,7 +9,7 @@ | ||
| 9 | Pod::Spec.new do |s| | 9 | Pod::Spec.new do |s| |
| 10 | s.name = 'CNLivePlayer' | 10 | s.name = 'CNLivePlayer' |
| 11 | s.version = '0.0.1' | 11 | s.version = '0.0.1' |
| 12 | - s.summary = '播放器和推流器' | 12 | + s.summary = 'CNLivePlayer' |
| 13 | 13 | ||
| 14 | # This description is used to generate tags and improve search results. | 14 | # This description is used to generate tags and improve search results. |
| 15 | # * Think: What does it do? Why did you write it? What is the focus? | 15 | # * Think: What does it do? Why did you write it? What is the focus? |
| @@ -18,7 +18,7 @@ Pod::Spec.new do |s| | @@ -18,7 +18,7 @@ Pod::Spec.new do |s| | ||
| 18 | # * Finally, don't worry about the indent, CocoaPods strips it! | 18 | # * Finally, don't worry about the indent, CocoaPods strips it! |
| 19 | 19 | ||
| 20 | s.description = <<-DESC | 20 | s.description = <<-DESC |
| 21 | -网家家视频播放器和推流器. | 21 | +网家家视频播放器. |
| 22 | DESC | 22 | DESC |
| 23 | 23 | ||
| 24 | s.homepage = 'http://bj.gitlab.cnlive.com/ios-team/CNLivePlayer' | 24 | s.homepage = 'http://bj.gitlab.cnlive.com/ios-team/CNLivePlayer' |
| @@ -31,53 +31,17 @@ Pod::Spec.new do |s| | @@ -31,53 +31,17 @@ Pod::Spec.new do |s| | ||
| 31 | s.ios.deployment_target = '9.0' | 31 | s.ios.deployment_target = '9.0' |
| 32 | # s.public_header_files = 'CNLivePlayer/Classes/Player/CNLivePlayer.h' | 32 | # s.public_header_files = 'CNLivePlayer/Classes/Player/CNLivePlayer.h' |
| 33 | 33 | ||
| 34 | - # 头文件 | ||
| 35 | - s.subspec 'Header' do |ss| | ||
| 36 | - ss.source_files = 'CNLivePlayer/Classes/Header/*' | ||
| 37 | - end | ||
| 38 | - # 数据请求 | ||
| 39 | - s.subspec 'DataRequest' do |ss| | ||
| 40 | - ss.source_files = 'CNLivePlayer/Classes/DataRequest/*.{h,m}' | ||
| 41 | - end | ||
| 42 | - # 数据库 | ||
| 43 | - s.subspec 'Database' do |ss| | ||
| 44 | - ss.source_files = 'CNLivePlayer/Classes/Database/*.{h,m}' | ||
| 45 | - ss.dependency 'FMDB' | ||
| 46 | - end | ||
| 47 | - # 文件下载 | ||
| 48 | - s.subspec 'Download' do |ss| | ||
| 49 | - ss.source_files = 'CNLivePlayer/Classes/Download/*.{h,m}' | ||
| 50 | - ss.dependency 'CNLivePlayer/Header' | ||
| 51 | - ss.dependency 'CNLivePlayer/Database' | ||
| 52 | - ss.dependency 'ksyhttpcache' | ||
| 53 | - end | ||
| 54 | - | ||
| 55 | - # 播放器 | ||
| 56 | - s.subspec 'Player' do |ss| | ||
| 57 | - ss.source_files = 'CNLivePlayer/Classes/Player/*.{h,m}' | 34 | + s.source_files = 'CNLivePlayer/Classes/**/*' |
| 58 | 35 | ||
| 59 | - ss.dependency 'CNLivePlayer/Database' | ||
| 60 | - ss.dependency 'CNLivePlayer/Download' | ||
| 61 | - ss.dependency 'CNLivePlayer/DataRequest' | ||
| 62 | - ss.dependency 'CNLivePlayer/Header' | 36 | + s.public_header_files = 'CNLivePlayer/Classes/Player/CNLivePlayer.h','CNLivePlayer/Classes/Player/CNLivePlayerManager.h','CNLivePlayer/Classes/Header/CNLivePlayerDelegate.h','CNLivePlayer/Classes/Header/CNLivePlayerHeader.h','CNLivePlayer/Classes/Header/CNLivePlayerTypeDef.h' |
| 63 | 37 | ||
| 64 | - ss.dependency 'libksygpulive/KSYGPUResource' | ||
| 65 | - ss.dependency 'libksygpulive/libksygpulive' | ||
| 66 | - ss.dependency 'GPUImage' | ||
| 67 | - | ||
| 68 | - ss.vendored_frameworks = 'CNLivePlayer/Classes/*.framework' | ||
| 69 | - end | ||
| 70 | - | ||
| 71 | - # 推流器 | ||
| 72 | - s.subspec 'Streamer' do |ss| | ||
| 73 | - ss.source_files = 'CNLivePlayer/Classes/Streamer/*.{h,m}' | ||
| 74 | - ss.dependency 'CNLivePlayer/Player' | 38 | + s.dependency 'PLPlayerKit' |
| 75 | 39 | ||
| 76 | - end | ||
| 77 | - | ||
| 78 | - s.static_framework = true | ||
| 79 | - s.libraries = 'z', 'bz2', 'stdc++' | ||
| 80 | - s.frameworks = 'UIKit', 'MediaPlayer', 'VideoToolbox' | 40 | + s.dependency 'FMDB' |
| 41 | + | ||
| 42 | + s.libraries = 'z', 'bz2', 'c++', 'resolv', 'iconv', 'sqlite3.0' | ||
| 43 | + | ||
| 44 | + s.frameworks = 'UIKit', 'AVKit', 'Foundation', 'AVFoundation', 'MediaPlayer', 'VideoToolbox', 'CoreTelephony', 'CoreMedia', 'OpenGLES', 'QuartzCore', 'Accelerate', 'AudioToolbox', 'CoreAudio','CoreGraphics' | ||
| 81 | 45 | ||
| 82 | 46 | ||
| 83 | end | 47 | end |
CNLivePlayer/Classes/CNLiveMsgTools.framework/CNLiveMsgTools deleted
100755 → 0
No preview for this file type
CNLivePlayer/Classes/CNLiveMsgTools.framework/Emoji.plist deleted
100644 → 0
No preview for this file type
CNLivePlayer/Classes/CNLiveMsgTools.framework/Headers/CNLiveMsgManager.h deleted
100644 → 0
| 1 | -// | ||
| 2 | -// CNLiveMsgManager.h | ||
| 3 | -// CNLiveMsgTools | ||
| 4 | -// | ||
| 5 | -// Created by cnliveJunBo on 17/2/23. | ||
| 6 | -// Copyright © 2017年 cnlive. All rights reserved. | ||
| 7 | -// | ||
| 8 | - | ||
| 9 | -#import <Foundation/Foundation.h> | ||
| 10 | - | ||
| 11 | -@interface CNLiveMsgManager : NSObject | ||
| 12 | - | ||
| 13 | -@property (nonatomic, readonly, assign) BOOL isTestEnvironment; | ||
| 14 | - | ||
| 15 | - | ||
| 16 | -+ (NSString *)getCNLiveMsgVersion; | ||
| 17 | - | ||
| 18 | - | ||
| 19 | -+ (instancetype)sharedCNLiveMsgManager; | ||
| 20 | - | ||
| 21 | - | ||
| 22 | -- (void)setToolsInfoWithInfoName:(NSString *)infoName | ||
| 23 | - infoUrl:(NSString *)infoUrl; | ||
| 24 | - | ||
| 25 | - | ||
| 26 | -- (NSDictionary *)getToolsInfo; | ||
| 27 | - | ||
| 28 | - | ||
| 29 | -- (NSDictionary *)messageWithImage:(id)image; | ||
| 30 | - | ||
| 31 | - | ||
| 32 | -- (NSDictionary *)messageWithVoice:(NSData *)voiceData duration:(long)duration; | ||
| 33 | - | ||
| 34 | - | ||
| 35 | -- (void)initCNLiveMsgToolsWithAppKey:(NSString *)appKey appId:(NSString *)appId; | ||
| 36 | - | ||
| 37 | - | ||
| 38 | -- (void)initCNLiveMsgToolsWithAppKey:(NSString *)appKey appId:(NSString *)appId isTestEnvironment:(BOOL)isTestEnvironment; | ||
| 39 | - | ||
| 40 | - | ||
| 41 | -- (void)onLineWithMsgToolsInfoId:(NSString *)infoId | ||
| 42 | - infoName:(NSString *)infoName | ||
| 43 | - infoUrl:(NSString *)infoUrl | ||
| 44 | - success:(void (^)(NSString *toolsId))successBlock | ||
| 45 | - error:(void (^)(NSInteger errors))errorBlock; | ||
| 46 | - | ||
| 47 | - | ||
| 48 | -- (void)justOnLineSuccess:(void (^)(NSString *toolsId))successBlock | ||
| 49 | - error:(void (^)(NSInteger errors))errorBlock; | ||
| 50 | - | ||
| 51 | - | ||
| 52 | -- (void)stealthCNLiveMsgTools; | ||
| 53 | - | ||
| 54 | - | ||
| 55 | -- (void)offLineCNLiveMsgTools:(BOOL)isReceivePush; | ||
| 56 | - | ||
| 57 | - | ||
| 58 | -- (void)gatherTogether:(NSString *)targetId | ||
| 59 | - messageCount:(int)messageCount | ||
| 60 | - success:(void (^)(NSString *targetId))successBlock | ||
| 61 | - error:(void (^)(NSInteger errors, NSString *targetId))errorBlock; | ||
| 62 | - | ||
| 63 | - | ||
| 64 | -- (void)gatherIfNullCreate:(NSString *)targetId | ||
| 65 | - messageCount:(int)messageCount | ||
| 66 | - success:(void (^)(NSString *targetId))successBlock | ||
| 67 | - error:(void (^)(NSInteger errors, NSString *targetId))errorBlock; | ||
| 68 | - | ||
| 69 | - | ||
| 70 | -- (void)gatherTogether:(NSString *)targetId | ||
| 71 | - isCreate:(BOOL)isCreate | ||
| 72 | - messageCount:(int)messageCount | ||
| 73 | - success:(void (^)(NSString *targetId))successBlock | ||
| 74 | - error:(void (^)(NSInteger errors, NSString *targetId))errorBlock; | ||
| 75 | - | ||
| 76 | - | ||
| 77 | -- (void)disperse:(NSString *)targetId | ||
| 78 | - success:(void (^)(NSString *targetId))successBlock | ||
| 79 | - error:(void (^)(NSInteger errors, NSString *targetId))errorBlock; | ||
| 80 | - | ||
| 81 | - | ||
| 82 | -- (void)sendCarrier:(NSDictionary *)carrier | ||
| 83 | - type:(NSInteger)type | ||
| 84 | - targetId:(NSString *)targetId | ||
| 85 | - success:(void (^)(NSString *carrier, NSString *messageId, NSString *targetId, NSInteger type, NSString *infoId, NSString *infoName, NSString *infoUrl, long long goTime, long long comeTime))successBlock | ||
| 86 | - error:(void (^)(NSInteger errors, NSString *messageId, NSString *targetId))errorBlock; | ||
| 87 | - | ||
| 88 | - | ||
| 89 | -- (void)sendMedia:(NSDictionary *)media type:(NSInteger)type | ||
| 90 | - targetId:(NSString *)targetId | ||
| 91 | - pushContent:(NSString *)pushContent | ||
| 92 | - pushData:(NSString *)pushData | ||
| 93 | - progress:(void (^)(int progress, NSString *messageId))progressBlock | ||
| 94 | - success:(void (^)(NSString *messageId, NSString *targetId, NSInteger type, NSString *infoId, NSString *infoName, NSString *infoUrl))successBlock | ||
| 95 | - error:(void (^)(NSInteger errorCode, NSString *messageId))errorBlock | ||
| 96 | - cancel:(void (^)(NSString * messageId))cancelBlock; | ||
| 97 | - | ||
| 98 | - | ||
| 99 | -- (NSInteger)getOnLineStatus; | ||
| 100 | - | ||
| 101 | - | ||
| 102 | -- (BOOL)judgeLoginStatus; | ||
| 103 | - | ||
| 104 | - | ||
| 105 | -- (NSArray *)getLatestCarriers:(NSInteger)type targetId:(NSString *)targetId count:(int)count; | ||
| 106 | - | ||
| 107 | -- (void)getMeetingPointContent:(NSString *)pointId | ||
| 108 | - success:(void (^)(int totalMemberCount))successBlock | ||
| 109 | - error:(void (^)(NSInteger errors))errorBlock; | ||
| 110 | - | ||
| 111 | -@end |
CNLivePlayer/Classes/CNLiveMsgTools.framework/Info.plist deleted
100644 → 0
No preview for this file type
CNLivePlayer/Classes/CNLiveMsgTools.framework/RCConfig.plist deleted
100644 → 0
No preview for this file type
CNLivePlayer/Classes/CNLiveMsgTools.framework/en.lproj/RongCloudKit.strings deleted
100644 → 0
No preview for this file type
CNLivePlayer/Classes/CNLiveMsgTools.framework/release_notes_ios.txt deleted
100755 → 0
| 1 | -Platform: iOS | ||
| 2 | -Version: 2.8.6 Stable | ||
| 3 | -Date:2017/02/15 | ||
| 4 | - | ||
| 5 | -Changing log: | ||
| 6 | - | ||
| 7 | -2.8.6 Stable | ||
| 8 | -------------------------------- | ||
| 9 | -1、优化并修复了一些偶现的 BUG,增强了稳定性。 | ||
| 10 | -2、增加了聊天室连接状态的监听。 | ||
| 11 | -3、更新红包 SDK。 | ||
| 12 | -4、更新表情 SDK。 | ||
| 13 | -5、优化了 VoIP 部分逻辑。 | ||
| 14 | - | ||
| 15 | -2.8.5 Dev | ||
| 16 | -------------------------------- | ||
| 17 | -1、增加了 VoIP 网络质量回调接口。 | ||
| 18 | -2、增加了小能客服。 | ||
| 19 | -3、优化了 imageCell 和 locationCell。 | ||
| 20 | -4、优化了相册加载速度。 | ||
| 21 | -5、修改了日志格式。 | ||
| 22 | -6、修改了聊天室心跳机制,加入聊天室后开启心跳。 | ||
| 23 | -7、修复了表情区域 ,下载、删除动态表情时,Tab 顺序错乱的问题。 | ||
| 24 | -8、删除了音视频录制接口。 | ||
| 25 | -9、更新了红包 SDK。 | ||
| 26 | -10、进行了多 cmp 连接优化。 | ||
| 27 | - | ||
| 28 | -2.8.4 Stable | ||
| 29 | -------------------------------- | ||
| 30 | -1、客服增加超时提醒配置功能。 | ||
| 31 | -2、修复了发送图片和文件时没有返回 messageUId 的问题。 | ||
| 32 | -3、修复了一些偶现的 BUG,增强了稳定性。 | ||
| 33 | - | ||
| 34 | - | ||
| 35 | -2.8.3 Stable | ||
| 36 | -------------------------------- | ||
| 37 | -1、优化了群组通知的解析问题。 | ||
| 38 | -2、修复了一些UI细节。 | ||
| 39 | - | ||
| 40 | -2.8.2 Dev | ||
| 41 | -------------------------------- | ||
| 42 | -1、针对苹果ATS的策略进行了适配。 | ||
| 43 | -如果您手动管理依赖,需要在 IMKit 的工程中增加 SafariServices.framework 引用;如果您使用 CocoaPods,请使用 RongCloudIM 新的 spec。 | ||
| 44 | -您可以根据您App的迭代情况,开启和设置https,具体可以参考官网文档。 | ||
| 45 | -2、优化了会话列表自定义 Cell 的 UI 细节。 | ||
| 46 | - | ||
| 47 | -2.8.1 Stable | ||
| 48 | -------------------------------- | ||
| 49 | -1、修复了某些机型上阅读回执的显示问题。 | ||
| 50 | -2、优化了会话列表的刷新。 | ||
| 51 | - | ||
| 52 | -2.8.0 Dev | ||
| 53 | -------------------------------- | ||
| 54 | -1、会话列表、会话界面的重构。 | ||
| 55 | -2、输入法表情、加号区域的重构。 | ||
| 56 | -3、增加了消息搜索功能,现在您可以进行消息搜索了。马上试用吧。 | ||
| 57 | -4、群消息回执增加了详情,现在您可以知道哪些人看过消息,也能知道哪些人没有看过消息了。 | ||
| 58 | -5、增加了文件消息发送过程中的取消功能。 | ||
| 59 | -6、我们将日志进行了标准化,并且支持您通过接口访问。 | ||
| 60 | -7、还修复了很多的小问题。 | ||
| 61 | - | ||
| 62 | -2.7.3 Stable | ||
| 63 | -------------------------------- | ||
| 64 | -1、优化了消息的时间和状态显示 | ||
| 65 | -2、优化了文本超链接的显示效果 | ||
| 66 | - | ||
| 67 | -2.7.2 Stable | ||
| 68 | -------------------------------- | ||
| 69 | -1、修复了几个微小的 BUG。 | ||
| 70 | - | ||
| 71 | -2.7.1 Dev | ||
| 72 | -------------------------------- | ||
| 73 | -1、增加了群组、讨论组消息阅读状态功能,发送一条文本消息后,可查看该消息的阅读用户数。 | ||
| 74 | -2、优化多端群组、讨论组未读消息数同步功能。 | ||
| 75 | -3、历史消息云存储功能,更改为通过导航下发配置控制功能是否开启,去掉了 IMKit 中的开关。 | ||
| 76 | - | ||
| 77 | -2.7.0 Dev | ||
| 78 | -------------------------------- | ||
| 79 | -1、新增了文件消息功能,消息标识为 RC:FileMsg ,消息类名 FileMessage ,需要在”+“号区添加功能后才能使用。 | ||
| 80 | -2、新增了群组、讨论组消息回执功能。 | ||
| 81 | -3、新增了 VoIP 录制功能,录制后文件会自动存储到服务器。 | ||
| 82 | -4、VoIP 支持视频分辨率设置功能。 | ||
| 83 | -5、针对历史消息云存储服务,在 IMKit 中实现了获取历史消息后本地自动存储功能,在 IMLib 中调用获取云端历史消息接口,也会自动存储到本地数据库中。 | ||
| 84 | -6、优化了会话列表及会话中时间显示规则。 | ||
| 85 | -7、对会话界面的 UI 显示样式进行了优化。 | ||
| 86 | - | ||
| 87 | -2.6.11 Stable | ||
| 88 | -------------------------------- | ||
| 89 | -1、这个版本我们完整的适配了 iOS 10。苹果的各项策略总是在不断变更,我们会根据其变化第一时间对 SDK 进行升级。对于近期要发布 App 的客户,我们建议您升级到这个版本。 | ||
| 90 | -2、实时地理位置共享功能,从收费使用变为免费,默认功能打开,请您参考我们的源码来实现。未在源代码里实现该功能的用户不受此次升级影响。 | ||
| 91 | - | ||
| 92 | -2.6.10 Stable | ||
| 93 | -------------------------------- | ||
| 94 | -1、从 8 月 9 日开始,因为苹果针对 iOS10 更新了私有接口审核的标准,导致我们一个内部函数因为重名而违规。会造成在最近几天内提交 App Store 的申请被拒,我们紧急更新了这个版本。已经上线的客户不受影响。您在升级的时候也不需要进行任何接口改动。 | ||
| 95 | -2、针对特别的海外客户和专有部署客户优化,增加一个针对导航的解析功能。普通客户不受影响。 | ||
| 96 | - | ||
| 97 | -2.6.9 Stable | ||
| 98 | -------------------------------- | ||
| 99 | -1、优化了 @ 消息的细节,增加了国际化。 | ||
| 100 | -2、修复了在有 1 万条离线消息以上的情况下,收消息比较慢的问题。解决后 1 万条以上的离线消息可以在 1 分钟以内接收完毕。 | ||
| 101 | - | ||
| 102 | -2.6.8 Dev | ||
| 103 | -------------------------------- | ||
| 104 | -1、群组、讨论组中增加了 @ 功能 | ||
| 105 | -2、增加了消息撤回功能 | ||
| 106 | -3、增加了客服技能组功能 | ||
| 107 | - | ||
| 108 | -2.6.7 Stable | ||
| 109 | -------------------------------- | ||
| 110 | -1、为了美化,进行了一些细微的 UI 更新,包括聊天界面,消息气泡背景,边距等等。并不影响集成使用。 | ||
| 111 | - | ||
| 112 | -2.6.6 Stable | ||
| 113 | -------------------------------- | ||
| 114 | -1、对超长或超宽的图片消息,进行了优化,显示更加美观清晰。 | ||
| 115 | -2、对发送地理位置、会话列表等界面的 UI 设计进行了细微的更新以确保美观。 | ||
| 116 | - | ||
| 117 | -2.6.5 Stable | ||
| 118 | -------------------------------- | ||
| 119 | -1、这个版本主要对 UI 交互进行了一些优化,包括对地理位置消息的图片,图片选择器,输入区域等等。 | ||
| 120 | - | ||
| 121 | -2.6.4 Stable | ||
| 122 | -------------------------------- | ||
| 123 | -1、配合融云的 IM 软件 SealTalk 的发布,对 IMKit 的 UI 进行了一些细节优化,增强了一些体验。不影响您的客户使用。 | ||
| 124 | -2、优化了草稿在会话列表中的显示。 | ||
| 125 | -3、优化了聊天室拉取大量消息时的响应速度。 | ||
| 126 | -4、增加了一个错误码 30015 (RC_CONN_OVERFREQUENCY),如果频繁的调用 connect,会返回此错误码。您只需要调用 connect 函数一次即可。 | ||
| 127 | - | ||
| 128 | -2.6.3 Stable | ||
| 129 | -------------------------------- | ||
| 130 | -1、优化重连逻辑,增强网络连接的可靠性。 | ||
| 131 | -2、修改了一些小的的 UI 问题。 | ||
| 132 | - | ||
| 133 | -2.6.2 Dev | ||
| 134 | -------------------------------- | ||
| 135 | -1、在 2.6.0 版本上出现了几例运营商网络劫持导致连接失败的报告,在这个版本上进行了修复。 | ||
| 136 | - | ||
| 137 | -2.6.1 Dev | ||
| 138 | -------------------------------- | ||
| 139 | -1、提升了用户信息缓存的性能。 | ||
| 140 | - | ||
| 141 | - | ||
| 142 | -2.6.0 Dev | ||
| 143 | -------------------------------- | ||
| 144 | -1、增加了用户信息缓存,如果您的 App 没有本地用户信息的缓存,将很大的节省您的开发量,同时支持离线缓存,性能更好。使用的方法没有改变。 | ||
| 145 | -2、优化了图片选择器的 UI 功能。 | ||
| 146 | -3、全面增加了对 HTTPS 的支持。 | ||
| 147 | -4、针对消息量非常大的情况下,性能进行了优化。 | ||
| 148 | -5、对客服的业务进行了一些优化。 | ||
| 149 | - | ||
| 150 | - | ||
| 151 | -2.5.2 Stable | ||
| 152 | -------------------------------- | ||
| 153 | -1、针对 iOS9 以上,支持 IPv6。请注意苹果规定从6月1号起,提交到 App Store 的版本都必须支持 IPv6-only 网络,您需要升级到这个版本。 | ||
| 154 | -2、针对消息量非常大的聊天室进行优化,在网络出错情况下减少了等待时间更快速的自动重连。不影响聊天室以外的场景。 | ||
| 155 | - | ||
| 156 | - | ||
| 157 | -2.5.1 Stable | ||
| 158 | -------------------------------- | ||
| 159 | -1、优化了部分客服业务的功能。 | ||
| 160 | -2、修复了一个小概率问题,当数据库操作出现意外问题的时候,连接会主动断开。再次重连后可以继续使用。以确保不会因为数据库操作异常而丢失消息。 | ||
| 161 | - | ||
| 162 | -2.5.0 Stable | ||
| 163 | -------------------------------- | ||
| 164 | -1、解决了一个与第三方库冲突问题。 | ||
| 165 | -2、优化了会话列表在 iOS7 及以下系统中 UI 界面的显示效果。 | ||
| 166 | - | ||
| 167 | -2.4.11 Stable | ||
| 168 | -------------------------------- | ||
| 169 | -1、对客服业务进行了小幅度的优化。 | ||
| 170 | - | ||
| 171 | -2.4.10 Dev | ||
| 172 | -------------------------------- | ||
| 173 | -1、发布了新版本的客服界面,请参考开源代码的 RCDCustomServiceViewController 来实现您的客服。新版本客服在性能功能上得到大幅优化,并且您可以自行配置您的界面。之前版本的客服界面仍然兼容。 | ||
| 174 | -2、RCReceivedStatus 增加一个属性 ReceivedStatus_RETRIEVED。表示该消息在当前端离线期间,已经被其他端收取过。您可以根据这个状态更新您的 UI 界面,比如已经被收取过就不再弹出提示。如果您开通了多端登录,请注意您的判断条件,对于没有开通多端同步的用户可以忽略。 | ||
| 175 | -3、再次提高了聊天室的性能。尤其优化了超大型聊天室的消息性能。 | ||
| 176 | -4、同时修复了功能性能上的一些问题。 | ||
| 177 | - | ||
| 178 | -2.4.9 Dev | ||
| 179 | -------------------------------- | ||
| 180 | -1、修复 VoIP 有时显示用户信息错误的问题,修复有时连续拨打 VoIP 不能很好接通的问题。 | ||
| 181 | -2、优化了一下会话界面消息体显示。 | ||
| 182 | -3、增加接口,getMessageByUId,从消息的唯一 ID 获得消息类。 | ||
| 183 | - | ||
| 184 | - | ||
| 185 | -2.4.8 Stable | ||
| 186 | -------------------------------- | ||
| 187 | -1、富文本消息修复了一个问题,使修改颜色正常起作用。 | ||
| 188 | -2、修复了一个短时间内连续多次点击发送消息,有的消息会不显示的问题。 | ||
| 189 | -3、优化了后台自动重连时的性能。 | ||
| 190 | - | ||
| 191 | - | ||
| 192 | -2.4.7 Stable | ||
| 193 | -------------------------------- | ||
| 194 | -1、消息阅读状态和对方正在输入状态等功能提交到稳定版。 | ||
| 195 | -2、修复了一些多端同步和发件箱功能引起的问题。 | ||
| 196 | - | ||
| 197 | - | ||
| 198 | -2.4.6 Dev | ||
| 199 | -------------------------------- | ||
| 200 | -1、新增消息阅读状态功能。发送端可以看到接收端是否阅读了该消息,当前只支持单聊。 | ||
| 201 | -2、新增对方正在输入状态功能。动态显示对方的输入状态,当前只支持单聊。 | ||
| 202 | -3、消息多端同步增加发件箱功能,发送端发出的消息也会被其他端拉取下来。 | ||
| 203 | -4、语音消息的最大长度可以设置,比如您可以设置为您的客户最多只能录制并发送 30 秒的语音,默认是 60 秒。 | ||
| 204 | - | ||
| 205 | -2.4.5 Stable | ||
| 206 | -------------------------------- | ||
| 207 | -1、略微增强了聊天室的性能。修复了一个问题,当客户端同一个 id 加入两个不同的聊天室时,增加了逻辑避免出现显示错误。 | ||
| 208 | -2、当发送消息失败时,如果原因是网络连接已经断开,会自动做一次重连。开发者不需要对此修改做额外处理。 | ||
| 209 | - | ||
| 210 | -2.4.4 Stable | ||
| 211 | -------------------------------- | ||
| 212 | -1、增加读取消息接口getMessage:,可以根据messageId来读取消息。 | ||
| 213 | -2、优化了聊天室逻辑,确保在网络较差情况下不会聊天室掉线。 | ||
| 214 | -3、增加查询聊天室信息接口getChatRoomInfo,您可以查询聊天室成员数目和部分成员ID。 | ||
| 215 | - | ||
| 216 | -2.4.3 Stable | ||
| 217 | -------------------------------- | ||
| 218 | - | ||
| 219 | -1、增加 messageUId(Universal Id),您可以通过该 UId 匹配一条具体的消息。 | ||
| 220 | - | ||
| 221 | -2、增加聊天室接口 JoinExistChatroom,加入一个存在的聊天室,如果聊天室不存在则加入不成功。 | ||
| 222 | - | ||
| 223 | - | ||
| 224 | -2.4.2 Stable | ||
| 225 | -------------------------------- | ||
| 226 | - | ||
| 227 | -1、增加了聊天室(Chatroom)禁言、封禁两个功能。请通过 Server API 使用。当前用户被禁言或者被封禁后客户端会显示提示。 | ||
| 228 | - | ||
| 229 | - | ||
| 230 | - | ||
| 231 | -2.4.1 Stable | ||
| 232 | -------------------------------- | ||
| 233 | - | ||
| 234 | -1、文本消息和富文本消息,可以通过 RCAttributedLable 的 attributedStrings 属性,给一些文字添加自定义的事件。请参考代码注释中的示例代码。 | ||
| 235 | -2、修复了一个遗留问题,如果您通过 enableMessageAttachUserInfo 在每条消息里携带用户信息,在 IMKit 里会强制显示该用户信息,请打开了这个开关的用户注意。 | ||
| 236 | - | ||
| 237 | - | ||
| 238 | -2.4.0 Stable | ||
| 239 | -------------------------------- | ||
| 240 | - | ||
| 241 | -1、修复了一个因为在 IMKit 里直接调用 SendMessage ,极特殊状态下因为网络速度导致返回太快,发送状态没有更新的问题。 | ||
| 242 | - | ||
| 243 | -2、增加了一种不保存、不计数的 RCCommandMessage(ObjectName 为 RC:CmdMsg),您需要这种类型的消息时可以直接使用,不需要再自定义。 | ||
| 244 | - | ||
| 245 | - | ||
| 246 | - | ||
| 247 | - | ||
| 248 | -2.3.9 Stable | ||
| 249 | -------------------------------- | ||
| 250 | -1、增加功能:未读的语音消息点击后默认连续播放。 | ||
| 251 | -2、增加功能:会话列表界面每条会话的头像右上角的未读数显示可以配置,可以改成红点而不显示未读数。 | ||
| 252 | -3、提高了 UI 显示性能。 | ||
| 253 | - | ||
| 254 | -2.3.8 Stable | ||
| 255 | -------------------------------- | ||
| 256 | - | ||
| 257 | -1、在 IMKit 中,重新封装了 sendMessage 方法,实现了您在使用这个方法发送消息后自动更新 UI 界面。 | ||
| 258 | - | ||
| 259 | -2、增加未注册的消息自定义显示功能,通过设置 showUnkownMessage 为 YES,实现在 App 中提示“当前版本不支持查看此消息”。默认值为 NO,没有该需求的用户不用做任何更改。 | ||
| 260 | - | ||
| 261 | -3、美化了公众服务相关菜单和界面的 UI 展示。 | ||
| 262 | - | ||
| 263 | - | ||
| 264 | -2.3.7 Stable | ||
| 265 | -------------------------------- | ||
| 266 | -1、新功能语音消息点击后自动播放,增加一个配置可以关闭和打开该功能。 | ||
| 267 | -2、优化了输入框区域点击表情和键盘图片后的交互过程。 | ||
| 268 | -3、美化了公众服务图文消息的显示效果。 | ||
| 269 | - | ||
| 270 | - | ||
| 271 | -2.3.6 Stable | ||
| 272 | -------------------------------- | ||
| 273 | -1、增加功能:未读的语音消息点击后默认连续播放。 | ||
| 274 | -2、增加功能:会话列表界面每条会话的头像右上角的未读数显示可以配置,可以改成红点而不显示未读数。 | ||
| 275 | -3、提高了 UI 显示性能。 | ||
| 276 | - | ||
| 277 | -2.3.5 Stable | ||
| 278 | -------------------------------- | ||
| 279 | -1、略微调整了网络状态栏、选择图片按钮的布局设计,以更加美观。 | ||
| 280 | - | ||
| 281 | - | ||
| 282 | - | ||
| 283 | - | ||
| 284 | -2.3.4 Stable | ||
| 285 | -------------------------------- | ||
| 286 | -1、增加了语音消息和图片消息质量的配置文件,支持用户在融云平台支持的范围内配置。 | ||
| 287 | - | ||
| 288 | -2、优化了输入界面的显示效果。 | ||
| 289 | - | ||
| 290 | -3、增加了稳定性。 | ||
| 291 | - | ||
| 292 | - | ||
| 293 | - | ||
| 294 | - | ||
| 295 | -2.3.3 Stable | ||
| 296 | -------------------------------- | ||
| 297 | - | ||
| 298 | -1、优化了会话界面下 UI 显示效率。 | ||
| 299 | -2、增加了对 iOS6.0 系统上的一些问题的修改。 | ||
| 300 | -3、修改了iOS7.0 下,当未读消息非常多的时候,反复下拉消息有一定概率崩溃的问题。 | ||
| 301 | -4、增加了对 XCode7 上的 Archive 支持。 | ||
| 302 | - | ||
| 303 | - | ||
| 304 | - | ||
| 305 | - | ||
| 306 | -2.3.2 Stable | ||
| 307 | -------------------------------- | ||
| 308 | - | ||
| 309 | -1、增加了一个 ImageMessage 的构造方法,从这个版本起可以支持通过参数发送原图。 | ||
| 310 | - | ||
| 311 | -2、增加群组禁言功能,请配合 Server API 使用。 | ||
| 312 | - | ||
| 313 | -3、修复了当取消关注公众服务号同时默认删除公众号会话时的一个显示错误。 | ||
| 314 | - | ||
| 315 | -4、修复了扩展区域功能多于 8 个时,左右滑动时的问题。少于 8 个并不会有问题。 | ||
| 316 | - | ||
| 317 | -5、略微修改了气泡和名称的布局使其对齐。 | ||
| 318 | - | ||
| 319 | -6、修复了 VoIP 的问题,目前在 iOS 各版本上使用正常。 | ||
| 320 | - | ||
| 321 | -2.3.1 Dev | ||
| 322 | -------------------------------- | ||
| 323 | - | ||
| 324 | -1、增加了对 BitCode 编译选项,更好的支持XCode7和iOS9。 | ||
| 325 | -2、将第三方开源语音库 libopencore-armnb.a 独立出来,以方便您同时集成其他第三方组件时避免冲突,请注意把这个库单独包含到您的工程里。 | ||
| 326 | -3、增加了远程通知数据统计,使用 recordLaunchOptionsEvent,recordRemoteNotificationEvent,recordLocalNotificationEvent,您可以将用户点击系统推送的数据记录到融云的后台。 | ||
| 327 | - | ||
| 328 | -2.3.0 Stable | ||
| 329 | -------------------------------- | ||
| 330 | -1、当您需要使用公众服务会话时,您需要在集成会话列表时,主动在 onSelectedTableRow 中添加 RC_CONVERSATION_MODEL_TYPE_PUBLIC_SERVICE,这是为了实现一些用户的自定义需求,请参考 Demo App 源码。 | ||
| 331 | -2、开放了公众号的图文消息点击事件,覆写 didTapImageTxtMsgCell 方法。可以实现自定义跳转。 | ||
| 332 | -3、替换了一个有可能跟其他 SDK 产生冲突的库。 | ||
| 333 | -4、实现了一些新客户的需求。 | ||
| 334 | - | ||
| 335 | -2.2.11 Dev | ||
| 336 | -------------------------------- | ||
| 337 | - | ||
| 338 | -1、修复了客户反馈的一些问题,增强了稳定性。 | ||
| 339 | - | ||
| 340 | - | ||
| 341 | - | ||
| 342 | - | ||
| 343 | -2.2.10 Stable | ||
| 344 | -------------------------------- | ||
| 345 | - | ||
| 346 | -1、增加了对系统会话未读消息的计数功能。 | ||
| 347 | -2、优化了公众服务号,单图文、多图文界面的显示。 | ||
| 348 | -3、增加了接口 sendImageMessage:pushContent:appUpload: ,可实现发送图片消息时将图片上传到自已的服务器发送。 | ||
| 349 | -4、会话中阅读新消息时,现实新消息与历史消息分界线提示功能。 | ||
| 350 | -5、增加了 Web 端和手机端用户同时在线状态下,Web 端发送信息手机端能同步发送的功能。 | ||
| 351 | -6、修改 IMKit 和 IMLib 支持最低版本号为 iOS 6.0。 | ||
| 352 | - | ||
| 353 | - | ||
| 354 | -2.2.9 Dev | ||
| 355 | -------------------------------- | ||
| 356 | -1、修复了轻微的界面问题。 | ||
| 357 | - | ||
| 358 | -2.2.8 Stable | ||
| 359 | -------------------------------- | ||
| 360 | -1、实时地理位置共享功能发布,您可以在融云的 Demo App 上体验该功能。来试试通过 RCRealTimeLocationManager 实现您 App 自己的实时位置共享吧。 | ||
| 361 | -2、修复了一些界面显示相关的小问题。 | ||
| 362 | - | ||
| 363 | -2.2.7 Stable | ||
| 364 | -------------------------------- | ||
| 365 | -1、在会话界面上下滚动时,如果收到新消息,不会自动显示新消息而会停留在当前位置。 | ||
| 366 | - | ||
| 367 | -2、增加了录音时来电切到后台的处理。 | ||
| 368 | - | ||
| 369 | -3、解决了一个在 iOS6.0 上的兼容问题。 | ||
| 370 | - | ||
| 371 | - | ||
| 372 | - | ||
| 373 | -2.2.6 Stable | ||
| 374 | -------------------------------- | ||
| 375 | -1、开放 appendAndDisplayMessage,用户可以自己加入消息并刷新会话界面。 | ||
| 376 | - | ||
| 377 | -2、为头像增加长按点击事件。 | ||
| 378 | - | ||
| 379 | -3、修复了一个文本消息不响应点击事件的 BUG。 | ||
| 380 | - | ||
| 381 | -4、修复了一个图片库界面显示不美观的问题。 | ||
| 382 | - | ||
| 383 | -5、修复了一个公众服务的内置浏览器问题。 | ||
| 384 | - | ||
| 385 | - | ||
| 386 | -2.2.5 Stable | ||
| 387 | -------------------------------- | ||
| 388 | -1、增加了正在连接中的状态。 | ||
| 389 | -2、增强了保护,对于通过 Server API 发送消息传入错误参数导致的问题全面保护。 | ||
| 390 | -3、位置消息增加长按删除。 | ||
| 391 | -4、设置消息免打扰由于网络原因失败时会弹出提示。 | ||
| 392 | -5、增加了界面上的一些定制化功能。 | ||
| 393 | -6、保护了一个发送系统广播时,由于没有针对发送广播的 id 设置用户信息,会导致本地通知不提示的问题。 | ||
| 394 | -7、共享服务平台自定义菜单的发送消息功能已经上线。 | ||
| 395 | - | ||
| 396 | -2.2.4 Stable | ||
| 397 | -------------------------------- | ||
| 398 | -1、删除 reconnect 方法。使用融云 SDK 时,connect 只需要执行一次。对于有场景需要重连时,比如 Token 过期,请使用 connect 方法。其他异常请不要或尽量避免调用重连。 | ||
| 399 | -2、修改了一些界面布局的问题,进行了界面优化。 | ||
| 400 | -3、修改了一个发送图片不显示进度条的问题。 | ||
| 401 | - | ||
| 402 | -2.2.3 Dev | ||
| 403 | -------------------------------- | ||
| 404 | - | ||
| 405 | -1、删除 reconnect 方法,请不要使用它进行重连。connect 只需要执行一次。对于有特殊业务场景强烈需要重连的用户,请使用 connect 方法。 | ||
| 406 | -2、修改了 joinChatRoom 接口,当加入聊天室时,传入 messageConunt 为 -1 表示不拉取历史消息。 | ||
| 407 | -3、为一些客户的定制化需求优化了显示相关的功能。 | ||
| 408 | - | ||
| 409 | -2.2.2 Stable | ||
| 410 | -------------------------------- | ||
| 411 | - | ||
| 412 | -1、initWithAppKey 去掉参数 DeviceToken,因为初始化阶段很难获得 DeviceToeken。 | ||
| 413 | - | ||
| 414 | -2、优化了 setDeviceToken 方法,增加了缓存。 | ||
| 415 | - | ||
| 416 | -3、增强了重连机制,增加了一些特殊的网络错误的自动重连。 | ||
| 417 | - | ||
| 418 | -4、修复了一些 iOS 6 系统下的异常。 | ||
| 419 | - | ||
| 420 | -5、修复了客户反馈的一些异常并增强了稳定性。 | ||
| 421 | - | ||
| 422 | - | ||
| 423 | -2.2.1 Dev | ||
| 424 | -------------------------------- | ||
| 425 | - | ||
| 426 | -1、initWithAppKey 去掉参数 DeviceToken,因为初始化阶段很难获得 DeviceToeken,同时优化了 setDeviceToken,做了内部缓存,以帮用户存储正确的值。 | ||
| 427 | - | ||
| 428 | -2、增强了重连机制。 | ||
| 429 | - | ||
| 430 | - | ||
| 431 | - | ||
| 432 | -2.2.0 Stable | ||
| 433 | -------------------------------- | ||
| 434 | - | ||
| 435 | -1、增加了公众服务自定义菜单。 | ||
| 436 | - | ||
| 437 | -2、为 sendMessage 增加一个 pushExtra 字段,可以附加消息到 APNS 推送里。 | ||
| 438 | - | ||
| 439 | -3、增加了 insertMessage 接口。 | ||
| 440 | - | ||
| 441 | -4、继续优化了连接稳定性和重连机制。 | ||
| 442 | - | ||
| 443 | -5、为了一些客户定制化需求优化了功能。 | ||
| 444 | - | ||
| 445 | -6、修复了各种渠道反馈的一些小概率异常和崩溃问题。 | ||
| 446 | - | ||
| 447 | - | ||
| 448 | -2.1.4 Stable | ||
| 449 | -------------------------------- | ||
| 450 | - | ||
| 451 | -1.增加了富文本消息的长按事件。 | ||
| 452 | -2.修改了图文消息的布局问题。 | ||
| 453 | -3.会话列表头像设置,放开大小限制。 | ||
| 454 | -4.解决了收到推送后跳转到聊天界面以后,如果有其他人或者群发来消息,导航栏的返回按钮就失效的问题。 | ||
| 455 | -5.修复了 2.1.3 版本上因为修改图片缓存导致发送图片消息会导致无法取到本地图片的问题。 | ||
| 456 | -6.修改了 tokenIncorrect 逻辑,增加了一次场景更准确的反馈该错误信息给客户。 | ||
| 457 | -7.增加了信令用于同步 WebSDK 的会话列表信息。 | ||
| 458 | -8.修复了一个可能导致无法拉取离线消息的小概率问题。 | ||
| 459 | - | ||
| 460 | -2.1.3 Stable | ||
| 461 | -------------------------------- | ||
| 462 | - | ||
| 463 | -1、Emoji 作为资源文件,可以自定义 Emoji 表情。 | ||
| 464 | -2、修改了 RCContactNotificationMessage 消息解析的一个问题 | ||
| 465 | -3、修改了会话列表最后一条消息显示的一个问题。 | ||
| 466 | -4、修改讨论组用户名显示的一个问题 | ||
| 467 | -5、修改了因为回调指针为空可能导致崩溃的一个底层问题。 | ||
| 468 | - | ||
| 469 | - | ||
| 470 | - | ||
| 471 | -2.1.2 Stable | ||
| 472 | -------------------------------- | ||
| 473 | - | ||
| 474 | -1、增加了一些网络错误状态发生后重连的次数,更容易重连。 | ||
| 475 | - | ||
| 476 | -2、修改了缓存文件存储的系统路径。 | ||
| 477 | - | ||
| 478 | -3、增加了公众服务的草稿。 | ||
| 479 | - | ||
| 480 | -4、Emoji库可以通过资源包替换。 | ||
| 481 | - | ||
| 482 | -5、修改了上一个版本出现的可能会导致消息发送时一直发不出去的问题。 | ||
| 483 | - | ||
| 484 | -6、修改了发送语音过程中的一些界面问题。 | ||
| 485 | - | ||
| 486 | - | ||
| 487 | - | ||
| 488 | - | ||
| 489 | -2.1.1 Stable | ||
| 490 | -------------------------------- | ||
| 491 | - | ||
| 492 | -1、增加了对 Apple WatchKit 的支持,现在您可以直接使用融云的 IMlib 实现 Apple Watch App,我们同时开源了 Demo App。 | ||
| 493 | - | ||
| 494 | -2、增加了单个 App 内群组的上限,旧的版本不能超过 300 个群组。 | ||
| 495 | - | ||
| 496 | -3、解决了一些界面在 iOS7.0 上不能使用并崩溃的问题。 | ||
| 497 | - | ||
| 498 | -4、解决了连续收到多条消息,会话列表刷新错误的问题。 | ||
| 499 | - | ||
| 500 | -5、解决了自定义消息透传的问题。 | ||
| 501 | - | ||
| 502 | -6、修复了一些语音消息的功能和界面问题,增加了点击语音消息可以停止语音播放的功能。 | ||
| 503 | - | ||
| 504 | -7、增加了一些消息的点击事件。 | ||
| 505 | - | ||
| 506 | -8、当您不在群组/讨论组/聊天室内,而又能进入界面发起消息是,给出文字提示。 | ||
| 507 | - | ||
| 508 | -9、解决了一个当自动重连时有可能会导致的不发起连接回调的问题。 | ||
| 509 | - | ||
| 510 | -10、增加了接口 refreshUserInfoCache 和 refreshGroupInfoCache 用于刷新用户信息缓存。 | ||
| 511 | - | ||
| 512 | - | ||
| 513 | - | ||
| 514 | -2.1.0 Stable | ||
| 515 | -------------------------------- | ||
| 516 | -1、增强了针对网络切换、网络不稳定时的连接稳定性问题。 | ||
| 517 | - | ||
| 518 | -2、修改了当网络出现问题,由于消息时间错误导致消息排列错误的问题。 | ||
| 519 | - | ||
| 520 | -3、修改了断网再连接导致小概率 crash 的问题。 | ||
| 521 | - | ||
| 522 | -4、修改了会话界面消息时间显示不准确的问题。 | ||
| 523 | - | ||
| 524 | -5、修改了发送接收表情,有时候显示为空的问题。 | ||
| 525 | - | ||
| 526 | -6、修复了自定义消息透传的问题。 | ||
| 527 | - | ||
| 528 | -7、修复了一些对低版本iOS的兼容问题。 | ||
| 529 | - | ||
| 530 | -8、优化了网络连接缓存机制。 | ||
| 531 | - | ||
| 532 | -2.0.5 Dev | ||
| 533 | -------------------------------- | ||
| 534 | -1、增加了消息置顶之后的显示效果。 | ||
| 535 | -2、增加了新消息提醒打开关闭的功能。 | ||
| 536 | -3、增加了“该账号已在另外一台设备上登录”提示。 | ||
| 537 | -4、修复了会话列表的一些时间顺序和显示问题。 | ||
| 538 | -5、优化了断网和弱网络下的重连机制,更快的从网络中恢复。 | ||
| 539 | -6、修复了小概率情况下收到消息后因为时间错误,导致消息被排列到错误的问题。 | ||
| 540 | -7、修复了一些测试过程中出现的小概率崩溃问题。 | ||
| 541 | -8、增加了连接缓存功能,增强了连接稳定性。 | ||
| 542 | - | ||
| 543 | -2.0.4 Dev | ||
| 544 | -------------------------------- | ||
| 545 | -1、增加了连接稳定性,修复了从后台进入前台后显示网络连接不可用的问题。 | ||
| 546 | -2、修复了一些小概率 Crash 问题。 | ||
| 547 | -3、修复了时间显示错误的问题。 | ||
| 548 | -4、增强了内存优化。 | ||
| 549 | -5、修复了录音和播放录音会跟系统播放冲突的问题。 | ||
| 550 | -6、增加了 DNS 缓存功能,以增强网络连接稳定性。 | ||
| 551 | - | ||
| 552 | -2.0.3 Dev | ||
| 553 | -------------------------------- | ||
| 554 | -1、增加了文本消息中连接高亮显示功能。 | ||
| 555 | -2、修复了会话界面输入法功能的显示问题。 | ||
| 556 | -3、解决了讨论组清空历史消息时会把此讨论组也删除的问题。 | ||
| 557 | -4、解决了系统播放音乐时,收到信息播放提示音之后音乐不继续播放的问题。 | ||
| 558 | -5、解决了发送图片,发送端点击不能放大缩小的问题。 | ||
| 559 | -6、修复了一些可能会显示网络连接不可用的问题。 | ||
| 560 | - | ||
| 561 | -2.0.2 Dev | ||
| 562 | -------------------------------- | ||
| 563 | -1、美化了一些 UI 界面显示。 | ||
| 564 | -2、修复了输入框输入大量字符显示相关的问题。 | ||
| 565 | -3、修复了接收未注册自定义消息显示错误的问题。 | ||
| 566 | -4、解决了接收 API 发送的图文混排消息显示错误的问题。 | ||
| 567 | -5、当手机中无照片时,点击相册默认会提示“无视频和照片”。 | ||
| 568 | -6、修复了发送 60s 语音,有一定概率崩溃的问题。 | ||
| 569 | -7、修复了一些业务层稳定性问题。 | ||
| 570 | -8、修复了在其它页面来新消息没有铃声提示的问题。 | ||
| 571 | -9、修复了发送表情后,点击发送按钮会发送空消息的问题。 | ||
| 572 | -10、解决了图片预览页图片不能放大和缩小的问题。 | ||
| 573 | - | ||
| 574 | -2.0.1 Dev | ||
| 575 | -------------------------------- | ||
| 576 | -1、增强了 UI 显示效果,修改了一些UI问题。 | ||
| 577 | -2、增加了自定义消息展示的接口,更方便的自定义自己消息类型展现。 | ||
| 578 | -3、修复了发送图像,发送端点击缩略图无法打开问题。 | ||
| 579 | -4、修改了一些内存泄露问题。 | ||
| 580 | - | ||
| 581 | -2.0.0 Dev | ||
| 582 | -------------------------------- | ||
| 583 | -1、重构了会话列表界面和会话界面的架构,支持丰富的界面自定义。 | ||
| 584 | -2、支持会话列表的自定义,聚合非聚合显示,支持插入用户自定义数据和展现。 | ||
| 585 | -3、支持自定义会话界面,预置了九种输入框界面。 | ||
| 586 | -4、支持自定义消息的展现,采用自动布局,可以自定义消息界面的展现。 | ||
| 587 | -5、开放了更多的 API 接口和监听。 | ||
| 588 | -6、优化了协议栈的重连机制,现在 App 从断线状态恢复重连将更快。 | ||
| 589 | -7、优化了数据库性能,消息的读写速度将更快。 | ||
| 590 | -8、支持搜索关注公众账号,支持公众账号的详情展现。 | ||
| 591 | -9、支持公众服务多图文消息、单图文消息的展现。 | ||
| 592 | -10、增加了对 Apple Watch 的内置支持。 |
CNLivePlayer/Classes/CNLiveMsgTools.framework/zh-Hans.lproj/RongCloudKit.strings deleted
100644 → 0
No preview for this file type
CNLivePlayer/Classes/DataRequest/CNLivePlayerNetworkManager.h deleted
100644 → 0
| 1 | -// | ||
| 2 | -// CNLivePlayerNetworkManager.h | ||
| 3 | -// CNLivePlayer_Example | ||
| 4 | -// | ||
| 5 | -// Created by CNLive-zxw on 2019/7/26. | ||
| 6 | -// Copyright © 2019 153993236@qq.com. All rights reserved. | ||
| 7 | -// | ||
| 8 | - | ||
| 9 | -#import <Foundation/Foundation.h> | ||
| 10 | - | ||
| 11 | -NS_ASSUME_NONNULL_BEGIN | ||
| 12 | -typedef void(^GetVideoURLSuccessBlock)(NSString * _Nullable videoURL, NSInteger code); | ||
| 13 | - | ||
| 14 | -@interface CNLivePlayerNetworkManager : NSObject | ||
| 15 | -/** | ||
| 16 | - * 设置超时时间 | ||
| 17 | - */ | ||
| 18 | -@property (nonatomic, assign) NSTimeInterval timeoutInterval; | ||
| 19 | - | ||
| 20 | -/** | ||
| 21 | - * 缓存策略 | ||
| 22 | - */ | ||
| 23 | -@property (nonatomic, assign) NSURLRequestCachePolicy cachePolicy; | ||
| 24 | - | ||
| 25 | -+ (CNLivePlayerNetworkManager *)manager; | ||
| 26 | - | ||
| 27 | -/** | ||
| 28 | - * GET 请求 | ||
| 29 | - * @param URL url | ||
| 30 | - * @param parameters 上传参数 | ||
| 31 | - * @param success 请求成功回调 | ||
| 32 | - * @param failure 请求失败回调 | ||
| 33 | - */ | ||
| 34 | -+ (void)GET:(nullable NSString *)URL parameters:(nullable NSDictionary *)parameters success:(nullable void (^)(NSURLResponse *_Nullable response, id _Nullable responseObject))success failure:(nullable void (^)(NSURLResponse *_Nullable response, NSError *_Nullable error))failure; | ||
| 35 | - | ||
| 36 | -/** | ||
| 37 | - * POST 请求 | ||
| 38 | - * @param URL url | ||
| 39 | - * @param parameters 上传参数 | ||
| 40 | - * @param success 请求成功回调 | ||
| 41 | - * @param failure 请求失败回调 | ||
| 42 | - */ | ||
| 43 | -+ (void)POST:(nullable NSString *)URL parameters:(nullable NSDictionary *)parameters success:(nullable void (^)(NSURLResponse *_Nullable response, id _Nullable responseObject))success failure:(nullable void (^)(NSURLResponse *_Nullable response, NSError *_Nullable error))failure; | ||
| 44 | - | ||
| 45 | -#pragma mark - 针对具体业务 | ||
| 46 | -/** | ||
| 47 | - * 鉴权 | ||
| 48 | - */ | ||
| 49 | -- (void)authenticate:(NSString *)URL success:(void (^)(NSURLResponse * _Nullable, id _Nullable))success failure:(void (^)(NSURLResponse * _Nullable, NSError * _Nullable))failure; | ||
| 50 | - | ||
| 51 | -/** | ||
| 52 | - * 获取视频url | ||
| 53 | - */ | ||
| 54 | -- (void)getVideoURL:(nullable NSString *)URL success:(GetVideoURLSuccessBlock)success; | ||
| 55 | - | ||
| 56 | - | ||
| 57 | -@end | ||
| 58 | - | ||
| 59 | -NS_ASSUME_NONNULL_END |
CNLivePlayer/Classes/DataRequest/CNLivePlayerNetworkManager.m deleted
100644 → 0
| 1 | -// | ||
| 2 | -// CNLivePlayerNetworkManager.m | ||
| 3 | -// CNLivePlayer_Example | ||
| 4 | -// | ||
| 5 | -// Created by CNLive-zxw on 2019/7/26. | ||
| 6 | -// Copyright © 2019 153993236@qq.com. All rights reserved. | ||
| 7 | -// | ||
| 8 | - | ||
| 9 | -#import "CNLivePlayerNetworkManager.h" | ||
| 10 | -#import "CNLiveURLRequestSerialization.h" | ||
| 11 | - | ||
| 12 | -/** | ||
| 13 | - * 网络超时时间 默认 30 | ||
| 14 | - */ | ||
| 15 | -static NSTimeInterval TimeoutInterval = 30.0 ; | ||
| 16 | - | ||
| 17 | -/** | ||
| 18 | - * 缓存策略 | ||
| 19 | - * NSURLRequestUseProtocolCachePolicy // 默认的缓存策略(取决于协议) | ||
| 20 | - * NSURLRequestReloadIgnoringLocalCacheData // 忽略缓存,重新请求 | ||
| 21 | - * NSURLRequestReloadIgnoringLocalAndRemoteCacheData // 未实现 | ||
| 22 | - * NSURLRequestReloadIgnoringCacheData = NSURLRequestReloadIgnoringLocalCacheData // 忽略缓存,重新请求 | ||
| 23 | - * NSURLRequestReturnCacheDataElseLoad// 有缓存就用缓存,忽略过期时间!没有缓存就重新请求 | ||
| 24 | - * NSURLRequestReturnCacheDataDontLoad// 有缓存就用缓存,没有缓存就不发请求,当做请求出错处理(用于离线模式) | ||
| 25 | - * NSURLRequestReloadRevalidatingCacheData // 未实现 | ||
| 26 | - */ | ||
| 27 | -static NSURLRequestCachePolicy CachePolicy = NSURLRequestUseProtocolCachePolicy; | ||
| 28 | - | ||
| 29 | -@interface CNLivePlayerNetworkManager() | ||
| 30 | -@property (nonatomic, assign) BOOL result; | ||
| 31 | -@property (nonatomic, copy) GetVideoURLSuccessBlock successBlock; | ||
| 32 | - | ||
| 33 | -@end | ||
| 34 | - | ||
| 35 | -@implementation CNLivePlayerNetworkManager | ||
| 36 | -static CNLivePlayerNetworkManager *_manager = nil; | ||
| 37 | - | ||
| 38 | -+ (CNLivePlayerNetworkManager *)manager { | ||
| 39 | - static dispatch_once_t predicate; | ||
| 40 | - dispatch_once(&predicate, ^{ | ||
| 41 | - _manager = [[CNLivePlayerNetworkManager alloc] init]; | ||
| 42 | - | ||
| 43 | - }); | ||
| 44 | - return _manager; | ||
| 45 | - | ||
| 46 | -} | ||
| 47 | - | ||
| 48 | -- (instancetype)init { | ||
| 49 | - self = [super init]; | ||
| 50 | - if (self) { | ||
| 51 | - self.cachePolicy = CachePolicy; | ||
| 52 | - self.timeoutInterval = TimeoutInterval; | ||
| 53 | - } | ||
| 54 | - return self; | ||
| 55 | -} | ||
| 56 | - | ||
| 57 | -+ (void)GET:(NSString *)URL parameters:(NSDictionary *)parameters success:(void (^)(NSURLResponse * _Nullable, id _Nullable))success failure:(void (^)(NSURLResponse * _Nullable, NSError * _Nullable))failure { | ||
| 58 | - NSURLSessionDataTask *task = [[CNLivePlayerNetworkManager manager] dataTaskWithRequestMethod:@"GET" URLString:URL parameters:parameters success:success failure:failure]; | ||
| 59 | - [task resume]; | ||
| 60 | - | ||
| 61 | -} | ||
| 62 | - | ||
| 63 | -+ (void)POST:(NSString *)URL parameters:(NSDictionary *)parameters success:(void (^)(NSURLResponse * _Nullable, id _Nullable))success failure:(void (^)(NSURLResponse * _Nullable, NSError * _Nullable))failure { | ||
| 64 | - NSURLSessionDataTask *task = [[CNLivePlayerNetworkManager manager] dataTaskWithRequestMethod:@"POST" URLString:URL parameters:parameters success:success failure:failure]; | ||
| 65 | - [task resume]; | ||
| 66 | -} | ||
| 67 | - | ||
| 68 | -#pragma mark - 公共方法 | ||
| 69 | -- (NSURLSessionDataTask *)dataTaskWithRequestMethod:(NSString *)method URLString:(NSString *)URLString parameters:(NSDictionary *)parameters success:(void (^)(NSURLResponse * _Nullable, id _Nullable))success failure:(void (^)(NSURLResponse * _Nullable, NSError * _Nullable))failure { | ||
| 70 | - | ||
| 71 | - if (![URLString isKindOfClass:[NSString class]]) { | ||
| 72 | - NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"please enter a valid url"}]; | ||
| 73 | - failure(nil, error); | ||
| 74 | - return nil; | ||
| 75 | - } | ||
| 76 | - | ||
| 77 | - NSString *newURLString; | ||
| 78 | - if ([method isEqualToString:@"GET"]) { | ||
| 79 | - if (parameters) { | ||
| 80 | - newURLString = [[URLString stringByAppendingString:@"?"] stringByAppendingString:[CNLiveURLRequestSerialization cn_QueryStringFromParameters:parameters]]; | ||
| 81 | - }else{ | ||
| 82 | - newURLString = URLString; | ||
| 83 | - } | ||
| 84 | - | ||
| 85 | - }else{ | ||
| 86 | - newURLString = URLString; | ||
| 87 | - } | ||
| 88 | - | ||
| 89 | - NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:newURLString] cachePolicy:self.cachePolicy timeoutInterval:self.timeoutInterval]; | ||
| 90 | - | ||
| 91 | - request.HTTPMethod = method; | ||
| 92 | - | ||
| 93 | - if([method isEqual:@"POST"]){ | ||
| 94 | - [request addValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"]; | ||
| 95 | - request.HTTPBody = [[CNLiveURLRequestSerialization cn_QueryStringFromParameters:parameters] dataUsingEncoding:NSUTF8StringEncoding]; | ||
| 96 | - } | ||
| 97 | - | ||
| 98 | - NSURLSessionDataTask *task = [self dataTaskWithRequest:request success:success failure:failure]; | ||
| 99 | - | ||
| 100 | - return task; | ||
| 101 | - | ||
| 102 | -} | ||
| 103 | - | ||
| 104 | -- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request success:(void (^)(NSURLResponse * _Nullable, id _Nullable))success failure:(void (^)(NSURLResponse * _Nullable, NSError * _Nullable))failure { | ||
| 105 | - NSURLSession *session = [NSURLSession sharedSession]; | ||
| 106 | - NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { | ||
| 107 | - | ||
| 108 | - dispatch_async(dispatch_get_main_queue(), ^{ | ||
| 109 | - if (error) { | ||
| 110 | - failure(response,error); | ||
| 111 | - } | ||
| 112 | - else{ | ||
| 113 | - if ([response isKindOfClass:[NSHTTPURLResponse class]]) { | ||
| 114 | - NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; | ||
| 115 | - if (httpResponse.statusCode < 400) { | ||
| 116 | - id jsonObj = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil]; | ||
| 117 | - success(httpResponse, jsonObj); | ||
| 118 | - } else { | ||
| 119 | - NSError *failureError = [NSError errorWithDomain:NSURLErrorDomain code:httpResponse.statusCode userInfo:httpResponse.allHeaderFields]; | ||
| 120 | - failure(httpResponse, failureError); | ||
| 121 | - } | ||
| 122 | - } | ||
| 123 | - else { | ||
| 124 | - failure(response, error); | ||
| 125 | - } | ||
| 126 | - } | ||
| 127 | - }); | ||
| 128 | - }]; | ||
| 129 | - | ||
| 130 | - return task; | ||
| 131 | - | ||
| 132 | -} | ||
| 133 | - | ||
| 134 | -#pragma mark - 针对具体业务 | ||
| 135 | -/** | ||
| 136 | - * 鉴权 | ||
| 137 | - */ | ||
| 138 | -- (void)authenticate:(NSString *)URL success:(void (^)(NSURLResponse * _Nullable, id _Nullable))success failure:(void (^)(NSURLResponse * _Nullable, NSError * _Nullable))failure { | ||
| 139 | - NSURLSessionDataTask *task = [self dataTaskWithRequestMethod:@"GET" URLString:URL parameters:nil success:success failure:failure]; | ||
| 140 | - [task resume]; | ||
| 141 | - | ||
| 142 | -} | ||
| 143 | - | ||
| 144 | -/** | ||
| 145 | - * 获取视频url | ||
| 146 | - */ | ||
| 147 | -- (void)getVideoURL:(nullable NSString *)URL success:(GetVideoURLSuccessBlock)success { | ||
| 148 | - NSURLSessionDataTask *task = [self dataTaskWithRequestMethod:@"GET" URLString:URL parameters:nil success:^(NSURLResponse * _Nullable response, id _Nullable responseObject) { | ||
| 149 | - if(success){ | ||
| 150 | - NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response; | ||
| 151 | - success([response.URL absoluteString], httpResponse.statusCode); | ||
| 152 | - } | ||
| 153 | - | ||
| 154 | - } failure:^(NSURLResponse * _Nullable response, NSError * _Nullable error) { | ||
| 155 | - if (success) { | ||
| 156 | - NSString *videoUrl302 = error.userInfo[@"NSErrorFailingURLStringKey"]; | ||
| 157 | - if (!videoUrl302 || ![videoUrl302 isKindOfClass:[NSString class]] || videoUrl302.length <= 0) { | ||
| 158 | - videoUrl302 = nil; | ||
| 159 | - } | ||
| 160 | - success(videoUrl302, error.code); | ||
| 161 | - } | ||
| 162 | - }]; | ||
| 163 | - [task resume]; | ||
| 164 | - | ||
| 165 | -} | ||
| 166 | - | ||
| 167 | - | ||
| 168 | -@end |
CNLivePlayer/Classes/DataRequest/CNLivePlayerRequest.h
0 → 100644
| 1 | +// | ||
| 2 | +// CNLivePlayerRequest.h | ||
| 3 | +// CNLivePlayer_Example | ||
| 4 | +// | ||
| 5 | +// Created by CNLive-zxw on 2019/5/25. | ||
| 6 | +// Copyright © 2019 CNLive. All rights reserved. | ||
| 7 | +// | ||
| 8 | + | ||
| 9 | +/** | ||
| 10 | + * 数据请求 | ||
| 11 | + */ | ||
| 12 | + | ||
| 13 | +#import <Foundation/Foundation.h> | ||
| 14 | + | ||
| 15 | +NS_ASSUME_NONNULL_BEGIN | ||
| 16 | +typedef void (^ResultsBlock) (NSString * _Nullable videoUrl, NSError * _Nullable error); | ||
| 17 | + | ||
| 18 | +@interface CNLivePlayerRequest : NSObject | ||
| 19 | + | ||
| 20 | +#pragma mark - 点播 | ||
| 21 | +// 获取点播播放地址 | ||
| 22 | ++ (void)getVodVideoURLWithVideoId:(NSString *)videoId rate:(NSString *)rate block:(nullable ResultsBlock)block; | ||
| 23 | + | ||
| 24 | +#pragma mark - 直播 | ||
| 25 | +// 获取直播播放地址 | ||
| 26 | ++ (void)getLiveVideoURLWithVideoId:(NSString *)videoId activityId:(NSString *)activityId block:(nullable ResultsBlock)block; | ||
| 27 | + | ||
| 28 | +#pragma mark - 鉴权 | ||
| 29 | +// 获取鉴权 | ||
| 30 | ++ (void)getAuthentication; | ||
| 31 | ++ (void)getAuthenticationBlock:(void (^)(NSString * _Nullable code, NSError * _Nullable error))block; | ||
| 32 | + | ||
| 33 | +#pragma mark - 公共方法 | ||
| 34 | +// 获取播放地址 | ||
| 35 | ++ (void)getVideoURL:(nullable NSString *)URL parameter:(nullable NSDictionary *)parameter block:(nullable ResultsBlock)block; | ||
| 36 | + | ||
| 37 | ++ (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request success:(void (^)(NSURLResponse * _Nullable, id _Nullable))success failure:(void (^)(NSURLResponse * _Nullable, NSError * _Nullable))failure; | ||
| 38 | + | ||
| 39 | +@end | ||
| 40 | + | ||
| 41 | +NS_ASSUME_NONNULL_END |
CNLivePlayer/Classes/DataRequest/CNLivePlayerRequest.m
0 → 100644
| 1 | +// | ||
| 2 | +// CNLivePlayerRequest.m | ||
| 3 | +// CNLivePlayer_Example | ||
| 4 | +// | ||
| 5 | +// Created by CNLive-zxw on 2019/5/25. | ||
| 6 | +// Copyright © 2019 CNLive. All rights reserved. | ||
| 7 | +// | ||
| 8 | + | ||
| 9 | +#import "CNLivePlayerRequest.h" | ||
| 10 | +#import "CommonCrypto/CommonDigest.h" | ||
| 11 | + | ||
| 12 | +#import "CNLivePlayerUrl.h" | ||
| 13 | +#import "CNLivePlayerManager.h" | ||
| 14 | + | ||
| 15 | +@implementation CNLivePlayerRequest | ||
| 16 | +#pragma mark - 直播 | ||
| 17 | +// 获取直播播放地址 | ||
| 18 | ++ (void)getLiveVideoURLWithVideoId:(NSString *)videoId activityId:(NSString *)activityId block:(nullable ResultsBlock)block{ | ||
| 19 | + NSDictionary *parameter = @{@"appId": Player_AppID, | ||
| 20 | + @"channelId": videoId, | ||
| 21 | + @"activityId": activityId ? activityId : @"", | ||
| 22 | + @"timestamp": [self getTimestamp], | ||
| 23 | + @"isHLS": @"1", | ||
| 24 | + @"plat": @"i", | ||
| 25 | + @"playType": @"v", | ||
| 26 | + @"platform_id": [NSBundle mainBundle].bundleIdentifier, | ||
| 27 | + @"channelName": Player_ChannelName, | ||
| 28 | + @"tag": Player_Tag, | ||
| 29 | + @"from": @"apple", | ||
| 30 | + @"sid": Player_UserId, | ||
| 31 | + @"uid": [self getUid]}; | ||
| 32 | + [self getVideoURL:liveUrl parameter:parameter block:block]; | ||
| 33 | + | ||
| 34 | +} | ||
| 35 | + | ||
| 36 | +// 获取播放地址 | ||
| 37 | ++ (void)getVideoURL:(nullable NSString *)URL parameter:(nullable NSDictionary *)parameter block:(nullable ResultsBlock)block{ | ||
| 38 | + NSString *dicString = [NSString stringWithFormat:@"%@&key=%@", [self dictionaryToSignValue:parameter], Player_AppKey]; | ||
| 39 | + | ||
| 40 | + NSString *signString = [[self sha1:dicString] uppercaseString]; | ||
| 41 | + | ||
| 42 | + NSString *videoUrl = [NSString stringWithFormat:@"%@?%@&sign=%@", vodUrl, [self dictionaryToSignValue:parameter], signString]; | ||
| 43 | + NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:videoUrl]]; | ||
| 44 | + [urlRequest setHTTPMethod:@"GET"]; | ||
| 45 | + [urlRequest setTimeoutInterval:30]; | ||
| 46 | + | ||
| 47 | + [NSURLConnection sendAsynchronousRequest:urlRequest queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) { | ||
| 48 | + dispatch_async(dispatch_get_main_queue(), ^{ | ||
| 49 | + if (connectionError) { | ||
| 50 | + NSError *error = connectionError.userInfo[NSUnderlyingErrorKey]; | ||
| 51 | + NSString *videoUrl302 = error.userInfo[NSURLErrorFailingURLStringErrorKey]; | ||
| 52 | + if (!videoUrl302 || ![videoUrl302 isKindOfClass:[NSString class]] || videoUrl302.length <= 0) { | ||
| 53 | + videoUrl302 = nil; | ||
| 54 | + } | ||
| 55 | + block?block(videoUrl302, nil):@""; | ||
| 56 | + | ||
| 57 | + } else if (response) { | ||
| 58 | + NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response; | ||
| 59 | + block?block([httpResponse.URL absoluteString], nil):@""; | ||
| 60 | + | ||
| 61 | + } else { | ||
| 62 | + NSString *domain = @"com.CNLive.Application.ErrorDomain"; | ||
| 63 | + NSString *desc = @"获取播放地址错误"; | ||
| 64 | + NSDictionary *userInfo = @{ NSLocalizedDescriptionKey : desc }; | ||
| 65 | + NSError *error = [NSError errorWithDomain:domain code:8888 userInfo:userInfo]; | ||
| 66 | + block?block(videoUrl, error):@""; | ||
| 67 | + | ||
| 68 | + } | ||
| 69 | + }); | ||
| 70 | + }]; | ||
| 71 | +} | ||
| 72 | + | ||
| 73 | +#pragma mark - 点播 | ||
| 74 | +// 获取点播播放地址 | ||
| 75 | ++ (void)getVodVideoURLWithVideoId:(NSString *)videoId rate:(NSString *)rate block:(nullable ResultsBlock)block{ | ||
| 76 | + NSDictionary *parameter = @{@"appId": Player_AppID, | ||
| 77 | + @"vId": videoId, | ||
| 78 | + @"timestamp": [self getTimestamp], | ||
| 79 | + @"isHLS": @"1", | ||
| 80 | + @"plat": @"i", | ||
| 81 | + @"playType": @"v", | ||
| 82 | + @"rate": rate?rate:@"2", | ||
| 83 | + @"platform_id": [NSBundle mainBundle].bundleIdentifier, | ||
| 84 | + @"channelName": Player_ChannelName, | ||
| 85 | + @"tag": Player_Tag, | ||
| 86 | + @"from": @"apple", | ||
| 87 | + @"sid":Player_UserId, | ||
| 88 | + @"uid": [self getUid]}; | ||
| 89 | + NSString *dicString = [NSString stringWithFormat:@"%@&key=%@", [self dictionaryToSignValue:parameter], Player_AppKey]; | ||
| 90 | + | ||
| 91 | + NSString *signString = [[self sha1:dicString] uppercaseString]; | ||
| 92 | + | ||
| 93 | + NSString *url = [NSString stringWithFormat:@"%@?%@&sign=%@", vodUrl, [self dictionaryToSignValue:parameter], signString]; | ||
| 94 | + | ||
| 95 | + NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]]; | ||
| 96 | + [urlRequest setHTTPMethod:@"GET"]; | ||
| 97 | + [urlRequest setTimeoutInterval:30.0]; | ||
| 98 | + NSURLSessionDataTask *task = [self dataTaskWithRequest:urlRequest success:^(NSURLResponse * _Nullable response, id _Nullable data) { | ||
| 99 | + block?block(response.URL.absoluteString, nil):@""; | ||
| 100 | + | ||
| 101 | + } failure:^(NSURLResponse * _Nullable response, NSError * _Nullable error) { | ||
| 102 | + block?block(response.URL.absoluteString, error):@""; | ||
| 103 | + | ||
| 104 | + }]; | ||
| 105 | + [task resume]; | ||
| 106 | + | ||
| 107 | +} | ||
| 108 | + | ||
| 109 | +#pragma mark - 鉴权 | ||
| 110 | +// 获取鉴权 | ||
| 111 | ++ (void)getAuthentication{ | ||
| 112 | + [self getAuthenticationBlock:^(NSString * _Nullable code, NSError * _Nullable error) { | ||
| 113 | + [[NSUserDefaults standardUserDefaults] setObject:code forKey:@"kCNLivePlayerAuthentication"]; | ||
| 114 | + [[NSUserDefaults standardUserDefaults] synchronize]; | ||
| 115 | + }]; | ||
| 116 | + | ||
| 117 | +} | ||
| 118 | + | ||
| 119 | ++ (void)getAuthenticationBlock:(void (^)(NSString * _Nullable code, NSError * _Nullable error))block { | ||
| 120 | + if(Player_Authentication){ | ||
| 121 | + block?block(@"0", nil):@""; | ||
| 122 | + return; | ||
| 123 | + } | ||
| 124 | + NSDictionary *parameter = @{@"platform_id": [NSBundle mainBundle].bundleIdentifier, | ||
| 125 | + @"timestamp": [self getTimestamp], | ||
| 126 | + @"appId": Player_AppID}; | ||
| 127 | + NSString *dicString = [NSString stringWithFormat:@"%@&key=%@", [self dictionaryToSignValue:parameter], Player_AppKey]; | ||
| 128 | + NSString *signString = [[self sha1:dicString] uppercaseString]; | ||
| 129 | + | ||
| 130 | + NSString *url = [NSString stringWithFormat:@"%@?%@&sign=%@", authUrl, [self dictionaryToSignValue:parameter], signString]; | ||
| 131 | + | ||
| 132 | + NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]]; | ||
| 133 | + [urlRequest setHTTPMethod:@"GET"]; | ||
| 134 | + [urlRequest setTimeoutInterval:30.0]; | ||
| 135 | + NSURLSessionDataTask *task = [self dataTaskWithRequest:urlRequest success:^(NSURLResponse * _Nullable response, id _Nullable data) { | ||
| 136 | + if (data && [data isKindOfClass:[NSDictionary class]]) { | ||
| 137 | + NSDictionary *dic = (NSDictionary *)data; | ||
| 138 | + [[NSUserDefaults standardUserDefaults] setObject:dic[@"errorCode"] forKey:@"kCNLivePlayerAuthentication"]; | ||
| 139 | + [[NSUserDefaults standardUserDefaults] synchronize]; | ||
| 140 | + block?block(dic[@"errorCode"], nil):@""; | ||
| 141 | + }else{ | ||
| 142 | + block?block(nil, nil):@""; | ||
| 143 | + } | ||
| 144 | + | ||
| 145 | + } failure:^(NSURLResponse * _Nullable response, NSError * _Nullable error) { | ||
| 146 | + NSString *code = [NSString stringWithFormat:@"%ld",(long)error.code]; | ||
| 147 | + block?block(code, error):@""; | ||
| 148 | + }]; | ||
| 149 | + [task resume]; | ||
| 150 | +} | ||
| 151 | + | ||
| 152 | +#pragma mark - 公共方法 | ||
| 153 | ++ (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request success:(void (^)(NSURLResponse * _Nullable, id _Nullable))success failure:(void (^)(NSURLResponse * _Nullable, NSError * _Nullable))failure { | ||
| 154 | + NSURLSession *session = [NSURLSession sharedSession]; | ||
| 155 | + NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { | ||
| 156 | + dispatch_async(dispatch_get_main_queue(), ^{ | ||
| 157 | + if (error) { | ||
| 158 | + failure(response,error); | ||
| 159 | + } | ||
| 160 | + else { | ||
| 161 | + if ([response isKindOfClass:[NSHTTPURLResponse class]]) { | ||
| 162 | + NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; | ||
| 163 | + if (httpResponse.statusCode < 400) { | ||
| 164 | + id jsonObj = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil]; | ||
| 165 | + success(httpResponse, jsonObj); | ||
| 166 | + } | ||
| 167 | + else { | ||
| 168 | + NSError *failureError = [NSError errorWithDomain:NSURLErrorDomain code:httpResponse.statusCode userInfo:httpResponse.allHeaderFields]; | ||
| 169 | + failure(httpResponse, failureError); | ||
| 170 | + } | ||
| 171 | + } | ||
| 172 | + else { | ||
| 173 | + failure(response, error); | ||
| 174 | + } | ||
| 175 | + } | ||
| 176 | + }); | ||
| 177 | + }]; | ||
| 178 | + | ||
| 179 | + return task; | ||
| 180 | +} | ||
| 181 | + | ||
| 182 | +#pragma mark - 工具方法 | ||
| 183 | ++ (NSString *)dictionaryToSignValue:(NSDictionary*)parameter { | ||
| 184 | + //对所有传入参数按照字段名的 ASCII 码从小到大排序 | ||
| 185 | + NSArray *keyArr = [parameter allKeys]; | ||
| 186 | + NSArray *arr = [keyArr sortedArrayUsingSelector:@selector(compare:)]; | ||
| 187 | + | ||
| 188 | + NSMutableString *string1 = [[NSMutableString alloc]init]; | ||
| 189 | + for (int i = 0; i < arr.count; i++) { | ||
| 190 | + NSString *parameterString = parameter[[arr objectAtIndex:i]]; | ||
| 191 | + if (parameterString.length > 0) { | ||
| 192 | + [string1 appendString:[NSString stringWithFormat:@"%@=%@&",[arr objectAtIndex:i],parameter[[arr objectAtIndex:i]]]]; | ||
| 193 | + } | ||
| 194 | + | ||
| 195 | + } | ||
| 196 | + if (string1.length > 0) { | ||
| 197 | + [string1 deleteCharactersInRange:NSMakeRange(string1.length-1, 1)]; | ||
| 198 | + } | ||
| 199 | + | ||
| 200 | + return string1; | ||
| 201 | +} | ||
| 202 | + | ||
| 203 | ++ (NSString *)sha1:(NSString *)string { | ||
| 204 | + NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding]; | ||
| 205 | + uint8_t digest[CC_SHA1_DIGEST_LENGTH]; | ||
| 206 | + CC_SHA1(data.bytes, data.length, digest); | ||
| 207 | + NSMutableString *output = [NSMutableString stringWithCapacity:CC_SHA1_DIGEST_LENGTH * 2]; | ||
| 208 | + for(int i = 0; i < CC_SHA1_DIGEST_LENGTH; i++) | ||
| 209 | + [output appendFormat:@"%02x", digest[i]]; | ||
| 210 | + | ||
| 211 | + while ([[output substringToIndex:1] isEqualToString:@"0"]) { | ||
| 212 | + output = [[NSMutableString alloc] initWithString:[output substringFromIndex:1]]; | ||
| 213 | + } | ||
| 214 | + return output; | ||
| 215 | +} | ||
| 216 | + | ||
| 217 | ++ (NSString *)getUid{ | ||
| 218 | + NSString *uid = @""; | ||
| 219 | + NSUserDefaults *defauts = [NSUserDefaults standardUserDefaults]; | ||
| 220 | + if ([defauts objectForKey:@"kCNLiveUserDefaultsUIDKey"]) { | ||
| 221 | + uid = [[defauts objectForKey:@"kCNLiveUserDefaultsUIDKey"] copy]; | ||
| 222 | + return uid; | ||
| 223 | + } | ||
| 224 | + else{ | ||
| 225 | + NSString *stamp = [NSString stringWithFormat:@"%.3f", [[NSDate date] timeIntervalSince1970]]; | ||
| 226 | + stamp = [stamp stringByReplacingOccurrencesOfString:@"." withString:@""]; | ||
| 227 | + | ||
| 228 | + NSString *idfv = [[[[UIDevice currentDevice] identifierForVendor] UUIDString] stringByReplacingOccurrencesOfString:@"-" withString:@""]; | ||
| 229 | + uid = [NSString stringWithFormat:@"%@_%@", idfv, stamp]; | ||
| 230 | + | ||
| 231 | + [defauts setObject:uid forKey:@"kCNLiveUserDefaultsUIDKey"]; | ||
| 232 | + [defauts synchronize]; | ||
| 233 | + } | ||
| 234 | + return uid; | ||
| 235 | +} | ||
| 236 | + | ||
| 237 | ++ (NSString *)getTimestamp{ | ||
| 238 | + NSInteger time = [[NSDate date] timeIntervalSince1970]; | ||
| 239 | + NSString *timeString = [NSString stringWithFormat:@"%ld", (long)time]; | ||
| 240 | + return timeString; | ||
| 241 | +} | ||
| 242 | + | ||
| 243 | +@end |
CNLivePlayer/Classes/DataRequest/CNLiveURLRequestSerialization.h deleted
100644 → 0
| 1 | -// | ||
| 2 | -// CNLiveURLRequestSerialization.h | ||
| 3 | -// CNLivePlayer_Example | ||
| 4 | -// | ||
| 5 | -// Created by CNLive-zxw on 2019/7/26. | ||
| 6 | -// Copyright © 2019 153993236@qq.com. All rights reserved. | ||
| 7 | -// | ||
| 8 | - | ||
| 9 | -#import <Foundation/Foundation.h> | ||
| 10 | - | ||
| 11 | -NS_ASSUME_NONNULL_BEGIN | ||
| 12 | - | ||
| 13 | -@interface CNLiveURLRequestSerialization : NSObject | ||
| 14 | -+ (nullable NSString *)cn_QueryStringFromParameters:(nullable NSDictionary *)parameters; | ||
| 15 | - | ||
| 16 | -@end | ||
| 17 | - | ||
| 18 | -NS_ASSUME_NONNULL_END |
CNLivePlayer/Classes/DataRequest/CNLiveURLRequestSerialization.m deleted
100644 → 0
| 1 | -// | ||
| 2 | -// CNLiveURLRequestSerialization.m | ||
| 3 | -// CNLivePlayer_Example | ||
| 4 | -// | ||
| 5 | -// Created by CNLive-zxw on 2019/7/26. | ||
| 6 | -// Copyright © 2019 153993236@qq.com. All rights reserved. | ||
| 7 | -// | ||
| 8 | - | ||
| 9 | -#import "CNLiveURLRequestSerialization.h" | ||
| 10 | - | ||
| 11 | -@interface CNLiveURLRequestSerialization () | ||
| 12 | -@property (readwrite, nonatomic, strong) id value; | ||
| 13 | -@property (readwrite, nonatomic, strong) id field; | ||
| 14 | - | ||
| 15 | -@end | ||
| 16 | - | ||
| 17 | -@implementation CNLiveURLRequestSerialization | ||
| 18 | - | ||
| 19 | -- (id)initWithField:(id)field value:(id)value { | ||
| 20 | - self = [super init]; | ||
| 21 | - if (self) { | ||
| 22 | - self.field = field; | ||
| 23 | - self.value = value; | ||
| 24 | - } | ||
| 25 | - | ||
| 26 | - return self; | ||
| 27 | - | ||
| 28 | -} | ||
| 29 | - | ||
| 30 | -FOUNDATION_EXPORT NSArray * CNKSYQueryStringPairsFromDictionary(NSDictionary *dictionary); | ||
| 31 | -FOUNDATION_EXPORT NSArray * CNKSYQueryStringPairsFromKeyAndValue(NSString *key, id value); | ||
| 32 | - | ||
| 33 | -+ (NSString *)cn_QueryStringFromParameters:(NSDictionary *)parameters { | ||
| 34 | - NSMutableArray *mutablePairs = [NSMutableArray array]; | ||
| 35 | - for (CNLiveURLRequestSerialization *pair in CNKSYQueryStringPairsFromDictionary(parameters)) { | ||
| 36 | - [mutablePairs addObject:[pair URLEncodedStringValue]]; | ||
| 37 | - } | ||
| 38 | - return [mutablePairs componentsJoinedByString:@"&"]; | ||
| 39 | -} | ||
| 40 | - | ||
| 41 | -NSArray * CNKSYQueryStringPairsFromDictionary(NSDictionary *dictionary) { | ||
| 42 | - return CNKSYQueryStringPairsFromKeyAndValue(nil, dictionary); | ||
| 43 | -} | ||
| 44 | - | ||
| 45 | -/** | ||
| 46 | - * 处理上传参数 | ||
| 47 | - */ | ||
| 48 | -NSArray * CNKSYQueryStringPairsFromKeyAndValue(NSString *key, id value) { | ||
| 49 | - NSMutableArray *mutableQueryStringComponents = [NSMutableArray array]; | ||
| 50 | - //排序方式 | ||
| 51 | - NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"description" ascending:YES selector:@selector(compare:)]; | ||
| 52 | - | ||
| 53 | - if ([value isKindOfClass:[NSDictionary class]]) { //字典 | ||
| 54 | - NSDictionary *dictionary = value; | ||
| 55 | - for (id nestedKey in [dictionary.allKeys sortedArrayUsingDescriptors:@[sortDescriptor]]) { | ||
| 56 | - id nestedValue = dictionary[nestedKey]; | ||
| 57 | - if (nestedKey) { | ||
| 58 | - [mutableQueryStringComponents addObjectsFromArray:CNKSYQueryStringPairsFromKeyAndValue((key ? [NSString stringWithFormat:@"%@[%@]", key, nestedKey] : nestedKey), nestedValue)]; | ||
| 59 | - } | ||
| 60 | - } | ||
| 61 | - } | ||
| 62 | - else if ([value isKindOfClass:[NSArray class]]){ //数组 | ||
| 63 | - NSArray *array = value; | ||
| 64 | - for (id nestedValue in array) { | ||
| 65 | - [mutableQueryStringComponents addObjectsFromArray:CNKSYQueryStringPairsFromKeyAndValue([NSString stringWithFormat:@"%@[]", key], nestedValue)]; | ||
| 66 | - } | ||
| 67 | - } | ||
| 68 | - else if ([value isKindOfClass:[NSSet class]]){ //集合 | ||
| 69 | - NSSet *set = value; | ||
| 70 | - for (id obj in [set sortedArrayUsingDescriptors:@[ sortDescriptor ]]) { | ||
| 71 | - [mutableQueryStringComponents addObjectsFromArray:CNKSYQueryStringPairsFromKeyAndValue(key, obj)]; | ||
| 72 | - } | ||
| 73 | - } | ||
| 74 | - else{ //其他类型 | ||
| 75 | - [mutableQueryStringComponents addObject:[[CNLiveURLRequestSerialization alloc] initWithField:key value:value]]; | ||
| 76 | - } | ||
| 77 | - | ||
| 78 | - return mutableQueryStringComponents; | ||
| 79 | -} | ||
| 80 | - | ||
| 81 | -static NSString * CNPercentEscapedStringFromString(NSString *string) { | ||
| 82 | - static NSString * const kCNCharactersGeneralDelimitersToEncode = @":#[]@"; // does not include "?" or "/" due to RFC 3986 - Section 3.4 | ||
| 83 | - static NSString * const kCNCharactersSubDelimitersToEncode = @"!$&'()*+,;="; | ||
| 84 | - | ||
| 85 | - NSMutableCharacterSet * allowedCharacterSet = [[NSCharacterSet URLQueryAllowedCharacterSet] mutableCopy]; | ||
| 86 | - [allowedCharacterSet removeCharactersInString:[kCNCharactersGeneralDelimitersToEncode stringByAppendingString:kCNCharactersSubDelimitersToEncode]]; | ||
| 87 | - | ||
| 88 | - static NSUInteger const batchSize = 50; | ||
| 89 | - | ||
| 90 | - NSUInteger index = 0; | ||
| 91 | - NSMutableString *escaped = @"".mutableCopy; | ||
| 92 | - | ||
| 93 | - while (index < string.length) { | ||
| 94 | -#pragma GCC diagnostic push | ||
| 95 | -#pragma GCC diagnostic ignored "-Wgnu" | ||
| 96 | - NSUInteger length = MIN(string.length - index, batchSize); | ||
| 97 | -#pragma GCC diagnostic pop | ||
| 98 | - NSRange range = NSMakeRange(index, length); | ||
| 99 | - | ||
| 100 | - range = [string rangeOfComposedCharacterSequencesForRange:range]; | ||
| 101 | - | ||
| 102 | - NSString *substring = [string substringWithRange:range]; | ||
| 103 | - NSString *encoded = [substring stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacterSet]; | ||
| 104 | - [escaped appendString:encoded]; | ||
| 105 | - | ||
| 106 | - index += range.length; | ||
| 107 | - | ||
| 108 | - } | ||
| 109 | - | ||
| 110 | - return escaped; | ||
| 111 | -} | ||
| 112 | - | ||
| 113 | -- (NSString *)URLEncodedStringValue { | ||
| 114 | - if (!self.value || [self.value isEqual:[NSNull null]]) { | ||
| 115 | - return CNPercentEscapedStringFromString([self.field description]); | ||
| 116 | - } else { | ||
| 117 | - return [NSString stringWithFormat:@"%@=%@", CNPercentEscapedStringFromString([self.field description]), CNPercentEscapedStringFromString([self.value description])]; | ||
| 118 | - } | ||
| 119 | -} | ||
| 120 | - | ||
| 121 | -@end |
CNLivePlayer/Classes/Database/CNLivePlayerDBTool.h
| @@ -7,7 +7,7 @@ | @@ -7,7 +7,7 @@ | ||
| 7 | // | 7 | // |
| 8 | 8 | ||
| 9 | #import <Foundation/Foundation.h> | 9 | #import <Foundation/Foundation.h> |
| 10 | -#import "CNLivePlayerDBModel.h" | 10 | +#import "CNLivePlayerModel.h" |
| 11 | 11 | ||
| 12 | NS_ASSUME_NONNULL_BEGIN | 12 | NS_ASSUME_NONNULL_BEGIN |
| 13 | @class FMDatabase; | 13 | @class FMDatabase; |
| @@ -42,7 +42,7 @@ typedef void(^DBReslutCallback)(int result,FMDatabase *db); | @@ -42,7 +42,7 @@ typedef void(^DBReslutCallback)(int result,FMDatabase *db); | ||
| 42 | * @return 返回数据模型 | 42 | * @return 返回数据模型 |
| 43 | * | 43 | * |
| 44 | */ | 44 | */ |
| 45 | -- (CNLivePlayerDBModel *)searchInTable:(NSString *)contentId; | 45 | +- (CNLivePlayerModel *)searchInTable:(NSString *)contentId; |
| 46 | 46 | ||
| 47 | /** | 47 | /** |
| 48 | * 更新数据是否存在表中 | 48 | * 更新数据是否存在表中 |
| @@ -62,7 +62,7 @@ typedef void(^DBReslutCallback)(int result,FMDatabase *db); | @@ -62,7 +62,7 @@ typedef void(^DBReslutCallback)(int result,FMDatabase *db); | ||
| 62 | * @return 返回插入是否成功 | 62 | * @return 返回插入是否成功 |
| 63 | * | 63 | * |
| 64 | */ | 64 | */ |
| 65 | -- (BOOL)insertTable:(CNLivePlayerDBModel *)model; | 65 | +- (BOOL)insertTable:(CNLivePlayerModel *)model; |
| 66 | 66 | ||
| 67 | /** | 67 | /** |
| 68 | * 删除数据 | 68 | * 删除数据 |
CNLivePlayer/Classes/Database/CNLivePlayerDBTool.m
| @@ -61,7 +61,7 @@ | @@ -61,7 +61,7 @@ | ||
| 61 | #pragma mark - 自定义操作 | 61 | #pragma mark - 自定义操作 |
| 62 | - (void)createTable{ | 62 | - (void)createTable{ |
| 63 | [self openDB:^(int result, FMDatabase *db) { | 63 | [self openDB:^(int result, FMDatabase *db) { |
| 64 | - NSString * sql = @"CREATE TABLE DATATABLE (contentId varchar, url varchar, finish bit)"; | 64 | + NSString * sql = @"CREATE TABLE DATATABLE (ID varchar, url varchar)"; |
| 65 | BOOL res = [db executeUpdate:sql]; | 65 | BOOL res = [db executeUpdate:sql]; |
| 66 | if (!res) { | 66 | if (!res) { |
| 67 | NSLog(@"建表失败"); | 67 | NSLog(@"建表失败"); |
| @@ -74,10 +74,10 @@ | @@ -74,10 +74,10 @@ | ||
| 74 | 74 | ||
| 75 | } | 75 | } |
| 76 | 76 | ||
| 77 | -- (BOOL)existInTable:(NSString *)contentId { | 77 | +- (BOOL)existInTable:(NSString *)ID { |
| 78 | __block BOOL isResult = NO; | 78 | __block BOOL isResult = NO; |
| 79 | [self openDB:^(int result, FMDatabase *db) { | 79 | [self openDB:^(int result, FMDatabase *db) { |
| 80 | - NSString * check = [NSString stringWithFormat:@"SELECT * FROM DATATABLE where contentId = '%@'",contentId]; | 80 | + NSString * check = [NSString stringWithFormat:@"SELECT * FROM DATATABLE where ID = '%@'",ID]; |
| 81 | FMResultSet * re = [db executeQuery:check]; | 81 | FMResultSet * re = [db executeQuery:check]; |
| 82 | while ([re next]) { | 82 | while ([re next]) { |
| 83 | isResult = YES; | 83 | isResult = YES; |
| @@ -89,14 +89,13 @@ | @@ -89,14 +89,13 @@ | ||
| 89 | 89 | ||
| 90 | } | 90 | } |
| 91 | 91 | ||
| 92 | -- (CNLivePlayerDBModel *)searchInTable:(NSString *)contentId{ | ||
| 93 | - __block CNLivePlayerDBModel *model = [[CNLivePlayerDBModel alloc] init]; | 92 | +- (CNLivePlayerModel *)searchInTable:(NSString *)ID{ |
| 93 | + __block CNLivePlayerModel *model = [[CNLivePlayerModel alloc] init]; | ||
| 94 | [self openDB:^(int result, FMDatabase *db) { | 94 | [self openDB:^(int result, FMDatabase *db) { |
| 95 | - NSString * check = [NSString stringWithFormat:@"SELECT * FROM DATATABLE where contentId = '%@'",contentId]; | 95 | + NSString * check = [NSString stringWithFormat:@"SELECT * FROM DATATABLE where ID = '%@'",ID]; |
| 96 | FMResultSet * rs = [db executeQuery:check]; | 96 | FMResultSet * rs = [db executeQuery:check]; |
| 97 | while ([rs next]) { | 97 | while ([rs next]) { |
| 98 | - model = [[CNLivePlayerDBModel alloc] initWithContenId:[rs stringForColumn:@"contentId"] url:[rs stringForColumn:@"url"]]; | ||
| 99 | - model.finish = [rs boolForColumn:@"finish"]; | 98 | + model = [[CNLivePlayerModel alloc] initWithID:[rs stringForColumn:@"ID"] url:[rs stringForColumn:@"url"]]; |
| 100 | } | 99 | } |
| 101 | [db close]; | 100 | [db close]; |
| 102 | 101 | ||
| @@ -125,13 +124,13 @@ | @@ -125,13 +124,13 @@ | ||
| 125 | 124 | ||
| 126 | } | 125 | } |
| 127 | 126 | ||
| 128 | -- (BOOL)insertTable:(CNLivePlayerDBModel *)model{ | 127 | +- (BOOL)insertTable:(CNLivePlayerModel *)model{ |
| 129 | __block BOOL isResult = NO; | 128 | __block BOOL isResult = NO; |
| 130 | [self openDB:^(int result, FMDatabase *db) { | 129 | [self openDB:^(int result, FMDatabase *db) { |
| 131 | - NSString * check = [NSString stringWithFormat:@"SELECT * FROM DATATABLE where contentId = '%@'",model.contentId]; | 130 | + NSString * check = [NSString stringWithFormat:@"SELECT * FROM DATATABLE where ID = '%@'",model.ID]; |
| 132 | FMResultSet * re = [db executeQuery:check]; | 131 | FMResultSet * re = [db executeQuery:check]; |
| 133 | if (![re next]) { | 132 | if (![re next]) { |
| 134 | - NSString * sql = [NSString stringWithFormat:@"INSERT INTO DATATABLE (contentId, url, finish) values('%@', '%@', '%d')", model.contentId, model.url, model.finish]; | 133 | + NSString * sql = [NSString stringWithFormat:@"INSERT INTO DATATABLE (ID, url) values('%@', '%@')", model.ID, model.url]; |
| 135 | BOOL res = [db executeUpdate:sql]; | 134 | BOOL res = [db executeUpdate:sql]; |
| 136 | if (res) { | 135 | if (res) { |
| 137 | isResult = YES; | 136 | isResult = YES; |
| @@ -144,10 +143,10 @@ | @@ -144,10 +143,10 @@ | ||
| 144 | 143 | ||
| 145 | } | 144 | } |
| 146 | 145 | ||
| 147 | -- (BOOL)deleteTable:(NSString *)contentId{ | 146 | +- (BOOL)deleteTable:(NSString *)ID{ |
| 148 | __block BOOL isResult = NO; | 147 | __block BOOL isResult = NO; |
| 149 | [self openDB:^(int result, FMDatabase *db) { | 148 | [self openDB:^(int result, FMDatabase *db) { |
| 150 | - NSString * sql = [NSString stringWithFormat:@"DELETE FROM DATATABLE where contentId = '%@'",contentId]; | 149 | + NSString * sql = [NSString stringWithFormat:@"DELETE FROM DATATABLE where ID = '%@'",ID]; |
| 151 | BOOL res = [db executeUpdate:sql]; | 150 | BOOL res = [db executeUpdate:sql]; |
| 152 | if (res) { | 151 | if (res) { |
| 153 | isResult = YES; | 152 | isResult = YES; |
| @@ -165,10 +164,9 @@ | @@ -165,10 +164,9 @@ | ||
| 165 | NSString *sql = @"SELECT * FROM DATATABLE"; | 164 | NSString *sql = @"SELECT * FROM DATATABLE"; |
| 166 | FMResultSet *rs = [db executeQuery:sql]; | 165 | FMResultSet *rs = [db executeQuery:sql]; |
| 167 | while ([rs next]) { | 166 | while ([rs next]) { |
| 168 | - CNLivePlayerDBModel *model = [[CNLivePlayerDBModel alloc] init]; | ||
| 169 | - model.contentId = [rs stringForColumn:@"contentId"]; | 167 | + CNLivePlayerModel *model = [[CNLivePlayerModel alloc] init]; |
| 168 | + model.ID = [rs stringForColumn:@"ID"]; | ||
| 170 | model.url = [rs stringForColumn:@"url"]; | 169 | model.url = [rs stringForColumn:@"url"]; |
| 171 | - model.finish = [rs boolForColumn:@"finish"]; | ||
| 172 | [list addObject:model]; | 170 | [list addObject:model]; |
| 173 | } | 171 | } |
| 174 | [db close]; | 172 | [db close]; |
CNLivePlayer/Classes/Database/CNLivePlayerDBModel.h renamed to CNLivePlayer/Classes/Database/CNLivePlayerModel.h
| 1 | // | 1 | // |
| 2 | -// CNLivePlayerDBModel.h | 2 | +// CNLivePlayerModel.h |
| 3 | // CNLivePlayer_Example | 3 | // CNLivePlayer_Example |
| 4 | // | 4 | // |
| 5 | // Created by CNLive-zxw on 2019/8/19. | 5 | // Created by CNLive-zxw on 2019/8/19. |
| @@ -10,13 +10,12 @@ | @@ -10,13 +10,12 @@ | ||
| 10 | 10 | ||
| 11 | NS_ASSUME_NONNULL_BEGIN | 11 | NS_ASSUME_NONNULL_BEGIN |
| 12 | 12 | ||
| 13 | -@interface CNLivePlayerDBModel : NSObject | 13 | +@interface CNLivePlayerModel : NSObject |
| 14 | //contentId:rate | 14 | //contentId:rate |
| 15 | -@property (nullable, nonatomic, copy) NSString *contentId; | 15 | +@property (nullable, nonatomic, copy) NSString *ID; |
| 16 | @property (nullable, nonatomic, copy) NSString *url; | 16 | @property (nullable, nonatomic, copy) NSString *url; |
| 17 | -@property (nonatomic, assign) BOOL finish; | ||
| 18 | 17 | ||
| 19 | -- (CNLivePlayerDBModel *)initWithContenId:(NSString *)contentId url:(NSString *)url; | 18 | +- (CNLivePlayerModel *)initWithID:(NSString *)ID url:(NSString *)url; |
| 20 | 19 | ||
| 21 | - (BOOL)isEmpty; | 20 | - (BOOL)isEmpty; |
| 22 | 21 |
CNLivePlayer/Classes/Database/CNLivePlayerDBModel.m renamed to CNLivePlayer/Classes/Database/CNLivePlayerModel.m
| 1 | // | 1 | // |
| 2 | -// CNLivePlayerDBModel.m | 2 | +// CNLivePlayerModel.m |
| 3 | // CNLivePlayer_Example | 3 | // CNLivePlayer_Example |
| 4 | // | 4 | // |
| 5 | // Created by CNLive-zxw on 2019/8/19. | 5 | // Created by CNLive-zxw on 2019/8/19. |
| 6 | // Copyright © 2019 153993236@qq.com. All rights reserved. | 6 | // Copyright © 2019 153993236@qq.com. All rights reserved. |
| 7 | // | 7 | // |
| 8 | 8 | ||
| 9 | -#import "CNLivePlayerDBModel.h" | 9 | +#import "CNLivePlayerModel.h" |
| 10 | 10 | ||
| 11 | -@implementation CNLivePlayerDBModel | ||
| 12 | -- (CNLivePlayerDBModel *)initWithContenId:(NSString *)contentId url:(NSString *)url{ | 11 | +@implementation CNLivePlayerModel |
| 12 | +- (CNLivePlayerModel *)initWithID:(NSString *)ID url:(NSString *)url { | ||
| 13 | self = [super init]; | 13 | self = [super init]; |
| 14 | if(self){ | 14 | if(self){ |
| 15 | - self.contentId = contentId; | 15 | + self.ID = ID; |
| 16 | self.url = url; | 16 | self.url = url; |
| 17 | - self.finish = NO; | ||
| 18 | } | 17 | } |
| 19 | return self; | 18 | return self; |
| 20 | 19 | ||
| @@ -28,12 +27,12 @@ | @@ -28,12 +27,12 @@ | ||
| 28 | 27 | ||
| 29 | } | 28 | } |
| 30 | 29 | ||
| 31 | - if(!self.contentId){ | 30 | + if(!self.ID){ |
| 32 | result = YES; | 31 | result = YES; |
| 33 | 32 | ||
| 34 | } | 33 | } |
| 35 | 34 | ||
| 36 | - if([self.contentId isEqualToString:@""]){ | 35 | + if([self.ID isEqualToString:@""]){ |
| 37 | result = YES; | 36 | result = YES; |
| 38 | 37 | ||
| 39 | } | 38 | } |
CNLivePlayer/Classes/Download/CNLiveHTTPProxyService.h deleted
100644 → 0
| 1 | -// | ||
| 2 | -// CNLiveHTTPProxyService.h | ||
| 3 | -// CNLivePlayer_Example | ||
| 4 | -// | ||
| 5 | -// Created by CNLive-zxw on 2019/7/26. | ||
| 6 | -// Copyright © 2019 153993236@qq.com. All rights reserved. | ||
| 7 | -// | ||
| 8 | - | ||
| 9 | -#import <Foundation/Foundation.h> | ||
| 10 | - | ||
| 11 | -NS_ASSUME_NONNULL_BEGIN | ||
| 12 | - | ||
| 13 | -@interface CNLiveHTTPProxyService : NSObject | ||
| 14 | - | ||
| 15 | -+ (CNLiveHTTPProxyService *)manager; | ||
| 16 | - | ||
| 17 | -/** | ||
| 18 | - * 设置缓存区位置 | ||
| 19 | - */ | ||
| 20 | -- (void)setCacheRoot:(NSString *)cacheRoot; | ||
| 21 | - | ||
| 22 | -/** | ||
| 23 | - * 查询缓存区位置 | ||
| 24 | - */ | ||
| 25 | -- (NSString *)cacheRoot; | ||
| 26 | - | ||
| 27 | - | ||
| 28 | -/** | ||
| 29 | - * 查询当前缓存策略,0代表限制缓存区总大小,1代表限制缓存区文件个数 | ||
| 30 | - */ | ||
| 31 | -- (NSInteger)getCacheStrategy; | ||
| 32 | - | ||
| 33 | -/** | ||
| 34 | - * 设置缓存区文件总大小限制 | ||
| 35 | - */ | ||
| 36 | -- (void)setMaxCacheSizeLimited:(long long)maxCacheSize; | ||
| 37 | - | ||
| 38 | -/** | ||
| 39 | - * 查询缓存区最大大小 | ||
| 40 | - */ | ||
| 41 | -- (long long)maxCacheSize; | ||
| 42 | - | ||
| 43 | -/** | ||
| 44 | - * 设置缓存区文件总个数限制 | ||
| 45 | - */ | ||
| 46 | -- (void)setMaxFilesCountLimited:(NSInteger)maxFilesCount; | ||
| 47 | - | ||
| 48 | -/** | ||
| 49 | - * 查询缓存区最大文件个数 | ||
| 50 | - */ | ||
| 51 | -- (NSInteger)maxFilesCount; | ||
| 52 | - | ||
| 53 | -/** | ||
| 54 | - * 设置单个文件大小限制,超过该大小的文件将不被缓存。 | ||
| 55 | - * 默认大小为500M | ||
| 56 | - */ | ||
| 57 | -- (void)setMaxSingleFileSize:(long long)maxSingleFileSize; | ||
| 58 | - | ||
| 59 | -/** | ||
| 60 | - * 设置单个文件大小限制,超过该大小的文件将不被缓存。 | ||
| 61 | - * 默认大小为500M | ||
| 62 | - */ | ||
| 63 | -- (void)setDisableCache:(BOOL)disableCache; | ||
| 64 | - | ||
| 65 | -/** | ||
| 66 | - * 启动server | ||
| 67 | - */ | ||
| 68 | -- (void)startServer; | ||
| 69 | - | ||
| 70 | -/** | ||
| 71 | - * 关闭server | ||
| 72 | - */ | ||
| 73 | -- (void)stopServer; | ||
| 74 | - | ||
| 75 | -/** | ||
| 76 | - * 查询server是否在运行状态 | ||
| 77 | - */ | ||
| 78 | -- (BOOL)isRunning; | ||
| 79 | - | ||
| 80 | - | ||
| 81 | -/** | ||
| 82 | - * 获取代理后的播放地址 | ||
| 83 | - */ | ||
| 84 | -- (NSString *)getProxyUrl:(NSString *)url; | ||
| 85 | - | ||
| 86 | -/** | ||
| 87 | - * 获取原始播放地址 | ||
| 88 | - */ | ||
| 89 | -- (NSString *)getOriginalUrl:(NSString *)url; | ||
| 90 | - | ||
| 91 | -/** | ||
| 92 | - * 获取代理后的播放地址 | ||
| 93 | - */ | ||
| 94 | -- (NSString *)getProxyUrl:(NSString *)url newCache:(BOOL)newCache; | ||
| 95 | - | ||
| 96 | -/** | ||
| 97 | - * 删除缓存区所以文件 | ||
| 98 | - */ | ||
| 99 | -- (void)deleteAllCachesWithError:(NSError **)error; | ||
| 100 | - | ||
| 101 | -/** | ||
| 102 | - * 删除某个url对应的缓存文件 | ||
| 103 | - */ | ||
| 104 | -- (void)deleteCacheForUrl:(NSURL *)url error:(NSError **)error; | ||
| 105 | - | ||
| 106 | -/** | ||
| 107 | - * 查询某个url缓存是否完成 | ||
| 108 | - */ | ||
| 109 | -- (BOOL)isCacheCompleteForUrl:(NSURL *)url; | ||
| 110 | - | ||
| 111 | -/** | ||
| 112 | - * 获得缓存已完成文件列表 | ||
| 113 | - * 返回的数组元素为NSDictionay, 包含为url和文件路径 | ||
| 114 | - */ | ||
| 115 | -- (NSArray *)getAllCachedFileListWithError:(NSError **)errors; | ||
| 116 | - | ||
| 117 | -/** | ||
| 118 | - * 获得缓存未完成文件列表 | ||
| 119 | - * 返回的数组元素为NSDictionay, 包含url、文件路径、content length、cache fragments | ||
| 120 | - */ | ||
| 121 | -- (NSArray *)getAllCachingFileListWithError:(NSError **)error; | ||
| 122 | - | ||
| 123 | -/** | ||
| 124 | - * 获得url对应缓存文件的路径 | ||
| 125 | - */ | ||
| 126 | -- (NSString *)getCachedFilePathForUrl:(NSURL *)url; | ||
| 127 | - | ||
| 128 | -/** | ||
| 129 | - * 获得url对应缓存未完成url对应的cache fragment | ||
| 130 | - */ | ||
| 131 | -- (NSArray *)getCacheFragmentForUrl:(NSURL *)url error:(NSError **)error; | ||
| 132 | - | ||
| 133 | -/** | ||
| 134 | - 发送http请求时需要header带上的字段 | ||
| 135 | - 必须 key:value 都是NSString类型 | ||
| 136 | - @param headers 请求头带的字段 | ||
| 137 | - */ | ||
| 138 | -- (void)setHttpHeaders:(NSDictionary<NSString *,NSString *> *)headers; | ||
| 139 | - | ||
| 140 | - | ||
| 141 | -@end | ||
| 142 | - | ||
| 143 | -NS_ASSUME_NONNULL_END |
CNLivePlayer/Classes/Download/CNLiveHTTPProxyService.m deleted
100644 → 0
| 1 | -// | ||
| 2 | -// CNLiveHTTPProxyService.m | ||
| 3 | -// CNLivePlayer_Example | ||
| 4 | -// | ||
| 5 | -// Created by CNLive-zxw on 2019/7/26. | ||
| 6 | -// Copyright © 2019 153993236@qq.com. All rights reserved. | ||
| 7 | -// | ||
| 8 | - | ||
| 9 | -#import "CNLiveHTTPProxyService.h" | ||
| 10 | -//缓存视频 | ||
| 11 | -#import <KSYHTTPCache/KSYHTTPProxyService.h> | ||
| 12 | -#import <KSYHTTPCache/HTTPCacheDefines.h> | ||
| 13 | -//数据库 | ||
| 14 | -#import "CNLivePlayerDBTool.h" | ||
| 15 | -#import "CNLivePlayerDBModel.h" | ||
| 16 | - | ||
| 17 | -@implementation CNLiveHTTPProxyService | ||
| 18 | - | ||
| 19 | -+ (CNLiveHTTPProxyService *)manager { | ||
| 20 | - static id instance = nil; | ||
| 21 | - static dispatch_once_t onceToken; | ||
| 22 | - dispatch_once(&onceToken, ^{ | ||
| 23 | - instance = [[self alloc] init]; | ||
| 24 | - }); | ||
| 25 | - | ||
| 26 | - return instance; | ||
| 27 | -} | ||
| 28 | - | ||
| 29 | -- (void)setCacheRoot:(NSString *)cacheRoot { | ||
| 30 | - [[KSYHTTPProxyService sharedInstance] setCacheRoot:cacheRoot]; | ||
| 31 | -} | ||
| 32 | - | ||
| 33 | -- (NSString *)cacheRoot { | ||
| 34 | - return [[KSYHTTPProxyService sharedInstance] cacheRoot]; | ||
| 35 | -} | ||
| 36 | - | ||
| 37 | -- (NSInteger)getCacheStrategy { | ||
| 38 | - return [[KSYHTTPProxyService sharedInstance] getCacheStrategy]; | ||
| 39 | -} | ||
| 40 | - | ||
| 41 | -- (void)setMaxCacheSizeLimited:(long long)maxCacheSize { | ||
| 42 | - [[KSYHTTPProxyService sharedInstance] setMaxCacheSizeLimited:maxCacheSize]; | ||
| 43 | -} | ||
| 44 | - | ||
| 45 | -- (long long)maxCacheSize { | ||
| 46 | - return [[KSYHTTPProxyService sharedInstance] maxCacheSize]; | ||
| 47 | -} | ||
| 48 | - | ||
| 49 | -- (void)setMaxFilesCountLimited:(NSInteger)maxFilesCount { | ||
| 50 | - [[KSYHTTPProxyService sharedInstance] setMaxFilesCountLimited:maxFilesCount]; | ||
| 51 | -} | ||
| 52 | - | ||
| 53 | -- (NSInteger)maxFilesCount { | ||
| 54 | - return [[KSYHTTPProxyService sharedInstance] maxFilesCount]; | ||
| 55 | -} | ||
| 56 | - | ||
| 57 | -- (void)setMaxSingleFileSize:(long long)maxSingleFileSize { | ||
| 58 | - [[KSYHTTPProxyService sharedInstance] setMaxSingleFileSize:maxSingleFileSize]; | ||
| 59 | -} | ||
| 60 | - | ||
| 61 | -- (void)setDisableCache:(BOOL)disableCache { | ||
| 62 | - [[KSYHTTPProxyService sharedInstance] setDisableCache:disableCache]; | ||
| 63 | -} | ||
| 64 | - | ||
| 65 | -- (void)startServer { | ||
| 66 | - [[KSYHTTPProxyService sharedInstance] startServer]; | ||
| 67 | -} | ||
| 68 | - | ||
| 69 | -- (void)stopServer { | ||
| 70 | - [[KSYHTTPProxyService sharedInstance] stopServer]; | ||
| 71 | -} | ||
| 72 | - | ||
| 73 | -- (BOOL)isRunning { | ||
| 74 | - return [[KSYHTTPProxyService sharedInstance] isRunning]; | ||
| 75 | -} | ||
| 76 | - | ||
| 77 | -- (NSString *)getProxyUrl:(NSString *)url { | ||
| 78 | - return [[KSYHTTPProxyService sharedInstance] getProxyUrl:url]; | ||
| 79 | -} | ||
| 80 | - | ||
| 81 | -- (NSString *)getOriginalUrl:(NSString *)url { | ||
| 82 | - return [[KSYHTTPProxyService sharedInstance] getOriginalUrl:url]; | ||
| 83 | -} | ||
| 84 | - | ||
| 85 | -- (NSString *)getProxyUrl:(NSString *)url newCache:(BOOL)newCache { | ||
| 86 | - return [[KSYHTTPProxyService sharedInstance] getProxyUrl:url newCache:newCache]; | ||
| 87 | - | ||
| 88 | -} | ||
| 89 | - | ||
| 90 | -- (void)deleteAllCachesWithError:(NSError *__autoreleasing *)error { | ||
| 91 | - [[KSYHTTPProxyService sharedInstance] deleteAllCachesWithError:error]; | ||
| 92 | - //清空数据缓存 | ||
| 93 | - [[CNLivePlayerDBTool shared] clearTable]; | ||
| 94 | -} | ||
| 95 | - | ||
| 96 | -- (void)deleteCacheForUrl:(NSURL *)url error:(NSError *__autoreleasing *)error { | ||
| 97 | - [[KSYHTTPProxyService sharedInstance] deleteCacheForUrl:url error:error]; | ||
| 98 | - | ||
| 99 | -} | ||
| 100 | - | ||
| 101 | -/** | ||
| 102 | - * 删除某个contentId对应的缓存文件 | ||
| 103 | - */ | ||
| 104 | -- (void)deleteCacheForContentId:(NSString *)contentId error:(NSError *__autoreleasing *)error { | ||
| 105 | - CNLivePlayerDBModel *model = [[CNLivePlayerDBTool shared] searchInTable:contentId]; | ||
| 106 | - if (model) { | ||
| 107 | - [[KSYHTTPProxyService sharedInstance] deleteCacheForUrl:[NSURL URLWithString:model.url] error:error]; | ||
| 108 | - [[CNLivePlayerDBTool shared] deleteTable:contentId]; | ||
| 109 | - } | ||
| 110 | -} | ||
| 111 | - | ||
| 112 | -- (BOOL)isCacheCompleteForUrl:(NSURL *)url { | ||
| 113 | - return [[KSYHTTPProxyService sharedInstance] isCacheCompleteForUrl:url]; | ||
| 114 | -} | ||
| 115 | - | ||
| 116 | - | ||
| 117 | -/** | ||
| 118 | - * 查看某个contentId缓存是否完成 | ||
| 119 | - */ | ||
| 120 | -- (BOOL)isCacheCompleteForContentId:(NSString *)contentId { | ||
| 121 | - CNLivePlayerDBModel *model = [[CNLivePlayerDBTool shared] searchInTable:contentId]; | ||
| 122 | - if (model) { | ||
| 123 | - return [[CNLiveHTTPProxyService manager] isCacheCompleteForUrl:[NSURL URLWithString:model.url]]; | ||
| 124 | - } | ||
| 125 | - return NO; | ||
| 126 | -} | ||
| 127 | - | ||
| 128 | -- (NSArray *)getAllCachedFileListWithError:(NSError *__autoreleasing *)errors { | ||
| 129 | - return [[KSYHTTPProxyService sharedInstance] getAllCachedFileListWithError:errors]; | ||
| 130 | -} | ||
| 131 | - | ||
| 132 | -- (NSArray *)getAllCachingFileListWithError:(NSError *__autoreleasing *)error { | ||
| 133 | - return [[KSYHTTPProxyService sharedInstance] getAllCachingFileListWithError:error]; | ||
| 134 | -} | ||
| 135 | - | ||
| 136 | -- (NSString *)getCachedFilePathForUrl:(NSURL *)url { | ||
| 137 | - return [[KSYHTTPProxyService sharedInstance] getCachedFilePathForUrl:url]; | ||
| 138 | -} | ||
| 139 | - | ||
| 140 | -- (NSArray *)getCacheFragmentForUrl:(NSURL *)url error:(NSError *__autoreleasing *)error { | ||
| 141 | - return [[KSYHTTPProxyService sharedInstance] getCacheFragmentForUrl:url error:error]; | ||
| 142 | -} | ||
| 143 | - | ||
| 144 | -- (void)setHttpHeaders:(NSDictionary<NSString *,NSString *> *)headers { | ||
| 145 | - [[KSYHTTPProxyService sharedInstance] setHttpHeaders:headers]; | ||
| 146 | -} | ||
| 147 | - | ||
| 148 | -@end |
CNLivePlayer/Classes/Download/CNLivePlayerDownloader.h deleted
100644 → 0
| 1 | -// | ||
| 2 | -// CNLivePlayerDownloader.h | ||
| 3 | -// CNLivePlayer_Example | ||
| 4 | -// | ||
| 5 | -// Created by CNLive-zxw on 2019/7/26. | ||
| 6 | -// Copyright © 2019 153993236@qq.com. All rights reserved. | ||
| 7 | -// | ||
| 8 | - | ||
| 9 | -#import <Foundation/Foundation.h> | ||
| 10 | -#import "CNLivePlayerDefines.h" | ||
| 11 | - | ||
| 12 | -NS_ASSUME_NONNULL_BEGIN | ||
| 13 | - | ||
| 14 | -/* | ||
| 15 | - * 下载进度回调 | ||
| 16 | - */ | ||
| 17 | -typedef void(^DownloaderProgressBlock)(float progress); | ||
| 18 | - | ||
| 19 | -/* | ||
| 20 | - * 成功回调 | ||
| 21 | - */ | ||
| 22 | -typedef void(^DownloaderSuccessBlock)(void); | ||
| 23 | - | ||
| 24 | -/* | ||
| 25 | - * 失败回调 | ||
| 26 | - */ | ||
| 27 | -typedef void(^DownloaderFailureBlock)(NSDictionary *errorInfo); | ||
| 28 | - | ||
| 29 | -@interface CNLivePlayerDownloader : NSObject | ||
| 30 | - | ||
| 31 | -/** | ||
| 32 | - * 初始化下载器 | ||
| 33 | - * @param urlString 视频原始地址(未经过代理处理的) | ||
| 34 | - * @param progressBlock 视频缓存进度回调 | ||
| 35 | - */ | ||
| 36 | -- (instancetype)initWithUrlString:(NSString *)urlString progressBlock:(DownloaderProgressBlock)progressBlock; | ||
| 37 | - | ||
| 38 | -/** | ||
| 39 | - * 开始缓存 | ||
| 40 | - */ | ||
| 41 | -- (void)startDownload; | ||
| 42 | - | ||
| 43 | -/** | ||
| 44 | - * 暂停缓存 | ||
| 45 | - */ | ||
| 46 | -- (void)pauseDownload; | ||
| 47 | - | ||
| 48 | -/** | ||
| 49 | - * 当前缓存的url | ||
| 50 | - */ | ||
| 51 | -- (NSString *)urlString; | ||
| 52 | - | ||
| 53 | -/** | ||
| 54 | - * 获取下载器的状态 | ||
| 55 | - */ | ||
| 56 | -- (CNLivePlayerDownloaderState)downloaderState; | ||
| 57 | - | ||
| 58 | -/** | ||
| 59 | - * 播放地址为url的视频时,调用此方法 | ||
| 60 | - */ | ||
| 61 | -+ (void)handleOpenPlayerForUrl:(NSString *)url progressBlock:(DownloaderProgressBlock)progressBlock; | ||
| 62 | - | ||
| 63 | -/** | ||
| 64 | - * 停止播放地址为url的视频时,调用此方法 | ||
| 65 | - */ | ||
| 66 | -+ (void)handleClosePlayerForUrl:(NSString *)url progressBlock:(DownloaderProgressBlock)progressBlock; | ||
| 67 | - | ||
| 68 | -@end | ||
| 69 | - | ||
| 70 | -NS_ASSUME_NONNULL_END |
CNLivePlayer/Classes/Download/CNLivePlayerDownloader.m deleted
100644 → 0
| 1 | -// | ||
| 2 | -// CNLivePlayerDownloader.m | ||
| 3 | -// CNLivePlayer_Example | ||
| 4 | -// | ||
| 5 | -// Created by CNLive-zxw on 2019/7/26. | ||
| 6 | -// Copyright © 2019 153993236@qq.com. All rights reserved. | ||
| 7 | -// | ||
| 8 | - | ||
| 9 | -#import "CNLivePlayerDownloader.h" | ||
| 10 | -#import <KSYHTTPCache/KSYFileDownloader.h> | ||
| 11 | - | ||
| 12 | -//缓存视频 | ||
| 13 | -#import <KSYHTTPCache/KSYHTTPProxyService.h> | ||
| 14 | -#import <KSYHTTPCache/HTTPCacheDefines.h> | ||
| 15 | -//数据库 | ||
| 16 | -#import "CNLivePlayerDBTool.h" | ||
| 17 | -#import "CNLivePlayerDBModel.h" | ||
| 18 | - | ||
| 19 | -@interface CNLivePlayerDownloader () | ||
| 20 | -@property (nonatomic, strong) KSYFileDownloader *downloader; | ||
| 21 | - | ||
| 22 | -@end | ||
| 23 | - | ||
| 24 | -@implementation CNLivePlayerDownloader | ||
| 25 | - | ||
| 26 | -/** | ||
| 27 | - * 初始化下载器 | ||
| 28 | - * @param urlString 视频原始地址(未经过代理处理的) | ||
| 29 | - * @param progressBlock 视频缓存进度回调 | ||
| 30 | - */ | ||
| 31 | -- (instancetype)initWithUrlString:(NSString *)urlString progressBlock:(DownloaderProgressBlock)progressBlock{ | ||
| 32 | - self = [super init]; | ||
| 33 | - if (self) { | ||
| 34 | - self.downloader = [[KSYFileDownloader alloc] initWithUrlString:urlString progressBlock:progressBlock]; | ||
| 35 | - } | ||
| 36 | - return self; | ||
| 37 | - | ||
| 38 | -} | ||
| 39 | - | ||
| 40 | -/** | ||
| 41 | - * 开始缓存 | ||
| 42 | - */ | ||
| 43 | -- (void)startDownload { | ||
| 44 | - [self.downloader startDownload]; | ||
| 45 | -} | ||
| 46 | - | ||
| 47 | -/** | ||
| 48 | - * 暂停缓存 | ||
| 49 | - */ | ||
| 50 | -- (void)pauseDownload { | ||
| 51 | - [self.downloader pauseDownload]; | ||
| 52 | -} | ||
| 53 | - | ||
| 54 | -/** | ||
| 55 | - * 当前缓存的url | ||
| 56 | - */ | ||
| 57 | -- (NSString *)urlString { | ||
| 58 | - return [self.downloader urlString]; | ||
| 59 | -} | ||
| 60 | - | ||
| 61 | -/** | ||
| 62 | - * 获取下载器的状态 | ||
| 63 | - */ | ||
| 64 | -- (CNLivePlayerDownloaderState)downloaderState { | ||
| 65 | - return (CNLivePlayerDownloaderState)[self.downloader downloaderState]; | ||
| 66 | -} | ||
| 67 | - | ||
| 68 | -/** | ||
| 69 | - * 播放地址为url的视频时,调用此方法 | ||
| 70 | - */ | ||
| 71 | -+ (void)handleOpenPlayerForUrl:(NSString *)url progressBlock:(void (^)(float))progressBlock { | ||
| 72 | - [KSYFileDownloader handleOpenPlayerForUrl:url progressBlock:progressBlock]; | ||
| 73 | -} | ||
| 74 | - | ||
| 75 | -/** | ||
| 76 | - * 停止播放地址为url的视频时,调用此方法 | ||
| 77 | - */ | ||
| 78 | -+ (void)handleClosePlayerForUrl:(NSString *)url progressBlock:(void (^)(float))progressBlock { | ||
| 79 | - [KSYFileDownloader handleClosePlayerForUrl:url progressBlock:progressBlock]; | ||
| 80 | -} | ||
| 81 | - | ||
| 82 | - | ||
| 83 | -@end |
CNLivePlayer/Classes/Header/CNLivePlayerConst.h deleted
100644 → 0
CNLivePlayer/Classes/Header/CNLivePlayerDefines.h deleted
100644 → 0
| 1 | -// | ||
| 2 | -// CNLivePlayerDefines.h | ||
| 3 | -// CNLivePlayer | ||
| 4 | -// | ||
| 5 | -// Created by CNLive-zxw on 2019/8/14. | ||
| 6 | -// Copyright © 2019 153993236@qq.com. All rights reserved. | ||
| 7 | -// | ||
| 8 | - | ||
| 9 | -#ifndef CNLivePlayerDefines_h | ||
| 10 | -#define CNLivePlayerDefines_h | ||
| 11 | - | ||
| 12 | -/* | ||
| 13 | - * 播放器类型 | ||
| 14 | - */ | ||
| 15 | -typedef NS_ENUM(NSUInteger, CNLivePlayerType) { | ||
| 16 | - CNLivePlayerVod, //点播 | ||
| 17 | - CNLivePlayerLive, //直播 | ||
| 18 | - CNLivePlayerAudio //音频 | ||
| 19 | - | ||
| 20 | -}; | ||
| 21 | -/** | ||
| 22 | - * 视频解码模式 | ||
| 23 | - */ | ||
| 24 | -typedef NS_ENUM(NSUInteger, CNLiveMovieVideoDecoderMode) { | ||
| 25 | - ///视频解码方式采用软解 | ||
| 26 | - CNLiveMovieVideoDecoderMode_Software = 0, | ||
| 27 | - ///视频解码方式采用硬解 | ||
| 28 | - CNLiveMovieVideoDecoderMode_Hardware, | ||
| 29 | - ///自动选择解码方式,8.0以上的系统优先选择硬解 | ||
| 30 | - CNLiveMovieVideoDecoderMode_AUTO, | ||
| 31 | - ///使用系统接口进行解码和渲染,只适用于8.0及以上系统,低于8.0的系统自动使用软解 | ||
| 32 | - CNLiveMovieVideoDecoderMode_DisplayLayer, | ||
| 33 | -}; | ||
| 34 | - | ||
| 35 | -/** | ||
| 36 | - * 主播直播状态 | ||
| 37 | - */ | ||
| 38 | -typedef NS_ENUM(NSUInteger, CNLiveHostStatus) { | ||
| 39 | - //主播直播中 | ||
| 40 | - CNLiveHostStatusLiving = 1, | ||
| 41 | - //直播已结束 | ||
| 42 | - CNLiveHostStatusEnd = 2, | ||
| 43 | - //主播已离开 | ||
| 44 | - CNLiveHostStatusLeaving = 3 | ||
| 45 | - | ||
| 46 | -}; | ||
| 47 | - | ||
| 48 | -/** | ||
| 49 | - * PlayerDownloader任务状态 | ||
| 50 | - */ | ||
| 51 | -typedef NS_ENUM(NSInteger, CNLivePlayerDownloaderState) { | ||
| 52 | - CNLivePlayerDownloaderUnknown = 0, //未知 | ||
| 53 | - CNLivePlayerDownloaderPause, //暂停 | ||
| 54 | - CNLivePlayerDownloaderDownloading, //下载中 | ||
| 55 | - CNLivePlayerDownloaderInvalid, //无效 | ||
| 56 | - CNLivePlayerDownloaderFinished //完成 | ||
| 57 | -}; | ||
| 58 | - | ||
| 59 | -#pragma mark - 音频播放状态 | ||
| 60 | -typedef NS_ENUM(NSUInteger, CNLiveBgmPlayerState) { | ||
| 61 | - /// 初始状态 | ||
| 62 | - CNLiveBgmPlayerStateInit, | ||
| 63 | - /// 背景音正在播放 | ||
| 64 | - CNLiveBgmPlayerStateStarting, | ||
| 65 | - /// 背景音停止 | ||
| 66 | - CNLiveBgmPlayerStateStopped, | ||
| 67 | - /// 背景音正在播放 | ||
| 68 | - CNLiveBgmPlayerStatePlaying, | ||
| 69 | - /// 背景音暂停 | ||
| 70 | - CNLiveBgmPlayerStatePaused, | ||
| 71 | - /// 背景音播放出错 | ||
| 72 | - CNLiveBgmPlayerStateError, | ||
| 73 | - /// 背景音被打断 | ||
| 74 | - CNLiveBgmPlayerStateInterrupted | ||
| 75 | -}; | ||
| 76 | - | ||
| 77 | -#pragma mark - 美颜 | ||
| 78 | -typedef NS_ENUM(NSUInteger, CNLiveBeautyType) { | ||
| 79 | - ///无 | ||
| 80 | - CNLiveBeautyTypeNone = 0, | ||
| 81 | - ///旧美颜 | ||
| 82 | - CNLiveBeautyTypeOld, | ||
| 83 | - ///红润美颜 | ||
| 84 | - CNLiveBeautyTypeRuddy, | ||
| 85 | - ///美颜特效 | ||
| 86 | - CNLiveBeautyTypeSpecial | ||
| 87 | -}; | ||
| 88 | - | ||
| 89 | -typedef NS_ENUM(NSUInteger, CNLiveSpecialEffects) { | ||
| 90 | - ///小清新 | ||
| 91 | - CNLiveSpecialEffectsFresh = 0, | ||
| 92 | - ///靓丽 | ||
| 93 | - CNLiveSpecialEffectsPretty, | ||
| 94 | - ///甜美可人 | ||
| 95 | - CNLiveSpecialEffectsSweet, | ||
| 96 | - ///怀旧 | ||
| 97 | - CNLiveSpecialEffectsNostalgia, | ||
| 98 | - ///蓝调 | ||
| 99 | - CNLiveSpecialEffectsBlues, | ||
| 100 | - ///老照片 | ||
| 101 | - CNLiveSpecialEffectsOldPicture, | ||
| 102 | - ///樱花 | ||
| 103 | - CNLiveSpecialEffectsCherry, | ||
| 104 | - ///樱花(光线较暗) | ||
| 105 | - CNLiveSpecialEffectsCherryDark, | ||
| 106 | - ///红润(光线较暗) | ||
| 107 | - CNLiveSpecialEffectsRuddyDark, | ||
| 108 | - ///阳光(光线较暗) | ||
| 109 | - CNLiveSpecialEffectsSunlightDark, | ||
| 110 | - ///红润 | ||
| 111 | - CNLiveSpecialEffectsRuddy, | ||
| 112 | - ///阳光 | ||
| 113 | - CNLiveSpecialEffectsSunlight, | ||
| 114 | - ///自然 | ||
| 115 | - CNLiveSpecialEffectsNature | ||
| 116 | -}; | ||
| 117 | - | ||
| 118 | -#pragma mark - Video Codec ID | ||
| 119 | -/*! | ||
| 120 | - * @abstract 视频编码器类型 | ||
| 121 | - */ | ||
| 122 | -typedef NS_ENUM(NSUInteger, CNLiveVideoCodec) { | ||
| 123 | - /// 视频编码器 - h264 软件编码器 | ||
| 124 | - CNLiveVideoCodec_X264 = 0, | ||
| 125 | - /// 视频编码器 - CNLive265 软件编码器 | ||
| 126 | - CNLiveVideoCodec_QY265, | ||
| 127 | - /// 视频编码器 - iOS VT264硬件编码器 (iOS 8.0以上支持) | ||
| 128 | - CNLiveVideoCodec_VT264, | ||
| 129 | - /// 视频编码器 - iOS VT265硬件编码器 (iOS 11.0 以上) | ||
| 130 | - CNLiveVideoCodec_VT265, | ||
| 131 | - /// 视频编码器 - 由SDK自动选择( VT264 > X264) | ||
| 132 | - CNLiveVideoCodec_AUTO = 100, | ||
| 133 | - /// 视频编码器 - gif | ||
| 134 | - CNLiveVideoCodec_GIF, | ||
| 135 | -}; | ||
| 136 | - | ||
| 137 | -/*! | ||
| 138 | - * @abstract 音频编码器类型 | ||
| 139 | - */ | ||
| 140 | -typedef NS_ENUM(NSUInteger, CNLiveAudioCodec) { | ||
| 141 | - /// aac音频软件编码器 - AAC_HE | ||
| 142 | - CNLiveAudioCodec_AAC_HE = 0, | ||
| 143 | - /// aac音频软件编码器 - AAC_LC | ||
| 144 | - CNLiveAudioCodec_AAC, | ||
| 145 | - /// iOS自带的audiotoolbox音频编码器 - AAC_LC (CPU占用较低,但推荐码率:64kbps单声道,128kbps双声道) | ||
| 146 | - CNLiveAudioCodec_AT_AAC, | ||
| 147 | - /// aac软件编码器 - AAC_HE_V2 (仅双声道音频可用;如果输入数据为单声道,则自动退化为 AAC_HE) | ||
| 148 | - CNLiveAudioCodec_AAC_HE_V2, | ||
| 149 | -}; | ||
| 150 | - | ||
| 151 | -/*! | ||
| 152 | - * @abstract 网络自适应模式类型 | ||
| 153 | - */ | ||
| 154 | -typedef NS_ENUM(NSUInteger, CNLiveBWEstimateMode) { | ||
| 155 | - /// 默认模式 (综合模式,比较平稳) | ||
| 156 | - CNLiveBWEstMode_Default = 0, | ||
| 157 | - /// 流畅优先模式(消极上调, 极速下调) | ||
| 158 | - CNLiveBWEstMode_Negtive, | ||
| 159 | - | ||
| 160 | - /// 禁用网络自适应网络调整 | ||
| 161 | - CNLiveBWEstMode_Disable = 1000, | ||
| 162 | -}; | ||
| 163 | - | ||
| 164 | -/// 直播场景 (CNLive内部会根据场景的特征进行参数调优) | ||
| 165 | -typedef NS_ENUM(NSUInteger, CNLiveLiveScene) { | ||
| 166 | - /// 默认通用场景(不确定场景时使用) | ||
| 167 | - CNLiveLiveScene_Default = 0, | ||
| 168 | - /// 秀场场景, 主播上半身为主 | ||
| 169 | - CNLiveLiveScene_Showself, | ||
| 170 | - /// 游戏场景 | ||
| 171 | - CNLiveLiveScene_Game, | ||
| 172 | - // others comming soon | ||
| 173 | -}; | ||
| 174 | - | ||
| 175 | -/// 录制场景 | ||
| 176 | -typedef NS_ENUM(NSUInteger, CNLiveRecScene) { | ||
| 177 | - /// 恒定码率场景 | ||
| 178 | - CNLiveRecScene_ConstantBitRate = 0, | ||
| 179 | - /// 恒定质量场景 | ||
| 180 | - CNLiveRecScene_ConstantQuality, | ||
| 181 | -}; | ||
| 182 | - | ||
| 183 | -/// 视频编码性能档次 (视频质量 和 设备资源之间的权衡) | ||
| 184 | -typedef NS_ENUM(NSUInteger, CNLiveVideoEncodePerformance) { | ||
| 185 | - /// 低功耗: cpu资源消耗低一些,视频质量差一些 | ||
| 186 | - CNLiveVideoEncodePer_LowPower= 0, | ||
| 187 | - /// 均衡档次: 性价比比较高 | ||
| 188 | - CNLiveVideoEncodePer_Balance, | ||
| 189 | - /// 高性能: 画面质量高 | ||
| 190 | - CNLiveVideoEncodePer_HighPerformance, | ||
| 191 | -}; | ||
| 192 | - | ||
| 193 | -#pragma mark - QYPublisher State | ||
| 194 | - | ||
| 195 | -/*! | ||
| 196 | - * @abstract 采集设备状态 | ||
| 197 | - */ | ||
| 198 | -typedef NS_ENUM(NSUInteger, CNLiveCaptureState) { | ||
| 199 | - /// 设备空闲中 | ||
| 200 | - CNLiveCaptureStateIdle, | ||
| 201 | - /// 设备工作中 | ||
| 202 | - CNLiveCaptureStateCapturing, | ||
| 203 | - /// 设备授权被拒绝 | ||
| 204 | - CNLiveCaptureStateDevAuthDenied, | ||
| 205 | - /// 关闭采集设备中 | ||
| 206 | - CNLiveCaptureStateClosingCapture, | ||
| 207 | - /// 参数错误,无法打开(比如设置的分辨率,码率当前设备不支持) | ||
| 208 | - CNLiveCaptureStateParameterError, | ||
| 209 | - /// 设备正忙,请稍后尝试 ( 该状态在发出通知0.5秒后被清除 ) | ||
| 210 | - CNLiveCaptureStateDevBusy, | ||
| 211 | -}; | ||
| 212 | - | ||
| 213 | -/*! | ||
| 214 | - * @abstract 推流状态 | ||
| 215 | - */ | ||
| 216 | -typedef NS_ENUM(NSUInteger, CNLiveStreamState) { | ||
| 217 | - /// 初始化时状态为空闲 | ||
| 218 | - CNLiveStreamStateIdle = 0, | ||
| 219 | - /// 连接中 | ||
| 220 | - CNLiveStreamStateConnecting, | ||
| 221 | - /// 已连接 | ||
| 222 | - CNLiveStreamStateConnected, | ||
| 223 | - /// 断开连接中 | ||
| 224 | - CNLiveStreamStateDisconnecting, | ||
| 225 | - /// 推流出错 | ||
| 226 | - CNLiveStreamStateError, | ||
| 227 | - /// 结束推流成功 | ||
| 228 | - CNLiveStopStreamSuccess, | ||
| 229 | - /// 结束推流失败 | ||
| 230 | - CNLiveStopStreamFailure, | ||
| 231 | -}; | ||
| 232 | - | ||
| 233 | -/*! | ||
| 234 | - * @abstract 推流错误码,用于指示推流失败的原因 | ||
| 235 | - */ | ||
| 236 | -typedef NS_ENUM(NSUInteger, CNLiveStreamErrorCode) { | ||
| 237 | - /// 正常无错误 | ||
| 238 | - CNLiveStreamErrorCode_NONE = 0, | ||
| 239 | - /// (obsolete) | ||
| 240 | - CNLiveStreamErrorCode_CNLiveAUTHFAILED __deprecated_enum_msg("auth removed"), | ||
| 241 | - /// 当前帧编码失败 | ||
| 242 | - CNLiveStreamErrorCode_ENCODE_FRAMES_FAILED, | ||
| 243 | - /// 无法打开配置指示的CODEC | ||
| 244 | - CNLiveStreamErrorCode_CODEC_OPEN_FAILED, | ||
| 245 | - /// 连接出错,检查地址 | ||
| 246 | - CNLiveStreamErrorCode_CONNECT_FAILED, | ||
| 247 | - /// 网络连接中断 | ||
| 248 | - CNLiveStreamErrorCode_CONNECT_BREAK = 5, | ||
| 249 | - /// rtmp 推流域名不存在 (CNLive 自定义) | ||
| 250 | - CNLiveStreamErrorCode_RTMP_NonExistDomain, | ||
| 251 | - /// rtmp 应用名不存在(CNLive 自定义) | ||
| 252 | - CNLiveStreamErrorCode_RTMP_NonExistApplication, | ||
| 253 | - /// rtmp 流名已存在(CNLive 自定义) | ||
| 254 | - CNLiveStreamErrorCode_RTMP_AlreadyExistStreamName, | ||
| 255 | - /// rtmp 被黑名单拒绝(CNLive 自定义) | ||
| 256 | - CNLiveStreamErrorCode_RTMP_ForbiddenByBlacklist, | ||
| 257 | - /// rtmp 内部错误(CNLive 自定义) | ||
| 258 | - CNLiveStreamErrorCode_RTMP_InternalError = 10, | ||
| 259 | - /// rtmp URL 地址已过期(CNLive 自定义) | ||
| 260 | - CNLiveStreamErrorCode_RTMP_URLExpired, | ||
| 261 | - /// rtmp URL 地址签名错误(CNLive 自定义) | ||
| 262 | - CNLiveStreamErrorCode_RTMP_SignatureDoesNotMatch, | ||
| 263 | - /// rtmp URL 中AccessKeyId非法(CNLive 自定义) | ||
| 264 | - CNLiveStreamErrorCode_RTMP_InvalidAccessKeyId, | ||
| 265 | - /// rtmp URL 中参数错误(CNLive 自定义) | ||
| 266 | - CNLiveStreamErrorCode_RTMP_BadParams, | ||
| 267 | - /// rtmp URL 中的推流不在发布点内(CNLive 自定义) | ||
| 268 | - CNLiveStreamErrorCode_RTMP_ForbiddenByRegion = 15, | ||
| 269 | - /// (obsolete) | ||
| 270 | - CNLiveStreamErrorCode_FRAMES_THRESHOLD, | ||
| 271 | - /// (obsolete) | ||
| 272 | - CNLiveStreamErrorCode_NO_INPUT_SAMPLE, | ||
| 273 | - /// 对于URL中的域名解析失败 | ||
| 274 | - CNLiveStreamErrorCode_DNS_Parse_failed, | ||
| 275 | - /// 对于URL对应的服务器连接失败(无法建立TCP连接) | ||
| 276 | - CNLiveStreamErrorCode_Connect_Server_failed, | ||
| 277 | - /// 跟RTMP服务器完成握手后,向{appname}/{streamname} 推流失败 | ||
| 278 | - CNLiveStreamErrorCode_RTMP_Publish_failed = 20, | ||
| 279 | - /// 音视频同步失败 (输入的音频和视频的时间戳的差值超过5s) | ||
| 280 | - CNLiveStreamErrorCode_AV_SYNC_ERROR, | ||
| 281 | - /// 非法地址(地址为空或url中的协议或本地文件的后缀SDK不支持, 请检查) | ||
| 282 | - CNLiveStreamErrorCode_INVALID_ADDRESS, | ||
| 283 | - /// 网络不通 | ||
| 284 | - CNLiveStreamErrorCode_NETWORK_UNREACHABLE, | ||
| 285 | - /// 获取user id失败 (CNLive 自定义) | ||
| 286 | - CNLiveStreamErrorCode_RTMP_GetUserIdFailed, | ||
| 287 | - /// AK和user id不匹配 (CNLive 自定义) | ||
| 288 | - CNLiveStreamErrorCode_RTMP_AkAndUserIsNotMatch = 25, | ||
| 289 | - /// 获取服务器信息失败 (CNLive 自定义) | ||
| 290 | - CNLiveStreamErrorCode_RTMP_GetServerInfoFailed, | ||
| 291 | - /// 非法的外部url (CNLive 自定义) | ||
| 292 | - CNLiveStreamErrorCode_RTMP_IllegalOutsideUrl, | ||
| 293 | - /// 外部鉴权失败 (CNLive 自定义) | ||
| 294 | - CNLiveStreamErrorCode_RTMP_OutsideAuthFailed, | ||
| 295 | - /// 简单鉴权失败(CNLive 自定义) | ||
| 296 | - CNLiveStreamErrorCode_RTMP_SimpleAuthFailed, | ||
| 297 | - /// 无效的鉴权类型 (CNLive 自定义) | ||
| 298 | - CNLiveStreamErrorCode_RTMP_InvalidAuthType = 30, | ||
| 299 | - /// 非法的user id (CNLive 自定义) | ||
| 300 | - CNLiveStreamErrorCode_RTMP_IllegalUserId, | ||
| 301 | - /// 异常 | ||
| 302 | - CNLiveStreamErrorCode_ABNORMAL, | ||
| 303 | - | ||
| 304 | -}; | ||
| 305 | - | ||
| 306 | -typedef NS_ENUM(NSUInteger, CNLiveReconnectStatus) { | ||
| 307 | - CNLiveReconnectStatusStart = 0, | ||
| 308 | - CNLiveReconnectStatusSuccess, | ||
| 309 | - CNLiveReconnectStatusFailed | ||
| 310 | -}; | ||
| 311 | - | ||
| 312 | -#pragma mark - 旁路录制 | ||
| 313 | -/* | ||
| 314 | - * @abstract 旁路录制状态 | ||
| 315 | - */ | ||
| 316 | -typedef NS_ENUM(NSInteger, CNLiveRecordState) { | ||
| 317 | - /// 初始状态 | ||
| 318 | - CNLiveRecordStateIdle, | ||
| 319 | - /// 录像中 | ||
| 320 | - CNLiveRecordStateRecording, | ||
| 321 | - /// 录像停止 | ||
| 322 | - CNLiveRecordStateStopped, | ||
| 323 | - /// 录像失败 | ||
| 324 | - CNLiveRecordStateError, | ||
| 325 | -}; | ||
| 326 | - | ||
| 327 | -/*! | ||
| 328 | - * @abstract 旁路录制错误码 | ||
| 329 | - */ | ||
| 330 | -typedef NS_ENUM(NSInteger, CNLiveRecordError) { | ||
| 331 | - /// 无错误 | ||
| 332 | - CNLiveRecordErrorNone, | ||
| 333 | - /// 地址错误 | ||
| 334 | - CNLiveRecordErrorPathInvalid, | ||
| 335 | - /// 格式不支持 | ||
| 336 | - CNLiveRecordErrorFormatNotSupport, | ||
| 337 | - /// 内部错误 | ||
| 338 | - CNLiveRecordErrorInternal, | ||
| 339 | -}; | ||
| 340 | - | ||
| 341 | - | ||
| 342 | -/* | ||
| 343 | - * 播放器类型 | ||
| 344 | - */ | ||
| 345 | -typedef NS_ENUM(NSUInteger, CNLiveStreamerType) { | ||
| 346 | - CNLiveStreamerVod, //视频 | ||
| 347 | - CNLiveStreamerAudio //音频 | ||
| 348 | - | ||
| 349 | -}; | ||
| 350 | - | ||
| 351 | -#endif /* CNLivePlayerDefines_h */ |
CNLivePlayer/Classes/Header/CNLivePlayerDelegate.h
0 → 100644
| 1 | +// | ||
| 2 | +// CNLivePlayerDelegate.h | ||
| 3 | +// CNLivePlayer_Example | ||
| 4 | +// | ||
| 5 | +// Created by CNLive-zxw on 2019/10/24. | ||
| 6 | +// Copyright © 2019 153993236@qq.com. All rights reserved. | ||
| 7 | +// | ||
| 8 | + | ||
| 9 | +#import <Foundation/Foundation.h> | ||
| 10 | + | ||
| 11 | +NS_ASSUME_NONNULL_BEGIN | ||
| 12 | + | ||
| 13 | +@class UIView; | ||
| 14 | +@class UIImageView; | ||
| 15 | +@class CNLivePlayer; | ||
| 16 | +/** | ||
| 17 | + 发送队列的代理协议。 | ||
| 18 | + | ||
| 19 | + @since v1.0.0 | ||
| 20 | + */ | ||
| 21 | +@protocol CNLivePlayerDelegate <NSObject> | ||
| 22 | + | ||
| 23 | +@optional | ||
| 24 | + | ||
| 25 | +/** | ||
| 26 | + 告知代理对象 CNLivePlayer 即将开始进入后台播放任务 | ||
| 27 | + | ||
| 28 | + @param player 调用该代理方法的 CNLivePlayer 对象 | ||
| 29 | + | ||
| 30 | + @since v1.0.0 | ||
| 31 | + */ | ||
| 32 | +- (void)playerWillBeginBackgroundTask:(nonnull CNLivePlayer *)player; | ||
| 33 | + | ||
| 34 | +/** | ||
| 35 | + 告知代理对象 CNLivePlayer 即将结束后台播放状态任务 | ||
| 36 | + | ||
| 37 | + @param player 调用该方法的 CNLivePlayer 对象 | ||
| 38 | + | ||
| 39 | + @since v2.1.1 | ||
| 40 | + */ | ||
| 41 | +- (void)playerWillEndBackgroundTask:(nonnull CNLivePlayer *)player; | ||
| 42 | + | ||
| 43 | +/** | ||
| 44 | + 告知代理对象播放器状态变更 | ||
| 45 | + | ||
| 46 | + @param player 调用该方法的 CNLivePlayer 对象 | ||
| 47 | + @param state 变更之后的 CNLivePlayer 状态 | ||
| 48 | + | ||
| 49 | + @since v1.0.0 | ||
| 50 | + */ | ||
| 51 | +- (void)player:(nonnull CNLivePlayer *)player statusDidChange:(CNLivePlayerStatus)state; | ||
| 52 | + | ||
| 53 | +/** | ||
| 54 | + 告知代理对象播放器因错误停止播放 | ||
| 55 | + | ||
| 56 | + @param player 调用该方法的 CNLivePlayer 对象 | ||
| 57 | + @param error 携带播放器停止播放错误信息的 NSError 对象 code -1 鉴权失败 -2 获取url失败 | ||
| 58 | + | ||
| 59 | + @since v1.0.0 | ||
| 60 | + */ | ||
| 61 | +- (void)player:(nonnull CNLivePlayer *)player stoppedWithError:(nullable NSError *)error; | ||
| 62 | + | ||
| 63 | +/** | ||
| 64 | + 点播已缓冲区域 | ||
| 65 | + | ||
| 66 | + @param player 调用该方法的 CNLivePlayer 对象 | ||
| 67 | + @param timeRange CMTime , 表示从0时开始至当前缓冲区域,单位秒。 | ||
| 68 | + | ||
| 69 | + @warning 仅对点播有效 | ||
| 70 | + | ||
| 71 | + @since v2.4.1 | ||
| 72 | + */ | ||
| 73 | +- (void)player:(nonnull CNLivePlayer *)player loadedTimeRange:(CMTime)timeRange; | ||
| 74 | + | ||
| 75 | +/** | ||
| 76 | + 回调将要渲染的帧数据 | ||
| 77 | + 该功能只支持直播 | ||
| 78 | + | ||
| 79 | + @param player 调用该方法的 CNLivePlayer 对象 | ||
| 80 | + @param frame 将要渲染帧 YUV 数据。 | ||
| 81 | + CVPixelBufferGetPixelFormatType 获取 YUV 的类型。 | ||
| 82 | + 软解为 kCVPixelFormatType_420YpCbCr8Planar. | ||
| 83 | + 硬解为 kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange. | ||
| 84 | + @param pts 显示时间戳 单位ms | ||
| 85 | + @param sarNumerator | ||
| 86 | + @param sarDenominator | ||
| 87 | + 其中sar 表示 storage aspect ratio | ||
| 88 | + 视频流的显示比例 sarNumerator sarDenominator | ||
| 89 | + @discussion sarNumerator = 0 表示该参数无效 | ||
| 90 | + | ||
| 91 | + @since v2.4.3 | ||
| 92 | + */ | ||
| 93 | +- (void)player:(nonnull CNLivePlayer *)player willRenderFrame:(nullable CVPixelBufferRef)frame pts:(int64_t)pts sarNumerator:(int)sarNumerator sarDenominator:(int)sarDenominator; | ||
| 94 | + | ||
| 95 | +/** | ||
| 96 | + 回调音频数据 | ||
| 97 | + | ||
| 98 | + @param player 调用该方法的 CNLivePlayer 对象 | ||
| 99 | + @param audioBufferList 音频数据 | ||
| 100 | + @param audioStreamDescription 音频格式信息 | ||
| 101 | + @param pts 显示时间戳 是解码器进行显示帧时相对于SCR(系统参考)的时间戳。SCR可以理解为解码器应该开始从磁盘读取数据时的时间 | ||
| 102 | + @param sampleFormat 采样位数 枚举:CNLivePlayerAVSampleFormat | ||
| 103 | + @return audioBufferList 音频数据 | ||
| 104 | + | ||
| 105 | + @since v2.4.3 | ||
| 106 | + */ | ||
| 107 | +- (nonnull AudioBufferList *)player:(nonnull CNLivePlayer *)player willAudioRenderBuffer:(nonnull AudioBufferList *)audioBufferList asbd:(AudioStreamBasicDescription)audioStreamDescription pts:(int64_t)pts sampleFormat:(CNLivePlayerAVSampleFormat)sampleFormat; | ||
| 108 | + | ||
| 109 | +/** | ||
| 110 | + 回调 SEI 数据 | ||
| 111 | + | ||
| 112 | + @param player 调用该方法的 CNLivePlayer 对象 | ||
| 113 | + @param SEIData SEI数据 | ||
| 114 | + @param ts 含有SEI数据的视频帧对应的时间戳 | ||
| 115 | + @since v3.4.0 | ||
| 116 | + */ | ||
| 117 | +- (void)player:(nonnull CNLivePlayer *)player SEIData:(nullable NSData *)SEIData ts:(int64_t)ts; | ||
| 118 | + | ||
| 119 | +/** | ||
| 120 | + 音视频渲染首帧回调通知 | ||
| 121 | + | ||
| 122 | + @param player 调用该方法的 CNLivePlayer 对象 | ||
| 123 | + @param firstRenderType 音视频首帧回调通知类型 | ||
| 124 | + | ||
| 125 | + @since v3.2.1 | ||
| 126 | + */ | ||
| 127 | +- (void)player:(nonnull CNLivePlayer *)player firstRender:(CNLivePlayerFirstRenderType)firstRenderType; | ||
| 128 | + | ||
| 129 | +/** | ||
| 130 | + 视频宽高数据回调通知 | ||
| 131 | + | ||
| 132 | + @param player 调用该方法的 CNLivePlayer 对象 | ||
| 133 | + @param width 视频流宽 | ||
| 134 | + @param height 视频流高 | ||
| 135 | + | ||
| 136 | + @since v3.3.0 | ||
| 137 | + */ | ||
| 138 | +- (void)player:(nonnull CNLivePlayer *)player width:(int)width height:(int)height; | ||
| 139 | + | ||
| 140 | +/** | ||
| 141 | + seekTo 完成的回调通知 | ||
| 142 | + | ||
| 143 | + @param player 调用该方法的 CNLivePlayer 对象 | ||
| 144 | + | ||
| 145 | + @since v3.3.0 | ||
| 146 | + */ | ||
| 147 | +- (void)player:(nonnull CNLivePlayer *)player seekToCompleted:(BOOL)isCompleted; | ||
| 148 | + | ||
| 149 | +/** | ||
| 150 | + switchRate 完成的回调通知 | ||
| 151 | + | ||
| 152 | + @param player 调用该方法的 CNLivePlayer 对象 | ||
| 153 | + | ||
| 154 | + @since v3.3.0 | ||
| 155 | + */ | ||
| 156 | +- (void)player:(nonnull CNLivePlayer *)player switchRateCompleted:(BOOL)isCompleted; | ||
| 157 | + | ||
| 158 | +@end | ||
| 159 | + | ||
| 160 | +NS_ASSUME_NONNULL_END |
CNLivePlayer/Classes/Header/CNLivePlayerHeader.h
0 → 100644
| 1 | +// | ||
| 2 | +// CNLivePlayerSDK-PrefixHeader.pch | ||
| 3 | +// CNLivePlayer_Example | ||
| 4 | +// | ||
| 5 | +// Created by CNLive-zxw on 2019/5/25. | ||
| 6 | +// Copyright © 2016年 CNLive. All rights reserved. | ||
| 7 | +// | ||
| 8 | + | ||
| 9 | +#ifndef CNLivePlayerHeader_h | ||
| 10 | +#define CNLivePlayerHeader_h | ||
| 11 | + | ||
| 12 | +#ifdef __OBJC__ | ||
| 13 | + | ||
| 14 | +#import <Foundation/Foundation.h> | ||
| 15 | +#import <UIKit/UIKit.h> | ||
| 16 | + | ||
| 17 | +#import "CNLivePlayer.h" | ||
| 18 | +#import "CNLivePlayerManager.h" | ||
| 19 | +#import "CNLivePlayerDelegate.h" | ||
| 20 | +#import "CNLivePlayerTypeDef.h" | ||
| 21 | + | ||
| 22 | +#endif | ||
| 23 | + | ||
| 24 | +#endif /* CNLivePlayerHeader_pch */ |
CNLivePlayer/Classes/Header/CNLivePlayerTypeDef.h
0 → 100644
| 1 | +// | ||
| 2 | +// CNLivePlayerTypeDef.h | ||
| 3 | +// CNLivePlayer_Example | ||
| 4 | +// | ||
| 5 | +// Created by CNLive-zCNLive on 2019/5/25. | ||
| 6 | +// Copyright © 2019 CNLive. All rights reserved. | ||
| 7 | +// | ||
| 8 | + | ||
| 9 | +#ifndef CNLivePlayerTypeDef_h | ||
| 10 | +#define CNLivePlayerTypeDef_h | ||
| 11 | + | ||
| 12 | +/** | ||
| 13 | + @brief 音频采样格式 | ||
| 14 | + | ||
| 15 | + @since 2.4.3 | ||
| 16 | + */ | ||
| 17 | +typedef NS_ENUM(NSInteger, CNLivePlayerAVSampleFormat) { | ||
| 18 | + CNLivePlayerAV_SAMPLE_FMT_NONE = -1, | ||
| 19 | + CNLivePlayerAV_SAMPLE_FMT_U8, ///< unsigned 8 bits | ||
| 20 | + CNLivePlayerAV_SAMPLE_FMT_S16, ///< signed 16 bits | ||
| 21 | + CNLivePlayerAV_SAMPLE_FMT_S32, ///< signed 32 bits | ||
| 22 | + CNLivePlayerAV_SAMPLE_FMT_FLT, ///< float | ||
| 23 | + CNLivePlayerAV_SAMPLE_FMT_DBL, ///< double | ||
| 24 | + | ||
| 25 | + CNLivePlayerAV_SAMPLE_FMT_U8P, ///< unsigned 8 bits, planar | ||
| 26 | + CNLivePlayerAV_SAMPLE_FMT_S16P, ///< signed 16 bits, planar | ||
| 27 | + CNLivePlayerAV_SAMPLE_FMT_S32P, ///< signed 32 bits, planar | ||
| 28 | + CNLivePlayerAV_SAMPLE_FMT_FLTP, ///< float, planar | ||
| 29 | + CNLivePlayerAV_SAMPLE_FMT_DBLP, ///< double, planar | ||
| 30 | + | ||
| 31 | + CNLivePlayerAV_SAMPLE_FMT_NB ///< Number of sample formats. DO NOT USE if linking dynamically | ||
| 32 | +}; | ||
| 33 | + | ||
| 34 | +/** | ||
| 35 | + @brief 播放画面旋转模式 | ||
| 36 | + | ||
| 37 | + @since v2.3.0 | ||
| 38 | + */ | ||
| 39 | + | ||
| 40 | +typedef NS_ENUM(NSInteger, CNLivePlayerRotationsMode) { | ||
| 41 | + CNLivePlayerNoRotation, // 无旋转 | ||
| 42 | + CNLivePlayerRotateLeft, // 向左旋 | ||
| 43 | + CNLivePlayerRotateRight, // 向右旋 | ||
| 44 | + CNLivePlayerFlipVertical, // 垂直翻转 | ||
| 45 | + CNLivePlayerFlipHorizonal, // 水平翻转 | ||
| 46 | + CNLivePlayerRotate180 // 旋转 180 度 | ||
| 47 | +}; | ||
| 48 | + | ||
| 49 | +/** | ||
| 50 | + CNLivePlayer 的播放状态 | ||
| 51 | + | ||
| 52 | + @since v1.0.0 | ||
| 53 | + */ | ||
| 54 | +typedef NS_ENUM(NSInteger, CNLivePlayerStatus) { | ||
| 55 | + | ||
| 56 | + /** | ||
| 57 | + CNLivePlayer 未知状态,只会作为 init 后的初始状态,开始播放之后任何情况下都不会再回到此状态。 | ||
| 58 | + @since v1.0.0 | ||
| 59 | + */ | ||
| 60 | + CNLivePlayerStatusUnknow = 0, | ||
| 61 | + | ||
| 62 | + /** | ||
| 63 | + CNLivePlayer 正在准备播放所需组件,在调用 -play 方法时出现。 | ||
| 64 | + | ||
| 65 | + @since v1.0.0 | ||
| 66 | + */ | ||
| 67 | + CNLivePlayerStatusPreparing, | ||
| 68 | + | ||
| 69 | + /** | ||
| 70 | + CNLivePlayer 播放组件准备完成,准备开始播放,在调用 -play 方法时出现。 | ||
| 71 | + | ||
| 72 | + @since v1.0.0 | ||
| 73 | + */ | ||
| 74 | + CNLivePlayerStatusReady, | ||
| 75 | + | ||
| 76 | + /** | ||
| 77 | + CNLivePlayer 播放组件准备完成,准备开始连接 | ||
| 78 | + | ||
| 79 | + @warning 请勿在此状态时,调用 playWithURL 切换 URL 操作 | ||
| 80 | + | ||
| 81 | + @since v3.2.1 | ||
| 82 | + */ | ||
| 83 | + CNLivePlayerStatusOpen, | ||
| 84 | + | ||
| 85 | + /** | ||
| 86 | + @abstract CNLivePlayer 缓存数据为空状态。 | ||
| 87 | + | ||
| 88 | + @discussion 特别需要注意的是当推流端停止推流之后,CNLivePlayer 将出现 caching 状态直到 timeout 后抛出 timeout 的 error 而不是出现 CNLivePlayerStatusStopped 状态,因此在直播场景中,当流停止之后一般做法是使用 IM 服务告知播放器停止播放,以达到即时响应主播断流的目的。 | ||
| 89 | + | ||
| 90 | + @since v1.0.0 | ||
| 91 | + */ | ||
| 92 | + CNLivePlayerStatusCaching, | ||
| 93 | + | ||
| 94 | + /** | ||
| 95 | + CNLivePlayer 正在播放状态。 | ||
| 96 | + | ||
| 97 | + @since v1.0.0 | ||
| 98 | + */ | ||
| 99 | + CNLivePlayerStatusPlaying, | ||
| 100 | + | ||
| 101 | + /** | ||
| 102 | + CNLivePlayer 暂停状态。 | ||
| 103 | + | ||
| 104 | + @since v1.0.0 | ||
| 105 | + */ | ||
| 106 | + CNLivePlayerStatusPaused, | ||
| 107 | + | ||
| 108 | + /** | ||
| 109 | + @abstract CNLivePlayer 停止状态 | ||
| 110 | + @discussion 该状态仅会在回放时播放结束出现,RTMP 直播结束并不会出现此状态 | ||
| 111 | + | ||
| 112 | + @since v1.0.0 | ||
| 113 | + */ | ||
| 114 | + CNLivePlayerStatusStopped, | ||
| 115 | + | ||
| 116 | + /** | ||
| 117 | + CNLivePlayer 错误状态,播放出现错误时会出现此状态。 | ||
| 118 | + | ||
| 119 | + @since v1.0.0 | ||
| 120 | + */ | ||
| 121 | + CNLivePlayerStatusError, | ||
| 122 | + | ||
| 123 | + /** | ||
| 124 | + * CNLivePlayer 自动重连的状态 | ||
| 125 | + */ | ||
| 126 | + CNLivePlayerStateAutoReconnecting, | ||
| 127 | + | ||
| 128 | + /** | ||
| 129 | + * CNLivePlayer 播放完成(该状态只针对点播有效) | ||
| 130 | + */ | ||
| 131 | + CNLivePlayerStatusCompleted, | ||
| 132 | + | ||
| 133 | +}; | ||
| 134 | + | ||
| 135 | +/** | ||
| 136 | + @brief 播放器音视频首帧数据类型 | ||
| 137 | + | ||
| 138 | + @since v3.2.1 | ||
| 139 | + */ | ||
| 140 | + | ||
| 141 | +typedef NS_ENUM(NSInteger, CNLivePlayerFirstRenderType) { | ||
| 142 | + CNLivePlayerFirstRenderTypeVideo = 0, // 视频 | ||
| 143 | + CNLivePlayerFirstRenderTypeAudio // 音频 | ||
| 144 | +}; | ||
| 145 | + | ||
| 146 | +/** | ||
| 147 | + @brief 播放器数据类型 | ||
| 148 | + | ||
| 149 | + @since v3.2.1 | ||
| 150 | + */ | ||
| 151 | + | ||
| 152 | +typedef NS_ENUM(NSInteger, CNLivePlayerDataType) { | ||
| 153 | + CNLivePlayerDataTypeVod = 0, // 点播 | ||
| 154 | + CNLivePlayerDataTypeLive, // 直播 | ||
| 155 | + CNLivePlayerDataTypeAudio // 音频 | ||
| 156 | +}; | ||
| 157 | + | ||
| 158 | +/** | ||
| 159 | + 视频清晰度 | ||
| 160 | + */ | ||
| 161 | +typedef enum : NSUInteger { | ||
| 162 | + /** | ||
| 163 | + * 蓝光 | ||
| 164 | + */ | ||
| 165 | + kCNLiveClarity_1080P, | ||
| 166 | + /** | ||
| 167 | + * 超清 | ||
| 168 | + */ | ||
| 169 | + kCNLiveClarity_720P, | ||
| 170 | + /** | ||
| 171 | + * 高清 | ||
| 172 | + */ | ||
| 173 | + kCNLiveClarity_480P, | ||
| 174 | + /** | ||
| 175 | + * 流畅 | ||
| 176 | + */ | ||
| 177 | + kCNLiveClarity_360P, | ||
| 178 | + | ||
| 179 | +} CNLiveClarityLevel; | ||
| 180 | + | ||
| 181 | +#endif /* CNLivePlayerTypeDef_h */ |
CNLivePlayer/Classes/Header/CNLivePlayerUrl.h
0 → 100644
| 1 | +// | ||
| 2 | +// CNLivePlayerUrl.h | ||
| 3 | +// CNLivePlayer_Example | ||
| 4 | +// | ||
| 5 | +// Created by CNLive-zxw on 2019/5/25. | ||
| 6 | +// Copyright © 2016年 CNLive. All rights reserved. | ||
| 7 | +// | ||
| 8 | + | ||
| 9 | +#ifndef CNLivePlayerUrl_h | ||
| 10 | +#define CNLivePlayerUrl_h | ||
| 11 | + | ||
| 12 | +#define HTTPorHTTPs @"http://" | ||
| 13 | + | ||
| 14 | +#pragma mark - 推流 | ||
| 15 | +//创建直播活动 | ||
| 16 | +#define StreamerURL [NSString stringWithFormat:@"%@/live_epg/createActivity", [CNLivePlayerManager manager].isTestEnvironment ? HTTPorHTTPs@"test.open.cnlive.com/openapi/api2" : HTTPorHTTPs@"api.cnlive.com/open/api2"] | ||
| 17 | + | ||
| 18 | +//启动直播活动 | ||
| 19 | +#define startStreamerURL [NSString stringWithFormat:@"%@/live_epg/startActivity", [CNLivePlayerManager manager].isTestEnvironment ? HTTPorHTTPs@"test.open.cnlive.com/openapi/api2" : HTTPorHTTPs@"api.cnlive.com/open/api2"] | ||
| 20 | + | ||
| 21 | +//停止直播活动 | ||
| 22 | +#define stopStreamerURL [NSString stringWithFormat:@"%@/live_epg/stopActivity", [CNLivePlayerManager manager].isTestEnvironment ? HTTPorHTTPs@"test.open.cnlive.com/openapi/api2" : HTTPorHTTPs@"api.cnlive.com/open/api2"] | ||
| 23 | + | ||
| 24 | +#pragma mark - 播放 | ||
| 25 | +//鉴权 | ||
| 26 | +#define authUrl [NSString stringWithFormat:@"%@/platform/valid", [CNLivePlayerManager manager].isTestEnvironment ? HTTPorHTTPs@"test.open.cnlive.com/openapi/api2" : HTTPorHTTPs@"api.cnlive.com/open/api2"] | ||
| 27 | + | ||
| 28 | +//查看活动状态 | ||
| 29 | +#define liveInfoUrl [NSString stringWithFormat:@"%@/live_epg/live_epg/getLiveActivityPlayInfo4Server", [CNLivePlayerManager manager].isTestEnvironment ? HTTPorHTTPs@"test.open.cnlive.com/openapi/api2" : HTTPorHTTPs@"api.cnlive.com/open/api2"] | ||
| 30 | + | ||
| 31 | +//点播 | ||
| 32 | +#define vodUrl [NSString stringWithFormat:@"%@/vod_ips/vodplayByAPP", [CNLivePlayerManager manager].isTestEnvironment ? HTTPorHTTPs@"test.open.cnlive.com/openapi/api2" : HTTPorHTTPs@"api.cnlive.com/open/api2"] | ||
| 33 | + | ||
| 34 | +//直播 | ||
| 35 | +#define liveUrl [NSString stringWithFormat:@"%@/live_ips/liveplayByAPP", [CNLivePlayerManager manager].isTestEnvironment ? HTTPorHTTPs@"test.open.cnlive.com/openapi/api2" : HTTPorHTTPs@"api.cnlive.com/open/api2"] | ||
| 36 | + | ||
| 37 | +#define vodInfoUrl [NSString stringWithFormat:@"%@/vod_epg/getVodInfo4App", [CNLivePlayerManager manager].isTestEnvironment ? HTTPorHTTPs@"test.open.cnlive.com/openapi/api2" : HTTPorHTTPs@"api.cnlive.com/open/api2"] | ||
| 38 | + | ||
| 39 | + | ||
| 40 | +#pragma mark - 探针 | ||
| 41 | +#define StatUrl [NSString stringWithFormat:@"%@", [CNLivePlayerManager manager].isTestEnvironment ? HTTPorHTTPs@"app.sta.cnlive.com/app.jpg" : HTTPorHTTPs@"app.sta.cnlive.com/app.jpg"] | ||
| 42 | + | ||
| 43 | +#endif /* CNLivePlayerUrl_h */ |
CNLivePlayer/Classes/Player/CNLivePlayer.h
| @@ -2,316 +2,162 @@ | @@ -2,316 +2,162 @@ | ||
| 2 | // CNLivePlayer.h | 2 | // CNLivePlayer.h |
| 3 | // CNLivePlayer_Example | 3 | // CNLivePlayer_Example |
| 4 | // | 4 | // |
| 5 | -// Created by CNLive-zxw on 2019/7/26. | ||
| 6 | -// Copyright © 2019 153993236@qq.com. All rights reserved. | 5 | +// Created by CNLive-zxw on 2019/5/25. |
| 6 | +// Copyright © 2019 CNLive. All rights reserved. | ||
| 7 | // | 7 | // |
| 8 | 8 | ||
| 9 | -#import <Foundation/Foundation.h> | ||
| 10 | -#import <libksygpulive/KSYMoviePlayerController.h> | ||
| 11 | -#import "KSYMoviePlayerDefines.h" | ||
| 12 | - | ||
| 13 | -// 枚举定义 | ||
| 14 | -#import "CNLivePlayerDefines.h" | ||
| 15 | - | ||
| 16 | -// 播放器管理类 | ||
| 17 | -#import "CNLivePlayerManager.h" | ||
| 18 | - | ||
| 19 | -// 缓存url | ||
| 20 | -#import "CNLivePlayerDBTool.h" | 9 | +/** |
| 10 | + * 播放器 | ||
| 11 | + */ | ||
| 21 | 12 | ||
| 22 | -// 缓存视频 | ||
| 23 | -#import "CNLiveHTTPProxyService.h" | 13 | +#import <UIKit/UIKit.h> |
| 14 | +#import <PLPlayerKit/PLPlayerKit.h> // 播放器 | ||
| 15 | +#import "CNLivePlayerTypeDef.h" // 枚举类型 | ||
| 16 | +#import "CNLivePlayerDelegate.h" // 协议 | ||
| 24 | 17 | ||
| 25 | NS_ASSUME_NONNULL_BEGIN | 18 | NS_ASSUME_NONNULL_BEGIN |
| 26 | 19 | ||
| 27 | -/* | ||
| 28 | - * 获取主播状态通知 | ||
| 29 | - */ | ||
| 30 | -UIKIT_EXTERN NSString *const CNLiveHostStatusChangedNotification; | ||
| 31 | - | ||
| 32 | -/* | ||
| 33 | - * 成功回调 | ||
| 34 | - */ | ||
| 35 | -typedef void(^AuthSuccessBlock)(void); | ||
| 36 | - | ||
| 37 | -/* | ||
| 38 | - * 失败回调 | ||
| 39 | - */ | ||
| 40 | -typedef void(^AuthFailureBlock)(NSDictionary *errorInfo); | 20 | +typedef void (^SuccessBlock) (void); |
| 21 | +typedef void (^FailureBlock) (NSError *error); | ||
| 41 | 22 | ||
| 42 | @interface CNLivePlayer : NSObject | 23 | @interface CNLivePlayer : NSObject |
| 43 | 24 | ||
| 44 | -/* | ||
| 45 | - * 播放器类型 | ||
| 46 | - */ | ||
| 47 | -@property (nonatomic, assign, readonly) CNLivePlayerType type; | ||
| 48 | - | ||
| 49 | -/* | ||
| 50 | - * 播放器 | ||
| 51 | - */ | ||
| 52 | -@property (nonatomic, strong) KSYMoviePlayerController *player; | ||
| 53 | - | ||
| 54 | -#pragma mark - 初始化方法 | ||
| 55 | -/* | ||
| 56 | - * 初始化播放器并设置播放地址 | ||
| 57 | - * | ||
| 58 | - * url 视频url | ||
| 59 | - * | ||
| 60 | - */ | ||
| 61 | -- (instancetype)initWithContentURL:(NSString *)url; | 25 | +- (instancetype)initWithUrl:(NSString *)url; |
| 62 | 26 | ||
| 27 | +#pragma mark - 视频 | ||
| 28 | +#pragma mark - 使用视讯云平台 直接通过节目id播放 | ||
| 29 | +/** | ||
| 30 | + 初始化点播播放器 初始化成功后才能对播放器进行设置 | ||
| 63 | 31 | ||
| 64 | -/* 初始化点播播放器 初始化成功后才能对播放器进行设置(如:自动播放、是否开启硬件解码等)和调用准备播放视频方法(prepareToPlay) | ||
| 65 | - * 码率默认2-标清 | ||
| 66 | - * | ||
| 67 | - * videoId 视频Id | ||
| 68 | - * authSuccess 初始化成功block | ||
| 69 | - * authFailed 初始化失败block 回调一个errorInfo的字典 | ||
| 70 | - * | ||
| 71 | - */ | ||
| 72 | -- (instancetype)initVodPlayerWithVideoId:(NSString *)videoId authSuccess:(AuthSuccessBlock)authSuccessBlock authFailure:(AuthFailureBlock)authFailureBlock; | ||
| 73 | - | ||
| 74 | - | ||
| 75 | -/* 初始化直播播放器(* 以channelId播放) 初始化成功后才能对播放器进行设置(如:自动播放、是否开启硬件解码等)和调用准备播放视频方法(prepareToPlay) | ||
| 76 | - * | ||
| 77 | - * channelId 直播视频Id (注* 若agree为YES时,必传,用于获取主播状态; 若agree为NO,可不传) | ||
| 78 | - * activityId 活动Id | ||
| 79 | - * agree 是否允许获取主播状态 YES:获取 NO:不获取 | ||
| 80 | - * authSuccess 初始化成功block | ||
| 81 | - * authFailed 初始化失败block 回调一个errorInfo的字典 | ||
| 82 | - * | ||
| 83 | - * result 获取主播状态 YES:获取状态成功 | ||
| 84 | - * NO:获取失败,不影响视频播放,但是获取不到主播离开、回来、直播结束等通知。 | ||
| 85 | - * 可通过- (void)getHostStatus:(void(^)(BOOL result))result;方法重新获取 | ||
| 86 | - */ | ||
| 87 | -- (instancetype)initLivePlayerWithChannelId:(NSString *)channelId activityId:(NSString *)activityId hostStatus:(BOOL)agree authSuccess:(AuthSuccessBlock)authSuccessBlock authFailure:(AuthFailureBlock)authFailureBlock getHostStatusResult:(void(^)(BOOL result))result; | ||
| 88 | - | ||
| 89 | - | ||
| 90 | -/* 初始化音频播放器 初始化成功后才能对播放器进行设置(如:自动播放、是否开启硬件解码等)和调用准备播放视频方法(prepareToPlay) | ||
| 91 | - * | ||
| 92 | - * audioId 音频Id | ||
| 93 | - * authSuccess 初始化成功block | ||
| 94 | - * authFailed 初始化失败block 回调一个errorInfo的字典 | ||
| 95 | - * | ||
| 96 | - */ | ||
| 97 | -- (instancetype)initAudioPlayerWithAudioId:(NSString *)audioId authSuccess:(AuthSuccessBlock)authSuccessBlock authFailure:(AuthFailureBlock)authFailureBlock; | ||
| 98 | - | 32 | + @param videoId 视频Id |
| 33 | + @param success 初始化成功block | ||
| 34 | + @param failure 初始化失败block 回调一个NSError的对象 | ||
| 99 | 35 | ||
| 100 | -#pragma mark - 属性 | ||
| 101 | -/** | ||
| 102 | - @abstract 正在播放的视频文件的地址,该地址可以是本地地址或者服务器地址。 | 36 | + @return 返回CNLivePlayer对象 |
| 37 | + | ||
| 38 | + @since v0.0.1 | ||
| 103 | */ | 39 | */ |
| 104 | -@property (nonatomic, readonly) NSURL *contentURL; | 40 | +- (instancetype)initVodPlayWithVideoId:(NSString *)videoId success:(SuccessBlock _Nullable)success failure:(FailureBlock _Nullable)failure; |
| 41 | +- (instancetype)initVodPlayWithVideoId:(NSString *)videoId; | ||
| 105 | 42 | ||
| 106 | /** | 43 | /** |
| 107 | - @abstract 包含视频播放内容的VIEW(只读)。 | ||
| 108 | - @discussion view的使用逻辑: | ||
| 109 | - | ||
| 110 | - * 可以通过frame设置view大大小 | ||
| 111 | - * 使用[scalingMode]([KSYMoviePlayerController scalingMode]) 可以更改视频内容在VIEW中的显示情况 | ||
| 112 | - | ||
| 113 | - @see scalingMode | ||
| 114 | - */ | ||
| 115 | -// The view in which the media and playback controls are displayed. | ||
| 116 | -@property (nonatomic, strong, readonly) UIView *view; | 44 | + 初始化直播播放器 |
| 117 | 45 | ||
| 118 | -/* | ||
| 119 | - * 主播直播状态(只读) | ||
| 120 | - */ | ||
| 121 | -@property (nonatomic, assign, readonly) CNLiveHostStatus hostStatus; | 46 | + @param videoId 视频Id |
| 47 | + @param activityId 活动Id | ||
| 48 | + @param success 初始化成功block | ||
| 49 | + @param failure 初始化失败block 回调一个NSError的对象 | ||
| 122 | 50 | ||
| 123 | -/* 解码方式(硬解/软解/自动/使用系统接口进行解码和渲染) | ||
| 124 | - * | ||
| 125 | - * 建议系统版本高于8.0 选择硬解,或者自动选择解码方式,8.0以上的系统优先选择硬解 | ||
| 126 | - * 只在[prepareToPlay]调用前设置生效 | ||
| 127 | - * | 51 | + @return 返回CNLivePlayer对象 |
| 52 | + | ||
| 53 | + @since v0.0.1 | ||
| 128 | */ | 54 | */ |
| 129 | -@property(nonatomic) CNLiveMovieVideoDecoderMode videoDecoderMode; | 55 | +- (instancetype)initLivePlayWithVideoId:(NSString *)videoId activityId:(NSString *)activityId success:(SuccessBlock _Nullable)success failure:(FailureBlock _Nullable)failure; |
| 56 | +- (instancetype)initLivePlayWithVideoId:(NSString *)videoId activityId:(NSString *)activityId; | ||
| 130 | 57 | ||
| 58 | +#pragma mark - 切换视频 | ||
| 131 | /** | 59 | /** |
| 132 | - @abstract 是否静音 | ||
| 133 | - @discussion | ||
| 134 | - * 默认不静音 | ||
| 135 | - * [prepareToPlay]方法前设置即生效,也可以在播放过程中动态切换 | ||
| 136 | - */ | ||
| 137 | -@property (nonatomic) BOOL shouldMute; | 60 | +切换视频 |
| 138 | 61 | ||
| 139 | -/** | ||
| 140 | - @abstract 是否循环播放 | ||
| 141 | - @discussion 默认不循环 | ||
| 142 | - * 只在[prepareToPlay]调用前设置生效; | ||
| 143 | - * 只有点播生效,直播场景请勿设置 | ||
| 144 | - */ | ||
| 145 | -@property (nonatomic) BOOL shouldLoop; | 62 | +@param videoId 视频id |
| 63 | +@param type 视频类型 vod 点播视频 live 直播视频 | ||
| 146 | 64 | ||
| 147 | -/** | ||
| 148 | - @abstract 指定逆时针旋转角度,只能是0/90/180/270, 不符合上述值不进行旋转 | ||
| 149 | - */ | ||
| 150 | -@property (nonatomic, assign) int rotateDegress; | 65 | +@since v0.0.1 |
| 66 | +*/ | ||
| 67 | +- (void)switchVideo:(NSString *)videoId type:(CNLivePlayerDataType)type; | ||
| 151 | 68 | ||
| 69 | +#pragma mark - 切换码率 | ||
| 152 | /** | 70 | /** |
| 153 | - @abstract 是否打断其他后台的音乐播放 | ||
| 154 | - @discussion 也可以理解为是否允许和其他音频同时播放 | ||
| 155 | - @discussion YES:开始播放时,会打断其他的后台播放音频,也会被其他音频播放打断 | ||
| 156 | - @discussion NO: 可以与其他后台播放共存,相互之间不会被打断 | ||
| 157 | - @discussion 默认为YES | ||
| 158 | - */ | ||
| 159 | -@property (nonatomic) BOOL bInterruptOtherAudio; | 71 | +切换码率 |
| 160 | 72 | ||
| 161 | -/* | ||
| 162 | - * 查询视频准备是否完成(只读) | ||
| 163 | - */ | ||
| 164 | -@property (nonatomic, readonly) BOOL isPreparedToPlay; | 73 | +@param level 视频清晰度 |
| 74 | + | ||
| 75 | +@since v0.0.1 | ||
| 76 | +*/ | ||
| 77 | +- (void)switchRate:(CNLiveClarityLevel)level; | ||
| 165 | 78 | ||
| 166 | /* | 79 | /* |
| 167 | - * 当前播放的时间 | ||
| 168 | - */ | ||
| 169 | -@property(nonatomic, assign) NSTimeInterval currentPlaybackTime; | 80 | + 开始播放 |
| 81 | + @return 是否成功播放 | ||
| 170 | 82 | ||
| 171 | -/** | ||
| 172 | - * 当前视频总时长 | ||
| 173 | - * 视频总时长,单位是秒。 | ||
| 174 | - * 如果是直播视频源,总时长为0. | ||
| 175 | - * 如果该信息未知,总时长默认为0. | 83 | + @since v0.0.1 |
| 176 | */ | 84 | */ |
| 177 | -@property (nonatomic, readonly) NSTimeInterval duration; | 85 | +- (BOOL)play; |
| 178 | 86 | ||
| 179 | -/** | ||
| 180 | - @abstract 当前视频宽高 | ||
| 181 | - @discussion 获取信息 | ||
| 182 | - | ||
| 183 | - * 监听MPMovieNaturalSizeAvailableNotification | ||
| 184 | - * 播放过程中,宽高信息可能会产生更改 | 87 | +/* |
| 88 | + 暂停播放当前视频 | ||
| 185 | 89 | ||
| 186 | - @since Available in KSYMoviePlayerController 1.0 and later. | 90 | + @since v0.0.1 |
| 187 | */ | 91 | */ |
| 188 | -@property (nonatomic, readonly) CGSize naturalSize; | 92 | +- (void)pause; |
| 189 | 93 | ||
| 190 | -/** | ||
| 191 | - @abstract 当前视频自带旋转(逆时针)角度 | ||
| 192 | - @discussion rotateDegress 是人为旋转角度,naturalRotate是文件meta信息中自带的旋转角度 | ||
| 193 | - @warning 该方法由金山云引入,不是原生系统接口 | ||
| 194 | - @since Available in KSYMoviePlayerController 2.2.0 and later. | 94 | +/* |
| 95 | + 继续播放当前视频 | ||
| 96 | + | ||
| 97 | + @since v0.0.1 | ||
| 195 | */ | 98 | */ |
| 196 | -@property (nonatomic, readonly) NSInteger naturalRotate; | 99 | +- (void)resume; |
| 197 | 100 | ||
| 198 | -/** | ||
| 199 | - @abstract 当前播放器的播放状态(只读)。 | ||
| 200 | - @discussion 可以通过该属性获取视频播放情况: | ||
| 201 | - | ||
| 202 | - <pre><code> | ||
| 203 | - typedef NS_ENUM(NSInteger, MPMoviePlaybackState) { | ||
| 204 | - MPMoviePlaybackStateStopped, // 播放停止 | ||
| 205 | - MPMoviePlaybackStatePlaying, // 正在播放 | ||
| 206 | - MPMoviePlaybackStatePaused, // 播放暂停 | ||
| 207 | - MPMoviePlaybackStateInterrupted, // 播放被打断 | ||
| 208 | - MPMoviePlaybackStateSeekingForward, // 向前seeking中 | ||
| 209 | - MPMoviePlaybackStateSeekingBackward // 向后seeking中 | ||
| 210 | - } NS_DEPRECATED_IOS(3_2, 9_0); | ||
| 211 | - </code></pre> | ||
| 212 | - @discussion 通知: | ||
| 213 | - | ||
| 214 | - * MPMoviePlayerPlaybackDidFinishNotification,当播放完成时提供通知 | ||
| 215 | - * MPMoviePlayerPlaybackStateDidChangeNotification,当播放状态变化时提供通知 | 101 | +/* |
| 102 | + 结束当前视频的播放 | ||
| 216 | 103 | ||
| 217 | - @since Available in KSYMoviePlayerController 1.0 and later. | 104 | + @since v0.0.1 |
| 218 | */ | 105 | */ |
| 219 | -// Returns the current playback state of the movie player. | ||
| 220 | -@property (nonatomic, readonly) MPMoviePlaybackState playbackState; | 106 | +- (void)stop; |
| 221 | 107 | ||
| 108 | +@property (nonatomic, strong) PLPlayer *player; | ||
| 222 | 109 | ||
| 223 | /** | 110 | /** |
| 224 | - @abstract 当前网络加载情况 | ||
| 225 | - @discussion 可以通过该属性获取视频加载情况: | 111 | + 代理对象,用于告知播放器状态改变或其他行为,对象需实现 CNLivePlayerDelegate 协议 |
| 226 | 112 | ||
| 227 | - <pre><code> | ||
| 228 | - typedef NS_OPTIONS(NSUInteger, MPMovieLoadState) { | ||
| 229 | - MPMovieLoadStateUnknown = 0, // 加载情况未知 | ||
| 230 | - MPMovieLoadStatePlayable = 1 << 0, // 加载完成,可以播放 | ||
| 231 | - MPMovieLoadStatePlaythroughOK = 1 << 1, // 加载完成,如果shouldAutoplay为YES,将自动开始播放 | ||
| 232 | - MPMovieLoadStateStalled = 1 << 2, // 如果视频正在加载中 | ||
| 233 | - } NS_DEPRECATED_IOS(3_2, 9_0); | ||
| 234 | - </code></pre> | ||
| 235 | - @discussion 通知: | ||
| 236 | - | ||
| 237 | - * MPMoviePlayerLoadStateDidChangeNotification,当加载状态变化时提供通知 | ||
| 238 | - | ||
| 239 | - @since Available in KSYMoviePlayerController 1.0 and later. | ||
| 240 | - */ | ||
| 241 | -// Returns the network load state of the movie player. | ||
| 242 | -@property (nonatomic, readonly) MPMovieLoadState loadState; | ||
| 243 | - | ||
| 244 | -#pragma mark - 方法 | ||
| 245 | -/* | ||
| 246 | - * 获取主播状态(离开/已结束) | 113 | + @since v0.0.1 |
| 247 | */ | 114 | */ |
| 248 | -- (void)getHostStatus:(void(^)(BOOL result))result; | ||
| 249 | - | ||
| 250 | -/* 给播放器设置内容id 设置成功后才能播放(如:自动播放、是否开启硬件解码等)和调用准备播放视频方法(prepareToPlay) | ||
| 251 | - * 切换视频 | ||
| 252 | - * contentId 内容Id(直播channelId+activityId) | ||
| 253 | - * type 内容类型 | ||
| 254 | - */ | ||
| 255 | -- (void)setPlayerWithContentId:(NSString *)contentId type:(CNLivePlayerType)type authSuccess:(AuthSuccessBlock)authSuccessBlock authFailure:(AuthFailureBlock)authFailureBlock; | ||
| 256 | - | ||
| 257 | -/* 给点播播放器切换码率设置内容id 设置成功后才能播放(如:自动播放、是否开启硬件解码等)和调用准备播放视频方法(prepareToPlay) | ||
| 258 | - * 切换视频清晰度 | ||
| 259 | - * contentId 内容Id(如果不切换视频,只切换清晰度,可以将contentId=nil) | ||
| 260 | - * rate 码率 1-流畅,2-标清,3-高清,4-超清,默认为2 | ||
| 261 | - */ | ||
| 262 | -- (void)setPlayerWithContentId:(NSString *)contentId rate:(NSString *)rate authSuccess:(AuthSuccessBlock)authSuccessBlock authFailure:(AuthFailureBlock)authFailureBlock; | 115 | +@property (nonatomic, weak, nullable) id<CNLivePlayerDelegate> delegate; |
| 263 | 116 | ||
| 264 | /* | 117 | /* |
| 265 | - * 销毁播放器 | ||
| 266 | - */ | ||
| 267 | -- (void)destroyPlayer; | 118 | +可以通过frame设置view大大小 |
| 268 | 119 | ||
| 269 | -/* | ||
| 270 | - * 准备视频播放 | ||
| 271 | - * MPMediaPlaybackIsPreparedToPlayDidChangeNotification 播放器完成对视频文件的初始化时发送通知 | 120 | +@since v0.0.1 |
| 272 | */ | 121 | */ |
| 273 | -- (void)prepareToPlay; | 122 | +@property(nonatomic, assign) CGRect frame; |
| 274 | 123 | ||
| 275 | /* | 124 | /* |
| 276 | - * 视频播放 | ||
| 277 | - */ | ||
| 278 | -- (void)play; | ||
| 279 | - | ||
| 280 | -/* | ||
| 281 | - * 当前播放器是否在播放 | 125 | + 包含视频播放内容的VIEW(只读) |
| 126 | + 可以通过frame设置view大大小 | ||
| 127 | + | ||
| 128 | + @since v0.0.1 | ||
| 282 | */ | 129 | */ |
| 283 | -- (BOOL)isPlaying; | 130 | +@property (nonatomic, strong, readonly) UIView *view; |
| 284 | 131 | ||
| 285 | /* | 132 | /* |
| 286 | - * 视频暂停 | 133 | + 支持后台播放 |
| 134 | + | ||
| 135 | + @since v0.0.1 | ||
| 287 | */ | 136 | */ |
| 288 | -- (void)pause; | 137 | +@property (nonatomic, assign) BOOL backgroundPlayEnable; |
| 289 | 138 | ||
| 290 | -/* | ||
| 291 | - * 视频停止 | 139 | +/** |
| 140 | + @abstract 需要播放的 URL | ||
| 141 | + | ||
| 142 | + @discussion 目前支持 http(s) (url 以 http:// https:// 开头) 与 rtmp (URL 以 rtmp:// 开头) 协议。 | ||
| 143 | + | ||
| 144 | + @since v1.0.0 | ||
| 292 | */ | 145 | */ |
| 293 | -- (void)stop; | 146 | +@property (nonatomic, copy, nonnull, readonly) NSURL *URL; |
| 294 | 147 | ||
| 295 | -/* | ||
| 296 | - * 视频截图 | 148 | +/** |
| 149 | + CNLivePlayer 的播放状态 | ||
| 150 | + | ||
| 151 | + @since v1.0.0 | ||
| 297 | */ | 152 | */ |
| 298 | -- (UIImage *)thumbnailImageAtCurrentTime; | 153 | +@property (nonatomic, assign, readonly) CNLivePlayerStatus status; |
| 299 | 154 | ||
| 300 | /** | 155 | /** |
| 301 | - @abstract 重新启动拉流 | ||
| 302 | - @param aUrl 视频播放地址,该地址可以是本地地址或者服务器地址.如果为nil,则使用前一次播放地址 | ||
| 303 | - @discussion 调用场景如下: | ||
| 304 | - | ||
| 305 | - * 当播放器调用方发现卡顿时,可以主动调用 | ||
| 306 | - * 当估计出更优质的拉流ip时,可以主动调用 | ||
| 307 | - * 当发生WiFi/3G网络切换时,可以主动调用 | ||
| 308 | - * 当播放器回调体现播放完成时,可以主动调用 | ||
| 309 | - * 播放器SDK不会自动调用reload功能 | 156 | + 指示当前 CNLivePlayer 是否处于正在播放状态 |
| 310 | 157 | ||
| 311 | - @warning 该方法由金山云引入,不是原生系统接口 | ||
| 312 | - @since Available in KSYMoviePlayerController 1.0 and later. | 158 | + @since v1.0.0 |
| 313 | */ | 159 | */ |
| 314 | -- (void)reload:(NSURL *)aUrl; | 160 | +@property (nonatomic, assign, readonly, getter=isPlaying) BOOL playing; |
| 315 | 161 | ||
| 316 | @end | 162 | @end |
| 317 | 163 |
CNLivePlayer/Classes/Player/CNLivePlayer.m
| @@ -2,875 +2,393 @@ | @@ -2,875 +2,393 @@ | ||
| 2 | // CNLivePlayer.m | 2 | // CNLivePlayer.m |
| 3 | // CNLivePlayer_Example | 3 | // CNLivePlayer_Example |
| 4 | // | 4 | // |
| 5 | -// Created by CNLive-zxw on 2019/7/26. | ||
| 6 | -// Copyright © 2019 153993236@qq.com. All rights reserved. | 5 | +// Created by CNLive-zxw on 2019/5/25. |
| 6 | +// Copyright © 2019 CNLive. All rights reserved. | ||
| 7 | // | 7 | // |
| 8 | 8 | ||
| 9 | #import "CNLivePlayer.h" | 9 | #import "CNLivePlayer.h" |
| 10 | -//数据请求 | ||
| 11 | -#import "CNLivePlayerNetworkManager.h" | ||
| 12 | -//工具 | ||
| 13 | -#import "CNLivePlayerTools.h" | ||
| 14 | -//IM | ||
| 15 | -#import <CNLiveMsgTools/CNLiveMsgManager.h> | ||
| 16 | -//url | 10 | +#import "CNLivePlayerManager.h" |
| 11 | +#import "CNLivePlayerDBTool.h" | ||
| 12 | +#import "CNLivePlayerRequest.h" | ||
| 13 | +#import "CommonCrypto/CommonDigest.h" | ||
| 17 | #import "CNLivePlayerUrl.h" | 14 | #import "CNLivePlayerUrl.h" |
| 18 | -//缓存视频 | ||
| 19 | -#import <KSYHTTPCache/HTTPCacheDefines.h> | ||
| 20 | -#import "CNLiveHTTPProxyService.h" | ||
| 21 | -//缓存的key | 15 | + |
| 22 | #define PlayerStringToKey(a, b) [NSString stringWithFormat:@"%@:%@",a,b] | 16 | #define PlayerStringToKey(a, b) [NSString stringWithFormat:@"%@:%@",a,b] |
| 23 | 17 | ||
| 24 | -@interface CNLivePlayer () { | ||
| 25 | - NSString *_idName; | ||
| 26 | - NSString *_contentType; | ||
| 27 | - NSString *_contentId; | ||
| 28 | - | ||
| 29 | - //直播 | ||
| 30 | - NSString *_activityId; | ||
| 31 | - | 18 | +@interface CNLivePlayer ()<PLPlayerDelegate>{ |
| 19 | + NSString * _videoId; | ||
| 20 | + NSString * _activityId; | ||
| 21 | + NSString * _type; | ||
| 32 | // 1-流畅,2-标清,3-高清,4-超清,默认为2 | 22 | // 1-流畅,2-标清,3-高清,4-超清,默认为2 |
| 33 | NSString *_rate; | 23 | NSString *_rate; |
| 34 | BOOL _switchRate; | 24 | BOOL _switchRate; |
| 35 | - NSTimeInterval _switchCurrentTime; | ||
| 36 | - | ||
| 37 | - //音频标识 | ||
| 38 | - BOOL _isAudio; | ||
| 39 | - NSString *_roomId; | ||
| 40 | - NSInteger _retryTime; | ||
| 41 | - | ||
| 42 | - //探针 | ||
| 43 | - NSTimer *_statTimer; | ||
| 44 | - NSInteger _playTime; | ||
| 45 | - | 25 | + CMTime _switchRateTime; |
| 46 | } | 26 | } |
| 27 | +@property (nonatomic, strong) PLPlayerOption *option; | ||
| 28 | +@property (nonatomic, strong) PLPlayer *switchPlayer; | ||
| 47 | @property (nonatomic, strong, readwrite) UIView *view; | 29 | @property (nonatomic, strong, readwrite) UIView *view; |
| 48 | -@property (nonatomic, copy) NSString *videoUrl; | ||
| 49 | -@property (nonatomic, weak) KSYMoviePlayerController *switchPlayer; | ||
| 50 | 30 | ||
| 51 | @end | 31 | @end |
| 52 | 32 | ||
| 53 | @implementation CNLivePlayer | 33 | @implementation CNLivePlayer |
| 54 | -//收到消息通知 | ||
| 55 | -NSString * const CNLivePlayerMsgReceiveNotification = @"CNLiveMsgToolsPlayerReceiveNotification"; | ||
| 56 | -//获取主播状态通知 | ||
| 57 | -NSString * const CNLiveHostStatusChangedNotification = @"CNLiveAnchorStatusChangedNotification"; | 34 | +- (instancetype)initWithUrl:(NSString *)url{ |
| 35 | + self = [super init]; | ||
| 36 | + if (self) { | ||
| 37 | + _switchRate = NO; | ||
| 38 | + NSURL *URL = [NSURL URLWithString:url]; | ||
| 39 | + self.player = [[PLPlayer alloc]initWithURL:URL option:self.option]; | ||
| 40 | + self.player.delegate = self; | ||
| 41 | + [self.view addSubview:self.player.playerView]; | ||
| 42 | + } | ||
| 43 | + return self; | ||
| 44 | +} | ||
| 58 | 45 | ||
| 59 | #pragma mark - 初始化方法 | 46 | #pragma mark - 初始化方法 |
| 60 | -/* | ||
| 61 | - * 初始化播放器并设置播放地址 | ||
| 62 | - * | ||
| 63 | - * url 视频url | ||
| 64 | - * | ||
| 65 | - */ | ||
| 66 | -- (instancetype)initWithContentURL:(NSString *)url { | 47 | +/** |
| 48 | +初始化点播播放器 初始化成功后才能对播放器进行设置 | ||
| 49 | + | ||
| 50 | +@param videoId 视频Id | ||
| 51 | +@param success 初始化成功block | ||
| 52 | +@param failure 初始化失败block 回调一个NSError的对象 | ||
| 53 | + | ||
| 54 | +@return 返回CNLivePlayer对象 | ||
| 55 | + | ||
| 56 | +@since v0.0.1 | ||
| 57 | +*/ | ||
| 58 | +- (instancetype)initVodPlayWithVideoId:(NSString *)videoId success:(SuccessBlock)success failure:(FailureBlock)failure{ | ||
| 67 | self = [super init]; | 59 | self = [super init]; |
| 68 | if (self) { | 60 | if (self) { |
| 69 | - [UIApplication sharedApplication].idleTimerDisabled = YES; | ||
| 70 | - | ||
| 71 | - _contentType = @""; | ||
| 72 | - _contentId = @""; | ||
| 73 | - _idName = @""; | ||
| 74 | - _rate = @"2"; | ||
| 75 | _switchRate = NO; | 61 | _switchRate = NO; |
| 76 | - | ||
| 77 | - [self addMovieNotificationObservers]; | ||
| 78 | - NSString *proxyUrl = [[CNLiveHTTPProxyService manager] getProxyUrl:url]; | ||
| 79 | - self.player = [[KSYMoviePlayerController alloc] initWithContentURL:[NSURL URLWithString:PLAYER_OpenCache?proxyUrl:url]]; | ||
| 80 | - [self.view addSubview:self.player.view]; | ||
| 81 | - _player.view.frame = self.view.bounds; | ||
| 82 | - _player.view.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight; | ||
| 83 | - _player.shouldEnableKSYStatModule = NO; | ||
| 84 | - | 62 | + _videoId = videoId; |
| 63 | + _type = @"vod"; | ||
| 64 | + _rate = @"2"; | ||
| 65 | + [self getCacheURLSuccess:success failure:failure]; | ||
| 85 | } | 66 | } |
| 86 | - | ||
| 87 | return self; | 67 | return self; |
| 88 | } | 68 | } |
| 69 | +- (instancetype)initVodPlayWithVideoId:(NSString *)videoId{ | ||
| 70 | + return [self initVodPlayWithVideoId:videoId success:nil failure:nil]; | ||
| 71 | +} | ||
| 89 | 72 | ||
| 90 | -/* 初始化点播播放器 初始化成功后才能对播放器进行设置(如:自动播放、是否开启硬件解码等)和调用准备播放视频方法(prepareToPlay) | ||
| 91 | - * 码率默认2-标清 | ||
| 92 | - * | ||
| 93 | - * videoId 点播视频Id | ||
| 94 | - * authSuccess 初始化成功block | ||
| 95 | - * authFailure 初始化失败block 回调一个errorInfo的字典 | ||
| 96 | - * | ||
| 97 | - */ | ||
| 98 | -- (instancetype)initVodPlayerWithVideoId:(NSString *)videoId authSuccess:(AuthSuccessBlock)authSuccessBlock authFailure:(AuthFailureBlock)authFailureBlock { | ||
| 99 | - if (self = [super init]) { | ||
| 100 | - if (![videoId isKindOfClass:[NSString class]] || !videoId) { | ||
| 101 | - videoId = videoId ? [NSString stringWithFormat:@"%@",videoId] : @""; | ||
| 102 | - | ||
| 103 | - } | ||
| 104 | - | ||
| 105 | - _type = CNLivePlayerVod; | 73 | +/** |
| 74 | +初始化直播播放器 | ||
| 106 | 75 | ||
| 107 | - _idName = @"vid"; | ||
| 108 | - _contentType = @"vod"; | ||
| 109 | - _contentId = videoId; | ||
| 110 | - _isAudio = NO; | 76 | +@param videoId 视频Id |
| 77 | +@param activityId 活动Id | ||
| 78 | +@param success 初始化成功block | ||
| 79 | +@param failure 初始化失败block 回调一个NSError的对象 | ||
| 111 | 80 | ||
| 112 | - [self createPlayerWithContentId:videoId authSuccess:authSuccessBlock authFailure:authFailureBlock]; | 81 | +@return 返回CNLivePlayer对象 |
| 113 | 82 | ||
| 83 | +@since v0.0.1 | ||
| 84 | +*/ | ||
| 85 | +- (instancetype)initLivePlayWithVideoId:(NSString *)videoId activityId:(NSString *)activityId success:(SuccessBlock)success failure:(FailureBlock)failure{ | ||
| 86 | + self = [super init]; | ||
| 87 | + if (self) { | ||
| 88 | + _switchRate = NO; | ||
| 89 | + _videoId = videoId; | ||
| 90 | + _activityId = activityId; | ||
| 91 | + _type = @"live"; | ||
| 92 | + _rate = @"2"; | ||
| 93 | + [self getCacheURLSuccess:success failure:failure]; | ||
| 114 | } | 94 | } |
| 115 | return self; | 95 | return self; |
| 116 | - | ||
| 117 | } | 96 | } |
| 118 | - | ||
| 119 | -/* 初始化直播播放器(* 以channelId播放) 初始化成功后才能对播放器进行设置(如:自动播放、是否开启硬件解码等)和调用准备播放视频方法(prepareToPlay) | ||
| 120 | - * | ||
| 121 | - * channelId 直播视频Id (注* 若agree为YES时,必传,用于获取主播状态; 若agree为NO,可不传) | ||
| 122 | - * activityId 活动Id | ||
| 123 | - * agree 是否允许获取主播状态 YES:获取 NO:不获取 | ||
| 124 | - * authSuccess 初始化成功block | ||
| 125 | - * authFailure 初始化失败block 回调一个errorInfo的字典 | ||
| 126 | - * | ||
| 127 | - * result 获取主播状态 YES:获取状态成功 | ||
| 128 | - * NO:获取失败,不影响视频播放,但是获取不到主播离开、回来、直播结束等通知。 | ||
| 129 | - * 可通过- (void)getHostStatus:(void(^)(BOOL result))result;方法重新获取 | ||
| 130 | - */ | ||
| 131 | -- (instancetype)initLivePlayerWithChannelId:(NSString *)channelId activityId:(NSString *)activityId hostStatus:(BOOL)agree authSuccess:(AuthSuccessBlock)authSuccessBlock authFailure:(AuthFailureBlock)authFailureBlock getHostStatusResult:(void(^)(BOOL result))result{ | ||
| 132 | - if (self = [super init]) { | ||
| 133 | - if (![channelId isKindOfClass:[NSString class]] || !channelId) { | ||
| 134 | - channelId = channelId ? [NSString stringWithFormat:@"%@",channelId] : @""; | ||
| 135 | - } | ||
| 136 | - if (![activityId isKindOfClass:[NSString class]] || !activityId) { | ||
| 137 | - activityId = activityId ? [NSString stringWithFormat:@"%@",activityId] : @""; | 97 | +- (instancetype)initLivePlayWithVideoId:(NSString *)videoId activityId:(NSString *)activityId { |
| 98 | + return [self initLivePlayWithVideoId:videoId activityId:activityId success:nil failure:nil]; | ||
| 99 | +} | ||
| 100 | +/** | ||
| 101 | +初始化播放器 | ||
| 102 | + | ||
| 103 | +@since v0.0.1 | ||
| 104 | +*/ | ||
| 105 | +- (void)createPlayerWithUrl:(NSString *)url{ | ||
| 106 | + NSURL *URL = [NSURL URLWithString:url]; | ||
| 107 | + if (_switchRate) { //切换码率 | ||
| 108 | + self.switchPlayer = [[PLPlayer alloc]initWithURL:URL option:self.option]; | ||
| 109 | + _switchPlayer.delegate = self; | ||
| 110 | + _switchPlayer.playerView.frame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height); | ||
| 111 | + _switchPlayer.playerView.contentMode = UIViewContentModeScaleAspectFit; | ||
| 112 | + [_switchPlayer play]; | ||
| 113 | + [self.view addSubview:self.switchPlayer.playerView]; | ||
| 114 | + [self.view bringSubviewToFront:self.player.playerView]; | ||
| 115 | + }else{ | ||
| 116 | + if(self.player){ // 切换视频 | ||
| 117 | + [self.player playWithURL:URL sameSource:NO]; | ||
| 138 | } | 118 | } |
| 139 | - | ||
| 140 | - _type = CNLivePlayerLive; | ||
| 141 | - | ||
| 142 | - _idName = @"cid"; | ||
| 143 | - _contentType = @"live"; | ||
| 144 | - _contentId = channelId; | ||
| 145 | - _activityId = [NSString stringWithFormat:@"%@",activityId]; | ||
| 146 | - _isAudio = NO; | ||
| 147 | - | ||
| 148 | - if (agree) { | ||
| 149 | - //初始化IM | ||
| 150 | - [[CNLiveMsgManager sharedCNLiveMsgManager] initCNLiveMsgToolsWithAppKey:PLAYER_AppKey appId:PLAYER_AppID]; | ||
| 151 | - | ||
| 152 | - //注册通知 | ||
| 153 | - [self addHostStatusNotificationObservers]; | ||
| 154 | - | ||
| 155 | - //加入聊天室,获取主播状态 | ||
| 156 | - [self getHostStatus:result]; | 119 | + else{ |
| 120 | + self.player = [[PLPlayer alloc]initWithURL:URL option:self.option]; | ||
| 121 | + _player.delegate = self; | ||
| 122 | + _player.playerView.frame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height); | ||
| 123 | + _player.playerView.contentMode = UIViewContentModeScaleAspectFit; | ||
| 124 | + if(!_player.isPlaying){ | ||
| 125 | + [_player play]; | ||
| 126 | + } | ||
| 127 | + [self.view addSubview:self.player.playerView]; | ||
| 157 | } | 128 | } |
| 158 | - | ||
| 159 | - [self createPlayerWithContentId:channelId authSuccess:authSuccessBlock authFailure:authFailureBlock]; | ||
| 160 | - | ||
| 161 | } | 129 | } |
| 162 | - return self; | ||
| 163 | - | ||
| 164 | } | 130 | } |
| 165 | 131 | ||
| 166 | -/* 初始化音频播放器 初始化成功后才能对播放器进行设置(如:自动播放、是否开启硬件解码等)和调用准备播放视频方法(prepareToPlay) | ||
| 167 | - * | ||
| 168 | - * audioId 音频Id | ||
| 169 | - * authSuccess 初始化成功block | ||
| 170 | - * authFailure 初始化失败block 回调一个errorInfo的字典 | ||
| 171 | - * | ||
| 172 | - */ | ||
| 173 | -- (instancetype)initAudioPlayerWithAudioId:(NSString *)audioId authSuccess:(AuthSuccessBlock)authSuccessBlock authFailure:(AuthFailureBlock)authFailureBlock{ | ||
| 174 | - if (self = [super init]) { | ||
| 175 | - if (![audioId isKindOfClass:[NSString class]] || !audioId) { | ||
| 176 | - audioId = audioId ? [NSString stringWithFormat:@"%@",audioId] : @""; | ||
| 177 | - | ||
| 178 | - } | ||
| 179 | - | ||
| 180 | - _type = CNLivePlayerAudio; | ||
| 181 | - | ||
| 182 | - _idName = @"vid"; | ||
| 183 | - _contentType = @"vod"; | ||
| 184 | - _contentId = audioId; | ||
| 185 | - _isAudio = YES; | ||
| 186 | - | ||
| 187 | - [self createPlayerWithContentId:audioId authSuccess:authSuccessBlock authFailure:authFailureBlock]; | ||
| 188 | - | ||
| 189 | - } | ||
| 190 | - return self; | ||
| 191 | - | 132 | +- (void)dealloc { |
| 133 | + NSLog(@"CNLivePlayer -- dealloc"); | ||
| 192 | } | 134 | } |
| 193 | 135 | ||
| 194 | -- (void)createPlayerWithContentId:(NSString *)contentId authSuccess:(AuthSuccessBlock)authSuccessBlock authFailure:(AuthFailureBlock)authFailureBlock{ | ||
| 195 | - [UIApplication sharedApplication].idleTimerDisabled = YES; | ||
| 196 | - | ||
| 197 | - _rate = @"2"; | ||
| 198 | - _switchRate = NO; | ||
| 199 | - | ||
| 200 | - _retryTime = 0; | ||
| 201 | - [self addMovieNotificationObservers]; | ||
| 202 | - | ||
| 203 | - __weak typeof(self) weakSelf = self; | ||
| 204 | - dispatch_async(dispatch_get_global_queue(0, 0), ^{ | ||
| 205 | - __strong typeof(weakSelf) strongSelf = weakSelf; | ||
| 206 | - if(strongSelf){ | ||
| 207 | - [strongSelf getUrlSuccess:authSuccessBlock failure:authFailureBlock]; | 136 | +#pragma mark - 切换视频 |
| 137 | +/** | ||
| 138 | +切换视频 | ||
| 208 | 139 | ||
| 209 | - } | ||
| 210 | - | ||
| 211 | - }); | ||
| 212 | - | ||
| 213 | -} | 140 | +@param videoId 视频id |
| 214 | 141 | ||
| 215 | -#pragma mark - 切换视频 | ||
| 216 | -/* 给播放器设置内容id 设置成功后才能播放(如:自动播放、是否开启硬件解码等)和调用准备播放视频方法(prepareToPlay) | ||
| 217 | - * 切换视频 | ||
| 218 | - * contentId 内容Id(直播channelId+activityId) | ||
| 219 | - * type 内容类型 | ||
| 220 | - */ | ||
| 221 | -- (void)setPlayerWithContentId:(NSString *)contentId type:(CNLivePlayerType)type authSuccess:(AuthSuccessBlock)authSuccessBlock authFailure:(AuthFailureBlock)authFailureBlock{ | ||
| 222 | - if([_contentId isEqualToString:contentId]){ | ||
| 223 | - return; | 142 | +@since v0.0.1 |
| 143 | +*/ | ||
| 144 | +- (void)switchVideo:(NSString *)videoId type:(CNLivePlayerDataType)type { | ||
| 145 | + if ([_videoId isEqualToString:videoId]) { | ||
| 146 | + return; | ||
| 224 | } | 147 | } |
| 225 | - //暂停当前视频 | ||
| 226 | - [self pause]; | ||
| 227 | - _retryTime = 0; | 148 | + _rate = @"2"; |
| 228 | _switchRate = NO; | 149 | _switchRate = NO; |
| 229 | - | ||
| 230 | - _type = type; | ||
| 231 | switch (type) { | 150 | switch (type) { |
| 232 | - case CNLivePlayerVod: | 151 | + case CNLivePlayerDataTypeVod: |
| 233 | { | 152 | { |
| 234 | - _idName = @"vid"; | ||
| 235 | - _contentType = @"vod"; | ||
| 236 | - _contentId = contentId; | ||
| 237 | - _isAudio = NO; | 153 | + _type = @"vod"; |
| 238 | } | 154 | } |
| 239 | break; | 155 | break; |
| 240 | - case CNLivePlayerLive: | 156 | + case CNLivePlayerDataTypeLive: |
| 241 | { | 157 | { |
| 242 | - _idName = @"cid"; | ||
| 243 | - _contentType = @"live"; | ||
| 244 | - NSArray *array = [contentId componentsSeparatedByString:@"+"]; | ||
| 245 | - if(array && array.count > 1){ | ||
| 246 | - _contentId = array[0]; | ||
| 247 | - _activityId = array[1]; | ||
| 248 | - }else { | ||
| 249 | - _contentId = contentId; | ||
| 250 | - _activityId = @""; | ||
| 251 | - } | ||
| 252 | - _isAudio = NO; | 158 | + _type = @"live"; |
| 253 | } | 159 | } |
| 254 | break; | 160 | break; |
| 255 | - case CNLivePlayerAudio: | 161 | + case CNLivePlayerDataTypeAudio: |
| 256 | { | 162 | { |
| 257 | - _idName = @"vid"; | ||
| 258 | - _contentType = @"vod"; | ||
| 259 | - _contentId = contentId; | ||
| 260 | - _isAudio = YES; | 163 | + _type = @"vod"; |
| 261 | } | 164 | } |
| 262 | break; | 165 | break; |
| 263 | - | ||
| 264 | } | 166 | } |
| 167 | + _videoId = videoId; | ||
| 168 | + [self getCacheURLSuccess:nil failure:nil]; | ||
| 265 | 169 | ||
| 266 | - __weak typeof(self) weakSelf = self; | ||
| 267 | - dispatch_async(dispatch_get_global_queue(0, 0), ^{ | ||
| 268 | - __strong typeof(weakSelf) strongSelf = weakSelf; | ||
| 269 | - if(strongSelf){ | ||
| 270 | - [strongSelf getUrlSuccess:authSuccessBlock failure:authFailureBlock]; | ||
| 271 | - | ||
| 272 | - } | ||
| 273 | - | ||
| 274 | - }); | ||
| 275 | - | ||
| 276 | } | 170 | } |
| 277 | - | ||
| 278 | #pragma mark - 切换码率 | 171 | #pragma mark - 切换码率 |
| 279 | -/* 给点播播放器切换码率设置内容id 设置成功后才能播放(如:自动播放、是否开启硬件解码等)和调用准备播放视频方法(prepareToPlay) | ||
| 280 | - * 切换视频清晰度 | ||
| 281 | - * contentId 内容Id(如果不切换视频,只切换清晰度,可以将contentId=nil) | ||
| 282 | - * rate 码率 1-流畅,2-标清,3-高清,4-超清,默认为2 | ||
| 283 | - */ | ||
| 284 | -- (void)setPlayerWithContentId:(NSString *)contentId rate:(NSString *)rate authSuccess:(AuthSuccessBlock)authSuccessBlock authFailure:(AuthFailureBlock)authFailureBlock { | ||
| 285 | - if(!contentId&&[_rate isEqualToString:rate]){ | ||
| 286 | - return; | ||
| 287 | - } | ||
| 288 | - if([_contentId isEqualToString:contentId]&&[_rate isEqualToString:rate]){ | 172 | +/** |
| 173 | +切换码率 | ||
| 174 | + | ||
| 175 | +@param level 视频清晰度 | ||
| 176 | + | ||
| 177 | +@since v0.0.1 | ||
| 178 | +*/ | ||
| 179 | +- (void)switchRate:(CNLiveClarityLevel)level{ | ||
| 180 | + if (_switchRate) { | ||
| 289 | return; | 181 | return; |
| 290 | } | 182 | } |
| 291 | - if(contentId && ![contentId isEqualToString:@""]){ | ||
| 292 | - _contentId = contentId; | ||
| 293 | - } | ||
| 294 | - _idName = @"vid"; | ||
| 295 | - _contentType = @"vod"; | ||
| 296 | - _isAudio = NO; | ||
| 297 | - _rate = rate; | ||
| 298 | _switchRate = YES; | 183 | _switchRate = YES; |
| 299 | - | ||
| 300 | - __weak typeof(self) weakSelf = self; | ||
| 301 | - dispatch_async(dispatch_get_global_queue(0, 0), ^{ | ||
| 302 | - __strong typeof(weakSelf) strongSelf = weakSelf; | ||
| 303 | - if(strongSelf){ | ||
| 304 | - [strongSelf getUrlSuccess:authSuccessBlock failure:authFailureBlock]; | 184 | + // 1-流畅,2-标清,3-高清,4-超清,默认为2 |
| 185 | + switch (level) { | ||
| 186 | + case kCNLiveClarity_1080P: | ||
| 187 | + { | ||
| 188 | + if ([_rate isEqualToString:@"4"]) return; | ||
| 189 | + _rate = @"4"; | ||
| 305 | } | 190 | } |
| 306 | - }); | ||
| 307 | -} | ||
| 308 | - | ||
| 309 | -/* | ||
| 310 | - * 销毁播放器 | ||
| 311 | - */ | ||
| 312 | -- (void)destroyPlayer { | ||
| 313 | - if (_statTimer) { | ||
| 314 | - [_statTimer invalidate]; | ||
| 315 | - _statTimer = nil; | ||
| 316 | - } | ||
| 317 | - | ||
| 318 | - if (_player) { | ||
| 319 | - [self stop]; | ||
| 320 | - _player = nil; | ||
| 321 | - } | ||
| 322 | - | ||
| 323 | - [self removeNotificationObservers]; | ||
| 324 | - | ||
| 325 | - if (PLAYER_TestEnvironment) { | ||
| 326 | - NSLog(@"CNLivePlayer-->dealloc"); | 191 | + break; |
| 192 | + case kCNLiveClarity_720P: | ||
| 193 | + { | ||
| 194 | + if ([_rate isEqualToString:@"3"]) return; | ||
| 195 | + _rate = @"3"; | ||
| 196 | + } | ||
| 197 | + break; | ||
| 198 | + case kCNLiveClarity_480P: | ||
| 199 | + { | ||
| 200 | + if ([_rate isEqualToString:@"2"]) return; | ||
| 201 | + _rate = @"2"; | ||
| 202 | + } | ||
| 203 | + break; | ||
| 204 | + case kCNLiveClarity_360P: | ||
| 205 | + { | ||
| 206 | + if ([_rate isEqualToString:@"1"]) return; | ||
| 207 | + _rate = @"1"; | ||
| 208 | + } | ||
| 209 | + break; | ||
| 327 | } | 210 | } |
| 328 | - | ||
| 329 | -} | ||
| 330 | - | ||
| 331 | -- (void)dealloc { | 211 | + int64_t seconds = self.player.currentTime.value+3; |
| 212 | + int32_t timeScale = self.player.currentTime.timescale; | ||
| 213 | + _switchRateTime = CMTimeMake(seconds, timeScale); | ||
| 214 | + [self getCacheURLSuccess:nil failure:nil]; | ||
| 332 | 215 | ||
| 333 | } | 216 | } |
| 334 | 217 | ||
| 335 | #pragma mark - 查找本地是否有缓存url | 218 | #pragma mark - 查找本地是否有缓存url |
| 336 | -- (void)getUrlSuccess:(void (^)(void))authSuccessBlock failure:(void (^)(NSDictionary *errorDic))authFailureBlock { | ||
| 337 | - id data = [[CNLivePlayerDBTool shared] searchInTable:PlayerStringToKey(_contentId,_rate)]; | ||
| 338 | - CNLivePlayerDBModel *model = (CNLivePlayerDBModel *)data; | ||
| 339 | - //1.判断是否存在缓存url | ||
| 340 | - if (![model isEmpty]) { | 219 | +//播放地址 |
| 220 | +- (void)getCacheURLSuccess:(SuccessBlock)success failure:(FailureBlock)failure { | ||
| 221 | + // 有鉴权 | ||
| 222 | + if(Player_Authentication){ | ||
| 223 | + // 有缓存 | ||
| 224 | + id data = [[CNLivePlayerDBTool shared] searchInTable:PlayerStringToKey(_videoId, _rate)]; | ||
| 225 | + CNLivePlayerModel *model = (CNLivePlayerModel *)data; | ||
| 226 | + if (![model isEmpty]) { | ||
| 227 | + [self createPlayerWithUrl:model.url]; | ||
| 228 | + success?success():@""; | ||
| 229 | + } | ||
| 230 | + // 无缓存 | ||
| 231 | + else{ | ||
| 232 | + // 请求视频url | ||
| 233 | + [self getVideoURLSuccess:success failure:failure]; | ||
| 234 | + } | ||
| 235 | + } | ||
| 236 | + // 无鉴权失败 | ||
| 237 | + else{ | ||
| 341 | __weak typeof(self) weakSelf = self; | 238 | __weak typeof(self) weakSelf = self; |
| 342 | - dispatch_async(dispatch_get_main_queue(), ^{ | 239 | + [CNLivePlayerRequest getAuthenticationBlock:^(NSString * _Nullable code, NSError * _Nullable error) { |
| 343 | __strong typeof(weakSelf) strongSelf = weakSelf; | 240 | __strong typeof(weakSelf) strongSelf = weakSelf; |
| 344 | if(!strongSelf){ | 241 | if(!strongSelf){ |
| 345 | return ; | 242 | return ; |
| 346 | } | 243 | } |
| 347 | - [strongSelf createPlayerWithUrl:model.url]; | ||
| 348 | - | ||
| 349 | - if (PLAYER_TestEnvironment) { | ||
| 350 | - NSLog(@"CNLivePlayer-->缓存数据,播放器初始化成功,可以对播放器进行设置并准备播放"); | ||
| 351 | - } | ||
| 352 | - | ||
| 353 | - authSuccessBlock ? authSuccessBlock() : nil; | ||
| 354 | - | ||
| 355 | - }); | ||
| 356 | - | ||
| 357 | - } else { | ||
| 358 | - //2.判断是否鉴权 | ||
| 359 | - NSDictionary *privilegeDic = [[NSUserDefaults standardUserDefaults] objectForKey:@"kCNLivePlayerSDKAuthentication"]; | ||
| 360 | - if(privilegeDic&&[privilegeDic[@"resultCode"] isEqualToString:@"0"]){ | ||
| 361 | - //3.存在鉴权成功,直接请求url | ||
| 362 | - if([_contentType isEqualToString:@"vod"]){ | ||
| 363 | - [self getVodVideoURLSuccess:authSuccessBlock failure:authFailureBlock]; | ||
| 364 | - | 244 | + if([code isEqualToString:@"0"]){ |
| 245 | + // 鉴权成功 | ||
| 246 | + // 请求视频url | ||
| 247 | + [strongSelf getVideoURLSuccess:success failure:failure]; | ||
| 365 | }else{ | 248 | }else{ |
| 366 | - [self getLiveVideoURLSuccess:authSuccessBlock failure:authFailureBlock]; | ||
| 367 | - | ||
| 368 | - } | ||
| 369 | - | ||
| 370 | - }else{ | ||
| 371 | - __weak typeof(self) weakSelf = self; | ||
| 372 | - //4.不存在鉴权成功,先请求鉴权 | ||
| 373 | - [self authenticateSuccess:^{ | ||
| 374 | - //5.鉴权成功,再请求url | ||
| 375 | - __strong typeof(weakSelf) strongSelf = weakSelf; | ||
| 376 | - if(strongSelf){ | ||
| 377 | - if([strongSelf->_contentType isEqualToString:@"vod"]){ | ||
| 378 | - [strongSelf getVodVideoURLSuccess:authSuccessBlock failure:authFailureBlock]; | ||
| 379 | - | ||
| 380 | - }else{ | ||
| 381 | - [strongSelf getLiveVideoURLSuccess:authSuccessBlock failure:authFailureBlock]; | ||
| 382 | - | 249 | + // 鉴权失败 |
| 250 | + if(strongSelf->_switchRate){//切换码率 | ||
| 251 | + if (strongSelf.delegate && [strongSelf.delegate respondsToSelector:@selector(player:switchRateCompleted:)]) { | ||
| 252 | + [strongSelf.delegate player:strongSelf switchRateCompleted:NO]; | ||
| 383 | } | 253 | } |
| 254 | + strongSelf->_switchRate = NO; | ||
| 384 | } | 255 | } |
| 385 | - | ||
| 386 | - | ||
| 387 | - } failure:authFailureBlock]; | ||
| 388 | - } | ||
| 389 | - | ||
| 390 | - } | ||
| 391 | - | ||
| 392 | -} | ||
| 393 | - | ||
| 394 | -//根据url创建播放器 | ||
| 395 | -- (void)createPlayerWithUrl:(NSString *)url { | ||
| 396 | - self.videoUrl = url; | ||
| 397 | - if(self->_switchRate){ | ||
| 398 | - _switchCurrentTime = self.player.currentPlaybackTime; | ||
| 399 | - | ||
| 400 | - //切换码率 | ||
| 401 | - NSString *proxyUrl = [[CNLiveHTTPProxyService manager] getProxyUrl:self.videoUrl]; | ||
| 402 | - KSYMoviePlayerController *switchPlayer = [[KSYMoviePlayerController alloc] initWithContentURL:[NSURL URLWithString:PLAYER_OpenCache?proxyUrl:self.videoUrl]]; | ||
| 403 | - switchPlayer.view.frame = self.view.bounds; | ||
| 404 | -// switchPlayer.view.frame = CGRectMake(0, 200, self.view.bounds.size.width, 200); | ||
| 405 | - switchPlayer.view.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight; | ||
| 406 | - switchPlayer.shouldEnableKSYStatModule = NO; | ||
| 407 | - switchPlayer.shouldMute = YES; | ||
| 408 | - [switchPlayer prepareToPlay]; | ||
| 409 | - [self.view addSubview:switchPlayer.view]; | ||
| 410 | - self.switchPlayer = switchPlayer; | ||
| 411 | - | ||
| 412 | - }else{ | ||
| 413 | - if(!self.player){ | ||
| 414 | - NSString *proxyUrl = [[CNLiveHTTPProxyService manager] getProxyUrl:self.videoUrl]; | ||
| 415 | - self.player = [[KSYMoviePlayerController alloc] initWithContentURL:[NSURL URLWithString:PLAYER_OpenCache?proxyUrl:self.videoUrl]]; | ||
| 416 | - self.player.shouldAutoplay = NO; | ||
| 417 | - [self.view addSubview:self.player.view]; | ||
| 418 | - self.player.view.frame = self.view.bounds; | ||
| 419 | - self.player.view.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight; | ||
| 420 | - self.player.shouldEnableKSYStatModule = NO; | ||
| 421 | - | ||
| 422 | - }else{ | ||
| 423 | - [self.player reset:YES]; | ||
| 424 | - [self.player setUrl:[NSURL URLWithString:self.videoUrl]]; | ||
| 425 | - | ||
| 426 | - } | 256 | + else{ |
| 257 | + if (strongSelf.delegate && [strongSelf.delegate respondsToSelector:@selector(player:stoppedWithError:)]) { | ||
| 258 | + NSError *customError = [NSError errorWithDomain:@"AuthenticationFailed" code:-1 userInfo:@{NSLocalizedDescriptionKey:@"鉴权失败",NSLocalizedFailureReasonErrorKey:error.localizedFailureReason?error.localizedFailureReason:@"鉴权失败", | ||
| 259 | + }]; | ||
| 260 | + [strongSelf.delegate player:strongSelf stoppedWithError:customError]; | ||
| 261 | + } | ||
| 262 | + } | ||
| 263 | + } | ||
| 264 | + }]; | ||
| 427 | } | 265 | } |
| 428 | - | ||
| 429 | -} | ||
| 430 | 266 | ||
| 431 | -#pragma mark - 鉴权 | ||
| 432 | -- (void)authenticateSuccess:(AuthSuccessBlock)authSuccessBlock failure:(AuthFailureBlock)authFailureBlock { | ||
| 433 | - | ||
| 434 | - NSDictionary *parameter = @{@"platform_id": [NSBundle mainBundle].bundleIdentifier, | ||
| 435 | - @"timestamp": [CNLivePlayerTools getTimestamp], | ||
| 436 | - @"appId": PLAYER_AppID}; | ||
| 437 | - | ||
| 438 | - NSString *videoUrl = [CNLivePlayerTools encryptionSignWithUrl:AuthUrl parameter:parameter]; | ||
| 439 | - | ||
| 440 | - __weak typeof(self) weakSelf = self; | ||
| 441 | - [[CNLivePlayerNetworkManager manager] authenticate:videoUrl success:^(NSURLResponse * _Nullable response, id _Nullable responseObject) { | ||
| 442 | - __strong typeof(weakSelf) strongSelf = weakSelf; | ||
| 443 | - if(!responseObject||![responseObject isKindOfClass:[NSDictionary class]]){ | ||
| 444 | - authFailureBlock ? authFailureBlock(responseObject) : nil; | ||
| 445 | - | ||
| 446 | - } | ||
| 447 | - | ||
| 448 | - NSDictionary *authErrorDic = @{@"code":responseObject[@"errorCode"], | ||
| 449 | - @"resultCode":@"0", | ||
| 450 | - @"errorMessage":responseObject[@"errorMessage"]}; | ||
| 451 | - [[NSUserDefaults standardUserDefaults] setObject:authErrorDic forKey:@"kCNLivePlayerSDKAuthentication"]; | ||
| 452 | - [[NSUserDefaults standardUserDefaults] synchronize]; | ||
| 453 | - if ([responseObject[@"errorCode"] isEqualToString:@"0"]) { | ||
| 454 | - authSuccessBlock ? authSuccessBlock() : nil; | ||
| 455 | - | ||
| 456 | - } | ||
| 457 | - else { | ||
| 458 | - if (PLAYER_TestEnvironment) { | ||
| 459 | - NSLog(@"CNLivePlayer-->播放器初始化失败,失败原因:%@", authErrorDic); | ||
| 460 | - } | ||
| 461 | - authFailureBlock ? authFailureBlock(responseObject) : nil; | ||
| 462 | - if(strongSelf){ | ||
| 463 | - [CNLivePlayerTools errorStat:authErrorDic[@"errorMessage"] contentId:strongSelf->_activityId ? strongSelf->_activityId : strongSelf->_contentId idName:strongSelf->_idName]; | ||
| 464 | - } | ||
| 465 | - | ||
| 466 | - | ||
| 467 | - } | ||
| 468 | - | ||
| 469 | - } failure:^(NSURLResponse * _Nullable response, NSError * _Nullable error) { | ||
| 470 | - __strong typeof(weakSelf) strongSelf = weakSelf; | ||
| 471 | - NSDictionary *authErrorDic = @{@"code":[NSString stringWithFormat:@"%ld", error.code], | ||
| 472 | - @"resultCode":@"1", | ||
| 473 | - @"errorMessage":[NSString stringWithFormat:@"%@", error.localizedDescription]}; | ||
| 474 | - [[NSUserDefaults standardUserDefaults] setObject:authErrorDic forKey:@"kCNLivePlayerSDKAuthentication"]; | ||
| 475 | - [[NSUserDefaults standardUserDefaults] synchronize]; | ||
| 476 | - | ||
| 477 | - if (PLAYER_TestEnvironment) { | ||
| 478 | - NSLog(@"CNLivePlayer-->播放器初始化失败,失败原因:%@", authErrorDic); | ||
| 479 | - } | ||
| 480 | - authFailureBlock ? authFailureBlock(authErrorDic) : nil; | ||
| 481 | - if(strongSelf){ | ||
| 482 | - [CNLivePlayerTools errorStat:authErrorDic[@"errorMessage"] contentId:strongSelf->_activityId ? strongSelf->_activityId : strongSelf->_contentId idName:strongSelf->_idName]; | ||
| 483 | - } | ||
| 484 | - | ||
| 485 | - }]; | ||
| 486 | - | ||
| 487 | } | 267 | } |
| 488 | 268 | ||
| 489 | -#pragma mark - 获取点播视频url | ||
| 490 | -- (void)getVodVideoURLSuccess:(AuthSuccessBlock)authSuccessBlock failure:(AuthFailureBlock)authFailureBlock { | ||
| 491 | - NSString *stamp = [CNLivePlayerTools getTimestamp]; | ||
| 492 | - NSDictionary *parameter = @{@"appId": PLAYER_AppID, | ||
| 493 | - @"vId": _contentId, | ||
| 494 | - @"timestamp": stamp, | ||
| 495 | - @"isHLS": @"1", | ||
| 496 | - @"plat": @"i", | ||
| 497 | - @"playType": _isAudio ? @"a": @"v", | ||
| 498 | - @"platform_id": [NSBundle mainBundle].bundleIdentifier, | ||
| 499 | - @"channelName": PLAYER_ChannelName, | ||
| 500 | - @"rate":_rate?_rate:@"2", | ||
| 501 | - @"tag": PLAYER_Tag, | ||
| 502 | - @"from": @"apple", | ||
| 503 | - @"sid": PLAYER_UserId, | ||
| 504 | - @"uid": [CNLivePlayerTools getUidForTimestamp:stamp]}; | ||
| 505 | - | ||
| 506 | - NSString *videoUrl = [CNLivePlayerTools encryptionSignWithUrl:VodUrl parameter:parameter]; | ||
| 507 | - | ||
| 508 | - __weak typeof(self) weakSelf = self; | ||
| 509 | - [[CNLivePlayerNetworkManager manager] getVideoURL:videoUrl success:^(NSString * _Nullable videoURL, NSInteger code) { | ||
| 510 | - __strong typeof(weakSelf) strongSelf = weakSelf; | ||
| 511 | - if(!strongSelf){ | ||
| 512 | - return ; | ||
| 513 | - } | ||
| 514 | - CNLivePlayerDBModel *model = [[CNLivePlayerDBModel alloc]initWithContenId:PlayerStringToKey(strongSelf->_contentId, strongSelf->_rate) url:videoURL]; | ||
| 515 | - [[CNLivePlayerDBTool shared] insertTable:model]; | ||
| 516 | - | ||
| 517 | - __weak typeof(self) weakSelf = strongSelf; | ||
| 518 | - dispatch_async(dispatch_get_main_queue(), ^{ | 269 | +#pragma mark - 获取播放地址 |
| 270 | +- (void)getVideoURLSuccess:(SuccessBlock)success failure:(FailureBlock)failure { | ||
| 271 | + if([_type isEqualToString:@"vod"]){ | ||
| 272 | + //点播播放地址 | ||
| 273 | + __weak typeof(self) weakSelf = self; | ||
| 274 | + [CNLivePlayerRequest getVodVideoURLWithVideoId:_videoId rate:_rate block:^(NSString * _Nonnull videoUrl, NSError *error) { | ||
| 519 | __strong typeof(weakSelf) strongSelf = weakSelf; | 275 | __strong typeof(weakSelf) strongSelf = weakSelf; |
| 520 | if(!strongSelf){ | 276 | if(!strongSelf){ |
| 521 | return ; | 277 | return ; |
| 522 | } | 278 | } |
| 523 | - [strongSelf createPlayerWithUrl:videoURL]; | ||
| 524 | - | ||
| 525 | - [CNLivePlayerTools statSDKWithContentId:strongSelf->_activityId ? strongSelf->_activityId : strongSelf->_contentId idName:strongSelf->_idName]; | ||
| 526 | - | ||
| 527 | - if (PLAYER_TestEnvironment) { | ||
| 528 | - NSLog(@"CNLivePlayer-->请求数据,点播播放器初始化成功,可以对播放器进行设置并准备播放"); | ||
| 529 | - } | ||
| 530 | - | ||
| 531 | - if (authSuccessBlock) { | ||
| 532 | - authSuccessBlock(); | ||
| 533 | - } | ||
| 534 | - | ||
| 535 | - }); | ||
| 536 | - }]; | ||
| 537 | -} | ||
| 538 | - | ||
| 539 | -#pragma mark - 获取直播视频url | ||
| 540 | -- (void)getLiveVideoURLSuccess:(AuthSuccessBlock)authSuccessBlock failure:(AuthFailureBlock)authFailureBlock { | ||
| 541 | - NSString *timestamp = [CNLivePlayerTools getTimestamp]; | ||
| 542 | - NSDictionary *parameter = @{@"appId": PLAYER_AppID, | ||
| 543 | - @"channelId": _contentId, | ||
| 544 | - @"activityId": _activityId ? _activityId : @"", | ||
| 545 | - @"timestamp": timestamp, | ||
| 546 | - @"isHLS": @"0", | ||
| 547 | - @"plat": @"i", | ||
| 548 | - @"playType": _isAudio ? @"a": @"v", | ||
| 549 | - @"platform_id": [NSBundle mainBundle].bundleIdentifier, | ||
| 550 | - @"channelName": PLAYER_ChannelName, | ||
| 551 | - @"tag": PLAYER_Tag, | ||
| 552 | - @"from": @"apple", | ||
| 553 | - @"sid": PLAYER_UserId, | ||
| 554 | - @"uid": [CNLivePlayerTools getUidForTimestamp:timestamp]}; | ||
| 555 | - | ||
| 556 | - NSString *videoUrl = [CNLivePlayerTools encryptionSignWithUrl:LiveUrl parameter:parameter]; | ||
| 557 | - | ||
| 558 | - // 拿到302跳转后的播放地址 | ||
| 559 | - NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:videoUrl]]; | ||
| 560 | - [urlRequest setHTTPMethod:@"GET"]; | ||
| 561 | - [urlRequest setTimeoutInterval:30]; | ||
| 562 | - | ||
| 563 | - __weak typeof(self) weakSelf = self; | ||
| 564 | - [NSURLConnection sendAsynchronousRequest:urlRequest queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) { | ||
| 565 | - __strong typeof(weakSelf) strongSelf = weakSelf; | ||
| 566 | - if(!strongSelf){ | ||
| 567 | - return ; | ||
| 568 | - } | ||
| 569 | - NSString *url = nil; | ||
| 570 | - if (connectionError) { | ||
| 571 | - NSError *error = connectionError.userInfo[NSUnderlyingErrorKey]; | ||
| 572 | - NSString *videoUrl302 = error.userInfo[NSURLErrorFailingURLStringErrorKey]; | ||
| 573 | - if (!videoUrl302 || ![videoUrl302 isKindOfClass:[NSString class]] || videoUrl302.length <= 0) { | ||
| 574 | - videoUrl302 = nil; | 279 | + if(!error){ |
| 280 | + [strongSelf createPlayerWithUrl:videoUrl]; | ||
| 281 | + CNLivePlayerModel *model = [[CNLivePlayerModel alloc]initWithID:PlayerStringToKey(strongSelf->_videoId, strongSelf->_rate) url:videoUrl]; | ||
| 282 | + [[CNLivePlayerDBTool shared] insertTable:model]; | ||
| 283 | + success?success():@""; | ||
| 284 | + }else{ | ||
| 285 | + // 获取url失败 | ||
| 286 | + if(strongSelf->_switchRate){//切换码率 | ||
| 287 | + if (strongSelf.delegate && [strongSelf.delegate respondsToSelector:@selector(player:switchRateCompleted:)]) { | ||
| 288 | + [strongSelf.delegate player:strongSelf switchRateCompleted:NO]; | ||
| 289 | + } | ||
| 290 | + strongSelf->_switchRate = NO; | ||
| 291 | + }else{ | ||
| 292 | + if (strongSelf.delegate && [strongSelf.delegate respondsToSelector:@selector(player:stoppedWithError:)]) { | ||
| 293 | + NSError *customError = [NSError errorWithDomain:@"GetUrlFailed" code:-2 userInfo:@{NSLocalizedDescriptionKey:@"获取url失败",NSLocalizedFailureReasonErrorKey:error.localizedFailureReason?error.localizedFailureReason:@"获取url失败"}]; | ||
| 294 | + [strongSelf.delegate player:strongSelf stoppedWithError:customError]; | ||
| 295 | + } | ||
| 296 | + } | ||
| 297 | + failure?failure(error):@""; | ||
| 575 | } | 298 | } |
| 576 | - url = videoUrl302; | ||
| 577 | - | ||
| 578 | - } else if (response) { | ||
| 579 | - url = response.URL.absoluteString; | ||
| 580 | - | ||
| 581 | - } else { | ||
| 582 | - url = videoUrl; | ||
| 583 | - | ||
| 584 | - } | ||
| 585 | - CNLivePlayerDBModel *model = [[CNLivePlayerDBModel alloc]initWithContenId:PlayerStringToKey(strongSelf->_contentId, strongSelf->_rate) url:url]; | ||
| 586 | - [[CNLivePlayerDBTool shared] insertTable:model]; | ||
| 587 | - | ||
| 588 | - __weak typeof(self) weakSelf = strongSelf; | ||
| 589 | - dispatch_async(dispatch_get_main_queue(), ^{ | 299 | + }]; |
| 300 | + | ||
| 301 | + }else if([_type isEqualToString:@"live"]){ | ||
| 302 | + //直播播放地址 | ||
| 303 | + __weak typeof(self) weakSelf = self; | ||
| 304 | + [CNLivePlayerRequest getLiveVideoURLWithVideoId:_videoId activityId:_activityId block:^(NSString * _Nonnull videoUrl, NSError *error) { | ||
| 590 | __strong typeof(weakSelf) strongSelf = weakSelf; | 305 | __strong typeof(weakSelf) strongSelf = weakSelf; |
| 591 | if(!strongSelf){ | 306 | if(!strongSelf){ |
| 592 | return ; | 307 | return ; |
| 593 | } | 308 | } |
| 594 | - [strongSelf createPlayerWithUrl:url]; | ||
| 595 | - | ||
| 596 | - [CNLivePlayerTools statSDKWithContentId:strongSelf->_activityId ? strongSelf->_activityId : strongSelf->_contentId idName:strongSelf->_idName]; | ||
| 597 | - | ||
| 598 | - if (PLAYER_TestEnvironment) { | ||
| 599 | - NSLog(@"CNLivePlayer-->请求数据,直播播放器初始化成功,可以对播放器进行设置并准备播放"); | ||
| 600 | - } | ||
| 601 | - | ||
| 602 | - if (authSuccessBlock) { | ||
| 603 | - authSuccessBlock(); | 309 | + if(!error){ |
| 310 | + [strongSelf createPlayerWithUrl:videoUrl]; | ||
| 311 | + CNLivePlayerModel *model = [[CNLivePlayerModel alloc]initWithID:PlayerStringToKey(strongSelf->_videoId, strongSelf->_rate) url:videoUrl]; | ||
| 312 | + [[CNLivePlayerDBTool shared] insertTable:model]; | ||
| 313 | + success?success():@""; | ||
| 314 | + }else{ | ||
| 315 | + // 获取url失败 | ||
| 316 | + if(strongSelf->_switchRate){//切换码率 | ||
| 317 | + if (strongSelf.delegate && [strongSelf.delegate respondsToSelector:@selector(player:switchRateCompleted:)]) { | ||
| 318 | + [strongSelf.delegate player:strongSelf switchRateCompleted:NO]; | ||
| 319 | + } | ||
| 320 | + strongSelf->_switchRate = NO; | ||
| 321 | + }else{ | ||
| 322 | + if (strongSelf.delegate && [strongSelf.delegate respondsToSelector:@selector(player:stoppedWithError:)]) { | ||
| 323 | + NSError *customError = [NSError errorWithDomain:@"GetUrlFailed" code:-2 userInfo:@{NSLocalizedDescriptionKey:@"获取url失败",NSLocalizedFailureReasonErrorKey:error.localizedFailureReason?error.localizedFailureReason:@"获取url失败"}]; | ||
| 324 | + [strongSelf.delegate player:strongSelf stoppedWithError:customError]; | ||
| 325 | + } | ||
| 326 | + } | ||
| 327 | + failure?failure(error):@""; | ||
| 604 | } | 328 | } |
| 605 | - | ||
| 606 | - }); | ||
| 607 | - }]; | ||
| 608 | -} | ||
| 609 | - | ||
| 610 | -#pragma mark - IM | ||
| 611 | -//获取主播状态(离开/已结束 | ||
| 612 | -- (void)getHostStatus:(void(^)(BOOL result))result { | ||
| 613 | - //连接IM服务器 | ||
| 614 | - [[CNLiveMsgManager sharedCNLiveMsgManager] justOnLineSuccess:^(NSString *toolsId) { | ||
| 615 | - //加入聊天室 | ||
| 616 | - [self joinChatRoomResult:result]; | ||
| 617 | - | ||
| 618 | - } error:^(NSInteger errors) { | ||
| 619 | - if (result) { | ||
| 620 | - result(NO); | ||
| 621 | - } | ||
| 622 | - | ||
| 623 | - }]; | ||
| 624 | - | ||
| 625 | -} | 329 | + }]; |
| 626 | 330 | ||
| 627 | -//加入聊天室 | ||
| 628 | -- (void)joinChatRoomResult:(void(^)(BOOL result))result { | ||
| 629 | - NSString *contentId = _activityId ? _activityId : _contentId; | ||
| 630 | - if (![_contentId containsString:@"_"]) { | ||
| 631 | - NSString *spId = [[PLAYER_AppID componentsSeparatedByString:@"_"] firstObject]; | ||
| 632 | - contentId = [NSString stringWithFormat:@"%@_%@", spId, _contentId]; | ||
| 633 | } | 331 | } |
| 634 | - | ||
| 635 | - _roomId = [NSString stringWithFormat:@"CNLiveAnchorStatus_%@",contentId]; | ||
| 636 | - | ||
| 637 | - //加入聊天室 | ||
| 638 | - [[CNLiveMsgManager sharedCNLiveMsgManager] gatherIfNullCreate:_roomId messageCount:1 success:^(NSString *targetId) { | ||
| 639 | - if (result) { | ||
| 640 | - result(YES); | ||
| 641 | - } | ||
| 642 | - | ||
| 643 | - } error:^(NSInteger errors, NSString *targetId) { | ||
| 644 | - if (result) { | ||
| 645 | - result(NO); | ||
| 646 | - } | ||
| 647 | - | ||
| 648 | - }]; | ||
| 649 | - | ||
| 650 | -} | ||
| 651 | 332 | ||
| 652 | -#pragma mark - 注册通知 | ||
| 653 | -- (void)addMovieNotificationObservers { | ||
| 654 | - // 视频加载状态 | ||
| 655 | - [[NSNotificationCenter defaultCenter] addObserver:self | ||
| 656 | - selector:@selector(loadStateDidChange:) | ||
| 657 | - name:MPMoviePlayerLoadStateDidChangeNotification | ||
| 658 | - object:nil]; | ||
| 659 | - // 视频播放结束 | ||
| 660 | - [[NSNotificationCenter defaultCenter] addObserver:self | ||
| 661 | - selector:@selector(moviePlayBackDidFinish:) | ||
| 662 | - name:MPMoviePlayerPlaybackDidFinishNotification | ||
| 663 | - object:nil]; | ||
| 664 | - [[NSNotificationCenter defaultCenter] addObserver:self | ||
| 665 | - selector:@selector(mediaIsPreparedToPlayDidChange:) | ||
| 666 | - name:MPMediaPlaybackIsPreparedToPlayDidChangeNotification | ||
| 667 | - object:nil]; | ||
| 668 | - if(PLAYER_OpenCache){ | ||
| 669 | - // 缓存状态 | ||
| 670 | - [[NSNotificationCenter defaultCenter] addObserver:self | ||
| 671 | - selector:@selector(mediaCacheDidChanged:) | ||
| 672 | - name:CacheStatusNotification | ||
| 673 | - object:nil]; | ||
| 674 | - // 缓存出错 | ||
| 675 | - [[NSNotificationCenter defaultCenter] addObserver:self | ||
| 676 | - selector:@selector(mediaCacheErrorDidChanged:) | ||
| 677 | - name:CacheErrorNotification | ||
| 678 | - object:nil]; | ||
| 679 | - } | ||
| 680 | - | ||
| 681 | } | 333 | } |
| 682 | 334 | ||
| 683 | -- (void)addHostStatusNotificationObservers { | ||
| 684 | - //获取主播状态 | ||
| 685 | - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(receiveMessage:) name:CNLivePlayerMsgReceiveNotification object:nil]; | ||
| 686 | - | 335 | +#pragma mark - 对外工具方法 |
| 336 | +/* | ||
| 337 | + 播放当前视频 | ||
| 338 | + | ||
| 339 | + @since v0.0.1 | ||
| 340 | + */ | ||
| 341 | +- (BOOL)play { | ||
| 342 | + return [self.player play]; | ||
| 687 | } | 343 | } |
| 688 | 344 | ||
| 689 | -- (void)removeNotificationObservers { | ||
| 690 | - [[NSNotificationCenter defaultCenter] removeObserver:self]; | ||
| 691 | - | ||
| 692 | - if (![CNLivePlayerTools isEmptyString:_roomId]) { | ||
| 693 | - [[CNLiveMsgManager sharedCNLiveMsgManager] disperse:_roomId success:^(NSString *targetId) { | ||
| 694 | - | ||
| 695 | - } error:^(NSInteger errors, NSString *targetId) { | ||
| 696 | - | ||
| 697 | - }]; | ||
| 698 | - } | ||
| 699 | - | 345 | +/* |
| 346 | + 暂停播放当前视频 | ||
| 347 | + | ||
| 348 | + @since v0.0.1 | ||
| 349 | + */ | ||
| 350 | +- (void)pause { | ||
| 351 | + [self.player pause]; | ||
| 700 | } | 352 | } |
| 701 | 353 | ||
| 702 | -#pragma mark - 通知响应方法 | ||
| 703 | -- (void)loadStateDidChange:(NSNotification *)notification { | ||
| 704 | - KSYMoviePlayerController *player = [notification object]; | ||
| 705 | - MPMovieLoadState loadState = player.loadState; | ||
| 706 | - if (PLAYER_TestEnvironment) { | ||
| 707 | - NSLog(@"CNLivePlayer-->播放器加载状态改变MPMovieLoadState:%ld", (unsigned long)loadState); | ||
| 708 | - } | ||
| 709 | - | ||
| 710 | - //切换码率 | ||
| 711 | - if(_switchRate){ | ||
| 712 | - if((self.switchPlayer == player)&&(loadState == 3)){ | ||
| 713 | - | ||
| 714 | - KSYMoviePlayerController *temp = self.player; | ||
| 715 | - temp.shouldMute = NO; | ||
| 716 | - self.player = player; | ||
| 717 | - self.switchPlayer = temp; | ||
| 718 | - | ||
| 719 | - __weak typeof(self)weakSelf = self; | ||
| 720 | - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ | ||
| 721 | - __strong typeof (weakSelf) strongSelf = weakSelf; | ||
| 722 | - if(!strongSelf){ | ||
| 723 | - return ; | ||
| 724 | - } | ||
| 725 | - strongSelf.player.shouldMute = NO; | ||
| 726 | - [strongSelf.switchPlayer reset:NO]; | ||
| 727 | - [strongSelf.switchPlayer stop]; | ||
| 728 | - [strongSelf.switchPlayer.view removeFromSuperview]; | ||
| 729 | - | ||
| 730 | - }); | ||
| 731 | - | ||
| 732 | - _switchRate = NO; | ||
| 733 | - | ||
| 734 | - } | ||
| 735 | - } | ||
| 736 | - [player play]; | 354 | +/* |
| 355 | + 继续播放当前视频 | ||
| 356 | + | ||
| 357 | + @since v0.0.1 | ||
| 358 | + */ | ||
| 359 | +- (void)resume{ | ||
| 360 | + [self.player resume]; | ||
| 737 | 361 | ||
| 738 | } | 362 | } |
| 739 | 363 | ||
| 740 | -- (void)mediaIsPreparedToPlayDidChange:(NSNotification *)notification { | ||
| 741 | - KSYMoviePlayerController *player = [notification object]; | ||
| 742 | - if(self.switchPlayer == player){ | ||
| 743 | -// [player seekTo:_switchCurrentTime accurate:YES]; | ||
| 744 | - player.currentPlaybackTime = _switchCurrentTime+5.0; | ||
| 745 | - [player play]; | ||
| 746 | - | ||
| 747 | - } | ||
| 748 | - if (PLAYER_TestEnvironment) { | ||
| 749 | - NSLog(@"CNLivePlayer-->播放器开始预播放"); | ||
| 750 | - } | ||
| 751 | - | ||
| 752 | - if (!_contentType || _contentType.length == 0) { | ||
| 753 | - return; | ||
| 754 | - } | ||
| 755 | - if(!_statTimer){ | ||
| 756 | - _statTimer = [NSTimer scheduledTimerWithTimeInterval:30 target:self selector:@selector(statTimerAction) userInfo:nil repeats:YES]; | ||
| 757 | - | ||
| 758 | - } | ||
| 759 | - | ||
| 760 | -} | 364 | +/* |
| 365 | + 结束当前视频的播放 | ||
| 366 | + | ||
| 367 | + @since v0.0.1 | ||
| 368 | + */ | ||
| 369 | +- (void)stop { | ||
| 370 | + [self.player stop]; | ||
| 371 | + _player = nil; | ||
| 372 | +} | ||
| 373 | + | ||
| 374 | +#pragma mark - 懒加载 | ||
| 375 | +- (PLPlayerOption *)option{ | ||
| 376 | + if(!_option){ | ||
| 377 | + _option = [PLPlayerOption defaultOption]; | ||
| 378 | + [_option setOptionValue:@15 forKey:PLPlayerOptionKeyTimeoutIntervalForMediaPackets]; | ||
| 379 | + [_option setOptionValue:@2000 forKey:PLPlayerOptionKeyMaxL1BufferDuration]; | ||
| 380 | + [_option setOptionValue:@1000 forKey:PLPlayerOptionKeyMaxL2BufferDuration]; | ||
| 381 | + [_option setOptionValue:@(NO) forKey:PLPlayerOptionKeyVideoToolbox]; | ||
| 382 | + if (Player_TestEnvironment) { | ||
| 383 | + [_option setOptionValue:@(kPLLogNone) forKey:PLPlayerOptionKeyLogLevel]; | ||
| 761 | 384 | ||
| 762 | -- (void)moviePlayBackDidFinish:(NSNotification *)notification { | ||
| 763 | - [UIApplication sharedApplication].idleTimerDisabled = NO; | ||
| 764 | - NSNumber *reason = [notification.userInfo valueForKey:MPMoviePlayerPlaybackDidFinishReasonUserInfoKey]; | ||
| 765 | - if (reason != nil){ | ||
| 766 | - NSInteger reasonAsInteger = [reason integerValue]; | ||
| 767 | - if (PLAYER_TestEnvironment) { | ||
| 768 | - NSLog(@"CNLivePlayer-->播放器播放结束原因MPMovieFinishReason:%ld", (long)reasonAsInteger); | ||
| 769 | - } | ||
| 770 | - switch (reasonAsInteger) { | ||
| 771 | - case MPMovieFinishReasonPlaybackEnded: | ||
| 772 | - { | ||
| 773 | - [_statTimer invalidate]; | ||
| 774 | - _statTimer = nil; | ||
| 775 | - _playTime = 0; | ||
| 776 | - } | ||
| 777 | - break; | ||
| 778 | - | ||
| 779 | - case MPMovieFinishReasonUserExited: | ||
| 780 | - { | ||
| 781 | - [_statTimer invalidate]; | ||
| 782 | - _statTimer = nil; | ||
| 783 | - _playTime = 0; | ||
| 784 | - } | ||
| 785 | - break; | ||
| 786 | - | ||
| 787 | - case MPMovieFinishReasonPlaybackError://视频异常探针 | ||
| 788 | - { | ||
| 789 | - [CNLivePlayerTools errorStat:notification.userInfo.description contentId:_activityId ? _activityId : _contentId idName:_idName]; | ||
| 790 | - } | ||
| 791 | - break; | ||
| 792 | - | ||
| 793 | - default: | ||
| 794 | - | ||
| 795 | - break; | 385 | + }else{ |
| 386 | + [_option setOptionValue:@(kPLLogNone) forKey:PLPlayerOptionKeyLogLevel]; | ||
| 387 | + | ||
| 796 | } | 388 | } |
| 797 | - | ||
| 798 | - } | ||
| 799 | - | ||
| 800 | - | ||
| 801 | -} | ||
| 802 | - | ||
| 803 | -- (void)mediaCacheDidChanged:(NSNotification *)notification { | ||
| 804 | - NSDictionary *userInfo = notification.userInfo; | ||
| 805 | - float progress = [userInfo[CacheProgressKey] floatValue]; | ||
| 806 | - if ((NSInteger)(progress * 10000) > 10000) { | ||
| 807 | - __weak typeof(self) weakSelf = self; | ||
| 808 | - dispatch_async(dispatch_get_main_queue(), ^{ | ||
| 809 | - __strong typeof(weakSelf) strongSelf = weakSelf; | ||
| 810 | - if(strongSelf){ | ||
| 811 | - [[CNLivePlayerDBTool shared] updateInTable:PlayerStringToKey(strongSelf->_contentId, strongSelf->_rate)]; | ||
| 812 | - } | ||
| 813 | - | ||
| 814 | - }); | ||
| 815 | - } | ||
| 816 | -} | ||
| 817 | - | ||
| 818 | - | ||
| 819 | -- (void)mediaCacheErrorDidChanged:(NSNotification *)notification { | ||
| 820 | - | ||
| 821 | -} | ||
| 822 | - | ||
| 823 | -- (void)receiveMessage:(NSNotification *)noti { | ||
| 824 | - | ||
| 825 | - if (!noti.userInfo[@"message"]) return; | ||
| 826 | - | ||
| 827 | - NSInteger newValue = [noti.userInfo[@"message"] intValue]; | ||
| 828 | - | ||
| 829 | - if (_hostStatus == newValue) return; | ||
| 830 | - | ||
| 831 | - if (newValue == 1) { | ||
| 832 | - _hostStatus = CNLiveHostStatusLiving; | ||
| 833 | - [_player play]; | ||
| 834 | - | ||
| 835 | - } else if (newValue == 2) { | ||
| 836 | - _hostStatus = CNLiveHostStatusEnd; | ||
| 837 | - [_player stop]; | ||
| 838 | 389 | ||
| 839 | - } else { | ||
| 840 | - _hostStatus = CNLiveHostStatusLeaving; | ||
| 841 | - [_player pause]; | ||
| 842 | - | ||
| 843 | - } | ||
| 844 | - | ||
| 845 | - if (PLAYER_TestEnvironment) { | ||
| 846 | - NSLog(@"CNLiveMoviePlayerController-->主播直播状态%lu",(unsigned long)_hostStatus); | ||
| 847 | - } | ||
| 848 | - | ||
| 849 | - //发送通知 | ||
| 850 | - dispatch_async(dispatch_get_main_queue(), ^{ | ||
| 851 | - [[NSNotificationCenter defaultCenter] postNotificationName:CNLiveHostStatusChangedNotification | ||
| 852 | - object:nil | ||
| 853 | - userInfo:nil]; | ||
| 854 | - }); | ||
| 855 | - | ||
| 856 | -} | ||
| 857 | - | ||
| 858 | -#pragma mark - 播放时长计算 | ||
| 859 | -- (void)statTimerAction{ | ||
| 860 | - if (!_contentType || _contentType.length == 0) { | ||
| 861 | - return; | ||
| 862 | } | 390 | } |
| 863 | - _playTime++; | ||
| 864 | - | ||
| 865 | - NSString *timestamp = [CNLivePlayerTools getTimestamp]; | ||
| 866 | - NSString *url = [NSString stringWithFormat:@"%@?sver=%@&uid=%@&tm=%@&from=apple&spid=&appid=%@&version=1001_%@&eventid=1001&uri=%@&sid=&plat=i_%@&objid=%@_%@", StatUrl, StatVersion, [CNLivePlayerTools getUidForTimestamp:timestamp], timestamp, PLAYER_AppID, PlayerSDKVersion, [NSString stringWithFormat:@"%ld_30", (long)_playTime], StatAppVersion,_contentId, _idName]; | ||
| 867 | - [NSURLConnection connectionWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:url]] delegate:nil]; | ||
| 868 | - | ||
| 869 | -} | ||
| 870 | - | ||
| 871 | -#pragma mark - 属性 | ||
| 872 | -- (NSURL *)contentURL { | ||
| 873 | - return _player.contentURL; | 391 | + return _option; |
| 874 | } | 392 | } |
| 875 | 393 | ||
| 876 | - (UIView *)view { | 394 | - (UIView *)view { |
| @@ -881,88 +399,436 @@ NSString * const CNLiveHostStatusChangedNotification = @"CNLiveAnchorStatusChang | @@ -881,88 +399,436 @@ NSString * const CNLiveHostStatusChangedNotification = @"CNLiveAnchorStatusChang | ||
| 881 | return _view; | 399 | return _view; |
| 882 | } | 400 | } |
| 883 | 401 | ||
| 884 | -- (void)setVideoDecoderMode:(CNLiveMovieVideoDecoderMode)videoDecoderMode{ | ||
| 885 | - _player.videoDecoderMode = (MPMovieVideoDecoderMode)videoDecoderMode; | 402 | +#pragma mark - Getter方法 |
| 403 | +- (NSURL *)URL { | ||
| 404 | + return _player.URL; | ||
| 886 | } | 405 | } |
| 887 | 406 | ||
| 888 | -- (void)setShouldMute:(BOOL)shouldMute { | ||
| 889 | - _player.shouldMute = shouldMute; | 407 | +- (CNLivePlayerStatus)status { |
| 408 | + CNLivePlayerStatus playerState; | ||
| 409 | + switch (_player.status) { | ||
| 410 | + case PLPlayerStatusUnknow: | ||
| 411 | + { | ||
| 412 | + playerState = CNLivePlayerStatusUnknow; | ||
| 413 | + } | ||
| 414 | + break; | ||
| 415 | + case PLPlayerStatusPreparing: | ||
| 416 | + { | ||
| 417 | + playerState = CNLivePlayerStatusPreparing; | ||
| 418 | + } | ||
| 419 | + break; | ||
| 420 | + case PLPlayerStatusReady: | ||
| 421 | + { | ||
| 422 | + playerState = CNLivePlayerStatusReady; | ||
| 423 | + } | ||
| 424 | + break; | ||
| 425 | + case PLPlayerStatusOpen: | ||
| 426 | + { | ||
| 427 | + playerState = CNLivePlayerStatusOpen; | ||
| 428 | + } | ||
| 429 | + break; | ||
| 430 | + | ||
| 431 | + case PLPlayerStatusCaching: | ||
| 432 | + { | ||
| 433 | + playerState = CNLivePlayerStatusCaching; | ||
| 434 | + } | ||
| 435 | + break; | ||
| 436 | + case PLPlayerStatusPlaying: | ||
| 437 | + { | ||
| 438 | + playerState = CNLivePlayerStatusPlaying; | ||
| 439 | + } | ||
| 440 | + break; | ||
| 441 | + case PLPlayerStatusPaused: | ||
| 442 | + { | ||
| 443 | + playerState = CNLivePlayerStatusPaused; | ||
| 444 | + } | ||
| 445 | + break; | ||
| 446 | + case PLPlayerStatusStopped: | ||
| 447 | + { | ||
| 448 | + playerState = CNLivePlayerStatusStopped; | ||
| 449 | + } | ||
| 450 | + break; | ||
| 451 | + case PLPlayerStatusError: | ||
| 452 | + { | ||
| 453 | + playerState = CNLivePlayerStatusError; | ||
| 454 | + } | ||
| 455 | + break; | ||
| 456 | + case PLPlayerStateAutoReconnecting: | ||
| 457 | + { | ||
| 458 | + playerState = CNLivePlayerStateAutoReconnecting; | ||
| 459 | + } | ||
| 460 | + break; | ||
| 461 | + case PLPlayerStatusCompleted: | ||
| 462 | + { | ||
| 463 | + playerState = CNLivePlayerStatusCompleted; | ||
| 464 | + } | ||
| 465 | + break; | ||
| 466 | + } | ||
| 467 | + return playerState; | ||
| 890 | } | 468 | } |
| 891 | 469 | ||
| 892 | -- (void)setShouldLoop:(BOOL)shouldLoop { | ||
| 893 | - _player.shouldLoop = shouldLoop; | 470 | +- (BOOL)playing { |
| 471 | + return _player.playing; | ||
| 894 | } | 472 | } |
| 895 | 473 | ||
| 896 | -- (void)setRotateDegress:(int)rotateDegress { | ||
| 897 | - _player.rotateDegress = rotateDegress; | 474 | +#pragma mark - Setter方法 |
| 475 | +- (void)setFrame:(CGRect)frame{ | ||
| 476 | + _frame = frame; | ||
| 477 | + if ((frame.size.width == 0.0)&&(frame.size.height == 0.0)) { | ||
| 478 | + frame = CGRectMake(frame.origin.x, frame.origin.y, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.width*9/16.0); | ||
| 479 | + } | ||
| 480 | + _view.frame = frame; | ||
| 481 | + _player.playerView.frame = frame; | ||
| 898 | } | 482 | } |
| 899 | - | ||
| 900 | -- (void)setBInterruptOtherAudio:(BOOL)bInterruptOtherAudio { | ||
| 901 | - _player.bInterruptOtherAudio = bInterruptOtherAudio; | 483 | +- (void)setDelegate:(id<CNLivePlayerDelegate>)delegate{ |
| 484 | + _delegate = delegate; | ||
| 485 | + _player.delegate = self; | ||
| 902 | } | 486 | } |
| 903 | 487 | ||
| 904 | -- (BOOL)isPreparedToPlay { | ||
| 905 | - return _player.isPreparedToPlay; | ||
| 906 | -} | 488 | +/* |
| 489 | +支持后台播放 | ||
| 907 | 490 | ||
| 908 | -- (NSTimeInterval)currentPlaybackTime{ | ||
| 909 | - return _player.currentPlaybackTime; | 491 | +@since v0.0.1 |
| 492 | +*/ | ||
| 493 | +- (void)setBackgroundPlayEnable:(BOOL)backgroundPlayEnable{ | ||
| 494 | + _player.backgroundPlayEnable = backgroundPlayEnable; | ||
| 910 | } | 495 | } |
| 911 | 496 | ||
| 912 | -- (void)setCurrentPlaybackTime:(NSTimeInterval)currentPlaybackTime{ | ||
| 913 | - _player.currentPlaybackTime = currentPlaybackTime; | 497 | +#pragma mark - 代理 |
| 498 | +/** | ||
| 499 | + 告知代理对象 PLPlayer 即将开始进入后台播放任务 | ||
| 500 | + | ||
| 501 | + @param player 调用该代理方法的 PLPlayer 对象 | ||
| 502 | + | ||
| 503 | + @since v1.0.0 | ||
| 504 | + */ | ||
| 505 | +- (void)playerWillBeginBackgroundTask:(nonnull PLPlayer *)player{ | ||
| 506 | + if (self.delegate && [self.delegate respondsToSelector:@selector(playerWillBeginBackgroundTask:)]) { | ||
| 507 | + [self.delegate playerWillBeginBackgroundTask:self]; | ||
| 508 | + } | ||
| 914 | } | 509 | } |
| 915 | 510 | ||
| 916 | -- (NSTimeInterval)duration{ | ||
| 917 | - return _player.duration; | 511 | +/** |
| 512 | + 告知代理对象 PLPlayer 即将结束后台播放状态任务 | ||
| 513 | + | ||
| 514 | + @param player 调用该方法的 PLPlayer 对象 | ||
| 515 | + | ||
| 516 | + @since v2.1.1 | ||
| 517 | + */ | ||
| 518 | +- (void)playerWillEndBackgroundTask:(nonnull PLPlayer *)player{ | ||
| 519 | + if (self.delegate && [self.delegate respondsToSelector:@selector(playerWillEndBackgroundTask:)]) { | ||
| 520 | + [self.delegate playerWillEndBackgroundTask:self]; | ||
| 521 | + } | ||
| 918 | } | 522 | } |
| 919 | 523 | ||
| 920 | -- (CGSize)naturalSize{ | ||
| 921 | - return _player.naturalSize; | 524 | +/** |
| 525 | + 告知代理对象播放器状态变更 | ||
| 526 | + | ||
| 527 | + @param player 调用该方法的 PLPlayer 对象 | ||
| 528 | + @param state 变更之后的 PLPlayer 状态 | ||
| 529 | + | ||
| 530 | + @since v1.0.0 | ||
| 531 | + */ | ||
| 532 | +- (void)player:(nonnull PLPlayer *)player statusDidChange:(PLPlayerStatus)state{ | ||
| 533 | + CNLivePlayerStatus playerState; | ||
| 534 | + switch (state) { | ||
| 535 | + case PLPlayerStatusUnknow: | ||
| 536 | + { | ||
| 537 | + playerState = CNLivePlayerStatusUnknow; | ||
| 538 | + } | ||
| 539 | + break; | ||
| 540 | + case PLPlayerStatusPreparing: | ||
| 541 | + { | ||
| 542 | + playerState = CNLivePlayerStatusPreparing; | ||
| 543 | + } | ||
| 544 | + break; | ||
| 545 | + case PLPlayerStatusReady: | ||
| 546 | + { | ||
| 547 | + playerState = CNLivePlayerStatusReady; | ||
| 548 | + } | ||
| 549 | + break; | ||
| 550 | + case PLPlayerStatusOpen: | ||
| 551 | + { | ||
| 552 | + playerState = CNLivePlayerStatusOpen; | ||
| 553 | + } | ||
| 554 | + break; | ||
| 555 | + | ||
| 556 | + case PLPlayerStatusCaching: | ||
| 557 | + { | ||
| 558 | + playerState = CNLivePlayerStatusCaching; | ||
| 559 | + } | ||
| 560 | + break; | ||
| 561 | + case PLPlayerStatusPlaying: | ||
| 562 | + { | ||
| 563 | + if(player == self.switchPlayer&&_switchRate){ | ||
| 564 | + [self.switchPlayer seekTo:_switchRateTime]; | ||
| 565 | + } | ||
| 566 | + playerState = CNLivePlayerStatusPlaying; | ||
| 567 | + } | ||
| 568 | + break; | ||
| 569 | + case PLPlayerStatusPaused: | ||
| 570 | + { | ||
| 571 | + playerState = CNLivePlayerStatusPaused; | ||
| 572 | + } | ||
| 573 | + break; | ||
| 574 | + case PLPlayerStatusStopped: | ||
| 575 | + { | ||
| 576 | + playerState = CNLivePlayerStatusStopped; | ||
| 577 | + } | ||
| 578 | + break; | ||
| 579 | + case PLPlayerStatusError: | ||
| 580 | + { | ||
| 581 | + if(player == self.switchPlayer&&_switchRate){ | ||
| 582 | + if (self.delegate && [self.delegate respondsToSelector:@selector(player:switchRateCompleted:)]) { | ||
| 583 | + [self.delegate player:self switchRateCompleted:NO]; | ||
| 584 | + } | ||
| 585 | + _switchRate = NO; | ||
| 586 | + } | ||
| 587 | + playerState = CNLivePlayerStatusError; | ||
| 588 | + } | ||
| 589 | + break; | ||
| 590 | + case PLPlayerStateAutoReconnecting: | ||
| 591 | + { | ||
| 592 | + playerState = CNLivePlayerStateAutoReconnecting; | ||
| 593 | + } | ||
| 594 | + break; | ||
| 595 | + case PLPlayerStatusCompleted: | ||
| 596 | + { | ||
| 597 | + playerState = CNLivePlayerStatusCompleted; | ||
| 598 | + } | ||
| 599 | + break; | ||
| 600 | + } | ||
| 601 | + | ||
| 602 | + if (self.delegate && [self.delegate respondsToSelector:@selector(player:statusDidChange:)]) { | ||
| 603 | + [self.delegate player:self statusDidChange:playerState]; | ||
| 604 | + } | ||
| 922 | } | 605 | } |
| 923 | 606 | ||
| 924 | -- (NSInteger)naturalRotate{ | ||
| 925 | - return _player.naturalRotate; | 607 | +/** |
| 608 | + 告知代理对象播放器因错误停止播放 | ||
| 609 | + | ||
| 610 | + @param player 调用该方法的 PLPlayer 对象 | ||
| 611 | + @param error 携带播放器停止播放错误信息的 NSError 对象 | ||
| 612 | + | ||
| 613 | + @since v1.0.0 | ||
| 614 | + */ | ||
| 615 | +- (void)player:(nonnull PLPlayer *)player stoppedWithError:(nullable NSError *)error{ | ||
| 616 | + if (self.delegate && [self.delegate respondsToSelector:@selector(player:stoppedWithError:)]) { | ||
| 617 | + [self.delegate player:self stoppedWithError:error]; | ||
| 618 | + } | ||
| 926 | } | 619 | } |
| 927 | 620 | ||
| 928 | -- (MPMoviePlaybackState)playbackState{ | ||
| 929 | - return _player.playbackState; | 621 | +/** |
| 622 | + 点播已缓冲区域 | ||
| 623 | + | ||
| 624 | + @param player 调用该方法的 PLPlayer 对象 | ||
| 625 | + @param timeRange CMTime , 表示从0时开始至当前缓冲区域,单位秒。 | ||
| 626 | + | ||
| 627 | + @warning 仅对点播有效 | ||
| 628 | + | ||
| 629 | + @since v2.4.1 | ||
| 630 | + */ | ||
| 631 | +- (void)player:(nonnull PLPlayer *)player loadedTimeRange:(CMTime)timeRange{ | ||
| 632 | + if (self.delegate && [self.delegate respondsToSelector:@selector(player:loadedTimeRange:)]) { | ||
| 633 | + [self.delegate player:self loadedTimeRange:timeRange]; | ||
| 634 | + } | ||
| 930 | } | 635 | } |
| 931 | 636 | ||
| 932 | -- (MPMovieLoadState)loadState{ | ||
| 933 | - return _player.loadState; | 637 | +/** |
| 638 | +回调将要渲染的帧数据 | ||
| 639 | +该功能只支持直播 | ||
| 640 | + | ||
| 641 | +@param player 调用该方法的 PLPlayer 对象 | ||
| 642 | +@param frame 将要渲染帧 YUV 数据。 | ||
| 643 | +CVPixelBufferGetPixelFormatType 获取 YUV 的类型。 | ||
| 644 | +软解为 kCVPixelFormatType_420YpCbCr8Planar. | ||
| 645 | +硬解为 kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange. | ||
| 646 | +@param pts 显示时间戳 单位ms | ||
| 647 | +@param sarNumerator 分子 | ||
| 648 | +@param sarDenominator 分母 | ||
| 649 | +其中sar 表示 storage aspect ratio | ||
| 650 | +视频流的显示比例 sarNumerator sarDenominator | ||
| 651 | +@discussion sarNumerator = 0 表示该参数无效 | ||
| 652 | + | ||
| 653 | +@since v2.4.3 | ||
| 654 | +*/ | ||
| 655 | +- (void)player:(nonnull PLPlayer *)player willRenderFrame:(nullable CVPixelBufferRef)frame pts:(int64_t)pts sarNumerator:(int)sarNumerator sarDenominator:(int)sarDenominator{ | ||
| 656 | + if (self.delegate && [self.delegate respondsToSelector:@selector(player:willRenderFrame:pts:sarNumerator:sarDenominator:)]) { | ||
| 657 | + [self.delegate player:self willRenderFrame:frame pts:pts sarNumerator:sarNumerator sarDenominator:sarDenominator]; | ||
| 658 | + } | ||
| 934 | } | 659 | } |
| 935 | 660 | ||
| 936 | - | ||
| 937 | -#pragma mark - 方法 | ||
| 938 | -- (void)prepareToPlay { | ||
| 939 | - [_player prepareToPlay]; | 661 | +/** |
| 662 | + 回调音频数据 | ||
| 663 | + | ||
| 664 | + @param player 调用该方法的 PLPlayer 对象 | ||
| 665 | + @param audioBufferList 音频数据 | ||
| 666 | + @param audioStreamDescription 音频格式信息 | ||
| 667 | + @param pts 显示时间戳 是解码器进行显示帧时相对于SCR(系统参考)的时间戳。SCR可以理解为解码器应该开始从磁盘读取数据时的时间 | ||
| 668 | + @param sampleFormat 采样位数 枚举:PLPlayerAVSampleFormat | ||
| 669 | + @return audioBufferList 音频数据 | ||
| 670 | + | ||
| 671 | + @since v2.4.3 | ||
| 672 | + */ | ||
| 673 | +- (nonnull AudioBufferList *)player:(nonnull PLPlayer *)player willAudioRenderBuffer:(nonnull AudioBufferList *)audioBufferList asbd:(AudioStreamBasicDescription)audioStreamDescription pts:(int64_t)pts sampleFormat:(PLPlayerAVSampleFormat)sampleFormat{ | ||
| 674 | + CNLivePlayerAVSampleFormat format; | ||
| 675 | + switch (sampleFormat) { | ||
| 676 | + case PLPlayerAV_SAMPLE_FMT_NONE: | ||
| 677 | + { | ||
| 678 | + format = CNLivePlayerAV_SAMPLE_FMT_NONE; | ||
| 679 | + } | ||
| 680 | + break; | ||
| 681 | + case PLPlayerAV_SAMPLE_FMT_U8: | ||
| 682 | + { | ||
| 683 | + format = CNLivePlayerAV_SAMPLE_FMT_U8; | ||
| 684 | + } | ||
| 685 | + break; | ||
| 686 | + case PLPlayerAV_SAMPLE_FMT_S16: | ||
| 687 | + { | ||
| 688 | + format = CNLivePlayerAV_SAMPLE_FMT_S16; | ||
| 689 | + } | ||
| 690 | + break; | ||
| 691 | + case PLPlayerAV_SAMPLE_FMT_S32: | ||
| 692 | + { | ||
| 693 | + format = CNLivePlayerAV_SAMPLE_FMT_S32; | ||
| 694 | + } | ||
| 695 | + break; | ||
| 696 | + case PLPlayerAV_SAMPLE_FMT_FLT: | ||
| 697 | + { | ||
| 698 | + format = CNLivePlayerAV_SAMPLE_FMT_FLT; | ||
| 699 | + } | ||
| 700 | + break; | ||
| 701 | + case PLPlayerAV_SAMPLE_FMT_DBL: | ||
| 702 | + { | ||
| 703 | + format = CNLivePlayerAV_SAMPLE_FMT_DBL; | ||
| 704 | + } | ||
| 705 | + break; | ||
| 706 | + case PLPlayerAV_SAMPLE_FMT_U8P: | ||
| 707 | + { | ||
| 708 | + format = CNLivePlayerAV_SAMPLE_FMT_U8P; | ||
| 709 | + } | ||
| 710 | + break; | ||
| 711 | + case PLPlayerAV_SAMPLE_FMT_S16P: | ||
| 712 | + { | ||
| 713 | + format = CNLivePlayerAV_SAMPLE_FMT_S16P; | ||
| 714 | + } | ||
| 715 | + break; | ||
| 716 | + case PLPlayerAV_SAMPLE_FMT_S32P: | ||
| 717 | + { | ||
| 718 | + format = CNLivePlayerAV_SAMPLE_FMT_S32P; | ||
| 719 | + } | ||
| 720 | + break; | ||
| 721 | + case PLPlayerAV_SAMPLE_FMT_FLTP: | ||
| 722 | + { | ||
| 723 | + format = CNLivePlayerAV_SAMPLE_FMT_FLTP; | ||
| 724 | + } | ||
| 725 | + break; | ||
| 726 | + case PLPlayerAV_SAMPLE_FMT_DBLP: | ||
| 727 | + { | ||
| 728 | + format = CNLivePlayerAV_SAMPLE_FMT_DBLP; | ||
| 729 | + } | ||
| 730 | + break; | ||
| 731 | + case PLPlayerAV_SAMPLE_FMT_NB: | ||
| 732 | + { | ||
| 733 | + format = CNLivePlayerAV_SAMPLE_FMT_NB; | ||
| 734 | + } | ||
| 735 | + break; | ||
| 736 | + } | ||
| 737 | + if (self.delegate && [self.delegate respondsToSelector:@selector(player:willAudioRenderBuffer:asbd:pts:sampleFormat:)]) { | ||
| 738 | + return [self.delegate player:self willAudioRenderBuffer:audioBufferList asbd:audioStreamDescription pts:pts sampleFormat:format]; | ||
| 739 | + } | ||
| 740 | + return nil; | ||
| 940 | } | 741 | } |
| 941 | 742 | ||
| 942 | -- (void)play{ | ||
| 943 | - [_player play]; | 743 | +/** |
| 744 | + 回调 SEI 数据 | ||
| 745 | + | ||
| 746 | + @param player 调用该方法的 PLPlayer 对象 | ||
| 747 | + @param SEIData SEI数据 | ||
| 748 | + @param ts 含有SEI数据的视频帧对应的时间戳 | ||
| 749 | + @since v3.4.0 | ||
| 750 | + */ | ||
| 751 | +- (void)player:(nonnull PLPlayer *)player SEIData:(nullable NSData *)SEIData ts:(int64_t)ts{ | ||
| 752 | + if (self.delegate && [self.delegate respondsToSelector:@selector(player:SEIData:ts:)]) { | ||
| 753 | + [self.delegate player:self SEIData:SEIData ts:ts]; | ||
| 754 | + } | ||
| 944 | } | 755 | } |
| 945 | 756 | ||
| 946 | -- (BOOL)isPlaying{ | ||
| 947 | - return _player.isPlaying; | 757 | +/** |
| 758 | + 音视频渲染首帧回调通知 | ||
| 759 | + | ||
| 760 | + @param player 调用该方法的 PLPlayer 对象 | ||
| 761 | + @param firstRenderType 音视频首帧回调通知类型 | ||
| 762 | + | ||
| 763 | + @since v3.2.1 | ||
| 764 | + */ | ||
| 765 | +- (void)player:(nonnull PLPlayer *)player firstRender:(PLPlayerFirstRenderType)firstRenderType{ | ||
| 766 | + CNLivePlayerFirstRenderType renderType; | ||
| 767 | + switch (firstRenderType) { | ||
| 768 | + case PLPlayerFirstRenderTypeVideo: | ||
| 769 | + { | ||
| 770 | + renderType = CNLivePlayerFirstRenderTypeVideo; | ||
| 771 | + } | ||
| 772 | + break; | ||
| 773 | + case PLPlayerFirstRenderTypeAudio: | ||
| 774 | + { | ||
| 775 | + renderType = CNLivePlayerFirstRenderTypeAudio; | ||
| 776 | + } | ||
| 777 | + break; | ||
| 778 | + } | ||
| 779 | + if (self.delegate && [self.delegate respondsToSelector:@selector(player:firstRender:)]) { | ||
| 780 | + [self.delegate player:self firstRender:renderType]; | ||
| 781 | + } | ||
| 948 | } | 782 | } |
| 949 | 783 | ||
| 950 | -- (void)pause{ | ||
| 951 | - [_player reset:YES]; | ||
| 952 | - [_player pause]; | ||
| 953 | -} | 784 | +/** |
| 785 | + 视频宽高数据回调通知 | ||
| 954 | 786 | ||
| 955 | -- (void)stop{ | ||
| 956 | - [_player reset:NO]; | ||
| 957 | - [_player stop]; | 787 | + @param player 调用该方法的 PLPlayer 对象 |
| 788 | + @param width 视频流宽 | ||
| 789 | + @param height 视频流高 | ||
| 790 | + | ||
| 791 | + @since v3.3.0 | ||
| 792 | + */ | ||
| 793 | +- (void)player:(nonnull PLPlayer *)player width:(int)width height:(int)height{ | ||
| 794 | + if (self.delegate && [self.delegate respondsToSelector:@selector(player:width:height:)]) { | ||
| 795 | + [self.delegate player:self width:width height:height]; | ||
| 796 | + } | ||
| 958 | } | 797 | } |
| 959 | 798 | ||
| 960 | -- (UIImage *)thumbnailImageAtCurrentTime { | ||
| 961 | - return _player.thumbnailImageAtCurrentTime; | ||
| 962 | -} | 799 | +/** |
| 800 | + seekTo 完成的回调通知 | ||
| 801 | + | ||
| 802 | + @param player 调用该方法的 PLPlayer 对象 | ||
| 803 | + | ||
| 804 | + @since v3.3.0 | ||
| 805 | + */ | ||
| 806 | +- (void)player:(nonnull PLPlayer *)player seekToCompleted:(BOOL)isCompleted{ | ||
| 807 | + if (_switchRate) { | ||
| 808 | + if (isCompleted) { | ||
| 809 | + PLPlayer *temp = self.player; | ||
| 810 | + self.player = self.switchPlayer; | ||
| 811 | + self.switchPlayer = temp; | ||
| 812 | + [self.switchPlayer.playerView removeFromSuperview]; | ||
| 813 | + self.switchPlayer = nil; | ||
| 814 | + // 切换码率完成 | ||
| 815 | + if (self.delegate && [self.delegate respondsToSelector:@selector(player:switchRateCompleted:)]) { | ||
| 816 | + [self.delegate player:self switchRateCompleted:YES]; | ||
| 817 | + } | ||
| 818 | + }else{ | ||
| 819 | + // 切换码率失败 | ||
| 820 | + if (self.delegate && [self.delegate respondsToSelector:@selector(player:switchRateCompleted:)]) { | ||
| 821 | + [self.delegate player:self switchRateCompleted:NO]; | ||
| 822 | + } | ||
| 823 | + } | ||
| 824 | + _switchRate = NO; | ||
| 825 | + return; | ||
| 963 | 826 | ||
| 964 | -- (void)reload:(NSURL *)aUrl { | ||
| 965 | - [_player reload:aUrl]; | 827 | + } |
| 828 | + if (self.delegate && [self.delegate respondsToSelector:@selector(player:seekToCompleted:)]) { | ||
| 829 | + [self.delegate player:self seekToCompleted:isCompleted]; | ||
| 830 | + } | ||
| 966 | } | 831 | } |
| 967 | 832 | ||
| 833 | + | ||
| 968 | @end | 834 | @end |