Commit 0502f728b80d4818f0d1c5bd341330baf83019d5
1 parent
197ebef1
no message
Showing
48 changed files
with
2328 additions
and
646 deletions
CNLivePlayerSDKTest/CNLiveMsgTools.framework/CNLiveMsgTools
0 → 100755
No preview for this file type
CNLivePlayerSDKTest/CNLiveMsgTools.framework/Emoji.plist
0 → 100644
No preview for this file type
CNLivePlayerSDKTest/CNLiveMsgTools.framework/Headers/CNLiveMsgManager.h
0 → 100644
| 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 | + | |
| 14 | ++ (NSString *)getVersion; | |
| 15 | + | |
| 16 | + | |
| 17 | ++ (instancetype)sharedCNLiveMsgManager; | |
| 18 | + | |
| 19 | + | |
| 20 | +- (void)setToolsInfoWithInfoId:(NSString *)infoId | |
| 21 | + infoName:(NSString *)infoName | |
| 22 | + infoUrl:(NSString *)infoUrl; | |
| 23 | + | |
| 24 | + | |
| 25 | +- (NSDictionary *)getToolsInfo; | |
| 26 | + | |
| 27 | + | |
| 28 | +- (void)initCNLiveMsgToolsWithAppKey:(NSString *)appKey appId:(NSString *)appId; | |
| 29 | + | |
| 30 | + | |
| 31 | +- (void)onLineWithMsgToolsInfoId:(NSString *)infoId | |
| 32 | + infoName:(NSString *)infoName | |
| 33 | + infoUrl:(NSString *)infoUrl | |
| 34 | + success:(void (^)(NSString *toolsId))successBlock | |
| 35 | + error:(void (^)(NSInteger errors))errorBlock; | |
| 36 | + | |
| 37 | + | |
| 38 | +- (void)justOnLineSuccess:(void (^)(NSString *toolsId))successBlock | |
| 39 | + error:(void (^)(NSInteger errors))errorBlock; | |
| 40 | + | |
| 41 | + | |
| 42 | +- (void)stealthCNLiveMsgTools; | |
| 43 | + | |
| 44 | + | |
| 45 | +- (void)offLineCNLiveMsgTools:(BOOL)isReceivePush; | |
| 46 | + | |
| 47 | + | |
| 48 | +- (void)gatherTogether:(NSString *)targetId | |
| 49 | + messageCount:(int)messageCount | |
| 50 | + success:(void (^)(NSString *targetId))successBlock | |
| 51 | + error:(void (^)(NSInteger errors, NSString *targetId))errorBlock; | |
| 52 | + | |
| 53 | + | |
| 54 | +- (void)gatherIfNullCreate:(NSString *)targetId | |
| 55 | + messageCount:(int)messageCount | |
| 56 | + success:(void (^)(NSString *targetId))successBlock | |
| 57 | + error:(void (^)(NSInteger errors, NSString *targetId))errorBlock; | |
| 58 | + | |
| 59 | + | |
| 60 | +- (void)gatherTogether:(NSString *)targetId | |
| 61 | + isCreate:(BOOL)isCreate | |
| 62 | + messageCount:(int)messageCount | |
| 63 | + success:(void (^)(NSString *targetId))successBlock | |
| 64 | + error:(void (^)(NSInteger errors, NSString *targetId))errorBlock; | |
| 65 | + | |
| 66 | + | |
| 67 | +- (void)disperse:(NSString *)targetId | |
| 68 | + success:(void (^)(NSString *targetId))successBlock | |
| 69 | + error:(void (^)(NSInteger errors, NSString *targetId))errorBlock; | |
| 70 | + | |
| 71 | + | |
| 72 | +- (void)sendCarrier:(NSString *)carrier | |
| 73 | + type:(NSInteger)type | |
| 74 | + targetId:(NSString *)targetId | |
| 75 | + success:(void (^)(NSString *carrier, NSString *targetId, NSInteger type, NSString *infoId, NSString *infoName, NSString *infoUrl))successBlock | |
| 76 | + error:(void (^)(NSInteger errors, NSString *targetId, NSInteger type))errorBlock; | |
| 77 | + | |
| 78 | + | |
| 79 | +- (NSInteger)getOnLineStatus; | |
| 80 | + | |
| 81 | + | |
| 82 | +- (BOOL)judgeLoginStatus; | |
| 83 | + | |
| 84 | + | |
| 85 | +- (NSArray *)getLatestCarriers:(NSInteger)type targetId:(NSString *)targetId count:(int)count; | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | +@end | ... | ... |
CNLivePlayerSDKTest/CNLiveMsgTools.framework/Info.plist
0 → 100644
No preview for this file type
CNLivePlayerSDKTest/CNLiveMsgTools.framework/RCConfig.plist
0 → 100644
No preview for this file type
CNLivePlayerSDKTest/CNLiveMsgTools.framework/en.lproj/RongCloudKit.strings
0 → 100644
No preview for this file type
CNLivePlayerSDKTest/CNLiveMsgTools.framework/release_notes_ios.txt
0 → 100755
| 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 的内置支持。 | ... | ... |
CNLivePlayerSDKTest/CNLiveMsgTools.framework/zh-Hans.lproj/RongCloudKit.strings
0 → 100644
No preview for this file type
CNLivePlayerSDKTest/CNLivePlayerSDK.framework/CNLivePlayerSDK
No preview for this file type
CNLivePlayerSDKTest/CNLivePlayerSDK.framework/Headers/CNLiveAudioCapture.h
0 → 100644
| 1 | +// | |
| 2 | +// CNLiveAudioCapture.h | |
| 3 | +// CNLivePlayerSDKDemo | |
| 4 | +// | |
| 5 | +// Created by iOS on 17/2/27. | |
| 6 | +// Copyright © 2017年 雷浩杰. All rights reserved. | |
| 7 | +// | |
| 8 | + | |
| 9 | +#import <Foundation/Foundation.h> | |
| 10 | + | |
| 11 | +@interface CNLiveAudioCapture : NSObject | |
| 12 | + | |
| 13 | +/** | |
| 14 | + @abstract 是否有耳机麦克风可用 | |
| 15 | + @return 是/否有耳机麦克风 | |
| 16 | + */ | |
| 17 | ++ (BOOL)isHeadsetPluggedIn; | |
| 18 | + | |
| 19 | +/** | |
| 20 | + @abstract 是否播放采集的声音 (又称"耳返") | |
| 21 | + @warning 如果在没有插入耳机的情况下启动, 容易出现很刺耳的声音 | |
| 22 | + */ | |
| 23 | +@property(nonatomic, assign) BOOL bPlayCapturedAudio; | |
| 24 | + | |
| 25 | +/** | |
| 26 | + @abstract 设置mic采集的声音音量 | |
| 27 | + @discussion 调整范围 0.0~1.0 | |
| 28 | + */ | |
| 29 | +@property(nonatomic, assign) Float32 micVolume; | |
| 30 | + | |
| 31 | +/** | |
| 32 | + @abstract 混响类型 | |
| 33 | + @discussion 目前提供了4种类型的混响场景, type和场景的对应关系如下 | |
| 34 | + | |
| 35 | +- 0 关闭 | |
| 36 | +- 1 录音棚 | |
| 37 | +- 2 KTV | |
| 38 | +- 3 小舞台 | |
| 39 | +- 4 演唱会 | |
| 40 | +*/ | |
| 41 | +@property(nonatomic, assign) int reverbType; | |
| 42 | + | |
| 43 | +@end | ... | ... |
CNLivePlayerSDKTest/CNLivePlayerSDK.framework/Headers/CNLiveBeautifyFaceFilter.h
0 → 100644
| 1 | +// | |
| 2 | +// CNLiveBeautifyFaceFilter.h | |
| 3 | +// CNLivePlayerSDKDemo | |
| 4 | +// | |
| 5 | +// Created by iOS on 17/3/2. | |
| 6 | +// Copyright © 2017年 雷浩杰. All rights reserved. | |
| 7 | +// | |
| 8 | + | |
| 9 | +#import <Foundation/Foundation.h> | |
| 10 | +#import "CNLiveTypeDef.h" | |
| 11 | +#import <UIKit/UIKit.h> | |
| 12 | + | |
| 13 | +/** | |
| 14 | + * 美颜((默认旧美颜,参数为0.5)) | |
| 15 | + */ | |
| 16 | + | |
| 17 | +@interface CNLiveBeautifyFaceFilter : NSObject | |
| 18 | + | |
| 19 | +/** | |
| 20 | + @abstract CNLiveBeautyTypeSpecial 美颜特效类型,需先设置CNLiveBeautyType为CNLiveBeautyTypeSpecial | |
| 21 | + */ | |
| 22 | +@property (nonatomic, assign) CNLiveSpecialEffects curEffectIdx; //美颜类型 | |
| 23 | + | |
| 24 | +/** | |
| 25 | + @abstract 美颜类型 | |
| 26 | + @param beautyType 类型 | |
| 27 | + */ | |
| 28 | +- (void)setBeautyType:(CNLiveBeautyType)beautyType; | |
| 29 | + | |
| 30 | +/** | |
| 31 | + @abstract 美颜参数调节 | |
| 32 | + @param grindRatio 磨皮 | |
| 33 | + @param whitenRatio 美白 | |
| 34 | + @param intensity 特效 | |
| 35 | + */ | |
| 36 | +- (void)filterAdjustingParameterWithGrindRatio:(CGFloat)grindRatio | |
| 37 | + whitenRatio:(CGFloat)whitenRatio | |
| 38 | + intensity:(CGFloat)intensity; | |
| 39 | + | |
| 40 | + | |
| 41 | +@end | ... | ... |
CNLivePlayerSDKTest/CNLivePlayerSDK.framework/Headers/CNLiveGPUStreamerKit.h
| ... | ... | @@ -7,34 +7,21 @@ |
| 7 | 7 | // |
| 8 | 8 | |
| 9 | 9 | #import <Foundation/Foundation.h> |
| 10 | -#import <AVFoundation/AVFoundation.h> | |
| 11 | -#import <UIKit/UIKit.h> | |
| 12 | -#import "CNLiveTypeDef.h" | |
| 10 | +#import "CNLiveWebcastManager.h" | |
| 13 | 11 | |
| 14 | 12 | @interface CNLiveGPUStreamerKit : NSObject |
| 15 | 13 | |
| 16 | -/** | |
| 17 | -@abstract 获取SDK版本号 | |
| 18 | -*/ | |
| 19 | -+ (NSString *)getVersion; | |
| 20 | - | |
| 21 | 14 | #pragma mark - 使用视讯云平台 |
| 22 | 15 | |
| 23 | 16 | /** |
| 24 | 17 | @abstract 初始化 |
| 25 | 18 | @warning 实例化的对象必须赋值给一个全局变量 |
| 26 | - @param appId 应用ID | |
| 27 | - @param appKey 注册后的应用key | |
| 28 | 19 | @param activityId 活动ID,确保SP下的唯一性 |
| 29 | 20 | @param channelId 主播ID |
| 30 | 21 | @param isHorizontalScreen 屏幕方向,0:垂直即竖屏 /1:水平即横屏 ,默认为1 |
| 31 | - @param success 用于返回初始化状态(成功) | |
| 32 | - @param failure 用于返回初始化状态(失败) | |
| 33 | 22 | @warning 必传参数 |
| 34 | 23 | */ |
| 35 | --(instancetype)initWithDefaultConfigWithAppId:(NSString *)appId | |
| 36 | - appKey:(NSString *)appKey | |
| 37 | - activityId:(NSString *)activityId | |
| 24 | +-(instancetype)initWithDefaultConfigWithActivityId:(NSString *)activityId | |
| 38 | 25 | channelId:(NSString *)channelId |
| 39 | 26 | isHorizontalScreen:(NSString *)isHorizontalScreen; |
| 40 | 27 | |
| ... | ... | @@ -99,81 +86,105 @@ |
| 99 | 86 | -(instancetype)initWithDefaultConfig; |
| 100 | 87 | |
| 101 | 88 | /** |
| 102 | - @abstract 视频分辨率 | |
| 103 | - @discussion width x height ( 此处width始终大于高度,是否竖屏取决于videoOrientation的值 ) | |
| 89 | + @abstract 获取水印工具 | |
| 90 | + @discussion 通过它来设置水印图片/文字 | |
| 104 | 91 | */ |
| 105 | -@property (nonatomic, assign) CNLiveVideoDimension videoDimension; | |
| 92 | +@property (nonatomic, readonly) CNLiveWatermark *watermark; | |
| 106 | 93 | |
| 107 | 94 | /** |
| 108 | - @abstract 用户定义的视频分辨率 | |
| 109 | - @discussion 当videoDimension 设置为 CNLiveVideoDimension_UserDefine_* 时有效 | |
| 110 | - @discussion 内部始终将较大的值作为宽度 (若需要竖屏,请设置 videoOrientation) | |
| 111 | - @discussion 宽高都会向上取整为4的整数倍 | |
| 112 | - @discussion 宽度有效范围[160, 1280] | |
| 113 | - @discussion 高度有效范围[ 90, 720] | |
| 95 | + @abstract 获取底层推流工具 | |
| 96 | + @discussion 通过它来设置推流参数 | |
| 114 | 97 | */ |
| 115 | -@property (nonatomic, assign) CGSize videoDimensionUserDefine; | |
| 98 | +@property (nonatomic, readonly) CNLiveStreamerBase *streamerBase; | |
| 116 | 99 | |
| 117 | 100 | /** |
| 118 | - @abstract 视频编码器 | |
| 119 | - @discussion video codec used for encode | |
| 101 | + @abstract 获取音频采集工具 | |
| 120 | 102 | */ |
| 121 | -@property (nonatomic, assign) CNLiveVideoCodec videoCodec; | |
| 103 | +@property (nonatomic, readonly) CNLiveAudioCapture *audioCapture; | |
| 122 | 104 | |
| 123 | 105 | /** |
| 124 | - @abstract 视频编码起始码率(单位:kbps) | |
| 125 | - @discussion 开始推流时的视频码率,开始推流后,根据网络情况在 [Min, Max]范围内调节 | |
| 126 | - @discussion 视频码率上调则画面更清晰,下调则画面更模糊 | |
| 106 | + @abstract 获取美颜工具 | |
| 127 | 107 | */ |
| 128 | -@property (nonatomic, assign) int videoInitBitrate; | |
| 108 | +@property (nonatomic, readonly) CNLiveBeautifyFaceFilter *beautifyFaceFilter; | |
| 109 | + | |
| 110 | +#pragma mark - capture & preview settings | |
| 129 | 111 | |
| 130 | 112 | /** |
| 131 | - @abstract 视频编码最高码率(单位:kbps) | |
| 132 | - @discussion 视频码率自适应调整的上限, 为目标码率 | |
| 113 | + @abstract 采集分辨率 (仅在开始采集前设置有效) | |
| 114 | + @discussion 参见iOS的 AVCaptureSessionPresetXXX的定义 默认值为AVCaptureSessionPreset640x480 | |
| 115 | + @discussion 不同设备支持的预设分辨率可能不同, 请尽量与预览分辨率一致 | |
| 133 | 116 | */ |
| 134 | -@property (nonatomic, assign) int videoMaxBitrate; | |
| 117 | +@property (nonatomic, assign) NSString * capPreset; | |
| 135 | 118 | |
| 136 | 119 | /** |
| 137 | - @abstract 视频编码最低码率(单位:kbps) | |
| 138 | - @discussion 视频码率自适应调整的下限 | |
| 120 | + @abstract 查询实际的采集分辨率 | |
| 121 | + @discussion 参见iOS的 AVCaptureSessionPresetXXX的定义 | |
| 139 | 122 | */ |
| 140 | -@property (nonatomic, assign) int videoMinBitrate; | |
| 123 | +- (CGSize) captureDimension; | |
| 141 | 124 | |
| 142 | 125 | /** |
| 143 | - @abstract 最大关键帧间隔(单位:秒, 默认:3) | |
| 144 | - @discussion 即GOP长度 画面静止时,隔n秒插入一个关键帧 | |
| 126 | + @abstract 预览分辨率 (仅在开始采集前设置有效) | |
| 127 | + @discussion 内部始终将较大的值作为宽度 (若需要竖屏,请设置 videoOrientation) | |
| 128 | + @discussion 宽高都会向上取整为4的整数倍 | |
| 129 | + @discussion 有效范围: 宽度[160, 1920] 高度[ 90, 1080], 超出范围会取边界有效值 | |
| 130 | + @discussion 当预览分辨率与采集分辨率不一致时: | |
| 131 | + 若宽高比不同, 先进行裁剪, 再进行缩放 | |
| 132 | + 若宽高比相同, 直接进行缩放 | |
| 133 | + @discussion 默认值为(640, 360) | |
| 145 | 134 | */ |
| 146 | -@property (nonatomic, assign) int maxKeyInterval; | |
| 135 | +@property (nonatomic, assign) CGSize previewDimension; | |
| 147 | 136 | |
| 148 | 137 | /** |
| 149 | - @abstract 音频编码码率(单位:kbps) | |
| 150 | - @discussion 音频目标编码码率 (比如48,96,128等) | |
| 138 | + @abstract 用户定义的视频 **推流** 分辨率 | |
| 139 | + @discussion 有效范围: 宽度[160, 1280] 高度[ 90, 720], 超出范围会取边界有效值 | |
| 140 | + @discussion 其他与previewDimension限定一致, | |
| 141 | + @discussion 当与previewDimension不一致时, 同样先裁剪到相同宽高比, 再进行缩放 | |
| 142 | + @discussion 默认值为(640, 360) | |
| 143 | + @see previewDimension | |
| 151 | 144 | */ |
| 152 | -@property (nonatomic, assign) int audiokBPS; | |
| 145 | +@property (nonatomic, assign) CGSize streamDimension; | |
| 146 | + | |
| 147 | +/** | |
| 148 | + @abstract 采集及编码视频帧率 (开始采集前设置有效) | |
| 149 | + @discussion video frame per seconds 有效范围[1~30], 超出范围会取边界有效值 | |
| 150 | + @discussion 默认值为15 */ | |
| 151 | +@property (nonatomic, assign) int videoFPS; | |
| 153 | 152 | |
| 154 | 153 | /** |
| 155 | - @abstract 启用自动调整码率 | |
| 156 | - @discussion 默认为关闭自动调整码率,开始预览前设置有效 | |
| 154 | + @abstract 摄像头位置 | |
| 155 | + @discussion 前后摄像头 默认为前摄像头 | |
| 157 | 156 | */ |
| 158 | -@property (nonatomic, assign) BOOL enAutoApplyEstimateBW; | |
| 157 | +@property (nonatomic, assign) AVCaptureDevicePosition cameraPosition; | |
| 158 | + | |
| 159 | 159 | |
| 160 | 160 | /** |
| 161 | - @abstract 视频帧率 | |
| 162 | - @discussion video frame per seconds 有效范围[15~30] | |
| 161 | + @abstract 获取当前采集设备的指针 | |
| 162 | + @return AVCaptureDevice* 预览开始前调用返回为nil,开始预览后,返回当前正在使用的摄像头 | |
| 163 | + @warning 请勿修改摄像头的像素格式,帧率,分辨率等参数,修改后会导致推流工作异常或崩溃 | |
| 163 | 164 | */ |
| 164 | -@property (nonatomic, assign) int videoFPS; | |
| 165 | +- (AVCaptureDevice *) getCurrentCameraDevices; | |
| 165 | 166 | |
| 166 | 167 | /** |
| 167 | - @abstract 是否开启美颜 | |
| 168 | - @discussion 默认为开启 | |
| 168 | + @abstract 采集设备状态 | |
| 169 | 169 | */ |
| 170 | -@property (nonatomic, assign) BOOL isOpenImageFilter; | |
| 170 | +@property (nonatomic, readonly) CNLiveCaptureState captureState; | |
| 171 | + | |
| 172 | +FOUNDATION_EXPORT NSString *const CNLiveCaptureStateDidChangeNotification NS_AVAILABLE_IOS(7_0); | |
| 173 | + | |
| 174 | +#pragma mark - stream settings | |
| 175 | + | |
| 171 | 176 | |
| 172 | 177 | /** |
| 173 | - @abstract 摄像头位置 | |
| 174 | - @discussion 前后摄像头 默认为前摄像头 | |
| 178 | + @abstract 推流状态 | |
| 175 | 179 | */ |
| 176 | -@property (nonatomic, assign) AVCaptureDevicePosition cameraPosition; | |
| 180 | +@property (nonatomic, readonly) CNLiveStreamState streamState; | |
| 181 | + | |
| 182 | +FOUNDATION_EXPORT NSString *const CNLiveStreamStateDidChangeNotification NS_AVAILABLE_IOS(7_0); | |
| 183 | + | |
| 184 | +/** | |
| 185 | + * @abstract 推流错误码,用于指示推流失败的原因 | |
| 186 | + */ | |
| 187 | +@property (nonatomic, readonly) CNLiveStreamErrorCode streamErrorCode; | |
| 177 | 188 | |
| 178 | 189 | /** |
| 179 | 190 | @abstract 切换摄像头 |
| ... | ... | @@ -190,7 +201,11 @@ |
| 190 | 201 | - (void) setTorchMode:(AVCaptureTorchMode)mode; |
| 191 | 202 | |
| 192 | 203 | /** |
| 193 | - @abstract 画面朝向描述的是横屏还是竖屏,需要与UI朝向一致,否则会出现画面缺失的情况,第一次设置有效 | |
| 204 | + @abstract 摄像头朝向 | |
| 205 | + @discussion 参见UIInterfaceOrientation | |
| 206 | + @discussion 竖屏时: width < height | |
| 207 | + @discussion 横屏时: width > height | |
| 208 | + @discussion 需要与UI方向一致 | |
| 194 | 209 | */ |
| 195 | 210 | - (void) setVideoOrientationBy:(UIInterfaceOrientation)uiOrien; |
| 196 | 211 | |
| ... | ... | @@ -221,29 +236,57 @@ |
| 221 | 236 | - (void) stopStream; |
| 222 | 237 | |
| 223 | 238 | /** |
| 224 | - @abstract 获取当前采集设备的指针 | |
| 225 | - @return AVCaptureDevice* 预览开始前调用返回为nil,开始预览后,返回当前正在使用的摄像头 | |
| 226 | - @warning 请勿修改摄像头的像素格式,帧率,分辨率等参数,修改后会导致推流工作异常或崩溃 | |
| 239 | + @abstract 进入后台: 暂停图像采集 | |
| 240 | + @discussion 暂停图像采集和预览 | |
| 227 | 241 | */ |
| 228 | -- (AVCaptureDevice *) getCurrentCameraDevices; | |
| 242 | +- (void) appEnterBackground; | |
| 229 | 243 | |
| 230 | 244 | /** |
| 231 | - @abstract 采集设备状态 | |
| 245 | + @abstract 回到前台: 恢复采集 | |
| 246 | + @discussion 恢复图像采集和预览 | |
| 232 | 247 | */ |
| 233 | -@property (nonatomic, readonly) CNLiveCaptureState captureState; | |
| 248 | +- (void) appBecomeActive; | |
| 234 | 249 | |
| 235 | -FOUNDATION_EXPORT NSString *const CNLiveCaptureStateDidChangeNotification NS_AVAILABLE_IOS(7_0); | |
| 250 | +#pragma mark - mirror & rotate | |
| 236 | 251 | |
| 237 | 252 | /** |
| 238 | - @abstract 推流状态 | |
| 253 | + @abstract 预览设置成镜像模式,默认为NO | |
| 239 | 254 | */ |
| 240 | -@property (nonatomic, readonly) CNLiveStreamState streamState; | |
| 255 | +@property (nonatomic, assign) BOOL previewMirrored; | |
| 241 | 256 | |
| 242 | -FOUNDATION_EXPORT NSString *const CNLiveStreamStateDidChangeNotification NS_AVAILABLE_IOS(7_0); | |
| 257 | +/** | |
| 258 | + @abstract 推流设置成镜像模式,默认为NO | |
| 259 | + */ | |
| 260 | +@property (nonatomic, assign) BOOL streamerMirrored; | |
| 243 | 261 | |
| 244 | 262 | /** |
| 245 | - * @abstract 推流错误码,用于指示推流失败的原因 | |
| 263 | + @abstract 根据UI的朝向旋转预览视图, 保证预览视图全屏铺满窗口 | |
| 264 | + @param orie 旋转到目标朝向, 需要从demo中获取UI的朝向传入 | |
| 265 | + @discussion 采集到的图像的朝向还是和启动时的朝向一致 | |
| 246 | 266 | */ |
| 247 | -@property (nonatomic, readonly) CNLiveStreamErrorCode streamErrorCode; | |
| 267 | +- (void) rotatePreviewTo: (UIInterfaceOrientation) orie; | |
| 268 | + | |
| 269 | +/** | |
| 270 | + @abstract 根据UI的朝向旋转推流画面 | |
| 271 | + @param orie 旋转到目标朝向, 需要从demo中获取UI的朝向传入 | |
| 272 | + */ | |
| 273 | +- (void) rotateStreamTo: (UIInterfaceOrientation) orie; | |
| 274 | + | |
| 275 | +/** | |
| 276 | + @abstract 当前采集设备是否支持自动变焦 | |
| 277 | + @param point相机对焦的位置 | |
| 278 | + */ | |
| 279 | +- (void)focusAtPoint:(CGPoint )point; | |
| 280 | + | |
| 281 | +/** | |
| 282 | + @abstract 当前采集设备是否支持自动曝光 | |
| 283 | + @param point相机曝光的位置 | |
| 284 | + */ | |
| 285 | +- (void)exposureAtPoint:(CGPoint )point; | |
| 286 | + | |
| 287 | +/** | |
| 288 | + @abstract 触摸缩放因子 | |
| 289 | + */ | |
| 290 | +@property (nonatomic, assign) CGFloat pinchZoomFactor; | |
| 248 | 291 | |
| 249 | 292 | @end | ... | ... |
CNLivePlayerSDKTest/CNLivePlayerSDK.framework/Headers/CNLiveMoviePlayerController.h
| ... | ... | @@ -7,29 +7,21 @@ |
| 7 | 7 | |
| 8 | 8 | #import <UIKit/UIKit.h> |
| 9 | 9 | #import <MediaPlayer/MediaPlayer.h> |
| 10 | +#import "CNLiveTypeDef.h" | |
| 10 | 11 | |
| 11 | 12 | @interface CNLiveMoviePlayerController : NSObject |
| 12 | 13 | |
| 13 | -/* | |
| 14 | - * 获取SDK版本号 | |
| 15 | - */ | |
| 16 | -+ (NSString *)getVersion; | |
| 17 | - | |
| 18 | 14 | #pragma mark - 使用视讯云平台 直接通过节目id播放 |
| 19 | 15 | /* 初始化点播播放器 初始化成功后才能对播放器进行设置(如:自动播放、是否开启硬件解码等)和调用准备播放视频方法(prepareToPlay) |
| 20 | 16 | * |
| 21 | 17 | * 当前版本只支持单实例的CNLiveMoviePlayerController对象,多实例将导致播放异常 |
| 22 | 18 | * |
| 23 | - * appId 在open.cnlive.com网站申请得到的appId | |
| 24 | - * appKey 在open.cnlive.com网站申请得到的app_key | |
| 25 | 19 | * vId 点播视频Id |
| 26 | 20 | * authSuccess 初始化成功block |
| 27 | 21 | * authFailed 初始化失败block 回调一个errorInfo的字典 |
| 28 | 22 | * |
| 29 | 23 | */ |
| 30 | -- (instancetype)initVodPlayWithAppId:(NSString *)appId | |
| 31 | - appKey:(NSString *)appKey | |
| 32 | - vId:(NSString *)vId | |
| 24 | +- (instancetype)initVodPlayWithVId:(NSString *)vId | |
| 33 | 25 | authSuccess:(void(^)(void))authSuccessBlock |
| 34 | 26 | authFailed:(void(^)(NSDictionary *errorInfo))authFailedBlock; |
| 35 | 27 | |
| ... | ... | @@ -37,18 +29,18 @@ |
| 37 | 29 | * |
| 38 | 30 | * 当前版本只支持单实例的CNLiveMoviePlayerController对象,多实例将导致播放异常 |
| 39 | 31 | * |
| 40 | - * appId 在open.cnlive.com网站申请得到的appId | |
| 41 | - * appKey 在open.cnlive.com网站申请得到的app_key | |
| 42 | 32 | * channelId 直播视频Id |
| 43 | 33 | * authSuccess 初始化成功block |
| 44 | 34 | * authFailed 初始化失败block 回调一个errorInfo的字典 |
| 45 | 35 | * |
| 36 | + * result 获取主播状态 YES:获取状态成功 | |
| 37 | + * NO:获取失败,不影响视频播放,但是获取不到主播离开、回来、直播结束等通知。可通过 | |
| 38 | + * - (void)retryToGetAnchorStatusResult:(void(^)(BOOL result))result;方法重新获取 | |
| 46 | 39 | */ |
| 47 | -- (instancetype)initLivePlayWithAppId:(NSString *)appId | |
| 48 | - appKey:(NSString *)appKey | |
| 49 | - channelId:(NSString *)channelId | |
| 50 | - authSuccess:(void(^)(void))authSuccessBlock | |
| 51 | - authFailed:(void(^)(NSDictionary *errorInfo))authFailedBlock; | |
| 40 | +- (instancetype)initLivePlayWithChannelId:(NSString *)channelId | |
| 41 | + authSuccess:(void(^)(void))authSuccessBlock | |
| 42 | + authFailed:(void(^)(NSDictionary *errorInfo))authFailedBlock | |
| 43 | + getAnchorStatusResult:(void(^)(BOOL result))result; | |
| 52 | 44 | |
| 53 | 45 | /* |
| 54 | 46 | * 是否开启控制台输出日志(默认YES) |
| ... | ... | @@ -73,12 +65,37 @@ |
| 73 | 65 | */ |
| 74 | 66 | - (void)retry; |
| 75 | 67 | |
| 68 | +/* 重新获取主播直播状态 | |
| 69 | + * | |
| 70 | + * result 重新获取主播直播状态,YES:成功 NO:失败 | |
| 71 | + * | |
| 72 | + */ | |
| 73 | +- (void)retryToGetAnchorStatusResult:(void(^)(BOOL result))result; | |
| 74 | + | |
| 75 | +/* 主播直播状态(只读) | |
| 76 | + * | |
| 77 | + */ | |
| 78 | +@property (nonatomic, assign, readonly) CNLiveAnchorStatus anchorStatus; | |
| 79 | + | |
| 80 | +/* | |
| 81 | + * 主播直播状态改变通知 | |
| 82 | + */ | |
| 83 | +FOUNDATION_EXPORT NSString *const CNLiveAnchorStatusChangedNotification NS_AVAILABLE_IOS(7_0); | |
| 84 | + | |
| 76 | 85 | #pragma mark - 普通播放器方法 |
| 77 | 86 | /* |
| 78 | 87 | * 初始化播放器并设置播放地址 |
| 79 | 88 | */ |
| 80 | 89 | - (instancetype)initWithContentURL:(NSURL *)url; |
| 81 | 90 | |
| 91 | +/* 解码方式(硬解/软解/自动) | |
| 92 | + * | |
| 93 | + * 建议系统版本高于8.0 选择硬解,或者自动选择解码方式,8.0以上的系统优先选择硬解 | |
| 94 | + * 只在[prepareToPlay]调用前设置生效 | |
| 95 | + * | |
| 96 | + */ | |
| 97 | +@property(nonatomic) CNLiveMovieVideoDecoderMode videoDecoderMode; | |
| 98 | + | |
| 82 | 99 | /* 重新启动拉流 |
| 83 | 100 | * |
| 84 | 101 | * url 视频播放地址,该地址可以是本地地址或者服务器地址.如果为nil,则使用前一次播放地址 |
| ... | ... | @@ -86,6 +103,13 @@ |
| 86 | 103 | */ |
| 87 | 104 | - (void)reload:(NSURL *)aUrl; |
| 88 | 105 | |
| 106 | +/** | |
| 107 | + * timeout指定拉流超时时间,单位是秒 | |
| 108 | + * prepareTimeout 建立链接超时时间,默认值是10秒 | |
| 109 | + * readTimeout 拉流超时时间,默认值是30秒 | |
| 110 | + */ | |
| 111 | +- (void)setTimeout:(int)prepareTimeout readTimeout:(int)readTimeout; | |
| 112 | + | |
| 89 | 113 | /* |
| 90 | 114 | * 播放视频时是否需要自动播放,默认值为YES |
| 91 | 115 | */ |
| ... | ... | @@ -277,12 +301,4 @@ MP_EXTERN NSString * const MPMoviePlayerFirstAudioFrameRenderedNotification; |
| 277 | 301 | */ |
| 278 | 302 | - (UIImage *)thumbnailImageAtCurrentTime; |
| 279 | 303 | |
| 280 | -/* 是否开启硬件解码 | |
| 281 | - * | |
| 282 | - * 默认是关闭 | |
| 283 | - * 只在[prepareToPlay]调用前设置生效 | |
| 284 | - * | |
| 285 | - */ | |
| 286 | -@property(nonatomic) BOOL shouldUseHWCodec; | |
| 287 | - | |
| 288 | 304 | @end | ... | ... |
CNLivePlayerSDKTest/CNLivePlayerSDK.framework/Headers/CNLiveStreamerBase.h
0 → 100644
| 1 | +// | |
| 2 | +// CNLiveStreamerBase.h | |
| 3 | +// CNLivePlayerSDKDemo | |
| 4 | +// | |
| 5 | +// Created by iOS on 17/2/27. | |
| 6 | +// Copyright © 2017年 雷浩杰. All rights reserved. | |
| 7 | +// | |
| 8 | + | |
| 9 | +#import <Foundation/Foundation.h> | |
| 10 | +#import "CNLiveTypeDef.h" | |
| 11 | +#import <UIKit/UIKit.h> | |
| 12 | + | |
| 13 | +@interface CNLiveStreamerBase : NSObject | |
| 14 | + | |
| 15 | +/** | |
| 16 | + @abstract 视频帧率 默认:15 | |
| 17 | + @discussion 当实际送入的视频帧率过高时会主动丢帧,有效范围[1~30] | |
| 18 | + */ | |
| 19 | +@property (nonatomic, assign) int videoFPS; | |
| 20 | + | |
| 21 | +/** | |
| 22 | + @abstract 视频帧率最小值,默认与videoFPS相同 | |
| 23 | + @discussion video frame per seconds 有效范围[1~30], 超出会提示参数错误 | |
| 24 | + @discussion 不设置该值时表示网络自适应不使用动态帧率 | |
| 25 | + */ | |
| 26 | +@property (nonatomic, assign) int videoMinFPS; | |
| 27 | + | |
| 28 | +/** | |
| 29 | + @abstract 视频帧率最大值,默认与videoFPS相同 | |
| 30 | + @discussion video frame per seconds 有效范围[1~30], 超出会提示参数错误 | |
| 31 | + @discussion 不设置该值时表示网络自适应不使用动态帧率 | |
| 32 | + */ | |
| 33 | +@property (nonatomic, assign) int videoMaxFPS; | |
| 34 | + | |
| 35 | +/** | |
| 36 | + @abstract 视频编码器 | |
| 37 | + @discussion video codec used for encode | |
| 38 | + */ | |
| 39 | +@property (nonatomic, assign) CNLiveVideoCodec videoCodec; | |
| 40 | + | |
| 41 | +/** | |
| 42 | + @abstract 视频编码起始码率(单位:kbps) | |
| 43 | + @discussion 开始推流时的视频码率,开始推流后,根据网络情况在 [Min, Max]范围内调节 | |
| 44 | + @discussion 视频码率上调则画面更清晰,下调则画面更模糊 | |
| 45 | + */ | |
| 46 | +@property (nonatomic, assign) int videoInitBitrate; // kbit/s of video | |
| 47 | + | |
| 48 | +/** | |
| 49 | + @abstract 视频编码最高码率(单位:kbps) | |
| 50 | + @discussion 视频码率自适应调整的上限, 为目标码率 | |
| 51 | + */ | |
| 52 | +@property (nonatomic, assign) int videoMaxBitrate; // kbit/s of video | |
| 53 | + | |
| 54 | +/** | |
| 55 | + @abstract 视频编码最低码率(单位:kbps) | |
| 56 | + @discussion 视频码率自适应调整的下限 | |
| 57 | + */ | |
| 58 | +@property (nonatomic, assign) int videoMinBitrate; // kbit/s of video | |
| 59 | + | |
| 60 | +/** | |
| 61 | + @abstract 最大关键帧间隔(单位:秒, 默认:3) | |
| 62 | + @discussion 即GOP长度 画面静止时,隔n秒插入一个关键帧 | |
| 63 | + */ | |
| 64 | +@property (nonatomic, assign) float maxKeyInterval; // seconds | |
| 65 | + | |
| 66 | +/** | |
| 67 | + @abstract 音频编码码率(单位:kbps) | |
| 68 | + @discussion 音频目标编码码率 (比如48,96,128等) | |
| 69 | + */ | |
| 70 | +@property (nonatomic, assign) int audiokBPS; // kbit/s of audio | |
| 71 | + | |
| 72 | +/** | |
| 73 | + @abstract 查询当前是否处于推流状态 (建立连接中, 或连接中) | |
| 74 | + */ | |
| 75 | +- (BOOL) isStreaming; | |
| 76 | + | |
| 77 | +/** | |
| 78 | + @abstract 静音推流 (仍然有音频输出发送, 只是音量为0) | |
| 79 | + @param bMute YES / ON | |
| 80 | + */ | |
| 81 | +- (void)muteStream:(BOOL)bMute; | |
| 82 | + | |
| 83 | +/** | |
| 84 | + @abstract 截图功能,目前只支持jpg格式 | |
| 85 | + @param jpegCompressionQuality 设置图像的压缩比例 | |
| 86 | + @param filename 图片的文件名 | |
| 87 | + */ | |
| 88 | +- (void)takePhotoWithQuality:(CGFloat)jpegCompressionQuality | |
| 89 | + fileName:(NSString *)filename; | |
| 90 | + | |
| 91 | +@end | ... | ... |
CNLivePlayerSDKTest/CNLivePlayerSDK.framework/Headers/CNLiveTypeDef.h
| ... | ... | @@ -46,6 +46,8 @@ typedef NS_ENUM(NSUInteger, CNLiveVideoCodec) { |
| 46 | 46 | CNLiveVideoCodec_QY265, |
| 47 | 47 | /// 视频编码器 - iOS VT264硬件编码器 (iOS 8.0以上支持) |
| 48 | 48 | CNLiveVideoCodec_VT264, |
| 49 | + /// 视频编码器 - 由SDK自动选择( VT264 > X264) | |
| 50 | + CNLiveVideoCodec_AUTO = 100, | |
| 49 | 51 | }; |
| 50 | 52 | |
| 51 | 53 | #pragma mark - QYPublisher State |
| ... | ... | @@ -94,7 +96,7 @@ typedef NS_ENUM(NSUInteger, CNLiveStreamState) { |
| 94 | 96 | typedef NS_ENUM(NSUInteger, CNLiveStreamErrorCode) { |
| 95 | 97 | /// 正常无错误 |
| 96 | 98 | CNLiveStreamErrorCode_NONE = 0, |
| 97 | - /// SDK 鉴权失败 (暂时正常推流5~8分钟后终止推流) | |
| 99 | + /// (obsolete) | |
| 98 | 100 | CNLiveStreamErrorCode_CNLiveAUTHFAILED, |
| 99 | 101 | /// 当前帧编码失败 |
| 100 | 102 | CNLiveStreamErrorCode_ENCODE_FRAMES_FAILED, |
| ... | ... | @@ -104,32 +106,96 @@ typedef NS_ENUM(NSUInteger, CNLiveStreamErrorCode) { |
| 104 | 106 | CNLiveStreamErrorCode_CONNECT_FAILED, |
| 105 | 107 | /// 网络连接中断 |
| 106 | 108 | CNLiveStreamErrorCode_CONNECT_BREAK, |
| 107 | - /// rtmp 推流域名不存在 | |
| 109 | + /// rtmp 推流域名不存在 (CNLive 自定义) | |
| 108 | 110 | CNLiveStreamErrorCode_RTMP_NonExistDomain, |
| 109 | - /// rtmp 应用名不存在 | |
| 111 | + /// rtmp 应用名不存在(CNLive 自定义) | |
| 110 | 112 | CNLiveStreamErrorCode_RTMP_NonExistApplication, |
| 111 | - /// rtmp 流名已存在 | |
| 113 | + /// rtmp 流名已存在(CNLive 自定义) | |
| 112 | 114 | CNLiveStreamErrorCode_RTMP_AlreadyExistStreamName, |
| 113 | - /// rtmp 被黑名单拒绝 | |
| 115 | + /// rtmp 被黑名单拒绝(CNLive 自定义) | |
| 114 | 116 | CNLiveStreamErrorCode_RTMP_ForbiddenByBlacklist, |
| 115 | - /// rtmp 内部错误 | |
| 117 | + /// rtmp 内部错误(CNLive 自定义) | |
| 116 | 118 | CNLiveStreamErrorCode_RTMP_InternalError, |
| 117 | - /// rtmp URL 地址已过期 | |
| 119 | + /// rtmp URL 地址已过期(CNLive 自定义) | |
| 118 | 120 | CNLiveStreamErrorCode_RTMP_URLExpired, |
| 119 | - /// rtmp URL 地址签名错误 | |
| 121 | + /// rtmp URL 地址签名错误(CNLive 自定义) | |
| 120 | 122 | CNLiveStreamErrorCode_RTMP_SignatureDoesNotMatch, |
| 121 | - /// rtmp URL 中AccessKeyId非法 | |
| 123 | + /// rtmp URL 中AccessKeyId非法(CNLive 自定义) | |
| 122 | 124 | CNLiveStreamErrorCode_RTMP_InvalidAccessKeyId, |
| 123 | - /// rtmp URL 中参数错误 | |
| 125 | + /// rtmp URL 中参数错误(CNLive 自定义) | |
| 124 | 126 | CNLiveStreamErrorCode_RTMP_BadParams, |
| 125 | - /// rtmp URL 中的推流不在发布点内 | |
| 127 | + /// rtmp URL 中的推流不在发布点内(CNLive 自定义) | |
| 126 | 128 | CNLiveStreamErrorCode_RTMP_ForbiddenByRegion, |
| 127 | - /// SDK 因为鉴权失败停止推流 | |
| 129 | + /// (obsolete) | |
| 128 | 130 | CNLiveStreamErrorCode_FRAMES_THRESHOLD, |
| 129 | - /// 没有输入的数据,无法开始推流 | |
| 131 | + /// (obsolete) | |
| 130 | 132 | CNLiveStreamErrorCode_NO_INPUT_SAMPLE, |
| 133 | + /// 对于URL中的域名解析失败 | |
| 134 | + CNLiveStreamErrorCode_DNS_Parse_failed, | |
| 135 | + /// 对于URL对应的服务器连接失败(无法建立TCP连接) | |
| 136 | + CNLiveStreamErrorCode_Connect_Server_failed, | |
| 137 | + /// 跟RTMP服务器完成握手后,向{appname}/{streamname} 推流失败 | |
| 138 | + CNLiveStreamErrorCode_RTMP_Publish_failed, | |
| 139 | + /// 音视频同步失败 (输入的音频和视频的时间戳的差值超过5s) | |
| 140 | + CNLiveStreamErrorCode_AV_SYNC_ERROR, | |
| 141 | + /// 非法地址(地址为空或url中的协议或本地文件的后缀SDK不支持, 请检查) | |
| 142 | + CNLiveStreamErrorCode_INVALID_ADDRESS, | |
| 131 | 143 | /// 异常 |
| 132 | 144 | CNLiveStreamErrorCode_ABNORMAL, |
| 145 | + | |
| 146 | +}; | |
| 147 | + | |
| 148 | +#pragma mark - 美颜 | |
| 149 | + | |
| 150 | +typedef NS_ENUM(NSUInteger, CNLiveBeautyType) { | |
| 151 | + ///无 | |
| 152 | + CNLiveBeautyTypeNone = 0, | |
| 153 | + ///旧美颜 | |
| 154 | + CNLiveBeautyTypeOld, | |
| 155 | + ///红润美颜 | |
| 156 | + CNLiveBeautyTypeRuddy, | |
| 157 | + ///美颜特效 | |
| 158 | + CNLiveBeautyTypeSpecial | |
| 159 | +}; | |
| 160 | + | |
| 161 | +typedef NS_ENUM(NSUInteger, CNLiveSpecialEffects) { | |
| 162 | + ///小清新 | |
| 163 | + CNLiveSpecialEffectsFresh = 0, | |
| 164 | + ///靓丽 | |
| 165 | + CNLiveSpecialEffectsPretty, | |
| 166 | + ///甜美可人 | |
| 167 | + CNLiveSpecialEffectsSweet, | |
| 168 | + ///怀旧 | |
| 169 | + CNLiveSpecialEffectsNostalgia, | |
| 170 | + ///蓝调 | |
| 171 | + CNLiveSpecialEffectsBlues, | |
| 172 | + ///老照片 | |
| 173 | + CNLiveSpecialEffectsOldPicture | |
| 174 | +}; | |
| 175 | + | |
| 176 | +#pragma mark - player | |
| 177 | +/** | |
| 178 | + * 视频解码模式 | |
| 179 | + */ | |
| 180 | +typedef NS_ENUM(NSUInteger, CNLiveMovieVideoDecoderMode) { | |
| 181 | + ///视频解码方式采用软解 | |
| 182 | + CNLiveMovieVideoDecoderMode_Software = 0, | |
| 183 | + ///视频解码方式采用硬解 | |
| 184 | + CNLiveMovieVideoDecoderMode_Hardware, | |
| 185 | + ///自动选择解码方式,8.0以上的系统优先选择硬解 | |
| 186 | + CNLiveMovieVideoDecoderMode_AUTO, | |
| 187 | +}; | |
| 188 | + | |
| 189 | +/** | |
| 190 | + * 主播直播状态 | |
| 191 | + */ | |
| 192 | +typedef NS_ENUM(NSUInteger, CNLiveAnchorStatus) { | |
| 193 | + ///主播直播中 | |
| 194 | + CNLiveAnchorStatusLiving = 0, | |
| 195 | + ///主播已离开 | |
| 196 | + CNLiveAnchorStatusLeaving, | |
| 197 | + ///直播已结束 | |
| 198 | + CNLiveAnchorStatusEnd, | |
| 133 | 199 | }; |
| 134 | 200 | |
| 135 | 201 | #endif /* CNLiveTypeDef_h */ | ... | ... |
CNLivePlayerSDKTest/CNLivePlayerSDK.framework/Headers/CNLiveWatermark.h
0 → 100644
| 1 | +// | |
| 2 | +// CNLiveWatermark.h | |
| 3 | +// CNLivePlayerSDKDemo | |
| 4 | +// | |
| 5 | +// Created by iOS on 17/2/27. | |
| 6 | +// Copyright © 2017年 雷浩杰. All rights reserved. | |
| 7 | +// | |
| 8 | + | |
| 9 | +#import <Foundation/Foundation.h> | |
| 10 | +#import <UIKit/UIKit.h> | |
| 11 | + | |
| 12 | +@interface CNLiveWatermark : NSObject | |
| 13 | + | |
| 14 | +/** | |
| 15 | + @abstract 水印logo的图片 | |
| 16 | + @discussion 设置为nil为清除水印图片 | |
| 17 | + @discussion 请注意背景图片的尺寸, 太大的图片会导致内存占用过高 | |
| 18 | + @see | |
| 19 | + */ | |
| 20 | +@property (nonatomic, readwrite) UIImage *logoPic; | |
| 21 | + | |
| 22 | +/** | |
| 23 | + @abstract 水印logo的图片(网络图片) | |
| 24 | + @discussion 设置为nil为清除水印图片 | |
| 25 | + @discussion 请注意背景图片的尺寸, 太大的图片会导致内存占用过高 | |
| 26 | + @see | |
| 27 | + */ | |
| 28 | +@property (nonatomic, readwrite) NSURL *logoURL; | |
| 29 | + | |
| 30 | +/** | |
| 31 | + @abstract 水印logo的图片的位置和大小 | |
| 32 | + @discussion 位置和大小的单位为预览视图的百分比, 左上角为(0,0), 右下角为(1.0, 1.0) | |
| 33 | + @discussion 如果宽为0, 则根据图像的宽高比, 和设置的高度比例, 计算得到宽度的比例 | |
| 34 | + @discussion 如果高为0, 方法同上 | |
| 35 | + */ | |
| 36 | +@property (nonatomic, readwrite) CGRect logoRect; | |
| 37 | + | |
| 38 | +/** | |
| 39 | + @abstract 水印logo的图片的透明度 | |
| 40 | + @discussion alpha为透明度(0-1),0完全透明,1完全不透明 | |
| 41 | + */ | |
| 42 | +@property (nonatomic, readwrite) CGFloat logoAlpha; | |
| 43 | + | |
| 44 | +/** | |
| 45 | + @abstract 水印文字的label | |
| 46 | + @discussion 借用UILabel来指定文字的颜色,字体, 透明度, 对齐方式等属性 | |
| 47 | + @discussion 请注意保证背景图片的尺寸, 太大的图片会导致内存占用过高 | |
| 48 | + @warning 如果使用非等宽字体, 可能导致闪烁(默认为Courier) | |
| 49 | + @warning picMixer和UILabel都有alpha属性, 建议只选用其中一个, 固定另一个为1.0, | |
| 50 | + 为了减少接口, 建议直接使用UILabel的属性, | |
| 51 | + 如果两者同时使用, 最终图层的alpha为两者乘积 | |
| 52 | + */ | |
| 53 | +@property (nonatomic, readwrite) UILabel *textLabel; | |
| 54 | + | |
| 55 | +/** | |
| 56 | + @abstract 水印文字的位置和大小 | |
| 57 | + @discussion 位置和大小的单位为预览视图的百分比, 左上角为(0,0), 右下角为(1.0, 1.0) | |
| 58 | + @discussion 如果宽为0, 则根据文字图像的宽高比, 和设置的高度比例, 计算得到宽度的比例 | |
| 59 | + @discussion 如果高为0, 方法同上 | |
| 60 | + */ | |
| 61 | +@property (nonatomic, readwrite) CGRect textRect; | |
| 62 | + | |
| 63 | +/** | |
| 64 | + @abstract 刷新水印文字的内容 | |
| 65 | + @discussion 先修改文字的内容或格式,调用该方法后生效 | |
| 66 | + */ | |
| 67 | +- (void)updateTextLabel; | |
| 68 | + | |
| 69 | +@end | ... | ... |
CNLivePlayerSDKTest/CNLivePlayerSDK.framework/Headers/CNLiveWebcastManager.h
0 → 100644
| 1 | +// | |
| 2 | +// CNLiveStreamManager.h | |
| 3 | +// CNLivePlayerSDKDemo | |
| 4 | +// | |
| 5 | +// Created by iOS on 17/2/23. | |
| 6 | +// Copyright © 2017年 雷浩杰. All rights reserved. | |
| 7 | +// | |
| 8 | + | |
| 9 | +#import <Foundation/Foundation.h> | |
| 10 | +#import "CNLiveTypeDef.h" | |
| 11 | +#import "CNLiveWatermark.h" | |
| 12 | +#import "CNLiveStreamerBase.h" | |
| 13 | +#import "CNLiveBeautifyFaceFilter.h" | |
| 14 | +#import "CNLiveAudioCapture.h" | |
| 15 | +#import <AVFoundation/AVFoundation.h> | |
| 16 | +#import <UIKit/UIKit.h> | |
| 17 | + | |
| 18 | +@interface CNLiveWebcastManager : NSObject | |
| 19 | + | |
| 20 | +/** | |
| 21 | + * 应用ID(只读) | |
| 22 | + */ | |
| 23 | +@property (nonatomic, readonly) NSString *appId; | |
| 24 | + | |
| 25 | +/** | |
| 26 | + * 应用KEY(只读) | |
| 27 | + */ | |
| 28 | +@property (nonatomic, readonly) NSString *appKey; | |
| 29 | + | |
| 30 | ++ (CNLiveWebcastManager *)manager; | |
| 31 | + | |
| 32 | +/** | |
| 33 | + @abstract 初始化直播云SDK | |
| 34 | + @param appId 在open.cnlive.com网站申请得到的appId | |
| 35 | + @param appKey 在open.cnlive.com网站申请得到的appKey | |
| 36 | + @warning 必传参数 | |
| 37 | + */ | |
| 38 | +- (void)initWithAppId:(NSString *)appId appKey:(NSString *)appKey; | |
| 39 | + | |
| 40 | +/** | |
| 41 | + @abstract 获取版本号 | |
| 42 | + */ | |
| 43 | ++ (NSString *)getVersion; | |
| 44 | + | |
| 45 | +@end | ... | ... |
CNLivePlayerSDKTest/CNLivePlayerSDK.framework/Info.plist
No preview for this file type
CNLivePlayerSDKTest/CNLivePlayerSDKTest/Assets.xcassets/btn_dot.imageset/Contents.json
0 → 100644
| 1 | +{ | |
| 2 | + "images" : [ | |
| 3 | + { | |
| 4 | + "idiom" : "universal", | |
| 5 | + "filename" : "btn_dot.png", | |
| 6 | + "scale" : "1x" | |
| 7 | + }, | |
| 8 | + { | |
| 9 | + "idiom" : "universal", | |
| 10 | + "scale" : "2x" | |
| 11 | + }, | |
| 12 | + { | |
| 13 | + "idiom" : "universal", | |
| 14 | + "scale" : "3x" | |
| 15 | + } | |
| 16 | + ], | |
| 17 | + "info" : { | |
| 18 | + "version" : 1, | |
| 19 | + "author" : "xcode" | |
| 20 | + } | |
| 21 | +} | |
| 0 | 22 | \ No newline at end of file | ... | ... |
CNLivePlayerSDKTest/CNLivePlayerSDKTest/Assets.xcassets/btn_dot.imageset/btn_dot.png
0 → 100644
1.17 KB
CNLivePlayerSDKTest/CNLivePlayerSDKTest/Assets.xcassets/cancel.imageset/Contents.json
0 → 100644
| 1 | +{ | |
| 2 | + "images" : [ | |
| 3 | + { | |
| 4 | + "idiom" : "universal", | |
| 5 | + "filename" : "cancel.png", | |
| 6 | + "scale" : "1x" | |
| 7 | + }, | |
| 8 | + { | |
| 9 | + "idiom" : "universal", | |
| 10 | + "scale" : "2x" | |
| 11 | + }, | |
| 12 | + { | |
| 13 | + "idiom" : "universal", | |
| 14 | + "scale" : "3x" | |
| 15 | + } | |
| 16 | + ], | |
| 17 | + "info" : { | |
| 18 | + "version" : 1, | |
| 19 | + "author" : "xcode" | |
| 20 | + } | |
| 21 | +} | |
| 0 | 22 | \ No newline at end of file | ... | ... |
CNLivePlayerSDKTest/CNLivePlayerSDKTest/Assets.xcassets/cancel.imageset/cancel.png
0 → 100644
5.67 KB
CNLivePlayerSDKTest/CNLivePlayerSDKTest/Assets.xcassets/互动电视180x180.imageset/Contents.json
0 → 100644
| 1 | +{ | |
| 2 | + "images" : [ | |
| 3 | + { | |
| 4 | + "idiom" : "universal", | |
| 5 | + "filename" : "互动电视180x180.png", | |
| 6 | + "scale" : "1x" | |
| 7 | + }, | |
| 8 | + { | |
| 9 | + "idiom" : "universal", | |
| 10 | + "scale" : "2x" | |
| 11 | + }, | |
| 12 | + { | |
| 13 | + "idiom" : "universal", | |
| 14 | + "scale" : "3x" | |
| 15 | + } | |
| 16 | + ], | |
| 17 | + "info" : { | |
| 18 | + "version" : 1, | |
| 19 | + "author" : "xcode" | |
| 20 | + } | |
| 21 | +} | |
| 0 | 22 | \ No newline at end of file | ... | ... |
CNLivePlayerSDKTest/CNLivePlayerSDKTest/Assets.xcassets/互动电视180x180.imageset/互动电视180x180.png
0 → 100644
21.2 KB
CNLivePlayerSDKTest/CNLivePlayerSDKTest/Info.plist
| ... | ... | @@ -6,6 +6,10 @@ |
| 6 | 6 | <string>en</string> |
| 7 | 7 | <key>CFBundleExecutable</key> |
| 8 | 8 | <string>$(EXECUTABLE_NAME)</string> |
| 9 | + <key>CFBundleIcons</key> | |
| 10 | + <dict/> | |
| 11 | + <key>CFBundleIcons~ipad</key> | |
| 12 | + <dict/> | |
| 9 | 13 | <key>CFBundleIdentifier</key> |
| 10 | 14 | <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string> |
| 11 | 15 | <key>CFBundleInfoDictionaryVersion</key> | ... | ... |
CNLivePlayerSDKTest/CNLivePlayerSDKTest/Player/BackgroundView.h
0 → 100644
| 1 | +// | |
| 2 | +// BackgroundView.h | |
| 3 | +// CNLivePlayerSDKDemo | |
| 4 | +// | |
| 5 | +// Created by iOS on 17/3/9. | |
| 6 | +// Copyright © 2017年 雷浩杰. All rights reserved. | |
| 7 | +// | |
| 8 | + | |
| 9 | +#import <UIKit/UIKit.h> | |
| 10 | + | |
| 11 | +@interface BackgroundView : UIView | |
| 12 | + | |
| 13 | +@property (nonatomic, assign) int anchorStatus; //设置主播状态 | |
| 14 | + | |
| 15 | + | |
| 16 | +@end | ... | ... |
CNLivePlayerSDKTest/CNLivePlayerSDKTest/Player/BackgroundView.m
0 → 100644
| 1 | +// | |
| 2 | +// BackgroundView.m | |
| 3 | +// CNLivePlayerSDKDemo | |
| 4 | +// | |
| 5 | +// Created by iOS on 17/3/9. | |
| 6 | +// Copyright © 2017年 雷浩杰. All rights reserved. | |
| 7 | +// | |
| 8 | + | |
| 9 | +#import "BackgroundView.h" | |
| 10 | + | |
| 11 | +@interface BackgroundView () | |
| 12 | + | |
| 13 | +@property (nonatomic, strong) UILabel *anchorLab; | |
| 14 | + | |
| 15 | +@end | |
| 16 | + | |
| 17 | + | |
| 18 | +@implementation BackgroundView | |
| 19 | + | |
| 20 | +- (instancetype)initWithFrame:(CGRect)frame | |
| 21 | +{ | |
| 22 | + self = [super initWithFrame:frame]; | |
| 23 | + if (self) { | |
| 24 | + self.backgroundColor = [UIColor colorWithRed:54 green:54 blue:54 alpha:1]; | |
| 25 | + [self addSubview:self.anchorLab]; | |
| 26 | + } | |
| 27 | + return self; | |
| 28 | +} | |
| 29 | + | |
| 30 | +- (void)setAnchorStatus:(int)anchorStatus | |
| 31 | +{ | |
| 32 | + switch (anchorStatus) { | |
| 33 | + case 0: | |
| 34 | + self.hidden = YES; | |
| 35 | + break; | |
| 36 | + | |
| 37 | + case 1: | |
| 38 | + _anchorLab.text = @"主播已离开,马上回来~"; | |
| 39 | + self.hidden = NO; | |
| 40 | + break; | |
| 41 | + | |
| 42 | + case 2: | |
| 43 | + _anchorLab.text = @"直播已结束"; | |
| 44 | + self.hidden = NO; | |
| 45 | + break; | |
| 46 | + | |
| 47 | + default: | |
| 48 | + break; | |
| 49 | + } | |
| 50 | +} | |
| 51 | + | |
| 52 | +#pragma mark - | |
| 53 | +#pragma mark - getter | |
| 54 | + | |
| 55 | +- (UILabel *)anchorLab | |
| 56 | +{ | |
| 57 | + if (!_anchorLab) { | |
| 58 | + _anchorLab = [[UILabel alloc] initWithFrame:CGRectMake(0, self.bounds.size.height/2-25, self.bounds.size.width, 40)]; | |
| 59 | + _anchorLab.textColor = [UIColor whiteColor]; | |
| 60 | + _anchorLab.font = [UIFont systemFontOfSize:15]; | |
| 61 | + _anchorLab.textAlignment = NSTextAlignmentCenter; | |
| 62 | + } | |
| 63 | + return _anchorLab; | |
| 64 | +} | |
| 65 | + | |
| 66 | +/* | |
| 67 | +// Only override drawRect: if you perform custom drawing. | |
| 68 | +// An empty implementation adversely affects performance during animation. | |
| 69 | +- (void)drawRect:(CGRect)rect { | |
| 70 | + // Drawing code | |
| 71 | +} | |
| 72 | +*/ | |
| 73 | + | |
| 74 | +@end | ... | ... |
CNLivePlayerSDKTest/CNLivePlayerSDKTest/Player/CNLiveViewController.h
| ... | ... | @@ -8,6 +8,16 @@ |
| 8 | 8 | |
| 9 | 9 | #import <UIKit/UIKit.h> |
| 10 | 10 | |
| 11 | +typedef NS_ENUM(NSInteger, PlayerType) { | |
| 12 | + PlayerTypeLiving = 0, | |
| 13 | + PlayerTypeVOD, | |
| 14 | + PlayerTypeNormal | |
| 15 | +}; | |
| 16 | + | |
| 11 | 17 | @interface CNLiveViewController : UIViewController |
| 12 | 18 | |
| 19 | +@property (nonatomic, assign) PlayerType type; | |
| 20 | +@property (nonatomic, strong)NSString *videoUrlString; | |
| 21 | + | |
| 22 | + | |
| 13 | 23 | @end | ... | ... |
CNLivePlayerSDKTest/CNLivePlayerSDKTest/Player/CNLiveViewController.m
| ... | ... | @@ -8,18 +8,28 @@ |
| 8 | 8 | |
| 9 | 9 | #import "CNLiveViewController.h" |
| 10 | 10 | #import <CNLivePlayerSDK/CNLiveMoviePlayerController.h> |
| 11 | +#import <CNLivePlayerSDK/CNLiveWebcastManager.h> | |
| 12 | +#import "LoadingView.h" | |
| 13 | +#import "BackgroundView.h" | |
| 14 | + | |
| 15 | +#define KAppId @"60_irn9edco29" | |
| 16 | +#define KAppKey @"69fe70bda67e059286ad509d9f5377f815955645b52f94" | |
| 11 | 17 | |
| 12 | 18 | @interface CNLiveViewController () |
| 13 | 19 | { |
| 14 | - CNLiveMoviePlayerController *_player; | |
| 15 | - UIButton *_playOrPauseBtn; | |
| 16 | - UISlider *_timeSlide; | |
| 17 | - UIProgressView *_bufferProgress; | |
| 18 | - UILabel *_currentTimeLabel; | |
| 19 | - UILabel *_durationLabel; | |
| 20 | 20 | NSTimer *_currentTimer; |
| 21 | 21 | } |
| 22 | 22 | |
| 23 | +@property (nonatomic, strong) LoadingView *loadingView; | |
| 24 | +@property (nonatomic, strong) UIButton *playOrPauseBtn; | |
| 25 | +@property (nonatomic, strong) UISlider *timeSlide; | |
| 26 | +@property (nonatomic, strong) UILabel *durationLabel; | |
| 27 | +@property (nonatomic, strong) UILabel *currentTimeLabel; | |
| 28 | +@property (nonatomic, strong) UIProgressView *bufferProgress; | |
| 29 | +@property (nonatomic, strong) UIButton *quitBtn; | |
| 30 | +@property (nonatomic, strong) BackgroundView *bgView; | |
| 31 | +@property (nonatomic, strong) UIView *toolBgView; | |
| 32 | +@property (nonatomic, strong) CNLiveMoviePlayerController *player; | |
| 23 | 33 | @end |
| 24 | 34 | |
| 25 | 35 | @implementation CNLiveViewController |
| ... | ... | @@ -35,87 +45,70 @@ |
| 35 | 45 | //添加播放器通知 |
| 36 | 46 | [self installMovieNotificationObservers]; |
| 37 | 47 | |
| 38 | - //获取SDK版本号 | |
| 39 | - NSLog(@">>>>>>>>>>>>%@", [CNLiveMoviePlayerController getVersion]); | |
| 40 | - | |
| 41 | - //测试vId | |
| 42 | - //a9c5f9a2d6114bf98f2aff89a55d4f98 | |
| 43 | - //0e12b70817c440509d88f67d4e975fe0 | |
| 44 | - //点播播放 | |
| 45 | - _player = [[CNLiveMoviePlayerController alloc] initVodPlayWithAppId:@"60_irg1rhs308" | |
| 46 | - appKey:@"4e7a37b27a717a9e9b0a7aea4620ee55ea61fbf923bf46" | |
| 47 | - vId:@"20161126" | |
| 48 | - authSuccess:^{ | |
| 49 | - | |
| 50 | - _player.shouldUseHWCodec = YES; | |
| 51 | - _player.view.frame = CGRectMake(0, 20, self.view.frame.size.width, self.view.frame.size.width*9/16); | |
| 52 | - _player.shouldAutoplay = YES; | |
| 53 | - [_player prepareToPlay]; | |
| 54 | - [self.view addSubview:_player.view]; | |
| 55 | - | |
| 56 | - } authFailed:^(NSDictionary *errorInfo) { | |
| 57 | - NSLog(@"初始化点播播放器失败%@", errorInfo); | |
| 58 | - }]; | |
| 59 | - | |
| 60 | - //直播播放 | |
| 61 | -// _player = [[CNLiveMoviePlayerController alloc] initLivePlayWithAppId:@"60_irg1rhs308" | |
| 62 | -// appKey:@"4e7a37b27a717a9e9b0a7aea4620ee55ea61fbf923bf46" | |
| 63 | -// channelId:@"news" | |
| 64 | -// authSuccess:^{ | |
| 65 | -// | |
| 66 | -// _player.shouldUseHWCodec = YES; | |
| 67 | -// _player.view.frame = CGRectMake(0, 20, self.view.frame.size.width, self.view.frame.size.width*9/16); | |
| 68 | -// _player.shouldAutoplay = YES; | |
| 69 | -// [_player prepareToPlay]; | |
| 70 | -// [self.view addSubview:_player.view]; | |
| 71 | -// | |
| 72 | -// } authFailed:^(NSDictionary *errorInfo) { | |
| 73 | -// NSLog(@"初始化直播播放器失败:%@", errorInfo); | |
| 74 | -// }]; | |
| 75 | - | |
| 76 | - _playOrPauseBtn = [UIButton buttonWithType:UIButtonTypeSystem]; | |
| 77 | - _playOrPauseBtn.frame = CGRectMake(10, self.view.frame.size.width*9/16+20+50, 50, 50); | |
| 78 | - [_playOrPauseBtn setImage:[UIImage imageNamed:@"play"] forState:UIControlStateNormal]; | |
| 79 | - [_playOrPauseBtn addTarget:self action:@selector(playOrPauseBtnAction) forControlEvents:UIControlEventTouchUpInside]; | |
| 80 | - [self.view addSubview:_playOrPauseBtn]; | |
| 81 | 48 | |
| 82 | - _currentTimeLabel = [[UILabel alloc] initWithFrame:CGRectMake(70, self.view.frame.size.width*9/16+20+50, 60, 50)]; | |
| 83 | - _currentTimeLabel.textColor = [UIColor blackColor]; | |
| 84 | - _currentTimeLabel.font = [UIFont systemFontOfSize:13]; | |
| 85 | - [_currentTimeLabel setText:[NSString stringWithFormat:@"00:00:00"]]; | |
| 86 | - [self.view addSubview:_currentTimeLabel]; | |
| 49 | + [[CNLiveWebcastManager manager] initWithAppId:KAppId appKey:KAppKey]; | |
| 87 | 50 | |
| 88 | - _bufferProgress = [[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault]; | |
| 89 | - _bufferProgress.frame = CGRectMake(130+2, self.view.frame.size.width*9/16+20+50+24, self.view.frame.size.width-130-80-4, 1); | |
| 90 | - _bufferProgress.trackTintColor = [UIColor darkGrayColor]; | |
| 91 | - _bufferProgress.progressTintColor = [UIColor blackColor]; | |
| 92 | - _bufferProgress.progress = 0; | |
| 93 | - [self.view addSubview:_bufferProgress]; | |
| 94 | - | |
| 95 | - _timeSlide = [[UISlider alloc] initWithFrame:CGRectMake(130, self.view.frame.size.width*9/16+20+50, self.view.frame.size.width-130-80, 50)]; | |
| 96 | - [_timeSlide setMinimumTrackTintColor:[UIColor redColor]]; | |
| 97 | - [_timeSlide setMaximumTrackTintColor:[UIColor clearColor]]; | |
| 98 | - [_timeSlide addTarget:self action:@selector(beginScrubbing) forControlEvents:UIControlEventTouchDown]; | |
| 99 | - [_timeSlide addTarget:self action:@selector(endScrubbing) forControlEvents:UIControlEventTouchUpInside]; | |
| 100 | - [_timeSlide addTarget:self action:@selector(endScrubbing) forControlEvents:UIControlEventTouchUpOutside]; | |
| 101 | - [self.view addSubview:_timeSlide]; | |
| 51 | + if (_type == PlayerTypeLiving) { | |
| 52 | + //直播播放 | |
| 53 | + _player = [[CNLiveMoviePlayerController alloc] initLivePlayWithChannelId:@"iOStest" | |
| 54 | + authSuccess:^{ | |
| 55 | + | |
| 56 | + _player.videoDecoderMode = CNLiveMovieVideoDecoderMode_AUTO; | |
| 57 | + _player.view.frame = self.view.bounds; | |
| 58 | + _player.shouldAutoplay = YES; | |
| 59 | + [_player prepareToPlay]; | |
| 60 | + [self.view addSubview:_player.view]; | |
| 61 | + [self.view sendSubviewToBack:_player.view]; | |
| 62 | + | |
| 63 | + } authFailed:^(NSDictionary *errorInfo) { | |
| 64 | + NSLog(@"初始化直播播放器失败:%@", errorInfo); | |
| 65 | + [_loadingView stopLoading]; | |
| 66 | + | |
| 67 | + } getAnchorStatusResult:^(BOOL result) { | |
| 68 | + | |
| 69 | + NSLog(@"-------------获取主播状态%d", result ? YES : NO); | |
| 70 | + | |
| 71 | + }]; | |
| 72 | + | |
| 73 | + } else if (_type == PlayerTypeVOD) { | |
| 74 | + | |
| 75 | + //点播播放 | |
| 76 | + _player = [[CNLiveMoviePlayerController alloc] initVodPlayWithVId:@"aaa123" | |
| 77 | + authSuccess:^{ | |
| 78 | + | |
| 79 | + _player.videoDecoderMode = CNLiveMovieVideoDecoderMode_AUTO; | |
| 80 | + _player.view.frame = self.view.bounds; | |
| 81 | + _player.shouldAutoplay = YES; | |
| 82 | + [_player prepareToPlay]; | |
| 83 | + [self.view addSubview:_player.view]; | |
| 84 | + [self.view sendSubviewToBack:_player.view]; | |
| 85 | + | |
| 86 | + } authFailed:^(NSDictionary *errorInfo) { | |
| 87 | + NSLog(@"初始化点播播放器失败%@", errorInfo); | |
| 88 | + }]; | |
| 89 | + | |
| 90 | + } else { | |
| 91 | + | |
| 92 | + //普通播放 | |
| 93 | + _player = [[CNLiveMoviePlayerController alloc] initWithContentURL:[NSURL URLWithString:_videoUrlString]]; | |
| 94 | + _player.videoDecoderMode = CNLiveMovieVideoDecoderMode_AUTO; | |
| 95 | + _player.view.frame = self.view.bounds; | |
| 96 | + _player.shouldAutoplay = YES; | |
| 97 | + [_player prepareToPlay]; | |
| 98 | + [self.view addSubview:_player.view]; | |
| 99 | + [self.view sendSubviewToBack:_player.view]; | |
| 100 | + } | |
| 102 | 101 | |
| 103 | - _durationLabel = [[UILabel alloc] initWithFrame:CGRectMake(self.view.frame.size.width-60-10, self.view.frame.size.width*9/16+20+50, 60, 50)]; | |
| 104 | - _durationLabel.textColor = [UIColor blackColor]; | |
| 105 | - _durationLabel.font = [UIFont systemFontOfSize:13]; | |
| 106 | - [_durationLabel setText:[NSString stringWithFormat:@"00:00:00"]]; | |
| 107 | - [self.view addSubview:_durationLabel]; | |
| 102 | + [self.view addSubview:self.bgView]; | |
| 103 | + [self.view addSubview:self.loadingView]; | |
| 104 | + [self.view addSubview:self.quitBtn]; | |
| 105 | + [self.view addSubview:self.toolBgView]; | |
| 106 | + [self.toolBgView addSubview:self.playOrPauseBtn]; | |
| 107 | + [self.toolBgView addSubview:self.currentTimeLabel]; | |
| 108 | + [self.toolBgView addSubview:self.bufferProgress]; | |
| 109 | + [self.toolBgView addSubview:self.timeSlide]; | |
| 110 | + [self.toolBgView addSubview:self.durationLabel]; | |
| 108 | 111 | |
| 109 | - UIButton *exitBtn = [UIButton buttonWithType:UIButtonTypeCustom]; | |
| 110 | - exitBtn.frame = CGRectMake((CGRectGetWidth(self.view.frame)-80)/2, 400, 80, 30); | |
| 111 | - [exitBtn setTitle:@"退出" forState:UIControlStateNormal]; | |
| 112 | - [exitBtn setTitleColor:[UIColor colorWithRed:23/255.0 green:110/255.0 blue:254/255.0 alpha:1] forState:UIControlStateNormal]; | |
| 113 | - [exitBtn addTarget:self action:@selector(exitBtnAction) forControlEvents:UIControlEventTouchUpInside]; | |
| 114 | - exitBtn.layer.cornerRadius = 5; | |
| 115 | - exitBtn.layer.masksToBounds = YES; | |
| 116 | - exitBtn.layer.borderColor = [UIColor blackColor].CGColor; | |
| 117 | - exitBtn.layer.borderWidth = 1; | |
| 118 | - [self.view addSubview:exitBtn]; | |
| 119 | 112 | } |
| 120 | 113 | |
| 121 | 114 | - (void)exitBtnAction |
| ... | ... | @@ -136,6 +129,8 @@ |
| 136 | 129 | } |
| 137 | 130 | } |
| 138 | 131 | |
| 132 | +#pragma mark - | |
| 133 | +#pragma mark - action | |
| 139 | 134 | - (void)playOrPauseBtnAction |
| 140 | 135 | { |
| 141 | 136 | if (_player.isPlaying) { |
| ... | ... | @@ -145,6 +140,18 @@ |
| 145 | 140 | } |
| 146 | 141 | } |
| 147 | 142 | |
| 143 | +- (void)retryBtnAction | |
| 144 | +{ | |
| 145 | + _loadingView.retryBtn.hidden = YES; | |
| 146 | + [_loadingView startLoading]; | |
| 147 | + | |
| 148 | + if (_player.isPlaying) { | |
| 149 | + [self getAnchorStatusRetryBtnAction]; | |
| 150 | + } else { | |
| 151 | + [_player reload:nil]; | |
| 152 | + } | |
| 153 | +} | |
| 154 | + | |
| 148 | 155 | - (void)beginScrubbing //currentSlide |
| 149 | 156 | { |
| 150 | 157 | [_player pause]; |
| ... | ... | @@ -156,6 +163,41 @@ |
| 156 | 163 | [_player play]; |
| 157 | 164 | } |
| 158 | 165 | |
| 166 | + | |
| 167 | +- (void)timeChange | |
| 168 | +{ | |
| 169 | + _bufferProgress.progress = _player.playableDuration/_player.duration; | |
| 170 | + if (!_player.isPreparedToPlay) { | |
| 171 | + return; | |
| 172 | + } | |
| 173 | + | |
| 174 | + _timeSlide.value = _player.currentPlaybackTime; | |
| 175 | + _timeSlide.maximumValue = _player.duration; | |
| 176 | + | |
| 177 | + NSInteger currentSeconds = (NSInteger)_player.currentPlaybackTime; | |
| 178 | + NSInteger seconds = currentSeconds % 60; | |
| 179 | + NSInteger minutes = currentSeconds / 60; | |
| 180 | + NSInteger hours = minutes / 60; | |
| 181 | + | |
| 182 | + NSInteger durationall = (NSInteger)_player.duration; | |
| 183 | + NSInteger durationSeconds = durationall % 60; | |
| 184 | + NSInteger durationMinutes = durationall / 60; | |
| 185 | + NSInteger durationHours = durationMinutes / 60; | |
| 186 | + | |
| 187 | + _currentTimeLabel.text = [NSString stringWithFormat:@"%02ld:%02ld:%02ld", (long)hours, (long)minutes%60, (long)seconds]; | |
| 188 | + _durationLabel.text = [NSString stringWithFormat:@"%02ld:%02ld:%02ld", (long)durationHours, (long)durationMinutes%60, (long)durationSeconds]; | |
| 189 | +} | |
| 190 | + | |
| 191 | +//重新获取主播连接状态 | |
| 192 | +- (void)getAnchorStatusRetryBtnAction | |
| 193 | +{ | |
| 194 | + [_player retryToGetAnchorStatusResult:^(BOOL result) { | |
| 195 | + _loadingView.retryBtn.hidden = result; | |
| 196 | + }]; | |
| 197 | +} | |
| 198 | + | |
| 199 | +#pragma mark - | |
| 200 | +#pragma mark - addObservers | |
| 159 | 201 | -(void)installMovieNotificationObservers //添加视频播放通知 |
| 160 | 202 | { |
| 161 | 203 | //视频加载状态改变 |
| ... | ... | @@ -181,45 +223,45 @@ |
| 181 | 223 | selector:@selector(moviePlayBackStateDidChange:) |
| 182 | 224 | name:MPMoviePlayerPlaybackStateDidChangeNotification |
| 183 | 225 | object:nil]; |
| 226 | + | |
| 227 | + if (_type != PlayerTypeLiving) return; | |
| 228 | + | |
| 229 | + //获取主播状态通知(点播和普通播放不需要注册该通知) | |
| 230 | + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(anchorStatusChanged:) | |
| 231 | + name:CNLiveAnchorStatusChangedNotification | |
| 232 | + object:nil]; | |
| 233 | + | |
| 184 | 234 | } |
| 185 | 235 | |
| 186 | 236 | - (void)removeMovieNotificationObservers |
| 187 | 237 | { |
| 188 | - [[NSNotificationCenter defaultCenter]removeObserver:self name:MPMoviePlayerLoadStateDidChangeNotification object:nil]; | |
| 189 | - [[NSNotificationCenter defaultCenter]removeObserver:self name:MPMoviePlayerPlaybackDidFinishNotification object:nil]; | |
| 190 | - [[NSNotificationCenter defaultCenter]removeObserver:self name:MPMediaPlaybackIsPreparedToPlayDidChangeNotification object:nil]; | |
| 191 | - [[NSNotificationCenter defaultCenter]removeObserver:self name:MPMoviePlayerPlaybackStateDidChangeNotification object:nil]; | |
| 192 | -} | |
| 193 | - | |
| 194 | -- (void)timeChange | |
| 195 | -{ | |
| 196 | - _bufferProgress.progress = _player.playableDuration/_player.duration; | |
| 197 | - if (!_player.isPreparedToPlay) { | |
| 198 | - return; | |
| 199 | - } | |
| 238 | + [[NSNotificationCenter defaultCenter]removeObserver:self | |
| 239 | + name:MPMoviePlayerLoadStateDidChangeNotification | |
| 240 | + object:nil]; | |
| 241 | + [[NSNotificationCenter defaultCenter]removeObserver:self | |
| 242 | + name:MPMoviePlayerPlaybackDidFinishNotification | |
| 243 | + object:nil]; | |
| 244 | + [[NSNotificationCenter defaultCenter]removeObserver:self | |
| 245 | + name:MPMediaPlaybackIsPreparedToPlayDidChangeNotification | |
| 246 | + object:nil]; | |
| 247 | + [[NSNotificationCenter defaultCenter]removeObserver:self | |
| 248 | + name:MPMoviePlayerPlaybackStateDidChangeNotification | |
| 249 | + object:nil]; | |
| 250 | + if (_type != PlayerTypeLiving) return; | |
| 200 | 251 | |
| 201 | - _timeSlide.value = _player.currentPlaybackTime; | |
| 202 | - _timeSlide.maximumValue = _player.duration; | |
| 203 | - | |
| 204 | - NSInteger currentSeconds = (NSInteger)_player.currentPlaybackTime; | |
| 205 | - NSInteger seconds = currentSeconds % 60; | |
| 206 | - NSInteger minutes = currentSeconds / 60; | |
| 207 | - NSInteger hours = minutes / 60; | |
| 208 | - | |
| 209 | - NSInteger durationall = (NSInteger)_player.duration; | |
| 210 | - NSInteger durationSeconds = durationall % 60; | |
| 211 | - NSInteger durationMinutes = durationall / 60; | |
| 212 | - NSInteger durationHours = durationMinutes / 60; | |
| 213 | - | |
| 214 | - _currentTimeLabel.text = [NSString stringWithFormat:@"%02ld:%02ld:%02ld", (long)hours, (long)minutes%60, (long)seconds]; | |
| 215 | - _durationLabel.text = [NSString stringWithFormat:@"%02ld:%02ld:%02ld", (long)durationHours, (long)durationMinutes%60, (long)durationSeconds]; | |
| 252 | + [[NSNotificationCenter defaultCenter]removeObserver:self | |
| 253 | + name:CNLiveAnchorStatusChangedNotification | |
| 254 | + object:nil]; | |
| 216 | 255 | } |
| 217 | 256 | |
| 257 | +#pragma mark - | |
| 258 | +#pragma mark - notification | |
| 218 | 259 | - (void)loadStateDidChange:(NSNotification *)notification |
| 219 | 260 | { |
| 220 | 261 | if (_player) { |
| 221 | 262 | MPMovieLoadState loadState = _player.loadState; |
| 222 | 263 | if ((loadState & MPMovieLoadStatePlaythroughOK) != 0) { |
| 264 | + [_loadingView stopLoading]; | |
| 223 | 265 | NSLog(@"loadStateDidChange: MPMovieLoadStatePlaythroughOK: %d\n", (int)loadState); |
| 224 | 266 | |
| 225 | 267 | } else if ((loadState & MPMovieLoadStateStalled) != 0) { |
| ... | ... | @@ -235,6 +277,13 @@ |
| 235 | 277 | { |
| 236 | 278 | if (_player) { |
| 237 | 279 | int reason = [[[notification userInfo] valueForKey:MPMoviePlayerPlaybackDidFinishReasonUserInfoKey] intValue]; |
| 280 | + if (MPMovieFinishReasonPlaybackError == reason) { | |
| 281 | + _loadingView.retryBtn.hidden = NO; | |
| 282 | + [_loadingView stopLoading]; | |
| 283 | + } else { | |
| 284 | + _loadingView.retryBtn.hidden = YES; | |
| 285 | + } | |
| 286 | + | |
| 238 | 287 | switch (reason) |
| 239 | 288 | { |
| 240 | 289 | case MPMovieFinishReasonPlaybackEnded: |
| ... | ... | @@ -302,6 +351,124 @@ |
| 302 | 351 | } |
| 303 | 352 | } |
| 304 | 353 | |
| 354 | + | |
| 355 | +- (void)anchorStatusChanged:(NSNotification *)notification | |
| 356 | +{ | |
| 357 | + if (_player.anchorStatus == 2) { | |
| 358 | + [self.loadingView removeFromSuperview]; | |
| 359 | + [self.loadingView stopLoading]; | |
| 360 | + } | |
| 361 | + | |
| 362 | + _bgView.anchorStatus = _player.anchorStatus; | |
| 363 | +} | |
| 364 | + | |
| 365 | + | |
| 366 | +- (void)anchorStatusConnectFailed | |
| 367 | +{ //重新连接获取主播状态 | |
| 368 | + _loadingView.retryBtn.hidden = NO; | |
| 369 | +} | |
| 370 | + | |
| 371 | +#pragma mark - | |
| 372 | +#pragma mark - gatter | |
| 373 | +- (LoadingView *)loadingView | |
| 374 | +{ | |
| 375 | + if (!_loadingView) { | |
| 376 | + _loadingView = [[LoadingView alloc] initWithFrame:CGRectMake(0, 0, 70, 70)]; | |
| 377 | + _loadingView.center = self.view.center; | |
| 378 | + [_loadingView.retryBtn addTarget:self action:@selector(retryBtnAction) forControlEvents:UIControlEventTouchUpInside]; | |
| 379 | + } | |
| 380 | + return _loadingView; | |
| 381 | +} | |
| 382 | + | |
| 383 | +- (UIButton *)playOrPauseBtn | |
| 384 | +{ | |
| 385 | + if (!_playOrPauseBtn) { | |
| 386 | + _playOrPauseBtn = [UIButton buttonWithType:UIButtonTypeCustom]; | |
| 387 | + _playOrPauseBtn.frame = CGRectMake(15, 10, 40, 40); | |
| 388 | + [_playOrPauseBtn setImage:[UIImage imageNamed:@"play"] forState:UIControlStateNormal]; | |
| 389 | + [_playOrPauseBtn addTarget:self action:@selector(playOrPauseBtnAction) forControlEvents:UIControlEventTouchUpInside]; | |
| 390 | + } | |
| 391 | + return _playOrPauseBtn; | |
| 392 | +} | |
| 393 | + | |
| 394 | +- (UIButton *)quitBtn | |
| 395 | +{ | |
| 396 | + if (!_quitBtn) { | |
| 397 | + _quitBtn = [UIButton buttonWithType:UIButtonTypeCustom]; | |
| 398 | + _quitBtn.frame = CGRectMake(self.view.bounds.size.width-50, 25, 30, 30); | |
| 399 | + [_quitBtn setImage:[UIImage imageNamed:@"cancel"] forState:UIControlStateNormal]; | |
| 400 | + [_quitBtn addTarget:self action:@selector(exitBtnAction) forControlEvents:UIControlEventTouchUpInside]; | |
| 401 | + } | |
| 402 | + return _quitBtn; | |
| 403 | +} | |
| 404 | + | |
| 405 | +- (UISlider *)timeSlide | |
| 406 | +{ | |
| 407 | + if (!_timeSlide) { | |
| 408 | + _timeSlide = [[UISlider alloc] initWithFrame:CGRectMake(130, 15, self.view.frame.size.width-130-80, 30)]; | |
| 409 | + [_timeSlide setThumbImage:[UIImage imageNamed:@"btn_dot"] forState:UIControlStateNormal]; | |
| 410 | + [_timeSlide setMinimumTrackTintColor:[UIColor redColor]]; | |
| 411 | + [_timeSlide setMaximumTrackTintColor:[UIColor clearColor]]; | |
| 412 | + [_timeSlide addTarget:self action:@selector(beginScrubbing) forControlEvents:UIControlEventTouchDown]; | |
| 413 | + [_timeSlide addTarget:self action:@selector(endScrubbing) forControlEvents:UIControlEventTouchUpInside]; | |
| 414 | + [_timeSlide addTarget:self action:@selector(endScrubbing) forControlEvents:UIControlEventTouchUpOutside]; | |
| 415 | + } | |
| 416 | + return _timeSlide; | |
| 417 | +} | |
| 418 | + | |
| 419 | +- (UILabel *)currentTimeLabel | |
| 420 | +{ | |
| 421 | + if (!_currentTimeLabel) { | |
| 422 | + _currentTimeLabel = [[UILabel alloc] initWithFrame:CGRectMake(65, 5, 60, 50)]; | |
| 423 | + _currentTimeLabel.textColor = [UIColor whiteColor]; | |
| 424 | + _currentTimeLabel.font = [UIFont systemFontOfSize:13]; | |
| 425 | + [_currentTimeLabel setText:[NSString stringWithFormat:@"00:00:00"]]; | |
| 426 | + } | |
| 427 | + return _currentTimeLabel; | |
| 428 | +} | |
| 429 | + | |
| 430 | +- (UILabel *)durationLabel | |
| 431 | +{ | |
| 432 | + if (!_durationLabel) { | |
| 433 | + _durationLabel = [[UILabel alloc] initWithFrame:CGRectMake(self.view.frame.size.width-60-10, 5, 60, 50)]; | |
| 434 | + _durationLabel.textColor = [UIColor whiteColor]; | |
| 435 | + _durationLabel.font = [UIFont systemFontOfSize:13]; | |
| 436 | + [_durationLabel setText:[NSString stringWithFormat:@"00:00:00"]]; | |
| 437 | + } | |
| 438 | + return _durationLabel; | |
| 439 | +} | |
| 440 | + | |
| 441 | +- (UIProgressView *)bufferProgress | |
| 442 | +{ | |
| 443 | + if (!_bufferProgress) { | |
| 444 | + _bufferProgress = [[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault]; | |
| 445 | + _bufferProgress.frame = CGRectMake(130+2, 29, self.view.frame.size.width-130-80-4, 1); | |
| 446 | + _bufferProgress.trackTintColor = [UIColor darkGrayColor]; | |
| 447 | + _bufferProgress.progressTintColor = [UIColor lightGrayColor]; | |
| 448 | + _bufferProgress.progress = 0; | |
| 449 | + } | |
| 450 | + return _bufferProgress; | |
| 451 | +} | |
| 452 | + | |
| 453 | +- (BackgroundView *)bgView | |
| 454 | +{ | |
| 455 | + if (!_bgView) { | |
| 456 | + _bgView = [[BackgroundView alloc] initWithFrame:self.view.bounds]; | |
| 457 | + _bgView.hidden = YES; | |
| 458 | + } | |
| 459 | + return _bgView; | |
| 460 | +} | |
| 461 | + | |
| 462 | +- (UIView *)toolBgView | |
| 463 | +{ | |
| 464 | + if (!_toolBgView) { | |
| 465 | + _toolBgView = [[UIView alloc] initWithFrame:CGRectMake(0, self.view.bounds.size.height-60, self.view.bounds.size.width, 60)]; | |
| 466 | + _toolBgView.backgroundColor = [UIColor colorWithWhite:0 alpha:0.5]; | |
| 467 | + } | |
| 468 | + return _toolBgView; | |
| 469 | +} | |
| 470 | + | |
| 471 | + | |
| 305 | 472 | - (void)didReceiveMemoryWarning { |
| 306 | 473 | [super didReceiveMemoryWarning]; |
| 307 | 474 | // Dispose of any resources that can be recreated. | ... | ... |
CNLivePlayerSDKTest/CNLivePlayerSDKTest/Player/LoadingView.h
0 → 100644
| 1 | +// | |
| 2 | +// LoadingView.h | |
| 3 | +// CNLivePlayerSDKDemo | |
| 4 | +// | |
| 5 | +// Created by iOS on 17/3/8. | |
| 6 | +// Copyright © 2017年 雷浩杰. All rights reserved. | |
| 7 | +// | |
| 8 | + | |
| 9 | +#import <UIKit/UIKit.h> | |
| 10 | + | |
| 11 | +@interface LoadingView : UIView | |
| 12 | + | |
| 13 | +@property (nonatomic, strong) UIButton *retryBtn; | |
| 14 | + | |
| 15 | +- (void)startLoading; | |
| 16 | + | |
| 17 | +- (void)stopLoading; | |
| 18 | + | |
| 19 | +@end | ... | ... |
CNLivePlayerSDKTest/CNLivePlayerSDKTest/Player/LoadingView.m
0 → 100644
| 1 | +// | |
| 2 | +// LoadingView.m | |
| 3 | +// CNLivePlayerSDKDemo | |
| 4 | +// | |
| 5 | +// Created by iOS on 17/3/8. | |
| 6 | +// Copyright © 2017年 雷浩杰. All rights reserved. | |
| 7 | +// | |
| 8 | + | |
| 9 | +#import "LoadingView.h" | |
| 10 | + | |
| 11 | +@interface LoadingView () | |
| 12 | + | |
| 13 | +@property (nonatomic, strong) UIActivityIndicatorView *activityIndicator; | |
| 14 | + | |
| 15 | +@end | |
| 16 | + | |
| 17 | +@implementation LoadingView | |
| 18 | + | |
| 19 | +- (instancetype)initWithFrame:(CGRect)frame | |
| 20 | +{ | |
| 21 | + self = [super initWithFrame:frame]; | |
| 22 | + if (self) { | |
| 23 | + [self addSubview:self.activityIndicator]; | |
| 24 | + [self addSubview:self.retryBtn]; | |
| 25 | + [self.activityIndicator startAnimating]; | |
| 26 | + | |
| 27 | + } | |
| 28 | + return self; | |
| 29 | +} | |
| 30 | + | |
| 31 | +- (void)startLoading | |
| 32 | +{ | |
| 33 | + [_activityIndicator startAnimating]; | |
| 34 | +} | |
| 35 | + | |
| 36 | +- (void)stopLoading | |
| 37 | +{ | |
| 38 | + dispatch_async(dispatch_get_main_queue(), ^{ | |
| 39 | + [self.activityIndicator stopAnimating]; | |
| 40 | + }); | |
| 41 | +} | |
| 42 | + | |
| 43 | +#pragma mark - | |
| 44 | +#pragma mark - getter | |
| 45 | +- (UIActivityIndicatorView *)activityIndicator | |
| 46 | +{ | |
| 47 | + if (!_activityIndicator) { | |
| 48 | + _activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge]; | |
| 49 | + _activityIndicator.center = CGPointMake(self.bounds.size.width/2, self.bounds.size.height/2); | |
| 50 | + } | |
| 51 | + return _activityIndicator; | |
| 52 | +} | |
| 53 | + | |
| 54 | +- (UIButton *)retryBtn | |
| 55 | +{ | |
| 56 | + if (!_retryBtn) { | |
| 57 | + _retryBtn = [UIButton buttonWithType:UIButtonTypeCustom]; | |
| 58 | + _retryBtn.frame = CGRectMake(self.bounds.size.width/2-30, self.bounds.size.height/2-20, 60, 40); | |
| 59 | + [_retryBtn setTitle:@"重试" forState:UIControlStateNormal]; | |
| 60 | + [_retryBtn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; | |
| 61 | + _retryBtn.hidden = YES; | |
| 62 | + _retryBtn.layer.cornerRadius = 10; | |
| 63 | + _retryBtn.layer.masksToBounds = YES; | |
| 64 | + _retryBtn.layer.borderColor = [UIColor blackColor].CGColor; | |
| 65 | + _retryBtn.layer.borderWidth = 1; | |
| 66 | + | |
| 67 | + } | |
| 68 | + return _retryBtn; | |
| 69 | +} | |
| 70 | + | |
| 71 | +@end | ... | ... |
CNLivePlayerSDKTest/CNLivePlayerSDKTest/Player/NormalViewController.h deleted
100644 → 0
| 1 | -// | |
| 2 | -// NormalViewController.h | |
| 3 | -// CNLivePlayerSDKTest | |
| 4 | -// | |
| 5 | -// Created by 雷浩杰 on 16/8/10. | |
| 6 | -// Copyright © 2016年 雷浩杰. All rights reserved. | |
| 7 | -// | |
| 8 | - | |
| 9 | -#import <UIKit/UIKit.h> | |
| 10 | - | |
| 11 | -@interface NormalViewController : UIViewController | |
| 12 | - | |
| 13 | -@property (nonatomic, strong)NSString *videoUrlString; | |
| 14 | - | |
| 15 | -@end |
CNLivePlayerSDKTest/CNLivePlayerSDKTest/Player/NormalViewController.m deleted
100644 → 0
| 1 | -// | |
| 2 | -// NormalViewController.m | |
| 3 | -// CNLivePlayerSDKTest | |
| 4 | -// | |
| 5 | -// Created by 雷浩杰 on 16/8/10. | |
| 6 | -// Copyright © 2016年 雷浩杰. All rights reserved. | |
| 7 | -// | |
| 8 | - | |
| 9 | -#import "NormalViewController.h" | |
| 10 | -#import <CNLivePlayerSDK/CNLiveMoviePlayerController.h> | |
| 11 | - | |
| 12 | -@interface NormalViewController () | |
| 13 | -{ | |
| 14 | - CNLiveMoviePlayerController *_player; | |
| 15 | - UIButton *_playOrPauseBtn; | |
| 16 | - UISlider *_timeSlide; | |
| 17 | - UIProgressView *_bufferProgress; | |
| 18 | - UILabel *_currentTimeLabel; | |
| 19 | - UILabel *_durationLabel; | |
| 20 | - NSTimer *_currentTimer; | |
| 21 | -} | |
| 22 | - | |
| 23 | -@end | |
| 24 | - | |
| 25 | -@implementation NormalViewController | |
| 26 | - | |
| 27 | -- (void)viewDidLoad { | |
| 28 | - [super viewDidLoad]; | |
| 29 | - self.view.backgroundColor = [UIColor grayColor]; | |
| 30 | - | |
| 31 | - _currentTimer = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(timeChange) userInfo:nil repeats:YES]; | |
| 32 | - | |
| 33 | - [self installMovieNotificationObservers]; | |
| 34 | - | |
| 35 | - _player = [[CNLiveMoviePlayerController alloc] initWithContentURL:[NSURL URLWithString:_videoUrlString]]; | |
| 36 | - _player.shouldUseHWCodec = YES; | |
| 37 | - _player.view.frame = CGRectMake(0, 20, self.view.frame.size.width, self.view.frame.size.width*9/16); | |
| 38 | - _player.shouldAutoplay = YES; | |
| 39 | - [_player prepareToPlay]; | |
| 40 | - [self.view addSubview:_player.view]; | |
| 41 | - | |
| 42 | - _playOrPauseBtn = [UIButton buttonWithType:UIButtonTypeSystem]; | |
| 43 | - _playOrPauseBtn.frame = CGRectMake(10, self.view.frame.size.width*9/16+20+50, 50, 50); | |
| 44 | - [_playOrPauseBtn setImage:[UIImage imageNamed:@"play"] forState:UIControlStateNormal]; | |
| 45 | - [_playOrPauseBtn addTarget:self action:@selector(playOrPauseBtnAction) forControlEvents:UIControlEventTouchUpInside]; | |
| 46 | - [self.view addSubview:_playOrPauseBtn]; | |
| 47 | - | |
| 48 | - _currentTimeLabel = [[UILabel alloc] initWithFrame:CGRectMake(70, self.view.frame.size.width*9/16+20+50, 60, 50)]; | |
| 49 | - _currentTimeLabel.textColor = [UIColor blackColor]; | |
| 50 | - _currentTimeLabel.font = [UIFont systemFontOfSize:13]; | |
| 51 | - [_currentTimeLabel setText:[NSString stringWithFormat:@"00:00:00"]]; | |
| 52 | - [self.view addSubview:_currentTimeLabel]; | |
| 53 | - | |
| 54 | - _bufferProgress = [[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault]; | |
| 55 | - _bufferProgress.frame = CGRectMake(130+2, self.view.frame.size.width*9/16+20+50+24, self.view.frame.size.width-130-80-4, 1); | |
| 56 | - _bufferProgress.trackTintColor = [UIColor darkGrayColor]; | |
| 57 | - _bufferProgress.progressTintColor = [UIColor blackColor]; | |
| 58 | - _bufferProgress.progress = 0; | |
| 59 | - [self.view addSubview:_bufferProgress]; | |
| 60 | - | |
| 61 | - _timeSlide = [[UISlider alloc] initWithFrame:CGRectMake(130, self.view.frame.size.width*9/16+20+50, self.view.frame.size.width-130-80, 50)]; | |
| 62 | - [_timeSlide setMinimumTrackTintColor:[UIColor redColor]]; | |
| 63 | - [_timeSlide setMaximumTrackTintColor:[UIColor clearColor]]; | |
| 64 | - [_timeSlide addTarget:self action:@selector(beginScrubbing) forControlEvents:UIControlEventTouchDown]; | |
| 65 | - [_timeSlide addTarget:self action:@selector(endScrubbing) forControlEvents:UIControlEventTouchUpInside]; | |
| 66 | - [_timeSlide addTarget:self action:@selector(endScrubbing) forControlEvents:UIControlEventTouchUpOutside]; | |
| 67 | - [self.view addSubview:_timeSlide]; | |
| 68 | - | |
| 69 | - _durationLabel = [[UILabel alloc] initWithFrame:CGRectMake(self.view.frame.size.width-60-10, self.view.frame.size.width*9/16+20+50, 60, 50)]; | |
| 70 | - _durationLabel.textColor = [UIColor blackColor]; | |
| 71 | - _durationLabel.font = [UIFont systemFontOfSize:13]; | |
| 72 | - [_durationLabel setText:[NSString stringWithFormat:@"00:00:00"]]; | |
| 73 | - [self.view addSubview:_durationLabel]; | |
| 74 | - | |
| 75 | - UIButton *exitBtn = [UIButton buttonWithType:UIButtonTypeCustom]; | |
| 76 | - exitBtn.frame = CGRectMake((CGRectGetWidth(self.view.frame)-80)/2, 400, 80, 30); | |
| 77 | - [exitBtn setTitle:@"退出" forState:UIControlStateNormal]; | |
| 78 | - [exitBtn setTitleColor:[UIColor colorWithRed:23/255.0 green:110/255.0 blue:254/255.0 alpha:1] forState:UIControlStateNormal]; | |
| 79 | - [exitBtn addTarget:self action:@selector(exitBtnAction) forControlEvents:UIControlEventTouchUpInside]; | |
| 80 | - exitBtn.layer.cornerRadius = 5; | |
| 81 | - exitBtn.layer.masksToBounds = YES; | |
| 82 | - exitBtn.layer.borderColor = [UIColor blackColor].CGColor; | |
| 83 | - exitBtn.layer.borderWidth = 1; | |
| 84 | - [self.view addSubview:exitBtn]; | |
| 85 | -} | |
| 86 | - | |
| 87 | -- (void)exitBtnAction | |
| 88 | -{ | |
| 89 | - [_player stop]; | |
| 90 | - [_currentTimer invalidate]; | |
| 91 | - _currentTimer = nil; | |
| 92 | - [self removeMovieNotificationObservers]; | |
| 93 | - [self dismissViewControllerAnimated:YES completion:NULL]; | |
| 94 | -} | |
| 95 | - | |
| 96 | -- (void)changePlayBtnStatus:(BOOL)playing | |
| 97 | -{ | |
| 98 | - if (playing) { | |
| 99 | - [_playOrPauseBtn setImage:[UIImage imageNamed:@"pause"] forState:UIControlStateNormal]; | |
| 100 | - } else { | |
| 101 | - [_playOrPauseBtn setImage:[UIImage imageNamed:@"play"] forState:UIControlStateNormal]; | |
| 102 | - } | |
| 103 | -} | |
| 104 | - | |
| 105 | -- (void)playOrPauseBtnAction | |
| 106 | -{ | |
| 107 | - if (_player.isPlaying) { | |
| 108 | - [_player pause]; | |
| 109 | - } else { | |
| 110 | - [_player play]; | |
| 111 | - } | |
| 112 | -} | |
| 113 | - | |
| 114 | -- (void)beginScrubbing //currentSlide | |
| 115 | -{ | |
| 116 | - [_player pause]; | |
| 117 | -} | |
| 118 | - | |
| 119 | -- (void)endScrubbing //currentSlide | |
| 120 | -{ | |
| 121 | - [_player setCurrentPlaybackTime:_timeSlide.value]; | |
| 122 | - [_player play]; | |
| 123 | -} | |
| 124 | - | |
| 125 | --(void)installMovieNotificationObservers //添加视频播放通知 | |
| 126 | -{ | |
| 127 | - [[NSNotificationCenter defaultCenter] addObserver:self | |
| 128 | - selector:@selector(loadStateDidChange:) | |
| 129 | - name:MPMoviePlayerLoadStateDidChangeNotification | |
| 130 | - object:nil]; | |
| 131 | - | |
| 132 | - [[NSNotificationCenter defaultCenter] addObserver:self | |
| 133 | - selector:@selector(moviePlayBackDidFinish:) | |
| 134 | - name:MPMoviePlayerPlaybackDidFinishNotification | |
| 135 | - object:nil]; | |
| 136 | - | |
| 137 | - [[NSNotificationCenter defaultCenter] addObserver:self | |
| 138 | - selector:@selector(mediaIsPreparedToPlayDidChange:) | |
| 139 | - name:MPMediaPlaybackIsPreparedToPlayDidChangeNotification | |
| 140 | - object:nil]; | |
| 141 | - | |
| 142 | - [[NSNotificationCenter defaultCenter] addObserver:self | |
| 143 | - selector:@selector(moviePlayBackStateDidChange:) | |
| 144 | - name:MPMoviePlayerPlaybackStateDidChangeNotification | |
| 145 | - object:nil]; | |
| 146 | -} | |
| 147 | - | |
| 148 | -- (void)removeMovieNotificationObservers | |
| 149 | -{ | |
| 150 | - [[NSNotificationCenter defaultCenter]removeObserver:self name:MPMoviePlayerLoadStateDidChangeNotification object:nil]; | |
| 151 | - [[NSNotificationCenter defaultCenter]removeObserver:self name:MPMoviePlayerPlaybackDidFinishNotification object:nil]; | |
| 152 | - [[NSNotificationCenter defaultCenter]removeObserver:self name:MPMediaPlaybackIsPreparedToPlayDidChangeNotification object:nil]; | |
| 153 | - [[NSNotificationCenter defaultCenter]removeObserver:self name:MPMoviePlayerPlaybackStateDidChangeNotification object:nil]; | |
| 154 | -} | |
| 155 | - | |
| 156 | -- (void)timeChange | |
| 157 | -{ | |
| 158 | - _bufferProgress.progress = _player.playableDuration/_player.duration; | |
| 159 | - if (!_player.isPreparedToPlay) { | |
| 160 | - return; | |
| 161 | - } | |
| 162 | - | |
| 163 | - _timeSlide.value = _player.currentPlaybackTime; | |
| 164 | - _timeSlide.maximumValue = _player.duration; | |
| 165 | - | |
| 166 | - NSInteger currentSeconds = (NSInteger)_player.currentPlaybackTime; | |
| 167 | - NSInteger seconds = currentSeconds % 60; | |
| 168 | - NSInteger minutes = currentSeconds / 60; | |
| 169 | - NSInteger hours = minutes / 60; | |
| 170 | - | |
| 171 | - NSInteger durationall = (NSInteger)_player.duration; | |
| 172 | - NSInteger durationSeconds = durationall % 60; | |
| 173 | - NSInteger durationMinutes = durationall / 60; | |
| 174 | - NSInteger durationHours = durationMinutes / 60; | |
| 175 | - | |
| 176 | - _currentTimeLabel.text = [NSString stringWithFormat:@"%02ld:%02ld:%02ld", (long)hours, (long)minutes%60, (long)seconds]; | |
| 177 | - _durationLabel.text = [NSString stringWithFormat:@"%02ld:%02ld:%02ld", (long)durationHours, (long)durationMinutes%60, (long)durationSeconds]; | |
| 178 | -} | |
| 179 | - | |
| 180 | -- (void)loadStateDidChange:(NSNotification *)notification | |
| 181 | -{ | |
| 182 | - if (_player) { | |
| 183 | - MPMovieLoadState loadState = _player.loadState; | |
| 184 | - if ((loadState & MPMovieLoadStatePlaythroughOK) != 0) { | |
| 185 | - NSLog(@"loadStateDidChange: MPMovieLoadStatePlaythroughOK: %d\n", (int)loadState); | |
| 186 | - | |
| 187 | - } else if ((loadState & MPMovieLoadStateStalled) != 0) { | |
| 188 | - NSLog(@"loadStateDidChange: MPMovieLoadStateStalled: %d\n", (int)loadState); | |
| 189 | - | |
| 190 | - } else { | |
| 191 | - NSLog(@"loadStateDidChange: ???: %d\n", (int)loadState); | |
| 192 | - } | |
| 193 | - } | |
| 194 | -} | |
| 195 | - | |
| 196 | -- (void)moviePlayBackDidFinish:(NSNotification *)notification | |
| 197 | -{ | |
| 198 | - if (_player) { | |
| 199 | - int reason = [[[notification userInfo] valueForKey:MPMoviePlayerPlaybackDidFinishReasonUserInfoKey] intValue]; | |
| 200 | - switch (reason) | |
| 201 | - { | |
| 202 | - case MPMovieFinishReasonPlaybackEnded: | |
| 203 | - NSLog(@"playbackStateDidChange: MPMovieFinishReasonPlaybackEnded: %d\n", reason); | |
| 204 | - break; | |
| 205 | - | |
| 206 | - case MPMovieFinishReasonUserExited: | |
| 207 | - NSLog(@"playbackStateDidChange: MPMovieFinishReasonUserExited: %d\n", reason); | |
| 208 | - break; | |
| 209 | - | |
| 210 | - case MPMovieFinishReasonPlaybackError: | |
| 211 | - NSLog(@"playbackStateDidChange: MPMovieFinishReasonPlaybackError: %d\n", reason); | |
| 212 | - break; | |
| 213 | - | |
| 214 | - default: | |
| 215 | - NSLog(@"playbackPlayBackDidFinish: ???: %d\n", reason); | |
| 216 | - break; | |
| 217 | - } | |
| 218 | - } | |
| 219 | -} | |
| 220 | - | |
| 221 | -- (void)mediaIsPreparedToPlayDidChange:(NSNotification *)notification | |
| 222 | -{ | |
| 223 | - if (_player) { | |
| 224 | - NSLog(@"isPreparedToPlay"); | |
| 225 | - } | |
| 226 | -} | |
| 227 | - | |
| 228 | -- (void)moviePlayBackStateDidChange:(NSNotification *)notification | |
| 229 | -{ | |
| 230 | - if (_player) { | |
| 231 | - | |
| 232 | - switch (_player.playbackState) { | |
| 233 | - case MPMoviePlaybackStateStopped: { | |
| 234 | - [self changePlayBtnStatus:NO]; | |
| 235 | - NSLog(@"moviePlayBackStateDidChange : MPMoviePlaybackStateStopped"); | |
| 236 | - break; | |
| 237 | - } | |
| 238 | - case MPMoviePlaybackStatePlaying: { | |
| 239 | - [self changePlayBtnStatus:YES]; | |
| 240 | - NSLog(@"moviePlayBackStateDidChange : MPMoviePlaybackStatePlaying"); | |
| 241 | - break; | |
| 242 | - } | |
| 243 | - case MPMoviePlaybackStatePaused: | |
| 244 | - [self changePlayBtnStatus:NO]; | |
| 245 | - NSLog(@"moviePlayBackStateDidChange : MPMoviePlaybackStatePaused"); | |
| 246 | - break; | |
| 247 | - case MPMoviePlaybackStateInterrupted: | |
| 248 | - [self changePlayBtnStatus:NO]; | |
| 249 | - NSLog(@"moviePlayBackStateDidChange : MPMoviePlaybackStateInterrupted"); | |
| 250 | - break; | |
| 251 | - case MPMoviePlaybackStateSeekingForward: | |
| 252 | - [self changePlayBtnStatus:NO]; | |
| 253 | - NSLog(@"moviePlayBackStateDidChange : MPMoviePlaybackStateSeekingForward"); | |
| 254 | - break; | |
| 255 | - case MPMoviePlaybackStateSeekingBackward: | |
| 256 | - [self changePlayBtnStatus:NO]; | |
| 257 | - NSLog(@"moviePlayBackStateDidChange : MPMoviePlaybackStateSeekingBackward"); | |
| 258 | - break; | |
| 259 | - default: | |
| 260 | - [self changePlayBtnStatus:NO]; | |
| 261 | - NSLog(@"moviePlayBackStateDidChange : %ld", _player.playbackState); | |
| 262 | - break; | |
| 263 | - } | |
| 264 | - } | |
| 265 | -} | |
| 266 | - | |
| 267 | -- (void)didReceiveMemoryWarning { | |
| 268 | - [super didReceiveMemoryWarning]; | |
| 269 | - // Dispose of any resources that can be recreated. | |
| 270 | -} | |
| 271 | - | |
| 272 | -/* | |
| 273 | -#pragma mark - Navigation | |
| 274 | - | |
| 275 | -// In a storyboard-based application, you will often want to do a little preparation before navigation | |
| 276 | -- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { | |
| 277 | - // Get the new view controller using [segue destinationViewController]. | |
| 278 | - // Pass the selected object to the new view controller. | |
| 279 | -} | |
| 280 | -*/ | |
| 281 | - | |
| 282 | -@end |
CNLivePlayerSDKTest/CNLivePlayerSDKTest/Player/PlayerViewController.m
| ... | ... | @@ -8,7 +8,6 @@ |
| 8 | 8 | |
| 9 | 9 | #import "PlayerViewController.h" |
| 10 | 10 | #import "CNLiveViewController.h" |
| 11 | -#import "NormalViewController.h" | |
| 12 | 11 | |
| 13 | 12 | @interface PlayerViewController ()<UITextFieldDelegate, UITableViewDelegate, UITableViewDataSource> |
| 14 | 13 | { |
| ... | ... | @@ -25,34 +24,29 @@ |
| 25 | 24 | |
| 26 | 25 | self.view.backgroundColor = [UIColor whiteColor]; |
| 27 | 26 | |
| 28 | - UIButton *cnlivePlayerBtn = [UIButton buttonWithType:UIButtonTypeCustom]; | |
| 29 | - cnlivePlayerBtn.frame = CGRectMake((CGRectGetWidth(self.view.frame)-150)/2, 50, 150, 50); | |
| 30 | - [cnlivePlayerBtn setTitle:@"视讯云内容播放" forState:UIControlStateNormal]; | |
| 31 | - [cnlivePlayerBtn setTitleColor:[UIColor colorWithRed:23/255.0 green:110/255.0 blue:254/255.0 alpha:1] forState:UIControlStateNormal]; | |
| 32 | - [cnlivePlayerBtn addTarget:self action:@selector(cnlivePlayerBtnAction) forControlEvents:UIControlEventTouchUpInside]; | |
| 33 | - cnlivePlayerBtn.layer.cornerRadius = 10; | |
| 34 | - cnlivePlayerBtn.layer.masksToBounds = YES; | |
| 35 | - cnlivePlayerBtn.layer.borderColor = [UIColor blackColor].CGColor; | |
| 36 | - cnlivePlayerBtn.layer.borderWidth = 1; | |
| 37 | - [self.view addSubview:cnlivePlayerBtn]; | |
| 27 | + NSArray *titlesArr = @[@"视讯云直播播放",@"视讯云点播播放",@"普通播放"]; | |
| 28 | + | |
| 29 | + for (int i = 0; i < 3; i ++) { | |
| 30 | + UIButton *cnlivePlayerBtn = [UIButton buttonWithType:UIButtonTypeCustom]; | |
| 31 | + cnlivePlayerBtn.frame = CGRectMake((CGRectGetWidth(self.view.frame)-150)/2, 80+i*(40+20), 150, 40); | |
| 32 | + [cnlivePlayerBtn setTitle:titlesArr[i] forState:UIControlStateNormal]; | |
| 33 | + [cnlivePlayerBtn setTitleColor:[UIColor colorWithRed:23/255.0 green:110/255.0 blue:254/255.0 alpha:1] forState:UIControlStateNormal]; | |
| 34 | + [cnlivePlayerBtn addTarget:self action:@selector(cnlivePlayerBtnAction:) forControlEvents:UIControlEventTouchUpInside]; | |
| 35 | + cnlivePlayerBtn.tag = 10000+i; | |
| 36 | + cnlivePlayerBtn.layer.cornerRadius = 10; | |
| 37 | + cnlivePlayerBtn.layer.masksToBounds = YES; | |
| 38 | + cnlivePlayerBtn.layer.borderColor = [UIColor blackColor].CGColor; | |
| 39 | + cnlivePlayerBtn.layer.borderWidth = 1; | |
| 40 | + [self.view addSubview:cnlivePlayerBtn]; | |
| 41 | + } | |
| 38 | 42 | |
| 39 | - UIButton *normalPlayerBtn = [UIButton buttonWithType:UIButtonTypeCustom]; | |
| 40 | - normalPlayerBtn.frame = CGRectMake((CGRectGetWidth(self.view.frame)-150)/2, 130, 150, 50); | |
| 41 | - [normalPlayerBtn setTitle:@"普通播放" forState:UIControlStateNormal]; | |
| 42 | - [normalPlayerBtn setTitleColor:[UIColor colorWithRed:23/255.0 green:110/255.0 blue:254/255.0 alpha:1] forState:UIControlStateNormal]; | |
| 43 | - [normalPlayerBtn addTarget:self action:@selector(normalPlayerBtnAction) forControlEvents:UIControlEventTouchUpInside]; | |
| 44 | - normalPlayerBtn.layer.cornerRadius = 10; | |
| 45 | - normalPlayerBtn.layer.masksToBounds = YES; | |
| 46 | - normalPlayerBtn.layer.borderColor = [UIColor blackColor].CGColor; | |
| 47 | - normalPlayerBtn.layer.borderWidth = 1; | |
| 48 | - [self.view addSubview:normalPlayerBtn]; | |
| 49 | 43 | |
| 50 | - UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(10, 200, 80, 30)]; | |
| 44 | + UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(10, 260, 80, 30)]; | |
| 51 | 45 | label.text = @"播放地址:"; |
| 52 | 46 | label.font = [UIFont systemFontOfSize:15]; |
| 53 | 47 | [self.view addSubview:label]; |
| 54 | 48 | |
| 55 | - _textField = [[UITextField alloc] initWithFrame:CGRectMake(90, 200, self.view.frame.size.width-100, 30)]; | |
| 49 | + _textField = [[UITextField alloc] initWithFrame:CGRectMake(90, 260, self.view.frame.size.width-100, 30)]; | |
| 56 | 50 | _textField.delegate = self; |
| 57 | 51 | [self.view addSubview:_textField]; |
| 58 | 52 | _textField.borderStyle = UITextBorderStyleRoundedRect; |
| ... | ... | @@ -66,13 +60,13 @@ |
| 66 | 60 | _textField.text = @"http://video00.cnlive.com/video/data1/2015/0518/cnlive_200.mp4"; |
| 67 | 61 | } |
| 68 | 62 | |
| 69 | - _historyTableView = [[UITableView alloc] initWithFrame:CGRectMake(10, 240, self.view.frame.size.width, [UIScreen mainScreen].bounds.size.height-80-240) style:UITableViewStylePlain]; | |
| 63 | + _historyTableView = [[UITableView alloc] initWithFrame:CGRectMake(10, 300, self.view.frame.size.width, [UIScreen mainScreen].bounds.size.height-80-300) style:UITableViewStylePlain]; | |
| 70 | 64 | _historyTableView.delegate = self; |
| 71 | 65 | _historyTableView.dataSource = self; |
| 72 | 66 | [self.view addSubview:_historyTableView]; |
| 73 | 67 | |
| 74 | 68 | UIButton *exitBtn = [UIButton buttonWithType:UIButtonTypeCustom]; |
| 75 | - exitBtn.frame = CGRectMake((CGRectGetWidth(self.view.frame)-150)/2, [UIScreen mainScreen].bounds.size.height-70, 150, 50); | |
| 69 | + exitBtn.frame = CGRectMake((CGRectGetWidth(self.view.frame)-150)/2, [UIScreen mainScreen].bounds.size.height-70, 150, 40); | |
| 76 | 70 | [exitBtn setTitle:@"退出" forState:UIControlStateNormal]; |
| 77 | 71 | [exitBtn setTitleColor:[UIColor colorWithRed:23/255.0 green:110/255.0 blue:254/255.0 alpha:1] forState:UIControlStateNormal]; |
| 78 | 72 | [exitBtn addTarget:self action:@selector(exitBtnAction) forControlEvents:UIControlEventTouchUpInside]; |
| ... | ... | @@ -88,10 +82,16 @@ |
| 88 | 82 | [self dismissViewControllerAnimated:YES completion:NULL]; |
| 89 | 83 | } |
| 90 | 84 | |
| 91 | -- (void)cnlivePlayerBtnAction | |
| 85 | +- (void)cnlivePlayerBtnAction:(UIButton *)button | |
| 92 | 86 | { |
| 93 | - CNLiveViewController *cnlive = [[CNLiveViewController alloc] init]; | |
| 94 | - [self presentViewController:cnlive animated:YES completion:NULL]; | |
| 87 | + if (button.tag == 10002) { | |
| 88 | + [self normalPlayerBtnAction]; | |
| 89 | + } else { | |
| 90 | + CNLiveViewController *cnlive = [[CNLiveViewController alloc] init]; | |
| 91 | + cnlive.type = (PlayerType)button.tag-10000; | |
| 92 | + [self presentViewController:cnlive animated:YES completion:NULL]; | |
| 93 | + } | |
| 94 | + | |
| 95 | 95 | } |
| 96 | 96 | |
| 97 | 97 | - (void)normalPlayerBtnAction |
| ... | ... | @@ -117,7 +117,8 @@ |
| 117 | 117 | [_historyTableView reloadData]; |
| 118 | 118 | } |
| 119 | 119 | |
| 120 | - NormalViewController *cnlive = [[NormalViewController alloc] init]; | |
| 120 | + CNLiveViewController *cnlive = [[CNLiveViewController alloc] init]; | |
| 121 | + cnlive.type = PlayerTypeNormal; | |
| 121 | 122 | cnlive.videoUrlString = _textField.text; |
| 122 | 123 | [self presentViewController:cnlive animated:YES completion:NULL]; |
| 123 | 124 | } |
| ... | ... | @@ -163,13 +164,13 @@ |
| 163 | 164 | } |
| 164 | 165 | |
| 165 | 166 | /* |
| 166 | -#pragma mark - Navigation | |
| 167 | - | |
| 168 | -// In a storyboard-based application, you will often want to do a little preparation before navigation | |
| 169 | -- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { | |
| 170 | - // Get the new view controller using [segue destinationViewController]. | |
| 171 | - // Pass the selected object to the new view controller. | |
| 172 | -} | |
| 173 | -*/ | |
| 167 | + #pragma mark - Navigation | |
| 168 | + | |
| 169 | + // In a storyboard-based application, you will often want to do a little preparation before navigation | |
| 170 | + - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { | |
| 171 | + // Get the new view controller using [segue destinationViewController]. | |
| 172 | + // Pass the selected object to the new view controller. | |
| 173 | + } | |
| 174 | + */ | |
| 174 | 175 | |
| 175 | 176 | @end | ... | ... |
CNLivePlayerSDKTest/CNLivePlayerSDKTest/Streamer/FilterFunctionView.h
0 → 100644
| 1 | +// | |
| 2 | +// FilterFunctionView.h | |
| 3 | +// CNLivePlayerSDKDemo | |
| 4 | +// | |
| 5 | +// Created by iOS on 16/12/21. | |
| 6 | +// Copyright © 2016年 雷浩杰. All rights reserved. | |
| 7 | +// | |
| 8 | + | |
| 9 | +#import <UIKit/UIKit.h> | |
| 10 | + | |
| 11 | +typedef void(^FilterViewBlock)(float grindRatio , float whitenRatio, float intensity); | |
| 12 | + | |
| 13 | +typedef void(^FilterCurrentType)(NSInteger currentType); | |
| 14 | + | |
| 15 | +typedef void(^FilterCurrentIdx)(NSInteger currentIdx); | |
| 16 | + | |
| 17 | +@interface FilterFunctionView : UIView | |
| 18 | + | |
| 19 | +@property (nonatomic, copy) FilterViewBlock filterViewBlock; | |
| 20 | + | |
| 21 | +@property (nonatomic, copy) FilterCurrentType currentTypeBlock; | |
| 22 | + | |
| 23 | +@property (nonatomic, copy) FilterCurrentIdx currentIdxBlock; | |
| 24 | + | |
| 25 | +@end | |
| 26 | + | ... | ... |
CNLivePlayerSDKTest/CNLivePlayerSDKTest/Streamer/FilterFunctionView.m
0 → 100644
| 1 | +// | |
| 2 | +// FilterFunctionView.m | |
| 3 | +// CNLivePlayerSDKDemo | |
| 4 | +// | |
| 5 | +// Created by iOS on 16/12/21. | |
| 6 | +// Copyright © 2016年 雷浩杰. All rights reserved. | |
| 7 | +// | |
| 8 | + | |
| 9 | +#import "FilterFunctionView.h" | |
| 10 | +#import "SliderView.h" | |
| 11 | + | |
| 12 | +@interface FilterFunctionView ()<SliderViewDelegate,UIPickerViewDelegate,UIPickerViewDataSource> | |
| 13 | +{ | |
| 14 | + NSArray *_dataArr; | |
| 15 | +} | |
| 16 | + | |
| 17 | +@property (nonatomic, strong) SliderView *slider1; | |
| 18 | +@property (nonatomic, strong) SliderView *slider2; | |
| 19 | +@property (nonatomic, strong) SliderView *slider3; | |
| 20 | +@property (nonatomic, strong) UISegmentedControl * filterGroupType; | |
| 21 | +@property (nonatomic, strong) UIPickerView *effectPicker; | |
| 22 | + | |
| 23 | +@end | |
| 24 | + | |
| 25 | +@implementation FilterFunctionView | |
| 26 | + | |
| 27 | +- (instancetype)initWithFrame:(CGRect)frame | |
| 28 | +{ | |
| 29 | + self = [super initWithFrame:frame]; | |
| 30 | + if (self) { | |
| 31 | + [self addSubview:self.slider1]; | |
| 32 | + [self addSubview:self.slider2]; | |
| 33 | + [self addSubview:self.slider3]; | |
| 34 | + [self addSubview:self.effectPicker]; | |
| 35 | + [self addSubview:self.filterGroupType]; | |
| 36 | + | |
| 37 | + _dataArr = @[@"小清新",@"靓丽",@"甜美可人",@"怀旧",@"蓝调",@"老照片"]; | |
| 38 | + | |
| 39 | + } | |
| 40 | + return self; | |
| 41 | +} | |
| 42 | + | |
| 43 | +#pragma mark - | |
| 44 | +#pragma mark - SliderViewDelegate | |
| 45 | + | |
| 46 | +- (void)sliderValueChenged | |
| 47 | +{ | |
| 48 | + if (_filterViewBlock) { | |
| 49 | + _filterViewBlock(_slider1.currentValue, _slider2.currentValue, _slider3.currentValue); | |
| 50 | + } | |
| 51 | +} | |
| 52 | + | |
| 53 | +- (void)onSegCtrl:(UISegmentedControl *)sender | |
| 54 | +{ | |
| 55 | + | |
| 56 | + if (sender.selectedSegmentIndex == 0) { | |
| 57 | + _slider1.hidden = YES; | |
| 58 | + _slider2.hidden = YES; | |
| 59 | + _slider3.hidden = YES; | |
| 60 | + _effectPicker.hidden = YES; | |
| 61 | + | |
| 62 | + } else if (sender.selectedSegmentIndex == 1) { | |
| 63 | + _slider1.hidden = NO; | |
| 64 | + _slider2.hidden = YES; | |
| 65 | + _slider3.hidden = YES; | |
| 66 | + _effectPicker.hidden = YES; | |
| 67 | + } else if (sender.selectedSegmentIndex == 2) { | |
| 68 | + _slider1.hidden = NO; | |
| 69 | + _slider2.hidden = NO; | |
| 70 | + _slider3.hidden = NO; | |
| 71 | + _effectPicker.hidden = YES; | |
| 72 | + } else { | |
| 73 | + _slider1.hidden = NO; | |
| 74 | + _slider2.hidden = NO; | |
| 75 | + _slider3.hidden = NO; | |
| 76 | + _effectPicker.hidden = NO; | |
| 77 | + } | |
| 78 | + | |
| 79 | + _currentTypeBlock ? _currentTypeBlock(sender.selectedSegmentIndex) : nil; | |
| 80 | + | |
| 81 | +} | |
| 82 | + | |
| 83 | +#pragma mark - | |
| 84 | +#pragma mark - effectPicker | |
| 85 | +- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView | |
| 86 | +{ | |
| 87 | + return 1; | |
| 88 | +} | |
| 89 | + | |
| 90 | +- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component | |
| 91 | +{ | |
| 92 | + return _dataArr.count; | |
| 93 | +} | |
| 94 | + | |
| 95 | +- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component | |
| 96 | +{ | |
| 97 | + return _dataArr[row]; | |
| 98 | +} | |
| 99 | + | |
| 100 | +- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component | |
| 101 | +{ | |
| 102 | + _currentIdxBlock ? _currentIdxBlock(row) : nil; | |
| 103 | +} | |
| 104 | + | |
| 105 | +#pragma mark - | |
| 106 | +#pragma mark - getter | |
| 107 | + | |
| 108 | +- (SliderView *)slider1 | |
| 109 | +{ | |
| 110 | + if (!_slider1) { | |
| 111 | + _slider1 = [[SliderView alloc] initWithFrame:CGRectMake(8, 50, self.bounds.size.width-16, 24) title:@"磨皮"]; | |
| 112 | + _slider1.delagate = self; | |
| 113 | + } | |
| 114 | + return _slider1; | |
| 115 | +} | |
| 116 | + | |
| 117 | +- (SliderView *)slider2 | |
| 118 | +{ | |
| 119 | + if (!_slider2) { | |
| 120 | + _slider2 = [[SliderView alloc] initWithFrame:CGRectMake(8, 80, self.bounds.size.width-16, 24) title:@"美白"]; | |
| 121 | + _slider2.delagate = self; | |
| 122 | + } | |
| 123 | + return _slider2; | |
| 124 | +} | |
| 125 | + | |
| 126 | +- (SliderView *)slider3 | |
| 127 | +{ | |
| 128 | + if (!_slider3) { | |
| 129 | + _slider3 = [[SliderView alloc] initWithFrame:CGRectMake(8, 110, self.bounds.size.width-16, 24) title:@"特效"]; | |
| 130 | + _slider3.delagate = self; | |
| 131 | + } | |
| 132 | + return _slider3; | |
| 133 | +} | |
| 134 | + | |
| 135 | +- (UIPickerView *)effectPicker | |
| 136 | +{ | |
| 137 | + if (!_effectPicker) { | |
| 138 | + _effectPicker = [[UIPickerView alloc] initWithFrame:CGRectMake(8, 130, self.bounds.size.width-16, 100)]; | |
| 139 | + _effectPicker.showsSelectionIndicator=YES; | |
| 140 | + _effectPicker.tintColor = [UIColor whiteColor]; | |
| 141 | + _effectPicker.dataSource = self; | |
| 142 | + _effectPicker.delegate = self; | |
| 143 | + _effectPicker.hidden = YES; | |
| 144 | + } | |
| 145 | + return _effectPicker; | |
| 146 | +} | |
| 147 | + | |
| 148 | +- (UISegmentedControl *)filterGroupType | |
| 149 | +{ | |
| 150 | + if (!_filterGroupType) { | |
| 151 | + NSArray *items = @[ @"关", | |
| 152 | + @"旧美颜", | |
| 153 | + @"红润美颜", | |
| 154 | + @"美颜特效", | |
| 155 | + ]; | |
| 156 | + | |
| 157 | + _filterGroupType = [[UISegmentedControl alloc] initWithItems:items]; | |
| 158 | + _filterGroupType.frame = CGRectMake(10, 0, self.bounds.size.width-20, 30); | |
| 159 | + _filterGroupType.tintColor= [UIColor blackColor]; | |
| 160 | + _filterGroupType.selectedSegmentIndex = 0; | |
| 161 | + _filterGroupType.layer.cornerRadius = 5; | |
| 162 | + _filterGroupType.selectedSegmentIndex = 1; | |
| 163 | + [_filterGroupType addTarget:self action:@selector(onSegCtrl:) forControlEvents:UIControlEventValueChanged]; | |
| 164 | + } | |
| 165 | + return _filterGroupType; | |
| 166 | +} | |
| 167 | +@end | ... | ... |
CNLivePlayerSDKTest/CNLivePlayerSDKTest/Streamer/SliderView.h
0 → 100644
| 1 | +// | |
| 2 | +// SliderView.h | |
| 3 | +// CNLivePlayerSDKDemo | |
| 4 | +// | |
| 5 | +// Created by iOS on 16/12/21. | |
| 6 | +// Copyright © 2016年 雷浩杰. All rights reserved. | |
| 7 | +// | |
| 8 | + | |
| 9 | +#import <UIKit/UIKit.h> | |
| 10 | + | |
| 11 | +@protocol SliderViewDelegate <NSObject> | |
| 12 | + | |
| 13 | +- (void)sliderValueChenged; | |
| 14 | + | |
| 15 | +@end | |
| 16 | + | |
| 17 | +@interface SliderView : UIView | |
| 18 | + | |
| 19 | +@property (nonatomic, strong) UILabel* nameL; // 滑块对应参数名称 | |
| 20 | +@property (nonatomic, strong) UISlider* slider; // 滑块 | |
| 21 | +@property (nonatomic, strong) UILabel* valueL; // 滑块当前的值 | |
| 22 | +@property (nonatomic, assign, readonly) float currentValue; | |
| 23 | +@property (nonatomic, assign) id<SliderViewDelegate> delagate; | |
| 24 | + | |
| 25 | +- (instancetype)initWithFrame:(CGRect)frame title:(NSString *)title; | |
| 26 | + | |
| 27 | +@end | ... | ... |
CNLivePlayerSDKTest/CNLivePlayerSDKTest/Streamer/SliderView.m
0 → 100644
| 1 | +// | |
| 2 | +// SliderView.m | |
| 3 | +// CNLivePlayerSDKDemo | |
| 4 | +// | |
| 5 | +// Created by iOS on 16/12/21. | |
| 6 | +// Copyright © 2016年 雷浩杰. All rights reserved. | |
| 7 | +// | |
| 8 | + | |
| 9 | +#import "SliderView.h" | |
| 10 | + | |
| 11 | +@implementation SliderView | |
| 12 | +{ | |
| 13 | + NSString *_title; | |
| 14 | +} | |
| 15 | + | |
| 16 | +- (instancetype)initWithFrame:(CGRect)frame title:(NSString *)title{ | |
| 17 | + self = [super initWithFrame:frame]; | |
| 18 | + if (self) { | |
| 19 | + _title = title; | |
| 20 | + [self addSubview:self.nameL]; | |
| 21 | + [self addSubview:self.valueL]; | |
| 22 | + [self addSubview:self.slider]; | |
| 23 | + } | |
| 24 | + return self; | |
| 25 | +} | |
| 26 | + | |
| 27 | +- (float)currentValue | |
| 28 | +{ | |
| 29 | + return _slider.value; | |
| 30 | +} | |
| 31 | + | |
| 32 | +#pragma mark - | |
| 33 | +#pragma mark - action | |
| 34 | + | |
| 35 | +- (void)onSlider:(UISlider *)slider | |
| 36 | +{ | |
| 37 | + _valueL.text = [NSString stringWithFormat:@"%d",(int)(slider.value * 100)]; | |
| 38 | + if (self.delagate && [self.delagate respondsToSelector:@selector(sliderValueChenged)]) { | |
| 39 | + [self.delagate sliderValueChenged]; | |
| 40 | + } | |
| 41 | +} | |
| 42 | + | |
| 43 | +#pragma mark - | |
| 44 | +#pragma mark - getter | |
| 45 | + | |
| 46 | +- (UILabel *)nameL | |
| 47 | +{ | |
| 48 | + if (!_nameL) { | |
| 49 | + _nameL = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 40, 24)]; | |
| 50 | + _nameL.text = _title; | |
| 51 | + _nameL.textAlignment = NSTextAlignmentCenter; | |
| 52 | + _nameL.textColor = [UIColor whiteColor]; | |
| 53 | + _nameL.backgroundColor = [UIColor colorWithRed:192/255.0 green:192/255.0 blue:192/255.0 alpha:1]; | |
| 54 | + _nameL.layer.cornerRadius = 5; | |
| 55 | + _nameL.layer.masksToBounds = YES; | |
| 56 | + } | |
| 57 | + return _nameL; | |
| 58 | +} | |
| 59 | + | |
| 60 | +- (UILabel *)valueL | |
| 61 | +{ | |
| 62 | + if (!_valueL) { | |
| 63 | + _valueL = [[UILabel alloc] initWithFrame:CGRectMake(self.bounds.size.width-40, 0, 40, 24)]; | |
| 64 | + _valueL.text = @"50"; | |
| 65 | + _valueL.textAlignment = NSTextAlignmentCenter; | |
| 66 | + _valueL.textColor = [UIColor whiteColor]; | |
| 67 | + _valueL.backgroundColor = [UIColor colorWithRed:192/255.0 green:192/255.0 blue:192/255.0 alpha:1]; | |
| 68 | + _valueL.layer.cornerRadius = 5; | |
| 69 | + _valueL.layer.masksToBounds = YES; | |
| 70 | + | |
| 71 | + } | |
| 72 | + return _valueL; | |
| 73 | +} | |
| 74 | + | |
| 75 | +- (UISlider *)slider | |
| 76 | +{ | |
| 77 | + if (!_slider) { | |
| 78 | + _slider = [[UISlider alloc] initWithFrame:CGRectMake(40, 0, self.bounds.size.width-80, 24)]; | |
| 79 | + _slider.value = 0.5; | |
| 80 | + [_slider addTarget:self action:@selector(onSlider:) forControlEvents:UIControlEventValueChanged]; | |
| 81 | + } | |
| 82 | + return _slider; | |
| 83 | +} | |
| 84 | +@end | ... | ... |
CNLivePlayerSDKTest/CNLivePlayerSDKTest/Streamer/StreamerDetailViewController.m
| ... | ... | @@ -8,44 +8,70 @@ |
| 8 | 8 | |
| 9 | 9 | #import "StreamerDetailViewController.h" |
| 10 | 10 | #import <CNLivePlayerSDK/CNLiveGPUStreamerKit.h> |
| 11 | -#import "CommonCrypto/CommonDigest.h" | |
| 11 | +#import <CNLivePlayerSDK/CNLiveWebcastManager.h> | |
| 12 | +#import "FilterFunctionView.h" | |
| 12 | 13 | |
| 13 | 14 | #define Width [UIScreen mainScreen].bounds.size.width |
| 14 | 15 | #define Height [UIScreen mainScreen].bounds.size.height |
| 15 | 16 | |
| 16 | -#define kAPPID @"60_irg1rhs308" | |
| 17 | -#define kAPPKEY @"4e7a37b27a717a9e9b0a7aea4620ee55ea61fbf923bf46" | |
| 18 | - | |
| 17 | +#define KAppId @"60_irn9edco29" | |
| 18 | +#define KAppKey @"69fe70bda67e059286ad509d9f5377f815955645b52f94" | |
| 19 | +#define KBundleId @"com.cnlive.video" | |
| 19 | 20 | |
| 20 | 21 | @interface StreamerDetailViewController ()<UIGestureRecognizerDelegate> |
| 21 | 22 | { |
| 22 | - UILabel *_stateLab; | |
| 23 | 23 | UIButton *_startLiveBtn; |
| 24 | - | |
| 25 | - NSString *_activityId; //用于查询活动状态 | |
| 24 | + CGFloat _currentPinchZoomFactor;//当前触摸缩放因子 | |
| 26 | 25 | } |
| 27 | 26 | |
| 28 | 27 | @property (nonatomic,strong) UIView *captureView; |
| 28 | +@property (nonatomic,strong) UILabel *stateLab; | |
| 29 | 29 | @property (nonatomic,strong) NSURL *hostURL; |
| 30 | - | |
| 30 | +@property (nonatomic,strong) FilterFunctionView *filterView; | |
| 31 | 31 | @property (nonatomic,strong) CNLiveGPUStreamerKit *kitCN; //直播推流 |
| 32 | 32 | |
| 33 | 33 | @end |
| 34 | 34 | |
| 35 | 35 | @implementation StreamerDetailViewController |
| 36 | 36 | |
| 37 | +- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation | |
| 38 | +{ | |
| 39 | + if (_kitCN) { | |
| 40 | + return UIInterfaceOrientationMaskAllButUpsideDown; | |
| 41 | + } else { | |
| 42 | + return UIInterfaceOrientationMaskPortrait; | |
| 43 | + } | |
| 44 | +} | |
| 45 | + | |
| 46 | +-(void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation { | |
| 47 | + | |
| 48 | + UIInterfaceOrientation orie = [[UIApplication sharedApplication] statusBarOrientation]; | |
| 49 | + [_kitCN rotatePreviewTo:orie]; | |
| 50 | + | |
| 51 | + if (_kitCN.streamerBase.isStreaming) { | |
| 52 | + [_kitCN rotateStreamTo:orie]; | |
| 53 | + } | |
| 54 | + | |
| 55 | + [UIView animateWithDuration:0.2 animations:^{ | |
| 56 | + _captureView.frame = self.view.bounds; | |
| 57 | + _stateLab.frame = CGRectMake(0, Height-100, Width, 100); | |
| 58 | + }]; | |
| 59 | +} | |
| 60 | + | |
| 37 | 61 | - (void)viewDidLoad { |
| 38 | 62 | [super viewDidLoad]; |
| 39 | 63 | |
| 40 | 64 | [self.view addSubview:self.captureView]; |
| 65 | + [self.view addSubview:self.filterView]; | |
| 66 | + [self.view addSubview:self.stateLab]; | |
| 41 | 67 | |
| 42 | 68 | if (_isAuthLive) { |
| 43 | 69 | |
| 44 | 70 | NSString *activityId = [NSString stringWithFormat:@"%d%d%d%d%d",arc4random()%9,arc4random()%9,arc4random()%9,arc4random()%9,arc4random()%9]; |
| 71 | + | |
| 72 | + [[CNLiveWebcastManager manager] initWithAppId:KAppId appKey:KAppKey]; | |
| 45 | 73 | |
| 46 | - _activityId = activityId; | |
| 47 | - | |
| 48 | - _kitCN = [[CNLiveGPUStreamerKit alloc] initWithDefaultConfigWithAppId:kAPPID appKey:kAPPKEY activityId:activityId channelId:@"test001" isHorizontalScreen:@"0"]; | |
| 74 | + _kitCN = [[CNLiveGPUStreamerKit alloc] initWithDefaultConfigWithActivityId:activityId channelId:@"iOStest" isHorizontalScreen:@"0"]; | |
| 49 | 75 | |
| 50 | 76 | // _kitCN.activityName = @"iOS test"; |
| 51 | 77 | // _kitCN.coverImgUrl = @"djfiwooa"; |
| ... | ... | @@ -68,38 +94,18 @@ |
| 68 | 94 | [_startLiveBtn setTitle:@"停止直播" forState:UIControlStateSelected]; |
| 69 | 95 | |
| 70 | 96 | [self addButton:@"关闭预览" frame:CGRectMake(10, 40, 80, 40) action:@selector(startPreview:)]; |
| 71 | - | |
| 72 | 97 | [self addButton:@"退出" frame:CGRectMake(190, 40, 80, 40) action:@selector(quit)]; |
| 73 | - | |
| 74 | 98 | [self addButton:@"美颜" frame:CGRectMake(10, 100, 80, 40) action:@selector(openFilter:)]; |
| 75 | - | |
| 76 | 99 | [self addButton:@"摄像头" frame:CGRectMake(100, 100, 80, 40) action:@selector(changeCamera:)]; |
| 77 | - | |
| 78 | 100 | [self addButton:@"闪光灯" frame:CGRectMake(190, 100, 80, 40) action:@selector(openFlash)]; |
| 79 | 101 | |
| 80 | - _stateLab = [[UILabel alloc] initWithFrame:CGRectMake(0, 160, Width, 200)]; | |
| 81 | - _stateLab.backgroundColor = [UIColor clearColor]; | |
| 82 | - _stateLab.numberOfLines = 0; | |
| 83 | - _stateLab.textColor = [UIColor redColor]; | |
| 84 | - [self.view addSubview:_stateLab]; | |
| 85 | 102 | |
| 86 | 103 | UITapGestureRecognizer * singleRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onTap:)]; |
| 87 | 104 | singleRecognizer.delegate = self; |
| 88 | 105 | [self.captureView addGestureRecognizer:singleRecognizer]; |
| 89 | 106 | |
| 90 | -} | |
| 91 | - | |
| 92 | -- (void)addObservers | |
| 93 | -{ | |
| 94 | - [[NSNotificationCenter defaultCenter] addObserver:self | |
| 95 | - selector:@selector(captureStateChange) | |
| 96 | - name:CNLiveCaptureStateDidChangeNotification | |
| 97 | - object:nil]; | |
| 98 | - | |
| 99 | - [[NSNotificationCenter defaultCenter] addObserver:self | |
| 100 | - selector:@selector(streamStateChange) | |
| 101 | - name:CNLiveStreamStateDidChangeNotification | |
| 102 | - object:nil]; | |
| 107 | + UIPinchGestureRecognizer *pinch = [[UIPinchGestureRecognizer alloc]initWithTarget:self action:@selector(pinchDetected:)]; | |
| 108 | + [self.view addGestureRecognizer:pinch]; | |
| 103 | 109 | |
| 104 | 110 | } |
| 105 | 111 | |
| ... | ... | @@ -110,7 +116,6 @@ |
| 110 | 116 | button = [UIButton buttonWithType:UIButtonTypeCustom]; |
| 111 | 117 | button.frame = frame; |
| 112 | 118 | [button setTitle: title forState: UIControlStateNormal]; |
| 113 | - button.backgroundColor = [UIColor lightGrayColor]; | |
| 114 | 119 | [button addTarget:self action:action forControlEvents:UIControlEventTouchUpInside]; |
| 115 | 120 | button.titleLabel.font = [UIFont systemFontOfSize:15]; |
| 116 | 121 | [button setTitleColor:[UIColor colorWithRed:23/255.0 green:110/255.0 blue:254/255.0 alpha:1] forState:UIControlStateNormal]; |
| ... | ... | @@ -120,21 +125,48 @@ |
| 120 | 125 | button.layer.borderWidth = 1; |
| 121 | 126 | [self.captureView addSubview:button]; |
| 122 | 127 | return button; |
| 128 | + | |
| 123 | 129 | } |
| 124 | -#pragma mark - initialization | |
| 125 | 130 | |
| 131 | +#pragma mark - initialization | |
| 126 | 132 | - (void)setStreamerCfg |
| 127 | 133 | { |
| 128 | - _kitCN.videoInitBitrate = 900; | |
| 129 | - _kitCN.videoMaxBitrate = 900; | |
| 130 | - _kitCN.videoMinBitrate = 300; | |
| 131 | - _kitCN.maxKeyInterval = 2; | |
| 132 | - _kitCN.audiokBPS = 48; | |
| 134 | + //采集配置 | |
| 135 | + _kitCN.capPreset = AVCaptureSessionPreset640x480; | |
| 136 | + _kitCN.previewDimension = CGSizeMake(360, 640); | |
| 137 | + _kitCN.streamDimension = CGSizeMake(360, 640); | |
| 133 | 138 | _kitCN.videoFPS = 15; |
| 134 | - _kitCN.enAutoApplyEstimateBW = YES; | |
| 135 | - _kitCN.isOpenImageFilter = YES; | |
| 136 | - [_kitCN setVideoOrientationBy:UIInterfaceOrientationPortrait]; | |
| 139 | + _kitCN.cameraPosition = AVCaptureDevicePositionFront; | |
| 140 | + | |
| 141 | + //推流配置 | |
| 142 | + _kitCN.streamerBase.videoCodec = CNLiveVideoCodec_AUTO; | |
| 143 | + _kitCN.streamerBase.videoInitBitrate = 900; | |
| 144 | + _kitCN.streamerBase.videoMaxBitrate = 900; | |
| 145 | + _kitCN.streamerBase.videoMinBitrate = 300; | |
| 146 | + _kitCN.streamerBase.maxKeyInterval = 2; | |
| 147 | + _kitCN.streamerBase.audiokBPS = 48; | |
| 148 | + | |
| 149 | + //耳返 | |
| 150 | + if ([CNLiveAudioCapture isHeadsetPluggedIn]) { | |
| 151 | + [_kitCN.audioCapture setBPlayCapturedAudio:YES]; | |
| 152 | + } | |
| 153 | + | |
| 154 | + //设置画面朝向 | |
| 155 | + [_kitCN setVideoOrientationBy:[[UIApplication sharedApplication] statusBarOrientation]]; | |
| 137 | 156 | [_kitCN startPreview:self.captureView]; |
| 157 | + | |
| 158 | + //水印图片 | |
| 159 | + _kitCN.watermark.logoPic = [UIImage imageNamed:@"互动电视180x180"]; | |
| 160 | + _kitCN.watermark.logoRect = CGRectMake(0.8, 0.8, 50, 50); | |
| 161 | + _kitCN.watermark.logoAlpha = 0.6; | |
| 162 | + | |
| 163 | + //水印文字 | |
| 164 | + _kitCN.watermark.textLabel = [[UILabel alloc] init]; | |
| 165 | + _kitCN.watermark.textLabel.text = [NSString stringWithFormat:@"cnlive"]; | |
| 166 | + _kitCN.watermark.textLabel.font = [UIFont systemFontOfSize:8]; | |
| 167 | + _kitCN.watermark.textRect = CGRectMake(0.78, 0.88, 0.2, 0.04); | |
| 168 | + [_kitCN.watermark updateTextLabel]; | |
| 169 | + | |
| 138 | 170 | } |
| 139 | 171 | |
| 140 | 172 | #pragma mark - Action |
| ... | ... | @@ -182,8 +214,34 @@ |
| 182 | 214 | |
| 183 | 215 | - (void)openFilter:(UIButton *)button |
| 184 | 216 | { |
| 185 | - _kitCN.isOpenImageFilter = button.selected; | |
| 186 | 217 | button.selected = !button.selected; |
| 218 | + | |
| 219 | + if (button.selected) { | |
| 220 | + | |
| 221 | + _filterView.hidden = NO; | |
| 222 | + | |
| 223 | + __weak StreamerDetailViewController *weakself = self; | |
| 224 | + | |
| 225 | + _filterView.filterViewBlock = ^(float grindRatio , float whitenRatio, float intensity){ | |
| 226 | + | |
| 227 | + [weakself.kitCN.beautifyFaceFilter filterAdjustingParameterWithGrindRatio:grindRatio | |
| 228 | + whitenRatio:whitenRatio | |
| 229 | + intensity:intensity]; | |
| 230 | + }; | |
| 231 | + | |
| 232 | + _filterView.currentTypeBlock = ^(NSInteger currentType){ | |
| 233 | + [weakself.kitCN.beautifyFaceFilter setBeautyType:currentType]; | |
| 234 | + }; | |
| 235 | + | |
| 236 | + _filterView.currentIdxBlock = ^(NSInteger currentIdx){ | |
| 237 | + weakself.kitCN.beautifyFaceFilter.curEffectIdx = currentIdx; | |
| 238 | + }; | |
| 239 | + | |
| 240 | + } else { | |
| 241 | + | |
| 242 | + _filterView.hidden = YES; | |
| 243 | + | |
| 244 | + } | |
| 187 | 245 | } |
| 188 | 246 | |
| 189 | 247 | - (void)changeCamera:(UIButton *)button |
| ... | ... | @@ -195,6 +253,7 @@ |
| 195 | 253 | // _kitCN.cameraPosition = AVCaptureDevicePositionFront; |
| 196 | 254 | // } |
| 197 | 255 | [_kitCN switchCamera]; |
| 256 | + | |
| 198 | 257 | } |
| 199 | 258 | |
| 200 | 259 | - (void)openFlash |
| ... | ... | @@ -215,8 +274,38 @@ |
| 215 | 274 | [self dismissViewControllerAnimated:YES completion:NULL]; |
| 216 | 275 | } |
| 217 | 276 | |
| 277 | +- (void)addObservers | |
| 278 | +{ | |
| 279 | + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(captureStateChange) name:CNLiveCaptureStateDidChangeNotification object:nil]; | |
| 280 | + | |
| 281 | + [[NSNotificationCenter defaultCenter] addObserver:self | |
| 282 | + selector:@selector(streamStateChange) | |
| 283 | + name:CNLiveStreamStateDidChangeNotification | |
| 284 | + object:nil]; | |
| 285 | + | |
| 286 | + [[NSNotificationCenter defaultCenter] addObserver:self | |
| 287 | + selector:@selector(enterBg) | |
| 288 | + name:UIApplicationDidEnterBackgroundNotification | |
| 289 | + object:nil]; | |
| 290 | + | |
| 291 | + [[NSNotificationCenter defaultCenter] addObserver:self | |
| 292 | + selector:@selector(becameActive) | |
| 293 | + name:UIApplicationDidBecomeActiveNotification | |
| 294 | + object:nil]; | |
| 295 | +} | |
| 296 | + | |
| 218 | 297 | #pragma mark - notification |
| 219 | 298 | |
| 299 | +- (void)enterBg | |
| 300 | +{ | |
| 301 | + [_kitCN appEnterBackground]; | |
| 302 | +} | |
| 303 | + | |
| 304 | +- (void)becameActive | |
| 305 | +{ | |
| 306 | + [_kitCN appBecomeActive]; | |
| 307 | +} | |
| 308 | + | |
| 220 | 309 | - (void)captureStateChange |
| 221 | 310 | { |
| 222 | 311 | |
| ... | ... | @@ -297,19 +386,14 @@ |
| 297 | 386 | _stateLab.text = @"CNLiveStopStreamSuccess"; |
| 298 | 387 | |
| 299 | 388 | } |
| 300 | - | |
| 301 | - //结束推流成功 | |
| 302 | - if ( _kitCN.streamState == CNLiveStopStreamFailure) { | |
| 303 | - NSLog(@"---------CNLiveStopStreamSuccess"); | |
| 304 | - _stateLab.text = @"CNLiveStopStreamFailure"; | |
| 305 | - | |
| 306 | - } | |
| 307 | 389 | } |
| 308 | 390 | |
| 309 | 391 | - (void)onStreamError { |
| 310 | 392 | |
| 311 | 393 | CNLiveStreamErrorCode err = _kitCN.streamErrorCode; |
| 312 | 394 | |
| 395 | + NSLog(@"onErr: %lu [%@]", (unsigned long) err, _stateLab.text); | |
| 396 | + | |
| 313 | 397 | if ( CNLiveStreamErrorCode_CNLiveAUTHFAILED == err ) { |
| 314 | 398 | _stateLab.text = @"SDK auth failed, \npls check ak/sk"; |
| 315 | 399 | } |
| ... | ... | @@ -326,61 +410,110 @@ |
| 326 | 410 | _stateLab.text = @"CNLiveStreamErrorCode_ABNORMAL"; |
| 327 | 411 | [self toast:@"推流异常中断~~"]; |
| 328 | 412 | _startLiveBtn.selected = NO; |
| 413 | + | |
| 414 | + } else if (err == CNLiveStreamErrorCode_CODEC_OPEN_FAILED) { | |
| 415 | + NSLog(@"video codec open failed, try software codec"); | |
| 416 | + _kitCN.streamerBase.videoCodec = CNLiveVideoCodec_VT264; | |
| 417 | + [self tryReconnect]; | |
| 329 | 418 | } |
| 330 | 419 | else { |
| 331 | 420 | |
| 332 | 421 | } |
| 333 | - NSLog(@"onErr: %lu [%@]", (unsigned long) err, _stateLab.text); | |
| 422 | + | |
| 334 | 423 | // 断网重连 |
| 335 | - if ( CNLiveStreamErrorCode_CONNECT_BREAK == err) { | |
| 336 | - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ | |
| 337 | - | |
| 338 | - [_kitCN stopStream]; | |
| 424 | + if (err == CNLiveStreamErrorCode_CONNECT_BREAK || err == CNLiveStreamErrorCode_AV_SYNC_ERROR) { | |
| 425 | + [self tryReconnect]; | |
| 426 | + } | |
| 427 | + | |
| 428 | +} | |
| 429 | + | |
| 430 | +- (void) tryReconnect | |
| 431 | +{ | |
| 432 | + dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)); | |
| 433 | + dispatch_after(delay, dispatch_get_main_queue(), ^{ | |
| 434 | + | |
| 435 | + NSLog(@"try again"); | |
| 436 | + | |
| 437 | + [_kitCN stopStream]; | |
| 438 | + | |
| 439 | + if (_isAuthLive) { | |
| 339 | 440 | |
| 340 | - if (_isAuthLive) { | |
| 341 | - | |
| 342 | - [_kitCN startStream:^{ | |
| 343 | - NSLog(@"开启推流成功 ------- success"); | |
| 344 | - } failure:^(NSDictionary *errorDic) { | |
| 345 | - [self toast:errorDic[@"errorMessage"]]; | |
| 346 | - _startLiveBtn.selected = NO; | |
| 347 | - }]; | |
| 441 | + [_kitCN startStream:^{ | |
| 442 | + NSLog(@"开启推流成功 ------- success"); | |
| 443 | + _startLiveBtn.selected = YES; | |
| 348 | 444 | |
| 349 | - }else{ | |
| 350 | - [_kitCN startStream:_hostURL]; | |
| 351 | - } | |
| 352 | - }); | |
| 353 | - } | |
| 445 | + } failure:^(NSDictionary *errorDic) { | |
| 446 | + [self toast:errorDic[@"errorMessage"]]; | |
| 447 | + _startLiveBtn.selected = NO; | |
| 448 | + }]; | |
| 449 | + | |
| 450 | + }else{ | |
| 451 | + [_kitCN startStream:_hostURL]; | |
| 452 | + } | |
| 453 | + | |
| 454 | + | |
| 455 | + }); | |
| 354 | 456 | } |
| 355 | 457 | |
| 356 | -//对焦 | |
| 458 | +//对焦与曝光 | |
| 357 | 459 | - (void)onTap:(UIGestureRecognizer *)sender |
| 358 | 460 | { |
| 359 | - CGPoint point = [sender locationInView:self.view]; | |
| 360 | - CGPoint tap; | |
| 361 | - tap.x = (point.x/Width); | |
| 362 | - tap.y = (point.y/Height); | |
| 363 | - | |
| 364 | - NSError __autoreleasing *error; | |
| 365 | - [self focusAtPoint:tap error:&error]; | |
| 461 | + CGPoint current = [sender locationInView:self.view]; | |
| 462 | + CGPoint point = [self convertToPointOfInterestFromViewCoordinates:current]; | |
| 463 | + [_kitCN exposureAtPoint:point]; | |
| 464 | + [_kitCN focusAtPoint:point]; | |
| 366 | 465 | } |
| 367 | 466 | |
| 368 | -- (BOOL)focusAtPoint:(CGPoint )point error:(NSError *__autoreleasing* )error | |
| 467 | +//缩放 | |
| 468 | +- (void)pinchDetected:(UIPinchGestureRecognizer *)recognizer{ | |
| 469 | + if (recognizer.state == UIGestureRecognizerStateBegan) { | |
| 470 | + _currentPinchZoomFactor = _kitCN.pinchZoomFactor; | |
| 471 | + } | |
| 472 | + CGFloat zoomFactor = _currentPinchZoomFactor * recognizer.scale;//当前触摸缩放因子*坐标比例 | |
| 473 | + [_kitCN setPinchZoomFactor:zoomFactor]; | |
| 474 | +} | |
| 475 | + | |
| 476 | +/** | |
| 477 | + @abstract 将UI的坐标转换成相机坐标 | |
| 478 | + */ | |
| 479 | +- (CGPoint)convertToPointOfInterestFromViewCoordinates:(CGPoint)viewCoordinates | |
| 369 | 480 | { |
| 370 | - AVCaptureDevice *dev = [_kitCN getCurrentCameraDevices]; | |
| 371 | - if ([dev isFocusPointOfInterestSupported] && [dev isFocusModeSupported:AVCaptureFocusModeAutoFocus]) { | |
| 372 | - if ([dev lockForConfiguration:error]) { | |
| 373 | - [dev setFocusPointOfInterest:point]; | |
| 374 | - [dev setFocusMode:AVCaptureFocusModeAutoFocus]; | |
| 375 | - [dev unlockForConfiguration]; | |
| 376 | - return YES; | |
| 481 | + CGPoint pointOfInterest = CGPointMake(.5f, .5f); | |
| 482 | + CGSize frameSize = self.view.frame.size; | |
| 483 | + CGSize apertureSize = [_kitCN captureDimension]; | |
| 484 | + CGPoint point = viewCoordinates; | |
| 485 | + CGFloat apertureRatio = apertureSize.height / apertureSize.width; | |
| 486 | + CGFloat viewRatio = frameSize.width / frameSize.height; | |
| 487 | + CGFloat xc = .5f; | |
| 488 | + CGFloat yc = .5f; | |
| 489 | + | |
| 490 | + if (viewRatio > apertureRatio) { | |
| 491 | + CGFloat y2 = frameSize.height; | |
| 492 | + CGFloat x2 = frameSize.height * apertureRatio; | |
| 493 | + CGFloat x1 = frameSize.width; | |
| 494 | + CGFloat blackBar = (x1 - x2) / 2; | |
| 495 | + if (point.x >= blackBar && point.x <= blackBar + x2) { | |
| 496 | + xc = point.y / y2; | |
| 497 | + yc = 1.f - ((point.x - blackBar) / x2); | |
| 498 | + } | |
| 499 | + }else { | |
| 500 | + CGFloat y2 = frameSize.width / apertureRatio; | |
| 501 | + CGFloat y1 = frameSize.height; | |
| 502 | + CGFloat x2 = frameSize.width; | |
| 503 | + CGFloat blackBar = (y1 - y2) / 2; | |
| 504 | + if (point.y >= blackBar && point.y <= blackBar + y2) { | |
| 505 | + xc = ((point.y - blackBar) / y2); | |
| 506 | + yc = 1.f - (point.x / x2); | |
| 377 | 507 | } |
| 378 | 508 | } |
| 379 | - return NO; | |
| 509 | + pointOfInterest = CGPointMake(xc, yc); | |
| 510 | + return pointOfInterest; | |
| 380 | 511 | } |
| 381 | 512 | |
| 513 | + | |
| 382 | 514 | - (void)toast:(NSString*)message |
| 383 | 515 | { |
| 516 | + | |
| 384 | 517 | UIAlertView *toast = [[UIAlertView alloc] initWithTitle:nil |
| 385 | 518 | message:message |
| 386 | 519 | delegate:nil |
| ... | ... | @@ -398,15 +531,37 @@ |
| 398 | 531 | - (UIView *)captureView |
| 399 | 532 | { |
| 400 | 533 | if (!_captureView) { |
| 401 | - _captureView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, Width, Height)]; | |
| 534 | + _captureView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height)]; | |
| 402 | 535 | _captureView.backgroundColor = [UIColor blackColor]; |
| 403 | 536 | } |
| 404 | 537 | return _captureView; |
| 405 | 538 | } |
| 406 | 539 | |
| 540 | +- (UILabel *)stateLab | |
| 541 | +{ | |
| 542 | + if (!_stateLab) { | |
| 543 | + _stateLab = [[UILabel alloc] initWithFrame:CGRectMake(0, Height-100, Width, 100)]; | |
| 544 | + _stateLab.backgroundColor = [UIColor clearColor]; | |
| 545 | + _stateLab.numberOfLines = 0; | |
| 546 | + _stateLab.textColor = [UIColor redColor]; | |
| 547 | + } | |
| 548 | + return _stateLab; | |
| 549 | +} | |
| 550 | + | |
| 551 | +- (FilterFunctionView *)filterView | |
| 552 | +{ | |
| 553 | + if (!_filterView) { | |
| 554 | + _filterView = [[FilterFunctionView alloc] initWithFrame:CGRectMake(0, 170, Width, 200)]; | |
| 555 | + _filterView.hidden = YES; | |
| 556 | + } | |
| 557 | + return _filterView; | |
| 558 | +} | |
| 407 | 559 | - (void)dealloc |
| 408 | 560 | { |
| 409 | 561 | [[NSNotificationCenter defaultCenter] removeObserver:self]; |
| 562 | + | |
| 563 | + [[NSNotificationCenter defaultCenter] removeObserver:self name:CNLiveCaptureStateDidChangeNotification object:nil]; | |
| 564 | + | |
| 410 | 565 | self.kitCN = nil; |
| 411 | 566 | } |
| 412 | 567 | ... | ... |
CNLivePlayerSDKTest/CNLivePlayerSDKTest/Streamer/StreamerViewController.m
| ... | ... | @@ -217,13 +217,13 @@ |
| 217 | 217 | } |
| 218 | 218 | |
| 219 | 219 | /* |
| 220 | -#pragma mark - Navigation | |
| 221 | - | |
| 222 | -// In a storyboard-based application, you will often want to do a little preparation before navigation | |
| 223 | -- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { | |
| 224 | - // Get the new view controller using [segue destinationViewController]. | |
| 225 | - // Pass the selected object to the new view controller. | |
| 226 | -} | |
| 227 | -*/ | |
| 220 | + #pragma mark - Navigation | |
| 221 | + | |
| 222 | + // In a storyboard-based application, you will often want to do a little preparation before navigation | |
| 223 | + - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { | |
| 224 | + // Get the new view controller using [segue destinationViewController]. | |
| 225 | + // Pass the selected object to the new view controller. | |
| 226 | + } | |
| 227 | + */ | |
| 228 | 228 | |
| 229 | 229 | @end | ... | ... |
CNLivePlayerSDKTest/KSYGPUResource.bundle/1_xiaoqingxin.png
0 → 100755
571 KB
CNLivePlayerSDKTest/KSYGPUResource.bundle/2_liangli.png
0 → 100755
677 KB
CNLivePlayerSDKTest/KSYGPUResource.bundle/3_tianmeikeren.png
0 → 100755
488 KB
CNLivePlayerSDKTest/KSYGPUResource.bundle/4_huaijiu.png
0 → 100755
598 KB
CNLivePlayerSDKTest/KSYGPUResource.bundle/5_landiao.png
0 → 100755
726 KB
CNLivePlayerSDKTest/KSYGPUResource.bundle/6_laozhaop.png
0 → 100755
303 KB
CNLivePlayerSDKTest/KSYGPUResource.bundle/Info.plist
0 → 100755
No preview for this file type
CNLivePlayerSDKTest/KSYGPUResource.bundle/whiten.png
0 → 100755
700 KB